# Google Shopping Scraper API (`shakamize/google-shopping-scraper`) Actor

Scrape Google Shopping into structured products: title, price, previous price and discount, merchant, rating, review count, delivery and returns terms. Optionally resolves each product to the retailers' own deep links rather than Google redirects.

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

## Pricing

from $3.33 / 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.
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?

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 API

Turns a Google Shopping search into structured products: title, price, the
struck-through previous price and its discount, merchant, rating, review count,
and the delivery and returns terms Google prints on the card.

It will also resolve each product to **the retailers' own deep links** - the
`zappos.com/product/...` or `bestbuy.com/.../sku/...` URL that opens the product
at the shop - rather than a Google redirect.

Priced **per product**, at $0.004. Retailer links are a separate $0.008 per
product and are **off by default**. A search that returns nothing is never
charged, and neither is a product Google publishes no retailer link for.

Use it to compare prices across merchants, check minimum-advertised-price
compliance, find which retailers stock a product, or monitor discounts and
delivery terms across a category. It reads Google's aggregated merchant
listings, so it covers many retailers shallowly rather than one retailer's full
catalogue; for one of those in depth, use **Amazon Product Scraper API** or
**Walmart Reviews API Scraper**.

### Retailer links, and why almost nothing else in this niche has them

Read this before comparing us to anything else on the store.

**Google's shopping results contain no outbound link of any kind.** Measured
live on 2026-08-30 across the modern `udm=28` results, the legacy `tbm=shop`
results, three non-browser user agents and a mobile one: every page carried
**zero** `/aclk` links, **zero** `/url?q=` redirects and **zero** retailer
hostnames. There is nothing on the results page to follow.

That is why the competing actors either omit the field or fill it with something
else. One of them publishes `product_url` with the description *"Direct link to
the product page on the merchant's website. Currently always null"*. Another
publishes `merchantUrl` described as *"derived from merchant name when
available"* - a guess from a string, not an observed link.

**The offers live on a different Google surface**, its per-product page, and
that one *is* rendered server side. Switch **Include retailer links** on and each
product is resolved there, returning the real destinations with the shop's own
SKU and query string intact, deduplicated per shop, with Google's `ved` and `opi`
tracking parameters stripped.

It is off by default because it is honest about its cost: Google publishes those
offers one product at a time, so each product becomes **a further request**
taking 14 to 40 seconds, charged as a separate event. A product with no link
published is still stored, and is not charged for links.

### How many products you get per search, and where Google stops

**Google serves about 40 products per request and roughly 134 in total for a
query.** Measured live on 2026-08-30 on `running shoes`:

| Request     | Products returned | Overlap with earlier pages |
| ----------- | ----------------- | -------------------------- |
| `start=0`   | 54                | -                          |
| `start=40`  | 40                | **none**                   |
| `start=80`  | 40                | **none**                   |
| `start=120` | **0**             | end of results             |

The three pages shared **not one product id**, so 134 distinct products came
back for three requests. `&num=100` is inert - it returns the same 40-card grid
as the default.

Set **Max products per search** above 40 and the actor pages through with
Google's own offset, one page at a time, stopping when Google does. Asking for
page four spends a request to learn there is no page four, so pages are fetched
only as long as they keep returning products, and the log says when the ceiling
was Google's rather than your cap.

### It reads both of Google's card layouts

Google serves the shopping vertical in at least two layouts and the difference
is invisible until it costs you results. `running shoes` renders a 40-card grid
plus carousels. `55 inch oled tv` renders **no grid at all** - only carousels.

A scraper keyed to the grid returns 40 products for one query and **zero** for
the other. This one keys on the product identifiers that both layouts carry, and
is tested against a captured page of each.

### What you get per product

| Field                            | Coverage, measured over 233 products |
| -------------------------------- | ------------------------------------ |
| `title`, `price`, `merchant`     | **100%**                             |
| `rating`, `reviewCount`          | 81 to 100%                           |
| `returnsPolicy`                  | 76 to 100%                           |
| `delivery`                       | ~60%                                 |
| `previousPrice`, `discountLabel` | 18 to 61%                            |

Coverage varies by category rather than by fault - Google simply does not print
a discount on a product that is not discounted. The actor's monitoring uses a
**separate floor per field** for that reason, so an optional field going quiet
is not reported as a collapse.

A few details that are easy to get wrong and are tested here:

- **`reviewCount` is expanded.** Google writes `(1.1K)`; this is stored as
  `1100`, not `1.1`.
- **`previousPrice` is checked against the price it discounts.** Google
  occasionally publishes a struck-through price *below* the current one, which
  reads as an enormous saving to anything that does not compare them. When it
  does not exceed the price, the field is null. It also covers Google's
  `Usually $X` reference price, which is the same slot worded differently.
- **A missing value is `null`, never `0`.** An unrated product is not a
  zero-star product, and the filters drop it rather than assuming it qualifies.
- **`hasOtherMerchants`** marks the products Google labels "& more", which are
  the ones worth resolving to retailer links.
- **Prices carry the currency they were written in**, read from the price
  string rather than assumed from the country.

### How to filter Google Shopping by price, rating and merchant

Every filter runs over what was parsed, never over the query, so a market that
words Google's own filters differently cannot silently return the wrong
products. Minimum and maximum price, minimum rating, minimum reviews, on-sale
only, and merchant include/exclude lists.

A product whose value is unknown is **dropped** by the matching filter rather
than kept. Asking for four stars and up returns products Google rated four and
up, not products Google said nothing about.

### How to scrape only new Google Shopping products on repeat runs

Switch **Only products not seen before** on and the actor remembers what it has
already stored in a named key-value store and skips it, which turns a saved
search into a change feed. Duplicates inside a single run are always removed,
with or without it.

### Example input for a Google Shopping search

```json
{
  "queries": ["running shoes", "espresso machine"],
  "countryCode": "US",
  "maxProductsPerQuery": 80,
  "minRating": 4,
  "includeRetailerLinks": false
}
```

### Google Shopping data you get back, as JSON, CSV or Excel

```json
{
  "productId": "16602690315143339002",
  "catalogId": "13366226642799457284",
  "title": "Brooks Men's Ghost 18",
  "price": { "value": 150, "currency": "$", "currencyCode": "USD" },
  "previousPrice": null,
  "discountLabel": null,
  "merchant": "Brooks Running",
  "hasOtherMerchants": true,
  "rating": 4.3,
  "reviewCount": 710,
  "delivery": "Free delivery by Thu",
  "returnsPolicy": "365-day returns",
  "retailerOffers": [
    {
      "merchant": "Fleet Feet",
      "url": "https://www.fleetfeet.com/products/mens-brooks-ghost-18?upc=198486775656",
      "domain": "fleetfeet.com"
    }
  ],
  "position": 3,
  "page": 1,
  "searchQuery": "running shoes",
  "countryCode": "US",
  "language": "en",
  "url": "https://www.google.com/search?ibp=oshop&hl=en&gl=us&q=Brooks+Men%27s+Ghost+18&prds=catalogid%3A13366226642799457284",
  "scrapedAt": "2026-08-30T09:15:00.000Z"
}
```

`url` is Google's own page for the product, and is labelled as Google's. The
retailers' links are in `retailerOffers`, and are null unless you asked for them.

### Errors are records, not silence

Every search leaves a trace. A search Google returned nothing for produces a
record saying so, with `"error": "no_products_found"`, so a caller can tell an
empty category from a page that could not be read. Nothing is charged for it.

### FAQ

#### How do I get the retailer's own link instead of a Google redirect?

Set `includeRetailerLinks: true`. Google's shopping results page carries no
outbound link in either card layout, so the links are resolved from a second
surface, one request per product.

Measured on 2026-09-01, a 20-product run returned **91 retailer offers across 43
distinct domains**, an average of 4.5 shops per product, including walmart.com,
ebay.com, lodgecastiron.com, wayfair.com, surlatable.com and target.com.

#### Why do retailer links cost extra, and how much?

Because each one is a whole further request to Google, where a product row costs
about a fortieth of a request. They are $0.008 per product against $0.004 for
the product itself, and **off by default** so a run cannot quietly cost three
times what you expected.

A product Google publishes no retailer link for is not charged for one. The run
reports that count directly:

```
INFO  Retailer links found for 20 product(s); 0 had none published and were not
      charged for them.
```

Expect them to be slow: 14 to 40 seconds each, and the run warns you before it
starts.

#### How many products can I get from one search?

Pagination steps 40 at a time and runs dry near 134 products. Where it runs dry
is a property of the query, not a fixed limit.

Note that a page renders more cards than it has distinct products. On a run
measured on 2026-09-01, page one showed **48 rendered cards for 20 kept
products** after deduplication, because Google renders the same product more
than once per page.

#### Which fields are always present, and which are not?

From a 20-product run on 2026-09-01: price, rating, review count and merchant on
**20 of 20**; returns policy on 18; delivery terms on 11; a struck-through
previous price on 7; and an explicit discount label on 4.

Missing sale fields mean the product is not on sale, not that the read failed.

#### Will I be charged for a search that returns nothing?

No. Charging is per product stored, so a search returning nothing charges
nothing, and products removed by your price, rating, review or merchant filters
are not charged either.

### Permissions the Google Shopping scraper needs

This actor asks for the **minimum permissions** Apify offers. It reads its
input, writes its dataset, and - only when you switch repeat-run memory on -
reads and writes the one named key-value store you name. It never asks for
account-wide access.

### Google Shopping scraper pricing, pay per product

| Event              | Price       | What it is                                                                                      |
| ------------------ | ----------- | ----------------------------------------------------------------------------------------------- |
| **Product**        | $0.004 each | One product row with everything in the table above.                                             |
| **Retailer links** | $0.008 each | The shops selling one product, as their own deep links. Only for products you asked to resolve. |

There is **no charge for starting a run**, no charge for a search that returns
nothing, and no charge for a retailer lookup that comes back empty.

### Telemetry

The Actor reports run health to its maintainer: error codes, the build number,
and per-field counts. On a failure it also sends a capped sample of the failed
requests, including **the Google URL you asked for, which contains your search
term**. It carries no personal data, but it is your input, and this section says
so rather than leave you to find out. Your proxy configuration and the scraped
products are never sent.

# Actor input Schema

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

What to search Google Shopping for, written as a shopper would type it. One entry per search.

## `countryCode` (type: `string`):

The Google country to shop from. It decides the currency prices are quoted in and which merchants appear.

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

Google interface language, as a two-letter code. Defaults to the language of the chosen country. It decides the wording of the delivery and returns terms, which Google publishes as text rather than as data.

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

Google returns about 40 products per request. Above 40 the actor pages through with Google's own <code>start</code> parameter, 40 at a time, and every extra request is logged. Measured 2026-08-30: a query runs dry at roughly 134 products, which is Google's ceiling rather than a cap applied here.

## `maxProducts` (type: `integer`):

Stop the whole run after this many products, across every search. Prefilled with 200 so a run over many search terms has a ceiling; clear it for no overall cap.

## `includeRetailerLinks` (type: `boolean`):

Resolve each product to the shops selling it, as the <b>retailer's own deep link</b> rather than a Google redirect. Google publishes these only on its per-product page, so this costs <b>one further request per product</b>, takes 14 to 40 seconds each, and is charged as a separate event. Off by default for that reason. Products with no link published are stored anyway and are not charged for links.

## `minPrice` (type: `integer`):

Keep only products at or above this price, in the currency of the chosen country. A product Google published no price for is dropped rather than kept.

## `maxPrice` (type: `integer`):

Keep only products at or below this price.

## `minRating` (type: `integer`):

Keep only products rated at or above this, out of five. Products Google published no rating for are dropped, never treated as unrated-but-acceptable.

## `minReviews` (type: `integer`):

Keep only products with at least this many reviews.

## `onSaleOnly` (type: `boolean`):

Keep only products showing a genuine struck-through price above the price it discounts.

## `merchants` (type: `array`):

Keep only products whose merchant name contains one of these, for example <code>Walmart</code>.

## `excludeMerchants` (type: `array`):

Drop products whose merchant name contains one of these.

## `onlyNewProducts` (type: `boolean`):

Remember the products stored by previous runs and skip them. Products repeated inside a single run are always removed, with or without this.

## `newProductsStoreName` (type: `string`):

The named key-value store holding the products already seen. Use a different name per saved search to keep their memories apart.

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

Defaults to Apify's unblocking service, which is what makes Google Shopping reachable over plain HTTP. Changing this is rarely useful: a plain datacenter or residential address is redirected away from the results.

## Actor input object example

```json
{
  "queries": [
    "running shoes"
  ],
  "countryCode": "US",
  "language": "en",
  "maxProductsPerQuery": 40,
  "maxProducts": 200,
  "includeRetailerLinks": false,
  "onSaleOnly": false,
  "onlyNewProducts": false,
  "newProductsStoreName": "google-shopping-seen",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# Actor output Schema

## `products` (type: `string`):

Every product Google returned for the searches you asked for, deduplicated across pages.

# 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": [
        "running shoes"
    ],
    "maxProducts": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("shakamize/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 = {
    "queries": ["running shoes"],
    "maxProducts": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("shakamize/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 '{
  "queries": [
    "running shoes"
  ],
  "maxProducts": 200
}' |
apify call shakamize/google-shopping-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,shakamize/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/vyGAIJ3SqYZ6ylqa7/builds/RmBMbacgszSxbh9zb/openapi.json
