# Google Shopping Search Scraper (`s-r/google-shopping-search-scraper`) Actor

Search Google Shopping by keyword and get the product grid as JSON: title, price as text and as a number, currency, the shop selling it, rating and review count, plus Google's own product identifiers so you can look a product up again without matching on its title. One row per product.

- **URL**: https://apify.com/s-r/google-shopping-search-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** E-commerce, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 product returneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Shopping Scraper: search by keyword, get prices and merchants

This google shopping scraper takes a search term the way a shopper would type
it and returns the product grid as structured JSON: what each product is, what
it costs as text and as a number, which shop is selling it at that price, and
how it is rated. One row per product.

It also returns Google's own product identifiers, which is the part most
shopping scrapers leave out. With those you can look a product up again later
without matching on its title, which is the difference between a price history
that tracks a product and one that tracks a string.

### What you get

- **The product grid for any search term**, roughly 40 products per search,
  in the order Google ranked them (`position`).
- **The price twice**: `price` exactly as displayed (`€ 469,00`) and
  `price_value` as a number (`469.0`), so sorting and comparison need no
  parsing on your side, and the currency as its own field.
- **The merchant** selling at that price (`merchant`), not just the product.
- **Ratings and review counts**, with the count as a real number (`6200`) next
  to the label Google showed (`6,2K`).
- **Google's product identifiers** (`catalog_id`, `cluster_id`, `merchant_id`,
  `offer_id`), the handles for re-fetching a specific product later.
- **Market targeting** by country and language, so a Dutch query returns Dutch
  shops in euros and a US query returns US shops in dollars.
- **No image blobs.** Google ships each thumbnail inline as tens of kilobytes
  of base64; those are dropped so a row stays a few hundred bytes instead of
  bloating every export.

### Why scrape Google Shopping

Google Shopping is the one place where a product's offers from many shops sit
side by side, already matched to each other. A retailer's own site tells you
what that retailer charges; the grid tells you where you sit against everyone
else selling the same thing, which is the number that decides whether you get
the sale.

The awkward part is the entry point. Most price data is keyed on a barcode or a
product ID you have to know in advance, which works for your own catalogue and
fails for everything else. Searching by keyword is how you find products you do
not already have on file: a category you are considering entering, a competitor's
new range, a term customers use that you have no product for.

The identifiers matter more than they look. A grid row is a snapshot, and a
title is a bad key because shops write it differently and Google rewrites it
over time. `catalog_id` names the product itself, so tomorrow's run can be
compared against today's with confidence rather than by fuzzy-matching strings.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `queries` | array of strings | yes | — | One search term per line. |
| `country` | string | no | `nl` | Two-letter country code. Decides the market's shops and the currency. |
| `language` | string | no | `nl` | Two-letter language code for titles and labels. |
| `maxResultsPerQuery` | integer | no | `40` | Cap on products per term, 1 to 100. |

Set `country` and `language` together. Asking for Dutch shops with English
labels returns a mix that matches neither, and the currency follows the country.

### Output

One row per product.

```json
{
  "query": "sonos era 300",
  "country": "nl",
  "language": "nl",
  "position": 1,

  "title": "Sonos Era 300",
  "product_url": "https://www.google.nl/search?ibp=oshop&q=sonos%20era%20300&...",

  "price": "€ 469,00",
  "price_value": 469.0,
  "currency": "EUR",
  "merchant": "Amazon.nl - Retail",

  "rating": 4.7,
  "reviews": 6200,
  "reviews_label": "6,2K",

  "catalog_id": "3719662623030852347",
  "cluster_id": "7079904976121266844",
  "merchant_id": "576462703233852971",
  "offer_id": "3320132363282883836",
  "image_id": "17103979546685020772"
}
```

### Use cases

**Competitive price monitoring.** Run your product names on a schedule and read
`price_value` and `merchant` per run. What you get is not just the lowest price
but who is holding it, which is the difference between "we are 8% over market"
and "one specific reseller is undercutting us and nobody else is".

**Finding products you do not have on file.** Barcode-keyed price tools need
you to know the product first. Searching by category term surfaces what is
actually being sold and ranked for that term, which is how you scope a range
before committing to it rather than after.

**Building a price history that survives.** Store `catalog_id` with every row
and tomorrow's run lines up with today's on the product rather than on its
title. Titles drift, get rewritten by shops, and differ per merchant; the
identifier does not.

**Market entry research.** Run the same term with different `country` values
and compare the merchant sets and price levels. A category crowded in one
market and thin in another is a straightforward read from two runs.

### How it compares

| | This actor | Typical alternative |
|---|---|---|
| Entry point | Keyword search | Often a barcode or product ID you must already have |
| Price | As displayed and as a number, plus currency | Usually a string you parse yourself |
| Merchant | Returned per product | Sometimes missing |
| Review count | Number and original label | Label only, e.g. "6,2K" |
| Product identity | Google's catalog, cluster, merchant and offer ids | Title only |
| Image data | Dropped, product URL kept | Base64 blobs inflating every row |

Among independent listings, `automation-lab/google-shopping-scraper` is the
closest comparison at roughly 9,000 runs; its per-result price is tiered by
Apify plan, so compare against your own tier. It returns title, price, merchant,
rating and delivery, and does not expose Google's product identifiers.

If you already have barcodes rather than search terms, the sibling actor linked
below is the cheaper route: it goes straight from EAN or SKU to offers without
searching at all.

### Pricing

Priced per product returned. Search terms that match nothing are reported in
the run summary and cost nothing. All pricing is pay-per-event, so you only pay
for results you receive. There are no per-compute-unit charges.

### Limits and gotchas

- Google returns roughly 40 products per search term and there is no deeper
  pagination here. For exhaustive coverage of one product's offers, use the
  EAN/SKU actor below, which is built for depth rather than discovery.
- The grid is ranked by Google, not sorted by price. Sort on `price_value`
  yourself if you want cheapest-first; position reflects Google's ordering.
- `price` is one offer's price, the one Google surfaced on the card. It is not
  the lowest across all merchants for that product.
- Some cards carry no rating, and `rating`, `reviews` and `reviews_label` are
  then empty. That is normal for newer or low-volume products.
- `reviews` is parsed from a shortened, locale-formatted label. Where it cannot
  be parsed cleanly it is left empty rather than guessed, and the original label
  is always kept in `reviews_label`.
- Results are regionalised. The same term in `nl` and `us` returns different
  shops, different currencies and often different products.
- Free Apify plans are capped at 10 rows per run. Split larger lists across runs
  or upgrade to remove the cap.

### FAQ

**Can I scrape Google Shopping without an API key?**
Yes. Run it from the Apify Store or call it through the Apify API. No Google
account or key is involved.

**Does it return the lowest price for a product?**
No. It returns the offer Google put on the card. For every offer on a product,
follow up with the EAN/SKU actor below using the `catalog_id` from the row.

**Can I search by barcode instead of a keyword?**
You can pass one as a search term, but the sibling EAN/SKU actor is built for
that and is the better route.

**Why are some rows missing a rating?**
Google only shows ratings on products that have them. Those fields are left
empty rather than filled with a placeholder.

**How do I track the same product over time?**
Store `catalog_id` alongside each row and join on it between runs. Matching on
the title breaks as soon as a shop rewrites it.

### Related Actors

- [Free Google Shopping Scraper](https://apify.com/s-r/free-google-shopping-scraper---extract-offers-from-any-ean-sku)
  for every offer on a product, straight from an EAN or SKU.
- [Google Shopping Variants](https://apify.com/s-r/free-google-shopping-variants)
  for the colour and size variants of a product.
- [Google AI Product Answers](https://apify.com/s-r/google-ai-product-answers)
  for a written, sourced answer about a product rather than a price grid.

# Actor input Schema

## `queries` (type: `array`):

One search term per line, typed the way a shopper would: a product name, a brand and model, or a category. Each term returns its own product grid.

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

Two-letter country code. Decides which market's shops and which currency you get back.

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

Two-letter language code for the product titles and labels.

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

Google returns roughly 40 products per search. Lower this to cap what you pay for per term.

## Actor input object example

```json
{
  "queries": [
    "airpods pro 2"
  ],
  "country": "nl",
  "language": "nl",
  "maxResultsPerQuery": 40
}
```

# Actor output Schema

## `results` (type: `string`):

One row per product.

## `output` (type: `string`):

OUTPUT record with the run's counts and status flags.

## `errors` (type: `string`):

Failures with a code and a redacted message. Absent when the run had none.

# 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 = {
    "queries": [
        "sonos era 300",
        "dyson v15 detect"
    ],
    "country": "nl",
    "language": "nl",
    "maxResultsPerQuery": 40
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/google-shopping-search-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 = {
    "queries": [
        "sonos era 300",
        "dyson v15 detect",
    ],
    "country": "nl",
    "language": "nl",
    "maxResultsPerQuery": 40,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/google-shopping-search-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 '{
  "queries": [
    "sonos era 300",
    "dyson v15 detect"
  ],
  "country": "nl",
  "language": "nl",
  "maxResultsPerQuery": 40
}' |
apify call s-r/google-shopping-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/google-shopping-search-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/Mln8Z9drZaWDXvi9f/builds/Ruq4kSkN3yYIM1Brx/openapi.json
