# LinkedIn Ad Library Scraper - Competitor Ads, No Login (`benthepythondev/linkedin-ad-library-scraper`) Actor

Returns the ads a company runs on LinkedIn, from LinkedIn's public Ad Library: ad text, headline, format, picture or video, landing page, who paid, and for ads shown in the EU the run dates, impression range, countries and targeting. Search by advertiser or keyword. No login, no cookies.

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

## Pricing

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

## 📣 LinkedIn Ad Library Scraper

Returns the ads a company runs on LinkedIn, read from LinkedIn's public [Ad Library](https://www.linkedin.com/ad-library/) without a login, a cookie or a browser. Enter an advertiser or a keyword and get one row per ad: advertiser, format, ad text, headline, picture and the link to the ad. On request each row also carries the full text, the landing page, the call to action, the video file, who paid for the ad, and for ads shown in the EU the run dates, impression range, countries and targeting.

**Price:** $1.50 per 1,000 ads on the Apify Free plan, down to $1.20 from the Gold plan up. Details add $2.00 per 1,000 (down to $1.60). A search without ads costs nothing. Export to JSON, CSV or Excel, run on a schedule, call via API, or connect to Make, Zapier or n8n.

### 🔎 What is the LinkedIn Ad Library Scraper?

LinkedIn publishes the ads of every advertiser in its Ad Library. The site shows them 24 at a time and has no export. This Actor reads the same pages a visitor gets and returns them as a table: 480 ads in about 39 seconds.

#### What data does it extract?

- **Ad:** id, link, format (single image, video, carousel, document, message, event, article, text ad), LinkedIn's internal type
- **Creative:** ad text, headline, picture
- **Advertiser:** name as shown on the ad
- **With details:** full ad text, landing page, button text, video file, every picture, the advertiser's company page, who paid for the ad
- **With details, for ads shown in the EU:** first and last day the ad ran, total impressions as LinkedIn's range, the countries with their share of impressions, language and location targeting, and which targeting parameters were used (audience, demographic, company, education, job, interests)

#### How it reads the site

| | This Actor | A scraper that drives a browser |
|---|---|---|
| Memory | 512 MB | 1 to 2 GB |
| Filters | LinkedIn's own: advertiser, keyword, countries, period | Varies |
| Landing page | The ad's real link, LinkedIn's tracking parameter removed | Varies |
| A search without ads | No rows, no charge, and the status message says so | Varies |

### ⬇️ Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `advertisers` | array | | Company names, one search each |
| `keywords` | array | | Words in the ad text, one search each |
| `urls` | array | | Search links copied from the Ad Library, links of single ads, or ad ids |
| `countries` | array | empty | Two-letter codes (DE, FR, US): only ads shown there |
| `dateOption` | string | `any` | `last-30-days`, `current-month`, `current-year`, `last-year` |
| `startDate`, `endDate` | string | empty | A custom period (YYYY-MM-DD), both needed |
| `maxResults` | integer | 24 | Ads to save per search, up to 5,000 |
| `includeDetails` | boolean | false | Opens each ad's own page. Second charge event |
| `onlyNewAds` | boolean | false | Skip every ad an earlier run with the same monitor name delivered |
| `monitorId` | string | empty | Name of that memory |

LinkedIn matches an advertiser name loosely: a search for "HubSpot" also returns ads of partners that carry the name ("HubSpot Platinum Partner"). To pin one company, set the advertiser in the Ad Library yourself and paste that search link into `urls`.

With an empty input the Actor reads the sample advertiser HubSpot, so a first run shows what the rows look like.

#### Example input

A competitor's ads in Germany and France in the last 30 days, with details:

```json
{
  "advertisers": ["Personio"],
  "countries": ["DE", "FR"],
  "dateOption": "last-30-days",
  "maxResults": 200,
  "includeDetails": true
}
```

Every advertiser that uses a phrase:

```json
{
  "keywords": ["crm software", "marketing automation"],
  "countries": ["DE"],
  "maxResults": 100
}
```

A search link and a single ad:

```json
{
  "urls": [
    "https://www.linkedin.com/ad-library/search?accountOwner=salesforce&countries=DE&dateOption=last-30-days",
    "https://www.linkedin.com/ad-library/detail/1578715144"
  ]
}
```

### ⬆️ Output

One row per ad. The first row of the default run:

```json
{
  "adId": "1566665103",
  "adUrl": "https://www.linkedin.com/ad-library/detail/1566665103",
  "advertiserName": "HubSpot",
  "format": "Single Image Ad",
  "creativeType": "SPONSORED_STATUS_UPDATE",
  "text": "Outgrowing your CRM? See how scaling teams unified their data and automated more with AI.",
  "headline": "CRM slowing you down?",
  "imageUrl": "https://media.licdn.com/dms/image/v2/D4D10AQEdZnzdWO4RFQ/image-shrink_1280/B4DaDugRnLJ0Ac-/0/1790707833528/IMAGE1png?e=2147483647&v=beta&t=Kq72LJ_SRzCFdNBd9D3UWEfI00GzOEfkhqsP0UZ1sF8",
  "query": "HubSpot",
  "position": 1,
  "hasDetails": false,
  "scrapedAt": "2026-10-03T15:50:02.272762+00:00"
}
```

With `includeDetails`, a row found for the keyword "crm software" in Germany:

```json
{
  "adId": "1559111273",
  "adUrl": "https://www.linkedin.com/ad-library/detail/1559111273",
  "advertiserName": "Byner",
  "format": "Single Image Ad",
  "creativeType": "SPONSORED_STATUS_UPDATE",
  "text": "Verheddern Sie sich auch manchmal in Ihrer Recruiting-Software?\nMit Byner wird aus Chaos Struktur: Alles in einer Umgebung, nahtlos vernetzt.",
  "headline": "Vom Chaos zur Plug-and-Play-Lösung.",
  "imageUrl": "https://media.licdn.com/dms/image/v2/D4D10AQG3mYlchmiU0g/image-shrink_1280/B4DZiiiPt.GgAM-/0/1755073543779/AirpodsDEpng?e=2147483647&v=beta&t=M36lYfkYcNL99HxfrzSqAjZ0xoDq8keywPfoLad5V6c",
  "query": "crm software",
  "position": 30,
  "hasDetails": true,
  "advertiserUrl": "https://www.linkedin.com/company/28840076",
  "paidBy": "Byner",
  "startDate": "2026-09-27",
  "endDate": "2026-10-02",
  "impressions": "< 1k",
  "impressionsMin": null,
  "impressionsMax": 1000,
  "countries": [
    {"country": "Germany", "share": "89%"},
    {"country": "Switzerland", "share": "9%"},
    {"country": "Austria", "share": "2%"}
  ],
  "targetingLanguage": "Targeting includes English",
  "targetingLocation": "Targeting includes DACH",
  "targeting": {"Audience": "targeted"},
  "callToAction": "Learn more",
  "destinationUrl": "https://byner.com/de/?utm_source=linkedin&utm_medium=paidsearch&utm_campaign=q4_2026_de&utm_content=aw_hp",
  "imageUrls": [
    "https://media.licdn.com/dms/image/v2/D4D10AQG3mYlchmiU0g/image-shrink_1280/B4DZiiiPt.GgAM-/0/1755073543779/AirpodsDEpng?e=2147483647&v=beta&t=M36lYfkYcNL99HxfrzSqAjZ0xoDq8keywPfoLad5V6c"
  ],
  "videoUrl": null,
  "scrapedAt": "2026-10-03T15:51:08.844981+00:00"
}
```

The run also writes a `SUMMARY` record with the searches that returned nothing and the reason a run stopped.

### 💰 What a run costs

| Apify plan | Per 1,000 ads | Per 1,000 ad details (added) |
|---|---|---|
| Free | $1.50 | $2.00 |
| Bronze | $1.40 | $1.90 |
| Silver | $1.30 | $1.80 |
| Gold, Platinum, Diamond | $1.20 | $1.60 |

Apify's standard start event ($0.00005) is the only other charge; proxies are included. Not charged: a search without ads, an ad already delivered in only-new mode, and the same ad found a second time in one run. A single ad opened by its link always comes with its details and is charged as one ad plus one detail. Set the run's maximum charge and the Actor stops at it with the rows it has saved.

### ⏱️ Measured on Apify (October 3, 2026)

| Run | Ads | Time |
|---|---|---|
| Empty input (sample advertiser, one page) | 24 | 8 s |
| One advertiser, 20 pages | 480 | 39 s |
| Keyword, ad link and search link with details | 121 | 49 s |
| Only-new watch on one advertiser in Germany, second run | 0 new of 448 | 35 s |

All at 512 MB. In the detail run, all 121 ads carried payer, run dates and impression range, 101 the countries, 86 a landing page and 8 a video file. Two aborted and resumed runs (480 ads, and 240 with details) saved no ad twice.

### 🔔 Watching for new ads

Turn on `onlyNewAds`, give the watch a `monitorId`, narrow the search with `dateOption` and put the run on a schedule. The first run delivers what is there. Later runs read the same search and deliver only the ads they have not delivered to you before; a run without news ends with zero rows and "Nothing new". The memory is a key-value store named `linkedin-ad-library-monitor` in your own account; delete a record there to start a watch over.

### 🤖 For AI agents

Smallest useful call:

```json
{"advertisers": ["HubSpot"], "maxResults": 24}
```

Each row is one ad with `advertiserName`, `format`, `text`, `headline`, `imageUrl` and `adUrl`. Add `"includeDetails": true` for `destinationUrl`, `callToAction`, `paidBy`, `startDate`, `endDate`, `impressions`, `countries` and `targeting`. `advertisers`, `keywords` and `urls` take several entries at once. A search without ads returns no rows; the status message and the `SUMMARY` record name it. No credentials are needed.

### 💡 Use cases

- 🎯 **B2B competitor research:** what a competitor says in its LinkedIn ads, where the ads lead, and in which countries they run.
- ✍️ **Ad copy research:** hundreds of real ads for a keyword with text, headline and call to action in one table.
- 🔔 **Launch monitoring:** a scheduled watch that reports only the ads that are new.
- 🇪🇺 **Reach and targeting in the EU:** impression ranges, country shares and the targeting parameters an advertiser used.

### ⚠️ Limits, stated plainly

- **Run dates, impressions, countries and targeting** exist only where LinkedIn publishes them, which is for ads shown in the EU. An ad shown only elsewhere has its creative, advertiser and payer, and nothing more.
- **The search list shortens long ad texts** with "…". The full text is in the details.
- **The advertiser search is a loose name match.** Partners and resellers that carry the name are included; see the note under the input table.
- **Landing page:** 86 of 121 ads in the test had one. Message ads, event ads and promoted posts of employees often have none.
- **Promoted posts of employees** show the company as advertiser, the employee as payer, and no company page link.
- **No spend.** LinkedIn does not publish it.
- **5,000 ads per search** is the upper limit; use countries or a period to split a larger advertiser.
- **LinkedIn limits how often one address may ask.** The Actor changes addresses by itself, which is why 480 ads take 39 seconds and not 5. If LinkedIn stops answering in the middle of a search, the rows saved so far stay and the status message says which search was cut.

### ❓ FAQ

**Do I need a LinkedIn account or cookies?** No. The Ad Library is public and the Actor reads it as a visitor. Your own LinkedIn account is never involved.

**How do I get the ads of exactly one company?** Open the Ad Library, pick the company in the advertiser field, copy the link of the result page and paste it into `urls`.

**Why are run dates and impressions empty for some ads?** LinkedIn publishes them for ads shown in the EU. Add EU countries to `countries` to get only such ads.

**Why do I get fewer ads than LinkedIn's count?** `maxResults` is 24 by default. Raise it. When a search stops at your limit, the status message quotes LinkedIn's count.

**Can I search by keyword and advertiser together?** Yes, through a search link: set both in the Ad Library and paste the link.

**Does it download the videos?** It returns the link of the video file in its best quality; the file itself is not copied.

**Can I run it every day?** Yes. Use a schedule with `onlyNewAds` so you pay only for ads you have not received.

**How do I call it from code?** With the Apify API or the Python and JavaScript clients; every run returns a dataset you can fetch as JSON or CSV. It also works as a tool through Apify's MCP server.

**Is it legal?** The Actor reads what LinkedIn publishes for everyone under its transparency rules. Payer names can be names of persons; GDPR, CCPA and similar rules apply to how you store and use them, and LinkedIn's terms apply to you as well.

### 🔗 You might also like

- [Google Ads Transparency Scraper](https://apify.com/benthepythondev/google-ads-transparency-scraper): the same question for Google Search, YouTube and Shopping ads
- [Meta Ad Library Scraper](https://apify.com/benthepythondev/meta-ad-library-scraper): Facebook and Instagram ads
- [LinkedIn Jobs Scraper](https://apify.com/benthepythondev/linkedin-jobs-scraper): what the same companies are hiring for
- [LinkedIn Company Scraper](https://apify.com/benthepythondev/linkedin-company-scraper): size, industry and website of the advertisers you find

**Keywords:** linkedin ad library scraper, linkedin ads scraper, linkedin ad library api, competitor linkedin ads, b2b ad intelligence, linkedin ad copy, linkedin ads export, ad library export csv, linkedin sponsored content, linkedin video ads, ad targeting data, DSA ad repository, linkedin ad impressions, competitor ad monitoring, b2b marketing research, linkedin ads spy tool, ad creative research

# Actor input Schema

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

Company names as they appear on LinkedIn, one search each. LinkedIn matches the name loosely, so partners and resellers that carry the name are included.

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

Words to look for in the ad text, one search each, for example "crm software". Finds the ads of every advertiser that uses them.

## `urls` (type: `array`):

Search links copied from linkedin.com/ad-library (with any filter set there) or links of single ads (…/ad-library/detail/1234567890). A bare ad id works too.

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

Two-letter country codes such as DE, FR or US: only ads shown there. Applies to advertisers and keywords, not to pasted links.

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

Only ads that ran in this period. Ignored when both dates below are set.

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

First day of a custom period (YYYY-MM-DD). Needs the last day too.

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

Last day of a custom period (YYYY-MM-DD).

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

Ads to save for each advertiser, keyword or search link. LinkedIn lists 24 ads per page.

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

Opens every ad's own page: full text, landing page, call to action, video file, who paid for the ad, and for ads shown in the EU the run dates, impression range, countries and targeting. Charged as a second event per ad. A link to a single ad always returns its details.

## `onlyNewAds` (type: `boolean`):

For scheduled runs: skips every ad an earlier run with the same monitor name delivered, so you pay only for ads that are new to you. The memory is a key-value store in your own account.

## `monitorId` (type: `string`):

Name of the memory used by the option above. Give each watch its own name; without a name the searches themselves are the name.

## Actor input object example

```json
{
  "advertisers": [
    "HubSpot"
  ],
  "dateOption": "any",
  "maxResults": 24,
  "includeDetails": false,
  "onlyNewAds": false
}
```

# Actor output Schema

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

No description

## `summary` (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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("benthepythondev/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"] }

# Run the Actor and wait for it to finish
run = client.actor("benthepythondev/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"
  ]
}' |
apify call benthepythondev/linkedin-ad-library-scraper --silent --output-dataset

```

## MCP server setup

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