API & MCP
Search open-web ads, look up advertisers, publishers and networks, and request crawls from your own code or an AI agent.
Last updated: 2026-11-07
The Adsup.ai API gives you the same data as the app: ads our crawler captured, advertiser, publisher and network profiles, the coverage ledger, and on-demand crawls. Every operation is also available as a tool on our MCP server, so an AI agent such as Claude can use it directly.
The machine-readable description is at /api/v1/openapi.json (OpenAPI 3.1).
Access and keys
API and MCP access are included in Pro and Team. Create a key under Settings → API Keys and send it with every request:
curl -H "Authorization: Bearer sk_your_key" \
"https://adsup.ai/api/v1/ads?q=vpn&geos=US,DE&pageSize=20"
Keys belong to your account. Delete a key to revoke it immediately. Browser cookies never authorize the API.
Credits
Calls spend credits from your plan. Pro includes 5,000 credits a month and Team 50,000 (yearly plans receive the year's credits up front). Your balance, this month's usage and the cost of each call are on the API Keys page and at GET /api/v1/me/usage.
| Call | Credits |
|---|---|
| Ad search (one page, up to 50 ads) | 1 |
| Ad detail | 1 |
| Advertiser / publisher / network profile | 1 |
| Coverage ledger | 1 |
| Crawl request status | 1 |
| On-demand crawl submission | 20 |
GET /api/v1/me/usage, MCP handshake and tool listing | 0 |
A call that fails (any 4xx or 5xx response, including "not found") is refunded. Each response carries an X-Credits-Charged header.
Limits
- Rate limit: 60 requests per minute per key. Over the limit you get HTTP 429 with
Retry-After. Every response carriesX-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset. - History: Pro sees the last 180 days, Team all history. Wider date filters are narrowed and the response says so in
meta.limited("history"). - On-demand crawls: Pro 20 and Team 100 per rolling 24 hours (shared with the app), at most one every 30 seconds.
Responses and errors
Every response uses the same envelope, with the HTTP status set to match:
{ "code": 0, "message": "ok", "data": { "items": [], "total": 0 } }
Errors have code: -1 and a machine-readable message:
| HTTP | message | Meaning |
|---|---|---|
| 400 | invalid_request, invalid_json, invalid_url | Bad parameters |
| 401 | missing_api_key, invalid_api_key | No key, or a deleted / unknown key |
| 402 | insufficient_credits | Not enough credits left for this call |
| 403 | plan_required | Your plan has no API / MCP access |
| 404 | not_found | No such ad, entity or crawl request |
| 422 | rejected | The site opted out of crawling |
| 429 | rate_limited, daily_limit, too_many_requests | Slow down / daily crawl limit reached |
| 503 | not_ready, unavailable | Data or crawl service temporarily unavailable |
Endpoints
| Method and path | What it does |
|---|---|
GET /api/v1/ads | Search ads. Filters as query parameters, lists comma-separated. |
POST /api/v1/ads/search | The same search with a JSON body (lists as arrays). |
GET /api/v1/ads/{id} | One ad: breakdowns, timeline, top publishers, landing pages and click chain. |
GET /api/v1/advertisers/{id} | Advertiser profile (?days=7,30,90,365). |
GET /api/v1/publishers/{domain} | Publisher (website) profile. |
GET /api/v1/networks/{id} | Ad network profile. |
GET /api/v1/coverage | Coverage ledger: networks × formats × countries, last 24 h and 7 days. |
POST /api/v1/crawl-requests | Queue a crawl: { "url", "geo", "device" }. |
GET /api/v1/crawl-requests | Your recent crawl requests and today's usage. |
GET /api/v1/crawl-requests/{id} | Status of one crawl request. |
GET /api/v1/me/usage | Plan, limits, credit balance and this month's usage. |
Search filters: q, adTypes, networks, geos, devices, platforms, advertiserIds, publisherIds (e.g. web:example.com), landingDomains, languages, verticals, sizes (300x250), seenFrom / seenTo, firstSeenFrom / firstSeenTo (new ads), minDaysSeen, minPublishers, hasLandingPage, riskMin, sort (last_seen, first_seen, days_seen, publishers, impressions), order, page, pageSize (max 50).
impressions counts how often our crawlers observed an ad. It is not delivered impressions, spend or reach.
curl -X POST https://adsup.ai/api/v1/ads/search \
-H "Authorization: Bearer sk_your_key" \
-H "Content-Type: application/json" \
-d '{"adTypes":["native"],"geos":["US"],"firstSeenFrom":"2026-11-01","sort":"first_seen"}'
MCP server
The MCP server speaks Streamable HTTP at https://adsup.ai/api/mcp and uses the same keys, credits and limits as the REST API. It is built on the official MCP TypeScript SDK and is stateless, so any compliant client works.
Tools: search_ads, get_ad, get_advertiser, get_publisher, get_network, coverage, request_crawl, get_crawl_request, get_usage. Each tool call costs the same as the REST call it maps to; the handshake and tool listing are free.
Claude Code:
claude mcp add --transport http adsup https://adsup.ai/api/mcp \
--header "Authorization: Bearer sk_your_key"
Any client that takes a JSON config (for example Claude Desktop, Cursor):
{
"mcpServers": {
"adsup": {
"type": "http",
"url": "https://adsup.ai/api/mcp",
"headers": { "Authorization": "Bearer sk_your_key" }
}
}
}
Then ask, for example: "Find native ads for VPN offers first seen in Germany this week and summarise the landing pages."
Alert webhooks
On Team, a saved search's daily alert can also be sent to a webhook (set it on Saved & alerts). Slack incoming-webhook URLs (hooks.slack.com) receive a Slack message. Other URLs receive:
{
"type": "alert.new_ads",
"alert_id": "…",
"rule": { "kind": "saved_search", "target": "…", "name": "VPN native US" },
"count": 3,
"ads": [
{
"id": "…",
"title": "…",
"thumb_url": "…",
"url": "https://adsup.ai/app/ads/…"
}
],
"sent_at": "2026-11-07T06:00:00.000Z"
}
Verify the X-Adsup-Signature: t=<unix>,v1=<hex> header: v1 is the HMAC-SHA256 of t + "." + body with the signing secret shown next to the webhook. Webhook URLs must be https:// on a public host; redirects are not followed.