# Google Shopping Scraper — Prices, Merchants & Offers (`architekture/google-shopping-scraper`) Actor

Google Shopping scraper by keyword and country: title, price, sale price, merchant, rating, shipping and image per product; offers mode returns up to 5 merchant offers per product with price, delivery, total and link. Watch mode delivers only new or changed rows. Pay per result, no login.

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

## Pricing

from $3.00 / 1,000 delivered 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

## Google Shopping Scraper — Prices, Merchants & Offers

**Recorded run: 9 products with 24 merchant offers from 10 product IDs in 301 seconds** (2026-10-23, build 0.1.7, run `mwQKGPC56O2U3wiyK`; the 10th product had no offer and was free). Merchant offers cost $12 per 1,000 products here; the one other Google Shopping offers actor in the Store charges $18 per 1,000 on its Bronze tier ($15 to $20 by plan), and the field leader has no offers mode. Keyword search costs $3.00 per 1,000 products, against $3.50 and $5.00 at the two most-used keyword scrapers. Every validation run on 2026-10-23 charged exactly the rows it delivered.

architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.

Google Shopping in two modes. **Search**: give it up to 50 search terms and a country, and get each product card Google shows, with title, price and currency, sale price, merchant, rating, review count, shipping text, Google product ID and image. **Offers**: give it up to 100 Google product IDs (the `offersId` of a search row) and get one row per product with its top merchant offers: merchant, price, old price, delivery text, delivery cost, total, stock and the offer link.

| Source | Market | From | Standout fields |
|---|---|---|---|
| Google Shopping search and product pages (google.<country>) | 24 countries; US and GB validated | $3.00 / 1,000 products; $12 / 1,000 offers rows | `priceValue` + `currency`, `merchants[].total`, `bestTotalPrice`, `totalOffersKnown` |

### ✅ Why choose this scraper

- **Merchant offers per product, at $12 per 1,000.** Offers mode returns the merchants Google shows on a product's page, each with `price`, `oldPrice`, `delivery`, `shippingCost`, `total`, `inStock`, merchant `rating` and its `url`, plus the product's `bestPrice`, `bestTotalPrice` and `totalOffersKnown`. Run `mwQKGPC56O2U3wiyK`: 9 of 10 products delivered with 24 offers; 23 of 24 offers had a delivery text and a computed total.
- **Search feeds offers directly.** Every search row carries `offersId`, which offers mode takes as-is: search for "laundry detergent", then pass the IDs to get the merchants for each product.
- **Charges equal delivered rows, measured.** 10 validation runs on build 0.1.7: 120 product rows and 21 offers rows delivered, 120 `product` and 21 `offers` charges. Status rows, products with no offer, GTIN misses and unchanged watch rows were free.
- **Localized to the country you pick.** `country` sets the Google domain, `gl` and the proxy country. Pages priced in another market's currency, and product pages that show no offer, are fetched again and never delivered.
- **Price watch that charges only for changes.** With `incrementalMode` and a `stateKey`, search delivers only new or re-priced products, and offers delivers only products whose offer set or best total price changed: the rerun `QMqAcZreUkfkXgYWv` charged 1 of 9 products and skipped 8 unchanged ones for free.
- **Filters that never cost you.** In search mode, `minPrice`, `maxPrice`, `condition`, `freeShipping` and `onSale` drop cards before delivery; dropped cards and nonsense queries are never charged.

### ⚠️ Worth knowing up front

- **Offers mode returns up to 5 merchant offers per product**, the ones Google's product page shows before "More stores" (validation: 3 offers on 7 of 9 products, 1 and 2 where Google knew only 1 and 2). `totalOffersKnown` is Google's own count of all offers (up to 378 in validation), not what is delivered. The remaining stores load through a session-bound request this route cannot make.
- **GTIN lookup is best-effort.** Google has no exact barcode lookup: each `gtins` item is searched and counts only when a result's title carries at least 60% of the words in the title you give. Recorded hit rate: 3 of 10 grocery GTINs on the wall test, 2 of 10 on validation (runs `fH2QJgzvav1Pxmh4T`, `pOwrC9XWNu6uZ4GjL`); 2 bogus GTINs were free misses. Every miss is a free `NO_RESULTS` row. Prefer `productIds` from search rows when you can.
- **Condition is not a stable field yet.** No live row has shown an item condition, so it is left out of the schema table until one does; the search `condition` filter works on card labels such as used, pre-owned or refurbished.
- Search returns the cards on Google's first results page, in its order: up to 40 per query. Page 2 came back empty in the US and GB, so `maxResultsPerQuery` stops at 40.
- `offersId` is `null` on search cards that Google shows as a single merchant listing without a catalog product (about half of the cards on a recorded US page); those cards have no comparison page.
- No product page link: Google's search cards carry IDs only. Offers carry Google's `/goto` redirect link to the merchant's offer. `merchantDomain` is always `null` in search rows.
- `shippingCost` is `0` for free delivery and the amount for a flat delivery price; it is `null` for conditional texts such as "2 hour delivery on orders $10+", and then `total` is `null` too. `bestTotalPrice` is the lowest known `total`.
- Pacing measured on 2026-10-23, two requests at a time: 10 US search queries in 106 seconds; 10 offers products in 301 to 344 seconds (product pages are about 1 MB and the SERP proxy takes up to about a minute on some). Each page is fetched with a 90-second timeout and retried once. A product page from another market can show no offer; it is fetched up to 3 times before a free `NO_RESULTS`.
- All search filters are applied by the actor to the card values: Google ignored the price filter sent to it when tested on 2026-10-22. `freeShipping` reads English labels; `language` other than `en` is accepted but was not validated.
- A run stops cleanly 3 minutes before its deadline (`PARTIAL`, `stopReason` `ACTOR_DEADLINE`), at your spending limit (`BUDGET_LIMIT`), and after two blocked items in a row (`PARTIAL`). The deadline is the run timeout, capped at 6 hours. Rerun with the same `stateKey` to continue.
- Invalid input (`INVALID_INPUT`), a `stateKey` still held by another run (`STATE_BUSY`), no matching products or offers (`NO_RESULTS`), a blocked, failed or other-market page (`BLOCKED`, `SOURCE_ERROR`, `SOURCE_CHANGED`), the deadline (`ACTOR_DEADLINE`) and the spending limit (`BUDGET_LIMIT`) each write a free status row (`"recordType": "status"`). An unconfirmed delivery (`RECONCILIATION_REQUIRED`) or a storage fault fails the run and replays nothing.

### ⚡ Try the example

This actor has no free preview of its own; the example below costs $0.036 (3 products with their offers).

Open the actor's input page, switch to JSON, paste this and start the run:

```json
{
  "mode": "offers",
  "country": "us",
  "productIds": ["catalogid:3426141390508526362", "catalogid:14697744329736728216", "catalogid:9908745006175137581"]
}
```

For keyword search use `{"queries": ["sony wh-1000xm5"], "country": "us", "maxResultsPerQuery": 10}` ($0.03), then pass the `offersId` values of the rows you want into `productIds`.

### ⚙️ Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `search` | `search`: product cards by keyword ($0.003 per product). `offers`: merchant offers per product ($0.012 per product). |
| `queries` | array | — (prefill `["sony wh-1000xm5"]`) | Search mode. 1–50 product searches, as typed into Google Shopping. Duplicates are removed. Ignored in offers mode. |
| `productIds` | array | — | Offers mode. Up to 100 IDs: a search row's `offersId` (`catalogid:<id>` or `gpcid:<id>`), or a search row's `productId` digits (read as a catalog ID). |
| `gtins` | array | — | Offers mode. Up to 100 objects `{"gtin": "...", "title": "..."}`; the title is required for the match check. Best-effort, misses are free. |
| `country` | select | `us` | `us`, `gb`, `ca`, `au`, `nz`, `ie`, `de`, `fr`, `es`, `it`, `nl`, `be`, `at`, `ch`, `se`, `no`, `dk`, `pl`, `in`, `jp`, `br`, `mx`, `sg`, `za`. Sets the Google domain, `gl`, the proxy country and the expected currency. |
| `language` | string | `en` | Google `hl` code, for example `en`, `en-GB`, `de`. Validated with `en`. |
| `maxResultsPerQuery` | integer | `40` (prefill `10`) | Search mode. 1–40 products per query. |
| `minPrice` | number | — | Search mode. Keep cards priced at least this much, in the country's currency. |
| `maxPrice` | number | — | Search mode. Keep cards priced at most this much, in the country's currency. |
| `condition` | select | `any` | Search mode. `any`; `new` drops cards labelled used, pre-owned or refurbished; `used` keeps only those. |
| `freeShipping` | boolean | `false` | Search mode. Keep cards whose shipping text says free. |
| `onSale` | boolean | `false` | Search mode. Keep cards with a was-price or a percent-off badge. |
| `incrementalMode` | boolean | `false` | Needs `stateKey`. Search: only products new or re-priced since the last run (identity: query, country, `productId`). Offers: only products whose offer set or best total price changed (identity: country, `offersId`, `gtin`). |
| `stateKey` | string | — | Any name up to 100 characters. Keeps the watch baseline and lets an interrupted run resume. One key per set of inputs. |
| `feedback` | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |

The run's spending limit (`maxTotalChargeUsd`, set in the run options) caps how many rows are delivered.

### 🧱 Stable fields

These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.

**Search rows** (`recordType` `product`):

| Field | Type | Meaning |
|---|---|---|
| `query` | string | The search term as run (whitespace collapsed). |
| `country` | string | The requested country code, for example `us`. |
| `position` | number | Rank of the product on Google's page for this query (1 = first), before your filters. |
| `title` | string | Product title as Google shows it. |
| `price` | string | Displayed price, for example `$254.99` or `£290+`. |
| `currency` | string | ISO currency of `price`, for example `USD`. |
| `priceValue` | number | `price` as a number; the lowest price for `+` ("from") prices. |
| `originalPrice` | number, nullable | The was-price Google shows next to a sale price. |
| `onSale` | boolean | `true` when the card shows a was-price or a percent-off badge. |
| `merchant` | string, nullable | Merchant name on the card; `null` on multi-seller "from" cards. |
| `merchantDomain` | string, nullable | Always `null` in this version (cards carry no domain). |
| `rating` | number, nullable | Product rating out of 5. |
| `reviewCount` | number, nullable | Displayed review count (`15K` = 15000). |
| `shippingText` | string, nullable | Shipping text, for example `Free delivery`. |
| `productId` | string | Google's catalog ID for the product (`data-cid`); the group or offer ID when the card has none. |
| `offersId` | string, nullable | The value offers mode takes in `productIds` (`catalogid:<id>` or `gpcid:<id>`); `null` on single-merchant cards. |
| `imageUrl` | string, nullable | Google's product thumbnail (`encrypted-tbn*.gstatic.com`). |
| `scrapedAt` | string | Collection time, UTC ISO 8601. |

**Offers rows** (`recordType` `offers`):

| Field | Type | Meaning |
|---|---|---|
| `gtin` | string, nullable | The GTIN you gave, on rows that came from `gtins`. |
| `productId` | string | Google's catalog or group ID (the digits of `offersId`). |
| `offersId` | string | `catalogid:<id>` or `gpcid:<id>`, as requested or as matched for a GTIN. |
| `title` | string, nullable | Product title on Google's product page. |
| `country` | string | The requested country code. |
| `offerCount` | number | Offers delivered in `merchants` (1–5). |
| `totalOffersKnown` | number, nullable | Google's count of all offers for the product. |
| `bestPrice` | number, nullable | Lowest offer `price`. |
| `bestTotalPrice` | number, nullable | Lowest known offer `total` (price plus delivery). |
| `currency` | string | The country's currency, for example `USD`. |
| `merchants` | array | One object per offer: `merchant`, `merchantId`, `offerId`, `offerTitle`, `price`, `priceText`, `currency`, `oldPrice`, `delivery`, `shippingCost`, `total`, `inStock`, `rating` (the merchant's, out of 5), `returns`, `url` (Google's `/goto` link). Any of them can be `null`. |
| `scrapedAt` | string | Collection time, UTC ISO 8601. |

`recordType` is `product`, `offers` or `status`. Data rows also carry `deliveryId` (unique per delivered row) and, in watch mode, `changeType` (`new`, `priceChanged` or `offersChanged`). Status rows carry `recordType`, `mode`, `inputRef`, `status` and `message`. Other fields are extras, not part of the contract. To have one pinned, say so in `feedback`.

### 📦 Output example

Abridged real offers row from run `mwQKGPC56O2U3wiyK` on 2026-10-23 (2 of its 3 merchants shown, links shortened); values are a historical snapshot.

```json
{
  "recordType": "offers",
  "gtin": null,
  "productId": "3745473023353098900",
  "offersId": "catalogid:3745473023353098900",
  "title": "Persil Liquid Laundry Detergent Original",
  "country": "us",
  "offerCount": 3,
  "totalOffersKnown": 377,
  "bestPrice": 12.97,
  "bestTotalPrice": 18.98,
  "currency": "USD",
  "merchants": [
    {"merchant": "Walmart", "merchantId": "8175035", "offerId": "8466545222292388478", "offerTitle": "Persil Deep Clean Liquid Laundry Detergent, Original, 84 fl oz, 64 loads", "price": 12.97, "priceText": "$12.97", "currency": "USD", "oldPrice": null, "delivery": "Delivery between Monday - Tuesday $6.99", "shippingCost": 6.99, "total": 19.96, "inStock": true, "rating": 4.3, "returns": "Free 90-day returns", "url": "https://www.google.com/goto?url=CAEStwEB6zsw..."},
    {"merchant": "Target", "merchantId": "10046", "offerId": "14342441942682820700", "offerTitle": "Persil Original HE Deep Cleaning Concentrated Liquid Laundry Detergent 64 Loads - 84 fl oz", "price": 12.99, "priceText": "$12.99", "currency": "USD", "oldPrice": null, "delivery": "Delivery between Monday - Thursday $5.99", "shippingCost": 5.99, "total": 18.98, "inStock": true, "rating": 4.5, "returns": "90-day returns", "url": "https://www.google.com/goto?url=CAESvQIB6zsw..."}
  ],
  "scrapedAt": "2026-10-23T14:23:32.140676+00:00",
  "changeType": "new",
  "deliveryId": "6d9bc6ec8004b1732d3f9beca972ab8a-0"
}
```

A search row (run `mlaXxQhmpNvpT1go5`, 2026-10-23, abridged; `offersId` added in 0.1.7):

```json
{"recordType": "product", "query": "sony wh-1000xm5", "country": "us", "position": 1, "title": "Sony WH-1000XM5 Wireless Noise Canceling Headphones", "price": "$254.99", "currency": "USD", "priceValue": 254.99, "originalPrice": 398, "onSale": true, "merchant": "Best Buy", "rating": 4.6, "reviewCount": 15000, "shippingText": "Free delivery", "productId": "17755277695162489284", "offersId": "catalogid:17755277695162489284", "scrapedAt": "2026-10-23T00:47:21.738123+00:00"}
```

A free status row (run `fH2QJgzvav1Pxmh4T`, a GTIN with no matching product):

```json
{"recordType": "status", "mode": "offers", "inputRef": "gtin:0000000000000", "status": "NO_RESULTS", "message": "No Google Shopping product for GTIN 0000000000000 matched the title (best match 0.00 < 0.6); nothing was charged"}
```

The run's status message reads `Done: N rows, 0 details, K status rows (STATUS)`, and the `RUN_SUMMARY` record of its key-value store holds the outcome (abridged, the offers run above):

```json
{"status": "COMPLETE", "delivered": 9, "charged": {"product": 0, "offers": 9}, "chargeUsd": "0.108", "statusRows": 1, "statusReasons": {"NO_RESULTS": 1}, "queriesFetched": 10, "pages": 12, "serpRequests": 12, "checkpointComplete": true}
```

### 🧠 Advanced usage

**From keyword to merchants.** Run search for your keywords, keep the rows you care about, and pass their `offersId` values as `productIds` in an offers run. Rows with `offersId` `null` are single-merchant listings: the search row already is the offer.

**Daily offers watch.** Schedule an offers run with `"incrementalMode": true` and `"stateKey": "us-offers"`. The first run sets the baseline; later runs deliver (and charge) only products whose merchants, offer prices or best total price changed, with `changeType` `offersChanged`.

**Daily price watch for a product list.** Schedule search with `"incrementalMode": true`, `"stateKey": "us-watch"`. Later runs deliver only rows with `changeType` `new` or `priceChanged`. Google reorders its top cards between days, so a product moving into your `maxResultsPerQuery` window counts as new.

**Compare two markets.** Run the same `queries` with `country` `us` and `gb` (one run each) and compare `priceValue` per `currency`.

**Resume an interrupted collection.** With a `stateKey`, a run that stops at the deadline, the spending limit or a forced stop keeps its progress; start the same input again with the same key. Rows already delivered are not delivered or charged again (offers run `jpUHL2YiCf3AQm7cM` was force-stopped after 3 rows; the resume `u7LnQ7QoUQOKo7TVT` delivered the other 6 with no overlap). After a forced stop, the key is free again once the stopped run's timeout plus 60 seconds has passed (`STATE_BUSY` before that).

### 🔌 Integrations

Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. A daily offers watch with `incrementalMode` turns a product list into a feed of merchant and price changes. Copy-paste code in Python, Node, curl and n8n: [architek-earth/examples/google-shopping-scraper](https://github.com/architek-earth/examples/tree/main/google-shopping-scraper).

### 💸 Pricing

Pay per result. Platform and proxy usage are included. Status rows, failed requests, products with no offer and unmatched GTINs are free. Each row is charged once, after it is written to the dataset; a resumed run continues after the last confirmed batch, a batch whose charge was left unconfirmed by a crash is not charged again (counted in `RUN_SUMMARY.recoveredUncertainCharges`), and unchanged rows in watch mode are skipped without a charge.

| Event | Price | What it covers |
|---|---|---|
| `product` | $3.00 / 1,000 products | One delivered search row (one Google Shopping product card). |
| `offers` | $12.00 / 1,000 products | One delivered offers row: one product with up to 5 merchant offers. |

1,000 search products cost $3.00; offers for 100 products cost $1.20; the 3-product offers example above costs $0.036.

### ❓ FAQ

**Is there a Google Shopping API?** This actor works as one: it returns Google Shopping search results and per-product merchant offers as JSON through the Apify API, callable from any language or from n8n, Make and Zapier.

**Do I need a Google account, cookies or my own proxy?** No. Requests go through Apify's Google SERP proxy for the chosen country; no login, cookies or Google API key.

**Why only up to 5 offers when Google says 300 stores?** Google's product page renders its top offers (up to 5) on the page; the rest load through a request bound to a browser session. `totalOffersKnown` tells you how many Google knows.

**Can I look up a product by GTIN, EAN or UPC?** Best-effort only. Google does not match barcodes exactly, so the actor searches the GTIN and accepts a result only when its title matches the title you give. 3 of 10 grocery GTINs matched in the wall test and 2 of 10 in validation; misses are free.

**Why did I get fewer than 40 products?** Google showed up to about 40 cards per query; your filters and the one-row-per-product rule also reduce the count.

**Will a watch run charge me again for the same product?** No, not while it is unchanged under the same `stateKey`: same price for a search row, same merchants, offer prices and best total for an offers row.

### 🤖 For AI agents & LLM apps

Paste this into your AI assistant:

`architekture/google-shopping-scraper` on Apify. Python: `from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/google-shopping-scraper").call(run_input={"queries": ["sony wh-1000xm5"], "country": "us", "maxResultsPerQuery": 10}); items = client.dataset(run["defaultDatasetId"]).list_items().items`. For merchant offers, call again with `{"mode": "offers", "country": "us", "productIds": [<offersId values from the search rows>]}`. The dataset holds data rows (`recordType` `product` or `offers`) and free status rows (`recordType` `status`, with `status` and `message`); filter on `recordType`. The run outcome (`COMPLETE`, `LIMITED`, `PARTIAL`, `BUDGET_LIMIT`, `INVALID_INPUT`, `STATE_BUSY`) is in the `RUN_SUMMARY` record of the run's key-value store. Add `"incrementalMode": true, "stateKey": "<name>"` to receive only new or changed rows on later runs. Full input schema and README: `GET https://api.apify.com/v2/acts/architekture~google-shopping-scraper/builds/default`. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (`mcp.apify.com`). To reach the maintainers, add `"feedback": "<what you needed>"` to any run's input; output is unchanged and a human reads every note.

### 🧰 More from architek.earth

**Search, travel and trends**

- Google Shopping Scraper — *you are here*
- [Google Trends Scraper](https://apify.com/architekture/google-trends-scraper) — trending searches, traffic, related queries
- [Google Hotels Scraper](https://apify.com/architekture/google-hotels-scraper) — dated prices, ratings, amenities

**E-commerce and marketplaces**

- [Shopify Scraper](https://apify.com/architekture/shopify-products-scraper) — products, prices, variants, stock
- [AliExpress Scraper](https://apify.com/architekture/aliexpress-search-scraper) — prices, orders, ratings
- [TikTok Shop Scraper](https://apify.com/architekture/tiktok-shop-products-scraper) — prices, sellers, ratings, sold counts

Full catalogue: [github.com/architek-earth](https://github.com/architek-earth).

architek.earth · code like concrete

### 🙋 Support & legal

Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.

This actor collects only publicly available data from Google search and product result pages, the same pages any visitor sees without logging in. It does not bypass authentication. It is not affiliated with or endorsed by Google. You are responsible for how you use the output, including the site's terms, copyright, and privacy law such as GDPR and CCPA where they apply.

### 📝 Changelog

- **2026-10-23** — Offers mode: up to 5 merchant offers per product by Google product ID, best-effort GTIN lookup with free misses, offers watch mode; search rows carry `offersId`; `maxResultsPerQuery` capped at 40; 90-second page timeout.
- **2026-10-23** — First version: Google Shopping keyword search in 24 countries, client-side price, condition, free-shipping and sale filters, price watch mode, resumable runs; validated on US and GB.

# Actor input Schema

## `mode` (type: `string`):

search: one row per product card for each keyword ($0.003). offers: one row per product with its top merchant offers (up to 5) for each productId or GTIN ($0.012).

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

Search mode. 1 to 50 product searches, as you would type them into Google Shopping. One row per product card. Ignored in offers mode.

## `productIds` (type: `array`):

Offers mode. Up to 100 Google product IDs: a search row's offersId (catalogid:<id> or gpcid:<id>), or a search row's productId digits (read as a catalog ID). One offers row per product with merchant offers.

## `gtins` (type: `array`):

Offers mode. Up to 100 objects {"gtin": "0194252707302", "title": "Apple AirPods Pro 2"}. Google has no exact barcode lookup: the GTIN is searched and a product counts only when its Google title carries at least 60% of your title's words. A miss is a free NO_RESULTS row.

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

Google's localized Shopping surface for this country (its Google domain and gl). Prices come in that country's currency.

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

Google interface language, e.g. en, en-GB, de. Validated with en; free-shipping filtering reads English labels.

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

Search mode. 1 to 40: Google's first results page carries up to 40 cards (page 2 came back empty in the US and GB). Each product row costs $0.003.

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

Applied by the actor to the card price, in the country's currency. Filtered-out cards are never charged.

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

Applied by the actor to the card price, in the country's currency. Filtered-out cards are never charged.

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

Applied by the actor to Google's condition label (Used, Pre-owned, Refurbished).

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

Keep cards whose shipping text says free (e.g. Free delivery). Applied by the actor.

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

Keep cards that show a was-price or a percent-off badge. Applied by the actor.

## `incrementalMode` (type: `boolean`):

Requires stateKey. Delivers (and charges) only products that are new or whose price changed since the last run with this stateKey.

## `stateKey` (type: `string`):

Any name. Keeps watch-mode memory and lets an interrupted run resume without re-charging. Use a new key for new queries or filters.

## `feedback` (type: `string`):

Tell us what to improve. Logged for human review; never changes output.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "sony wh-1000xm5"
  ],
  "country": "us",
  "language": "en",
  "maxResultsPerQuery": 10,
  "condition": "any",
  "freeShipping": false,
  "onSale": false,
  "incrementalMode": false,
  "stateKey": "",
  "feedback": ""
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (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": [
        "sony wh-1000xm5"
    ],
    "maxResultsPerQuery": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("architekture/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": ["sony wh-1000xm5"],
    "maxResultsPerQuery": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("architekture/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": [
    "sony wh-1000xm5"
  ],
  "maxResultsPerQuery": 10
}' |
apify call architekture/google-shopping-scraper --silent --output-dataset

```

## MCP server setup

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