# Linkedin Ad Library Scraper (`serj_henrique/linkedin-ad-library-scraper`) Actor

Scrape and analyze paid advertising campaigns from the LinkedIn Ad Library — extract headlines, body copy, images, videos, CTAs, targeting details, impressions, and advertiser information across multiple countries.

- **URL**: https://apify.com/serj_henrique/linkedin-ad-library-scraper.md
- **Developed by:** [Sergio Henrique](https://apify.com/serj_henrique) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Scrape and analyze paid advertising campaigns from the LinkedIn Ad Library — extract headlines, body copy, images, videos, CTAs, targeting details, impressions, and advertiser information across multiple countries.

***

### Overview

The LinkedIn Ad Library is a publicly available database where advertisers disclose the creative and performance details of their paid ads. However, it provides no bulk export, no search API, and no way to filter by impression range or targeting criteria.

This Actor automates the entire workflow: it constructs a search query for LinkedIn's Ad Library, paginates through all matching results, opens each ad's detail page, and extracts 20+ structured fields — including creative type, headline, body text, image/video URLs, call-to-action buttons, advertiser info, impressions (and per-country breakdown), targeting parameters, and more.

Whether you're a marketer benchmarking competitor campaigns, a researcher tracking political or brand messaging, or a product team tracking ad creative trends, this Actor turns an otherwise manual process into a single configurable run that exports clean, queryable JSON.

***

### Use Cases

- **Competitive ad intelligence** — Enter a competitor's company name or a keyword like `"project management software"` and retrieve every active paid ad they've disclosed, along with the creative they're running and estimated reach.
- **Creative and copy analysis** — Pull headlines, body copy, images, and CTAs at scale to study messaging strategies, visual styles, or CTA patterns across an industry or over a date range.
- **Political and brand messaging research** — Filter by ad payer (e.g., a PAC or organization) and date range to track how messaging evolves across election cycles or product launches.
- **Market and audience insights** — Inspect targeting parameters (industry, job function, education level, location, language) to understand how advertisers segment their LinkedIn audiences.
- **Performance estimation** — Filter ads by impression range (`minImpressions` / `maxImpressions`) to surface high-reach campaigns or narrow in on niche, low-spend placements.

***

### Input

Configure the scraper through the following fields:

| Input | Type | Description |
|-------|------|-------------|
| **keyword** | string | Search term used to find ads on the Ad Library (e.g. `"python course"`). |
| **payer** | string | Name of the entity paying for the ads (e.g. a company or organization). |
| **company** | string | LinkedIn company page name to filter ads by. |
| **countries** | array of ISO codes | Limit results to specific countries (e.g. `["DE", "FR", "US"]`). Defaults to `["DE"]`. |
| **startDate** | date (`YYYY-MM-DD`) | Start of the reporting period. Required together with `endDate`. |
| **endDate** | date (`YYYY-MM-DD`) | End of the reporting period. Required together with `startDate`. |
| **minImpressions** | integer | Minimum estimated impressions to include (0 – 1,000,000,000). |
| **maxImpressions** | integer | Maximum estimated impressions to include (0 – 1,000,000,000). |
| **maxLimit** | integer **(required)** | Maximum number of ad detail pages to scrape (1 – 10,000; default: 5). |
| **proxy** | object | Optional proxy configuration to avoid rate limits or geo-blocks during scraping. |

***

### Output

Each run produces a structured JSON record per ad. A representative example:

```json
{
  "adId": "1534932424",
  "adUrl": "https://www.linkedin.com/ad-library/detail/1534932424",
  "advertiserLogo": "https://media.licdn.com/dms/image/v2/C4D0BAQE_-3BSObKKhw/company-logo_100_100/company-logo_100_100/0/1630458623850/hogeschool_van_arnhem_en_nijmegen_logo?e=1793232000&v=beta&t=7h4JfSmEjCr-ucIXCmU-VVobsvuePPW-61gWInec92E",
  "advertiserName": "HAN University of Applied Sciences",
  "advertiserUrl": "https://www.linkedin.com/company/12219",
  "availabilityStart": "Sep 1, 2026",
  "availabilityEnd": "Sep 15, 2026",
  "body": "🐍 𝗣𝘆𝘁𝗵𝗼𝗻 𝗹𝗲𝗿𝗲𝗻? 𝗕𝗲𝗴𝗶𝗻 𝗯𝗶𝗷 𝗱𝗲 𝗛𝗔𝗡!\n\nPython is toegankelijk, krachtig en breed inzetbaar – van data-analyse tot automatisering. In deze cursus leg je een stevige basis en leer je programmeren met praktijkgerichte opdrachten.\n\nPerfect als je:\n🚀 Sneller wilt werken met data of bestanden\n🧠 Je probleemoplossend vermogen wil vergroten\n💪 Een waardevolle skill wil toevoegen aan je werk\n\n\nStartdatum: 27 Oktober 2026 of 8 april 2026\nLestijden: 8 lesdagen van 09:30-15:00\nLocatie: HAN University of Applied Sciences, Academie Toegepaste Biowetenschappen en Chemie in Nijmegen",
  "ctasUrl": [
    "https://www.han.nl/opleidingen/cursus/programmeren-in-python"
  ],
  "ctas": [
    "Inschrijven"
  ],
  "creativeType": "SPONSORED_STATUS_UPDATE",
  "format": "Single Image Ad",
  "impressions": "< 1k",
  "impressionsPerCountry": [
    {
      "country": "Netherlands",
      "impressions": "100%"
    }
  ],
  "paidBy": "Stichting Hogeschool van Arnhem en Nijmegen",
  "headline": "Meld je vandaag nog aan voor de cursus!",
  "imageUrl": [
    "https://media.licdn.com/dms/image/v2/D4E10AQEeBPbfaCXtog/image_627_1200/B4DZjGW4x.H0AY-/0/1755674545433/BijdeHANlerenprogrammerenmetPythonjpg?e=2147483647&v=beta&t=BRB6ha1EklGp5wXzpoXoI05zdZmhPhq_DrGDX6NnRPs"
  ],
  "videoUrl": null,
  "targetingLanguage": "Nederlands",
  "targetingLocation": "Nederland",
  "targetingParameter": [
    {
      "targeting_parameter": "Audience",
      "targeted": false,
      "excluded": false
    },
    {
      "targeting_parameter": "Demographic",
      "targeted": false,
      "excluded": false
    },
    {
      "targeting_parameter": "Company",
      "targeted": false,
      "excluded": false
    },
    {
      "targeting_parameter": "Education",
      "targeted": true,
      "excluded": false
    },
    {
      "targeting_parameter": "Job",
      "targeted": true,
      "excluded": false
    },
    {
      "targeting_parameter": "Member Interests and Traits",
      "targeted": false,
      "excluded": false
    }
  ]
}
```

***

### Notes

- **No login required.** The Actor accesses the public LinkedIn Ad Library and does not need user credentials.
- **Proxy recommended.** A rotating proxy (configured via the `proxy` input) is strongly advised for large `maxLimit` values — LinkedIn may throttle or block repeated requests without one.
- **Impressions are estimates.** LinkedIn reports impression ranges (e.g. `<1k`, `1k–10k`) rather than exact figures.
- **Date and impression filters are co-dependent.** If you provide `startDate`, you must also provide `endDate`, and vice versa. The same applies to `minImpressions` / `maxImpressions`.
- **Rate limiting.** The Actor respects page-load timing but does not implement intentional delays between requests. For runs exceeding a few hundred ads, use a proxy with multiple IP pools.

# Actor input Schema

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

Keyword to search in the LinkedIn Ad Library.

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

Name of the ad payer to filter results.

## `company` (type: `string`):

LinkedIn company page name to filter ads by the paying company.

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

Select one or more countries to filter ads by target region.

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

Start date of the ad campaign date range (inclusive). Format: YYYY-MM-DD. Must be provided together with end_date.

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

End date of the ad campaign date range (inclusive). Format: YYYY-MM-DD. Must be provided together with start_date.

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

Minimum number of ad impressions to filter by.

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

Maximum number of ad impressions to filter by.

## `maxLimit` (type: `integer`):

Maximum number of ad detail pages to scrape from the search results.

## `proxy` (type: `object`):

Proxy settings for web requests. Use either Apify proxies or custom proxy URLs.

## Actor input object example

```json
{
  "countries": [
    "DE"
  ],
  "maxLimit": 10,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `default` (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 = {
    "countries": [
        "DE"
    ],
    "maxLimit": 10,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("serj_henrique/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 = {
    "countries": ["DE"],
    "maxLimit": 10,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("serj_henrique/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 '{
  "countries": [
    "DE"
  ],
  "maxLimit": 10,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call serj_henrique/linkedin-ad-library-scraper --silent --output-dataset

```

## MCP server setup

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