LinkedIn Ad Library Scraper: Competitor Ads, No Login avatar

LinkedIn Ad Library Scraper: Competitor Ads, No Login

Pricing

from $1.26 / 1,000 ads

Go to Apify Store
LinkedIn Ad Library Scraper: Competitor Ads, No Login

LinkedIn Ad Library Scraper: Competitor Ads, No Login

Scrape the LinkedIn Ad Library without login. Search ads by company, keyword or payer and get ad text, CTA, images, video, run dates, impressions by country and targeting. Track your competitors' new ads.

Pricing

from $1.26 / 1,000 ads

Rating

0.0

(0)

Developer

Cliqto Media

Cliqto Media

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 days ago

Last modified

Security

Limited permissions

Share

Scrape ads from the public LinkedIn Ad Library by company, keyword or payer, and get each ad's text, CTA, images or video, run dates, impressions by country and targeting.

Use it to see what your competitors advertise on LinkedIn. You don't need a LinkedIn account. The smallest run is one company name and 10 ads: it takes a few seconds and costs $0.037 on the Free plan.

LinkedIn Ad Library Scraper: search by company or keyword, get ads with full text, reach by country and targeting

Table of contents

What it does

The LinkedIn Ad Library shows ads that companies run on LinkedIn. This Actor searches the library the same way you do in a browser and saves one row for each ad.

For each ad you get:

  • Who runs it: advertiser name, LinkedIn company ID and page, logo, and who paid for the ad.
  • The ad itself: format (image, video, document, carousel, text, message and more), headline, full text, CTA button and link, landing domain, images, video, document pages, carousel cards.
  • When and how much: first and last date shown, days running, impressions range, and the share of impressions by country.
  • Targeting: what LinkedIn shows about the audience: language, location, job, company and other categories, with included and excluded values.

LinkedIn shows run dates, impressions and targeting only for ads shown in the EU. For other ads these fields are empty. The Actor only reads company ads in the public library. It does not log in and does not open personal profiles.

Good fit

  • Watch a competitor's LinkedIn ads: texts, offers, CTAs and landing pages.
  • Find ads about a topic in chosen countries and a date range.
  • Get only ads you have not seen before, on a schedule.

👉 Try it now: open the input form

How it works

  1. You give company names, company page URLs, keywords, payers, Ad Library URLs or ad IDs. You can add countries, a date range and other filters.
  2. The Actor runs each search in the public LinkedIn Ad Library and reads the result pages. If you keep Open each ad for full details on, it also opens every ad's page for the full text, media, dates, impressions and targeting.
  3. It saves one row for each ad in the dataset. A short report of the run (status, searches, counts) is saved as RUN_SUMMARY in the key-value store.

👉 Try it now: open the input form

Quick start

Input form: Company names with Apify in box 1, the Limits and details section in box 2

  1. Open the Input tab. Company names already has Apify (box 1 in the picture). Replace it with your competitor's name.

  2. Open Limits and details (box 2). Keep Max ads per search at 10 for a first test. Keep Open each ad for full details on to get the full text, dates, impressions and targeting.

  3. Click Start at the bottom of the page (below the form, not in the picture).

  4. Open the Output tab (box 1 below). The Ads view (box 2) shows the advertiser, format, text, CTA, landing URL, dates and impressions. Dates, impressions and targeting shows reach by country and targeting.

    Output tab: Ads view with advertiser, format, ad text, CTA, landing URL and dates of 10 Apify ads

Expected result: 10 rows, one for each Apify ad, newest first, in about 10 seconds. Some ads have no dates or impressions: LinkedIn shows them only for ads shown in the EU.

👉 Try it now: open the input form

Use cases

Buyer jobInput pathOutput entityKey fieldsNext integration
See all ads of a competitorcompanyUrls or companyNamesadheadline, body, ctaUrl, firstSeen, lastSeenCSV or Excel export
Find ads about a topic in some countrieskeywords + countries + dateRangeadadvertiserName, body, format, impressionsTextGoogle Sheets via export
Check where and to whom an ad was shownadIds or companyNamesadimpressionsByCountry, targetingyour BI tool via API
Get only new competitor ads every weekcompanyUrls + onlyNewadchangeStatus, firstFoundAt, adLibraryUrlSchedule + Slack or Notion

👉 Try it now: open the input form

Input

You need at least one search: a company name, company URL, keyword, payer, Ad Library URL or ad ID. Each value is a separate search. Empty input stops with INVALID_INPUT and costs nothing. The same ad found by two searches is saved once.

Input fields

FieldPlain nameTypeRequiredDefault / prefillAllowed valuesEffectExampleRecommendation
companyNamesCompany namesstring listone search fieldprefill ["Apify"]any textsearches ads by advertiser name["HubSpot"]quick, but can match companies with similar names
companyUrlsCompany page URLs or IDsstring listone search field—linkedin.com/company/<name>, /showcase/<name> or a numberexact search by company ID["https://www.linkedin.com/company/salesforce"]best for one exact company
keywordsKeywordsstring listone search field—any textsearches ad text["crm software"]combine with countries
payersPaid bystring listone search field—any textsearches the "Paid by" name["HubSpot, Inc."]for agencies and legal entities
startUrlsAd Library URLsURL listone search field—linkedin.com/ad-library/search?... or /detail/<id>copies filters from the URLa search URL from your browserwhen you already set filters on LinkedIn
adIdsAd IDsstring listone search field—numeric ID or detail URLreads these ads only["1612091784"]to refresh known ads
countriesCountriesstring listnoall countriesISO codes like US, DE, GB; ALLwhere the ad was shown["DE", "FR"]—
dateRangeDate rangetextnoall-timeall-time, last-30-days, current-month, current-year, last-year, custom-date-rangewhen the ad was shownlast-30-days—
startDate, endDateFrom / to datedatewith custom-date-range—YYYY-MM-DDcustom period2026-09-01—
impressionsMin, impressionsMaxImpressions rangenumberno—0 and moreonly ads with this reach10000—
targetingIncluded, targetingExcludedTargeting includes / excludeslistno—LANGUAGE, LOCATION, AUDIENCE, DEMOGRAPHIC, COMPANY, EDUCATION, JOB, INTERESTS_AND_TRAITSonly ads with this targeting["JOB"]EU ads only have targeting
adFormatsAd formatslistnoallimage, video, document, carousel, text, spotlight, follower, message, event, article, job, otherkeeps only these formats["video"]other ads are charged as checked ads
exactCompanyMatchExact company matchyes/nonofalse—drops ads of companies with a different nametrueuse with companyNames
sortOrderOrdertextnonewestnewest, oldestorder of resultsoldest—
maxAdsMax ads per searchnumberno100, prefill 101–5000stops each search at this number500start small
includeDetailsOpen each ad for full detailsyes/nonotrue—opens every ad pagefalseoff is faster and cheaper
trackChangesTrack new adsyes/nonofalse—marks each ad new or seentruefor monitoring
onlyNewOnly new adsyes/nonofalse—saves only ads not seen beforetruefor schedules
stateNameTracking memory nametextnolinkedin-ad-library-statestore namewhere seen ads are keptcompetitor-aone name per watch list
includeAdvertiserSummaryAdvertiser comparison summaryyes/nonofalse—saves ADVERTISER_SUMMARYtrueto compare advertisers
concurrencyAd pages read at oncenumberno5 (1 without proxy)1–10speed of detail pages3lower it if you see many retries
requestDelayMsPause between requests (ms)numberno250 (1000 without proxy)0–10000slower, more polite pace1000—
proxyConfigurationProxyproxynoApify ProxyApify Proxy groupsIPs for requestsdefaultkeep the default
deliveryConnector, deliveryTargetSend results totextno—Slack or Notion connector; channel ID or page URLposts a short summary—see below

The input also accepts resultsLimit (same as maxAds), skipDetails (opposite of includeDetails) and maxConcurrency (same as concurrency), so you can reuse input from other LinkedIn Ad Library Actors.

Interacting settings

WhenInput AInput BEffective behaviorExampleCost/result effect
You use bothonlyNewtrackChangesonlyNew turns tracking ononlyNew: trueseen ads are not saved, they cost $0.0001 each
You filter formatsadFormatsmaxAdsreads more results to fill maxAds10 videosfiltered ads cost $0.0001 each
You use an Ad Library URLstartUrlscountries, dateRangefilters in the URL are used; the form filters are added when the URL has nonea URL with countries=DEnone
Details are offincludeDetails: falsetargeting…, impressions… filtersLinkedIn still filters by them, but the rows have no targeting or impressions—about half the price

Input examples

All ads of one company

{
"companyUrls": ["https://www.linkedin.com/company/salesforce"],
"maxAds": 1000
}

Expected behavior: finds the company ID and reads up to 1000 of its ads with details. Expected output: one row for each ad, with headline, body, ctaUrl, firstSeen, impressionsText. Limit or cost note: 1000 ads with details took about 11 minutes and cost $3.60 at the Free plan price.

New ads only, every week

{
"companyNames": ["HubSpot"],
"exactCompanyMatch": true,
"onlyNew": true,
"stateName": "hubspot-watch",
"maxAds": 200
}

Expected behavior: the first run saves all ads and remembers them. Later runs save only new ads. Expected output: rows with changeStatus: "new". The list of new ad IDs is in CHANGES. Limit or cost note: an ad you have already seen costs $0.0001.

👉 Try it now: open the input form

Output

Output fields

FieldMeaningTypeCan be empty?Example / enumSource / notes
adIdLinkedIn ad IDtextno1612091784search result
adLibraryUrllink to the ad pageURLnohttps://www.linkedin.com/ad-library/detail/1612091784—
advertiserNameadvertisertextrarelyApify—
advertiserId, advertiserUrlLinkedIn company ID and pagetext, URLyes10608457—
advertiserLogoUrllogo imageURLyes—LinkedIn image links expire after some time
paidBywho paid for the adtextyesApify Technologies s.r.o.—
format, formatLabelad formattextnoimage, video, document, carousel, text, spotlight, follower, message, event, article, job, other—
headline, body, descriptionad textstextyes—bodyIsPreview: true means the text is the short version from the search page
ctaLabel, ctaUrl, landingDomainbutton and its linktext, URLyesLearn moretracking parameters are removed from ctaUrl
bodyLinks, hashtagslinks and hashtags in the textlistempty list——
images, videoUrl, videoPosterUrlmediaURLyes—details only
documentdocument title, pages, cover imagesobjectyes{"pageCount": 12}document ads
carousel, messageButtons, variantscarousel cards, message buttons, other versions of the adlistempty list—details only
firstSeen, lastSeen, daysRunningwhen the ad was showndate, numberyes2026-10-26details only; EU ads only
impressionsText, impressionsMin, impressionsMaximpressions rangetext, numberyes150k-200kEU ads only
impressionsByCountryshare of impressions by countrylistempty list{"country": "Germany", "percent": 9}EU ads only; under 1% is shown as 0.5 with lessThanOnePercent: true
targetingtargeting by categorylistempty list{"category": "Location", "included": ["DACH"], "excluded": ["India"]}EU ads only
targetingParameterswhich categories were usedlistempty list{"parameter": "Job", "targeted": true}EU ads only
hasTransparencyDatathe ad has impressions or targetingyes/nonotrue—
detailsStatusresult of opening the ad pagetextnook, not_requested, not_found, failed, skipped—
changeStatus, firstFoundAtnew or seen beforetext, dateyesnew, seenwith trackChanges or onlyNew
searchSourcewhich search found the adtextnoApify—
fetchedAtwhen the row was saveddate-timeno2026-11-01T04:42:47.016Z—

Output entities and surfaces

SituationDataset/API/KVS/live viewResult shapeUser action
Normal itemDataset, views Ads and Dates, impressions and targetingone row for each adexport or use the API
No ads for this searchRUN_SUMMARY.searches[].status = "not_found"; if no search found ads, RUN_SUMMARY.status = "NO_RESULTS" (run succeeds, you pay $0.001 for each search)no rowscheck the name, try companyUrls or fewer filters
Part of the adsRUN_SUMMARY.status = "CAPPED" (cost limit) or "PARTIAL" (time or request limit: run succeeds; a failed search: run fails)rows that were saved stay; you pay only for themread RUN_SUMMARY.reason and message
Run aborted by youRUN_SUMMARY.status = "CANCELLED"rows saved before the abort stay; you pay only for themstart a new run
LinkedIn blocked or failedRUN_SUMMARY.status = "ERROR", the run failsno rows, no chargerun again later or lower concurrency

RUN_SUMMARY also has totalCountEstimate for each search: how many ads LinkedIn says it has. With trackChanges or onlyNew the Actor saves CHANGES (new ad IDs). With includeAdvertiserSummary it saves ADVERTISER_SUMMARY (ads per advertiser, formats, dates, countries).

Output example

{
"adId": "1612091784",
"adLibraryUrl": "https://www.linkedin.com/ad-library/detail/1612091784",
"advertiserName": "Apify",
"advertiserId": "10608457",
"paidBy": "Apify Technologies s.r.o.",
"format": "document",
"headline": null,
"body": "How would you explain your job to your grandparents? …",
"bodyIsPreview": false,
"ctaLabel": null,
"document": { "title": "How would you explain your job to your grandparents", "pageCount": 12 },
"firstSeen": "2026-10-26",
"lastSeen": "2026-11-01",
"daysRunning": 7,
"impressionsText": "150k-200k",
"impressionsByCountry": [
{ "country": "United Kingdom", "percent": 45, "lessThanOnePercent": false },
{ "country": "Canada", "percent": 23, "lessThanOnePercent": false }
],
"targeting": [
{ "category": "Language", "included": ["English"], "excluded": [] },
{ "category": "Location", "included": ["DACH", "United States", "Canada"], "excluded": ["India"] }
],
"hasTransparencyData": true,
"detailsStatus": "ok",
"searchSource": "Apify",
"fetchedAt": "2026-11-01T04:42:47.016Z"
}

This example is trimmed. It is one row from the prefill run (companyNames: ["Apify"], maxAds: 10). null means LinkedIn does not show this value for the ad. This ad has no CTA button. An empty list means there is nothing of this kind.

Send results to Slack or Notion

Pick your connector in the optional deliveryConnector field. After the run saves its data, the Actor posts a short summary (up to 25 rows and a link to the full dataset) there. Put a Notion page link or a Slack channel ID in deliveryTarget: in Notion every run is added on top of that one page, so your workspace does not fill up with new pages. A delivery error never fails the run and costs nothing extra. Connect a service once in Apify Console → MCP connectors.

Use case → fields

Buyer jobInput pathOutput entityKey fieldsNext integration
Compare offers and CTAscompanyUrlsadheadline, body, ctaLabel, landingDomainExcel export
See which ads run longestcompanyNamesadfirstSeen, lastSeen, daysRunningGoogle Sheets
See EU reach and audiencecountries + detailsadimpressionsByCountry, targetingAPI
Alert on new adsonlyNew + scheduleadchangeStatus, adLibraryUrlSlack or Notion

👉 Try it now: open the input form

Pricing

Pricing unit

You pay per event. Prices on the Free plan:

EventWhenFreeBronzeSilverGold and higher
adone ad saved to the dataset$0.0018$0.00162$0.00144$0.00126
ad-detailsthe ad page was opened and read (only with Open each ad for full details)$0.0018$0.00162$0.00144$0.00126
searchone search ran (each company, keyword, payer or URL; a list of ad IDs is one search)$0.001$0.001$0.001$0.001
ad-checkedan ad was read but not saved: seen before in onlyNew, removed by adFormats or exactCompanyMatch, or an ad ID that does not exist$0.0001$0.0001$0.0001$0.0001

So 1000 ads cost $1.80 without details and $3.60 with details on the Free plan. We pay the platform usage (compute and proxy), you pay only the events above.

  • A search that finds no ads costs only the search: $0.001.
  • An onlyNew run with no new ads costs the search and $0.0001 for each seen ad.
  • If LinkedIn blocks a search or an ad page does not load, you pay nothing for it. A failed ad page still gives you the row from the search page; you pay ad but not ad-details.
  • Invalid input costs nothing.
  • If you set a maximum cost for the run, the Actor stops before it goes over it. It does not open ad pages it cannot pay for. The run ends with CAPPED.

Cost examples

ScenarioInput scopeExpected item/event countBase chargeOptional chargeStop/cap behavior
First test1 company, maxAds: 101 search, 10 ads, 10 details$0.019$0.018 detailsabout 10 s
One competitor1 company, maxAds: 10001 search, 1000 ads, 1000 details$1.801$1.80 detailsabout 11 min
Quick list1 keyword, maxAds: 5000, no details1 search, 5000 ads$9.001noneabout 8 min
Weekly check, nothing new1 company, onlyNew, 100 ads seen before1 search, 100 checked$0.011none—
Nothing found1 keyword with no ads1 search$0.001none—

Prices are for the Free plan. Paid Apify plans get the lower prices in the table above.

👉 Try it now: open the input form

Limits

  • Up to 5000 ads for each search and up to 100 searches in one run.
  • One run makes at most 3000 requests to LinkedIn and runs at most 1 hour. With details, each ad needs about 1.25 requests, so one run gives about 2000–2400 ads with details (our test: 2400 ads in 33 minutes). Without details, 5000 ads need about 220 requests. When a limit is reached, the run stops, keeps the saved ads and ends with PARTIAL and reason TIME_LIMIT (the run still succeeds). The default run timeout is 1 hour; a shorter timeout gives fewer ads.
  • If two searches in a row fail because LinkedIn does not answer, the Actor skips the remaining searches and says so in RUN_SUMMARY.warnings. You pay nothing for skipped searches.
  • onlyNew remembers up to 20,000 ads for each stateName; the oldest are forgotten first.
  • Run dates, impressions and targeting exist only for ads shown in the EU. LinkedIn shows impressions as a range, not an exact number.
  • The library has ads from about the last year. LinkedIn decides what it shows; the Actor does not promise every ad an advertiser ever ran.
  • Search by company name can find companies with similar names. Use companyUrls or exactCompanyMatch for one exact company.
  • No personal profiles, no login, no private data. Image and video links come from LinkedIn and can expire.

👉 Try it now: open the input form

API, export and integrations

  • API: start a run with POST https://api.apify.com/v2/acts/cliqtomedia~linkedin-ad-library/runs and the input JSON. Read rows from the run's dataset.
  • Export: download the dataset as JSON, CSV, Excel, XML or HTML from the Output tab or the API.
  • Scheduling: create a schedule in Apify Console with onlyNew: true to get only new ads each time.
  • Webhooks / no-code: use Apify integrations (Make, Zapier, n8n, Google Sheets) on the run's dataset.

Use with AI agents (MCP)

  • MCP server URL with only this Actor: https://mcp.apify.com?tools=cliqtomedia/linkedin-ad-library
  • Smallest useful input: {"companyNames": ["Apify"], "maxAds": 10}
  • Key output fields: advertiserName (who runs the ad), body (ad text), ctaUrl (where the ad leads), firstSeen / lastSeen (when it ran), impressionsByCountry (EU reach).
  • The prefill run takes about 10 seconds and costs $0.037 on the Free plan. Keep maxAds small; a run has at most 3000 requests and 1 hour.

👉 Try it now: open the input form

Troubleshooting

No ads for a company

Symptom: the dataset is empty and RUN_SUMMARY.searches[].status is not_found. Likely reason: the company has no ads in the library for these filters, or LinkedIn knows it under another name. Check: open the company in the LinkedIn Ad Library in your browser. Action: use the company page URL in companyUrls, or remove countries and dateRange.

The run stopped before maxAds

Symptom: fewer ads than maxAds; RUN_SUMMARY.status is CAPPED or PARTIAL. Likely reason: CAPPED with reason CHARGE_LIMIT means your maximum cost for the run was reached. PARTIAL with reason TIME_LIMIT means the 3000 request limit or the run timeout was reached. Check: RUN_SUMMARY.reason and RUN_SUMMARY.message. Action: raise the maximum cost or the run timeout, split the searches into more runs, or turn details off.

Some rows have detailsStatus "failed"

Symptom: rows have the short text from the search page and no dates. Likely reason: LinkedIn did not answer for this ad page after several tries. Check: RUN_SUMMARY.http.retries. Action: run these ads again with adIds, or lower concurrency. You did not pay ad-details for them.

The run failed with ERROR

Symptom: the run failed and has no rows. Likely reason: LinkedIn refused all searches for a while. Check: RUN_SUMMARY.message. Action: run again later. You were not charged for the blocked searches.

👉 Try it now: open the input form

FAQ

Do I need a LinkedIn account or cookies?

No. The Actor reads the public LinkedIn Ad Library without login.

Why are dates, impressions and targeting empty for some ads?

LinkedIn shows them only for ads shown in the EU. Use countries with EU countries to find such ads.

Does it get every ad of a company?

It gets what the LinkedIn Ad Library shows for your search, up to maxAds. RUN_SUMMARY.searches[].totalCountEstimate tells you how many ads LinkedIn says it has.

How fresh is the data?

Each row is read at run time (fetchedAt). lastSeen is the last day LinkedIn says the ad ran.

Can I use the input from another LinkedIn Ad Library Actor?

Mostly yes: startUrls, resultsLimit, skipDetails and maxConcurrency are supported.

👉 Try it now: open the input form

👉 Try it now: open the input form

Support

For a bug, question or feature request, open the Actor's Issues tab on Apify. Include the run ID, your input without private data and the RUN_SUMMARY message. Never send tokens, cookies or other secrets.

👉 Try it now: open the input form