LinkedIn Ad Library Scraper - LinkedIn Ads Library Scraper
Pricing
from $0.90 / 1,000 fast ad results
LinkedIn Ad Library Scraper - LinkedIn Ads Library Scraper
Scrape the LinkedIn Ads Library by company, ad-text keyword, or payer name. Get ad copy, headlines and creatives in fast mode; add landing pages, impressions, targeting and transparency fields in full mode. Track competitor B2B campaigns across countries in a single run.
Pricing
from $0.90 / 1,000 fast ad results
Rating
0.0
(0)
Developer
jy-labs
Maintained by CommunityActor stats
0
Bookmarked
65
Total users
42
Monthly active users
3 days ago
Last modified
Categories
Share
LinkedIn Ad Library Multi-Search Scraper
This is an independent, unofficial tool. It is not affiliated with, endorsed by, or sponsored by LinkedIn. It only reads information exposed by the public LinkedIn Ad Library and does not accept LinkedIn credentials, cookies, or member data.
Search multiple company names, ad text keywords, and payer names in one run. Results from every search are merged by LinkedIn ad ID, while the matches field preserves exactly which searches found each ad.
Use Fast mode for affordable creative research or Full mode when you need the public transparency fields LinkedIn exposes on individual ad pages.
What it collects
- Every mode, from the search card: advertiser name and logo (null on member post cards, which show the member's photo instead), ad format, ad text as shown on the card, headline, creatives, and source URL
- Every mode: the post author's name and headline for promoted member posts (thought leader ads)
- Every mode,
creativesby format: an image ad's image, every carousel item, a document ad's pages (as images; LinkedIn serves no file), an article's cover, and a video ad's cover. Message ads have none.fullmode makes a video'surlits MP4 file and keeps the cover asthumbnailUrl - Every matching company, keyword, or payer search with its rank
fullmode adds, from the ad detail page: the full ad text, the CTA label, company ID and public company URL, public payer name, run period, landing URL, impression range, country breakdown, and targeting- Restricted ads as explicit rows without inventing hidden values
In fast mode, adText is the card's visible copy, which LinkedIn shortens
with "…" for long posts, and ctaLabel is normally null because search cards
usually carry no call-to-action button (one is read when a card shows it). A
Message ad's card runs its paragraphs together and drops straight apostrophes
("Im" where the ad page has "I'm").
full mode replaces them with the whole post copy (paragraph breaks kept) and
the CTA label from the ad detail page. A Message ad's text and button label come
from its ad page too, line breaks and apostrophes included. Spotlight and
Follow Company ads have no post copy and keep the card's text. An ad without
a CTA, such as most member posts, keeps ctaLabel: null. An article ad takes
the article's own title as its headline, and when its advertiser wrote no
commentary above the article, the article's summary becomes its adText.
When a company promotes a member's post, advertiser is the sponsoring company
(LinkedIn's "Promoted by" line) and postAuthor holds the member's name and
headline. Company ads have postAuthor: null. postAuthor is personal data:
a member's public name and headline as shown in the Ad Library. You need a
lawful basis, such as one under the GDPR, to store or process it.
The Actor does not scrape LinkedIn profiles, emails, contacts, or logged-in content.
Input
Provide at least one value in companyNames, keywords, or payerNames. Across the three lists, you can submit up to 20 unique searches.
{"companyNames": ["HubSpot", "Salesforce", "Monday.com"],"keywords": ["CRM", "AI agent"],"countries": ["US", "GB", "DE"],"dateOption": "last-30-days","detailMode": "fast","maxResultsPerSearch": 25,"maxTotalResults": 100}
Date filters
Choose last-30-days, current-month, current-year, last-year, or custom-date-range. A custom range requires both dates, must stay within the most recent year, and must end no later than the previous UTC day because LinkedIn only exposes completed days.
{"keywords": ["cybersecurity"],"countries": ["FR", "DE"],"dateOption": "custom-date-range","startDate": "2026-07-01","endDate": "2026-07-23","detailMode": "full"}
ALL must be used by itself. Otherwise, use up to five ISO two-letter country codes.
Fast and Full modes
fast mode returns fields visible on search cards and is the best choice for
creative discovery and competitor monitoring. It is billed per saved
result at the Fast price on the Pricing tab.
full mode opens each public ad detail page and is billed per saved result
at the Full price, twice the Fast price. LinkedIn only exposes some
transparency fields for eligible ads and regions. Missing non-EU transparency data is returned as null and is not
considered an error. If a detail page fails, the search-card result is retained
with detailStatus: "failed".
Output
Each Dataset row represents one unique ad ID. A full mode row:
{"adId": "1455310303","matches": [{"type": "company","value": "JetBrains","rank": 1,"countries": ["FR", "DE"],"searchUrl": "https://www.linkedin.com/ad-library/search?accountOwner=JetBrains"}],"advertiser": {"name": "JetBrains","companyId": "12515","companyUrl": "https://www.linkedin.com/company/12515","logoUrl": "https://media.licdn.com/..."},"postAuthor": null,"payerName": "JetBrains s.r.o.","adFormat": "Single Image Ad","adText": "Build better software.","headline": "Try IntelliJ IDEA","ctaLabel": "Learn more","landingUrl": "https://www.jetbrains.com/idea/","creatives": [{"type": "image","url": "https://media.licdn.com/...","thumbnailUrl": null}],"runPeriod": {"startDate": "2026-07-22","endDate": "2026-07-23"},"restricted": false,"impressions": {"display": "30k-50k","min": 30000,"max": 50000,"byCountry": [{ "country": "France", "percentage": 60 }]},"targeting": [{"category": "Location","included": ["France", "Germany"],"excluded": [],"isTargeted": true,"isExcluded": null},{"category": "Company","included": [],"excluded": [],"isTargeted": true,"isExcluded": false}],"transparency": "published","detailStatus": "complete","retrievalMode": "http","sourceUrl": "https://www.linkedin.com/ad-library/detail/1455310303","scrapedAt": "2026-07-24T00:00:00.000Z"}
Each targeting entry is one category. included and excluded list the
segments LinkedIn names, such as languages and locations. For categories
LinkedIn only marks in its "Targeted / Excluded" table (Audience, Company, Job
and others), the lists stay empty and isTargeted / isExcluded record the
mark: true for a check, false for a dash. A listed "Targeting includes" or
"Targeting excludes" segment sets the matching flag to true; a flag the page
does not state is null.
Every row read from its detail page carries transparency: "published" when
LinkedIn showed the impressions and targeting sections for that ad,
"unavailable" when it published neither. LinkedIn publishes them for ads it
served in the EU, so a non-EU campaign comes back "unavailable" with its
advertiser, payer, text, CTA and landing URL all filled. That is the site's own
gap, not a failed read — a detail page the Actor could not fetch is marked
detailStatus: "failed" and charged at the Fast price. The run status message
and SUMMARY report the split
(transparencyPublishedCount / transparencyUnavailableCount), so a run says
how much transparency data LinkedIn actually had.
impressions.display is LinkedIn's total-impressions bucket as shown, and
min/max are its bounds: "30k-50k" gives 30000/50000, "< 1k" gives
0/1000, and an open-ended bucket such as "1M+" gives 1000000/null. A
bucket the Actor does not recognise keeps display with null bounds; the
country breakdown is kept either way.
The default Key-Value Store record SUMMARY contains search-level statuses, raw and deduplicated counts, confirmed empty searches, partial results, browser fallbacks, bounded error messages, final output count, the transparency split, and billing-limit state. Summary data is never mixed into the Dataset.
Empty results and blocking
An HTTP page is considered empty only when LinkedIn shows an explicit empty-result message. A 403, 429, error shell, meaningless 404, or page without both ads and empty-result evidence is treated as blocking or a parse failure.
A search page gets up to six attempts, with a 1–3 second pause before each retry (five when you set the proxy yourself); above a 60-second request timeout it gets four (three when you set the proxy yourself). Detail pages get up to four (three when you set the proxy yourself), see Proxy below. Independent HTTP searches run with a maximum concurrency of five. If a search still fails, only that search is reprocessed in a browser, with at most two browser fallbacks at once. LinkedIn blocks per exit IP, so when a proxied browser fallback's first page is blocked or shows neither ads nor a no-results notice, it retries on a new proxy session (a new exit IP on Apify Proxy) after a 1–3 second pause: five attempts at most, and no new attempt once the run is within one request timeout plus 30 seconds plus 150 ms per result row of its timeout. A browser fallback without a proxy gets one attempt, since a new session would leave from the same IP. An explicit no-results page is never retried. A run succeeds partially when at least one search succeeds. It fails only when every search is blocked or unparsable; an all-empty run succeeds normally.
Proxy
Leave Proxy configuration at its default and the Actor goes direct first. It switches to Apify Proxy only when LinkedIn rate-limits or keeps blocking:
- Every search starts without a proxy. LinkedIn's occasional 403 is often random per request, so a page is first retried on the same route. A search that keeps getting blocked climbs on its own: two direct attempts, then one through Apify Proxy (datacenter), then up to three through residential proxy (US), then up to two through Apify's Unblocker proxy (built for sites behind anti-bot protection such as Cloudflare), each proxy attempt on a new exit IP: eight attempts in all. Above a 60-second request timeout the steps are one, one, two and one: five in all. Residential and Unblocker are used only by a search that needs them; if your account cannot use one of them, that step is skipped. Unblocker is billed per successful request ($1–1.5 per 1,000 requests depending on your Apify plan), so a search that gets through earlier never uses it. A search still blocked after these is named in the run's status message.
- The first 429 or 999 answer switches the rest of the run to Apify Proxy (datacenter). The search that got it retries through the proxy, and every search that starts later begins on the proxy. If the proxy cannot be set up, the run stays direct.
- A proxied search keeps its proxy and one proxy session (the same exit IP and cookies) for all its pages. It moves to a new exit IP only after one of its requests is blocked.
- Detail pages start the same way: direct unless the run has switched to the proxy. A blocked detail page climbs on its own: two attempts on its starting route, then one through Apify Proxy (datacenter), one through residential proxy (US) and one through Unblocker, each proxy attempt on a new exit IP: five attempts at most. Other failures (a missing page, unusable content) end it after three attempts on its route.
- A search's browser fallback goes through residential when the search reached it, else the proxy its search got through on (residential in place of Unblocker, which a browser does not use); otherwise it follows the run's route. At most five searches per run use residential this way; later ones go through datacenter. A proxied browser fallback skips images, video, fonts and stylesheets.
- Any detail page whose HTTP attempts failed, blocked or not, goes to a
browser fallback, except one that ended on a 404 or 410 (the ad page is
gone). On the default proxy setting it goes through residential (else
datacenter, else direct), two pages at a time. The browser loads only the
page's HTML document, with no script, style, image or other requests (inline
scripts still run), since a logged-out detail page is fully rendered on the
server. A blocked page (403/429/999), an unusable one (such as a Cloudflare
challenge) or one that failed to load is retried on a new proxy session, a
new exit IP on Apify Proxy, after a 1–3 second pause: five attempts per page
at most. A run makes at most 20 detail browser attempts or 3 per page sent
to the browser, whichever is more; retries never use the attempts still
needed for other pages' first try. No attempt starts, first ones included,
once the run is within one request timeout plus 30 seconds plus 150 ms per
result row of its timeout, so the rows are saved in time. Without a proxy a
page gets one attempt. A page that still fails keeps its search card data
with
detailStatus: "failed"and is charged as a Fast result.
The run log shows each search's route (Search route: direct, datacenter,
residential or unblocker), every switch, and each search page's size, and each detail page
fetched through a proxy with its size. The proxyTiers field of the SUMMARY
record counts search page attempts, pages and bytes per route, the same for
detail pages under proxyTiers.details, search browser fallbacks and their
approximate bytes under proxyTiers.browserFallback (with retries, the
new-session retries of blocked browser pages, also counted in each search's
retryCount), and moves onto each proxy
under proxyTiers.climbs. Detail browser fallbacks, with their pages, bytes
and new-session retries, are under proxyTiers.detailBrowserFallback.
If you set the proxy yourself (proxy groups such as residential, a country, your own proxy URLs, or turning the proxy off), the Actor uses exactly that setting for every request and never switches routes.
Pricing
| Charge | Price |
|---|---|
| Fast saved result | per result, see Pricing tab |
| Full saved result | per result, see Pricing tab |
| Actor start event | $0.00005 per charged start unit |
The current per-result prices, including the discount for your Apify plan, are
on the Actor's Pricing tab. A Full result costs twice a Fast result. Apify
automatically scales the synthetic start-event count with allocated memory; at
the default 1 GB, a run charges one start unit for a total of $0.00005. The
start event also makes the first five seconds of compute eligible for Apify's
PPE startup coverage.
The Pricing tab also shows the minimum maximum charge a run accepts. Set a higher maximum charge when requesting more results.
Before Full detail work, the Actor checks how many full-ad-result events still
fit within the user's limit. Fast mode checks fast-ad-result instead. Each row
is validated, saved, and charged together. When another result cannot be
charged, the Actor stops and records chargeLimitReached: true in SUMMARY.
Ads with no extractable content (no advertiser name, or no text, headline, or creative) are delivered free, marked contentStatus: "empty".
In Full mode, an ad whose detail page could not be fetched is delivered with its
Fast data, marked detailStatus: "failed", and charged fast-ad-result
instead of full-ad-result. SUMMARY.fastChargedDetailFailures counts them.
Result estimate:
Fast = unique saved results × Fast result priceFull = unique saved results × Full result priceDefault-memory start = $0.00005
SUMMARY.pricing records the observed Fast, Full, and start prices and the
highest price this build accepts for each. Production runs refuse to start when
a deployed price is above that ceiling, preventing accidental overbilling.
Run through the API
Run and wait for Dataset output with an Apify API token in the authorization header:
curl --request POST \"https://api.apify.com/v2/acts/jy-labs~linkedin-ad-library-multi-search-scraper/run-sync-get-dataset-items?format=json&clean=true" \--header "Authorization: Bearer $APIFY_TOKEN" \--header "Content-Type: application/json" \--data '{"companyNames": ["HubSpot", "Salesforce"],"countries": ["US", "DE"],"detailMode": "fast","maxResultsPerSearch": 10,"maxTotalResults": 20}'
For larger runs, start the Actor asynchronously with /runs, then read the
default Dataset and the SUMMARY record using the storage IDs returned by the
run.
Common use cases
- Compare LinkedIn ads from multiple competitors
- Search B2B categories such as CRM, cybersecurity, or AI agents
- Find ads by the disclosed payer entity
- Inspect public EU impression and targeting transparency
- Compare creative strategies across countries
FAQ and troubleshooting
Why did a successful search return zero rows?
confirmed_no_results means LinkedIn explicitly showed that no ads matched the
filters. Broaden the date range, use ALL, or try the advertiser's exact public
name. A blocked or ambiguous page is never reported as an empty result.
Why are impressions or targeting null?
LinkedIn exposes these fields only for eligible ads and regions. Use Full mode
and include relevant European countries. Missing public transparency data is
represented as null; the Actor does not infer it.
What does completed_partial mean?
At least one search succeeded, but another search, pagination page, or detail
page was unavailable. Review SUMMARY.searches, detailErrors, and
browserFallbackSearches. Successfully collected card rows remain in the
Dataset.
Why did output stop before maxTotalResults?
The run's maximum charge may not allow another result. Increase the maximum
charge and rerun. SUMMARY.chargeLimitReached distinguishes this from search
limits or deduplication.
Does the Actor require a LinkedIn account?
No. It accepts no LinkedIn login, cookie, or member data and reads only the public Ad Library.
Something is missing or wrong — what helps most?
Open an issue on the Actor's page with the run ID: the SUMMARY record and the
run log carry what is needed to reproduce it, and both stay in your account. If a
run did what you needed, a short
review
is what other buyers read before they trust a scraper with their own searches.
Responsible use
You are responsible for using the output in accordance with applicable laws, LinkedIn's current terms, robots policies, and Apify's platform rules. Rows for promoted member posts include personal data (postAuthor: the member's public name and headline), so make sure you have a lawful basis, such as one under the GDPR, before storing or processing it. Public web content can change without notice. Before Store publication, the latest relevant policies must be reviewed; if publication is not permitted, keep the Actor private.