Facebook Ads Scout
API

Three on-ramps. Same data.

Ounie accounts hit REST endpoints with a bearer token. AI assistants connect via MCP and get a native tool surface. One-off scripts can skip the account and pay per call via x402 in USDC. All three return the same normalized ad rows.

POST/api/scrape
auth: Bearer
Start a scrape job
Returns a job_id immediately. The actual run completes async (1–3 minutes); poll /api/jobs/{id} or wait on the webhook.
Body
{
  "mode": "page",                     // page | search
  "targets": ["https://www.facebook.com/nike"],
  "max_ads": 500,
  "ad_details": false,                // +1 credit/ad: EU reach + demographics
  "period": "last30d",                // page mode: last24h | last7d | last14d | last30d
  "active_status": "active",          // page mode: all | active | inactive
  "sort_by": "impressions_desc",      // page mode: impressions_desc | most_recent
  "country_code": "US",               // page mode: 2-letter ISO, default ALL
  "webhook_url": "https://your-host.com/hook"   // optional
}
Response
{ "ok": true, "job_id": "job_01HX...", "credits_reserved": 500 }
GET/api/jobs/{id}
auth: Bearer
Check job status
Returns the job row plus a 10-minute signed download URL once succeeded.
Response
{
  "job": { "status": "succeeded", "results_count": 412, "mode": "page", "targets": ["https://www.facebook.com/nike"], ... },
  "preview_url": "https://...presigned..."
}
GET/api/jobs/{id}/export?format=csv|json
auth: Bearer
Download the dataset
Streams the file directly. CSV is the default; JSON returns the full normalized shape with nested page + creative + eu objects.
POST/api/x402/scrape
auth: none · x402
Pay-per-call (agents)
No account, no API key. Returns 402 with payment terms; sign and retry with X-Payment header. Synchronous; capped at 500 ads per call.
Body
{
  "mode": "page",
  "targets": ["https://www.facebook.com/hellofresh"],
  "max_ads": 50,
  "ad_details": false
}
Response
// 402 Payment Required (initial)
{ "x402Version": 1, "accepts": [{ "scheme":"exact", "network":"eip155:8453", "maxAmountRequired": "600000", ... }] }

// 200 OK (after settled X-Payment header)
{ "ok": true, "count": 47, "ads": [...], "payment": { "tx_hash": "0x..." } }
HTTP/api/mcp
MCP · Streamable HTTP + SSE
Model Context Protocol server
Drop our tool surface into any MCP host (Cursor, Claude desktop, ChatGPT GPTs, the Ounie AI Team, the AI SDK). Seven tools: scrape_facebook_ads, get_job_status, export_job_results, list_jobs, get_credit_balance, get_pricing, whoami. Auth: Bearer API key minted in the dashboard — or append ?api_key=fbk_live_… to the URL for MCP clients that can't set headers. Calls draw your Ounie credits; when the balance runs short the run is refused with a top-up link, so an agent can never overdraw.
Body
// .cursor/mcp.json (Cursor / any Streamable HTTP host)
{
  "mcpServers": {
    "facebook-ads": {
      "url": "https://facebook-ads.ounie.com/api/mcp",
      "headers": { "Authorization": "Bearer fbk_live_..." }
    }
  }
}
Response
// Tool call: scrape_facebook_ads
{
  "mode": "page",
  "targets": ["https://www.facebook.com/gymshark"],
  "max_ads": 250
}
→ { "ok": true, "job_id": "...", "credits_reserved": 250, "poll_with": { ... } }
// One request. Async job.
const res = await fetch("https://facebook-ads.ounie.com/api/scrape", {
  method: "POST",
  headers: {
    "Authorization": "Bearer ${API_KEY}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    mode: "page",
    targets: ["https://www.facebook.com/nike"],
    max_ads: 500,
    active_status: "active",
  }),
});

const { job_id } = await res.json();
// → poll /api/jobs/${job_id} or wait on the webhook
Schema
Every ad row.
{
  "id": "839212...",
  "url": "https://www.facebook.com/ads/library/?id=839212...",
  "page": {
    "id": "1442...",
    "name": "Nike",
    "url": "https://www.facebook.com/nike",
    "likes": 39400000,
    "categories": ["Sportswear Store"]
  },
  "isActive": true,
  "startDate": "2026-06-12",
  "endDate": "",
  "platforms": ["facebook", "instagram"],
  "creative": {
    "body": "Step into summer...",
    "title": "Air Max Season",
    "ctaText": "Shop Now",
    "ctaType": "SHOP_NOW",
    "linkUrl": "https://nike.com/...",
    "displayFormat": "VIDEO",
    "imageUrl": "https://...jpg",
    "videoUrl": "https://...mp4",
    "cardCount": 1
  },
  "variantCount": 24,
  "currency": "USD",
  "eu": {
    "totalReach": 1240000,
    "targetsEu": true,
    "genderAudience": "All",
    "ageMin": 18, "ageMax": 65
  }
}
Availability
What happens when the engine is down.

A scheduled probe checks the scrape engine. While it is unavailable we refuse up front and charge nothing — rather than quote a price for work we already know cannot finish.

  • · REST and MCP: 503 upstream_unavailable. No credits are reserved.
  • · x402: 503 instead of a 402 quote, so no signature is wasted.
  • · Both carry Retry-After. Service resumes automatically.
Rate limits
  • · REST: 60 jobs / min / key
  • · x402: 5 req / sec / payer address
  • · Max ads per job: 2,000 (REST) · 500 (x402)