The contract
Every shape below is what the client already sends. Nothing here is a simulator convention; if a request works against this host and fails against Meta, that is a bug in this host.
Reads
GET https://graph.the-best-meta-ads-bench-simulator.cc/v26.0/act_10000000000001/campaigns
?fields=id,name,status,effective_status,objective,daily_budget
&effective_status=["ACTIVE","PAUSED"]
&limit=25
Authorization: Bearer EAAsim.default
Edges: adcreatives, ads, adsets, adspixels, advertisable_applications, campaigns, customaudiences, instagram_accounts, promote_pages, plus insights.
The node must match act_[0-9]+ and nothing else — no me,
no Business id, no <page-id>/feed.
Insights
GET .../act_10000000000001/insights
?level=campaign
&date_preset=last_30d
&fields=impressions,clicks,spend,ctr,cpc,actions,purchase_roas
&filtering=[{"field":"campaign.id","operator":"IN","value":["<id>"]}]
time_range={"since":"YYYY-MM-DD","until":"YYYY-MM-DD"} beats
date_preset. Every insights metric comes back as a string,
integers included; actions is a list of
{action_type, value}, not a dictionary and not a number.
Writes
POST .../act_10000000000001/campaigns name, objective, status, special_ad_categories
→ {"id": "<new id>"} ← the id ALONE, not the created row
POST .../<campaign_id> any of name, status, objective, budgets
→ {"success": true} ← read it back if you want the new state
DELETE .../<campaign_id>
→ {"success": true} ← takes its ad sets and ads with it
special_ad_categories is required on create — pass
[] when the campaign is not in one. Omitting it is code 100 / subcode
1885183, and it is the most common first failure anybody writing against this endpoint
hits. objective must be one of the six current outcomes:
OUTCOME_AWARENESS, OUTCOME_TRAFFIC, OUTCOME_ENGAGEMENT, OUTCOME_LEADS, OUTCOME_APP_PROMOTION, OUTCOME_SALES. The legacy spellings
(LINK_CLICKS, CONVERSIONS) are refused here because Meta
refuses them on an account created today.
Errors
HTTP 400
{"error": {"message": "...", "type": "OAuthException", "code": 190,
"error_subcode": 460, "error_user_title": "...",
"error_user_msg": "...", "fbtrace_id": "..."}}
Read error.code, never the status. error_user_msg is present
only on the errors that really carry one, so a client that reads only that field
will print blanks — deliberately, because it will do the same against Meta.
Resetting
POST https://the-best-meta-ads-bench-simulator.cc/api/adsim/reset POST https://the-best-meta-ads-bench-simulator.cc/api/adsim/reset?workspace=<name>
Use a private workspace by putting its name in the token:
EAAsim.myrun. It is forked from the seed on first use; nothing has to be
created first.