# LinkedIn Ad Library Scraper (`pulsedata/linkedin-ad-library-scraper`) Actor

Extract every ad a company runs on LinkedIn from the public Ad Library: ad copy, creative, headline, destination URL, run dates, estimated impressions per country and the advertiser's targeting. Search by advertiser or keyword.

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

## Pricing

from $2.00 / 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

## LinkedIn Ad Library Scraper

Extract **every ad a company runs on LinkedIn** from the public [LinkedIn Ad Library](https://www.linkedin.com/ad-library/home) – ad copy, creative, headline, destination URL, when the ad ran, estimated impressions per country and the targeting the advertiser selected. Search by **advertiser, payer, keyword, Ad Library URL or single ad ID**.

**$2 per 1,000 ads. Impressions and targeting cost $3 per 1,000 – and only for ads that actually carry them** (see "What LinkedIn publishes" below). Plus $0.01 per run.

### What you get

| Field | Description |
|---|---|
| `adId`, `url` | Stable LinkedIn ad ID and the link back to the Ad Library |
| `advertiserName`, `advertiserProfileUrl`, `advertiserCompanyId`, `advertiserLogoUrl` | Who runs the ad, with the LinkedIn company page behind it |
| `paidBy` | The legal entity that paid – often the parent company, and a better filter than the page name |
| `adType`, `creativeType` | "Single Image Ad", "Video Ad", "Document Ad", "Carousel Ad", … |
| `commentary` | The full, untruncated ad text |
| `headline`, `destinationUrl` | Headline under the creative and the landing page it points to |
| `imageUrls` | Creative image assets |
| `firstShown`, `lastShown`, `availabilityText` | The period the ad ran |
| `totalImpressions`, `impressionsLow`, `impressionsHigh` | LinkedIn's estimated impression range, parsed into numbers |
| `impressionsByCountry[]` | Country and its share of impressions in percent |
| `topCountry` | The country with the largest impression share |
| `targeting` | Language and location targeting including exclusions, plus a matrix of which parameter groups (audience, demographic, company, education, job, interests) were targeted or excluded |
| `hasTransparencyData` | Whether LinkedIn published impressions/targeting/run dates for this ad |

Example item:

```json
{
  "adId": "1552638623",
  "url": "https://www.linkedin.com/ad-library/detail/1552638623",
  "advertiserName": "ZEOS",
  "advertiserProfileUrl": "https://www.linkedin.com/company/112941442",
  "paidBy": "Zalando SE",
  "adType": "Single Image Ad",
  "commentary": "What future is business creating today? …",
  "headline": "90 minutes · Live online",
  "destinationUrl": "https://unlock.zeos.eu/loyalty-in-the-agentic-era",
  "firstShown": "2026-06-19",
  "lastShown": "2026-07-06",
  "totalImpressions": "5k-10k",
  "impressionsLow": 5000,
  "impressionsHigh": 10000,
  "impressionsByCountry": [
    { "country": "United Kingdom", "sharePercent": 18 },
    { "country": "Germany", "sharePercent": 15 }
  ],
  "targeting": {
    "language": "Targeting includes English",
    "location": "Targeting includes European Union | Targeting excludes Kazakhstan, India, Russia",
    "parameters": [
      { "parameter": "Company", "targeted": true, "excluded": false },
      { "parameter": "Job", "targeted": true, "excluded": false }
    ]
  },
  "hasTransparencyData": true
}
```

### Use cases

- **B2B competitor intelligence** – every creative a competitor runs, how long each ran, and which countries got the impressions.
- **Find out who a company targets** – LinkedIn discloses whether the advertiser targeted by company, job, seniority, education or interests, plus the language and locations.
- **Ad copy and creative research** – full ad text and image assets for swipe files or LLM analysis.
- **Agency pitches** – walk into a pitch with the prospect's current LinkedIn ads, their run dates and their reach.
- **Lead generation** – check which companies advertise on LinkedIn at all: pass a list of names and set "Max ads per query" to 1.
- **Brand monitoring** – catch ads that use your brand name in the copy with `Match by: keyword`.

### Input

| Field | Meaning |
|---|---|
| `queries` | Advertiser names, keywords, Ad Library search URLs, ad URLs or bare ad IDs – one per line |
| `matchBy` | `advertiser` (the LinkedIn page running the ad), `payer` (the entity that paid – catches a whole group) or `keyword` (searches the ad text) |
| `countryCode` | ISO country code, e.g. `us`, `de`, `gb`. Empty means all countries |
| `dateOption` + `startDate`/`endDate` | `last-30-days`, `current-month`, `current-year`, `last-year` or a custom range |
| `maxAdsPerQuery` | Stop after N ads per query; `0` means no limit |
| `includeDetails` | Open each ad's detail page for impressions, targeting, full copy and destination URL |

```json
{
  "queries": ["Salesforce"],
  "matchBy": "payer",
  "countryCode": "de",
  "dateOption": "last-30-days",
  "maxAdsPerQuery": 200,
  "includeDetails": true
}
```

### What LinkedIn publishes

LinkedIn shows the ad itself for every ad in the library, but **impressions, run dates and targeting only for ads that fall under the EU transparency rules**. A US-only campaign usually has an "About the ad" box and nothing else. Two consequences:

- `hasTransparencyData` tells you which is which, so you can filter.
- You are only charged the detail price for ads that actually carried that data. Ads without it cost the base price only.

If you mainly want impressions and targeting, set a European `countryCode` – those searches come back almost fully populated.

### Proxies

LinkedIn answers datacenter and residential proxies with a challenge page, so this actor uses the **Apify Unblocker** by default and there is nothing to configure. Proxy usage is billed to your Apify account as usual; a run of 100 ads with details needs roughly 105 requests.

### Speed and limits

The Ad Library returns 24 ads per page and the actor follows LinkedIn's own pagination, so a query with 6,000 matches is paginated, not truncated. Detail pages are fetched four at a time. Expect roughly 2–4 minutes per 100 ads with details, a few seconds per 100 without.

### FAQ

**Do I need a LinkedIn account or cookies?**
No. The Ad Library is public; this actor never logs in and never touches private data.

**Why does an advertiser search return unrelated companies?**
LinkedIn matches advertiser names loosely. Use `matchBy: payer` for the legal entity, or paste an Ad Library search URL you built in the browser – the actor uses its filters verbatim.

**Can I get all ads of a company, not just 100?**
Set `maxAdsPerQuery` to `0`. Large advertisers have thousands of ads; the run charges per ad, so keep an eye on the run's max charge setting.

**How current is the data?**
Live – every run reads the Ad Library at that moment. Ads appear there shortly after they start running.

### Related actors

- [Google Ads Transparency Center Scraper](https://apify.com/pulsedata/google-ads-transparency-scraper) – the same competitive picture for Google Search, YouTube, Display, Shopping and Maps.
- [Website Tech Stack Detector](https://apify.com/pulsedata/website-tech-stack-detector) – what an advertiser's landing pages are built with.
- [Career Page Jobs Scraper](https://apify.com/pulsedata/career-page-jobs-scraper) – hiring signals for the same companies.

# Actor input Schema

## `queries` (type: `array`):

One entry per line. Accepts advertiser names (e.g. `Salesforce`), keywords when `Match by` is set to keyword, LinkedIn Ad Library search URLs, or single ad URLs / ad IDs (e.g. `https://www.linkedin.com/ad-library/detail/1552638623`).

## `matchBy` (type: `string`):

`advertiser` searches the LinkedIn page that runs the ad, `payer` the legal entity that paid for it (often the parent company - the precise way to catch every ad of a group), `keyword` searches the ad text.

## `countryCode` (type: `string`):

Only ads shown in this country, as an ISO 3166-1 alpha-2 code (`us`, `de`, `gb`, …). Leave empty for all countries.

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

Restrict to when the ad ran. Use `custom-date-range` together with the two date fields below.

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

Format `YYYY-MM-DD`. Only used with the custom date range.

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

Format `YYYY-MM-DD`. Only used with the custom date range.

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

Stop after this many ads per advertiser or keyword. Set 0 for no limit (big advertisers run thousands of ads).

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

Opens each ad's detail page for estimated total impressions, the impression share per country, the advertiser's targeting parameters, the untruncated ad text and the destination URL. One extra request per ad.

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

LinkedIn answers datacenter and residential proxies with a challenge page, so this actor uses the Apify Unblocker by default. Only change this if you know your proxies get through.

## Actor input object example

```json
{
  "queries": [
    "Salesforce"
  ],
  "matchBy": "advertiser",
  "countryCode": "",
  "dateOption": "",
  "maxAdsPerQuery": 100,
  "includeDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# Actor output Schema

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

LinkedIn ads stored in the default dataset (JSON/CSV/Excel via the dataset API).

# 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 = {
    "queries": [
        "Salesforce"
    ],
    "matchBy": "advertiser",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("pulsedata/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 = {
    "queries": ["Salesforce"],
    "matchBy": "advertiser",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("pulsedata/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 '{
  "queries": [
    "Salesforce"
  ],
  "matchBy": "advertiser",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}' |
apify call pulsedata/linkedin-ad-library-scraper --silent --output-dataset

```

## MCP server setup

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