# LinkedIn Ad Library Scraper — Ads, Reach & New-Ad Alerts (`brilliant_gum/linkedin-ad-library-scraper`) Actor

Scrape the LinkedIn Ad Library by advertiser or keyword: every ad's creative, copy, format, payer and campaign dates — plus EU impressions and targeting. Tracks which ads are new since your last run. No login, no cookies. Competitor ad intelligence on autopilot.

- **URL**: https://apify.com/brilliant\_gum/linkedin-ad-library-scraper.md
- **Developed by:** [Yuliia Kulakova](https://apify.com/brilliant_gum) (community)
- **Categories:** Lead generation, Automation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.015 / ad scraped

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

## LinkedIn Ad Library Scraper — Ads, Reach & New-Ad Alerts

![LinkedIn Ad Library Scraper](https://i.imgur.com/IVhurgv.png)

Track any company's advertising on LinkedIn. Search the LinkedIn Ad Library by advertiser or keyword and get every ad — creative, copy, format, who paid for it, when it ran, how many impressions it drew and who it was targeted at — plus, on repeat runs, exactly which ads are **new since last time**.

No login, no LinkedIn account, no cookies to paste.

***

### What you get

Point it at company names or keywords:

```json
{
  "advertisers": ["HubSpot", "Salesforce"],
  "keywords": ["crm software"],
  "countries": ["US", "DE"]
}
```

An unedited record from a live EU campaign:

```json
{
  "query": "Salesforce",
  "advertiserName": "Salesforce",
  "adCopy": "Our sales teams in Dublin are growing!",
  "format": "Job Ad",
  "payer": "Salesforce, Inc.",
  "ranFrom": "2026-08-07",
  "ranTo": "2026-08-17",
  "totalImpressions": "1k-5k",
  "impressionsByCountry": [
    { "country": "Ireland", "share": "28%" },
    { "country": "United Kingdom", "share": "14%" },
    { "country": "Germany", "share": "13%" }
  ],
  "targetingLanguage": "Targeting includes English",
  "targetingLocation": "Targeting includes Slovakia, Benelux and 16 others",
  "creativeImageUrl": "https://media.licdn.com/dms/image/...",
  "advertiserLogoUrl": "https://media.licdn.com/dms/image/...",
  "adUrl": "https://www.linkedin.com/ad-library/detail/1537674606",
  "isNewSinceLastRun": true,
  "scrapedAt": "2026-08-17T09:10:00.000Z"
}
```

Every ad also carries its `adId`, the search that found it, and the direct link to LinkedIn's ad-detail page.

It reads **every** ad an advertiser is running, not just the first page — a search for Microsoft returns all 60+ live ads across its accounts in one run, paginated automatically.

***

### Track campaigns, not just ads

With `trackChanges: true` (default), every run remembers each search and the next run tells you what moved:

```
"HubSpot": 84 ads from 4 pages, 6 pushed | 6 new, 3 no longer shown since 2026-08-10
```

- Every ad carries `isNewSinceLastRun`.
- Set `newAdsOnly: true` and a scheduled run delivers only the fresh creatives — a launch-detection feed for the competitors you watch.
- "New" always means new **in that search on LinkedIn**. Changing your filters between runs never relabels old ads as new — the snapshot tracks the advertiser's ads, not your query.

Why it matters: a competitor's new ad is a signal. Marketers read a burst of new creatives as a campaign launch; sales teams read a hiring ad as budget moving; analysts read creative volume as spend direction.

***

### The transparency data others skip

For ads shown in the EU, LinkedIn is legally required to disclose reach and targeting. This scraper pulls all of it into clean fields:

- **`ranFrom` / `ranTo`** — the real campaign window, as ISO dates.
- **`totalImpressions`** — LinkedIn's disclosed range (e.g. `1k-5k`).
- **`impressionsByCountry`** — the full percentage split across every country the ad reached.
- **`targetingLanguage` / `targetingLocation` / `targeting`** — the audience parameters the advertiser actually selected.

This is the closest thing to a competitor's media plan that exists publicly — and most scrapers stop at the ad copy.

***

### Search options

| Input | What it does |
|---|---|
| `advertisers` | Company names as they appear on LinkedIn, or pasted ad-library URLs |
| `keywords` | Free-text searches across all advertisers |
| `countries` | Two-letter codes to filter where ads ran; EU codes unlock the transparency data |
| `dateOption` | `last-30-days`, `current-month`, or all time |
| `includeDetails` | Open each ad for dates, impressions and targeting (richer, a little slower) |
| `advertiserMatch` | `smart` (default) keeps the brand and its sub-brands; `exact` keeps only the exact name (for common-word brands like "Apple"); `off` returns raw results |
| `maxAdsPerQuery` | Cap ads per search |
| `trackChanges` | Remember searches between runs |
| `newAdsOnly` | Return only ads new since the previous run |

A typical competitor-monitoring setup:

```json
{
  "advertisers": ["HubSpot", "Salesforce", "Zoho"],
  "countries": ["US"],
  "trackChanges": true,
  "newAdsOnly": true
}
```

Schedule it daily and the dataset becomes your feed of every new ad those companies launch.

***

### Use cases

- **Competitive intelligence** — watch rivals' creatives, messaging and campaign cadence in one dataset.
- **Ad-creative research** — build a swipe file of what formats and hooks a whole industry is running.
- **Sales intelligence** — a company advertising job ads or a new product line is a company with a moving budget.
- **Agencies** — show clients exactly what competitors are spending attention on, with real dates and reach.
- **Market research** — creative volume and targeting trends across a set of advertisers over time.

***

### Honest limitations

**You supply the advertiser or keyword.** LinkedIn has no public list of every advertiser, and its advertiser search is a name match — so a common-word brand like "Apple" also surfaces unrelated companies named "Apple Rubber" or "Apple Glass". The `advertiserMatch` mode handles this: `smart` (default) keeps the brand and its sub-brands ("Microsoft Azure", "HubSpot for Developers") while dropping unrelated advertisers; `exact` keeps only advertisers whose name is the brand itself — the clean choice for common-word names; `off` returns LinkedIn's raw results.

**Impressions and targeting are EU-only.** LinkedIn discloses reach and targeting for ads shown in the European Union (a legal requirement there). Ads shown only outside the EU return creative, copy, format, payer and dates, but no impressions or targeting — that data simply isn't published. Add EU country codes to `countries` to surface it.

**Impressions are ranges, not exact counts.** LinkedIn publishes `1k-5k`-style bands; the scraper returns them exactly as disclosed, never invented.

**Creative image is per format.** Image and video ads return a `creativeImageUrl`; Document Ads (PDF carousels) and Message Ads have no single cover image — their content is multi-page or text, captured in full in `adCopy`.

**LinkedIn ads only.** This actor does one library well; it does not cover Meta, Google or TikTok ad libraries.

***

### FAQ

**Do I need a LinkedIn account or cookies?**
No. The Ad Library is public; this actor reads it directly through residential proxies.

**Why residential proxies?**
LinkedIn serves the Ad Library only to residential-looking traffic. The default proxy setting is already correct — just run it.

**How do I monitor a competitor over time?**
Leave `trackChanges` on and schedule the actor. Each run flags new ads and notes which ones stopped showing.

**Can it find every company advertising on LinkedIn?**
No public index of advertisers exists. Curate your watch-list once; the actor tracks it on schedule.

**What happens on the very first run?**
Every ad returns with `isNewSinceLastRun: null` and the baseline is saved; deltas start from the second run.

***

Maintained by **brilliant\_gum**. Issues and feature requests welcome on the Issues tab.

# Actor input Schema

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

Company names exactly as they appear on LinkedIn, e.g. \["HubSpot", "Salesforce"]. Each name becomes one Ad Library search. You can also paste a full linkedin.com/ad-library/search URL.

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

Free-text searches across all ads, e.g. \["crm software", "webinar"]. Runs in addition to advertiser searches.

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

Two-letter ISO codes to filter where ads were shown, e.g. \["US", "DE", "FR"]. Empty = worldwide. Tip: ads shown in the EU disclose impressions and targeting.

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

How far back to search.

## `includeDetails` (type: `boolean`):

Opens each ad's detail page for campaign dates, payer, format, impressions by country and targeting. One extra request per ad — slower but much richer.

## `maxAdsPerQuery` (type: `integer`):

Cap on ads collected per advertiser/keyword search.

## `trackChanges` (type: `boolean`):

Remembers each search between runs and marks every ad as new or already seen. Ideal for scheduled competitor monitoring.

## `newAdsOnly` (type: `boolean`):

From the second run on, return only ads that appeared since the previous run. Needs change tracking enabled.

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

LinkedIn advertiser search is a fuzzy name match. Smart keeps the brand and its sub-brands ("Microsoft Azure", "HubSpot for Developers") and drops unrelated individuals. Exact keeps only advertisers whose name equals your search — use it for common-word brands like "Apple" where other companies share the name. Off returns LinkedIn's raw results.

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

LinkedIn requires residential proxies — keep the default.

## Actor input object example

```json
{
  "advertisers": [
    "HubSpot",
    "Salesforce"
  ],
  "keywords": [
    "crm software"
  ],
  "countries": [
    "US",
    "DE"
  ],
  "dateOption": "",
  "includeDetails": true,
  "maxAdsPerQuery": 50,
  "trackChanges": true,
  "newAdsOnly": false,
  "advertiserMatch": "smart",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `ads` (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": [
        "HubSpot"
    ],
    "includeDetails": true,
    "maxAdsPerQuery": 50,
    "trackChanges": true,
    "advertiserMatch": "smart",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("brilliant_gum/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 = {
    "advertisers": ["HubSpot"],
    "includeDetails": True,
    "maxAdsPerQuery": 50,
    "trackChanges": True,
    "advertiserMatch": "smart",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("brilliant_gum/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 '{
  "advertisers": [
    "HubSpot"
  ],
  "includeDetails": true,
  "maxAdsPerQuery": 50,
  "trackChanges": true,
  "advertiserMatch": "smart",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call brilliant_gum/linkedin-ad-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,brilliant_gum/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/Ac9qdZp4y7SbHIwcb/builds/WHoXKUkWRXDxeF3HS/openapi.json
