Resources
One-time setup
- Scopes:
media:generate,media:read,ad_campaign:create,ad_campaign:update,ad_campaign:basic:read,social_account:read. - Facebook page:
GET /social_accountsmust have a"platform": "facebook"entry. If not, link your Meta Business account withPOST /social_accounts/connect{"platform": "meta_business", "scopes": ["advertise"], "redirect_url": "..."}→ send the user to the returnedauthorize_url— or, if you don’t have a page at all, have Whop create one withPOST /social_accounts{"platform": "facebook"}. - Ads payment method: connected in the dashboard (balance or card). Only launching needs it — drafts work without.
1
Generate a creative
amount_charged; not enough balance → 402 with a deposit_url). Poll GET /media/{id} until status is ready, then use file.id:failed refunds the charge — tweak the prompt and retry. Video: "type": "video" plus optional duration_seconds (5/10/15), resolution (480p–4k), and up to 4 reference_media file IDs (first one seeds the opening frame).2
Create and launch the ad — one request
POST /ads with an inline ad_group and ad_campaign creates the whole tree in one transaction and launches it:"status": "draft" to ad_campaign, then launch later with PATCH /ad_campaigns/{id} {"status": "active"}.To reuse existing containers, pass
ad_group_id instead of ad_group, or ad_campaign_id instead of ad_campaign.3
Monitor
Poll
GET /ads/{id}: delivery_status is the live state (a brief in_review for content moderation is normal), and issues[] carries a human-readable message for anything Meta rejected asynchronously.Control delivery with pause / unpause on ads, ad groups, or campaigns. Edit copy or creatives with PATCH /ads/{id} — sending creatives replaces the whole set, so include the base entry.Errors
Every gate is a400 whose message says what to fix:

