API reference

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.

EndpointLibrarySearch byAds a pageCreditsCovers
GET /v1/meta/adsMeta Ad Libraryq or page_idup to 301Every country Meta runs ads in
GET /v1/google/adsGoogle Ads Transparency Centeradvertiser_id or domainup to 1001Every country Google runs ads in
GET /v1/tiktok/adsTikTok Commercial Content Libraryq or advertiserup to 361The EEA, the UK and Switzerland
GET /v1/linkedin/adsLinkedIn Ad Libraryq, company_id or companyabout 242Every 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

Authorizationstringheaderrequired
Bearer <key>. Keys starting pads_test_ get the sandbox (sample ads, never charged); keys starting pads_live_ read the platform and use credits.
date_fromYYYY-MM-DDquery
Ads shown on or after this day, YYYY-MM-DD.
Example: date_from=2026-01-01
date_toYYYY-MM-DDquery
Ads shown on or before this day, YYYY-MM-DD. Not before date_from.
Example: date_to=2026-10-21
cursorstringquery
The next_cursor of the page before. Send it with exactly the same other parameters: a cursor from a different search is 400 INVALID_INPUT.
includecomma-separatedquery
raw adds each ad's own record from the platform as raw, for fields we don't map. Values: raw.
Example: include=raw

TikTok 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_INPUT with field naming 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=gb is read as GB, format=VIDEO as video.
  • 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.

EndpointAlso acceptedRead as
GET /v1/meta/adsqueryq
GET /v1/meta/adskeywordq
GET /v1/meta/adscountry_codecountry
GET /v1/meta/adsmedia_typeformat
GET /v1/google/adscountry_codecountry
GET /v1/google/adsmedia_typeformat
GET /v1/tiktok/adsqueryq
GET /v1/tiktok/adskeywordq
GET /v1/tiktok/adscountry_codecountry
GET /v1/linkedin/adsqueryq
GET /v1/linkedin/adskeywordq
GET /v1/linkedin/adscountrycountries
GET /v1/linkedin/adscountry_codecountries

The response

FieldTypeWhat it is
platformstringThe platform searched.
queryobjectWhat was searched, defaults filled in, so you can check how your parameters were read.
adsarray of AdThis page of ads. Empty when nothing matched.
total_estimateinteger or nullThe platform's own count of matching ads (Meta caps it at 50,000), or null where it gives none.
has_morebooleanWhether another page follows.
next_cursorstring or nullSend as cursor for the next page; null on the last.
credits_usedintegerCredits this call used.
credits_remaininginteger or nullLive keys: credits left this month. Sandbox keys and Enterprise: null.
200 OK
{
  "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_cursor carries the platform's own position and a fingerprint of your search. Send it with the same parameters; with any other search it is 400 INVALID_INPUT on cursor.
  • 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.