# LinkedIn Ad Library Advertiser Disclosure Scraper (`jungle_synthesizer/linkedin-ad-library-advertiser-disclosure-scraper`) Actor

Scrape LinkedIn Ad Library ads by keyword, company ID, or country: advertiser name, ad creative, and the full EU DSA disclosure block — total and per-country impressions, audience targeting, and the legal entity paying for each ad.

- **URL**: https://apify.com/jungle\_synthesizer/linkedin-ad-library-advertiser-disclosure-scraper.md
- **Developed by:** [BowTiedRaccoon](https://apify.com/jungle_synthesizer) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 71.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 record scrapeds

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/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 Advertiser Disclosure Scraper

Scrapes LinkedIn's Ad Library by keyword, company ID, or country and returns the full disclosure record for each ad — not just the creative. That includes the EU DSA block most competing scrapers never fetch: total and per-country impressions, audience targeting, and the legal entity paying for the ad.

***

### LinkedIn Ad Library Scraper Features

- Search by free-text keyword, LinkedIn company ID, or both — run them independently in the same job
- Restrict results to specific countries or a date window (last 30 days, this month, this year, last year)
- Returns every ad creative field: headline, body copy, CTA text, destination URL, images, video, document title
- Pulls the "Paid for by" legal entity — on agency-run campaigns this is the media agency, not the brand
- Pulls the EU DSA disclosure block when LinkedIn publishes it: total impressions bucket, impressions by country, and the targeting parameters (language, location, and whether company/demographic/education/interest targeting was used)
- Deduplicates ads found across multiple keyword or company searches automatically

***

### Who Uses This

- **B2B sales and prospecting teams** — build a list of who's advertising on LinkedIn in a given category or country, and who's actually paying for it
- **Competitive intelligence** — track a competitor's ad cadence, creative angles, and targeting choices over time
- **Agencies** — audit which agency runs media for which brand, visible directly in the "Paid for by" field
- **Compliance and research teams** — pull the EU DSA disclosure fields (impressions by country, targeting parameters) LinkedIn is legally required to publish for EEA-targeted ads

***

### How It Works

1. Give the actor one or more search keywords, LinkedIn company IDs, or both.
2. Optionally narrow by country and/or a date range.
3. The actor walks the Ad Library search results for each combination and opens every ad's detail page.
4. Each record comes back with the creative, the advertiser, and whatever disclosure data LinkedIn publishes for that ad — some fields are only present for ads targeted at the EU/EEA, since that's what triggers the DSA disclosure requirement.

***

### Input

```json
{
  "keywords": ["hubspot"],
  "companyIds": [],
  "countries": [],
  "dateOption": "",
  "maxItems": 25
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `keywords` | array of strings | `["hubspot"]` | Free-text search terms. Each keyword is searched independently. |
| `companyIds` | array of strings | `[]` | LinkedIn company IDs (from `linkedin.com/company/<id>`) to fetch ads for directly. At least one of `keywords` or `companyIds` is required. |
| `countries` | array of strings | `[]` | Restrict results to ads targeting these countries (ISO-style 2-letter codes, e.g. `US`, `GB`, `DE`). Leave empty to search worldwide. |
| `dateOption` | string | `""` (all time) | One of `last-30-days`, `current-month`, `current-year`, `last-year`, or empty for no date filter. |
| `maxItems` | integer | `10` | Maximum number of ads to return. |

#### Resuming a large crawl

Every run emits a `resumeCursor` in its Output. To continue a crawl that stopped — hit `maxItems`, a per-run spend cap, or was aborted — start a new run with the same input plus that `resumeCursor`. You are not re-charged for ads already returned. Resume within your account's data-retention window.

***

### LinkedIn Ad Library Scraper Output Fields

```json
{
  "ad_id": "1486704414",
  "detail_url": "https://www.linkedin.com/ad-library/detail/1486704414",
  "advertiser_name": "Microsoft",
  "advertiser_company_url": "https://www.linkedin.com/company/1035",
  "advertiser_company_id": "1035",
  "paid_for_by": "ZENITH UK (MEDIA) LIMITED",
  "ad_type": "Single Image Ad",
  "creative_type": "SPONSORED_STATUS_UPDATE",
  "promoted_label": "Promoted",
  "ad_headline": "Experience FabCon+SQLCon Europe 2026",
  "ad_body_text": "Join data leaders, architects, analysts, developers, and IT professionals...",
  "ad_cta_text": "Register",
  "ad_destination_url": "https://espc.tech/conference/fabcon-europe-2026/",
  "image_urls": ["https://media.licdn.com/dms/image/v2/..."],
  "video_url": null,
  "document_title": null,
  "first_shown_date": "Aug 5, 2026",
  "last_shown_date": "Aug 10, 2026",
  "total_impressions_range": "10k-20k",
  "impressions_by_country": ["Italy: 26%", "France: 24%"],
  "audience_targeting_language": ["English"],
  "audience_targeting_location": ["Germany", "Netherlands", "Italy", "France"],
  "audience_targeting_company": [],
  "audience_targeting_demographic": [],
  "audience_targeting_education": [],
  "audience_targeting_interests_traits": [],
  "search_keyword": null,
  "search_country": null,
  "scraped_at": "2026-08-10T04:21:18.789Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `ad_id` | string | LinkedIn Ad Library ad id |
| `detail_url` | string | URL of the ad's Ad Library detail page |
| `advertiser_name` | string | Advertiser company name |
| `advertiser_company_url` | string | LinkedIn company page URL of the advertiser |
| `advertiser_company_id` | string | Numeric LinkedIn company id of the advertiser |
| `paid_for_by` | string | The legal entity disclosed as paying for the ad — often a media agency, not the brand |
| `ad_type` | string | Ad format: Single Image Ad, Video Ad, Carousel Ad, Document Ad, Message Ad, etc. |
| `creative_type` | string | Raw LinkedIn creative-type code, e.g. `SPONSORED_STATUS_UPDATE` |
| `promoted_label` | string | The "Promoted" label text shown on the ad card |
| `ad_headline` | string | Ad headline text |
| `ad_body_text` | string | Ad body / commentary text |
| `ad_cta_text` | string | Call-to-action button text, e.g. Learn more, Register |
| `ad_destination_url` | string | Destination URL the ad links to, when disclosed |
| `image_urls` | array of strings | Creative image URLs |
| `video_url` | string | Creative video URL, for video ads |
| `document_title` | string | Document title, for document ads |
| `first_shown_date` | string | Start of the disclosed run window |
| `last_shown_date` | string | End of the disclosed run window |
| `total_impressions_range` | string | EU DSA disclosure: total impressions bucket, e.g. `10k-20k` |
| `impressions_by_country` | array of strings | EU DSA disclosure: per-country impression share, formatted as `Country: NN%` |
| `audience_targeting_language` | array of strings | Targeted languages, when disclosed |
| `audience_targeting_location` | array of strings | Targeted locations, when disclosed |
| `audience_targeting_company` | array of strings | Whether Company was a targeted or excluded parameter |
| `audience_targeting_demographic` | array of strings | Whether Demographic was a targeted or excluded parameter |
| `audience_targeting_education` | array of strings | Whether Education was a targeted or excluded parameter |
| `audience_targeting_interests_traits` | array of strings | Whether Member Interests and Traits was a targeted or excluded parameter |
| `search_keyword` | string | The search keyword that discovered this ad, if any |
| `search_country` | string | The country filter used to discover this ad, if any |
| `scraped_at` | string | ISO 8601 timestamp when the record was scraped |

**A note on the DSA fields**: LinkedIn only publishes the impressions and targeting block for ads targeted at the EU/EEA. Ads outside that scope return the creative and advertiser fields with the disclosure fields left `null` or empty — that's LinkedIn's own publishing rule, not a gap in the actor.

***

### FAQ

#### Does this need a LinkedIn login?

No. The Ad Library is public, and every field this actor returns comes from that public page.

#### Why are some fields null on certain ads?

LinkedIn only discloses total/per-country impressions and audience targeting for ads run in the EU/EEA, per the DSA. Ads outside that scope simply don't carry that data on LinkedIn's own page.

#### Can I search by company instead of keyword?

Yes. Pass one or more numeric LinkedIn company IDs in `companyIds` — find one by opening a company's LinkedIn page and reading the id out of the URL.

#### Does the country filter change which ads I get?

Yes — it restricts results to ads LinkedIn shows as targeting that country, not just ads posted by an advertiser based there.

***

### Need More Features?

Open an issue on the actor's Apify page and describe what you need.

### Why Use This LinkedIn Ad Library Scraper?

- **Reaches the disclosure page, not just the search card** — impressions by country, full targeting parameters, and the paying legal entity, all fields most competing scrapers stop short of
- **Two discovery modes in one actor** — sweep a whole category by keyword, or track specific advertisers by company ID, in the same run
- **Deduplicates automatically** — search the same market from multiple angles without paying for the same ad twice

# Actor input Schema

## `sp_intended_usage` (type: `string`):

What will this data feed? E.g. lead lists, KYB checks, price tracking.

## `sp_improvement_suggestions` (type: `string`):

Provide any feedback or suggestions for improvements.

## `sp_contact` (type: `string`):

We'll personally help with your use case. No spam.

## `resumeCursor` (type: `string`):

Leave empty for a fresh crawl. To CONTINUE a previous run where it stopped — without paying again for records you already received — paste the `resumeCursor` value from that run's Output (the run's OUTPUT key). Resume promptly: the previous run's data expires with your account's retention window (free tier: your ~10 most recent runs).

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

One or more keywords to search the Ad Library for (advertiser name, brand, or free-text query). Each keyword is searched independently. Leave empty if using Company IDs instead.

## `companyIds` (type: `array`):

Optional. Numeric LinkedIn company IDs (from linkedin.com/company/<id>) to fetch ads for directly, instead of a keyword search. At least one of Search Keywords or Company IDs is required.

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

Restrict results to ads that ran targeting these countries. Leave empty to search worldwide.

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

Restrict results to ads that ran within this window.

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

Maximum number of ads to scrape.

## Actor input object example

```json
{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "keywords": [
    "hubspot"
  ],
  "companyIds": [],
  "countries": [],
  "dateOption": "",
  "maxItems": 10
}
```

# Actor output Schema

## `results` (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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "keywords": [
        "hubspot"
    ],
    "companyIds": [],
    "countries": [],
    "dateOption": "",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("jungle_synthesizer/linkedin-ad-library-advertiser-disclosure-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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "keywords": ["hubspot"],
    "companyIds": [],
    "countries": [],
    "dateOption": "",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("jungle_synthesizer/linkedin-ad-library-advertiser-disclosure-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 '{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "keywords": [
    "hubspot"
  ],
  "companyIds": [],
  "countries": [],
  "dateOption": "",
  "maxItems": 10
}' |
apify call jungle_synthesizer/linkedin-ad-library-advertiser-disclosure-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jungle_synthesizer/linkedin-ad-library-advertiser-disclosure-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/QTSGR9ghSahh3Wo9R/builds/ZKyuPvV1gVJQZWPe7/openapi.json
