# Google Shopping Scraper — Prices, Sellers & Product Offers (`tortuga/google-shopping-scraper`) Actor

Scrape Google Shopping for any search and country: title, price, old price, store, rating, reviews, delivery, condition, deal badges, with optional all-store offers and direct store links.

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

## Pricing

from $2.50 / 1,000 product scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 — Prices, Sellers & Product Offers

Scrape **Google Shopping** results for any search term and country: product title, brand, price, old ("Was") price,
discount and deal badges, store name, whether more stores sell it, delivery and returns texts, condition, rating,
review count, image and the Google product page link. Optionally open each product's Google page to get **every
store's offer** (store, price, availability, delivery, returns, store rating) with the **direct link to the store**,
plus the typical price range, description and specifications.

Use it for price monitoring, competitor and MAP checks, product research, assortment and deal tracking.

### What data does it extract?

| Field | Example | Notes |
|---|---|---|
| `title` | Bose QuietComfort Wireless Noise Cancelling Earbuds | |
| `brand` | Bose | when Google knows it |
| `price` / `priceText` / `currency` | 129 / "$129.00" / USD | price as shown on the card (lowest or featured store) |
| `isPriceFrom` | false | true for "£24+" style prices |
| `wasPrice` / `savingsAmount` / `discountPercent` | 179 / 50 / 27 | crossed-out price and "27% OFF" badge |
| `usualPrice` | 49 | Google's "Usually $49" next to a "Low price" badge |
| `isOnSale` / `badges` | true / \["27% OFF"] | "27% OFF", "SALE", "Low price", "30-day low" ... |
| `sellerName` / `hasMoreStores` | Best Buy / true | store shown on the card; true when Google says "& more" |
| `shippingText` / `freeShipping` | "Free delivery by Thu" / true | |
| `returnsText` | "15-day returns" | |
| `condition` / `isUsed` | "Pre-owned" / true | Google labels only used, pre-owned and refurbished items; null = no label |
| `installmentText`, `foreignPriceText` | "MX$8.57 per month for 24 months", "$45" | where Google shows them |
| `rating` / `reviewCount` | 4.2 / 5052 | exact review count when Google provides it |
| `imageUrl` | https://encrypted-tbn0.gstatic.com/shopping?q=tbn:... | |
| `url` | https://www.google.com/search?ibp=oshop\&q=...\&prds=catalogid:... | Google product page (compare prices) |
| `productId`, `catalogId`, `offerId`, `gpcid`, `imageDocid` | 9675719922937365154 ... | Google's ids; `productId` is stable across searches and runs |
| `section`, `position`, `page`, `searchTerm`, `searchVariant`, `country`, `language`, `scrapedAt` | "Deals on Wireless Earbuds", 6, 1, ... | where the product appeared |

With **Include all store offers** on, products sold by several stores also get:

| Field | Example |
|---|---|
| `offers` | `[{"sellerName": "Skate Warehouse", "price": 99.99, "currency": "USD", "title": "Skullcandy x Bose Method 360 ANC Headphones Black", "availability": "In stock online", "sellerRating": 4.8, "shippingText": "Free delivery by Oct 20", "returnsText": "Free 60-day returns", "url": "https://www.skatewarehouse.com/...", "domain": "skatewarehouse.com", "merchantId": "1271790", "offerId": "13824700559495535735"}, ...]` |
| `offersCount`, `hasMoreOffers`, `lowestOfferPrice` | 4, true, 99.99 |
| `merchantUrl` | the store link of the store shown on the card |
| `typicalPriceRange` | "$92 to $130" |
| `description`, `specs` | product description; `{"Bluetooth Version": "5.3", "Driver Size": "12 mm", ...}` |

### How to scrape Google Shopping

1. Enter one or more **search terms** (e.g. `wireless earbuds`, `air fryer`, `Nike Pegasus 41`).
2. Pick the **country** (United States by default) and, if you like, a **sort order**, **price range**,
   **condition**, **on sale only** or **free shipping only**.
3. Set **Max products** and run. Download the results as JSON, CSV or Excel, or read them through the API.

Example input:

```json
{
  "queries": ["wireless earbuds"],
  "country": "us",
  "sortBy": "price_low",
  "minPrice": 30,
  "maxPrice": 60,
  "maxItems": 100
}
```

Example output (one product, abridged):

```json
{
  "productId": "9675719922937365154",
  "catalogId": "11042967417490666683",
  "title": "Bose QuietComfort Wireless Noise Cancelling Earbuds",
  "brand": "Bose",
  "price": 129,
  "priceText": "$129.00",
  "currency": "USD",
  "wasPrice": 179,
  "savingsAmount": 50,
  "discountPercent": 27,
  "isOnSale": true,
  "badges": ["27% OFF"],
  "sellerName": "Best Buy",
  "hasMoreStores": true,
  "shippingText": "Free delivery by Thu",
  "freeShipping": true,
  "returnsText": "15-day returns",
  "condition": null,
  "rating": 4.2,
  "reviewCount": 5052,
  "section": "Deals on Wireless Earbuds",
  "position": 6,
  "page": 1,
  "url": "https://www.google.com/search?ibp=oshop&q=Bose+QuietComfort+Wireless+Noise+Cancelling+Earbuds&prds=catalogid:11042967417490666683,...",
  "searchTerm": "wireless earbuds",
  "country": "us"
}
```

### How many products can I get per search?

Google shows about **40-50 products per results page** (the results grid plus, on the first page, a few shelves
such as "Deals on ..."). The actor pages through Google's results with `start=10, 20, ...` (the pages Google's own
"More results" link loads). In tests the pages of "wireless earbuds" added 50, 34, 25 and 31 new products
(140 unique from 4 pages) and Google answered the 11th page with no products. When a search runs dry and you have
not fixed the sort order or price range yourself, the actor continues with Google's other sort orders (price low /
high, rating) and Google's own price bands for that search, removing duplicates. Expect **roughly 100-250 unique
products per broad search term**, fewer for narrow ones; use several search terms for bigger catalogs.
**Max Google pages per search term** (default 5) caps the cost; the actor stops early when pages stop adding new
products.

### How much does it cost?

You pay per product saved (see the Pricing tab) plus Apify platform usage: the Google SERP proxy (about $0.0025
per Google page of 40-50 products, i.e. roughly $0.06-0.10 per 1,000 products) and a little compute. With
**Include all store offers** each product page is one more SERP proxy request (about $0.0025) and the
`product-offers` event; products listed by a single store are skipped (their store is already on the card).
With a price range set, the first page of a search can be requested a second time with the search's category id
(Google's price filter needs it), i.e. one extra SERP request per search.

### Does it need a Google account or a browser?

No. It reads the public Google Shopping pages over plain HTTP through Apify's **GOOGLE_SERP proxy** (Google
answers ordinary datacenter and residential requests with a JavaScript wall, so the proxy group is required and
the actor always uses it). No login, no cookies, no headless browser. The store links are read from Google's
redirect header; the stores' own pages are never opened.

### Can I scrape other countries and currencies?

Yes: pick the country (United States, United Kingdom, Canada, Australia, Germany, France, India, Spain, Italy,
Netherlands, Brazil, Mexico, Poland and more). The actor searches that country's Google domain and pins the
location, so prices, stores and currency are those of that market; answers Google localised to another country
are detected and retried. Prices are parsed for local formats (`1.269,00 PLN`, `MX$1,736.29`, `£24+`).

### Monitor changes on a schedule

Turn on **Only new or changed items** and save the input as a task with a Schedule (e.g. daily). The actor
remembers which products earlier runs of the same search returned and outputs only **new products** and products
whose **price changed**; unchanged products are not saved and not charged. Each output product gets `changeType`
(`new` or `changed`) and, for changed ones, `previousValues` with the old price. Use **Monitoring key** to keep
several schedules on the same search separate.

Google reshuffles Shopping results between fetches, so "new" means new to this watch, not newly listed: in our
test, a second run of a 40-product search 15 minutes later returned 30 products the first run had not seen. The
listed price is the store Google shows first, so a "changed" price can also mean Google picked a different store.
To track specific products closely, turn on **Include all store offers** and compare all stores.

### Is it legal to scrape Google Shopping?

The actor collects publicly visible product listings (product titles, prices, store names, ratings) that Google
shows to anyone without logging in. It does not collect personal data. Check Google's terms and the laws that
apply to you (e.g. on database rights) for your use case; use the data responsibly.

### Limits

- Google Shopping's "from N stores" count is not on the results page any more; `hasMoreStores` tells you whether
  more stores sell the product, and **Include all store offers** lists them (Google server-renders the first
  \~2-10 offers; `hasMoreOffers` = true when it hides more behind "More stores").
- Store links (`merchantUrl`, `offers[].url`) come only with **Include all store offers**; the results page itself
  carries no store links. Products listed by a single store are not opened (no extra offers to get).
- Filters are Google's own (the same tokens and query words its filter chips use). Sort orders, on sale and free
  shipping were applied by Google in every test. Google applied the **price range** for some searches ("wireless
  earbuds") but ignored it for others ("robot vacuum"); the actor therefore re-checks price range, condition,
  on sale and free shipping on every card and drops products that do not match (they are not charged), which can
  mean fewer products per page for price-filtered searches. SUMMARY `notes.priceFilter.pagesIgnoringIt` counts it.
- About 1 in 8 Google answers come back localised to another country even with the location pinned; the actor
  detects them and retries (each retry is another SERP proxy request).

# Actor input Schema

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

One Google Shopping search per term, as you would type it on Google, e.g. "wireless earbuds", "air fryer", "Nike Pegasus 41".

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

Which country's Google Shopping is searched: stores, prices and currency of that market (google.com for the US, google.co.uk, google.de, ...).

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

Google interface language code, e.g. en, de, fr, es. Labels such as delivery and returns texts come in this language.

## `sortBy` (type: `string`):

Google Shopping's own sort order.

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

Only products at or above this price, in the country's currency. Sent to Google as its own price filter (Google applies it for many searches but not all); products outside the range are always dropped before saving and never charged.

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

Only products at or below this price, in the country's currency.

## `condition` (type: `string`):

New: drops products Google labels pre-owned, used or refurbished. Used: searches Google's used listings and keeps only products labelled pre-owned, used or refurbished.

## `onSale` (type: `boolean`):

Only products with a deal: a crossed-out 'Was' price, a discount badge or Google's 'Low price' badge.

## `freeShipping` (type: `boolean`):

Only products whose card says free delivery / free shipping.

## `maxItems` (type: `integer`):

Stop after this many products in total (you pay per product saved).

## `maxPagesPerQuery` (type: `integer`):

Google shows about 40-50 products per page. The actor pages through results (start=10, 20, ...) and, when Google runs out, through its other sort orders and price bands, removing duplicates. Each page is one Google SERP proxy request (about $0.0025, billed by Apify as proxy usage). It stops early when pages stop adding new products.

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

Also open Google's product page for each product sold by several stores: every store's offer (store, price, availability, delivery, returns, store rating, link to the store), the typical price range, description and specifications. One extra Google SERP proxy request per product (about $0.0025) plus the 'product-offers' event.

## `resolveStoreLinks` (type: `boolean`):

With store offers on: turn Google's offer links (google.com/goto redirects) into the store's own product URL. Reads the redirect only; the store's page is not opened.

## `startUrls` (type: `array`):

Optional: Google Shopping search URLs, e.g. https://www.google.com/search?q=air+fryer\&udm=28. The q, gl and hl parameters and Google's filter token (shoprs) are reused.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this search returned and output only new items or items whose price/status changed. Use with a Schedule; unchanged items are not charged. For Google Shopping: new products and products whose price changed; Max products is then the number of products checked per run.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input

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

Google Shopping loads only through Apify's GOOGLE_SERP proxy group (Google blocks plain datacenter and residential requests with a JavaScript wall). The actor always uses it. Apify bills it as proxy usage, about $0.0025 per Google page (40-50 products) and per product page.

## Actor input object example

```json
{
  "queries": [
    "wireless earbuds"
  ],
  "country": "us",
  "language": "en",
  "sortBy": "relevance",
  "condition": "any",
  "onSale": false,
  "freeShipping": false,
  "maxItems": 100,
  "maxPagesPerQuery": 5,
  "includeOffers": false,
  "resolveStoreLinks": true,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}
```

# 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"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "GOOGLE_SERP"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/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"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["GOOGLE_SERP"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/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"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}' |
apify call tortuga/google-shopping-scraper --silent --output-dataset

```

## MCP server setup

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