LinkedIn Ad Library scraper artworkB2B ads & EU transparency

LinkedIn Ad Library Scraper.

See how other companies advertise to professional audiences. Compare their offers and creative formats, then use the available EU disclosures to research audience and market choices.

Run on Apify

Ways to use this scraper.

Try these examples for marketing, growth and competitor research.

01

Compare B2B offers before a campaign

For a SaaS campaign, follow three competitors and tag their ads as demos, webinars, reports or product offers. Compare the promises and calls to action before choosing your next acquisition test.

Keep an offer comparison with ad copy, creative links and your test ideas.

02

Research the audience and markets an advertiser uses

For ads with EU disclosures, review the targeting fields and country impression shares. Record what LinkedIn discloses separately from your assumptions about the intended audience.

Write a brief with source links and keep impression ranges as ranges.

03

Make an agency audit more specific

Review a prospect's public ads, creative formats and landing pages. Use examples from those ads to ask about message consistency and campaign objectives.

Prepare a short creative audit with the prospect's own ads as references.

FROM FIRST RUN TO REPEATABLE WORKFLOW

Your first run, step by step.

  1. Open the Actor on Apify, select its Input tab and switch to the JSON editor if you want to paste a configuration.
  2. Replace HubSpot with the advertiser name as LinkedIn displays it. Start with exactAdvertiserMatch true for a focused check. If that returns nothing, try false and inspect the advertiser names before accepting the broader matches.
  3. Choose DE for a small EU sample, maxAds 30 and scrapeAdDetails false. Once the advertiser matches, enable details to collect the available payer, date, targeting and impression fields.
  4. Start the run and watch the log. Check which targets and filters were actually read before assuming an empty result means nothing exists.
  5. Open Storage → Dataset, inspect several records and export JSON for nested data or CSV for a first spreadsheet review.
First-run configuration
{
  "advertisers": ["HubSpot"],
  "countries": ["DE"],
  "maxAds": 30,
  "exactAdvertiserMatch": true,
  "scrapeAdDetails": false
}

Paste this into the Actor’s JSON input editor. Replace the example targets with yours before running.

Check the current input form on Apify

Choose how to search.

Advertiser names and keywords can be combined to narrow a search. You can also reuse a filtered LinkedIn Ad Library URL. Exact advertiser matching is optional, and EU ad-detail fields are richer than those for many other markets.

Search methodWhen to use itWhat changes
Advertiser or brand nameWhen to use itYou have a competitor watchlist.What changesLinkedIn company names or company-page links. Name matching is loose by default and can include partners or agencies. Enable exactAdvertiserMatch if you want only the named advertiser.
Keyword discoveryWhen to use itYou want to find relevant results before choosing specific targets.What changesWords in the ad content. With advertisers also provided, every advertiser is searched for every keyword. Without advertisers, the keywords discover ads across companies.
Ad Library URLsWhen to use itYou already configured the search on the source website.What changesLinkedIn Ad Library search or detail links. Search URLs keep their own filters. A detail URL retrieves one ad; it is not a company-page URL or a LinkedIn job search.

The difference that matters

Adds payer, CTA, landing links, media and available EU run dates, impression ranges and targeting. One extra request per ad. Outside EU disclosures, some detail fields stay empty.

Compare the related scraper

Every input, explained.

Use the exact field names below in JSON. In Apify’s form, enter list items separately, choose filters, and keep numbers and booleans in their proper types.

Default and prefill are different. A default applies when you omit a setting; a prefill is an example already entered in Apify’s form. Review prefilled targets and limits before every run. Some settings have no schema default. You still need to supply at least one supported target.

Targets and search inputs3
advertisers
ListForm prefill: ["HubSpot"]

LinkedIn company names or company-page links. Name matching is loose by default and can include partners or agencies. Enable exactAdvertiserMatch if you want only the named advertiser.

keywords
List

Words in the ad content. With advertisers also provided, every advertiser is searched for every keyword. Without advertisers, the keywords discover ads across companies.

startUrls
List

LinkedIn Ad Library search or detail links. Search URLs keep their own filters. A detail URL retrieves one ad; it is not a company-page URL or a LinkedIn job search.

Markets, dates and filters6
countries
List

Countries where ads were shown. Codes, names and groups such as EU or DACH are accepted. Several countries form a union, not a separate row for each country; EU detail disclosures vary by ad.

dateRange
TextDefault: all-time

Choose a preset or custom-date-range. The latter uses startDate and endDate. These describe the ad’s run period, not when you collected the record.

Suggested JSON values
all-time last-30-days current-month current-year last-year custom-date-range
startDate
Text

Beginning of a custom range, preferably YYYY-MM-DD. Supplying a custom date activates a custom-range search; use the matching dateRange value in copyable configurations for clarity.

endDate
Text

End of the custom range. Use YYYY-MM-DD or a supported relative date such as today. Keep the same window when comparing several advertisers.

sortBy
TextDefault: newest

newest starts with recent ads; oldest starts at the other end. With maxAds or maxAdsPerSearch, this changes the slice you see first, not the size of the archive.

Suggested JSON values
newest oldest
exactAdvertiserMatch
True or falseDefault: false

Keeps advertiser names matching the search after ignoring case and punctuation. Use for a brand-only audit. It can exclude subsidiaries with different legal names, so check sample results first.

Limits, details and proxies6
maxAds
IntegerForm prefill: 100

A hard cap on unique ads across the whole run, including all targets and markets. Start with 50. Leaving it empty removes this cap; a broad search can then become much larger.

maxAdsPerSearch
Integer

Cap each advertiser-keyword search separately. Pair with maxAds to stop one large advertiser from consuming the whole run. The global cap still wins if reached first.

scrapeAdDetails
True or falseDefault: true

Adds payer, CTA, landing links, media and available EU run dates, impression ranges and targeting. One extra request per ad. Outside EU disclosures, some detail fields stay empty.

maxConcurrency
IntegerDefault: 3

1-10 advertiser or keyword searches in parallel. Each search uses its own proxy sessions. Without a usable proxy, requests are paced one at a time.

detailConcurrency
IntegerDefault: 4

1-10 detail requests in parallel within each search. This is separate from maxConcurrency. Increasing both multiplies simultaneous work and proxy demand; keep the defaults for a first run.

proxyConfiguration
ObjectDefault: {"useApifyProxy":true}Form prefill: {"useApifyProxy":true}

A proxy spreads LinkedIn’s per-IP rate limit across sessions. Residential exits are generally more reliable than datacenter. If blocked, lower concurrency and check proxy availability rather than only retrying faster.

Advanced settings and recovery3
proxyRotations
IntegerDefault: 4

Retries refused work with a new proxy session. More retries can recover temporary blocks but add time and traffic. Keep the default until the log shows a reason to change it.

resume
True or falseDefault: true

Saves progress about every 30 seconds so an Apify restart or migration can continue the current run. Leave it on for normal use.

continueFromLastRun
True or falseDefault: false

Continues unfinished work from the previous run with matching input. Earlier results remain in that run’s dataset. Keep false for a fresh collection or a recurring snapshot.

This reference follows the Actor’s published input fields. Check the live form before changing a production workflow. Check the current input form on Apify.

Configurations you can copy.

Each example is a separate run. Start small, inspect the results, then increase coverage. Update the targets, countries and dates to match your question.

A focused EU advertiser audit

Collect the last 30 days for one exact advertiser in Germany, including ad details. Keep the payer and available impression fields with source links. Missing fields remain missing; do not fill them with assumed values.

A focused EU advertiser audit
{
  "advertisers": ["HubSpot"],
  "countries": ["DE"],
  "dateRange": "last-30-days",
  "exactAdvertiserMatch": true,
  "maxAds": 50,
  "scrapeAdDetails": true
}

Discover messages across advertisers

Search three topics across the EU without advertiser restrictions. Each search gets at most 25 ads, so this configuration can yield up to 75 unique ads even though the global cap is 100. Overlap can reduce that total further.

Discover messages across advertisers
{
  "keywords": ["marketing automation", "sales CRM", "lead generation"],
  "countries": ["EU"],
  "maxAds": 100,
  "maxAdsPerSearch": 25,
  "scrapeAdDetails": false
}

Compare a fixed campaign period

Compare two advertisers in Germany and France during September 2026. Countries form a combined search filter. Change the dates, inspect loose advertiser matches and use the per-search cap to stop one brand dominating the sample.

Compare a fixed campaign period
{
  "advertisers": ["HubSpot", "Salesforce"],
  "countries": ["DE", "FR"],
  "dateRange": "custom-date-range",
  "startDate": "2026-09-01",
  "endDate": "2026-09-30",
  "maxAds": 100,
  "maxAdsPerSearch": 50,
  "exactAdvertiserMatch": false,
  "scrapeAdDetails": true
}

Run, check, export, repeat.

Keep ad IDs and advertiser names alongside creative text, source links and available detail fields. EU details can include payer, impressions by country and targeting; other markets often expose less. Use nested JSON as the source record, flatten selected fields for reporting and treat missing values separately from zero.

  1. Check the dataset and the run’s SUMMARY record. Compare the number collected with your cap, inspect failed or skipped inputs, and verify a few original source links.
  2. Keep the original IDs and add collected_at and run_id when saving results. Export CSV for flat columns; retain JSON when arrays or nested details matter.
  3. Save the tested configuration as an Apify Task and schedule it. For repeated snapshots, leave continueFromLastRun false. Deduplicate new records by source ID while retaining each observation date.
  4. In Make or n8n, wait for a successful run, fetch its dataset and map fields into Sheets or your warehouse. Send records to Looker Studio through a reporting table; use dbt to flatten and test warehouse models.
  5. The same JSON works with Apify’s Actor API. In Claude with Apify MCP, name this Actor, ask it to inspect the live schema, and give explicit targets, markets and result limits before it runs.

Resume is not a fresh snapshot

resume protects the current run if Apify restarts it. continueFromLastRun continues an earlier run with the same configuration; earlier records stay in the earlier dataset. Combine both datasets for the complete collection, and raise a previously reached result cap when continuing. Start fresh when you want to see what changed today.

Follow the Sheets, Claude, Looker and BigQuery setup guides
Run this Actor from the API

Save one configuration above as input.json. Set APIFY_TOKEN to your Apify API token in your terminal, then send the file as the request body.

Start the run
curl --fail-with-body --request POST \
  --url "https://api.apify.com/v2/actors/jmlp~linkedin-ad-library-scraper/runs" \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header "Content-Type: application/json" \
  --data-binary @input.json

The response contains a run ID and defaultDatasetId, not finished results. Wait for the run to succeed, set DATASET_ID to that dataset ID, then fetch its items. For large datasets, use limit and offset to page through the export.

Fetch the dataset
curl --fail-with-body \
  --url "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=json" \
  --header "Authorization: Bearer $APIFY_TOKEN"

Apify’s run and export API reference
Dataset export options

When the results look wrong.

Change one setting at a time, keep a small cap, and check the run summary before scaling up.

The results belong to the wrong advertiser.

Keeps advertiser names matching the search after ignoring case and punctuation. Use for a brand-only audit. It can exclude subsidiaries with different legal names, so check sample results first.

The listing exists, but detailed fields are missing.

Adds payer, CTA, landing links, media and available EU run dates, impression ranges and targeting. One extra request per ad. Outside EU disclosures, some detail fields stay empty.

Detailed requests start getting blocked.

A proxy spreads LinkedIn’s per-IP rate limit across sessions. Residential exits are generally more reliable than datacenter. If blocked, lower concurrency and check proxy availability rather than only retrying faster.

The run succeeded but returned nothing

Success means the Actor finished handling the request, not that the source returned data. Check SUMMARY.inputProblem, SUMMARY.problem and the log for missing targets, unsupported filters or refused requests. Test one known target with fewer filters.

Fewer records than expected

Check the global limit, per-search or per-page limits, platform coverage and deduplication. Several searches can find the same record. A source’s headline count can include records the public endpoint does not return. Review unfinished jobs before treating the dataset as complete.

Use the results in your tools.

Google Sheets

Append ad_id, advertiser_name, format, headline, CTA and landing link. Add offer categories and review notes. Keep impression ranges as text and country shares in separate fields.

Read the setup
Claude + MCP

Ask Claude to group the offers and summarize published targeting disclosures, citing ad_library_url. Have it separate audience guesses from the fields in the source.

Read the setup
Looker Studio

Compare creative formats and offers by advertiser. Show impression lower and upper bounds as ranges, and add an EU-data filter to the report.

Read the setup
BigQuery + dbt

Keep dated observations keyed by ad_id. Put country impression shares and carousel cards in child tables, and store payer identity separately from advertiser identity.

Read the setup
Copy a prompt for Claude
Prompt for Claude + Apify MCP
Inspect jmlp/linkedin-ad-library-scraper, then collect up to 50 HubSpot ads in DE with details and exact advertiser matching. Group offers and CTAs, cite ad_library_url, and summarize only the targeting and impression disclosures actually present. Do not infer spend or conversions.

The fields you’ll get.

Keep the collection time and original IDs with your records. You’ll need them to check where a result came from or compare it with a later run.

Before you draw conclusions

LinkedIn publishes dates, impression ranges and targeting disclosures for ads delivered in the EU; other records may lack them. A targeting flag doesn't give you every exact audience value. Name matches can include agencies or partners unless you enable exactAdvertiserMatch. Impression figures don't tell you conversions or spend.

ad_id / ad_library_url
Stable ID and the original public ad record.
advertiser_name / paid_by
The advertiser and entity that paid for the ad.
body / headline / cta_text
The published creative text and action.
image_urls / video_urls / carousel_cards
Available creative files and carousel contents.
impressions / impressions_by_country
Published EU impression ranges and country shares.
targeting / shown_in_eu
Available targeting disclosures and whether EU transparency data is present.

Common questions.

Do I need a LinkedIn account or cookies?

No. This Actor reads the public LinkedIn Ad Library. You need an Apify account to run it, but no LinkedIn login or session cookie.

Why are impression or targeting fields missing?

LinkedIn publishes those disclosures for ads shown in the EU. Check shown_in_eu and whether detailed collection was enabled before treating missing values as meaningful.

Source and current product details: JMLP’s LinkedIn Ad Library Actor on Apify.

Other scrapers
you might use.

Let’s talk about
your project.

Tell me what you need to collect or understand. I can help with a custom scraper, a pipeline or the analysis.

Start a project