Search: GET /v1/{platform}/ads
Each platform has its own search endpoint, which takes only the filters that platform's library has. Every one answers with the same envelope and the same Ad record.
| Endpoint | Library | Search by | Ads a page | Credits | Covers |
|---|---|---|---|---|---|
| GET /v1/meta/ads | Meta Ad Library | q or page_id | up to 30 | 1 | Every country Meta runs ads in |
| GET /v1/google/ads | Google Ads Transparency Center | advertiser_id or domain | up to 100 | 1 | Every country Google runs ads in |
| GET /v1/tiktok/ads | TikTok Commercial Content Library | q or advertiser | up to 36 | 1 | The EEA, the UK and Switzerland |
| GET /v1/linkedin/ads | LinkedIn Ad Library | q, company_id or company | about 24 | 2 | Every country, the last 12 months |
There is no search across every platform at once: each library has different filters. To find one advertiser everywhere, make one call per platform.
Parameters every platform takes
Bearer <key>. Keys starting pads_test_ get the sandbox (sample ads, never charged); keys starting pads_live_ read the platform and use credits.YYYY-MM-DD.date_from=2026-01-01YYYY-MM-DD. Not before date_from.date_to=2026-10-21next_cursor of the page before. Send it with exactly the same other parameters: a cursor from a different search is 400 INVALID_INPUT.raw adds each ad's own record from the platform as raw, for fields we don't map. Values: raw.include=rawTikTok needs a date window, so its default is the last 12 months. LinkedIn's dates must be within the last year. Each platform's page lists the rest.
How parameters are read
- A parameter the platform doesn't take is
400 INVALID_INPUTwithfieldnaming it, so a filter is never silently ignored. - Each search needs exactly one subject (a keyword, an advertiser or a domain, by platform). None, or two, is
400 INVALID_INPUT. - Send each parameter once. Lists (
placements,countries,include) are comma-separated. - Country codes and filter values are read case-insensitively:
country=gbis read asGB,format=VIDEOasvideo. - An empty value is refused: leave the parameter out instead.
Names from other APIs
Code written for another ad library API often uses other names. These are read as ours; sending a name and its alias together is refused.
| Endpoint | Also accepted | Read as |
|---|---|---|
GET /v1/meta/ads | query | q |
GET /v1/meta/ads | keyword | q |
GET /v1/meta/ads | country_code | country |
GET /v1/meta/ads | media_type | format |
GET /v1/google/ads | country_code | country |
GET /v1/google/ads | media_type | format |
GET /v1/tiktok/ads | query | q |
GET /v1/tiktok/ads | keyword | q |
GET /v1/tiktok/ads | country_code | country |
GET /v1/linkedin/ads | query | q |
GET /v1/linkedin/ads | keyword | q |
GET /v1/linkedin/ads | country | countries |
GET /v1/linkedin/ads | country_code | countries |
The response
| Field | Type | What it is |
|---|---|---|
| platform | string | The platform searched. |
| query | object | What was searched, defaults filled in, so you can check how your parameters were read. |
| ads | array of Ad | This page of ads. Empty when nothing matched. |
| total_estimate | integer or null | The platform's own count of matching ads (Meta caps it at 50,000), or null where it gives none. |
| has_more | boolean | Whether another page follows. |
| next_cursor | string or null | Send as cursor for the next page; null on the last. |
| credits_used | integer | Credits this call used. |
| credits_remaining | integer or null | Live keys: credits left this month. Sandbox keys and Enterprise: null. |
{
"platform": "meta",
"query": {
"q": "running shoes",
"page_id": null,
"exact": false,
"country": "GB",
"active": null,
"format": null,
"placements": null,
"category": "all",
"language": null,
"sort": "impressions",
"date_from": null,
"date_to": null
},
"ads": [
{
"id": "meta_900000000000001",
"platform": "meta",
"platform_ad_id": "900000000000001",
"library_url": "https://www.facebook.com/ads/library/?id=900000000000001",
"advertiser": {
"id": "100000000000001",
"name": "Northwind Running Co",
"domain": null,
"library_url": "https://www.facebook.com/ads/library/?view_all_page_id=100000000000001"
},
"status": "active",
"first_shown": "2026-08-04T07:00:00Z",
"last_shown": "2026-10-18T07:00:00Z",
"format": "image",
"text": {
"headline": "Trail shoes, half price this week",
"body": "Grip that holds on wet rock. Free returns for 60 days.",
"description": null,
"cta": "Shop now"
},
"text_source": "platform",
"link_url": "https://northwind-running.example/trail",
"media": [
{
"type": "image",
"url": "https://example.com/sandbox-media/meta-trail.jpg",
"preview_url": null,
"width": 1080,
"height": 1080
}
],
"placements": [
"facebook",
"instagram",
"audience_network",
"messenger"
],
"regions": null,
"metrics": {
"impressions": null,
"reach": null,
"spend": null
},
"variants": 1
}
],
"total_estimate": 5,
"has_more": true,
"next_cursor": "eyJwIjoibWV0YSIsImMiOiIzIiwicSI6IldZZVBvV0xGd1Bkei1OejUifQ",
"credits_used": 0,
"credits_remaining": null
}Paging
- A page holds what the platform shows in one go: up to 30 ads on Meta, 100 on Google, 36 on TikTok, about 24 on LinkedIn. A page can hold fewer.
next_cursorcarries the platform's own position and a fingerprint of your search. Send it with the same parameters; with any other search it is400 INVALID_INPUToncursor.- Each page is a call, and uses credits only when it has ads.
- Cursors don't expire on our side, but a platform's position can go stale after a while: page through a search in one go.
Empty results
No matching ads is 200 with "ads": [], "has_more": false and "credits_used": 0. On Meta, an empty page is checked against a search that always has ads before we answer, so a silent block by the platform is never passed off as "no ads".
How long a call takes
A live call reads the platform as it runs: usually 2 to 15 seconds, most of it the platform, and longer when a platform blocks and we retry on new IPs. We give up at 60 seconds with 504 TIMEOUT (never charged), so set your client's timeout to at least 60 seconds. Run several searches side by side rather than one after another.