# LinkedIn Ad Tracker — Competitor Ads, Spend & Targeting (`foxlabs/linkedin-ad-tracker`) Actor

Track every ad a company runs on LinkedIn's public Ad Library — creative copy, payer, run dates, EU/DSA impressions + per-country split + targeting — enriched with the advertiser's real firmographics (domain, industry, size). Breaks the 24-ad limit. No login.

- **URL**: https://apify.com/foxlabs/linkedin-ad-tracker.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Marketing, Business
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 results

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 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

<p align="center"><a href="https://apify.com/foxlabs/linkedin-ad-tracker"><img src="https://data.foxlabs.com.tr/img/linkedin-ad-tracker-banner.svg" alt="LinkedIn Ad Tracker" width="100%" /></a></p>

## LinkedIn Ad Tracker — Competitor Ads, Spend Signals & Targeting

**Name a competitor and get every ad they run on LinkedIn.** Feed in a company name, LinkedIn company URL or numeric id, and this Actor pulls that advertiser's ads from the **public LinkedIn Ad Library** (no login) — creative copy, the paying entity, run dates, EU/DSA impression ranges, per-country split and targeting — then enriches each advertiser with **real firmographics** (domain, industry, employee count, HQ, followers) that no other ad-library scraper attaches.

- 🎯 **You name the companies, it returns their ads** — this is a *tracker* for advertisers you already know (its sibling, **[LinkedIn Ad Discovery](https://apify.com/foxlabs/linkedin-ad-discovery)**, does the reverse: a keyword in → who's advertising on it).
- 🚀 **Breaks the ~24-ads-per-query ceiling** — LinkedIn caps public search at ~24 ads; this fans out across date × country facets and de-duplicates (**6×+ in testing**) toward your cap.
- 🏢 **Advertiser resolved to a real company** — domain, industry, size, HQ and followers fused onto every ad, turning an ad dump into competitive *company* intelligence.
- ⚡ **One run → one flat dataset** (JSON / CSV / Excel / API), cookieless, residential-proxied.

### Quick start (API)

Track two competitors' last-30-days ads in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/foxlabs~linkedin-ad-tracker/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "advertisers": ["HubSpot", "https://www.linkedin.com/company/salesforce/"],
        "dateRange": "last-30-days",
        "maxAdsPerAdvertiser": 150
      }'
```

Prefer no code? Open the **Input** tab, list your advertisers, set the options, and click **Start** — then download the results.

### What you get

One clean, flat record per ad:

| Field | Type | Description |
|---|---|---|
| `adId` | string | LinkedIn Ad Library ad identifier |
| `adUrl` | string | Direct link to the ad's public detail page |
| `advertiserName` | string | Advertiser — resolved company name, or the name you supplied |
| `advertiserCompanyId` | string | Advertiser's numeric LinkedIn company id |
| `advertiserUrl` | string | Advertiser's LinkedIn company URL (numeric-id form) |
| `paidBy` | string | The funding/paying entity ("Paid for by …") — often an agency ≠ the advertiser |
| `headline` | string | Ad headline |
| `body` | string | Ad creative copy (commentary text) |
| `cta` | string | Call-to-action button label (best-effort) |
| `clickUrl` | string | Ad destination URL (best-effort; omitted when login-gated) |
| `format` | string | `image` or `video` — derived from the creative itself, so a text-only ad is left blank rather than called an image |
| `imageUrl` | string | The **ad creative** image (or the video's still). Omitted when the ad has no creative — it is never back-filled with the advertiser's logo |
| `advertiserLogoUrl` | string | The advertiser's logo shown beside the ad — useful, but not the creative, so it has its own field |
| `availabilityStart` | string | First date the ad ran (e.g. `Mar 3, 2026`) |
| `availabilityEnd` | string | Last date the ad ran |
| `adDurationDays` | number | Days between start and end |
| `impressions` | string | EU/DSA impression **range** (e.g. `10k-50k`) — ranges, never exact counts |
| `impressionsPerCountry` | array | Per-country impression split, `[{ country, share }]` (EU/DSA) |
| `targetingLanguage` | string | Targeting language(s) (EU/DSA) |
| `targetingLocation` | string | Targeting location(s) (EU/DSA) |
| `dateRangeVerified` | boolean | Only when a date range is requested. `true` — LinkedIn published run dates for this ad and they fall inside your window. `false` — LinkedIn publishes **no** dates for this ad (Spotlight ads carry none), so it passed LinkedIn's own filter but could not be checked here. It is never silently claimed as in-window |
| `advertiserCompany` | object | Firmographics `{ domain, industry, employeeCount, hq, followers, website }` (when enrichment is on). Present only when it actually holds firmographics — never an empty object standing in for "enriched" |
| `sources` | object | Provenance `{ ad, firmographics }` |
| `scrapedAt` | string | ISO timestamp of collection |

Fields are only present when public data exists for them — missing values are omitted rather than guessed.

#### Sample output

```json
{
  "adId": "526342715",
  "adUrl": "https://www.linkedin.com/ad-library/detail/526342715",
  "advertiserName": "HubSpot",
  "advertiserCompanyId": "68529",
  "advertiserUrl": "https://www.linkedin.com/company/68529",
  "paidBy": "HubSpot, Inc.",
  "headline": "The AI-powered customer platform",
  "body": "Marketing, sales and service software that helps your business grow without compromise. Get a demo today.",
  "cta": "Learn more",
  "clickUrl": "https://www.hubspot.com/products/crm",
  "format": "image",
  "imageUrl": "https://media.licdn.com/dms/image/v2/D4D10AQ.../image-shrink_1280/0/…",
  "advertiserLogoUrl": "https://media.licdn.com/dms/image/v2/D560BAQ.../company-logo_100_100/0/…",
  "dateRangeVerified": true,
  "availabilityStart": "Mar 3, 2026",
  "availabilityEnd": "Apr 12, 2026",
  "adDurationDays": 40,
  "impressions": "10k-50k",
  "impressionsPerCountry": [
    { "country": "Germany", "share": "34%" },
    { "country": "France", "share": "21%" },
    { "country": "Netherlands", "share": "8%" },
    { "country": "Ireland", "share": "<1%" }
  ],
  "targetingLanguage": "English",
  "targetingLocation": "Germany, France, Netherlands, Ireland",
  "advertiserCompany": {
    "domain": "hubspot.com",
    "industry": "Software Development",
    "employeeCount": 11000,
    "hq": "Cambridge, Massachusetts, US",
    "followers": 2500000,
    "website": "https://www.hubspot.com"
  },
  "sources": {
    "ad": "LinkedIn Ad Library (public)",
    "firmographics": "LinkedIn company page"
  },
  "scrapedAt": "2026-07-05T09:12:44.201Z"
}
```

*Illustrative record — field shapes are real (as emitted by the Actor); the values are an example.*

### Input & filters

- **Advertisers** — the companies to track. Each entry is a **name** (`Salesforce`), a **LinkedIn company URL** (`https://www.linkedin.com/company/hubspot/`) or a **numeric company id**. A URL or id resolves exactly; a plain name is a best-effort guess at the handle, checked against the company page's own name so a namesake can't be enriched by mistake — if nothing matches, the ads still come back, without firmographics. **Pass the company URL when you can.**
- **Date range** — `all` (full ~1-year retention), `last-7-days`, `last-30-days`, `last-90-days`, `current-month`, `current-year` or `last-year`. LinkedIn's Ad Library natively supports the last four; `last-7-days` and `last-90-days` are served by the nearest native filter and narrowed exactly here, with each ad's status recorded in `dateRangeVerified`.
- **Max ads per advertiser** — cap on ads collected per advertiser (1–1000). The facet fan-out works toward this cap to beat LinkedIn's ~24-per-query limit.
- **Enrich advertiser firmographics** — on by default; resolves each advertiser to its real company page and attaches `advertiserCompany`. Turn off for a faster, ads-only run.
- **Proxy** — residential proxy is the recommended default; LinkedIn rate-limits and blocks un-proxied and datacenter traffic.

### Example inputs (copy & paste)

```jsonc
// 1) Monitor two competitors' recent ads, enriched
{ "advertisers": ["Salesforce", "HubSpot"], "dateRange": "last-30-days", "maxAdsPerAdvertiser": 150 }

// 2) Deep pull for a single advertiser (exact match via company URL)
{ "advertisers": ["https://www.linkedin.com/company/hubspot/"], "dateRange": "all", "maxAdsPerAdvertiser": 1000 }

// 3) Fast creative swipe file — many advertisers, no enrichment
{ "advertisers": ["Notion", "Airtable", "Asana"], "enrichAdvertiser": false, "maxAdsPerAdvertiser": 50 }

// 4) Change detection — only the last 7 days of new creatives
{ "advertisers": ["Adobe", "Canva", "Figma"], "dateRange": "last-7-days", "maxAdsPerAdvertiser": 100 }

// 5) Quarterly campaign review — last 90 days, enriched firmographics
{ "advertisers": ["Snowflake", "Databricks"], "dateRange": "last-90-days", "maxAdsPerAdvertiser": 300 }

// 6) Pin an advertiser by numeric LinkedIn company id (avoids name-match ambiguity)
{ "advertisers": ["1035"], "dateRange": "all", "maxAdsPerAdvertiser": 200 }

// 7) Category watchlist — several advertisers, enriched, recent window
{ "advertisers": ["Zoom", "Slack", "Miro", "Loom", "Notion"], "enrichAdvertiser": true, "dateRange": "last-30-days", "maxAdsPerAdvertiser": 120 }
```

### Use cases

- **Competitive intelligence.** Track a rival's `advertisers` list on a schedule and see exactly what they promote, to whom (`targetingLocation`/`targetingLanguage`), since when (`availabilityStart`), and how heavily (`impressions` range + `adDurationDays`) — no manual library browsing.
- **Creative & messaging research.** Build a swipe file of live B2B ad copy: pull `body`, `headline`, `cta`, `format` and `imageUrl` across the players in your category to see which angles are running.
- **Agency & spend-signal monitoring.** `paidBy` reveals the funding entity (often the agency behind the campaign); combine it with impression ranges and flight length as directional spend signals.
- **Campaign change detection.** Re-run on a cadence and diff the dataset to catch new creatives, paused ads (an `availabilityEnd` appears) or a ramp in impression ranges.
- **Sales & ABM context.** Before you reach out, see what a target account is currently advertising — and use the fused `advertiserCompany` firmographics (domain, industry, size, HQ) to prioritise.
- **Category & market research.** Compare several companies' ad strategies side by side — volume, formats, geographies and messaging — to size a category's ad activity.

### Performance & throughput

Each advertiser is resolved once (a single company-page fetch for firmographics), then its ads are collected with a **two-phase, adaptive facet fan-out** and pulled at concurrency 12. Small and medium advertisers finish in a **single cheap pass** (base + date facets); only advertisers that actually hit LinkedIn's ~24-per-query cap trigger the wider EU-first country fan-out — so you don't pay for fan-out you don't need. Runs use a **residential proxy** (the recommended default) to avoid LinkedIn's rate-limiting. Throughput scales with your Apify plan and is bounded by `maxAdsPerAdvertiser`; there is no login, cookie or API key.

### Integrations

**JavaScript** (`apify-client`):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('foxlabs/linkedin-ad-tracker').call({
  advertisers: ['Salesforce', 'HubSpot'], dateRange: 'last-30-days', maxAdsPerAdvertiser: 150,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**Python** (`apify-client`):

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("foxlabs/linkedin-ad-tracker").call(run_input={
    "advertisers": ["Salesforce", "HubSpot"], "dateRange": "last-30-days", "maxAdsPerAdvertiser": 150,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["advertiserName"], item.get("headline"), item.get("impressions"))
```

Also works with **Make / n8n / Zapier** (Apify app → run this Actor, map the input), scheduled runs, webhooks, and the **Apify MCP server** for AI agents that need live competitor-ad data.

### Data quality (honest)

- **Ad copy, payer, formats, dates and the ad URL** come straight from each ad's public detail page — read, never fabricated. Missing values are omitted, not guessed.
- **EU/DSA fields are conditional.** `impressions`, `impressionsPerCountry` and the two `targeting*` fields exist **only for ads served in the EU** (DSA transparency). For a global advertiser only a fraction of ads carry them — **≈20–25% in testing** — while EU-served ads fill them reliably. This is a structural property of the library that every scraper shares, not a gap unique to this tool.
- **Beyond 24 per query.** LinkedIn's public search returns ~24 ads per query; the facet fan-out pulls **many multiples (6×+ in testing)** toward `maxAdsPerAdvertiser`. What's dropped for very large advertisers is bounded by your cap, not silently truncated at 24.
- **`cta` / `clickUrl` are best-effort** — LinkedIn often routes the click through a login-gated redirect, so the public destination isn't always available; when it isn't, the field is omitted.
- **Firmographics** are attached only when the advertiser's public company page resolves; otherwise the ad still carries its advertiser name and id.

No invented fill-rate percentages are claimed here — the figures above (≈20–25% EU-served, 6×+ fan-out) are the observations recorded during development.

### Pricing

**Pay per result** — you're billed per ad record returned. Advertiser enrichment reuses a **single company-page fetch per advertiser**, so it adds firmographic intelligence without a per-ad cost. There's an Apify **free tier** to evaluate the full feature set before you scale. Runs use Apify's residential proxy (included in your Apify usage).

### FAQ

**How is this different from LinkedIn Ad Discovery?** This is a **tracker**: you supply the companies (`advertisers`) and it returns **their** ads. **[LinkedIn Ad Discovery](https://apify.com/foxlabs/linkedin-ad-discovery)** does the reverse — a keyword in → the list of companies advertising on that topic. Use Discovery to *find* advertisers, this Actor to *monitor* ones you already know.

**Do I need a LinkedIn login or API key?** No. It reads only the **public** LinkedIn Ad Library — cookieless, no login, no key.

**Does it include ad spend?** No. LinkedIn does not publish spend anywhere in the library, and no tool can source it. You get spend **signals**: the paying entity (`paidBy`), EU/DSA **impression ranges** and flight duration (`adDurationDays`).

**How does it get more than 24 ads per advertiser?** LinkedIn's public search caps ~24 ads per query. The Actor fans out across date × country facets and de-duplicates the ad ids (6×+ in testing) up to `maxAdsPerAdvertiser`.

**Why do some ads have no impressions or targeting?** Those are **EU/DSA** transparency fields; LinkedIn only publishes them for ads **served in the EU**. Non-EU ads simply omit them.

**How do I identify an advertiser reliably?** Pass a **LinkedIn company URL** or **numeric id** for an exact match. A plain name is slugified as a best-effort guess and may not resolve to the right page.

**How fresh is the data, and how far back does it go?** Each run reads the library live. LinkedIn retains ads in the library for **~1 year**; older ads aren't available.

**What export formats are available?** JSON, CSV, Excel, or via the Apify API/integrations.

**How many ads can I collect per advertiser?** Up to **1,000** via `maxAdsPerAdvertiser`.

**Can I use this commercially?** It's public ad-library data, but **you** are responsible for complying with LinkedIn's Terms of Service and applicable law (see the legal note below). Use it for legitimate competitive research and B2B intelligence.

### Troubleshooting

- **0 ads for an advertiser** → either they've run no ads within the retention/date window, or a plain-name input didn't resolve to their company page. Pass the **LinkedIn company URL** or **numeric id** instead.
- **`impressions` / per-country / `targeting*` are empty** → those are **EU-only** DSA fields; expect them on only a fraction of a global advertiser's ads. This is by design, not a miss.
- **No `advertiserCompany` block** → the public company page couldn't be resolved, or the page a plain name resolved to belongs to a *different* company of the same name and was rejected rather than attached (numeric-id pages are login-walled; only the `/company/<slug>/` page serves public firmographics). Supply the slug URL — e.g. `https://www.linkedin.com/company/ge/` rather than `General Electric` — and it resolves exactly.
- **Blocked, slow or empty results** → keep the **residential proxy** on (the default). Un-proxied or datacenter traffic is rate-limited and blocked by LinkedIn.

### Notes, limits & legal (honest)

- **Public data only.** The Actor reads the public LinkedIn Ad Library and public company pages — no login, no cookies, no private or gated data.
- **LinkedIn ToS & your responsibility.** LinkedIn does not offer an official public Ad Library API, and its Terms restrict automated access. **You are responsible** for how you use this Actor and its output, and for compliance with LinkedIn's Terms and applicable law — including data-protection law where ad copy or firmographics contain personal data.
- **Not affiliated.** Not affiliated with, endorsed by, or sponsored by LinkedIn Corporation; "LinkedIn" is a trademark of its respective owner.
- **Impression ranges, not spend.** LinkedIn exposes impression **ranges** (e.g. `10k-50k`) for EU-served ads and never publishes spend or exact counts. No tool can source those from this library.
- **EU/DSA conditionality.** Impressions, per-country split and targeting exist only for ads served in the EU (DSA transparency), so they appear on a subset of ads.
- **Best-effort fields.** `cta` and `clickUrl` are taken from the public creative when present; login-gated redirect destinations are omitted rather than guessed.
- **Retention & completeness.** The library retains ads ~1 year; coverage is broad but not guaranteed-complete for very large advertisers — what's dropped is bounded by `maxAdsPerAdvertiser`.

### Support

Questions, a field you'd like added, or a custom build? Open the **Issues** tab on this Actor, or email **info@foxlabs.com.tr**. We reply fast.

*If this Actor saves you time, a ⭐ review really helps.*

### Changelog

#### 0.4 — 2026-07-28

- **The date range is now actually applied.** Two things broke it. LinkedIn only honours the `dateOption` values `last-30-days`, `current-month`, `current-year` and `last-year`; the Actor was sending `last-24-hours`, `last-7-days` and `last-90-days`, which LinkedIn ignores — so it served the **unfiltered** set. On top of that, the collector always fired one bare, date-less query and never date-scoped its country fan-out. Measured on a live advertiser: the old country fan-out pulled **343 ads, 183 of them (53%) outside the requested 30-day window** — sampled leakers had finished running six weeks earlier. Every query is now date-scoped, and each ad is re-checked against the exact window on our side (**0 of 111 dated ads out of window** across a 192-ad verification run).
- **`dateRangeVerified` on every windowed row.** LinkedIn publishes run dates for only some ad types; those ads are checked and marked `true`, while ads it dates not at all are kept and marked `false` rather than quietly presented as in-window. New date-range choices **This month / This year / Last year** match LinkedIn's own filter exactly.
- **Advertiser enrichment lands far more often — and never on the wrong company.** A plain advertiser name was turned into a handle by deleting every separator, so `Palo Alto Networks` became `paloaltonetworks` (a dead page) and no row of that advertiser got firmographics; worse, `General Electric` landed on an unrelated same-named page whose data was then stamped onto every ad row. The Actor now tries LinkedIn's real handle conventions and accepts a page only if its own name matches the advertiser you asked for. Measured: *Palo Alto Networks* 0/8 → **8/8** rows enriched, *Red Bull* 0/8 → **8/8**, and *General Electric* correctly returns **no** firmographics instead of another company's.
- **Hyphenated company handles work again.** Handles like `red-bull` or `palo-alto-networks` produced an invalid proxy session id; the resulting error was swallowed by the retry loop, so the page silently came back empty and the advertiser lost its firmographics with nothing in the log. Handles are now sanitised before use.
- **`imageUrl` is the ad creative.** It used to return whatever image appeared first inside the ad preview — the advertiser's logo, a poster's avatar or a static UI icon on **20/20** sampled rows, never the creative. It now reads the creative element only (**0/79** logos in a 192-ad run), the logo moved to the new `advertiserLogoUrl`, and `format` follows the same signal so a text-only ad is no longer labelled `image`.
- **No more "…see more" in the copy.** LinkedIn's truncation button was being read as part of the ad text and ended **13/20** sampled `body` values. Stripped from `body` and `headline` (0/192 after).
- **An advertiser with no ads no longer ends the run.** The loop used to `break` on the first empty advertiser, silently skipping every advertiser after it.
- **`advertiserCompany` is never an empty object.** When a company page carried no firmographics the field was still emitted as `{}`, which reads as "enriched" to anything checking whether the key exists.

#### 0.3 — 2026-07-16

- **Ultimate beneficial owner (KYB).** Each advertiser is now resolved to its ultimate beneficial owner — a real person or a cross-border parent — by walking UK holding-company ownership chains (Companies House PSC + GLEIF). See who *really* controls the companies running these ads (e.g. an advertiser owned via layers by a US or private-equity parent). Confidence-gated: the owner is attached only when resolved with high confidence, never guessed. New `includeOwnership` option (on by default). No other ad-library scraper links ads to real ownership.

#### 0.2 — 2026-07-05

- Reworked docs: API quick-start, full field table, real-shape sample output, integration snippets (JS/Python/Make/MCP), FAQ, troubleshooting, and honest EU/DSA + legal notes.
- Faster runs — higher concurrency and an adaptive facet fan-out (small advertisers finish in one cheap pass; only large advertisers trigger the full country fan-out).

#### 0.1

- Initial release. Facet fan-out beyond the 24-ad cap; full ad records (creative, payer, dates, EU/DSA impressions + per-country + targeting) with inline advertiser firmographic enrichment + provenance. Cookieless.

***

Part of the **[foXLabs data platform](https://data.foxlabs.com.tr/)** — public-data company, jobs, ads, procurement & AI-search intelligence scrapers. Browse the full suite at **[data.foxlabs.com.tr](https://data.foxlabs.com.tr/)**.

# Actor input Schema

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

Companies whose ads you want to track — a name, a LinkedIn company URL, or a numeric company id. E.g. "Salesforce", "https://www.linkedin.com/company/hubspot/".

## `dateRange` (type: `string`):

Restrict to ads shown within this window, or All for the full ~1-year retention. LinkedIn's Ad Library natively offers Last 30 days / This month / This year / Last year; the 7- and 90-day windows are served by the nearest native filter and then narrowed exactly on our side, so ads LinkedIn publishes no run dates for are kept and flagged `dateRangeVerified: false`.

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

Cap on ads collected per advertiser (1–1000). Facet fan-out breaks LinkedIn's ~24-per-query limit to reach this.

## `maxResults` (type: `integer`):

Maximum number of ad records to push across all advertisers combined — helps control cost. Set 0 for unlimited.

## `enrichAdvertiser` (type: `boolean`):

Resolve each advertiser to its real company and attach firmographics (domain, industry, employee count, HQ, followers). On by default — this is what no other ad-library scraper provides.

## `includeOwnership` (type: `boolean`):

Resolve each advertiser to its ULTIMATE beneficial owner — a real person or a cross-border parent company — by walking UK holding-company ownership chains (Companies House PSC + GLEIF). E.g. an advertiser owned via layers by a US or PE parent. Confidence-gated: the owner is only attached when it can be resolved with high confidence (never guessed). Requires advertiser enrichment.

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

Proxy configuration. Residential proxy (the default) is strongly recommended — LinkedIn rate-limits and blocks un-proxied and datacenter traffic.

## Actor input object example

```json
{
  "advertisers": [
    "Salesforce",
    "HubSpot"
  ],
  "dateRange": "all",
  "maxAdsPerAdvertiser": 150,
  "maxResults": 500,
  "enrichAdvertiser": true,
  "includeOwnership": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "advertisers": [
        "Salesforce",
        "HubSpot"
    ],
    "maxAdsPerAdvertiser": 150,
    "maxResults": 500,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/linkedin-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": [
        "Salesforce",
        "HubSpot",
    ],
    "maxAdsPerAdvertiser": 150,
    "maxResults": 500,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/linkedin-ad-tracker").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 '{
  "advertisers": [
    "Salesforce",
    "HubSpot"
  ],
  "maxAdsPerAdvertiser": 150,
  "maxResults": 500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call foxlabs/linkedin-ad-tracker --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/oVdRYc3mpEM4edYjK/builds/AGRJ91NU6zey75yWC/openapi.json
