# Competitor Ad Tracker: Google Ads + LinkedIn Ad Library (`jeanswiegers/competitor-ad-tracker`) Actor

- **URL**: https://apify.com/jeanswiegers/competitor-ad-tracker.md
- **Developed by:** [Jean Swiegers](https://apify.com/jeanswiegers) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Competitor Ad Tracker: Google Ads Transparency Center (+ LinkedIn beta)

**See every ad a competitor runs on Google (Search, YouTube, Display, Shopping and Maps) in one clean table, and get alerted to new and stopped ads.** Enter a website domain, brand name or Google advertiser ID. The Actor pulls the ads from the public **Google Ads Transparency Center** and returns ad text, images, YouTube videos, first and last shown dates, countries and the landing page. Schedule it in monitor mode and you only get (and pay for) what changed.

Built for performance agencies, SaaS and e-commerce marketers and competitive-intelligence teams who want ad-spy data without a $29-$349/month subscription.

### What you get

- **Every Google ad format**: Search text ads, YouTube video ads, Display/image ads, Shopping and Maps ads.
- **Lookup by domain, brand or ID**: `hubspot.com`, `HubSpot`, `AR10072600183532683265`. Domains give the most precise match. Brand names only match the exact advertiser (e.g. "Nike, Inc.", not "Nikena").
- **Full creative details**: headline, all visible ad copy, image URLs, YouTube URLs, countries shown and landing page.
- **Monitor mode**: label ads as **new**, **stopped**, **restarted** or unchanged since the last run, and output only the changes.
- **Region filter**: only ads shown in the US, UK, South Africa, Germany and 60+ other countries.
- **No login, cookies or API keys.** The Transparency Center is public by design.
- **LinkedIn Ad Library (beta)**: see the limits below.

### How to use it

1. Add one or more advertisers (domains work best).
2. Set **Max ads per advertiser** (newest first) and whether to **include ad details**.
3. Optional: add **regions**.
4. For tracking, switch on **Monitor mode** and schedule the Actor daily or weekly. The first run saves a baseline; later runs output only changes.

#### Example input

```json
{
  "advertisers": ["hubspot.com", "salesforce.com"],
  "platforms": ["google"],
  "maxAdsPerAdvertiser": 50,
  "includeAdDetails": true,
  "regions": ["US", "GB"],
  "monitorMode": true
}
```

### Output

One row per ad. Download as JSON, CSV, Excel or HTML, or use the API.

| Field | Example | Notes |
|---|---|---|
| platform | `google` | `linkedin` for beta results |
| advertiserName | `Hubspot, Inc.` | As listed by Google |
| advertiserId | `AR10072600183532683265` | |
| adId | `CR11277772207268298753` | Creative ID |
| format | `TEXT`, `IMAGE`, `VIDEO` | |
| headline | `HubSpot Customer Platform - Better Data. Better Decisions` | With details |
| adText | Full visible ad copy | With details |
| mediaUrls / videoUrls | Image URLs, YouTube URLs | |
| firstShown / lastShown | `2026-01-29T08:00:00.000Z` | |
| isActive | `true` | Shown in the last N days (default 3) |
| regions | `["US", "GB"]` | With details |
| landingUrl / landingDomain | `https://www.hubspot.com/` | |
| changeStatus | `new`, `stopped`, `restarted`, `unchanged`, `baseline` | Monitor mode |
| adUrl | Link to the ad in the Transparency Center | |

#### Sample row

```json
{
  "platform": "google",
  "advertiserName": "HubSpot",
  "adId": "CR12207894138821017601",
  "format": "TEXT",
  "headline": "HubSpot Customer Platform - Better Data. Better Decisions",
  "firstShown": "2026-10-02T12:00:00.000Z",
  "lastShown": "2026-10-19T09:30:00.000Z",
  "isActive": true,
  "regions": ["US"],
  "landingUrl": "https://www.hubspot.com/",
  "changeStatus": "new"
}
```

### How "stopped" is detected

- **Google**: an ad counts as stopped when its last shown date is older than your threshold (default 3 days). Google keeps stopped ads in the library, so this is exact.
- **Ads no longer listed** are only reported as stopped when the advertiser's full ad list was fetched. If the list was cut off by **Max ads per advertiser**, unseen ads keep their previous state instead of being falsely flagged.

### LinkedIn Ad Library (beta)

Add `"linkedin"` to platforms to also search the LinkedIn Ad Library. It's in beta because:

- LinkedIn frequently blocks cloud and proxy IPs, so LinkedIn lookups can fail even after retries. Failed lookups are free and don't stop the Google part.
- Without a login, LinkedIn shows about the first 24 ads per advertiser.
- Public ad pages show the advertiser, ad text, headline, images or video, format, call to action and payer, but usually **no dates, regions, impressions or landing URL**.
- Stopped LinkedIn ads can only be detected by disappearing from the list.

### Pricing

Pay per event: a charge per ad returned plus a small extra per ad when you ask for full details. In monitor mode with "only changes", you only pay for new, stopped and restarted ads. Failed lookups are free.

### FAQ

**Why does a brand name return a different advertiser?** Google lists legal entities. Use the domain (e.g. `nike.com`) or the advertiser ID (AR…) for an exact match.

**Is this legal?** The Google Ads Transparency Center and LinkedIn Ad Library are public transparency tools. The Actor collects ad creatives and advertiser names, not personal data.

# Actor input Schema

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

Brand names ("HubSpot"), domains ("hubspot.com"), Google advertiser IDs (AR…), LinkedIn company IDs or LinkedIn company URLs. Domains give the most precise Google results.

## `platforms` (type: `array`):

Ad libraries to search: google (Google Ads Transparency Center: Search, YouTube, Display, Shopping, Maps) and/or linkedin (LinkedIn Ad Library).

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

Newest ads first (by last shown date).

## `includeAdDetails` (type: `boolean`):

Fetch each ad's full creative: ad text and headline, video URLs, image URLs, regions shown and landing page. Charged as an extra event per ad.

## `regions` (type: `array`):

Optional two-letter country codes (US, GB, DE, ZA…) to only return ads shown there. Empty = all regions.

## `monitorMode` (type: `boolean`):

Remember the ads seen on each run (in the named key-value store 'competitor-ad-tracker-state') and label ads as new, stopped, restarted or unchanged compared with the previous run with the same advertisers. Schedule the Actor daily or weekly.

## `onlyChanges` (type: `boolean`):

In monitor mode, only output (and charge for) new, stopped and restarted ads. The first run outputs everything as 'baseline'.

## `monitorKey` (type: `string`):

Optional name for this watch list. By default it is derived from the advertisers, platforms and regions.

## `stoppedAfterDays` (type: `integer`):

A Google ad whose last shown date is older than this counts as inactive.

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

Google is fetched without a proxy where possible. LinkedIn usually needs one; Apify datacenter proxy is tried first.

## `debugSaveHtml` (type: `boolean`):

Save raw LinkedIn pages to the key-value store.

## Actor input object example

```json
{
  "advertisers": [
    "hubspot.com"
  ],
  "platforms": [
    "google"
  ],
  "maxAdsPerAdvertiser": 10,
  "includeAdDetails": true,
  "monitorMode": false,
  "onlyChanges": true,
  "stoppedAfterDays": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "debugSaveHtml": false
}
```

# Actor output Schema

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

One row per ad.

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

Counts of ads, details, new and stopped ads.

# 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 = {
    "advertisers": [
        "hubspot.com"
    ],
    "platforms": [
        "google"
    ],
    "maxAdsPerAdvertiser": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("jeanswiegers/competitor-ad-tracker").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 = {
    "advertisers": ["hubspot.com"],
    "platforms": ["google"],
    "maxAdsPerAdvertiser": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("jeanswiegers/competitor-ad-tracker").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 '{
  "advertisers": [
    "hubspot.com"
  ],
  "platforms": [
    "google"
  ],
  "maxAdsPerAdvertiser": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call jeanswiegers/competitor-ad-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jeanswiegers/competitor-ad-tracker"
        }
    }
}
```

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/EIqf0hhIoJSx42YnU/builds/JIh1fqyAWIbSXGbUy/openapi.json
