# LinkedIn Ads Library Scraper (`maximedupre/linkedin-ads-library`) Actor

Search public LinkedIn Ads Library ads by advertiser, keyword, URL, or source filters. Get structured ad records with creative, publication, impressions, targeting, payer, and source-link fields, or return a source-backed match count.

- **URL**: https://apify.com/maximedupre/linkedin-ads-library.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Marketing, Business, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.45 / 1,000 ads

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

### 🔎 Search the LinkedIn Ads Library for focused ad research

For marketers, competitor researchers, agencies, and analysts, this Actor searches public LinkedIn Ads Library ads and saves structured ad records or a source-backed match count. Review ad copy, creative formats, source links, publication dates, impressions, targeting, and payer details when LinkedIn publishes them.

Use it to:

- Search a public library query with **[LinkedIn ads](https://apify.com/maximedupre/linkedin-ads-library/examples/linkedin-ads)**.
- Review one advertiser's public campaign with **[LinkedIn campaign](https://apify.com/maximedupre/linkedin-ads-library/examples/linkedin-campaign)**.
- Group returned ads by source-published format with **[LinkedIn ad types](https://apify.com/maximedupre/linkedin-ads-library/examples/linkedin-ad-types)**.
- Browse ad copy, media, and links with **[LinkedIn ad examples](https://apify.com/maximedupre/linkedin-ads-library/examples/linkedin-ad-examples)**.
- Check the fields available in each row with **[LinkedIn ad specs](https://apify.com/maximedupre/linkedin-ads-library/examples/linkedin-ad-specs)**.

#### 📦 Structured LinkedIn ad records and match counts

Use `resultType` to save one row for each matching ad or one source-backed count row. Ad rows can include advertiser and payer data, creative copy and media, destination links, publication dates, impression ranges, country breakdowns, targeting categories, and the source result count. Optional fields stay absent when LinkedIn does not publish them.

#### ▶️ Run one focused LinkedIn Ads Library search

Choose one result type and one way to find ads for each run. Fields for other search choices are ignored. Use advertiser or keyword searches, start from a public search URL, or provide public ad-detail URLs. Add country, date, impression, targeting, payer, order, and ad-count settings when needed.

**Run steps**

1. Choose `ads` to save ad rows or `count` to save one source-backed count.
2. Choose `advertiser`, `keyword`, `searchUrl`, or `adUrls` in `findAdsBy`.
3. Add the matching search value, then add any filters for the source-published fields you need.
4. Open the `results` link to review the default dataset.

#### ⚙️ Input

The form runs one focused search at a time. Keyword and ad URL inputs accept lists of the same kind, and each run uses the selected `findAdsBy` choice.

**Input fields**

| Field | Type | What it does |
| --- | --- | --- |
| `resultType` | string: `ads` or `count` | Chooses ad rows or one source-backed match count. |
| `findAdsBy` | string: `advertiser`, `keyword`, `searchUrl`, or `adUrls` | Chooses how this run finds public LinkedIn ads. |
| `advertiser` | string | Searches for ads associated with this advertiser or company name. |
| `keywords` | array of strings | Searches ad copy for one or more keywords or phrases. Ads matching any value are included. |
| `searchUrl` | URL string | Starts the search from one public LinkedIn Ads Library search URL. |
| `adUrls` | array of objects | Adds one or more public LinkedIn Ads Library ad-detail URLs. |
| `adUrls[].url` | URL string | Gives one public ad-detail URL to retrieve. |
| `targetCountries` | array of strings | Limits matches to ads that target one or more country names or codes. |
| `publicationDateRange` | object | Limits matches to the source-published publication date range. |
| `publicationDateRange.from` | date string | Includes ads published on or after this date. Use `YYYY-MM-DD`. |
| `publicationDateRange.to` | date string | Includes ads published on or before this date. Use `YYYY-MM-DD`. |
| `impressionRange` | object | Limits matches to the source-published impression range. |
| `impressionRange.min` | integer | Includes ads whose published impression range reaches this value. |
| `impressionRange.max` | integer | Includes ads whose published impression range does not exceed this value. |
| `targeting` | object | Filters by categories published in the ad targeting details. |
| `targeting.include` | array of strings | Includes ads with these targeting categories. |
| `targeting.exclude` | array of strings | Excludes ads with these targeting categories. |
| `payer` | string | Limits matches to ads where the published payer matches this text. |
| `maxItems` | integer | For `ads`, stops after this many matching ads. `count` ignores this setting. |
| `sortOrder` | string: `newest` or `oldest` | Orders ad rows newest first or oldest first. `count` ignores this setting. |

**Work limit**

For `ads`, leaving `maxItems` empty returns all available results until the source is exhausted. The schema has no fixed upper bound for this field. When `maxItems` is set, it is a limit on saved ad rows. `count` ignores it.

**Public input example**

This example is copied from a successful current-beta default-input run.

```json
{
  "resultType": "ads",
  "findAdsBy": "keyword",
  "keywords": [
    "demo"
  ],
  "maxItems": 1,
  "sortOrder": "newest"
}
```

#### 🧾 Output

The run output exposes the default dataset through one link.

**Dataset link**

| Field | Type | What it does |
| --- | --- | --- |
| `results` | URL string | Opens the default dataset overview for the run. |

**Ad record shape**

Each ad row has `resultType: "ad"`. Optional fields appear only when the source publishes them.

| Field | Type | What it does |
| --- | --- | --- |
| `resultType` | string: `ad` | Identifies this row as an ad record. |
| `adId` | string | Gives the identifier published for the ad. |
| `adUrl` | URL string | Links to the public LinkedIn Ads Library ad-detail page. |
| `advertiser` | object | Holds the advertiser name and available branding. |
| `advertiser.name` | string | Gives the advertiser name published for the ad. |
| `advertiser.logoUrl` | URL string | Links to the advertiser logo when published. |
| `payer` | object | Holds the paying advertiser when the source publishes it. |
| `payer.name` | string | Gives the published payer name. |
| `payer.logoUrl` | URL string | Links to the payer logo when published. |
| `creative` | object | Holds the source-published ad format, copy, and media. |
| `creative.format` | string | Gives the ad format published by the source. |
| `creative.body` | string | Gives the ad body copy when published. |
| `creative.headline` | string | Gives the ad headline when published. |
| `creative.media` | array of objects | Lists published image, video, document, carousel, or other media. |
| `creative.media[].type` | string | Gives the type of one media item. |
| `creative.media[].url` | URL string | Links to one source media item when published. |
| `creative.media[].thumbnailUrl` | URL string | Links to one media thumbnail when published. |
| `destinationUrl` | URL string | Links to the ad destination or landing page when published. |
| `publication` | object | Holds the source-published ad run dates. |
| `publication.startDate` | date string | Gives the date when the source shows that the ad started. |
| `publication.endDate` | date string | Gives the date when the source shows that the ad stopped, when available. |
| `impressions` | object | Holds the source-published impression range. |
| `impressions.min` | integer | Gives the lower bound of the impression range. |
| `impressions.max` | integer | Gives the upper bound of the impression range when available. |
| `countryBreakdowns` | array of objects | Lists source-published impression or reach ranges by country. |
| `countryBreakdowns[].country` | string | Gives the country named by the source. |
| `countryBreakdowns[].impressions` | object | Holds the impression range for one country when published. |
| `countryBreakdowns[].impressions.min` | integer | Gives the lower bound of one country's impression range. |
| `countryBreakdowns[].impressions.max` | integer | Gives the upper bound of one country's impression range when available. |
| `countryBreakdowns[].reach` | object | Holds the reach range for one country when published. |
| `countryBreakdowns[].reach.min` | integer | Gives the lower bound of one country's reach range. |
| `countryBreakdowns[].reach.max` | integer | Gives the upper bound of one country's reach range when available. |
| `targeting` | object | Holds the source-published included and excluded targeting categories. |
| `targeting.included` | array of strings | Lists categories included by the source. |
| `targeting.excluded` | array of strings | Lists categories excluded by the source. |
| `sourceResultCount` | integer | Gives the number of matching ads reported by the source when available. |

This genuine, unshortened ad row comes from a successful current-beta run.

```json
{
  "resultType": "ad",
  "adId": "1558614953",
  "adUrl": "https://www.linkedin.com/ad-library/detail/1558614953",
  "advertiser": {
    "name": "Patryk Bandurski",
    "logoUrl": "https://media.licdn.com/dms/image/v2/D4E03AQHNN5rKH2ds0w/profile-displayphoto-scale_100_100/B4EZrvOOFWHgAc-/0/1764950074209?e=1792022400&v=beta&t=gL1Eg9mbP_fAXxmZmp3NwXwNAcbGMnuS2D9geRVD-ZI"
  },
  "payer": {
    "name": "Integration Trails"
  },
  "creative": {
    "format": "SPONSORED_STATUS_UPDATE",
    "body": "I'm inviting you to build agents with me for 5 weeks. 𝗠𝘂𝗹𝗲 𝗔𝗜 𝗙𝗼𝗿𝗴𝗲 𝘀𝘁𝗮𝗿𝘁𝘀 𝗢𝗰𝘁𝗼𝗯𝗲𝗿 𝟭𝟵𝘁𝗵. At Dreamforce, one thing was clear. Customers no longer ask \"what is an agent?\" They ask how to run agents safely and get real value from them. Honestly, this feels familiar. We went through digital transformation 10 years ago. 𝗔𝗴𝗲𝗻𝘁𝗶𝗰 𝘁𝗿𝗮𝗻𝘀𝗳𝗼𝗿𝗺𝗮𝘁𝗶𝗼𝗻 𝗵𝗮𝘀 𝗮 𝗹𝗼𝘁 𝗶𝗻 𝗰𝗼𝗺𝗺𝗼𝗻 𝘄𝗶𝘁𝗵 𝗶𝘁. Same questions, new layer. However, most AI training stops at the demo. In real projects, 𝗴𝗼𝘃𝗲𝗿𝗻𝗮𝗻𝗰𝗲 𝗮𝗻𝗱 𝗰𝗼𝘀𝘁 𝗰𝗼𝗻𝘁𝗿𝗼𝗹 𝗱𝗲𝗰𝗶𝗱𝗲 𝗶𝗳 𝗮𝗴𝗲𝗻𝘁𝘀 𝗯𝗿𝗶𝗻𝗴 𝗥𝗢𝗜 or a surprise invoice. So Forge covers the full path: → Build: agents, MCP servers, A2A → Govern: Omni Gateway, policies, identity, PII masking → Control cost: token usage, model routing, budgets → Ship: CLI, CI/CD 𝟮𝟱 𝗱𝗮𝘆𝘀, 𝟮𝟱 𝗰𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲𝘀, You also learn when to tell a client: \"no, an agent is the wrong tool here.\" Why it matters for you: clients need people who can build agents and keep them under control. There are not many yet. 𝗧𝗵𝗮𝘁 𝗶𝘀 𝗮 𝗿𝗲𝗮𝗹 𝗰𝗮𝗿𝗲𝗲𝗿 𝗼𝗽𝗽𝗼𝗿𝘁𝘂𝗻𝗶𝘁𝘆 𝗳𝗼𝗿 𝗠𝘂𝗹𝗲𝗦𝗼𝗳𝘁 𝗮𝗻𝗱 𝗦𝗮𝗹𝗲𝘀𝗳𝗼𝗿𝗰𝗲 𝗰𝗼𝗻𝘀𝘂𝗹𝘁𝗮𝗻𝘁𝘀. It's 100% online, about 2 hours a day, at a time you pick. You get the cohort rhythm and a live session every week. Busy week? The materials stay with you. 𝗘𝗮𝗿𝗹𝘆 𝗯𝗶𝗿𝗱: $𝟵𝟵𝟳 Join → https://lnkd.in/dfmxxg_s What is harder at your clients right now: building agents or governing them? #AgentFabric #Salesforce #AIAgents #MuleSoftCommunity #MuleSoft",
    "media": [
      {
        "type": "image",
        "url": "https://media.licdn.com/dms/image/v2/D4D22AQG2HKhvETKcrw/feedshare-image-high-res/B4DaDOt3jPJoAU-/0/1790174526195?e=1792022400&v=beta&t=7idPqhc4TpADENc0Jbydd3Au-MHqGmgsPm6d_JzRaTk"
      },
      {
        "type": "image",
        "url": "https://media.licdn.com/dms/image/v2/D4D22AQEb3e98VEcFiQ/feedshare-shrink_1280/B4DaDOt3cOIcAQ-/0/1790174525424?e=1792022400&v=beta&t=PohWnlgrwKupplafC5f2YS9kmsJdIOJsrtjBOaxbZ4I"
      },
      {
        "type": "image",
        "url": "https://media.licdn.com/dms/image/v2/D4D22AQFMIzra4gQ5zg/feedshare-shrink_1280/B4DaDOt3bvKUAM-/0/1790174525417?e=1792022400&v=beta&t=DvR9VEkSo1-uHMELoQDynHQ6wP8oCH8pbXCmFQlR5Lk"
      }
    ]
  },
  "destinationUrl": "https://lnkd.in/dfmxxg_s",
  "publication": {
    "startDate": "2026-09-23",
    "endDate": "2026-09-24"
  },
  "impressions": {
    "min": 150000,
    "max": 200000
  },
  "targeting": {
    "included": [
      "Targeting includes English",
      "Targeting includes United States, United Kingdom and 1 others European Union"
    ]
  }
}
```

**Match count shape**

Each count row has `resultType: "count"` and the number reported by the source.

| Field | Type | What it does |
| --- | --- | --- |
| `resultType` | string: `count` | Identifies this row as a match count. |
| `sourceResultCount` | integer | Gives the number of matching ads reported by the source. |

This genuine count row comes from a successful current-beta run.

```json
{
  "resultType": "count",
  "sourceResultCount": 1148740
}
```

#### 💳 Pricing

This Actor uses pay-per-event pricing. The primary event applies to each matching ad saved to the dataset. A count-only run saves a count row, so it does not use the ad event.

| Buyer-facing event | What it covers |
| --- | --- |
| `Ad` | One matching ad with its available source fields is saved to your dataset. |

Tier prices are shown in the Apify Store pricing panel.

#### 🔌 Integrations

Open the `results` link in Apify or use the default Dataset API to read the saved rows.

https://www.youtube.com/watch?v=bNACk1\_S\_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

#### ❓ FAQ

##### Can one run combine several independent searches?

No. Each run uses one selected search method and one set of filters. Keyword lists and ad URL lists contain same-kind values for that selected method, not separate filter sets.

##### How do keyword and ad URL lists handle repeats?

When the same source ad appears again from another submitted value, the first eligible occurrence is saved and later matches are ignored. The saved row describes that first match.

##### Can I search by keyword or advertiser?

Yes. Choose `keyword` or `advertiser`, or start from a public search URL or public ad-detail URL list.

##### What does an empty `maxItems` value do?

For `ads`, leaving `maxItems` empty returns all available results until the source is exhausted. `count` ignores this setting.

##### What does a count run return?

It saves one row with `resultType: "count"` and `sourceResultCount`. It does not save matching ad rows.

##### Why is a field missing from an ad row?

LinkedIn does not publish every detail for every ad. Optional fields are omitted when the source does not provide them, and the Actor does not invent replacement values.

##### Does the Actor return spend, clicks, or conversions?

No. It returns source-published ad fields such as impressions, country breakdowns, and targeting. It does not provide spend, clicks, conversions, or campaign management data.

##### Can I use private LinkedIn data?

No. This Actor reads public LinkedIn Ads Library pages. It does not cover private or account-only LinkedIn surfaces.

### 📝 Changelog

**v0.0** (25-09-2026)

- Initial release.

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~linkedin-ads-library/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Related Actors

- [Google Ads Scraper](https://apify.com/maximedupre/google-ads-scraper) for Google Ads Transparency Center creative research.
- [Facebook Page Transparency Scraper](https://apify.com/maximedupre/facebook-page-transparency-scraper) for Meta Ad Library ads and Page transparency.
- [TikTok Creative Center Scraper for Top Ads](https://apify.com/maximedupre/tiktok-creative-center-scraper) for ranked TikTok ad creative research.
- [Snapchat Ads Scraper](https://apify.com/maximedupre/snapchat-ads) for public Snapchat ads and targeting data.
- [Pinterest Ads Scraper](https://apify.com/maximedupre/pinterest-ads) for public Pinterest ad creative and reach research.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `resultType` (type: `string`):

Choose the kind of result to return.

## `findAdsBy` (type: `string`):

Choose one way to find public LinkedIn ads for this run.

## `advertiser` (type: `string`):

Enter the name of the advertiser or company to search for.

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

Enter one or more keywords or phrases. Ads matching any value are included.

## `searchUrl` (type: `string`):

Paste one public LinkedIn Ads Library search URL.

## `adUrls` (type: `array`):

Add one or more public LinkedIn Ads Library ad-detail URLs.

## `targetCountries` (type: `array`):

Limit matches to ads that target one or more countries. Enter a country name or code for each value.

## `publicationDateRange` (type: `object`):

Limit matches to ads published within this date range. Leave either date empty for an open-ended range.

## `impressionRange` (type: `object`):

Limit matches to the source-published impression range. Leave either value empty for an open-ended range.

## `targeting` (type: `object`):

Filter by categories published in the ad targeting details.

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

Limit matches to ads where the published payer matches this text.

## `maxItems` (type: `integer`):

For Ad records, stop after this many matching ads. Leave it empty to collect all available matches until the source has no more. Match count ignores this setting.

## `sortOrder` (type: `string`):

For Ad records, choose how matching ads are ordered. Match count ignores this setting.

## Actor input object example

```json
{
  "resultType": "ads",
  "findAdsBy": "keyword",
  "keywords": [
    "demo"
  ],
  "maxItems": 1,
  "sortOrder": "newest"
}
```

# Actor output Schema

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

Open the ad records or source-backed count for this run.

# 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": [
        "demo"
    ],
    "maxItems": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/linkedin-ads-library").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": ["demo"],
    "maxItems": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/linkedin-ads-library").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": [
    "demo"
  ],
  "maxItems": 1
}' |
apify call maximedupre/linkedin-ads-library --silent --output-dataset

```

## MCP server setup

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

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/AuDfosTBVtmZchOdI/builds/J5mWMbFwdBpg9lD4c/openapi.json
