# Google Shopping Scraper: Prices, Merchants and Ratings (`santamaria-automations/google-shopping-scraper`) Actor

Extract Google Shopping search results by keyword and country. Returns title, price, old price, currency, merchant, rating, review count, delivery and returns text, condition, badges and thumbnail. Pay per event.

- **URL**: https://apify.com/santamaria-automations/google-shopping-scraper.md
- **Developed by:** [NanoScrape](https://apify.com/santamaria-automations) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Shopping Scraper

Search [Google Shopping](https://shopping.google.com) by keyword and country and get one record per product from the results page: title, current price as a number and as shown, currency, previous price, store name, rating, review count, delivery and returns text, condition, badges and thumbnail. Use it for price monitoring, competitor and assortment research, market comparisons between countries and product feeds for analysis.

### What you get

One record per product. Fields a product card does not show are `null` (an empty list for `badges`).

| Field | Type | Description |
|-------|------|-------------|
| `product_id` | string | Google's product id for the card |
| `title` | string | Product title |
| `price` | number or null | Current price as a number |
| `price_text` | string or null | Current price as shown, for example `$98.00` or `55,00 €` |
| `currency` | string or null | ISO currency code (`USD`, `EUR`, `MXN` and so on), read from the price text and the country |
| `old_price` | number or null | Previous price, only when the card shows a higher price next to the current one |
| `old_price_text` | string or null | Previous price as shown |
| `merchant` | string or null | Store name shown on the card |
| `merchant_count` | string or null | Hint that more stores sell the product, as Google words it (for example `& more`); `N stores` when the card says so |
| `rating` | number or null | Average rating out of 5 |
| `review_count` | integer or null | Number of reviews. Google shows large counts as `3.9K`; these are returned rounded (3900) |
| `delivery` | string or null | Delivery text, in the language of the `language` input |
| `returns` | string or null | Returns text |
| `condition` | string or null | Condition label such as Refurbished or Used |
| `badges` | array | Badges on the card, for example `SALE`, `LOW PRICE` or `29% OFF` |
| `google_search_url` | string | Google Shopping search link that reopens the product by its title |
| `thumbnail` | string or null | Image URL when the results page provides one, otherwise `null` |
| `position` | integer | Rank of the product within the query, counted across pages |
| `page` | integer | Results page the product was found on |
| `search_query` | string | The query that returned the product |
| `country` | string | Country code the search ran for (the requested country) |
| `served_language` | string or null | Language the results page was served in (`en`, `de`) |
| `scraped_at` | string | UTC timestamp, ISO 8601 |

#### What the listing does not contain

Google's results page shows the store name but not a link to the store's own offer, so there is no direct merchant URL in the output. `google_search_url` opens the same product on Google Shopping, where the store links are. Product shelves that Google adds below the main results (for example a row of related machine types) are not part of the output; only the main results grid is read.

`thumbnail` is empty for the first results on a page, where Google embeds the image inline instead of linking it.

#### Fill rates

Measured on one live run for the US market (60 products, two queries, 30 each):

| Field | Filled |
|-------|--------|
| `title`, `price`, `price_text`, `currency`, `merchant` | 60 of 60 |
| `rating`, `review_count` | 59 of 60 |
| `returns` | 52 of 60 |
| `delivery` | 39 of 60 |
| `merchant_count` | 38 of 60 |
| `old_price`, `badges` | 26 of 60 |
| `thumbnail` | 9 of 60 (none for the first query, 9 of 30 for the second) |
| `condition` | 0 of 60 |

Other markets and query types fill differently; `condition` appears mainly on refurbished and used items.

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `searchQueries` | list of text | `["espresso machine"]` | Product keywords. A comma or line separated text also works, and a Google results link with a `q=` keyword is accepted. The same keyword given twice is searched once. |
| `country` | text | `US` | Two-letter country code (`US`, `GB`, `DE`, `FR`, `MX` and so on). It decides which stores, prices and currency are shown. |
| `language` | text | `en` | Language of the results page, for example `de` or `pt-BR`. It sets the language of delivery and returns texts. |
| `maxResults` | integer | `10` | Maximum number of products across all queries. `0` means no limit. The limit is shared equally between queries; a share that is not used goes to the later ones. |
| `maxResultsPerQuery` | integer | `0` | Maximum number of products for each query. `0` means no separate limit. |
| `proxyConfiguration` | object | default settings | Proxy settings. The default works for most runs. |

Numbers may be given as text (`"25"`). An empty input `{}` runs a small sample search (espresso machine, US, up to 10 products) and the run status says so. If the input cannot be read, the actor runs that same sample and writes a warning to the log. A product that appears in several queries is delivered once.

#### Example input

```json
{
  "searchQueries": ["espresso machine", "milk frother"],
  "country": "US",
  "language": "en",
  "maxResults": 80
}
```

Prices for the German market:

```json
{
  "searchQueries": ["Kaffeevollautomat"],
  "country": "DE",
  "language": "de",
  "maxResultsPerQuery": 40,
  "maxResults": 40
}
```

#### Example output

Values below come from a saved results page of the US market.

```json
{
  "product_id": "7332261587708140616",
  "title": "CASABREWS 20 Bar Semi-Automatic Espresso Machine with Steam Wand cm5418basic",
  "price": 98,
  "price_text": "$98.00",
  "currency": "USD",
  "old_price": 139,
  "old_price_text": "$139",
  "merchant": "Walmart",
  "merchant_count": "& more",
  "rating": 4.3,
  "review_count": 67,
  "delivery": "Free next-day delivery",
  "returns": "Free 90-day returns",
  "condition": null,
  "badges": ["29% OFF"],
  "google_search_url": "https://www.google.com/search?gl=us&hl=en&q=CASABREWS+20+Bar+Semi-Automatic+Espresso+Machine+with+Steam+Wand+cm5418basic&udm=28",
  "thumbnail": null,
  "position": 1,
  "page": 1,
  "search_query": "espresso machine",
  "country": "US",
  "scraped_at": "2026-10-27T10:00:00Z"
}
```

### Pricing

Pay per event. There are two events: a one-time start fee, charged once after the first product is delivered, and a fee for each delivered product. The prices are shown on the Pricing tab of this Actor. A run that delivers nothing is not charged. If you set a maximum cost per run, the run stops at that limit and the status message says how many products were delivered.

### Use with AI agents (MCP)

Add the Actor to an MCP client through the Apify MCP server:

```json
{
  "mcpServers": {
    "google-shopping-scraper": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.apify.com/?tools=santamaria-automations/google-shopping-scraper",
        "--header",
        "Authorization: Bearer <YOUR_API_TOKEN>"
      ]
    }
  }
}
```

### FAQ

**Why is the currency not the one I expected?** Prices follow the `country` input. Set `country` to the market you want to compare (`DE` for euro prices, `GB` for pound prices). If Google answers for a different country than the one requested, the actor asks once more and otherwise ends the run without delivering or charging.

**Why is there no link to the store?** Google's results page names the store but does not link to it. See "What the listing does not contain".

**Why are `rating` and `review_count` empty for some products?** New or little-reviewed products have none on the card.

**What happens with a query Google cannot match?** Google Shopping then shows loosely related products instead of an empty page, and the actor returns what the page shows. Check `title` against your keyword when you run unusual search texts.

**Why is `thumbnail` empty for some products?** Google delivers part of the images inside the page itself rather than as a link. The actor returns an image URL only when the page gives one and never returns embedded image data.

**How many products per query?** A results page holds about 40 products (40 and 38 on the pages tested). The actor follows the page's own "more results" link while a page has one and stops when it does not; a live run with `maxResults` 70 returned 40 products from page 1 and 30 from page 2, with no repeated products. The depth per query depends on what Google offers.

**How do I stop at a budget?** Set a maximum cost per run, or use `maxResults`. The status message reports how many products were delivered.

**Are the results the same as in my browser?** Google personalises and varies results by country, language and time. Positions and prices can differ between runs and from what you see when signed in.

### Related actors

- [Google Search Scraper](https://apify.com/santamaria-automations/google-search-scraper) for organic web results
- [Amazon Search Scraper](https://apify.com/santamaria-automations/amazon-search-scraper) for Amazon listings
- [eBay Scraper](https://apify.com/santamaria-automations/ebay-scraper) for eBay listings
- [Walmart Scraper](https://apify.com/santamaria-automations/walmart-scraper) for Walmart listings

### Limits

- Only the main results grid is read. Shelves, ads and related-product rows are skipped.
- No store offer link is available from the listing.
- Counts such as `3.9K` reviews are returned rounded.
- Only public listing data is returned.

# Actor input Schema

## `searchQueries` (type: `array`):

Product keywords to search on Google Shopping (for example espresso machine, running shoes). Each query runs as a separate search and a product that appears in several queries is delivered once. Leave empty to run the sample search.

## `country` (type: `string`):

Two-letter country code of the market to search (US, GB, DE, FR, MX and so on). It decides which merchants, prices and currency Google shows.

## `language` (type: `string`):

Interface language of the results page (en, de, fr, es, pt-BR and so on). It sets the language of delivery and returns texts.

## `maxResults` (type: `integer`):

Maximum number of products to return across all queries. Set to 0 for no limit.

## `maxResultsPerQuery` (type: `integer`):

Maximum number of products to return for one query. Set to 0 to use only the overall limit.

## `proxyConfiguration` (type: `object`):

Proxy settings for the run. The default works for most runs; change it only if you need your own proxy.

## Actor input object example

```json
{
  "searchQueries": [
    "espresso machine"
  ],
  "country": "US",
  "language": "en",
  "maxResults": 10,
  "maxResultsPerQuery": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}
```

# Actor output Schema

## `defaultDataset` (type: `string`):

Products from Google Shopping search results with price, old price, currency, merchant, rating, review count, delivery text, badges and thumbnail.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "espresso machine"
    ],
    "country": "US",
    "language": "en",
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "GOOGLE_SERP"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("santamaria-automations/google-shopping-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["espresso machine"],
    "country": "US",
    "language": "en",
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["GOOGLE_SERP"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("santamaria-automations/google-shopping-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "espresso machine"
  ],
  "country": "US",
  "language": "en",
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}' |
apify call santamaria-automations/google-shopping-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,santamaria-automations/google-shopping-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/kJx5Jh6zex7g7JCSJ/builds/tTnupzBuvO9SIdt5W/openapi.json
