# LinkedIn Ad Library Scraper — Competitor LinkedIn Ads Library (`hyperbach/linkedin-ad-library-scraper`) Actor

Scrape every ad a company runs in the LinkedIn Ads Library, with the full detail page: copy, landing URL with UTMs, payer, run dates, impressions by country and targeting. Pin a competitor by company URL, or search by keyword or country. Monitoring, company facts, AI tags. No login.

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

## Pricing

from $0.35 / 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 — Competitor LinkedIn Ads Library

**Every ad a company runs on LinkedIn, with the whole detail page — the full copy, the destination URL with its UTMs, who paid, the run dates, the impressions by country and the targeting the advertiser chose.** Pin the company by its LinkedIn URL, id or slug and get its own ads, not the partner agencies' that mention its name. Search by keyword, payer or country too, with LinkedIn's own filters. Every format: single image, video, carousel with every card, document, article, event, message, text, spotlight, thought-leader ads with the member who posted, and Employer Brand cards. **[Follow a competitor on a schedule](https://apify.com/hyperbach/linkedin-ad-library-scraper/examples/follow-a-competitors-linkedin-ads-weekly) and get only their new ads.** Same column names as our [Google Ads Transparency Scraper](https://apify.com/hyperbach/google-ads-transparency-scraper) and [Meta Ad Library Scraper](https://apify.com/hyperbach/meta-ads-library-scraper). **Proxies are handled for you.**

### Why this scraper, not the other LinkedIn ad actors

- **It runs.** LinkedIn started refusing Apify's datacenter IPs around 2026-09-06: of four actors in this category run in parallel on 2026-09-26, three came back blocked, and nine of the actors we ran ended a blocked or empty run as "0 rows, SUCCEEDED". This actor reads through residential exits, paces each under LinkedIn's limit (about 25 requests a minute per IP, measured), and moves to a fresh exit when one is refused. A run that could not read the source ends PARTIAL or FAILED and says why — never green and empty.
- **The company, not everyone who mentions it.** LinkedIn's advertiser search is a name match: "hubspot" finds 2,552 ads, and 8 of the first 25 are partner agencies. Give a company URL, id or slug and the run pins the company itself (1,412 ads, 1,410 delivered on a full walk). `includeRelatedAdvertisers` asks for the agencies too, when you want them.
- **The whole detail page, not the list card.** The list shows three lines of copy; most actors stop there or drop fields on the way. Each row here carries everything the page publishes: full copy, headline, CTA, destination URL with the advertiser's UTMs, links inside the copy, every carousel card, payer, run dates, total impressions, impressions for every country including the "< 1%" rows, languages and locations targeted and excluded (the hidden "and N others" expanded), and the six-category targeting matrix. Thought-leader ads name the member who posted and the company that paid.
- **LinkedIn's own filters, all of them.** Countries (several at once), date presets or your own dates, impressions range, targeting categories used or excluded, newest or oldest first, and any search you set up on linkedin.com/ad-library pasted as a URL. Several searches per run, each with its own filters and cap, deduplicated across searches.
- **Monitoring.** `onlyNewAds` skips every ad an earlier run delivered — not returned, not charged. Every row carries `first_seen_at` and `last_seen_at`, and `includeEndedAds` adds a free `ended` row for each ad a complete scan no longer lists (LinkedIn keeps stopped ads listed for up to a year; whether an ad still runs is `is_active` and `last_shown` on every EU row).
- **Who the advertiser is.** `includeCompanyInfo` adds the company's public facts — industry, size, headquarters, founded, website, followers, specialties — to every ad row, and one `advertiser` row per company.
- **What the ad is doing, as columns.** `tagAds` returns typed tags — angle, CTA intent, tone, offer type, funnel stage, the job function it speaks to, sentiment — each with its confidence. `analyzeAds` writes the hook, the concrete offer, the audience and the strategy. `ocrImageAds` reads the words in the artwork; `transcribeVideos` what the video says. No API key of yours needed.
- **Counts and presence.** `resultType: counts` returns LinkedIn's own total per search from one page load. A company with no ads is free — run a list of companies through it to see who advertises at all. Ready-made: [Check which companies advertise on LinkedIn](https://apify.com/hyperbach/linkedin-ad-library-scraper/examples/count-linkedin-ads-per-company).
- **The same columns as our Google and Meta ads scrapers.** `advertiser_name`, `advertiser_id`, `creative_id`, `ad_format`, `headline`, `body_text`, `destination_url`, `cta`, `first_shown`, `last_shown`, `days_shown`, `impressions_min`, `impressions_max`, `image_url`, `ad_url` mean the same thing in all three. One sheet holds a competitor's Google, Meta and LinkedIn advertising.

### Who it's for

- **B2B marketers & demand-gen teams** — read a competitor's live LinkedIn ads — the offer, the landing page with its UTMs, the formats, and which job functions and companies they target.
- **Agencies** — a client's category on LinkedIn in one table: who advertises, how long each ad runs, where it is shown, and what the long runners say.
- **Sales & RevOps** — who is spending on LinkedIn right now in your market, with the company facts to qualify them — a list of accounts with budget, from a public source.
- **Researchers & compliance teams** — the EU transparency data LinkedIn publishes per ad — payer, impressions by country, targeting — in columns, over time.

### Quick start

**Every ad one company runs**

```json
{
  "companies": [
    "https://www.linkedin.com/company/hubspot"
  ],
  "maxAds": 100
}
```

**Ads shown in Germany and France in the last 30 days**

```json
{
  "companies": [
    "hubspot"
  ],
  "countries": [
    "DE",
    "FR"
  ],
  "dateRange": "last-30-days",
  "maxAds": 50
}
```

**A keyword across every advertiser**

```json
{
  "keywords": [
    "cybersecurity"
  ],
  "maxAds": 100
}
```

**Which of these companies advertise, and how much**

```json
{
  "companies": [
    "hubspot",
    "salesforce",
    "pipedrive"
  ],
  "resultType": "counts"
}
```

**Monitoring — only what appeared since the last run**

```json
{
  "companies": [
    "hubspot"
  ],
  "onlyNewAds": true
}
```

### Output

One record per ad:

| field | meaning |
|---|---|
| `row_type` | `ad` for an ad; `count` for a `resultType: counts` row; `advertiser` for the company facts row (`includeCompanyInfo`); `advertiser_profile` and `landing_page` for the free `summaryRows`; `ended` for an ad a complete scan no longer lists (`includeEndedAds`). |
| `creative_id` | LinkedIn's ad id — the number in the ad's detail URL. Unique per ad; the key for `onlyNewAds`. Employer Brand cards, which have none, carry `eb-<companyId>-<channelId>`. |
| `ad_url` | The ad's page in LinkedIn's Ad Library. |
| `ad_format` | `image`, `video`, `carousel`, `document`, `article`, `event`, `message`, `conversation`, `text`, `spotlight`, `follower`, `job` or `employer_brand`. |
| `creative_type` | LinkedIn's own name for the format (`SPONSORED_STATUS_UPDATE`, `SPONSORED_VIDEO`, `SPONSORED_UPDATE_CAROUSEL`, …). |
| `format_label` | The format as LinkedIn labels it on the page ("Single Image Ad"). |
| `advertiser_name` | The advertiser as LinkedIn shows it. |
| `advertiser_id` | The advertiser's LinkedIn company id. Join on this across runs and platforms; `companies` takes it. |
| `advertiser_slug` | The company's LinkedIn vanity name (`hubspot`), when the page shows it. |
| `advertiser_type` | `company`, or `member` for an ad run from a person's profile. |
| `advertiser_url` | The advertiser's LinkedIn page. |
| `advertiser_logo_url` | The advertiser's logo. |
| `member_name` | Thought-leader ads: the member whose post the company promoted. |
| `member_headline` | Thought-leader ads: that member's headline. |
| `member_url` | Thought-leader ads: that member's profile. |
| `payer` | The "Paid for by" entity, without the prefix — often the legal name. |
| `headline` | The ad's headline (the bold line under the image, the event or document title, the text ad's title). |
| `headline_description` | The line under the headline, when the ad has one. |
| `body_text` | The ad's full copy from the detail page, line breaks kept. For a message ad, the message. Null when details are off and the list cut the copy. |
| `body_text_preview` | The copy as the list shows it — cut after about three lines, ending in "…" when cut. |
| `body_text_truncated` | Whether `body_text_preview` was cut by LinkedIn. |
| `cta` | The call-to-action button, as the ad words it ("Register", "Demo erhalten"). |
| `cta_buttons` | Message ads: every button in the message ("Yes, I'm interested", "Maybe later"). |
| `cta_type` | The CTA as one value across languages: `learn_more`, `register`, `sign_up`, `download`, `apply`, `request_demo`, `view_event`, … `other`. |
| `destination_url` | Where the ad sends the click, with the advertiser's UTM and tracking parameters kept (LinkedIn's own `trk` tag removed). |
| `landing_domain` | The destination's domain. |
| `links_in_text` | Links written into the copy (`lnkd.in`, hashtags, mentions). |
| `image_url` | The creative's image; the first card's for a carousel, the cover for an article or Employer Brand card. |
| `image_urls` | Every image of the creative. |
| `video_url` | The video file, highest quality. |
| `video_urls` | Every rendition of the video: `url`, `bitrate`, `type`, best first. |
| `video_preview_url` | The video's poster frame. |
| `document_title` | Document ads: the document's title. |
| `document_url` | Document ads: the document's page manifest on LinkedIn's CDN. |
| `document_cover_url` | Document ads: the cover page image. |
| `document_pages` | Document ads: how many pages the document has. |
| `cards` | Carousel ads: every card — `position`, `title`, `image_url`, `destination_url` (when the card has its own link). |
| `event_name` | Event ads: the event. |
| `event_time` | Event ads: when it happens, as LinkedIn states it. |
| `event_location` | Event ads: where ("Online", a city). |
| `message_sender_name` | Message ads: the person the message comes from. |
| `message_sender_image_url` | Message ads: the sender's photo. |
| `employer_brand_line` | Employer Brand cards: the company line under the name (size · location). |
| `variants_count` | How many versions of the ad the page shows (LinkedIn allows several; one is the norm). |
| `variants` | The other versions, when there are several, with the same fields as the row. |
| `run_dates_text` | The run dates as LinkedIn writes them ("Ran from Sep 23, 2026 to Sep 27, 2026"). |
| `first_shown` | The ad's first day. EU ads only. |
| `last_shown` | The ad's last day so far. EU ads only. |
| `days_shown` | Days from first to last, both counted. |
| `is_active` | Whether the ad's last day is today or yesterday — still running. Null without dates. |
| `launched_last_7_days` | Whether the ad started in the last 7 days. |
| `launched_last_30_days` | Whether the ad started in the last 30 days. |
| `long_running` | Whether the ad has run 30 days or more. |
| `has_eu_transparency` | Whether the page carries LinkedIn's EU transparency block (dates, impressions, targeting) — true for ads shown in the EU. |
| `impressions_bucket` | Total impressions as LinkedIn states the range ("5k-10k", "< 1k"). |
| `impressions_min` | Lower end of that range, as a number. |
| `impressions_max` | Upper end of that range, as a number. Null for an open range ("1M+"). |
| `impressions_by_country` | Every country LinkedIn lists for the ad, in its order: `country`, `country_code` (ISO-2), `share` as written ("42%", "< 1%"), `share_min`, `share_max` as numbers. |
| `top_country` | The country with the largest share of impressions. |
| `countries_count` | How many countries the ad was shown in. |
| `regions` | ISO-2 codes of the countries the ad was shown in (EU ads), the shape our Google and Meta actors use. |
| `targeting_language` | Languages the advertiser targeted. |
| `targeting_language_excluded` | Languages the advertiser excluded. |
| `targeting_locations` | Locations targeted, the ones behind "and N others" included ("Dallas, TX" stays one value). |
| `targeting_locations_excluded` | Locations excluded, all of them. |
| `targeting_parameters` | The targeting matrix: for `audience`, `demographic`, `company`, `education`, `job`, `interests_and_traits`, whether the advertiser `targeted` and whether it `excluded` by it. |
| `targeting_other` | Any other targeting line the page shows: `category`, `kind` (includes / excludes), `values`. |
| `copy_length` | Characters in the copy. |
| `text_language` | The copy's language (ISO code), from its common words; null when too short to tell. |
| `sentiment_score` | Free sentiment of English copy, -1..1 (AFINN word list); null for other languages and when no word is scored. |
| `analysis_angle` | `tagAds`: the persuasion angle — `problem_solution`, `social_proof`, `scarcity`, `discount`, `feature`, `lifestyle`, `curiosity`, `education`, `comparison`, `brand`, `other`. |
| `analysis_cta_intent` | `tagAds`: what the ad asks the reader to do — `purchase`, `signup`, `download`, `learn_more`, `contact`, `visit`, `watch`, `apply`, `book`, `other`. |
| `analysis_tone` | `tagAds`: the tone of the copy. |
| `analysis_sentiment` | `tagAds`: emotional valence, -1..1. |
| `analysis_offer_type` | `tagAds`: what is offered — `event_or_webinar`, `demo_or_consultation`, `free_tool_or_trial`, `gated_content`, `product_announcement`, `customer_story`, `hiring`, `thought_leadership`, `discount`, `other`. |
| `analysis_funnel_stage` | `tagAds`: `awareness`, `consideration` or `conversion`. |
| `analysis_target_function` | `tagAds`: the job function the ad speaks to — `marketing`, `sales`, `customer_success`, `it_security`, `hr_recruiting`, `finance`, `operations_logistics`, `executives_founders`, `engineering`, `general`. |
| `analysis_confidence` | `tagAds`: the confidence (0..1) of each tag, by tag name. Drop the unsure ones in your own pipeline. |
| `analysis_hook` | `analyzeAds`: the line meant to stop the scroll. |
| `analysis_offer` | `analyzeAds`: the concrete offer, or null when the ad makes none. |
| `analysis_audience` | `analyzeAds`: who the ad is written for, in a few words. |
| `analysis_strategy` | `analyzeAds`: what the advertiser is trying to achieve with the ad, in one sentence. |
| `ad_ocr_text` | `ocrImageAds`: the words in an image ad's artwork (image ads only; a video's poster frame is not read). |
| `transcript` | `transcribeVideos`: what the video says. |
| `transcript_language` | `transcribeVideos`: the language spoken. |
| `hook_3s` | `transcribeVideos`: the first three seconds of speech. |
| `speech_seconds` | `transcribeVideos`: seconds of speech in the video. |
| `video_duration_s` | `transcribeVideos`: the video's length in seconds. |
| `transcript_segments` | `transcribeVideos`: the transcript in timed segments — `start`, `end` (seconds), `text`. |
| `advertiser_industry` | `includeCompanyInfo`: the company's industry on LinkedIn. |
| `advertiser_size` | `includeCompanyInfo`: company size as LinkedIn states it ("5,001-10,000 employees"). |
| `advertiser_employees_min` | `includeCompanyInfo`: lower end of the size range. |
| `advertiser_employees_max` | `includeCompanyInfo`: upper end of the size range. |
| `advertiser_employees_on_linkedin` | `includeCompanyInfo`: how many members list the company as their employer on LinkedIn. |
| `advertiser_tagline` | `includeCompanyInfo`: the company's tagline on its LinkedIn page. |
| `advertiser_similar_pages` | `includeCompanyInfo`: the companies LinkedIn shows as similar — `name`, `slug`, `industry`, `location`. |
| `advertiser_headquarters` | `includeCompanyInfo`: headquarters. |
| `advertiser_founded` | `includeCompanyInfo`: year founded. |
| `advertiser_company_type` | `includeCompanyInfo`: public, private, non-profit… |
| `advertiser_website` | `includeCompanyInfo`: the company's website. |
| `advertiser_followers` | `includeCompanyInfo`: LinkedIn followers. |
| `advertiser_specialties` | `includeCompanyInfo`: the specialties the company lists. |
| `detail_status` | `ok` when the detail page was read; `skipped` with `includeDetails` off; `failed: …` when it could not be read (not billed for details); `not_found` when LinkedIn no longer has the ad. |
| `matched_query` | The search that found the ad, as you gave it. |
| `matched_queries` | The search that delivered the ad. An ad is delivered once per run; when several of your searches listed it, all of them are in the run's `MATCHES` record. |
| `claimed_count` | LinkedIn's own total for the search ("N ads match") — check completeness against it. |
| `first_seen_at` | Monitoring (`onlyNewAds` / `includeEndedAds`): the day a run of yours first saw the ad; null without monitoring. |
| `last_seen_at` | Monitoring: the last day a run of yours saw it. |
| `ended_at` | `ended` rows: the day a complete scan stopped LISTING the ad. LinkedIn keeps an ad listed for up to a year after it stops; whether an ad still runs is `is_active` / `last_shown`. |
| `scraped_at` | When the row was read (UTC). |
| `run_tag` | Your `runTag`, copied onto every row. |
| `has_ads` | `count` rows: whether the search found any ad. |
| `search_query` | `count` rows: the Ad Library query that was counted. |
| `advertiser_description` | `advertiser` rows: the company's About text. |
| `summary_ad_count` | Summary rows: ads delivered for the advertiser or landing page. |
| `summary_active_count` | Summary rows: of those, still running. |
| `summary_longest_days` | Summary rows: the longest run, in days. |
| `summary_formats` | Summary rows: ads per format. |
| `summary_landing_domains` | Summary rows: every landing domain. |
| `summary_countries` | Summary rows: countries by number of ads shown there. |
| `summary_distinct_copies` | Summary rows: distinct copies among the ads. |
| `summary_ad_ids` | Summary rows: the ads counted. |
| `display_url` | `landing_page` rows: the destination's domain. |

Example record:

```json
{
  "row_type": "ad",
  "creative_id": "1560106944",
  "ad_url": "https://www.linkedin.com/ad-library/detail/1560106944",
  "ad_format": "image",
  "creative_type": "SPONSORED_STATUS_UPDATE",
  "format_label": "Single Image Ad",
  "advertiser_name": "HubSpot",
  "advertiser_id": "68529",
  "advertiser_slug": null,
  "advertiser_type": "company",
  "advertiser_url": "https://www.linkedin.com/company/68529",
  "advertiser_logo_url": "https://media.licdn.com/dms/image/v2/C4D0BAQF8H-SLmMDZlA/company-logo_100_100/company-logo_100_100/0/1646683330132/hubspot_logo?e=1792022400&v=beta&t=qdOzAJgZz5obWiFRXGHnfG_E3HiBfKVIzZV5WxPsM-U",
  "member_name": null,
  "member_headline": null,
  "member_url": null,
  "payer": "HubSpot, Inc.",
  "headline": "Sell from anywhere, close everywhere.",
  "headline_description": null,
  "body_text": "Remote-first and built on trust.\nSell from wherever you're sharpest, on your own rhythm. The results are what matter here.",
  "body_text_preview": "Remote-first and built on trust.\nSell from wherever you're sharpest, on your own rhythm. The results are what matter he…",
  "body_text_truncated": false,
  "cta": "Learn more",
  "cta_buttons": null,
  "cta_type": "learn_more",
  "destination_url": "https://www.linkedin.com/organization/68529/campaign/b9cb9124-f8ce-4243-b345-6033313f7d9d",
  "landing_domain": "linkedin.com",
  "links_in_text": null,
  "image_url": "https://media.licdn.com/dms/image/v2/D4E10AQGeVkCkkr6L1w/image-shrink_1280/B4EaBr72KLHkAc-/0/1788517245905/VariantCpng?e=2147483647&v=beta&t=ZMyfSupMWzPFERk9TiTQFkDvJFs3P-lIxdjLGxYIaTA",
  "image_urls": [
    "https://media.licdn.com/dms/image/v2/D4E10AQGeVkCkkr6L1w/image-shrink_1280/B4EaBr72KLHkAc-/0/1788517245905/VariantCpng?e=2147483647&v=beta&t=ZMyfSupMWzPFERk9TiTQFkDvJFs3P-lIxdjLGxYIaTA"
  ],
  "video_url": null,
  "video_urls": null,
  "video_preview_url": null,
  "document_title": null,
  "document_url": null,
  "document_cover_url": null,
  "document_pages": null,
  "cards": null,
  "event_name": null,
  "event_time": null,
  "event_location": null,
  "message_sender_name": null,
  "message_sender_image_url": null,
  "employer_brand_line": null,
  "variants_count": 1,
  "variants": null,
  "run_dates_text": "Ran from Sep 14, 2026 to Sep 27, 2026",
  "first_shown": "2026-09-14",
  "last_shown": "2026-09-27",
  "days_shown": 14,
  "is_active": true,
  "launched_last_7_days": false,
  "launched_last_30_days": true,
  "long_running": false,
  "has_eu_transparency": true,
  "impressions_bucket": "30k-50k",
  "impressions_min": 30000,
  "impressions_max": 50000,
  "impressions_by_country": [
    {
      "country": "United Kingdom",
      "share": "42%",
      "share_min": 42.0,
      "share_max": 42.0
    },
    {
      "country": "Ireland",
      "share": "18%",
      "share_min": 18.0,
      "share_max": 18.0
    },
    {
      "country": "Germany",
      "share": "18%",
      "share_min": 18.0,
      "share_max": 18.0
    },
    {
      "country": "Australia",
      "share": "14%",
      "share_min": 14.0,
      "share_max": 14.0
    },
    {
      "country": "Canada",
      "share": "5%",
      "share_min": 5.0,
      "share_max": 5.0
    },
    {
      "country": "United States",
      "share": "2%",
      "share_min": 2.0,
      "share_max": 2.0
    },
    {
      "country": "New Zealand",
      "share": "< 1%",
      "share_min": 0.0,
      "share_max": 1.0
    },
    {
      "country": "Spain",
      "share": "< 1%",
      "share_min": 0.0,
      "share_max": 1.0
    },
    {
      "country": "United Arab Emirates",
      "share": "< 1%",
      "share_min": 0.0,
      "share_max": 1.0
    },
    {
      "country": "Netherlands",
      "share": "< 1%",
      "share_min": 0.0,
      "share_max": 1.0
    }
  ],
  "top_country": "United Kingdom",
  "countries_count": 10,
  "regions": null,
  "targeting_language": [
    "English"
  ],
  "targeting_language_excluded": null,
  "targeting_locations": [
    "Dallas",
    "TX",
    "Germany",
    "Chicago",
    "IL",
    "Toronto",
    "ON",
    "Australia",
    "Ireland",
    "United Kingdom"
  ],
  "targeting_locations_excluded": [
    "India",
    "New Zealand",
    "United Arab Emirates"
  ],
  "targeting_parameters": {
    "audience": {
      "targeted": false,
      "excluded": false
    },
    "demographic": {
      "targeted": false,
      "excluded": false
    },
    "company": {
      "targeted": true,
      "excluded": true
    },
    "education": {
      "targeted": false,
      "excluded": false
    },
    "job": {
      "targeted": true,
      "excluded": false
    },
    "interests_and_traits": {
      "targeted": false,
      "excluded": false
    }
  },
  "targeting_other": null,
  "copy_length": 122,
  "text_language": "en",
  "sentiment_score": 0.2,
  "analysis_angle": "lifestyle",
  "analysis_cta_intent": "learn_more",
  "analysis_tone": "professional",
  "analysis_sentiment": 0.51,
  "analysis_offer_type": "hiring",
  "analysis_funnel_stage": "consideration",
  "analysis_target_function": "sales",
  "analysis_confidence": {
    "angle": 0.97,
    "cta_intent": 1.0,
    "tone": 0.55,
    "offer_type": 0.56,
    "funnel_stage": 0.86,
    "target_function": 1.0,
    "sentiment": 0.97
  },
  "analysis_hook": null,
  "analysis_offer": null,
  "analysis_audience": null,
  "analysis_strategy": null,
  "ad_ocr_text": null,
  "transcript": null,
  "transcript_language": null,
  "hook_3s": null,
  "speech_seconds": null,
  "video_duration_s": null,
  "transcript_segments": null,
  "advertiser_industry": null,
  "advertiser_size": null,
  "advertiser_employees_min": null,
  "advertiser_employees_max": null,
  "advertiser_employees_on_linkedin": null,
  "advertiser_tagline": null,
  "advertiser_similar_pages": null,
  "advertiser_headquarters": null,
  "advertiser_founded": null,
  "advertiser_company_type": null,
  "advertiser_website": null,
  "advertiser_followers": null,
  "advertiser_specialties": null,
  "detail_status": "ok",
  "matched_query": "hubspot",
  "matched_queries": [
    "hubspot"
  ],
  "claimed_count": 187,
  "first_seen_at": null,
  "last_seen_at": null,
  "ended_at": null,
  "scraped_at": "2026-09-27T09:59:51Z",
  "run_tag": null,
  "has_ads": null,
  "search_query": null,
  "advertiser_description": null,
  "summary_ad_count": null,
  "summary_active_count": null,
  "summary_longest_days": null,
  "summary_formats": null,
  "summary_landing_domains": null,
  "summary_countries": null,
  "summary_distinct_copies": null,
  "summary_ad_ids": null,
  "display_url": null
}
```

### Pricing

**Pay only for what a run actually returns**, with no start fee. Prices are per
1,000 and fall with your Apify plan:

| per 1,000 | Free | Starter | Scale | Business |
|---|---|---|---|---|
| Ad — id, format, advertiser, headline, first lines of the copy, image | $0.70 | $0.595 | $0.49 | $0.35 |
| Detail page on that ad — full copy, destination with UTMs, payer, dates, impressions by country, targeting | +$1.10 | +$0.935 | +$0.77 | +$0.55 |
| Count row (`resultType: counts`), only when the search found ads | $10.00 | $8.50 | $7.00 | $5.00 |
| Advertiser company facts (`includeCompanyInfo`), once per company | $4.00 | $3.40 | $2.80 | $2.00 |
| Typed tags — angle, offer, funnel stage, audience function… (`tagAds`) | +$1.00 | +$0.85 | +$0.70 | +$0.50 |
| Written analysis — hook, offer, audience, strategy (`analyzeAds`), model usage included | +$6.00 | +$5.10 | +$4.20 | +$3.00 |
| Video transcript, when the video has speech (`transcribeVideos`) | +$20.00 | +$17.00 | +$14.00 | +$10.00 |
| Image text read by a vision model, when words were found (`ocrImageAds`) | +$2.00 | +$1.70 | +$1.40 | +$1.00 |

Each fee is charged **per row that actually carries the thing**. A detail page that could
not be read is delivered marked (`detail_status`) and not billed for details; `maxAds` is
exact and never overshot; an ad two of your searches both find is returned and billed once.
Enterprise plans have their own rates — the Actor's Pricing tab shows the price for your plan.

### Usage patterns

- **Follow a competitor weekly** — Put their company URL in `companies`, tick `onlyNewAds`, save it as a task and schedule it. The first run delivers the current set; every later run only what appeared since, and `is_active` / `last_shown` tell you which ads still run. Connect the task to Slack or email through Apify's integrations. Ready-made: [Monitor a competitor's new LinkedIn ads weekly](https://apify.com/hyperbach/linkedin-ad-library-scraper/examples/follow-a-competitors-linkedin-ads-weekly).
- **Find the ads worth copying** — `minDaysRunning: 30` keeps the ads an advertiser has paid to run for a month or more — the ones that work. Add `tagAds` to see which angle and offer they use. Ready-made: [a competitor's long-running LinkedIn ads, tagged by AI](https://apify.com/hyperbach/linkedin-ad-library-scraper/examples/long-running-linkedin-ads-with-ai-tags).
- **Map a market** — A keyword with `countries`, or `countries` alone in `searches` for every ad shown there, plus `includeCompanyInfo`. The result is who advertises in your market, with their industry and size.
- **Compare against their Google and Meta ads** — Run the same competitor through our Google Ads Transparency Scraper and Meta Ad Library Scraper. The three outputs share the column names, so one sheet shows all three platforms.
- **Use a search you already set up** — Set the filters on linkedin.com/ad-library, copy the URL from the address bar into `startUrls`. Every filter in it is kept. A published Google Sheet or a text file with one URL per line works as well.

### Input configuration

| field | type | default | what it does |
|---|---|---|---|
| `companies` | `array` | `[]` | Each entry is one advertiser: a LinkedIn company URL (`linkedin.com/company/hubspot`), a company id (`68529`), a slug (`hubspot`) or a name. An id, URL or slug pins the exact company — only ads that company runs, not the agencies that mention it. A name with spaces is matched as LinkedIn's advertiser-name search and kept to advertisers of exactly that name (switch on `includeRelatedAdvertisers` to keep the agencies too). |
| `keywords` | `array` | `[]` | Words in the ad. One search per line, across every advertiser. |
| `payers` | `array` | `[]` | The entity LinkedIn shows as "Paid for by" — often the legal name, e.g. `HubSpot, Inc.`. |
| `startUrls` | `array` | `[]` | LinkedIn URLs: an Ad Library search you set up on linkedin.com/ad-library (every filter in it is kept), an ad's detail page, an Employer Brand page, or a company page. A text file, CSV or published Google Sheet with one URL per line works too (Link remote text file). |
| `adIds` | `array` | `[]` | Single ads by id or detail URL (`linkedin.com/ad-library/detail/1558773703`): re-read an ad you already know, e.g. to watch its impressions move. |
| `searches` | `array` | `[]` | Several searches in one run, each with its own filters and cap. Each entry names one of `company`, `keyword`, `payer` or `url`, plus any of `countries`, `dateRange`, `startDate`, `endDate`, `impressionsMin`, `impressionsMax`, `targetingIncludes`, `targetingExcludes`, `sortOrder`, `includeRelatedAdvertisers`, `maxAds`. An entry with only `countries` is every ad shown there. Example: `[{"company": "hubspot", "countries": ["DE", "FR"]}, {"keyword": "webinar", "dateRange": "last-30-days", "maxAds": 200}]`. |
| `resultType` | `ads` / `counts` | `"ads"` | `counts` returns one row per search with LinkedIn's own total, from one page load and no ads scraped. A search that finds nothing is free: use it to check which of a list of companies advertise at all. |
| `countries` | `array` | `[]` | Ads shown in these countries. Empty = everywhere. Several are combined (shown in any of them). LinkedIn ignores this filter when a targeting or impressions filter is set. |
| `dateRange` | `any` / `last-30-days` / `current-month` / `current-year` / `last-year` / `custom` | `"any"` | When the ad ran, as LinkedIn's own date filter. For your own dates set `startDate` / `endDate`. |
| `startDate` | `string` |  | YYYY-MM-DD. With an end date or alone. |
| `endDate` | `string` |  | YYYY-MM-DD. |
| `impressionsMin` | `integer` |  | LinkedIn's impressions filter. It applies to ads shown in the EU (the only ads LinkedIn publishes impressions for), and LinkedIn then ignores the country filter (measured 2026-09-28). |
| `impressionsMax` | `integer` |  | Upper bound for the impressions filter; EU ads only. |
| `targetingIncludes` | `array` | `[]` | Only ads whose advertiser targeted by these categories (EU ads). LinkedIn ignores the country filter when this is set. |
| `targetingExcludes` | `array` | `[]` | Only ads whose advertiser excluded audiences by these categories (EU ads). |
| `sortOrder` | `newest` / `oldest` | `"newest"` | The order LinkedIn lists the ads in. |
| `includeRelatedAdvertisers` | `boolean` | `false` | For companies: also return ads by other advertisers whose name contains the company's (partner agencies, resellers, subsidiaries) — LinkedIn's own advertiser-name search. Off, only the company itself. |
| `adFormats` | `array` | `[]` | Keep only these formats. Filtered on the list before any detail page is opened, so a skipped ad costs nothing. Conversation ads are labelled Message Ad by LinkedIn and come as `message`. |
| `minDaysRunning` | `integer` | `0` | Only ads that ran this many days or more — the long runners, the ones worth copying. Needs run dates, which LinkedIn publishes for ads shown in the EU. |
| `onlyActive` | `boolean` | `false` | Only ads whose last day is today or yesterday (EU ads carry dates). |
| `includeDetails` | `boolean` | `true` | On: the full copy, destination URL with UTMs, payer, run dates, impressions by country and the targeting. Off: the list view only (the first lines of the copy, headline, format, image) — cheaper and faster. |
| `includeCompanyInfo` | `boolean` | `false` | Industry, company size, headquarters, founded, type, website, followers and specialties from the company's public LinkedIn page — on every ad row, plus one `advertiser` row per company. |
| `tagAds` | `boolean` | `false` | Typed tags per ad from a classification model: angle, CTA intent, tone, offer type, funnel stage, target job function and sentiment, each with a confidence. |
| `analyzeAds` | `boolean` | `false` | A language model reads the ad and writes its hook, the concrete offer, who it is written for and the advertiser's apparent strategy. Model usage included. |
| `ocrImageAds` | `boolean` | `false` | The words baked into the artwork, read by a vision model (`ad_ocr_text`). Model usage included. |
| `transcribeVideos` | `boolean` | `false` | What the video says, as text, with its language and first three seconds. A silent video is checked and not billed. |
| `openaiApiKey` | `string` |  | Your own key for `analyzeAds`, `ocrImageAds` and `transcribeVideos`, if you want the model usage on your account. Not needed: the Actor has its own. |
| `summaryRows` | `boolean` | `false` | Free rows after the ads: one `advertiser_profile` per advertiser (ads, still running, longest run, formats, countries, landing domains) and one `landing_page` per destination URL. |
| `outputFormat` | `full` / `compact` | `"full"` | `compact` keeps the columns most people use and drops the nested ones. |
| `maxAds` | `integer` | `0` | Ads delivered in the whole run, exactly (0 = no limit). Never exceeded, and you are never billed for more. |
| `maxAdsPerSearch` | `integer` | `0` | Ads per company, keyword, payer or URL (0 = no limit). |
| `onlyNewAds` | `boolean` | `false` | Skip every ad an earlier run with the same searches already delivered — not returned, not charged. Schedule the run and each one brings only what is new. |
| `includeEndedAds` | `boolean` | `false` | Add an `ended` row for every ad an earlier run saw that a complete scan of the same search no longer lists. Free. |
| `stateStoreName` | `string` |  | Name for the memory `onlyNewAds` and `includeEndedAds` use. Empty: named after your searches, so the same input on a schedule finds its own memory. |
| `runTag` | `string` |  | Your label, copied onto every row (`run_tag`), to tell runs apart in one sheet. |
| `resumeFromRunId` | `string` |  | The id of a run that stopped early (timeout, abort) with the same input: this run carries on from where it stopped, with no ad delivered or billed twice. |

### FAQ

**Why are the destination, dates, impressions and targeting empty on some ads?**

LinkedIn publishes them only for ads shown in the EU (the Digital Services Act): on an ad shown only outside the EU the page carries no destination link, no run dates, no impressions and no targeting. That ad has the copy, the headline, the CTA label, the payer, the format and the media, and `has_eu_transparency: false`. Add EU countries to `countries` to get the ads that carry them. `minDaysRunning` and `onlyActive` read those dates, so they keep EU ads only.

**I searched for a company by name and got ads from other advertisers.**

A name with spaces is LinkedIn's advertiser-name search, which matches any advertiser whose name contains it. Without `includeRelatedAdvertisers` the run keeps only advertisers of exactly that name. For the company itself and nothing else, give its LinkedIn company URL, id or slug: that pins it.

**How complete is a company's list?**

A company's walk runs to the end of LinkedIn's list: 1,410 of HubSpot's 1,412 ads on the full walk we measured (2026-09-27). Every row carries `claimed_count`, LinkedIn's own total, so you can check it on your run.

**What does a row cost me, and what if a page fails?**

Per ad, and per detail page read. A detail page that could not be read is still delivered, marked `detail_status: failed…`, and not billed for details. `maxAds` is exact. Nothing is billed for a run that failed.

**What are Employer Brand ads?**

LinkedIn's "Company Discovery Reminder" ads: a company card inviting candidates to follow it. They carry no ad id; the row's `creative_id` is `eb-<companyId>-<channelId>`, stable across runs.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/linkedin-ad-library-scraper').call({"companies": ["https://www.linkedin.com/company/hubspot"], "maxAds": 100});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/linkedin-ad-library-scraper').call(run_input={'companies': ['https://www.linkedin.com/company/hubspot'], 'maxAds': 100})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/linkedin-ad-library-scraper --input '{"companies": ["https://www.linkedin.com/company/hubspot"], "maxAds": 100}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~linkedin-ad-library-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"companies": ["https://www.linkedin.com/company/hubspot"], "maxAds": 100}'
```

### Support

apify@hyperbach.com

*This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.*

# Actor input Schema

## `companies` (type: `array`):

Each entry is one advertiser: a LinkedIn company URL (`linkedin.com/company/hubspot`), a company id (`68529`), a slug (`hubspot`) or a name. An id, URL or slug pins the exact company — only ads that company runs, not the agencies that mention it. A name with spaces is matched as LinkedIn's advertiser-name search and kept to advertisers of exactly that name (switch on `includeRelatedAdvertisers` to keep the agencies too).

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

Words in the ad. One search per line, across every advertiser.

## `payers` (type: `array`):

The entity LinkedIn shows as "Paid for by" — often the legal name, e.g. `HubSpot, Inc.`.

## `startUrls` (type: `array`):

LinkedIn URLs: an Ad Library search you set up on linkedin.com/ad-library (every filter in it is kept), an ad's detail page, an Employer Brand page, or a company page. A text file, CSV or published Google Sheet with one URL per line works too (Link remote text file).

## `adIds` (type: `array`):

Single ads by id or detail URL (`linkedin.com/ad-library/detail/1558773703`): re-read an ad you already know, e.g. to watch its impressions move.

## `searches` (type: `array`):

Several searches in one run, each with its own filters and cap. Each entry names one of `company`, `keyword`, `payer` or `url`, plus any of `countries`, `dateRange`, `startDate`, `endDate`, `impressionsMin`, `impressionsMax`, `targetingIncludes`, `targetingExcludes`, `sortOrder`, `includeRelatedAdvertisers`, `maxAds`. An entry with only `countries` is every ad shown there. Example: `[{"company": "hubspot", "countries": ["DE", "FR"]}, {"keyword": "webinar", "dateRange": "last-30-days", "maxAds": 200}]`.

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

`counts` returns one row per search with LinkedIn's own total, from one page load and no ads scraped. A search that finds nothing is free: use it to check which of a list of companies advertise at all.

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

Ads shown in these countries. Empty = everywhere. Several are combined (shown in any of them). LinkedIn ignores this filter when a targeting or impressions filter is set.

## `dateRange` (type: `string`):

When the ad ran, as LinkedIn's own date filter. For your own dates set `startDate` / `endDate`.

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

YYYY-MM-DD. With an end date or alone.

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

YYYY-MM-DD.

## `impressionsMin` (type: `integer`):

LinkedIn's impressions filter. It applies to ads shown in the EU (the only ads LinkedIn publishes impressions for), and LinkedIn then ignores the country filter (measured 2026-09-28).

## `impressionsMax` (type: `integer`):

Upper bound for the impressions filter; EU ads only.

## `targetingIncludes` (type: `array`):

Only ads whose advertiser targeted by these categories (EU ads). LinkedIn ignores the country filter when this is set.

## `targetingExcludes` (type: `array`):

Only ads whose advertiser excluded audiences by these categories (EU ads).

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

The order LinkedIn lists the ads in.

## `includeRelatedAdvertisers` (type: `boolean`):

For companies: also return ads by other advertisers whose name contains the company's (partner agencies, resellers, subsidiaries) — LinkedIn's own advertiser-name search. Off, only the company itself.

## `adFormats` (type: `array`):

Keep only these formats. Filtered on the list before any detail page is opened, so a skipped ad costs nothing. Conversation ads are labelled Message Ad by LinkedIn and come as `message`.

## `minDaysRunning` (type: `integer`):

Only ads that ran this many days or more — the long runners, the ones worth copying. Needs run dates, which LinkedIn publishes for ads shown in the EU.

## `onlyActive` (type: `boolean`):

Only ads whose last day is today or yesterday (EU ads carry dates).

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

On: the full copy, destination URL with UTMs, payer, run dates, impressions by country and the targeting. Off: the list view only (the first lines of the copy, headline, format, image) — cheaper and faster.

## `includeCompanyInfo` (type: `boolean`):

Industry, company size, headquarters, founded, type, website, followers and specialties from the company's public LinkedIn page — on every ad row, plus one `advertiser` row per company.

## `tagAds` (type: `boolean`):

Typed tags per ad from a classification model: angle, CTA intent, tone, offer type, funnel stage, target job function and sentiment, each with a confidence.

## `analyzeAds` (type: `boolean`):

A language model reads the ad and writes its hook, the concrete offer, who it is written for and the advertiser's apparent strategy. Model usage included.

## `ocrImageAds` (type: `boolean`):

The words baked into the artwork, read by a vision model (`ad_ocr_text`). Model usage included.

## `transcribeVideos` (type: `boolean`):

What the video says, as text, with its language and first three seconds. A silent video is checked and not billed.

## `openaiApiKey` (type: `string`):

Your own key for `analyzeAds`, `ocrImageAds` and `transcribeVideos`, if you want the model usage on your account. Not needed: the Actor has its own.

## `summaryRows` (type: `boolean`):

Free rows after the ads: one `advertiser_profile` per advertiser (ads, still running, longest run, formats, countries, landing domains) and one `landing_page` per destination URL.

## `outputFormat` (type: `string`):

`compact` keeps the columns most people use and drops the nested ones.

## `maxAds` (type: `integer`):

Ads delivered in the whole run, exactly (0 = no limit). Never exceeded, and you are never billed for more.

## `maxAdsPerSearch` (type: `integer`):

Ads per company, keyword, payer or URL (0 = no limit).

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

Skip every ad an earlier run with the same searches already delivered — not returned, not charged. Schedule the run and each one brings only what is new.

## `includeEndedAds` (type: `boolean`):

Add an `ended` row for every ad an earlier run saw that a complete scan of the same search no longer lists. Free.

## `stateStoreName` (type: `string`):

Name for the memory `onlyNewAds` and `includeEndedAds` use. Empty: named after your searches, so the same input on a schedule finds its own memory.

## `runTag` (type: `string`):

Your label, copied onto every row (`run_tag`), to tell runs apart in one sheet.

## `resumeFromRunId` (type: `string`):

The id of a run that stopped early (timeout, abort) with the same input: this run carries on from where it stopped, with no ad delivered or billed twice.

## Actor input object example

```json
{
  "companies": [
    "hubspot"
  ],
  "keywords": [],
  "payers": [],
  "startUrls": [],
  "adIds": [],
  "searches": [],
  "resultType": "ads",
  "countries": [],
  "dateRange": "any",
  "targetingIncludes": [],
  "targetingExcludes": [],
  "sortOrder": "newest",
  "includeRelatedAdvertisers": false,
  "adFormats": [],
  "minDaysRunning": 0,
  "onlyActive": false,
  "includeDetails": true,
  "includeCompanyInfo": false,
  "tagAds": false,
  "analyzeAds": false,
  "ocrImageAds": false,
  "transcribeVideos": false,
  "summaryRows": false,
  "outputFormat": "full",
  "maxAds": 50,
  "maxAdsPerSearch": 0,
  "onlyNewAds": false,
  "includeEndedAds": false
}
```

# Actor output Schema

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

All scraped records in the default dataset. One record per ad:

## `runSummary` (type: `string`):

How the run went, per search: LinkedIn's own total, ads delivered, pages read, ads skipped as already delivered or filtered out, detail pages that failed, and why the walk stopped; plus whether the run is complete.

## `matches` (type: `string`):

For ads that more than one of your searches found: the ad id and every search that listed it. Each ad is delivered and billed once.

# 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 = {
    "companies": [
        "hubspot"
    ],
    "maxAds": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/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 = {
    "companies": ["hubspot"],
    "maxAds": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/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 '{
  "companies": [
    "hubspot"
  ],
  "maxAds": 50
}' |
apify call hyperbach/linkedin-ad-library-scraper --silent --output-dataset

```

## MCP server setup

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