# LinkedIn Ad Library Scraper - Impressions, Targeting & Dates (`topapi/linkedin-ad-library-scraper`) Actor

Scrape the LinkedIn Ad Library by keyword, advertiser, payer, country and date: ad copy, format, media, landing URL, CTA, paid-for-by, run dates, total impressions, impressions by country and targeting. Monitor new ads. No login, no cookies.

- **URL**: https://apify.com/topapi/linkedin-ad-library-scraper.md
- **Developed by:** [Top Api](https://apify.com/topapi) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.47 / 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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does LinkedIn Ad Library Scraper do?

**LinkedIn Ad Library Scraper** extracts ads from the public [LinkedIn Ad Library](https://www.linkedin.com/ad-library/home) and turns any keyword, advertiser, payer or Ad Library URL into structured rows: one ad per row with the advertiser, format, ad copy and media. **Ad details** (on by default) opens each ad's detail page and adds the full copy, call-to-action labels, "Paid for by", creative variants, the landing URL (the CTA link on ads shown in the EU; on other ads, a link from the ad copy with short links followed) and, for ads shown in the EU, run dates, total impressions, impressions by country and targeting. Built for B2B marketers, agencies and competitive-intelligence teams who track what competitors run on LinkedIn.

LinkedIn Ads Scraper - LinkedIn Ad Library API - LinkedIn ads spy tool

<a href="https://console.apify.com/actors/2Tig6P4frVu4NC07D"><img src="https://api.apify.com/v2/key-value-stores/qNQ9zP0xaj2yxGeMY/records/linkedin-ad-library-scraper-source.png" alt="LinkedIn Ad Library search page that LinkedIn Ad Library Scraper turns into rows" style="width:70%"></a>

- **Every ad format:** single image, video, carousel, document, event, message / InMail, text, article, job and follow-company ads, each tagged with a `format` value; thought-leader ads (a member's post run as an ad) carry `isThoughtLeaderAd: true`
- **Impressions and targeting:** total impressions range (`50k-100k`) with numeric bounds, impressions by country, run dates, languages, locations and the six targeting parameters, for ads shown in the EU
- **Landing URLs outside the EU too:** LinkedIn shows the CTA link only on ads shown in the EU; for other ads `landingUrl` comes from the links in the ad copy, with lnkd.in, bit.ly, hubs.la and other short links followed to the page they open (`landingUrlSource` says which)
- **All of LinkedIn's filters:** country, date range, impressions range, targeting categories and sort order, plus many searches in one run
- **No login, cookies or LinkedIn account:** the actor reads the public Ad Library
- **Monitoring:** only new ads since the last run; with Ad details on, optionally the known ads whose impressions or end date changed
- **Export anywhere:** JSON, CSV, Excel, XML; schedules, webhooks, Make, Zapier, n8n, Google Sheets, Slack
- **Works with AI agents:** call it through the Apify API or the MCP server; `compact` output keeps rows small

### What data can you get from the LinkedIn Ad Library?

<table>
<tr>
<td>🆔 Ad id and Ad Library URL</td>
<td>🏢 Advertiser, logo, company id</td>
<td>💳 Paid for by (`paidBy`)</td>
</tr>
<tr>
<td>🖼️ Format and creative type</td>
<td>📝 Headline and full ad copy</td>
<td>🔘 CTA labels (`ctas`)</td>
</tr>
<tr>
<td>🔗 Landing URL with UTM tags (`landingUrl`)</td>
<td>🌐 Landing domain</td>
<td>🎞️ Image, video MP4, carousel cards</td>
</tr>
<tr>
<td>📄 Document title and page count</td>
<td>📅 Event name, time, location</td>
<td>✉️ Message sender and buttons</td>
</tr>
<tr>
<td>📆 Run dates and days running</td>
<td>👁️ Total impressions (`impressions`)</td>
<td>🌍 Impressions by country</td>
</tr>
<tr>
<td>🗣️ Targeted languages and locations</td>
<td>🎯 Targeting matrix (Audience, Company, Job ...)</td>
<td>👤 Thought-leader poster and "Promoted by"</td>
</tr>
</table>

On a 100-ad run for "insurance" in Germany, 100 of 100 ads had impressions, run dates and targeting, 67 had a landing URL (55 from the CTA link, 12 from links in the ad copy) and 82 an image. On the same search in the United States, 24 of the 92 ads shown only outside the EU got a landing URL from their ad copy.

### How to scrape the LinkedIn Ad Library with LinkedIn Ad Library Scraper

1. [Create a free Apify account](https://console.apify.com/sign-up) - $5 of monthly credit, no card needed.
2. Open [LinkedIn Ad Library Scraper](https://apify.com/TopApi/linkedin-ad-library-scraper) and click **Try for free**.
3. Type keywords (e.g. `salesforce`) or advertisers (e.g. `HubSpot`), pick countries and a date range, and set **Max ads**.
4. Click **Start**. Ads stream into the dataset as they are found.
5. Open the **Storage** tab and **Export** as JSON, CSV or Excel - or let a schedule run it for you.

### ⬇️ Input

| Parameter | Type | Description |
|---|---|---|
| **🔍 What to scrape** | | At least one of these. Each entry is its own search. |
| `keywords` | List | Words in the ad copy, e.g. `salesforce`, `AI agents`. |
| `advertisers` | List | Company names, slugs or company URLs, e.g. `HubSpot`, `the-home-depot`, `https://www.linkedin.com/company/nvidia/`. |
| `payers` | List | The "Paid for by" entity, e.g. `HubSpot, Inc.`. |
| `startUrls` | List | Ad Library search URLs (all their filters are kept), ad URLs (`/ad-library/detail/<id>`) or company URLs. |
| `adIds` | List | Ad Library ad ids, e.g. `1633495836`. |
| `searches` | JSON | Searches with their own filters: `[{"keyword":"crm","countries":["DE"]},{"advertiser":"HubSpot","maxResults":50}]`. |
| **🎯 Filters** (applied by LinkedIn) | | |
| `countries` | Select | Where the ad was shown (LinkedIn's 249 countries). Default: all. |
| `dateOption` | Select | `all-time`, `last-30-days`, `current-month`, `current-year`, `last-year`, `custom-date-range`. Default: all time. |
| `startDate` / `endDate` | Date | Custom range (YYYY-MM-DD); setting one switches to a custom range. |
| `impressionsMinValue` / `impressionsMinUnit` | Integer / Select | Lower impressions bound, e.g. `100` + `thousand`. |
| `impressionsMaxValue` / `impressionsMaxUnit` | Integer / Select | Upper bound, e.g. `10` + `million`. A missing bound is filled in. |
| `includedTargetingFacetCategories` | Select | Ads whose targeting uses these: Language, Location, Audience, Demographic, Company, Education, Job, Member interests and traits. |
| `excludedTargetingFacetCategories` | Select | Ads whose targeting excludes these. |
| `sortBy` | Select | `newest` (default) or `oldest`. |
| **✂️ Refine results** | | |
| `advertiserMatch` | Select | `contains` (default, LinkedIn's matching) or `exact` (the company itself, no partners). |
| `adFormats` | Select | Keep only these formats. Default: all. |
| `includeEmployerBranding` | Boolean | Off drops job and follow-company ads. Default: `true`. |
| `excludeKeywords` | List | Drop ads whose advertiser, headline or copy contains these words. |
| `onlyWithImpressions` | Boolean | Keep only ads with impressions data. Needs Ad details. |
| `onlyActive` | Boolean | Keep only ads still running (end date today or yesterday). Needs Ad details. |
| `minDaysRunning` | Integer | Keep only ads that ran at least this many days. Needs Ad details. |
| **➕ Extra data** | | |
| `scrapeAdDetails` | Boolean | Open each ad's detail page (full copy, CTAs, "Paid for by", landing URL, EU transparency data) - charged as **ad-details** at $0.00038 per ad. Default: `true`. |
| **🔁 Monitoring** | | |
| `monitorMode` | Boolean | Return only ads not delivered by earlier runs with the same store. |
| `monitorChanges` | Boolean | Also return known ads whose impressions range or end date changed. Needs Ad details. Each poll re-checks the known ads on the pages it reads (the newest 24 per search once nothing new appears). |
| `monitorStoreName` | String | Named store that remembers delivered ads. |
| **📦 Output** | | |
| `maxResults` | Integer | Most ads per run. Default `100`; `0` = no limit. |
| `maxResultsPerSearch` | Integer | Ceiling per search. Default: none. |
| `countOnly` | Boolean | One free row per search with LinkedIn's total ad count, no ads. Ad ids and ad URLs are skipped (not delivered, not charged). |
| `compact` | Boolean | Core fields only, for AI agents and MCP. |
| **⚙️ Advanced** | | |
| `proxyConfiguration` | Proxy | Apify Proxy (default). |
| `maxConcurrency` | Integer | Parallel proxy sessions, 1-8. Default `4`. |
| `requestDelayMs` | Integer | Pause between two requests on one session. Default `2200`. |

#### Input JSON

```json
{
  "keywords": ["crm"],
  "advertisers": ["HubSpot"],
  "countries": ["DE", "FR", "NL"],
  "dateOption": "last-30-days",
  "scrapeAdDetails": true,
  "maxResults": 200,
  "maxResultsPerSearch": 100
}
```

#### Search EU countries when you need impressions and targeting

LinkedIn publishes run dates, impressions and targeting (and the CTA link) only for ads shown in the EU. **Do:** `"countries": ["DE", "FR", "NL"]` · **Don't:** a US-only search when you need impressions - on our 100-ad US-only test, 8 ads carried them.

#### Use advertiser names, not company ids

The Ad Library searches advertisers by name. **Do:** `HubSpot`, `the-home-depot` or `linkedin.com/company/hubspot/` · **Don't:** a numeric id like `68529` (LinkedIn only resolves ids for signed-in members; the run skips it and says so). LinkedIn's name match also returns partners and agencies ("HubSpot Platinum Partner"); set `advertiserMatch` to `exact` to keep the company itself.

### ⬆️ Output

Every ad becomes one dataset item. Here is a run for `crm` in Germany, France and the Netherlands:

<a href="https://console.apify.com/actors/2Tig6P4frVu4NC07D"><img src="https://api.apify.com/v2/key-value-stores/qNQ9zP0xaj2yxGeMY/records/linkedin-ad-library-scraper-output.png" alt="LinkedIn Ad Library Scraper output with impressions, run dates and targeting" style="width:70%"></a>

A real row from an ad shown in the EU (trimmed):

```json
{
  "adId": "1577245703",
  "adLibraryUrl": "https://www.linkedin.com/ad-library/detail/1577245703",
  "format": "SINGLE_IMAGE",
  "advertiserName": "Scania Deutschland",
  "advertiserUrl": "https://www.linkedin.com/company/10953382",
  "paidBy": "Scania Deutschland",
  "headline": "Versichern Sie jetzt Ihren Lkw zu unseren attraktiven Sonderkonditionen!",
  "body": "Schutz für Ihren Leistungsträger – Jetzt zu den Versicherungsexperten von Scania wechseln ...",
  "ctas": ["View Quote"],
  "clickUrl": "https://www.scania.com/de/de/home/services/finance-and-insurance/insurance/scania-versicherungswechsel-2026.html?cid=de_0926_tc_sop_li_6599",
  "landingUrl": "https://www.scania.com/de/de/home/services/finance-and-insurance/insurance/scania-versicherungswechsel-2026.html?cid=de_0926_tc_sop_li_6599",
  "landingUrlSource": "cta",
  "landingDomain": "scania.com",
  "availability": { "start": "2026-10-08", "end": "2026-10-09" },
  "daysRunning": 2,
  "adStatus": "ACTIVE",
  "impressions": "1k-5k",
  "impressionsMin": 1000,
  "impressionsMax": 5000,
  "targetingLanguage": ["English"],
  "targetingLocation": ["Germany"],
  "targetingMatrix": [{ "parameter": "Company", "facet": "COMPANY", "targeted": true, "excluded": true }],
  "detailsFetched": true
}
```

An ad shown only in the United States: no CTA link on LinkedIn's page, so the landing URL comes from the short link in its copy (trimmed):

```json
{
  "adId": "1637885506",
  "format": "VIDEO",
  "advertiserName": "Munich Re Specialty - North America",
  "paidBy": "Munich Reinsurance America, Inc.",
  "ctas": ["Learn more"],
  "clickUrl": null,
  "externalLinksInText": ["https://ow.ly/PRXs50ZVTSW"],
  "copyLinkTargets": [{ "link": "https://ow.ly/PRXs50ZVTSW", "url": "https://www.munichre.com/specialty/north-america/en/solutions/binding-authorities/in-house-underwriting/dealers-open-lot.html?utm_source=linkedin..." }],
  "landingUrl": "https://www.munichre.com/specialty/north-america/en/solutions/binding-authorities/in-house-underwriting/dealers-open-lot.html?utm_source=linkedin...",
  "landingUrlSource": "adCopy",
  "landingDomain": "munichre.com",
  "hasEuTransparency": false
}
```

#### Fields

Fill measured on two runs with Ad details on: EU = 100 ads (`insurance`, Germany); US = 100 ads (`insurance`, United States only; 8 of them were also shown in the EU).

| Field | What it is | EU | US |
|---|---|---|---|
| `adId`, `adLibraryUrl`, `format`, `creativeType` | Ad identity and format | 100% | 100% |
| `advertiserName`, `advertiserLogo` | Advertiser as LinkedIn shows it | 100% | 100% |
| `advertiserUrl` | Advertiser's LinkedIn page | 99% | 99% |
| `paidBy` | "Paid for by" entity | 100% | 100% |
| `body` | Ad copy (full text with details) | 100% | 100% |
| `headline` | Headline / title | 76% | 72% |
| `ctas` | Call-to-action labels | 38% | 61% |
| `clickUrl` | The ad's CTA link (LinkedIn prints it only on ads shown in the EU) | 55% | 8% |
| `landingUrl`, `landingUrlSource` | Landing URL: the CTA link (`cta`), else the first link in the ad copy with short links followed (`adCopy`) | 67% (55 `cta`, 12 `adCopy`) | 32% (8 `cta`, 24 `adCopy`) |
| `landingDomain` | Domain of `landingUrl` (empty when it is a LinkedIn page) | 62% | 31% |
| `externalLinksInText` | Links written in the ad copy, as written | 32% | 25% |
| `copyLinkTargets` | Where the first two copy links lead, `{link, url}` (ads without a CTA link) | 12% | 24% |
| `imageUrl` / `videoUrl` | Main image / video MP4 | 82% / 16% | 88% / 9% |
| `availability`, `daysRunning`, `adStatus` | Run dates (EU-shown ads only) | 100% | 8% |
| `impressions`, `impressionsMin`, `impressionsMax` | Total impressions range | 100% | 8% |
| `impressionsPerCountry` | Share per country (see Limits) | 1% | 0% |
| `targetingLanguage`, `targetingLocation`, `targetingMatrix` | Targeting | 100% | 8% |
| `isThoughtLeaderAd` (true), `posterName` | Thought-leader ad: a member's own post run as an ad | 15% | 19% |
| `promotedBy` | Company that promoted the member's post (empty when the member promoted it) | 6% | 2% |
| `variantsCount` | Number of creative variants | 100% | 100% |
| `variants` | Every variant's creative (only on ads with more than one) | 0% | 0% |
| `carouselCards`, `documentTitle`, `eventName`, `senderName`, `sponsoredMessageButtons` | Per-format fields | when the format has them | |
| `searchType`, `searchValue`, `startUrl`, `totalAdsInSearch`, `position` | Which search found the ad | 100% | 100% |
| `detailsFetched`, `scrapedAt` | Row status | 100% | 100% |
| `changeType`, `previousImpressions`, `previousEndDate` | `new` or `changed`, with the earlier values (`monitorMode` runs only) | monitor runs | monitor runs |

### How can I use LinkedIn ads data?

- 🎯 **Competitive intelligence** - track every ad a competitor runs, with run dates and impression ranges per market
- 📣 **Performance marketers** - collect winning hooks, headlines, CTAs and landing pages across a category
- 🏢 **Agencies** - audit a prospect's LinkedIn advertising before the pitch
- 🔎 **Sales teams** - find companies actively spending on LinkedIn ads (`advertiserName`, `paidBy`, `landingDomain`)
- 🧭 **Market research** - measure advertising pressure by country with `impressionsPerCountry` and targeting
- 🤖 **AI and research teams** - feed ad copy and targeting to an LLM; `compact` keeps rows small
- 📈 **Monitoring** - run it daily with `monitorMode` and get only the new ads

### How much does it cost to scrape the LinkedIn Ad Library?

LinkedIn Ad Library Scraper is pay-per-result: **$0.47 per 1,000 ads** (event `ad`), plus **$0.38 per 1,000 ads with Ad details** (event `ad-details`), so $0.85 per 1,000 fully detailed ads. No start fee. `countOnly` rows are free.

| Ads | Without details | With details |
|---:|---:|---:|
| 10 | $0.0047 | $0.0085 |
| 100 | $0.047 | $0.085 |
| 1,000 | $0.47 | $0.85 |
| 10,000 | $4.70 | $8.50 |

There is nothing else to pay: Apify platform usage is included in these prices. Measured on 256 MB: 100 ads with details in 86-97 s (Germany and United States runs); 1,000 ads without details in 170 s.

### Integrations, API, scheduling & MCP

- **Schedules:** run LinkedIn Ad Library Scraper daily or weekly from the **Schedules** tab; with `monitorMode` each run returns only new ads.
- **Webhooks:** fire a URL when a run finishes - push new ads to Slack, a CRM or a sheet.
- **No-code:** Make, Zapier, n8n, Google Sheets, Airtable, Slack via [Apify integrations](https://docs.apify.com/integrations).
- **API:** call it from Node.js or Python with `apify-client` - code on the [API tab](https://apify.com/TopApi/linkedin-ad-library-scraper/api/python) and the [Apify API reference](https://docs.apify.com/api/v2).
- **MCP / AI agents:** connect Claude, Cursor or any MCP client through the [MCP tab](https://apify.com/TopApi/linkedin-ad-library-scraper/api/mcp) and ask for a company's LinkedIn ads in plain language.

### More scrapers from TopApi

<table>
<tr>
<td>📢 <a href="https://apify.com/TopApi/facebook-search-posts-scraper">Facebook Search Posts Scraper</a></td>
<td>🎵 <a href="https://apify.com/TopApi/tiktok-scraper">TikTok Scraper</a></td>
<td>▶️ <a href="https://apify.com/TopApi/youtube-shorts-scraper">YouTube Shorts Scraper</a></td>
</tr>
<tr>
<td>💼 <a href="https://apify.com/TopApi/indeed-jobs-scraper">Indeed Jobs Scraper</a> - which advertisers are hiring</td>
<td>🎬 <a href="https://apify.com/TopApi/tiktok-video-scraper">TikTok Video Scraper</a></td>
<td>™️ <a href="https://apify.com/TopApi/uspto-trademark-scraper">USPTO Trademark Scraper</a></td>
</tr>
<tr>
<td>⭐ <a href="https://apify.com/TopApi/google-maps-reviews-scraper">Google Maps Reviews Scraper</a></td>
<td>💬 <a href="https://apify.com/TopApi/facebook-comments-scraper">Facebook Comments Scraper</a></td>
<td>🧭 <a href="https://apify.com/TopApi">All TopApi actors</a></td>
</tr>
</table>

### Limits & empty runs

- **EU-only transparency data:** LinkedIn prints run dates, impressions, targeting and the CTA link (`clickUrl`) only for ads shown in the EU. In our runs 212 of 277 EU-shown ads had the CTA link and none of 883 other ads did; their button has no link on LinkedIn's page. For those other ads, `landingUrl` comes from the links written in the ad copy when there are any (`landingUrlSource: "adCopy"`): 24 of 92 ads on our US-only run. A copy link is usually the advertiser's landing page, but it can be any page the ad links to, such as an article, a form or a job post.
- **Impressions by country:** LinkedIn adds the country split later. Ads from the last 30 days showed "may take up to 48 hours to update" (0 of 60); ads from March-June 2026 had it (24 of 24).
- **Depth:** one search (`software`, US) was walked 7,200 ads deep (300 pages) with no duplicates and LinkedIn still offering more; for faster runs, split very broad searches by country or date range.
- **Speed:** LinkedIn allows about one request every 2 seconds per IP, so the actor spreads requests over `maxConcurrency` proxy sessions; more sessions = faster.
- **Empty dataset:** no ads match the search (check the totals with `countOnly`), the advertiser name is spelled differently on LinkedIn, or every ad was filtered out - the run's status message and `RUN_SUMMARY` say which. Nothing is charged for an empty run.
- **Not available:** ad spend (LinkedIn publishes impression ranges, never cost) and anything behind a LinkedIn login.

### FAQ

#### How many ads can LinkedIn Ad Library Scraper collect in one run?

As many as the search returns up to `maxResults` (`0` = no limit). A keyword like `crm` matches 228,378 ads across all countries; we walked 7,200 ads of one search with no duplicates. Use several searches or `searches` entries to cover more.

#### Can I run LinkedIn Ad Library Scraper on a schedule to get only new ads?

Yes. Turn on `monitorMode`, give the run a `monitorStoreName`, save it as a task and add a schedule. Each run returns only ads not delivered before; a poll with nothing new reads one page and charges nothing.

#### Why do some ads have no impressions or targeting?

LinkedIn publishes them only for ads shown in the EU. Search EU countries, or set `onlyWithImpressions` to keep only ads that have them.

#### Why do some LinkedIn ads have no landing URL?

LinkedIn shows the CTA link only on ads shown in the EU. For other ads, LinkedIn Ad Library Scraper takes the landing URL from the links written in the ad copy and follows short links (lnkd.in, bit.ly, hubs.la ...) to the page they open; `landingUrlSource` says `cta` or `adCopy`. An ad whose copy has no link has no landing URL.

#### Does LinkedIn Ad Library Scraper return ad spend?

No. LinkedIn publishes impression ranges (e.g. `50k-100k`) and per-country shares, not cost.

#### Do I need a LinkedIn account, cookies or an API key?

No. LinkedIn Ad Library Scraper reads the public Ad Library without a login.

#### How do I get only one company's ads, not its partners'?

Search the company in `advertisers` and set `advertiserMatch` to `exact`. On a HubSpot search (Germany, France and the Netherlands, last year: 195 ads), `exact` kept HubSpot's own 100 ads, including 14 members' posts that HubSpot promoted and its job and follow-company ads, and dropped 95 ads from partners and agencies such as "Systony - HubSpot Elite Partner".

#### Can I paste a LinkedIn Ad Library URL?

Yes. Paste search URLs into `startUrls` - keyword, advertiser, payer, countries, dates, impressions, targeting and sort are kept - or ad detail URLs and ad ids.

#### Can I use LinkedIn Ad Library Scraper with the Apify API or Python?

Yes. Use `apify-client` for Node.js or Python; the [API tab](https://apify.com/TopApi/linkedin-ad-library-scraper/api/python) shows ready code.

#### Can AI agents use LinkedIn Ad Library Scraper through MCP?

Yes. Connect an MCP client through the [MCP tab](https://apify.com/TopApi/linkedin-ad-library-scraper/api/mcp); set `compact` for small rows.

#### Why did my LinkedIn Ad Library run return fewer ads than Max ads?

The search had fewer matches, your filters removed some, or the run reached your spending limit. `RUN_SUMMARY` lists per search: LinkedIn's total, ads delivered, filtered and duplicates.

#### Your feedback

Found a bug or need a field? Open an issue on the [Issues tab](https://apify.com/TopApi/linkedin-ad-library-scraper/issues/open) and we will look at it.

This actor is not affiliated with, endorsed by or sponsored by LinkedIn Corporation.

# Actor input Schema

## `keywords` (type: `array`):

Words in the ad copy, as typed into the Ad Library's keyword box, e.g. "salesforce", "AI agents", "cybersecurity".

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

Company or advertiser names, slugs or company URLs, e.g. "HubSpot", "the-home-depot", "https://www.linkedin.com/company/nvidia/". LinkedIn matches names that contain the text; see Advertiser match.

## `payers` (type: `array`):

The "Paid for by" entity, e.g. "HubSpot, Inc.". Finds ads an agency or parent company paid for.

## `startUrls` (type: `array`):

Paste Ad Library search URLs (every filter in the URL is kept), ad URLs (/ad-library/detail/<id>) or company URLs.

## `adIds` (type: `array`):

Ad Library ad ids, e.g. "1633495836". Each one is read from its detail page.

## `searches` (type: `array`):

Independent searches, each with its own filters: \[{"keyword":"crm","countries":\["DE"],"dateOption":"last-30-days"},{"advertiser":"HubSpot","maxResults":50}]. Keys: keyword, advertiser, payer, countries, dateOption, startDate, endDate, impressions\*, includedTargetingFacetCategories, excludedTargetingFacetCategories, sortBy, maxResults.

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

Countries where the ad was shown. Ads shown in the EU carry impressions and targeting on their detail page.

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

When the ad ran. "Custom" uses Start and End date below. LinkedIn keeps about a year of history.

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

Custom range start (YYYY-MM-DD or relative, e.g. "30 days"). Setting a date switches to a custom range.

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

Custom range end (YYYY-MM-DD). Empty = today.

## `impressionsMinValue` (type: `integer`):

Lower bound of LinkedIn's impressions filter, with the unit below, e.g. 100 + Thousand.

## `impressionsMinUnit` (type: `string`):

Unit of the lower bound.

## `impressionsMaxValue` (type: `integer`):

Upper bound, e.g. 10 + Million. LinkedIn needs both bounds; a missing one is filled in.

## `impressionsMaxUnit` (type: `string`):

Unit of the upper bound.

## `includedTargetingFacetCategories` (type: `array`):

Only ads whose targeting includes these categories, e.g. Job (titles, functions, seniority).

## `excludedTargetingFacetCategories` (type: `array`):

Only ads whose targeting excludes these categories.

## `sortBy` (type: `string`):

LinkedIn's two sort orders.

## `advertiserMatch` (type: `string`):

For advertiser searches. LinkedIn returns every advertiser whose name contains the text ("HubSpot" also finds "HubSpot Platinum Partner"); Exact keeps the company itself.

## `adFormats` (type: `array`):

Keep only these formats. Empty = all.

## `includeEmployerBranding` (type: `boolean`):

Off = drop employer-brand promos (job ads, follow-company ads) from the results.

## `excludeKeywords` (type: `array`):

Drop ads whose advertiser, headline or copy contains any of these words (case-insensitive), e.g. "webinar".

## `onlyWithImpressions` (type: `boolean`):

Keep only ads whose detail page shows impressions (ads shown in the EU). Needs Ad details on.

## `onlyActive` (type: `boolean`):

Keep only ads whose run dates reach today or yesterday. Run dates exist for ads shown in the EU; needs Ad details on.

## `minDaysRunning` (type: `integer`):

Keep only ads that ran at least this many days (from the run dates on the detail page), e.g. 30 for proven creatives.

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

Open each ad's detail page: full copy, CTA labels, paid-for-by, variants and, for ads shown in the EU, the CTA landing URL, run dates, impressions, impressions by country and targeting. On other ads the landing URL comes from links in the ad copy, with short links followed (landingUrlSource "adCopy"). Charged as "ad-details" per ad.

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

Return only ads not delivered by earlier runs that used the same monitor store. Ads already known are skipped and not charged.

## `monitorChanges` (type: `boolean`):

With monitoring and Ad details on: also deliver known ads whose impressions range or end date changed (with the previous values). Each poll re-checks the known ads on the pages it reads (the newest 24 per search once nothing new appears); unchanged ads are not charged.

## `monitorStoreName` (type: `string`):

Named key-value store that remembers delivered ads. Use one name per tracked set, e.g. "competitor-ads".

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

Most ads for the whole run. 0 = no limit (a broad keyword can match 200,000+ ads).

## `maxResultsPerSearch` (type: `integer`):

Ceiling for each keyword, advertiser, payer or URL, so one large search does not use the whole run. 0 = no per-search limit.

## `countOnly` (type: `boolean`):

Return one free row per search with LinkedIn's total ad count, without the ads. Ad ids and ad URLs are skipped in this mode (not delivered, not charged).

## `compact` (type: `boolean`):

Core fields only, sized for AI agents and MCP clients.

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

Apify Proxy (default, datacenter). Each parallel request stream uses its own proxy session.

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

Proxy sessions working in parallel. LinkedIn allows about one request every 2 seconds per IP, so more sessions = faster.

## `requestDelayMs` (type: `integer`):

Pause between two requests on the same session.

## Actor input object example

```json
{
  "keywords": [
    "salesforce"
  ],
  "dateOption": "all-time",
  "impressionsMinUnit": "thousand",
  "impressionsMaxUnit": "thousand",
  "sortBy": "newest",
  "advertiserMatch": "contains",
  "includeEmployerBranding": true,
  "onlyWithImpressions": false,
  "onlyActive": false,
  "scrapeAdDetails": true,
  "monitorMode": false,
  "monitorChanges": false,
  "monitorStoreName": "linkedin-ad-library-monitor",
  "maxResults": 24,
  "countOnly": false,
  "compact": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 4,
  "requestDelayMs": 2200
}
```

# Actor output Schema

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

One item per LinkedIn ad: advertiser, format, copy, landing URL, media, paid-for-by, run dates, impressions and targeting.

## `summary` (type: `string`):

Per search: LinkedIn's ad count, pages, delivered, filtered and duplicate ads; requests and proxy sessions used.

# 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 = {
    "keywords": [
        "salesforce"
    ],
    "maxResults": 24
};

// Run the Actor and wait for it to finish
const run = await client.actor("topapi/linkedin-ad-library-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 = {
    "keywords": ["salesforce"],
    "maxResults": 24,
}

# Run the Actor and wait for it to finish
run = client.actor("topapi/linkedin-ad-library-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "salesforce"
  ],
  "maxResults": 24
}' |
apify call topapi/linkedin-ad-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,topapi/linkedin-ad-library-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2Tig6P4frVu4NC07D/builds/TEJPkK8cix8c2ZsPd/openapi.json
