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

Scrapes Google Shopping results for any search in 68 countries: title, price, old price, currency, store, rating, reviews, delivery, returns, thumbnail and product link, about 40 products per request, with price filters and optional offers from every store.

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

## Pricing

from $2.00 / 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

### What does Google Shopping Scraper do?

**Google Shopping Scraper** collects **Google Shopping results** for any search
words in **68 countries**: the product **title**, **price as a number** with its
**currency**, the **old (was) price**, the **store** selling it, the **brand**,
the **star rating and review count**, the **delivery and returns** text, the
**thumbnail**, Google's **product page link** and the product's ids. Each
Google request brings back **about 40 products**, so runs are fast and cheap.
Turn on one option and it also saves **every store's offer** for each product,
with the **store's own product link**.

Try it with the prefilled search: open the Actor, click **Start**, and 40
wireless earbuds from US stores arrive in under a minute. Because it runs on
the Apify platform, you also get API access, scheduled runs, integrations
(Google Sheets, Make, Zapier, webhooks), run monitoring and Apify Proxy built
in, so there is nothing to set up to read
[Google Shopping](https://shopping.google.com).

### Why scrape Google Shopping?

- **Price monitoring**: track what your products and your competitors' cost
  across stores, every day, in each country you sell in.
- **Competitor and assortment research**: see which brands and stores Google
  shows for your category keywords, and at which prices.
- **Deal finding**: collect discounted products (old price, "% OFF" badge) or
  everything within a price range.
- **Market research**: compare prices, ratings and delivery terms for the same
  search in different countries and currencies.
- **Product catalog enrichment**: get brands, ratings, review counts and
  thumbnails for product names.

### How to scrape Google Shopping

1. Type your search words into **Search words or Google Shopping links**, one
   search per line (for example `wireless earbuds`).
2. Or search on Google Shopping and paste the address bar link instead; its
   country, language and filters are used as they are.
3. Set **Max products per search** (100 by default) and the **Country**.
4. Optionally set a **Minimum price** and **Maximum price**, or turn on
   **Include every store's offers**.
5. Click **Start**, then download the products when the run finishes.

### Input

Everything is set on the Input tab. In JSON, the input looks like this:

```json
{
    "queries": [
        "wireless earbuds",
        "https://www.google.de/search?q=kopfh%C3%B6rer&udm=28&hl=de&gl=de"
    ],
    "maxProductsPerQuery": 100,
    "countryCode": "us",
    "languageCode": "en",
    "minPrice": 20,
    "maxPrice": 150,
    "includeOffers": false
}
```

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `queries` | array of strings | (required) | Search words, or Google Shopping result links (with or without `https://`, `udm=28` or the older `tbm=shop`). API callers can also send `{"url": "..."}` objects. |
| `maxProductsPerQuery` | integer | 100 | Products to save per search, 1 to 500. |
| `countryCode` | string | `us` | Two-letter country whose stores, prices and currency you want, such as `gb`, `de`, `in` or `br` (68 countries; `uk` works too). |
| `languageCode` | string | `en` | Interface language (Google's `hl`), such as `de` or `pt-BR`. It changes the labels Google writes, such as delivery and returns. |
| `minPrice` | number | none | Only products at this price or more, in the country's currency. |
| `maxPrice` | number | none | Only products at this price or less, in the country's currency. |
| `includeOffers` | boolean | `false` | Also save up to 5 stores' offers per product, with each store's own link (one more Google request per product). |

A pasted link keeps its own country, language and filters. The input settings
fill in only what the link leaves out.

### Output

One row per product, in Google's order. A real row from the prefilled search:

```json
{
    "query": "wireless earbuds",
    "position": 3,
    "title": "soundcore P31i",
    "price": 34.99,
    "currency": "USD",
    "oldPrice": 50,
    "merchant": "soundcore US",
    "moreMerchants": true,
    "brand": "Soundcore",
    "rating": 4.8,
    "reviewCount": 1117,
    "ratingDrawSuspect": false,
    "delivery": "Free delivery",
    "returns": null,
    "badge": "30% OFF",
    "thumbnail": "https://encrypted-tbn1.gstatic.com/shopping?q=tbn:ANd9GcRl7vZkyy9eXzlVJGyFLScPRAK90BT7UqABaGow9wRYl3xmoTjwnfWJ0W6o9ZuswnyMcCB1p-Gwb1Cwt54Jy5nddtSXFPbM0WxHnX-T6A3uIcYrATP7Y9f7a9E",
    "productId": "9824472621292795091",
    "offerId": "18409718912619203230",
    "productLink": "https://www.google.com/search?q=wireless+earbuds&ibp=oshop&udm=28&hl=en&gl=us&prds=catalogid%3A9824472621292795091%2Cpvt%3Ahg",
    "country": "us",
    "searchUrl": "https://www.google.com/search?q=wireless+earbuds&udm=28&hl=en&gl=us",
    "scrapedAt": "2026-10-20T14:40:57.980817+00:00"
}
```

With **Include every store's offers** on, each row also has `merchantUrl` and
an `offers` list. A real offer from an `airpods pro 3` search:

```json
{
    "merchant": "Best Buy",
    "price": 228,
    "currency": "USD",
    "oldPrice": null,
    "title": "Apple - AirPods Pro 3, Wireless Active Noise Cancelling Earbuds with Heart Rate Sensing Feature - White",
    "merchantRating": null,
    "availability": "In stock online",
    "shipping": "Free delivery",
    "link": "https://www.bestbuy.com/product/apple-airpods-pro-3-wireless-active-noise-cancelling-earbuds-with-heart-rate-sensing-feature-white/JJGCQLYK5F/sku/12213138?ref=212&loc=marketplace"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or
Excel, or read it through the Apify API.

### Data fields

| Field | Type | Description |
| --- | --- | --- |
| `query` | string | The search words the product was found for. |
| `position` | integer | Rank of the product in the results for that search, from 1. |
| `title` | string | Product title as Google shows it. |
| `price` | number | Current full price, read in the country's number style (`1.299,00 €` is 1299). For a price range, the lowest price. Monthly instalments (`R$ 699,94/mês x 18`) are never used as the price. |
| `currency` | string | ISO currency code, such as `USD`, `EUR` or `INR`. |
| `oldPrice` | number or null | The crossed-out "was" or usual price (such as `Was $249`, `40% off ₹799`), when the product is discounted. |
| `merchant` | string or null | The store whose offer Google shows. Null when Google shows only "& more". |
| `moreMerchants` | boolean | True when other stores sell the product too ("& more"). |
| `brand` | string or null | Brand Google lists for the product. |
| `rating` | number or null | Star rating out of 5, as Google printed it on that page (approximate; see the FAQ). |
| `reviewCount` | integer or null | Number of reviews behind the rating. |
| `ratingDrawSuspect` | boolean | True when the page the row came from had 5 or more products rated exactly 5.0 on 100+ reviews, a sign that Google served a version whose ratings differ from a browser's. See the FAQ. |
| `delivery` | string or null | Delivery text, such as `Free delivery` or `Free delivery by Fri`. |
| `returns` | string or null | Returns text, such as `30-day returns`. |
| `badge` | string or null | Google's label on the card, such as `30% OFF`, `SALE` or `LOW PRICE`. |
| `thumbnail` | string | Product image served by Google. |
| `productId` | string | Google's catalog id of the product, or for a product sold by one store only, the id of that listing. |
| `offerId` | string or null | Google's id of the offer shown. |
| `productLink` | string or null | Google's product page with every store's price. Null for products sold by one store only, which have no such page. |
| `country` | string | Country of the search. |
| `searchUrl` | string | Google Shopping link for the search, to open it yourself. |
| `merchantUrl` | string or null | Offers option only: the store's own product page for the offer Google shows (or the cheapest offer). |
| `offers` | array | Offers option only: up to 5 store offers, each with `merchant`, `price`, `currency`, `oldPrice`, `title`, `merchantRating`, `availability`, `shipping` and the store's `link`. Empty for products sold by one store only. |
| `scrapedAt` | string | When the row was saved (ISO 8601, UTC). |

### How much does it cost to scrape Google Shopping?

You pay per product saved; see the **Pricing** tab for the exact price. With
**Include every store's offers** on, each product saved with offers is charged
one extra event, also listed on the Pricing tab. Searches that return nothing
and entries that are not searches are never charged. Each Google request
brings back about 40 products, so the platform usage stays small: a run of 240
products across two searches used about $0.02 of platform resources. The Actor
stops by itself when a run reaches the maximum cost you set for it.

### Tips for scraping Google Shopping

- **How many products can I get?** Google lists about 250 to 300 products for
  a broad search; later pages partly repeat earlier ones, and duplicates are
  removed, so `position` counts unique products.
- **Pick the country, not just the language.** Stores, prices and currency
  follow the country; the language only changes Google's own labels.
- **Price filters are checked**: Google sometimes ignores a price range, so
  such pages are asked again and products outside the range are dropped.
- **Offers take longer.** Each product's page is one more Google request
  (five are read at a time), so a run with offers on takes a few seconds more
  per product sold by several stores; only those products have offers.
- **Use several close searches** (`wireless earbuds`, `noise cancelling
  earbuds`) to cover a category.
- **Each Google page takes from a few seconds to about half a minute**;
  several searches are read at the same time.

### FAQ, disclaimers, and support

**Is it legal to scrape Google Shopping?** The Actor reads only the public
results Google shows to anyone without logging in: products, prices and store
names. It does not collect personal data. Web data scraping is legal in many
cases, but if you are unsure, consult a lawyer.

**Why did a search give nothing?** Google had no products for it, or Google
answered it without Shopping. Such a search is listed in the `FAILED_INPUTS`
record of the run's key-value store with the
reason, and nothing is charged for it. Countries where Google offers no
Shopping at all, such as Egypt and Kenya, are not accepted.

**Can I sort by price?** Not yet: Google's sort options are not applied to
results read this way. Use `minPrice` and `maxPrice` to narrow the range
instead.

**Are sponsored products included?** The products are those of Google
Shopping's results grid; Google shows no separate sponsored listings there
for these searches, so the Actor has no sponsored flag.

**Are the ratings reliable?** Treat them as approximate. Ratings are saved
exactly as Google printed them on the page the Actor read, but Google serves
different versions of the same page, and on some of them most ratings on the
page differ from what a browser shows (for example a product rated 4.1 in a
browser shows 2.2, or many products show exactly 5.0 on thousands of reviews).
Such pages are common for the requests this Actor makes. Rows from a page
with 5 or more products rated exactly 5.0 on 100+ reviews have
`ratingDrawSuspect: true`. If you need reliable ratings, check them in a
browser or re-run the search and compare.

**Why is `merchant` empty on some rows?** Google showed only "& more" on that
card. Turn on **Include every store's offers** to get the stores and their
prices.

Found a bug or need another field? Open an issue on the **Issues** tab and it
will be looked at. Need a custom scraper or a different output format? Get in
touch through the Issues tab too.

# Actor input Schema

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

One search per line. Type words as you would on Google Shopping (for example wireless earbuds), or paste the address of a Google Shopping results page: its search words, country, language and filters are used as they are. Entries that are not searches are listed in the FAILED_INPUTS record.

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

Stops after this many products for each search (1 to 500). Each Google page holds about 40 products, and Google lists about 250-300 for a broad search, so very high numbers return everything Google has.

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

Two-letter code of the country whose Google Shopping you want: its stores, prices and currency, for example us, gb, de, fr, in or br. Supported: us, gb, ca, au, nz, ie, de, at, ch, fr, be, nl, lu, es, pt, it, gr, fi, ee, lv, lt, sk, si, hr, cy, mt, se, no, dk, pl, cz, hu, ro, bg, rs, ua, tr, il, ae, sa, qa, kw, bh, om, jo, ma, za, ng, in, pk, bd, lk, jp, kr, tw, hk, sg, my, th, vn, ph, id, mx, br, ar, cl, co, pe. A country in a pasted link (Google's gl parameter) takes the place of this for that link. Google does not offer Shopping everywhere (not in Egypt or Kenya, for example); a search Google answers without Shopping is listed in FAILED_INPUTS.

## `languageCode` (type: `string`):

Interface language code (Google's hl parameter), for example en, de, fr or pt-BR. It changes the language of the labels Google writes itself, such as delivery and returns.

## `minPrice` (type: `number`):

Only products priced at least this much, in the country's currency. Leave empty for no lower limit.

## `maxPrice` (type: `number`):

Only products priced at most this much, in the country's currency. Leave empty for no upper limit.

## `includeOffers` (type: `boolean`):

Also open each product's Google page and save the offers of up to 5 stores selling it (store, price, availability, delivery and the store's own link), plus the store link of the offer shown in the results as merchantUrl. It takes one more Google request per product, so runs are slower and each product with offers is charged an extra event. Products sold by one store only have no such page.

## Actor input object example

```json
{
  "queries": [
    "wireless earbuds"
  ],
  "maxProductsPerQuery": 40,
  "countryCode": "us",
  "languageCode": "en",
  "includeOffers": false
}
```

# Actor output Schema

## `dataset` (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"
    ],
    "maxProductsPerQuery": 40
};

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

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

```

## MCP server setup

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