# Ultimate LinkedIn Ad Scraper (`apt_marble/ultimate-linkedin-ad-scraper`) Actor

Extract LinkedIn ads at scale: search the Ad Library by keyword, advertiser or country, or scrape specific ad URLs. Export ad creatives, copy, impressions by country, run dates and audience targeting to JSON or CSV. No login required.

- **URL**: https://apify.com/apt\_marble/ultimate-linkedin-ad-scraper.md
- **Developed by:** [Hamza](https://apify.com/apt_marble) (community)
- **Categories:** Lead generation, Jobs, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 ad scrapeds

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/docs.md):

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.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/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

## Ultimate LinkedIn Ad Scraper 🔎📣

Everything **LinkedIn's public Ad Library** knows — in one actor. Search **over 2 million ads** by keyword, company, payer, country and date, and/or paste specific **ad URLs or ids** to pull the complete record of any ad. See exactly what competitors run on LinkedIn: their creatives, their copy, their CTAs, **who actually paid for each ad**, and (for ads shown in the EU) **how long each ad ran, impression counts, per-country reach and full targeting parameters — including exclusions**. No login or cookies required.

The LinkedIn equivalent of a Facebook Ad Library scraper — built for competitive ad intelligence, with a deep-dive mode built in.

### 🧭 Two modes, one output

- **Search mode** — the Ad Library's own search: keyword, companies, payer, account owner, plus country/date/impressions filters. Fast card-level records; switch on **Scrape full ad details** to add the deep detail fields to every result.
- **Ad-URLs mode** — paste Ad Library detail URLs (or bare numeric ids) and get the complete detail record of each ad, always in full depth.

Use either mode alone or both in the same run: direct ad URLs are scraped first, then the search runs, and any ad that appears in both is **deduplicated automatically** (the URL-mode record wins). Every record carries `sourceMode` (`"adUrls"` or `"search"`) so you always know where a row came from.

### ✨ What you get

One unified record shape for both modes:

| Field | Description |
|---|---|
| `adId` / `adUrl` | The ad and its canonical Ad Library detail URL |
| `advertiserName` / `advertiserUrl` / `advertiserLogoUrl`† | Who is advertising (company or person) |
| `advertiserSubtitle` | For personal ads, the person's headline (e.g. "CEO at …") |
| `promotedLabel` | "Promoted" or "Promoted by ⟨company⟩" (thought-leader ads) |
| `adText` / `fullAdText` | The ad copy — card version and full untruncated version* |
| `headline` / `ctaText` | Sponsored headline and call-to-action ("Learn more", …) |
| `mediaType` / `mediaUrl` / `creativeType`† | video / image / text + the creative asset URL (URL mode extracts the actual MP4 for video ads) |
| `adFormat` | "Video Ad", "Image Ad", "Document Ad", …* |
| `paidBy` | The "Paid for by" entity — who actually paid* |
| `ranFrom` / `ranTo` | Exact run dates (ISO), open-ended when still running — EU transparency data* |
| `totalImpressionsRange` | e.g. "30k-50k" — EU transparency data* |
| `impressionsByCountry` | Per-country impression share, e.g. France 100%* |
| `targeting` | Languages, locations included/excluded, and the full targeting parameter table (Audience, Demographic, Company, Education, Job, Member Interests)* |
| `isEuTransparency` | `true` when the ad carries the EU transparency disclosure* |
| `sourceMode` | `"search"` or `"adUrls"` — which mode produced the record |
| `position` / `searchQuery` / `searchUrl` / `totalMatchingAds` / `scrapedAt` | Provenance (search-mode fields are `null` for URL-mode records) |

\* Fields marked with an asterisk come from each ad's detail page — they are always present for URL-mode records, and for search results when **Scrape full ad details** is on (otherwise they are `null`).

† `advertiserLogoUrl` and `creativeType` come from the search result card — they are always `null` for URL-mode records.

### 🇪🇺 EU transparency data, explained

Under the EU **Digital Services Act**, LinkedIn publicly discloses extra data for every ad that was shown in the European Union: exact run dates, a total impressions range (e.g. `30k-50k`), the impression share per country, and the targeting setup — languages, locations (including exclusions), and whether Audience / Demographic / Company / Education / Job / Interests targeting was used. This actor extracts that disclosure in full and flags it with `isEuTransparency: true`. Ads never shown in the EU legitimately return these fields as `null` — that is LinkedIn's data policy, not a scraper gap.

### ⚙️ Search options

- **Keyword** — free-text search across ad content.
- **Companies** — company names, numeric LinkedIn company IDs, or `linkedin.com/company/...` URLs; names are resolved to the official company automatically and each company gets its own search.
- **Paid by (payer)** — find every ad a given entity paid for, even across different advertiser pages.
- **Account owner** — filter by the ad account's owner.
- **Countries** — one or more ISO codes (`US`, `FR`, `DE`, …) or `ALL` for worldwide.
- **Date shown** — last 30 days, current month, current year, last year, or a custom date range.
- **Impressions** — minimum / maximum impressions (in thousands).
- **Max ads per search** + the **Scrape full ad details** toggle.

### 🚀 Examples

Search input:

```json
{
    "keyword": "crm software",
    "countries": ["FR"],
    "dateOption": "last-30-days",
    "maxAds": 50,
    "scrapeAdDetails": true
}
````

Direct ad URLs input (mix full URLs and bare ids freely):

```json
{
    "adUrls": [
        "https://www.linkedin.com/ad-library/detail/1513233886",
        "1468985674"
    ]
}
```

One record from the output:

```json
{
    "adId": "1497599253",
    "adUrl": "https://www.linkedin.com/ad-library/detail/1497599253",
    "advertiserName": "Cegid pour les entreprises",
    "advertiserUrl": "https://www.linkedin.com/company/12650121",
    "advertiserLogoUrl": "https://media.licdn.com/dms/image/v2/company-logo_100_100.png",
    "advertiserSubtitle": null,
    "promotedLabel": "Promoted",
    "adText": "Les équipes finance attendent aujourd'hui bien plus qu'un simple logiciel fiscal…",
    "fullAdText": "Les équipes finance attendent aujourd'hui bien plus qu'un simple logiciel fiscal.\n\nElles recherchent des solutions capables de simplifier le quotidien…",
    "headline": "Cegid Tax Flex",
    "ctaText": "Learn more",
    "mediaType": "video",
    "mediaUrl": "https://media.licdn.com/dms/image/v2/videocover-high.jpg",
    "creativeType": "SPONSORED_VIDEO",
    "adFormat": "Video Ad",
    "paidBy": "OnlySo",
    "ranFrom": "2026-07-22",
    "ranTo": "2026-07-24",
    "totalImpressionsRange": "30k-50k",
    "impressionsByCountry": [
        { "country": "France", "percentage": "100%" },
        { "country": "Luxembourg", "percentage": "< 1%" }
    ],
    "targeting": {
        "languages": ["Français"],
        "locationsIncluded": ["France"],
        "locationsExcluded": [],
        "parameters": [
            { "category": "Audience", "targeted": false, "excluded": false },
            { "category": "Job", "targeted": true, "excluded": false }
        ]
    },
    "isEuTransparency": true,
    "sourceMode": "search",
    "position": 1,
    "searchQuery": "keyword: crm software; countries: FR; dates: last-30-days",
    "searchUrl": "https://www.linkedin.com/ad-library/search?keyword=crm+software&countries=FR&dateOption=last-30-days",
    "totalMatchingAds": 11802,
    "scrapedAt": "2026-07-24T18:40:11.000Z"
}
```

### 💡 Use cases

- **Competitive ad intelligence** — monitor exactly what competitors run, which creatives they iterate on, and how long each ad stays live.
- **Creative research** — build swipe files of headlines, hooks, CTAs and video creatives that companies in your space keep paying for.
- **EU DSA transparency research** — impressions, reach by country and targeting choices for any EU-served ad, in clean JSON instead of screenshots.
- **Compliance & brand safety** — verify who paid for an ad and how it was targeted (including exclusion targeting); audit ads mentioning your brand or category.
- **Agency prospecting & reporting** — find companies actively spending on LinkedIn ads, or archive your clients' ads with the full disclosure record attached.

### 💳 Pricing — two simple events

- **Per ad scraped (base event)** — every ad record pushed to your dataset bills one base event. That's it for plain search results.
- **Per full ad detail (`full-ad-details` event)** — when an ad's detail page is fetched and parsed in full, that ad additionally bills one `full-ad-details` event. This happens for every ad in your **Ad URLs** list (those are always full-depth), and for every search result when **Scrape full ad details** is switched on.

So: a plain search bills only the base event per ad. A detailed search or a URL-mode run bills the base event **plus** one `full-ad-details` event per ad. Removed or unreachable ads are never billed, and if your run's budget for details runs out mid-run, search results keep flowing without the detail fields (you are only ever billed for details actually delivered). The Ad-URLs mode is different: since every Ad-URLs record includes full details, that mode stops early once the `full-ad-details` budget is exhausted — the remaining URLs are not scraped and not billed (raise the run's maximum charge to scrape them).

### ❓ FAQ

**Do I need a LinkedIn account or cookies?** No. No login, password or session cookie is involved.

**Where do impressions and targeting data come from?** LinkedIn publishes them (under the EU Digital Services Act) for every ad that was shown in the EU. Ads never shown in the EU legitimately have `ranFrom`, `ranTo`, `totalImpressionsRange`, `impressionsByCountry` and `targeting` as `null`.

**Do both modes produce the same fields?** Yes — one unified record shape. URL-mode records set the search-card-only fields (`position`, `searchQuery`, `searchUrl`, `totalMatchingAds`, `promotedLabel`, `advertiserSubtitle`, `advertiserLogoUrl`, `creativeType`, card `adText`) to `null`, and search-mode records without **Scrape full ad details** leave the detail fields `null`. Check `sourceMode` to tell rows apart.

**Where do I find ad URLs?** On the LinkedIn Ad Library website (every ad card links to a detail page) — or simply from this actor's own search output: the `adUrl` field feeds straight back into the `adUrls` input.

**What does "impressions in thousands" mean?** LinkedIn's impression filter works in units of a thousand: `minImpressions: 50` means "at least 50,000 impressions".

**Can I search several companies at once?** Yes — each company becomes its own search (combined with your other filters), and `maxAds` applies per search.

**Why is `adText` truncated but `fullAdText` complete?** The search results page truncates long copy with an ellipsis; the full text lives on each ad's detail page. URL-mode records never saw a search card, so their copy is in `fullAdText` and `adText` is `null`.

**Are the impression numbers exact?** No — LinkedIn discloses ranges (e.g. `30k-50k`) and per-country percentages (small shares appear as `< 1%`). The scraper preserves them exactly as disclosed.

**How fresh is the data?** Ads are fetched live from the Ad Library at run time; `scrapedAt` stamps every record.

### ⚠️ Honest limitations

- **EU-only transparency**: run dates, impressions and targeting are only published for ads shown in the EU — that's a LinkedIn/DSA rule, not a scraper limitation. Non-EU ads still return format, payer, copy and creative.
- **Detail fields need the detail page**: `adFormat`, `paidBy`, `fullAdText`, run dates, impressions and targeting come from each ad's detail page — always included in URL mode, and in search mode only with **Scrape full ad details** on.
- **Targeting is disclosed at category level.** The parameter table says *whether* Audience/Demographic/Company/Education/Job/Interests targeting was used (and whether exclusions were applied); languages and locations are disclosed as values. LinkedIn does not publish, for example, the exact company list an advertiser targeted.
- **Location lists are disclosed as comma-separated text.** Multi-part place names (e.g. `Melbourne, VIC`) can therefore split into two entries — an ambiguity in the source disclosure itself. Country names containing "and" (`Trinidad and Tobago`, `Bosnia and Herzegovina`, …) are recognized and kept whole.
- **Expired or removed ads can't be scraped** — they leave the Ad Library once a campaign ends or its retention window lapses. They are skipped with a warning and never billed.
- **Very large searches**: a single search combination paginates up to ~2,000 ads; use filters (countries, dates, companies) to slice bigger result sets.
- **Payer/account-owner filters** match LinkedIn's own filter semantics — an exact-entity text match, not fuzzy search.
- **Media URLs are time-limited**: LinkedIn signs its CDN URLs, so download creatives soon after scraping.

# Actor input Schema

## `adUrls` (type: `array`):

Specific Ad Library ads to scrape in full — one entry per line. Each entry is either a full detail URL like https://www.linkedin.com/ad-library/detail/1513233886 (query strings and trailing slashes are fine) or a bare numeric ad id like 1513233886. Every ad listed here returns a complete detail record. Optional — leave empty to only search. Up to 1000 ads per run; invalid entries are skipped with a warning.

## `keyword` (type: `string`):

Free-text search across ad content. To search, provide at least one of: keyword, companies, payer or account owner.

## `companies` (type: `array`):

Filter to ads from specific advertisers. Accepts company names ("Microsoft"), numeric LinkedIn company IDs ("1035") or company page URLs ("https://www.linkedin.com/company/microsoft"). Names and URLs are resolved to the official company automatically; each company gets its own search with the other filters applied. At most 50 companies per run.

## `payer` (type: `string`):

Filter by the entity that paid for the ad — the "Paid for by" name shown on each ad.

## `accountOwner` (type: `string`):

Filter by the owner of the ad account that ran the ad.

## `countries` (type: `array`):

Only ads shown in these countries. Two-letter ISO codes like "US", "FR", "DE" — or "ALL" for worldwide. Leave empty for no country filter.

## `dateOption` (type: `string`):

Only ads that ran within this window.

## `startDate` (type: `string`):

First day of the custom range (YYYY-MM-DD). Only used when "Date shown" is set to Custom date range.

## `endDate` (type: `string`):

Last day of the custom range (YYYY-MM-DD). Only used when "Date shown" is set to Custom date range.

## `minImpressions` (type: `integer`):

Only ads with at least this many impressions, in THOUSANDS — e.g. 50 means 50,000+ impressions. Leave empty for no minimum.

## `maxImpressions` (type: `integer`):

Only ads with at most this many impressions, in THOUSANDS — e.g. 100 means up to 100,000 impressions. Leave empty for no maximum.

## `maxAds` (type: `integer`):

How many ads to collect for each search combination (each company gets its own search). Does not limit the ad URLs list above.

## `scrapeAdDetails` (type: `boolean`):

Open every search-result ad's detail page to add the deep fields: ad format, untruncated ad copy, who paid for the ad, and — for ads shown in the EU — exact run dates, total impressions, per-country impression share and full targeting parameters. One extra page fetch per ad, and each detailed ad additionally bills one "full-ad-details" event. Ads from the URL list above are always scraped in full.

## `proxyCountry` (type: `string`):

Two-letter country code for the request IP. Supported: us, gb, ca, au, de, fr, es, it, nl, ie, in, sg — any other code falls back to "us" (with a warning in the log). Leave as "us" unless you need the pages served from a specific country.

## `maxConcurrency` (type: `integer`):

How many ad pages to scrape in parallel.

## Actor input object example

```json
{
  "adUrls": [],
  "keyword": "crm software",
  "companies": [],
  "payer": "",
  "accountOwner": "",
  "countries": [],
  "dateOption": "",
  "startDate": "",
  "endDate": "",
  "maxAds": 25,
  "scrapeAdDetails": false,
  "proxyCountry": "us",
  "maxConcurrency": 3
}
```

# Actor output Schema

## `dataset` (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 = {
    "keyword": "crm software",
    "maxAds": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/ultimate-linkedin-ad-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 = {
    "keyword": "crm software",
    "maxAds": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/ultimate-linkedin-ad-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 '{
  "keyword": "crm software",
  "maxAds": 25
}' |
apify call apt_marble/ultimate-linkedin-ad-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Ultimate LinkedIn Ad Scraper",
        "description": "Extract LinkedIn ads at scale: search the Ad Library by keyword, advertiser or country, or scrape specific ad URLs. Export ad creatives, copy, impressions by country, run dates and audience targeting to JSON or CSV. No login required.",
        "version": "0.0",
        "x-build-id": "p5cPNBdWLVjaKMr4m"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/apt_marble~ultimate-linkedin-ad-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-apt_marble-ultimate-linkedin-ad-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/apt_marble~ultimate-linkedin-ad-scraper/runs": {
            "post": {
                "operationId": "runs-sync-apt_marble-ultimate-linkedin-ad-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/apt_marble~ultimate-linkedin-ad-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-apt_marble-ultimate-linkedin-ad-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",
                "properties": {
                    "adUrls": {
                        "title": "Ad URLs or ad IDs",
                        "maxItems": 1000,
                        "type": "array",
                        "description": "Specific Ad Library ads to scrape in full — one entry per line. Each entry is either a full detail URL like https://www.linkedin.com/ad-library/detail/1513233886 (query strings and trailing slashes are fine) or a bare numeric ad id like 1513233886. Every ad listed here returns a complete detail record. Optional — leave empty to only search. Up to 1000 ads per run; invalid entries are skipped with a warning.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "keyword": {
                        "title": "Keyword",
                        "type": "string",
                        "description": "Free-text search across ad content. To search, provide at least one of: keyword, companies, payer or account owner."
                    },
                    "companies": {
                        "title": "Companies",
                        "maxItems": 50,
                        "type": "array",
                        "description": "Filter to ads from specific advertisers. Accepts company names (\"Microsoft\"), numeric LinkedIn company IDs (\"1035\") or company page URLs (\"https://www.linkedin.com/company/microsoft\"). Names and URLs are resolved to the official company automatically; each company gets its own search with the other filters applied. At most 50 companies per run.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "payer": {
                        "title": "Paid by (payer)",
                        "type": "string",
                        "description": "Filter by the entity that paid for the ad — the \"Paid for by\" name shown on each ad.",
                        "default": ""
                    },
                    "accountOwner": {
                        "title": "Account owner",
                        "type": "string",
                        "description": "Filter by the owner of the ad account that ran the ad.",
                        "default": ""
                    },
                    "countries": {
                        "title": "Countries",
                        "type": "array",
                        "description": "Only ads shown in these countries. Two-letter ISO codes like \"US\", \"FR\", \"DE\" — or \"ALL\" for worldwide. Leave empty for no country filter.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "dateOption": {
                        "title": "Date shown",
                        "enum": [
                            "",
                            "last-30-days",
                            "current-month",
                            "current-year",
                            "last-year",
                            "custom-date-range"
                        ],
                        "type": "string",
                        "description": "Only ads that ran within this window.",
                        "default": ""
                    },
                    "startDate": {
                        "title": "Start date",
                        "type": "string",
                        "description": "First day of the custom range (YYYY-MM-DD). Only used when \"Date shown\" is set to Custom date range.",
                        "default": ""
                    },
                    "endDate": {
                        "title": "End date",
                        "type": "string",
                        "description": "Last day of the custom range (YYYY-MM-DD). Only used when \"Date shown\" is set to Custom date range.",
                        "default": ""
                    },
                    "minImpressions": {
                        "title": "Minimum impressions (thousands)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Only ads with at least this many impressions, in THOUSANDS — e.g. 50 means 50,000+ impressions. Leave empty for no minimum."
                    },
                    "maxImpressions": {
                        "title": "Maximum impressions (thousands)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Only ads with at most this many impressions, in THOUSANDS — e.g. 100 means up to 100,000 impressions. Leave empty for no maximum."
                    },
                    "maxAds": {
                        "title": "Maximum ads per search",
                        "minimum": 0,
                        "maximum": 2000,
                        "type": "integer",
                        "description": "How many ads to collect for each search combination (each company gets its own search). Does not limit the ad URLs list above.",
                        "default": 50
                    },
                    "scrapeAdDetails": {
                        "title": "Scrape full ad details",
                        "type": "boolean",
                        "description": "Open every search-result ad's detail page to add the deep fields: ad format, untruncated ad copy, who paid for the ad, and — for ads shown in the EU — exact run dates, total impressions, per-country impression share and full targeting parameters. One extra page fetch per ad, and each detailed ad additionally bills one \"full-ad-details\" event. Ads from the URL list above are always scraped in full.",
                        "default": false
                    },
                    "proxyCountry": {
                        "title": "IP country (advanced)",
                        "type": "string",
                        "description": "Two-letter country code for the request IP. Supported: us, gb, ca, au, de, fr, es, it, nl, ie, in, sg — any other code falls back to \"us\" (with a warning in the log). Leave as \"us\" unless you need the pages served from a specific country.",
                        "default": "us"
                    },
                    "maxConcurrency": {
                        "title": "Concurrency (advanced)",
                        "minimum": 1,
                        "maximum": 5,
                        "type": "integer",
                        "description": "How many ad pages to scrape in parallel.",
                        "default": 3
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
