# Google Ads Scraper (`surefind/google-ads-scraper`) Actor

Scrapes the Google Product Listing Ad (PLA) carousel for a list of search terms and filters the products by store name.

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

## Pricing

from $8.00 / 1,000 search-completeds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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 Products (PLA) Scraper

**Scrape the Google Product Listing Ad (PLA) carousel** — the row of shopping ads at the top of Google search results — for any list of search terms, and filter the products down to the **stores you care about**. For each matching product you get the product name, the store, and the destination store link, delivered as structured JSON you can download as JSON, CSV, or Excel or pull straight from the [Apify API](https://docs.apify.com/api/v2).

This Actor is a headless, cloud-native port of a standalone Puppeteer project. It uses [`puppeteer-real-browser`](https://www.npmjs.com/package/puppeteer-real-browser) with a real (headful, under a virtual display) Chrome to get past Google's bot checks, and runs on the Apify platform so you get scheduling, proxy rotation, monitoring, and API access for free.

### Why use it?

- **Competitive shopping research** — see which products a given store is running as shopping ads across many keywords.
- **Price and assortment monitoring** — track how a store's PLA presence changes over time by scheduling repeat runs.
- **Store-filtered by design** — instead of a flood of every advertiser, you get only the stores you list in `storeNames`.

### How to use it

1. Open the Actor in Apify Console and go to the **Input** tab.
2. Enter your **Search queries** as a comma-separated list, e.g. `wireless earbuds, running shoes`.
3. Optionally set **Store names to keep** to the stores you care about (e.g. `Best Buy`, `Dyson`). Matching is a case-insensitive substring, so `dyson` matches `Dyson Official`. Leave empty to keep every product.
4. (Recommended) Set **Proxy configuration** to **Residential** — Google blocks datacenter IPs.
5. Optionally set a **Region**, and tune **Minimum successful queries** (set it low, e.g. `2`, for a quick test).
6. Click **Start**. Watch results stream into the **Dataset** / Output tab.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `searchQueries` | string | ✅ | — | Comma-separated search terms. One Google search per term. Empties are dropped; if nothing remains the run fails. High commercial-intent terms ("buy ...") are far more likely to show shopping ads. |
| `storeNames` | string[] | | `[]` | Case-insensitive substring filter against the store name on each card (e.g. `Best Buy`). Empty = keep every product. |
| `region` | string | | (none) | Two-letter country code. Sets the `gl` search param, the proxy country, and `Accept-Language` in sync. |
| `retryLimit` | integer | | `3` | Retries per query after a block. Each retry relaunches the browser with a fresh proxy. |
| `retryDelayMs` | integer | | `1000` | Delay (ms) before each retry. |
| `minSuccessfulQueries` | integer | | `100` | The Actor keeps cycling the query list until this many queries complete cleanly. |
| `maxItems` | integer | | `100` | **Cost cap.** Maximum products pushed to the dataset. The run stops as soon as this many rows are collected (or once `minSuccessfulQueries` clean queries complete — whichever comes first). |
| `proxyConfiguration` | object | | `{ "useApifyProxy": false }` | Proxy settings. **Residential strongly recommended.** |

#### Looping behavior

Unlike a one-pass scraper, this Actor **repeats** your `searchQueries` — wrapping back to the start of the list — until it has accumulated `minSuccessfulQueries` clean completions. A page that loads cleanly but matches zero products **still counts as a success**. A query that exhausts all its retries is skipped for now and may be retried on a later cycle. Set `minSuccessfulQueries` low for testing (e.g. `2`) and higher for production sweeps.

> **Thin proxy pools:** for regions with few residential proxies available, raise `retryLimit` so a run can push through transient blocks.

### Output

Each matched product is pushed to the dataset as one flat object:

```json
{
  "searchQuery": "buy dyson v15 vacuum",
  "productName": "Dyson V15 Detect",
  "price": "$849.99",
  "storeName": "Dyson Official",
  "storeLink": "https://www.google.com/aclk?sa=L&ai=..."
}
````

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Data fields

| Field | Description |
|---|---|
| `searchQuery` | The search term this product was found under. |
| `productName` | The product title from the shopping ad card. |
| `price` | The displayed price, e.g. `$849.99`. |
| `storeName` | The store shown on the ad card, e.g. `Best Buy`. |
| `storeLink` | The ad-click (`/aclk`) destination URL for the product. |

### Cost estimation

This Actor is billed **pay-per-event: you pay a flat price per search**, with unlimited products per search included. A search that returns 25 products costs the same as one that returns 3 — you're never penalized for high-yield searches, and you never pay a per-product fee. A search that happens to show no shopping ads is still one completed search.

Two settings bound the run: **`minSuccessfulQueries`** caps the number of searches (and therefore the number of billed events), and **`maxItems`** caps the number of products collected — whichever is hit first ends the run. To bound your bill before pressing Start, just multiply your search count by the per-search price. You can also set a max total charge on the run. Start small to confirm your numbers, then scale up.

### FAQ, limitations, and support

- **Why are results empty?** Almost always datacenter-proxy blocking. Switch `proxyConfiguration` to **Residential** and/or raise `retryLimit`.
- **Why do I need a proxy at all?** Google aggressively rate-limits repeated searches from one IP; the looping behavior makes this more likely.
- **Selectors may drift.** Google rotates the obfuscated markup behind the PLA carousel. Extraction anchors on the shopping ad-click (`/aclk`) links, which is more stable than class names; if a run returns zero products consistently on terms you know have shopping ads, the extraction may need re-deriving.
- **Store matching is a case-insensitive substring.** A `storeNames` entry of `best buy` matches `Best Buy`; `dyson` matches `Dyson Official`. Leave it empty to keep every product.
- **Legality.** Scrape responsibly and in line with Google's Terms of Service and applicable law. This Actor collects only publicly visible ad data.
- **Support / feedback.** Use the **Issues** tab on the Actor page.

# Actor input Schema

## `searchQueries` (type: `string`):

Comma-separated list of search terms, e.g. `buy dyson v15 vacuum, nespresso vertuo pods`. Each term runs one Google search and its Shopping (Product Listing) ads are scraped. Split on commas; empty/whitespace-only entries are dropped. High commercial-intent terms ("buy ...", specific product models) are far more likely to show shopping ads.

## `storeNames` (type: `array`):

Keep only products whose store name contains one of these entries (case-insensitive substring match against the store shown on the ad card, e.g. `Best Buy`, `Dyson Official`). Leave empty to return every product in the shopping ads.

## `region` (type: `string`):

Two-letter country code. Drives three things that stay in sync: the Google `gl` search parameter, the proxy country (when a proxy is used), and the `Accept-Language` header. Leave empty to behave like a plain US search with no proxy country pinning.

## `retryLimit` (type: `integer`):

Maximum retry attempts per search query after a block/error. On each retry the browser is fully closed and relaunched with a fresh proxy. Regions with a thin proxy pool may need a higher value.

## `retryDelayMs` (type: `integer`):

Delay in milliseconds before each retry attempt.

## `minSuccessfulQueries` (type: `integer`):

The Actor keeps cycling through the search queries, repeating them as needed, until this many queries have completed cleanly (a clean run with zero matches still counts). Set low for testing; 100+ for production.

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

Maximum number of products pushed to the dataset. The run stops as soon as this many rows are collected (or once `minSuccessfulQueries` clean queries complete — whichever comes first). This is your cost cap: it bounds the number of billed results before you press Start.

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

Proxy settings. If omitted, the Actor runs without a proxy (like the original). Google blocks datacenter IPs aggressively — RESIDENTIAL is strongly recommended. When `region` is set and Apify Proxy is used, the proxy is pinned to that country.

## Actor input object example

```json
{
  "searchQueries": "buy dyson v15 vacuum, nespresso vertuo pods",
  "storeNames": [],
  "retryLimit": 3,
  "retryDelayMs": 1000,
  "minSuccessfulQueries": 100,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "searchQueries": "buy dyson v15 vacuum, nespresso vertuo pods, buy iphone 15 pro case",
    "storeNames": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("surefind/google-ads-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 = {
    "searchQueries": "buy dyson v15 vacuum, nespresso vertuo pods, buy iphone 15 pro case",
    "storeNames": [],
}

# Run the Actor and wait for it to finish
run = client.actor("surefind/google-ads-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": "buy dyson v15 vacuum, nespresso vertuo pods, buy iphone 15 pro case",
  "storeNames": []
}' |
apify call surefind/google-ads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=surefind/google-ads-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Ads Scraper",
        "description": "Scrapes the Google Product Listing Ad (PLA) carousel for a list of search terms and filters the products by store name.",
        "version": "0.3",
        "x-build-id": "TGdFQ1pMraExfqw95"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/surefind~google-ads-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-surefind-google-ads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/surefind~google-ads-scraper/runs": {
            "post": {
                "operationId": "runs-sync-surefind-google-ads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/surefind~google-ads-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-surefind-google-ads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "searchQueries"
                ],
                "properties": {
                    "searchQueries": {
                        "title": "Search queries",
                        "type": "string",
                        "description": "Comma-separated list of search terms, e.g. `buy dyson v15 vacuum, nespresso vertuo pods`. Each term runs one Google search and its Shopping (Product Listing) ads are scraped. Split on commas; empty/whitespace-only entries are dropped. High commercial-intent terms (\"buy ...\", specific product models) are far more likely to show shopping ads."
                    },
                    "storeNames": {
                        "title": "Store names to keep",
                        "type": "array",
                        "description": "Keep only products whose store name contains one of these entries (case-insensitive substring match against the store shown on the ad card, e.g. `Best Buy`, `Dyson Official`). Leave empty to return every product in the shopping ads.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "region": {
                        "title": "Region (country code)",
                        "enum": [
                            "",
                            "us",
                            "gb",
                            "ca",
                            "au",
                            "de",
                            "fr",
                            "es",
                            "it",
                            "nl",
                            "in",
                            "br"
                        ],
                        "type": "string",
                        "description": "Two-letter country code. Drives three things that stay in sync: the Google `gl` search parameter, the proxy country (when a proxy is used), and the `Accept-Language` header. Leave empty to behave like a plain US search with no proxy country pinning."
                    },
                    "retryLimit": {
                        "title": "Retry limit",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum retry attempts per search query after a block/error. On each retry the browser is fully closed and relaunched with a fresh proxy. Regions with a thin proxy pool may need a higher value.",
                        "default": 3
                    },
                    "retryDelayMs": {
                        "title": "Retry delay (ms)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Delay in milliseconds before each retry attempt.",
                        "default": 1000
                    },
                    "minSuccessfulQueries": {
                        "title": "Minimum successful queries",
                        "minimum": 1,
                        "type": "integer",
                        "description": "The Actor keeps cycling through the search queries, repeating them as needed, until this many queries have completed cleanly (a clean run with zero matches still counts). Set low for testing; 100+ for production.",
                        "default": 100
                    },
                    "maxItems": {
                        "title": "Max items",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Maximum number of products pushed to the dataset. The run stops as soon as this many rows are collected (or once `minSuccessfulQueries` clean queries complete — whichever comes first). This is your cost cap: it bounds the number of billed results before you press Start.",
                        "default": 100
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings. If omitted, the Actor runs without a proxy (like the original). Google blocks datacenter IPs aggressively — RESIDENTIAL is strongly recommended. When `region` is set and Apify Proxy is used, the proxy is pinned to that country.",
                        "default": {
                            "useApifyProxy": false
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
