LinkedIn Ad Library Scraper - Reliable
Pricing
from $0.80 / 1,000 ad scrapeds
LinkedIn Ad Library Scraper - Reliable
Scrape the LinkedIn Ad Library by advertiser, keyword, payer, country and date. Get ad text, creatives, CTA, landing URL, run dates, impressions, country split and targeting. Real Chrome + residential proxy, per-input status, $1.50 per 1,000 ads with details.
Pricing
from $0.80 / 1,000 ad scrapeds
Rating
0.0
(0)
Developer
Christian Machon
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
18 hours ago
Last modified
Categories
Share
Scrape the public LinkedIn Ad Library by advertiser / company name, keyword, "paid for by" payer, country and date range. You get every ad's text, creative (images, video file, carousel), headline, CTA, landing URL, and for ads shown in the EU the transparency data: run dates, impression ranges, impression share by country and targeting (languages, locations, audience, company, job and demographic parameters).
It runs in a real Chrome browser on residential IPs, because LinkedIn's Cloudflare protection blocks plain HTTP scrapers (about 90% of plain requests got HTTP 403 in our tests). Blocked or rate-limited sessions are swapped for new ones and the run picks up where it stopped, so a 403 does not lose results.
What can you use it for?
- Competitor ad research: see every ad a competitor is running on LinkedIn, with copy, creative and landing pages.
- Messaging and creative benchmarks: compare formats (image, video, document, carousel, message ads) and CTAs across an industry keyword.
- Spend proxies: impression ranges and country split for ads delivered in the EU.
- Targeting intelligence: which targeting dimensions (company, job, audience, demographics…) an advertiser uses, and which locations and languages.
- Lead generation: find companies actively advertising on LinkedIn for a keyword, with their company page URL.
Why this scraper?
- Measured reliability: all 36 test runs on the current build succeeded (100%), covering advertiser, keyword, payer, pasted search/detail URLs, multi-input, country (DE/FR/NL), date-range, card-only and 200-ad runs. Across all 110 development runs, 105 (95.5%) returned their data; the 5 failures were all on earlier builds, fixed since (see Limits). The fixes:
- Chrome-only browser fingerprints, so Firefox or Safari fingerprints don't get blocked.
- Automatic session rotation on 403/429.
- Pagination that resumes from the same page.
- Partial results are saved, never thrown away.
- Per-input status: the
OUTPUTrecord gives each input one status:SUCCEEDED,PARTIAL,NO_DATA,FAILEDorDUPLICATE. It also gives counts, errors and network stats, so you know exactly what you got. - Full ad text and details: it opens each ad's detail page (optional), so ad text is not cut off at "…see more".
- Clean, flat schema: numbers are parsed (
impressionsMin/impressionsMax), dates are ISO (YYYY-MM-DD) and tracking parameters are stripped from URLs. - Fair pricing: you pay per ad, and the detail-page enrichment is charged only when it actually loaded.
How to use it
- Enter one or more advertiser names (e.g.
HubSpot), keywords (e.g.CRM) or paid-for-by names. Each entry is a separate search. - Optionally restrict countries (ISO codes, e.g.
US,DE) and the date range. - Set Max ads per search (LinkedIn serves 24 per page).
- Leave Include ad details on for full data, or switch it off for a fast, cheap card-only scrape.
- Run. Results go to the dataset; the per-input summary goes to the
OUTPUTrecord in the key-value store.
You can also paste LinkedIn Ad Library search URLs or single ad URLs (https://www.linkedin.com/ad-library/detail/<id>).
Input example
{"advertisers": ["HubSpot", "Salesforce"],"keywords": ["CRM"],"countries": ["US", "DE"],"dateRange": "last-30-days","maxAdsPerSearch": 100,"includeDetails": true}
| Field | Description |
|---|---|
advertisers | Advertiser (company or person) names; LinkedIn's "Advertiser name" filter. |
keywords | Keywords contained in the ad. |
keywordFilter | Optional keyword that is combined with each advertiser search. |
payers | "Paid for by" names. |
adLibraryUrls | LinkedIn Ad Library search or ad detail URLs. |
countries | ISO 3166 alpha-2 codes; empty means all countries. Unknown codes are reported in OUTPUT.inputErrors. |
dateRange | all, last-30-days, current-month, current-year, last-year or custom (with startDate / endDate). LinkedIn allows custom ranges within the last 12 months only; older start dates are clamped. |
maxAdsPerSearch | 1–5000, default 50. |
includeDetails | Default true. |
maxConcurrency | Parallel browser sessions; default 5. |
maxRetries | Retries per page, each in a new session and IP; default 10. |
proxyConfiguration | Default Apify residential proxy (US). Datacenter proxies get blocked. |
What data you get
The fill rates below were measured on 3,360 ads from our test runs (October 2026). There are three columns:
- All ads (with details): 1,960 ads.
- EU-delivered: 766 ads, the ones that have transparency data.
- Card-only (
includeDetails: false): 1,400 ads.
LinkedIn publishes run dates, impressions and targeting only for ads that were shown in the EU (DSA transparency). That is why those fields are filled for about 39% of ads overall and almost 100% of EU-delivered ads. For ads shown only outside the EU they are null, and that is LinkedIn's data, not a scraping failure.
| Field | All ads (details) | EU-delivered | Card-only | Notes |
|---|---|---|---|---|
adId, adUrl, searchInput, searchType, detailsStatus, scrapedAt | 100% | 100% | 100% | |
advertiserName | 100% | 100% | 99% | |
advertiserUrl, advertiserType | 98% | 98% | 0% | Company page (company) or member profile (person). Detail page only. |
advertiserLogoUrl | 94% | 93% | 92% | |
paidBy | 100% | 100% | 0% | "Paid for by" entity. Detail page only. |
format | 100% | 100% | 99% | E.g. Single Image Ad, Video Ad, Document Ad, Carousel Ad, Message Ad, LinkedIn Article Ad, Event Ad, Job Ad, Spotlight Ad, Follow Company Ad. |
creativeType | 100% | 100% | 100% | LinkedIn's internal type, e.g. SPONSORED_VIDEO. |
adText | 99% | 99% | 99% | Full text with details. Cards are usually cut off (adTextTruncated: true). |
messageSender | 100% of message ads | Sender shown on Message/InMail ads. | ||
headline | 54% | 63% | 58% | Only formats that have a headline. |
ctaText | 54% | 63% | 0% | E.g. "Learn more", "Register", "Download the Guide". |
destinationUrl | 34% | 79% | 0% | Landing page (tracking trk parameter removed). LinkedIn often omits it for ads not shown in the EU. |
imageUrls | 93% | 96% | 93% | Image, carousel, document pages and video cover. Text-only ads have none. |
videoUrl | 100% of video ads (20% of all) | 0% | Highest-bitrate MP4. | |
carouselCardCount | 100% of multi-card ads (5%) | 0% | ||
firstShown, lastShown | 39% | 100% | 0% | From "Ran from … to …". |
totalImpressions, impressionsMin, impressionsMax | 39% | 100% | 0% | E.g. "20k-30k" → 20000 / 30000; "> 1M" → min 1000000, max null; "< 1k" → 0 / 1000. |
impressionsByCountry | 39% | 100% | 0% | [{country, percent, percentLabel}]; percent is null when LinkedIn shows "< 1%". |
targetingLanguages, targetingLocationsIncluded | 39% | 99–100% | 0% | |
targetingLocationsExcluded | 2% | 4% | 0% | Only when the advertiser excluded locations. |
targetingParameters | 39% | 100% | 0% | [{parameter, targeted, excluded}] for Audience, Demographic, Company, Education, Job, Member Interests and Traits. LinkedIn only reveals whether each was used, not the values. |
detailsStatus can be:
OK: the detail page was parsed.NOT_REQUESTED: details were switched off.NOT_AVAILABLE: the ad was removed or has no detail page.FAILED: the detail page could not be loaded after all retries. The card data is still returned.
Not available from LinkedIn and therefore not in the output: spend amounts, exact impressions, the targeting values themselves (e.g. which job titles), and ad engagement.
Output example
{"adId": "1611644514","adUrl": "https://www.linkedin.com/ad-library/detail/1611644514","searchInput": "HubSpot","searchType": "advertiser","advertiserName": "HubSpot for Developers","advertiserUrl": "https://www.linkedin.com/company/104750988","advertiserType": "company","advertiserLogoUrl": "https://media.licdn.com/dms/image/v2/.../hubspot_for_developers_logo","paidBy": "HubSpot, Inc.","format": "Single Image Ad","creativeType": "SPONSORED_STATUS_UPDATE","adText": "Less guesswork. More step-by-step 👀\n\nOne week until our Agent Builder Workshop!! ...","adTextTruncated": false,"messageSender": null,"headline": "3.0 Agent Builder Workshop","ctaText": null,"destinationUrl": "https://bit.ly/4yzZ7Sc","imageUrls": ["https://media.licdn.com/dms/image/v2/D4E10AQHRTgYZFiIaDQ/image-shrink_1280/..."],"videoUrl": null,"carouselCardCount": null,"firstShown": "2026-10-23","lastShown": "2026-10-26","totalImpressions": "> 1M","impressionsMin": 1000000,"impressionsMax": null,"impressionsByCountry": [{ "country": "Colombia", "percent": 96, "percentLabel": "96%" },{ "country": "Mexico", "percent": 3, "percentLabel": "3%" },{ "country": "United States", "percent": null, "percentLabel": "<1%" }],"targetingLanguages": ["English"],"targetingLocationsIncluded": ["Germany", "Netherlands", "United States", "Canada", "Mexico", "Ireland", "Colombia", "United Kingdom"],"targetingLocationsExcluded": null,"targetingParameters": [{ "parameter": "Audience", "targeted": false, "excluded": false },{ "parameter": "Company", "targeted": true, "excluded": false },{ "parameter": "Job", "targeted": false, "excluded": true }],"detailsStatus": "OK","scrapedAt": "2026-10-26T16:44:10.512Z"}
Run summary (OUTPUT record)
{"status": "SUCCEEDED","totalAds": 100,"searches": [{ "input": "Salesforce", "type": "advertiser", "status": "SUCCEEDED", "ads": 100, "adsWithDetails": 100, "detailFailures": 0, "pagesLoaded": 5, "reachedEnd": false, "errors": [] }],"network": { "pageNavigations": 10, "fragmentFetches": 4, "detailFetches": 91, "http429": 0, "http403": 0, "sessionsRetired": 0 }}
If every input fails, the run is marked as failed, so you are not left with an empty "successful" run.
Pricing
Pay per event, with no subscription:
| Event | Price | When |
|---|---|---|
| Actor start | $0.0025 per GB of memory ($0.005 at the default 2 GB) | Once per run |
| Ad | $0.0008 | Every ad in the dataset |
| Ad details | $0.0007 | Only for ads whose detail page loaded (detailsStatus: "OK") |
That is $1.50 per 1,000 ads with full details, or $0.80 per 1,000 ads card-only, plus the start fee. Searches with no results cost only the start fee. Apify's "Maximum cost per run" setting is respected: the scraper stops cleanly when it is reached.
Examples: 50 ads with details cost $0.005 + 50 × $0.0015 = $0.08; 1,000 ads with details cost about $1.51; 200 ads card-only cost about $0.17.
Typical runs from our tests (with details):
- 30–50 ads: about 55–65 s (median 55 s; slowest 128 s).
- 200 ads: about 2.5 minutes at concurrency 8.
- 1,000 ads: about 12 minutes at concurrency 8.
- Card-only (
includeDetails: false): 24 ads in 5–30 s, 200 ads in about 35 s.
FAQ
Do I need a LinkedIn account or cookies? No. The Ad Library is public, and the scraper never logs in.
Why are dates and impressions empty for some ads? LinkedIn only publishes them for ads delivered in the EU. Use the countries filter with EU countries (e.g. DE, FR) if you need those fields for most ads.
How far back can I search? "All time" returns everything LinkedIn still lists. Custom date ranges are limited to the last 12 months by LinkedIn.
Can I scrape one specific ad? Yes. Put its https://www.linkedin.com/ad-library/detail/<id> URL in adLibraryUrls.
How many ads can a search return? As many as LinkedIn serves. We tested up to 1,000 per search; the input allows up to 5,000.
Limits
- Proxy: residential proxy is required. Datacenter IPs are blocked by Cloudflare.
- Pacing: LinkedIn rate-limits fast clients, so detail pages are paced (about 2 s apart per session). Higher
maxConcurrencyuses more parallel sessions and is faster. - Advertiser search: the advertiser filter is LinkedIn's own fuzzy name match, so a search for "Google" can also return ads from pages such as "Google Cloud".
- Location lists: targeted locations are given by LinkedIn as one comma-separated text. We split it into a list and re-attach region codes (e.g. "London, ON"), but names that themselves contain commas may be split.
- Test failures: 5 of our 110 development runs returned no data, all on earlier builds: one search blocked 7 times in a row, and 4 runs on one build whose first page was blocked by Cloudflare on every retry. The current build starts a fresh browser (new fingerprint) after each block in addition to a new IP; all 36 runs on it succeeded. Cloudflare can still occasionally block a search; that run is then marked
FAILEDwith the reason inOUTPUT, and you pay only the start fee.