API reference

Every tool, parameter and error, generated from the schema.

find_advertiser

Find the Meta advertiser (a Facebook/Instagram page) behind a brand name or website domain, and return ranked candidates with their page ids. Free (an API key is still required).

After this call, STOP and ask your user which page they mean before calling get_advertiser_ads, even when one candidate is ranked first, verified, or marked proposed. Brand names are usually ambiguous, and a wrong page silently returns the wrong company's ads. Skip the question only if the user gave you the page id themselves or already confirmed this page in this conversation. If your user explicitly refuses to choose, you may pick, but name the page and market you picked in your answer. status: "ambiguous" is a normal result, not an error.

Call this whenever you have a name rather than a page id: get_advertiser_ads accepts page ids only.

A regional page family (e.g. IKEA: one page per country, all named "IKEA") comes back as one candidate with members, each labelled by Meta with the market it serves. Each member runs only its own market's ads: there is no global page covering every country. Pass country and the member(s) serving that market are marked proposed (sometimes several); confirm that set with your user, then pass all of them to get_advertiser_ads. With no country known, ask for one. If marketMember is "none", Meta labels no member for that country: tell the user, and offer get_keyword_ads instead of guessing a page.

ParameterTypeRequiredDescription
querystringyesBrand name ("IKEA", "Telenet") or website domain ("bol.com"). A domain is matched against the links in running ads, which is slower (~4 s) but confirmable.
countrystringnoOptional ISO 3166-1 alpha-2 code ("BE") of the market the user cares about. Only used to mark which members of a regional page family serve that market. It does not filter candidates.

get_advertiser_ads

Get the ads one or more Meta advertisers are running (or ran) from the Meta Ad Library, newest first, fetched live from Meta on every call. Billed per ad delivered: €3.99 per 1000 ads (about €0.004 each) from the account's prepaid credit.

Takes page ids only. With a brand name, call find_advertiser first and have your user confirm the page(s). Up to 10 page ids run as one job (e.g. a family's members for one market); maxAds is the total across them.

Cost control: maxAds defaults to 500 (about €2). Credit for the job is held at submit and only delivered ads are billed. Only use maxAds: "all" when your user asked for everything: big advertisers run thousands of ads (Walmart US: ~8,000, about €32). If the job could cost more than the account's approval threshold, you get approval_required with the price and nothing is crawled: tell your user the price and ask. Set approved: true only after they say yes, never on your own initiative.

The call waits for the job (up to about 50 s; in Claude Code until it finishes). If it is not done you get a job handle: call get_job with its jobId. Every response starts with a next line saying what to call or tell the user; follow it.

A result can be partial. If stoppedBy is blocked or cancelled, only the newest ads were reached: say so to your user and offer resumeFrom, never present it as the full set. stoppedBy: "maxAds" means more ads exist; "exhausted" means that was all of them.

Reading the result: compact (default) is a JSON header plus CSV tables for ads, distinct copy, and media URLs. For an overview of many ads use mode: "extra-compact" (no media URLs, ~15x fewer tokens), then re-read the few ads you need with get_job in full. Re-reading a job is free for 90 days. Media URLs are Meta's and expire within days; the ad's permanent link is the header's permalink template with <id> replaced by the ad's id (permalinkExample shows one filled in); never show the template itself. dataAsOf is when Meta was read.

ParameterTypeRequiredDescription
advertiserIdsstring[]yesMeta page ids, from `find_advertiser` or from the user. Several ids run as one job, merged newest first.
countrystringnoWhere the ad was SHOWN (ISO alpha-2, or "ALL"), never where the advertiser is based. Default "ALL". A Belgian company's ads shown in France need "FR" or "ALL", not "BE". EU reach and audience data (`euInsights`) come only with one EU/EEA country: when your user cares about one market (a Belgian brand in Belgium), pass it.
activeStatus"active" | "inactive" | "all"noDefault "active": ads running now. When your user's request does not say whether they mean ads running now or past ads too ("Walmart's ads" can mean either), ask them before calling. Pass "all" when they ask about history or past campaigns.
maxAdsinteger or "all"noMaximum ads to return and bill, across all advertiserIds. Default 500 (about €2). A number, or "all" (only when your user asked for everything; can be thousands of ads).
euInsightsbooleannoFetch Meta's EU transparency data per ad: reach, age/gender/country audience, payer and beneficiary. Exists only for ads shown in the EU; the header's `euEligible` says how many ads have it even when this is off. Included in the price. About 0.5 s per such ad; set false for a faster list-only result at the same price. Default true.
variantsbooleannoAdd `copyKey` / `assetKey` fingerprints so ads sharing the same text or the same media can be grouped. +€1.00 per 1000 ads. Default false.
newOnlybooleannoMonthly plan only. Return and bill only ads this account has never received before. The memory is account-wide: any key or automation on the account that already received an ad makes it not new. The first run of a query returns its current ads; later runs return what appeared since. Default false.
mode"compact" | "extra-compact" | "full"noOutput encoding. compact (default): JSON header + CSV tables incl. media URLs. extra-compact: same without media URLs. full: every field as JSON. Parse CSV by column name.
projection"volatile" | "default" | "full"noWhich fields. volatile: only status fields that change (isActive, endedAt, lastSeenActiveAt, gatedType, euInsights). Same price whatever you pick.
inlineTokensintegernoApproximate token budget for results returned in this call (default 20000). Beyond it you get a `cursor` for `get_job`. 0 = header only.
waitSecondsintegernoOverride how long this call waits for the job before returning a handle.
idempotencyKeystringnoReuse the same key when retrying the same request, so a retry never starts or bills a second job (honoured 24 h).
approvedbooleannoOnly after your user approved the price quoted in an `approval_required` response.
resumeFromstringnojobId of an earlier job that stopped early (`stoppedBy: "blocked"`). Continues where it stopped with the same parameters; ads that job already delivered are never billed again.

get_keyword_ads

Search the Meta Ad Library for ads by keyword, fetched live on every call, across all advertisers. Use it for "who advertises on X" and topic or market scans. Billed per ad delivered: €3.99 per 1000 ads, same as get_advertiser_ads.

The header includes an advertiser breakdown (name, page id, verified, ad count in this result). To answer "who advertises on X" cheaply, set a modest maxAds and inlineTokens: 0: you get just the header, billed for the ads found.

Matching is Meta's and loose: some returned ads (often 3-33%) show no keyword in their visible text, and nothing in the data says why they matched. Say so if exactness matters. match: "phrase" (default) needs the words together; all_words accepts them anywhere, in any order, and returns far more.

sort decides which ads you get, and so what you pay for. impressions (default) is Meta's ranking by total impressions: the big, long-running campaigns first, the best answer to "who advertises on X". recent is newest first, for "what launched this week"; on broad keywords the newest ads are mostly small sellers and spam, so keep maxAds modest. Tell your user which order you used.

Partial results (stoppedBy), cost control, waiting, job handles, next, output modes and re-reads work exactly as in get_advertiser_ads (by impressions, a partial result holds the top-ranked ads, not the newest): never set approved: true without your user's yes to the quoted price.

ParameterTypeRequiredDescription
keywordstringyesWord or phrase, in the language the ads are written in.
match"phrase" | "all_words"nophrase (default): the exact phrase. all_words: every word, any order, anywhere; broader and looser.
sort"impressions" | "recent"noimpressions (default): Meta's ranking by total impressions, the major advertisers first. recent: newest first. newOnly always runs recent.
countrystringnoWhere the ad was SHOWN (ISO alpha-2, or "ALL"), never where the advertiser is based. Default "ALL". "internet" shown in BE includes US brands.
activeStatus"active" | "inactive" | "all"noDefault "active". When your user's request does not say whether they mean ads running now or past ads too ("Walmart's ads" can mean either), ask them before calling. Pass "all" when they ask about history or past campaigns.
maxAdsinteger or "all"noMaximum ads to return and bill. Default 500. A number, or "all" (broad keywords run to tens of thousands: check with your user first).
newOnlybooleannoMonthly plan only. Return and bill only ads this account has never received before (account-wide, shared by every key and automation on it). Runs newest first (sort: recent). Default false.
mode"compact" | "extra-compact" | "full"noAs in get_advertiser_ads. Default compact.
projection"volatile" | "default" | "full"noAs in get_advertiser_ads.
inlineTokensintegernoDefault 20000. 0 = header only (the advertiser breakdown).
waitSecondsintegerno
idempotencyKeystringnoReuse when retrying the same request (24 h).
approvedbooleannoOnly after your user approved the quoted price.
resumeFromstringnojobId of an earlier job that stopped early (`stoppedBy: "blocked"`). Continues where it stopped with the same parameters; ads that job already delivered are never billed again.

get_job

Wait for a job and read its results. Free. Use it with any jobId returned by get_advertiser_ads, get_keyword_ads or refresh_ads: while the job runs it waits (about 50 s, or until done in Claude Code) and returns progress; once completed it returns results, with a cursor if they exceed inlineTokens.

Results are frozen at delivery and readable for 90 days; re-reading, paging, or switching mode/projection never bills again. Typical use: read extra-compact first, then full for the handful of ads that matter.

ParameterTypeRequiredDescription
jobIdstringyes
cursorstringnoFrom the previous page's response, to read the next page of results.
mode"compact" | "extra-compact" | "full"no
projection"volatile" | "default" | "full"no
inlineTokensintegerno
waitSecondsintegerno

cancel_job

Stop a queued or running job. Free to call. A queued job, or one stopped before its first page, costs nothing. A running job delivers and bills the ads already crawled (the newest ones, as if maxAds had been that number) and ends completed with stoppedBy: "cancelled".

ParameterTypeRequiredDescription
jobIdstringyes

refresh_ads

Get the current state of ads this account already received, by ad id: is it still active, when did it end, was it taken down, current EU reach. Monthly plan only. Billed per ad at the normal rate (€3.99 per 1000; it is a new crawl, not a free re-read), but only for the ads you name, so it is far cheaper than re-running a whole advertiser to check a few ads.

On pay-as-you-go this returns plan_required. If your user wants current status for ads they hold and is on pay-as-you-go, tell them the monthly plan offers this, rather than silently re-running the advertiser.

Each ad comes back as refreshed, no_eu_data (billed: still no EU data), not_found (Meta no longer has it; free) or not_yours (this account never received it; free). Returns only the fields that change by default (projection: "volatile"); ask for full at the same price. Runs as a job, like the other ad tools.

ParameterTypeRequiredDescription
adIdsstring[]noAd ids (ad_archive_id) this account received. Give this or fromJobId.
fromJobIdstringnoRefresh every ad delivered by this earlier job. Give this or adIds.
projection"volatile" | "default" | "full"noDefault volatile.
mode"compact" | "extra-compact" | "full"no
inlineTokensintegerno
waitSecondsintegerno
idempotencyKeystringno
approvedbooleannoOnly after your user approved the quoted price.

get_account

The account behind this API key: remaining credit (plan credit, grant, balance), plan, spend guards (approval threshold, this key's spend cap), recent jobs (recentJobs: id, query, ads, date), and whether Meta is currently reachable per capability (resolve, list, detail: ok | degraded | paused). Free. Call it before a large job, when your user asks about credit, to find the jobId of an earlier job, or after a source_paused or blocked response.

No parameters.

Errors

Every error has one shape, { "error": { "code", "message", "next" } }. next tells your agent, in one line, what to call or what to tell you. The amounts below are examples.

CodeHTTPnext
invalid_argument400Nothing was started or charged. Fix the argument the message names and call again.
invalid_api_key401Tell your user this API key was not recognised or was revoked; create a new one at https://adsentinel.eu/dashboard (or run `npx adsentinel login`), then set ADSENTINEL_API_KEY.
no_api_key401Tell your user they need an AdSentinel API key: sign up at https://adsentinel.eu/signup (€2 of free credit), then run `npx adsentinel login` in a terminal, which stores the key for the CLI and the Claude Code plugin. Or set ADSENTINEL_API_KEY.
approval_required402Nothing was crawled or charged. Ask your user whether to spend up to €12.34. Only if they agree, call again with the same arguments plus approved: true; or lower maxAds.
insufficient_credit402Tell your user the balance is too low for even one ad; top up at https://adsentinel.eu/dashboard or with `npx adsentinel topup`.
key_spend_cap_reached402Nothing was started or charged: this API key reached its spend cap of €20.00 per month (UTC). Tell your user; they can raise it at https://adsentinel.eu/dashboard, or wait for the next month.
plan_required403Nothing was billed. Tell your user refresh needs the monthly plan (€20/mo, credited back as usage: https://adsentinel.eu/pricing). The pay-as-you-go alternative is re-running the advertiser, which bills every ad again: don't do that without their OK on the cost.
job_not_found404No such job on this account. Check the jobId; get_account lists recent jobs.
not_found404No such endpoint. The API is described at https://adsentinel.eu/docs.
idempotency_key_mismatch409This idempotencyKey was already used for a different request in the last 24 h. Nothing was started or charged. Use a new key for a new request.
job_expired410This job's results expired after 90 days and cannot be read again. If your user still needs them, run the query again (it bills again).
rate_limited429Too many calls with this key. Nothing was started or charged. Wait 30s, then call again.
internal500Something failed on our side. Nothing was charged. Retry once; if it fails again, tell your user.
source_blocked503Meta's Ad Library did not answer us just now. Nothing was charged. Wait a minute and call again; if it keeps failing, tell your user and point them at https://adsentinel.eu/status.
source_paused503We paused 'advertiser' because Meta's Ad Library changed and our results would be wrong; a human is on it. Nothing was charged. Do not retry now: tell your user, and point them at https://adsentinel.eu/status.