Get started

Quickstart

Search an ad library with a sandbox key, read the next page with next_cursor, test your error handling, then switch to a live key.

1Get a sandbox key

Sign up and your sandbox key is on the dashboard. It starts pads_test_, returns sample ads from made-up advertisers and never uses a credit. Keep it in the PADS_API_KEY environment variable. An AI agent can get one itself: see Agents.

Sign up free and your sandbox key is filled in here and in every example. The examples read it from PADS_API_KEY.Log in

One request to the Meta Ad Library: the keyword running shoes, ads shown in the UK. The other platforms work the same way, each with its own filters.

curl "https://api.publicadsapi.com/v1/meta/ads?q=running+shoes&country=GB" \
  -H "authorization: Bearer $PADS_API_KEY"
200 OK (the first of 3 ads shown)
{
  "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
}

query echoes what was searched, defaults filled in. Each ad is the same Ad record whichever platform it came from. The sandbox returns its sample ads whatever you search for, so the shapes are real but the ads don't match the keyword.

3Read the next page

While has_more is true, send next_cursor back as cursor with the same parameters. A cursor only works with the search it came from.

curl "https://api.publicadsapi.com/v1/meta/ads?q=running+shoes&country=GB&cursor=eyJwIjoibWV0YSIsImMiOiIzIiwicSI6IldZZVBvV0xGd1Bkei1OejUifQ" \
  -H "authorization: Bearer $PADS_API_KEY"
Every page, in Node.js
const ads = [];
let cursor = null;
do {
  const url = new URL('https://api.publicadsapi.com/v1/meta/ads');
  url.searchParams.set('q', 'running shoes');
  url.searchParams.set('country', 'GB');
  if (cursor) url.searchParams.set('cursor', cursor);
  const res = await fetch(url, {
    headers: { authorization: `Bearer ${process.env.PADS_API_KEY}` },
    signal: AbortSignal.timeout(60_000),
  });
  const page = await res.json();
  if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);
  ads.push(...page.ads);
  cursor = page.next_cursor;
} while (cursor && ads.length < 300);
Every page, in Python
import os
import requests

ads, cursor = [], None
while True:
    params = {"q": "running shoes", "country": "GB"}
    if cursor:
        params["cursor"] = cursor
    res = requests.get(
        "https://api.publicadsapi.com/v1/meta/ads",
        params=params,
        headers={"authorization": f"Bearer {os.environ['PADS_API_KEY']}"},
        timeout=60,
    )
    page = res.json()
    if not res.ok:
        raise RuntimeError(f"{page['error']['code']}: {page['error']['message']}")
    ads += page["ads"]
    cursor = page["next_cursor"]
    if not cursor or len(ads) >= 300:
        break

4Test your error handling

With a sandbox key, these searches answer the way a platform sometimes does, so you can build for them before going live. They work as q, company or the first part of a domain on every platform.

Search forSandbox answer
q=empty200 with no ads (0 credits)
q=blocked503 UPSTREAM_BLOCKED with Retry-After
q=timeout504 TIMEOUT
q=changed502 UPSTREAM_CHANGED

5Go live

Create a live key on Keys (it starts pads_live_ and is shown once) and send it in place of the sandbox key. Nothing else changes. A page with ads uses 1 credit (LinkedIn 2); an empty page uses none. A live call reads the platform as it runs and can take 2 to 15 seconds, so set your HTTP client's timeout to at least 60 seconds.

Or give it to your coding agent

Prompt
Add ad library search to this project using the Public Ads API. Follow the quickstart: https://publicadsapi.com/docs/quickstart.md. Read my API key from the PADS_API_KEY environment variable. If it isn't set, get a sandbox key yourself with POST https://api.publicadsapi.com/v1/agent/signup (send your name and what we are building as name and use_case), store the api_key it returns in PADS_API_KEY, and give me the claim_url so I can keep the key. Test it against the sandbox first: try GET https://api.publicadsapi.com/v1/meta/ads?q=running+shoes&country=GB, follow next_cursor for the next page, and handle the scenario queries q=empty, q=blocked and q=timeout.