# Google Ads Transparency Scraper & Competitor Ad Library (`egeusta/google-ads-transparency-competitor-intelligence`) Actor

Scrape Google Ads Transparency Center data to monitor competitor ads, creatives, landing pages, active campaigns and ad changes over time.

- **URL**: https://apify.com/egeusta/google-ads-transparency-competitor-intelligence.md
- **Developed by:** [Ege Usta](https://apify.com/egeusta) (community)
- **Categories:** SEO tools
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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 Ads Transparency Competitor Intelligence

Scrape the Google Ads Transparency Center and turn competitor advertising into a
searchable intelligence feed. Use it for competitor ad research, creative
discovery, landing-page monitoring, active campaign tracking and ad change
detection.

Turns Google Ads Transparency Center listings into structured competitor
intelligence: normalized ad creatives, activity flags, creative clusters,
per-advertiser summaries, and change events between runs.

### Input

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `advertisers` | string\[] | — (required) | Per competitor: a domain (`nike.com`), a name (`Nike`), or a Transparency Center advertiser ID (`AR…`). URLs are accepted and reduced to a domain. |
| `region` | string | `"anywhere"` | `anywhere` or a two-letter ISO country code. |
| `maxAdsPerAdvertiser` | integer | `200` | Creative cap per advertiser. |
| `maxPages` | integer | `10` | Result-page cap (see limitations). |
| `requestTimeoutSecs` | integer | `30` | Per-request timeout. |
| `concurrency` | integer | `2` | Advertisers processed in parallel. |
| `minRequestDelayMs` | integer | `800` | Client-side rate limit between requests / retries. |
| `maxRetries` | integer | `2` | Retries for transient failures (`timeout`, `network-error`, `http-error`). Safety failures are never retried. |
| `maxResponseBytes` | integer | `4000000` | Byte ceiling per response. |
| `longRunningDays` | integer | `30` | Run-duration threshold for the long-running flag. |
| `newAdWindowDays` | integer | `7` | First-shown recency for the new flag. |
| `activeWindowDays` | integer | `14` | Last-shown recency for the active flag. |
| `resetSnapshot` | boolean | `false` | Ignore the stored snapshot; fresh baseline. |

### Output

Three item types (see `.actor/dataset_schema.json`):

- `type: "ad"` — normalized creative (`format`, `adCopy`, `landingPageUrl` /
  `landingPageDomain`, `regions`, `firstShownAt`, `lastShownAt`, `variantCount`)
  plus derived `runDurationDays`, `isActive`, `isNew`, `isLongRunning`, and a
  deterministic `creativeClusterId` (format + landing domain + first 8 normalized
  copy tokens, FNV-hashed).
- `type: "change"` — `new_ad`, `stopped_ad`, `resumed_ad`, `copy_changed` vs. the
  stored snapshot. The first observation of an advertiser is a baseline (no
  events). Events are fully sorted for reproducibility.
- `type: "advertiser_summary"` — `totalAds`, `activeAds`, `newAds`,
  `longRunningAds`, `formatBreakdown`, `topLandingDomains`, `creativeClusters`,
  first/last shown, a deterministic `activityScore` (0–100), and `partial` /
  `error` when the run for that advertiser was incomplete.

A `SUMMARY` record with per-advertiser reports, totals, `changeCounts` and
`failuresByReason` is written to the key-value store.

### Resilience

- Broken or incomplete ad objects are **counted** (`malformedAds`) and skipped —
  they never stop the run.
- Transient request failures are retried up to `maxRetries` with the configured
  delay; each retry is recorded.
- If every attempt fails, the advertiser's stored snapshot is left untouched and
  its summary is marked `partial` with an `error` — no false "all ads stopped".
- One failing advertiser never affects the others.
- No Google account, no CAPTCHA solving, no private endpoints. All fetches go
  through the shared `safeFetch` (SSRF-guarded, redirect-capped, byte-limited,
  timeout cleared in `finally`).

### Known limitations

- The Transparency Center is a JavaScript application backed by an internal RPC
  that expects `POST` with signed tokens. This Actor issues a **GET** against the
  public advertiser / search page and parses framed JSON or an embedded state
  blob. When Google returns an interstitial or an unrecognised layout the parser
  yields **zero ads and a warning**, never a crash. `maxPages` is honoured by the
  interface but the GET path is effectively single-page; supply advertiser IDs
  and/or run behind an unblocker for depth. All normalization, clustering,
  scoring, diffing and resilience logic is fully implemented and fixture-tested.
- `regions`, `firstShownAt` / `lastShownAt`, `format` and `variantCount` are only
  populated when the payload exposes them.

### Commands

```bash
npm ci                                                          # monorepo root
npm run check -w google-ads-transparency-competitor-intelligence
npm run smoke -w google-ads-transparency-competitor-intelligence  # offline
npx apify validate-schema
```

### Deployment

Not deployed here. `apify push` is intentionally not run — the lead engineer
handles deployment.

# Actor input Schema

## `advertisers` (type: `array`):

One entry per competitor: an advertiser domain (nike.com), an advertiser name (Nike), or a Transparency Center advertiser ID (AR12345678901234567890).

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

"anywhere" or a two-letter ISO country code (US, GB, DE) to scope the ad search.

## `maxAdsPerAdvertiser` (type: `integer`):

Upper bound on ad creatives collected per advertiser.

## `maxPages` (type: `integer`):

Upper bound on result pages requested per advertiser.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout for Transparency Center fetches.

## `concurrency` (type: `integer`):

Number of advertisers processed in parallel.

## `minRequestDelayMs` (type: `integer`):

Client-side rate limiting between requests / retries.

## `maxRetries` (type: `integer`):

Retries for transient request failures (timeout, network, HTTP error).

## `maxResponseBytes` (type: `integer`):

Hard ceiling on bytes read from a single Transparency Center response.

## `longRunningDays` (type: `integer`):

An ad is flagged long-running when its observed run duration is at least this many days.

## `newAdWindowDays` (type: `integer`):

An ad is flagged new when it was first shown within this many days of the run date.

## `activeWindowDays` (type: `integer`):

An ad is treated as active when last shown within this many days of the run date (or still running).

## `resetSnapshot` (type: `boolean`):

Ignore any stored snapshot and treat this run as a fresh baseline (no change events).

## Actor input object example

```json
{
  "region": "anywhere",
  "maxAdsPerAdvertiser": 200,
  "maxPages": 10,
  "requestTimeoutSecs": 30,
  "concurrency": 2,
  "minRequestDelayMs": 800,
  "maxRetries": 2,
  "maxResponseBytes": 4000000,
  "longRunningDays": 30,
  "newAdWindowDays": 7,
  "activeWindowDays": 14,
  "resetSnapshot": false
}
```

# Actor output Schema

## `results` (type: `string`):

Structured competitor ads, creatives, landing pages and campaign signals.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("egeusta/google-ads-transparency-competitor-intelligence").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("egeusta/google-ads-transparency-competitor-intelligence").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 '{}' |
apify call egeusta/google-ads-transparency-competitor-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,egeusta/google-ads-transparency-competitor-intelligence"
        }
    }
}

```

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/vBhDwZjSeudcrDbyE/builds/MJdNISKyRIivi5TAg/openapi.json
