# Google Shopping Scraper: prices per merchant, bulk queries (`steadydata/google-shopping`) Actor

Product listings from Google Shopping per search query, so you can compare prices across merchants: title, current price, previous price, discount, merchant, delivery, returns, rating and review count, with the product id and URL. Up to 200 queries per run. Only delivered products are charged.

- **URL**: https://apify.com/steadydata/google-shopping.md
- **Developed by:** [Steadydata Team](https://apify.com/steadydata) (community)
- **Categories:** E-commerce, AI, Automation
- **Features:** Limited permissions
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.76 / 1,000 product listeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 (reliable, bulk)

Product listings from Google Shopping for **up to 200 search queries per run**: title,
current price, previous price, discount, merchant, delivery, returns, rating and review
count, with the product id and a link to the Google Shopping product page. **You only
pay for products that are actually delivered.**

### Why this scraper

- **No browser, no bot wall.** Google's shopping results are fetched as server-rendered
  HTML through a country-matched search proxy. Browser-based scrapers get sent to a
  CAPTCHA; this route does not, and it is a fraction of the cost.
- **Parsed on the accessibility label, not on CSS classes.** Google renames its classes
  constantly, which is why class-based scrapers break every few weeks. Every product
  card carries a screen-reader label with the full description, price, merchant and
  rating in one sentence. That is what this actor reads, and it is far more stable.
- **Only delivered products are charged.** A query without results, or a temporary
  block, comes back as a clear error record at no cost.
- **A hard cost ceiling you control.** `maxProductsPerQuery` is the cap: one delivered
  product is one charged event.
- **Region aware.** `country` sets both the proxy exit country and the Google region, so
  Dutch prices come from a Dutch exit node instead of a US one.

### Who this is for

Price monitoring across a product list, competitor and assortment research, and anyone
who needs "what does Google Shopping show for these 200 searches" as a table instead of
200 browser tabs.

### Who this is not for

If you need the stock level or the exact checkout price at one specific shop, go to that
shop's own page. This actor reports what Google Shopping shows, which is a snapshot of
the offers Google knows about at that moment.

It returns public product and merchant data only. No buyer data, no reviewer names, no
personal data.

### Input fields

| Field | Type | Required or default | What it does |
|---|---|---|---|
| `queries` | list of text | required | Product searches, e.g. 'wireless earbuds' or 'dyson v15'. Up to 200 per run. |
| `maxProductsPerQuery` | number | 60 | Stop after this many products per query. This is your cost ceiling: one delivered product is one charged event. |
| `country` | text | US | ISO country code, e.g. US, NL, DE. Sets both the proxy exit country and the Google region. Prices and merchants are region bound, so match this to the market you want. |
| `language` | text | en | Interface language, e.g. en, nl, de. |

### Input example

```json
{
    "queries": [
        "wireless earbuds",
        "dyson v15 detect",
        "espresso machine"
    ],
    "maxProductsPerQuery": 60,
    "country": "US",
    "language": "en"
}
```

### Output example

| Field | Type | What it holds | Example |
|---|---|---|---|
| `query` | text | The input this row was found with, so rows from several inputs can be told apart. | `wireless earbuds` |
| `country` | text | The country the request was made from; Google answers differently per country. | `US` |
| `position` | number | The place in Google's own order, counting from 1. | `1` |
| `productId` | text | Google's id for the product listing, the key to join rows on. | `14736686963622067380` |
| `productUrl` | text | The product page Google links to. | `https://www.google.com/shopping/product/14736686963622067380` |
| `title` | text | The title as Google shows it. | `JLab Go Air Pop True Wireless Earbuds` |
| `price` | number | The price as a number, so you can sort and compare on it. | `19.88` |
| `priceText` | text | The price exactly as the page prints it, symbol and separators included. | `$19.88` |
| `currency` | text | The currency that price is in. | `USD` |
| `previousPrice` | number | The price this was struck through from; empty when nothing is struck through. | `25` |
| `discountPercent` | number | How far the price is below that previous price, in percent. | `20` |
| `merchant` | text | The shop offering the product. | `Walmart` |
| `delivery` | text | What the listing says about delivery, for example free delivery or a price. | `delivery $7` |
| `returns` | text | What the listing says about returns, for example a free return window. | `90-day returns` |
| `rating` | number | The average star rating of the product. | `4.4` |
| `reviewCount` | number | How many reviews that average rests on. | `6900` |

```json
{
    "query": "wireless earbuds",
    "country": "US",
    "position": 1,
    "productId": "7310953537322941042",
    "productUrl": "https://www.google.com/shopping/product/7310953537322941042",
    "title": "JLab GO Air Pop True Wireless Earbuds",
    "price": 19.99,
    "priceText": "$19.99",
    "currency": "USD",
    "previousPrice": 25.0,
    "discountPercent": 20,
    "merchant": "Target",
    "delivery": "Free delivery on $35+",
    "returns": "30-day returns",
    "rating": 4.3,
    "reviewCount": 31000,
    "status": "ok"
}
```

A query that cannot be delivered produces an error record instead, and is **not**
charged:

```json
{
    "input": "asdkjhasdkjh",
    "status": "error",
    "errorCode": "NO_RESULTS",
    "error": "No products on the results page for 'asdkjhasdkjh'"
}
```

Error codes: `INVALID_QUERY`, `NO_RESULTS`, `BLOCKED`. `INPUT_TRUNCATED` appears once when your input is longer than this actor accepts.

### Related actors from steadydata

- [shopify-store-report](https://apify.com/steadydata/shopify-store-report): the catalogue and prices of a Shopify store itself
- [woocommerce-store-report](https://apify.com/steadydata/woocommerce-store-report): the same for WooCommerce stores
- [google-images](https://apify.com/steadydata/google-images): product images for the same query

### Pricing

Pay per event: one `product-listed` event per delivered product. No charge for queries
that fail or return nothing, no separate platform-usage surcharge.

**Free Apify plan:** this actor delivers up to 25 rows per run for accounts on the Apify free
plan, and then stops with a message. That limit is set by us, not by Apify. It exists so the
actor keeps paying for itself for the people who do pay. Any paid Apify plan runs it at full
size, billed per delivered row, with failed rows never charged.

**Reviews:** if this actor saves you time, a short review on this page is the one thing that
helps most. Ratings are what other buyers look at first, and we have no other way to ask.

### Use with AI agents

AI assistants such as Claude, ChatGPT or Cursor can run this actor through Apify's own MCP server. Ask for the actor `steadydata/google-shopping` by its full name, so the assistant does not pick a different one, and start from this minimal input:

```json
{
  "queries": [
    "wireless earbuds"
  ]
}
```

The assistant can read the price per event from the Pricing section above before the first paid run. Failed input comes back as an error row, as described above.

### FAQ

**Why do some fields come back empty?**
Because Google did not show them for that product. A listing without a discount has no
previous price, and a listing without reviews has no rating. Those fields are left empty
rather than filled with a guess.

**Do prices include tax and shipping?**
They are the prices Google displays, which follow the merchant's own convention per
country. Delivery information is returned separately in the `delivery` field when Google
shows it.

**Why does the same product appear more than once?**
Because several merchants offer it. Each row is one offer, with its own merchant and
price, which is exactly what you want for price comparison.

**Can I track prices over time?**
Yes, and that is the common pattern: run the same query list on a schedule and keep the
rows with a timestamp. Prices move, so a run is a snapshot.

**Is personal data collected?**
No. Public product and merchant data only.

**What happens when Google changes something?**
Google changes its markup constantly; that is the nature of this work. Reading the
accessibility label instead of the CSS classes makes this actor far less sensitive to
those changes, and it is monitored daily and fixed fast. While it is broken you are not
charged, because only delivered products cost anything.

**Can I call it from my own code, like an API?**
Yes. Every run is available through the Apify API: start it with one HTTP request, then read
the results as JSON, CSV or Excel from the dataset endpoint, or use the official Python and
JavaScript clients. The input is the same JSON as in the input example above, so a scheduled
script, a Make or Zapier step or an AI agent can call it without any changes.

# Changelog

This Actor's version history is a separate document: https://apify.com/steadydata/google-shopping/changelog.md

# Actor input Schema

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

Product searches, e.g. 'wireless earbuds' or 'dyson v15'. Up to 200 per run.

## `maxProductsPerQuery` (type: `integer`):

Stop after this many products per query. This is your cost ceiling: one delivered product is one charged event.

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

ISO country code, e.g. US, NL, DE. Sets both the proxy exit country and the Google region. Prices and merchants are region bound, so match this to the market you want.

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

Interface language, e.g. en, nl, de.

## Actor input object example

```json
{
  "queries": [
    "wireless earbuds"
  ],
  "maxProductsPerQuery": 60,
  "country": "US",
  "language": "en"
}
```

# Actor output Schema

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

No description

# 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": [
        "wireless earbuds"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadydata/google-shopping").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": ["wireless earbuds"] }

# Run the Actor and wait for it to finish
run = client.actor("steadydata/google-shopping").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": [
    "wireless earbuds"
  ]
}' |
apify call steadydata/google-shopping --silent --output-dataset

```

## MCP server setup

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

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/DPeIoVIPYIojd6IIo/builds/vxn4VGbNwxGkVrB32/openapi.json
