# Google Ads Scraper — Live Search Sponsored Ads (`khadinakbar/google-ads-scraper`) Actor

Scrape live Google Search sponsored (PPC) ads by keyword, country, city and device: one row per ad with advertiser, ad copy, slot, sitelinks and landing URL, plus a free advertiser share-of-voice summary. Live search ads, not the Ads Transparency Center.

- **URL**: https://apify.com/khadinakbar/google-ads-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** SEO tools, MCP servers, Marketing
- **Features:** Limited permissions
- **Stats:** 141 total users, 38 monthly users, 62.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 sponsored ads

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

### What is Google Ads Scraper — Live Search Sponsored Ads?

Google Ads Scraper is an Apify Actor for people who need live Google Search sponsored-ad data by keyword, country, language, location, and device. It accepts one or more search keywords and returns one record per paid ad, with fields such as advertiser, title, description, display URL, landing URL, position, rank within the ad block, sitelinks, extensions, phone, rating, and scrape context. The outcome is a structured dataset of the sponsored ads currently shown for each query, ready for analysis, reporting, or downstream automation. It is also usable through Apify MCP.

### Best fit and connected workflows

This Actor fits workflows where the question is, "Which paid ads are visible right now for this search term in this market?"

It is a strong match for:

- PPC competitive monitoring across keywords, countries, and devices
- ad copy research based on live search results
- share-of-voice checks across a tracked keyword set
- geo-specific monitoring with country and optional city targeting
- desktop-versus-mobile comparisons for the same query
- AI agent workflows that need sponsored-ad records from Apify MCP

For adjacent Google workflows, pair it with:

- Use Google Ads Transparency Scraper - Creatives & Run Dates when the next step is advertiser creative history from the Transparency Center
- Use Google AI Overviews Scraper when the next step is AI Overview content for the same search context
- Use Google Maps Reviews Scraper when the next step is collecting public reactions tied to a returned business or place record

### Practical scenario

Maya, a PPC analyst, starts with the keyword list `["car insurance", "crm software"]`. She sets the country to `us`, language to `en`, and device to `desktop` so the search surface matches her reporting view.

The dataset returns fields such as:

- `advertiser`
- `title`
- `description`
- `displayUrl`
- `finalUrl`
- `position`
- `rankInBlock`
- `sitelinks`
- `extensions`

From that output, Maya compares which advertisers appear in the top ad slots and reviews the landing pages and sitelinks. Her next action is to group the records by advertiser and keyword, then compare the same keywords on mobile for a second view.

### Input fields

| Field | Type | Description |
|---|---|---|
| `queries` | array | Search keywords to run on Google. One ad becomes one record. Required. |
| `countryCode` | string | Two-letter ISO country code used for geo-targeting, such as `us`, `gb`, or `de`. |
| `languageCode` | string | Two-letter ISO language code for the Google interface and results, such as `en` or `fr`. |
| `locationName` | string | Optional canonical city location in `City,Region,Country` form for city-level targeting. |
| `device` | string | `desktop` or `mobile`. |
| `maxAdsPerQuery` | integer | Cost cap per keyword. Range 1-100. |
| `maxPagesPerQuery` | integer | Number of Google result pages to fetch per keyword. Range 1-5. |
| `includeBottomAds` | boolean | Capture sponsored ads shown at the bottom of the page as well as the top block. |
| `includeSummaryRows` | boolean | Also add one free advertiser share-of-voice row per advertiser to the dataset. Defaults to `false`. |
| `engine` | string | Fetching mode: `auto` (recommended), managed search API, or browser first. |
| `serpApiKey` | string | Optional own key for the managed search API, to use your own quota. Leave blank to use the built-in key. |

#### Focused input example

```json
{
  "queries": ["car insurance", "plumber near me"],
  "countryCode": "us",
  "languageCode": "en",
  "locationName": "New York,New York,United States",
  "device": "desktop",
  "maxAdsPerQuery": 10,
  "maxPagesPerQuery": 1,
  "includeBottomAds": true,
  "includeSummaryRows": true,
  "engine": "auto"
}
```

### Output fields

Each ad row (`recordType: "ad"`) represents one sponsored ad and bills one ad-found event.

| Field | Type | Description |
|---|---|---|
| `recordType` | string | `ad` for a sponsored ad, `advertiserSummary` for a free share-of-voice row. |
| `query` | string | Keyword that produced the ad. |
| `advertiser` | string | Advertiser domain or name captured from the ad. |
| `title` | string | Ad headline. |
| `description` | string | Ad body copy. |
| `displayUrl` | string | Vanity URL shown in the ad. |
| `finalUrl` | string | Landing page URL. |
| `position` | string | Ad block position, such as top or bottom. |
| `rankInBlock` | integer | Rank within the ad block. |
| `sitelinks` | array | Sitelink objects attached to the ad. |
| `extensions` | array | Additional ad extensions captured when present. |
| `phone` | string or null | Call extension phone number when shown. |
| `rating` | string or null | Rating value when shown. |
| `countryCode` | string | Country used for the search. |
| `languageCode` | string | Language used for the search. |
| `device` | string | Device surface used for the search. |
| `locationName` | string or null | City location used for targeting, when provided. |
| `page` | integer | Google result page number. |
| `searchUrl` | string | Search URL used for the scrape. |
| `scrapedAt` | string | ISO timestamp for the record. |

#### Example ad row

A subset of a real row from a recent run (`car insurance`, United States, desktop):

```json
{
  "recordType": "ad",
  "query": "car insurance",
  "advertiser": "GEICO",
  "title": "Get GEICO® Auto Insurance | Free Online Quotes 24/7",
  "description": "GEICO®'s Car Insurance Quotes Are Personalized For You. Get a Quote Now.",
  "displayUrl": "https://www.geico.com",
  "finalUrl": "https://www.geico.com/landingpage/go558/",
  "position": "top",
  "rankInBlock": 2,
  "sitelinks": [
    { "title": "Get A Quote", "url": "https://www.geico.com/landingpage/go503/?soa=48312" },
    { "title": "Auto Coverage Calculator", "url": "https://www.geico.com/coverage-calculator/?soa=48134" }
  ],
  "extensions": [],
  "phone": null,
  "rating": null,
  "countryCode": "us",
  "languageCode": "en",
  "device": "desktop",
  "locationName": null,
  "page": 1
}
```

### Advertiser share of voice

Every run also builds a free share-of-voice summary from the ads it saved. It is always written to the `OUTPUT` and `RUN_SUMMARY` records under `shareOfVoice`, and with `includeSummaryRows: true` it is also added to the dataset as one `advertiserSummary` row per advertiser. Summary rows are never billed.

| Field | Description |
|---|---|
| `advertiser` | Advertiser domain, normalised (lowercase, no `www.`). |
| `ads` | Sponsored ads saved for this advertiser in the run. |
| `adShare` | Share of all saved ads (0-1). |
| `keywordsWithAds` | Number of requested keywords where the advertiser appeared. |
| `keywordShare` | Share of requested keywords where the advertiser appeared (0-1). |
| `topOfPageAds` / `bottomOfPageAds` | Ads in the top and bottom ad blocks. |
| `topOfPageShare` | Share of the advertiser's ads shown in the top block (0-1). |
| `avgTopRank` | Average rank inside the top block (1 = first ad), or null. |
| `keywords` | The keywords where the advertiser appeared. |

Share of voice reflects only the ads saved in this run, so it depends on the keywords, country, device and the `maxAdsPerQuery` cap you chose.

### Outcome codes

The `OUTPUT` record has an `outcome` field:

- `COMPLETE`: every requested keyword page was fetched.
- `VALID_EMPTY`: the pages loaded but Google showed no sponsored ads; nothing is charged for ads.
- `PARTIAL`: some keyword pages failed, the run charge limit was reached, or the main search source was unavailable and a backup source was used; the ads already saved are kept. The backup source covers the first results page only, checks each page up to three times and merges the results, and can still miss ads, so an empty backup result does not prove no ads were running. `backupSourceQueryPages` in `OUTPUT` shows how many pages it served.
- `INVALID_INPUT`: no usable keywords (or no key for the forced managed engine); nothing is charged for ads.
- `UPSTREAM_FAILED`: every route failed and no ads were collected. The run is marked failed.

### How it works

This Actor follows the live Google Search results page for the keywords you provide and extracts sponsored ads from the visible ad blocks. The contract shows support for:

- top and bottom sponsored-ad detection
- sitelink parsing
- extension parsing
- geo targeting through country and optional city location
- desktop and mobile targeting
- multi-strategy fallback selectors
- managed search API route with a residential-proxy browser fallback
- per-ad pay-per-event billing

The output dataset stores one record per paid ad, and the key-value store includes a run summary record.

### Pricing

This Actor uses Pay per event, plus Apify platform usage.

The primary charged event is **Sponsored ad**, billed for each sponsored Google Search ad extracted. There is also an Actor Start event that applies when a run begins, with the amount tied to Actor memory usage. The input includes a per-keyword cost cap through `maxAdsPerQuery`, and your total event volume is bounded by the number of keywords multiplied by that cap.

For example, ten keywords with a cap of twenty ads per keyword can charge at most 200 Sponsored ad events. Share-of-voice summary rows are free. See the Pricing tab for current event prices.

#### Free Apify plan

Runs started from a free Apify plan return up to 40 results per run, with every input mode and output field available. Paid Apify plans have no per-run limit, so your usual result settings apply.

### Use with AI agents (MCP)

This Actor is available through Apify MCP as a tool for retrieving live Google Search sponsored-ad records.

**Actor identity:** `khadinakbar/google-ads-scraper`

Tool description: accept a keyword list and targeting options, then return one dataset record per sponsored ad with advertiser, headline, copy, landing URL, rank, and search context.

> Find the live sponsored ads for these keywords in the United States on desktop. Return one record per ad, keep the query and rank fields, and include the search URL so I can trace each result back to the source search.

When an agent reads the output, the dataset items are the ad records. The `ads` output link points to the dataset items endpoint, and `runSummary` points to the key-value store record for the run summary. For large keyword sets, pagination and cost scale with the number of keywords and the `maxAdsPerQuery` cap, so agent prompts work best when they specify a focused query list and target surface. The `output` link returns the OUTPUT record with the run outcome and the advertiser share-of-voice table.

### API example

```javascript
import { ApifyClient } from "apify-client";

const client = new ApifyClient({
  token: process.env.APIFY_TOKEN,
});

const input = {
  queries: ["car insurance", "crm software"],
  countryCode: "us",
  languageCode: "en",
  device: "desktop",
  maxAdsPerQuery: 10,
  includeBottomAds: true,
  engine: "auto",
};

const run = await client.actor("khadinakbar/google-ads-scraper").call(input);

const { items } = await client.dataset(run.defaultDatasetId).listItems();

console.log(items);
```

### Best results and outcome guidance

Use the smallest input that answers the immediate question. A focused keyword list keeps the dataset easier to review and keeps the event count aligned with the ads you want to analyze. For city-level comparisons, provide `locationName` in the exact `City,Region,Country` form. For market-by-market research, keep `countryCode` and `languageCode` aligned with the audience and the Google interface language used in that market. If you want to compare search surfaces, run the same keywords on both `desktop` and `mobile`.

### Continue the workflow

- Use [Google Ads Transparency Scraper](https://apify.com/khadinakbar/google-ads-transparency-scraper) for an advertiser's creative history and run dates from the Transparency Center.
- Use [Meta Ad Library Scraper](https://apify.com/khadinakbar/meta-ad-library-scraper) when the brief also covers Facebook and Instagram ads.

### Design note

Google injects many search ads with JavaScript, so a browser page that shows no ad blocks is not trusted as "no ads" on its own: when a search API key is available, the same keyword page is re-checked through the managed search API before the run reports zero ads. Ad rows are billed only after they are saved, and the per-keyword cap is enforced before each charge.

### FAQ

#### Is this Actor for live Google Search ads or Transparency Center data?

This Actor returns live sponsored ads from Google Search results pages. For advertiser creative history and run dates, use the Google Ads Transparency Scraper instead.

#### What is one record in the dataset?

One record is one paid ad returned for one keyword on one search surface, with its advertiser, creative text, landing page, and targeting context.

#### Can I compare desktop and mobile ads?

Yes. The `device` field supports both `desktop` and `mobile`, so you can run the same keywords on each surface and compare the resulting ad sets.

#### Can I target a specific city?

Yes. Provide `locationName` in the exact `City,Region,Country` format to add city-level targeting on top of `countryCode`.

#### How do I measure advertiser share of voice?

Run your tracked keyword set and read `shareOfVoice` in the `OUTPUT` record, or set `includeSummaryRows: true` to get one free summary row per advertiser in the dataset. It shows each advertiser's share of ads, share of keywords and top-of-page share.

#### How should AI agents use the output?

Agents can read the dataset items endpoint for the ad records and use the run summary record for run-level context. The most useful fields for downstream routing are `query`, `advertiser`, `title`, `finalUrl`, `position`, `rankInBlock`, and `searchUrl`.

### Responsible use

Use this Actor to collect publicly visible sponsored-ad data from Google Search and handle the results in line with applicable laws, Google's terms, and your own data governance practices. Keep keyword sets and targeting choices aligned with the research question, and treat the output as search-surface data for analysis, monitoring, and workflow automation.

# Changelog

This Actor's version history is a separate document: https://apify.com/khadinakbar/google-ads-scraper/changelog.md

# Actor input Schema

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

Keywords to search on Google. The scraper returns the SPONSORED (paid) ads shown for each keyword, one record per ad. Example: \['car insurance', 'crm software']. This is live SERP ad data, NOT Google Ads Transparency Center creatives — for advertiser creative history use a Transparency Center scraper instead. Free Apify plan runs return up to 40 results per run.

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

Two-letter ISO country code that geo-targets the Google search (the gl parameter), e.g. 'us', 'gb', 'de'. Ads are country-specific, so this strongly affects which advertisers appear. Defaults to 'us'. For city-level targeting also set locationName.

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

Two-letter ISO language code for the Google interface and results (the hl parameter), e.g. 'en', 'de', 'fr'. Defaults to 'en'. Set this to the language advertisers in your target country actually bid in, not your own.

## `locationName` (type: `string`):

Optional canonical location for city-level ad targeting via Google's uule parameter, e.g. 'New York,New York,United States' or 'London,England,United Kingdom'. Use the exact 'City,Region,Country' form. Leave blank to target the whole country set by countryCode. NOT a latitude/longitude.

## `device` (type: `string`):

Whether to fetch the desktop or mobile Google results page. Mobile and desktop often show different ads and ad counts, so pick the surface you want to monitor. Defaults to 'desktop'.

## `maxAdsPerQuery` (type: `integer`):

Hard cap on how many ads are saved and billed per keyword. This is your cost control: the run charges at most (number of keywords x maxAdsPerQuery) ad-found events — see the Pricing tab for the per-event price. Defaults to 20. A typical results page shows 3-7 ads, so 20 rarely truncates. Range 1-100.

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

How many Google result pages to fetch per keyword. Ads concentrate on page 1, so the default 1 is best for most users; raise it only to capture bottom-of-funnel ads on deeper pages. Range 1-5.

## `includeBottomAds` (type: `boolean`):

Whether to also capture sponsored ads shown at the BOTTOM of the results page, not just the top block. Bottom ads are usually lower-bid competitors. Defaults to true. Set false to monitor only premium top-of-page ad slots.

## `includeSummaryRows` (type: `boolean`):

When enabled, the dataset also gets one free summary row per advertiser (recordType 'advertiserSummary'): ads seen, share of all ads, keywords with ads, top-of-page share and average top rank. Summary rows are never billed. The same share-of-voice summary is always written to the OUTPUT record. Defaults to false, so the dataset contains only ad rows.

## `engine` (type: `string`):

How ads are fetched. 'auto' (recommended) uses the reliable managed search engine when a key is available (built in by default) and falls back to a stealth browser otherwise. 'managed search' forces the managed search API. 'browser' tries a stealth browser first and only falls back to managed search if Google blocks it. Most users should leave this on 'auto'.

## `serpApiKey` (type: `string`):

Optional: bring your own key for the managed Google search API this Actor uses (the provider indicated by this field's name) to run on your own quota and rate limits. Leave blank to use the built-in managed key. This is a credential, not a search term — it is never stored in your output.

## Actor input object example

```json
{
  "queries": [
    "best vpn",
    "plumber near me"
  ],
  "countryCode": "us",
  "languageCode": "en",
  "locationName": "New York,New York,United States",
  "device": "desktop",
  "maxAdsPerQuery": 20,
  "maxPagesPerQuery": 1,
  "includeBottomAds": true,
  "includeSummaryRows": false,
  "engine": "auto"
}
```

# Actor output Schema

## `ads` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

## `output` (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": [
        "car insurance",
        "project management software"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/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 = { "queries": [
        "car insurance",
        "project management software",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/google-ads-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": [
    "car insurance",
    "project management software"
  ]
}' |
apify call khadinakbar/google-ads-scraper --silent --output-dataset

```

## MCP server setup

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