LinkedIn Ad Library API — Getting Started — FetchLayer Docs
Documentation menu
FetchLayer FetchLayer API

Getting Started

Search the LinkedIn Ad Library in two steps, then follow an ad through to its full copy and creative files.

1. Get your API key

Create an account and generate a FetchLayer API key from your dashboard. No LinkedIn developer account, partner application, or OAuth flow is involved.

Create free account →

2. Search for ads

bash
curl -X POST https://api.fetchlayer.dev/linkedin-ad-library/search-ads \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"query":"marketing automation","country":"US","dateRange":"current-year","limit":30}'

dateRange defaults to the last 30 days

Leave it out and you search one month, not the whole archive. Coverage runs from 1 June 2023, so pass current-year, last-year, or a custom-date-range with startDate and endDate before concluding an advertiser has gone quiet.

3. Go from a brand name to its whole rotation

There is no advertiser profile endpoint — LinkedIn does not publish one. Resolve a brand with /search-advertisers, which returns the advertiser name, LinkedIn page, profile type and a sample ad id, then pass that name to /advertiser-ads.

bash
curl -X POST https://api.fetchlayer.dev/linkedin-ad-library/search-advertisers \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"query":"Adobe","pages":5}'

Tip

Advertiser names match by substring, so /advertiser-ads can span several companies. The response's matchedAdvertisers array names the ones the results actually cover — check it before attributing a campaign to one brand.

4. Follow an ad to its copy and creative

Every ad carries an adId. Search results carry a shortened body (creative.bodyTruncated: true) — call /ad-detail for the full text, EU impressions band and targeting, then /ad-media for the files.

bash
curl -X POST https://api.fetchlayer.dev/linkedin-ad-library/ad-media \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"ad":"1624146516","kinds":["video","image"],"probe":true}'

Tip

GET /media is the one endpoint that takes query parameters instead of a JSON body, and it supports HTTP Range requests — so a video streams and seeks in a player instead of downloading whole. Use it rather than LinkedIn's own asset URLs, which are signed and expire.

5. Read the notes array

Every response carries a notes array of caveat strings — a paging walk cut short, a filter the upstream capped, a list that may be incomplete. A well-formed response is not the same as a complete one, so read notes alongside hasNextPage and totalMatchCount before reporting a number.

Error responses

Every error, on every route, is JSON in one shape: error (what went wrong, in words), status, code, retryable and requestId, which also comes as the X-Request-Id header. Branch on code and retryable rather than on the text; on a 5xx the text is generic. Quote requestId when reporting a problem.

json
{
  "error": "Missing required parameter: url",
  "status": 400,
  "missing": ["url"],
  "code": "invalid_input",
  "retryable": false,
  "requestId": "922874e3-c0d0-40d8-8866-d198af132420"
}
codeStatusRetryableMeaning
invalid_input400noThe request needs fixing; error names the field. A missing required parameter also lists it in missing.
unauthorized401noThe API key is missing or not valid.
insufficient_credits402noNothing left to spend: no credits, a used-up free trial, or a monthly allowance and credit balance both spent. error says which. Waiting does not help; topping up does.
forbidden403noThis key or account is not allowed this request.
not_found404noWhat the request names does not exist or is not public.
expired410noWhat the request refers to is no longer available; ask for it afresh.
quota_exceeded429noA request-rate limit was reached; error says which. Slow down before sending more.
cancelled499noThe caller closed or cancelled the request before it finished. The work was stopped and nothing was charged.
time_limit503yesYour timeoutMs ran out before there was anything to return; ask again, or allow more time.
temporarily_unavailable503yesNot available right now; ask again shortly.
store_unreachable503yesStore routes (Shopify, WooCommerce): the store itself can’t be reached right now — it may be offline. Ask again later. Where a store route doesn’t say so yet, an offline store is temporarily_unavailable.
upstream_error502yesThe source answered in a way that could not be used; ask again later.
internal_error500yesSomething failed on our side; ask again later, and quote requestId if it persists.
tool_error—noMCP only: a tool call failed in a way no other code describes; error says what.

More codes may be added; treat one you do not recognise by its retryable flag. Over MCP, a tool error carries the same fields in structuredContent, and as a JSON text block beside the message.

Next steps