Meta Ads

Boost Facebook and Instagram posts and manage Meta campaigns, ad sets, ads, audiences and insights through the Postgale REST API and MCP server.

Postgale lets your backend or AI agent run Facebook and Instagram ads through Meta's Marketing API: list ad accounts, boost a published post, build campaigns, ad sets (targeting, budget, schedule), creatives and ads, manage customer-list custom audiences and read insights. Everything is on the REST API (paths relative to https://staging.postgale.com/api/v1) and the MCP server (tools ads_*). Calls act through a connected Facebook Page account (accountId) whose user granted ads_management / ads_read.

Safety: nothing spends by accident

  • Every call that can create spend (creating a campaign, ad set or ad, boosting, setting status ACTIVE, changing budget, bid, schedule or targeting) needs "confirm": true. Without it the answer is 400 confirmation_required and nothing is created.
  • Created objects are PAUSED unless you send "status": "ACTIVE" together with confirm. Pausing (PATCH … {"status":"PAUSED"}) never needs confirm.
  • Agents should show the plan (ad account, budget, dates, targeting) to a human and only then send confirm.
  • Budgets and bids are integers in the ad account currency's minor units (cents): 2000 = 20.00 USD.
  • When approval applies to the key, organization or account, ads writes answer 403 approval_required. Writes are audited.
  • Instances without PROVIDER_LIVE_ADS=1 answer from a deterministic mock ("mock": true in every response) and never call Meta.

Ad accounts

GET /api/v1/ads/accounts
curl https://staging.postgale.com/api/v1/ads/accounts?accountId=acc_fb_page \
  -H 'Authorization: Bearer sk_…'

# {"adAccounts":[{"id":"act_1234567890","name":"Coffee Co. Ads","currency":"USD","account_status":1,…}],"mock":false}

Boost a post

Turns a published Facebook Page post or Instagram post into an ad that keeps its likes and comments: Postgale creates the campaign, ad set, creative and ad. Pass postId (a Postgale post with a published Facebook or Instagram target) or platformPostId + platform. Instagram needs instagramUserId, the Instagram business account linked to the Page. goal: engagement (default), traffic, awareness, video_views, lead_generation, conversions, app_promotion. A lifetime budget needs schedule.endDate; linkUrl goes with callToAction.

POST /api/v1/ads/boost
curl -X POST https://staging.postgale.com/api/v1/ads/boost \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","adAccountId":"act_1234567890","postId":"cmpost123","goal":"traffic","budget":{"amount":2000,"type":"daily"},"schedule":{"startDate":"2026-11-01T09:00:00Z","endDate":"2026-11-08T09:00:00Z"},"targeting":{"countries":["US","CA"],"ageMin":25,"ageMax":45,"interests":[{"id":"6003139266461","name":"Coffee"}]},"linkUrl":"https://shop.example/beans","callToAction":"SHOP_NOW","confirm":true}'
{
  "boost": {
    "platform": "facebook",
    "platformPostId": "811889972008357_1029384756",
    "status": "PAUSED",
    "campaignId": "120250000000000002",
    "adSetId": "120250000000000003",
    "creativeId": "120250000000000004",
    "adId": "120260000000000001"
  },
  "mock": false
}

Campaigns, ad sets, creatives and ads

The full tree, step by step (each create answers 201 with the new object):

POST /api/v1/ads/campaigns
curl -X POST https://staging.postgale.com/api/v1/ads/campaigns \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","adAccountId":"act_1234567890","name":"Spring beans","goal":"traffic","confirm":true}'
POST /api/v1/ads/adsets
curl -X POST https://staging.postgale.com/api/v1/ads/adsets \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","adAccountId":"act_1234567890","campaignId":"120250000000000002","name":"US 25-45 coffee","dailyBudget":1500,"startTime":"2026-11-01T09:00:00Z","endTime":"2026-11-15T09:00:00Z","optimizationGoal":"LINK_CLICKS","targeting":{"countries":["US"],"ageMin":25,"ageMax":45,"publisherPlatforms":["facebook","instagram"]},"confirm":true}'
POST /api/v1/ads/creatives
curl -X POST https://staging.postgale.com/api/v1/ads/creatives \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","adAccountId":"act_1234567890","name":"Beans link ad","link":"https://shop.example/beans","message":"Fresh roast, shipped today","headline":"Spring beans","imageUrl":"https://media.example/beans.jpg","callToAction":"SHOP_NOW"}'
POST /api/v1/ads/ads
curl -X POST https://staging.postgale.com/api/v1/ads/ads \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","adAccountId":"act_1234567890","name":"Beans ad","adSetId":"120250000000000003","creativeId":"120250000000000004","confirm":true}'

Read with GET /ads/campaigns, /ads/adsets?parentId=, /ads/ads?parentId=, /ads/creatives (?accountId&adAccountId&statuses&limit&after) and GET /ads/campaigns/{id}?accountId=. Update or pause with PATCH /ads/campaigns|adsets|ads/{id}:

PATCH /api/v1/ads/campaigns/{id}
curl -X PATCH https://staging.postgale.com/api/v1/ads/campaigns/120250000000000002 \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","status":"PAUSED"}'
curl -X PATCH https://staging.postgale.com/api/v1/ads/adsets/120250000000000003 \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","dailyBudget":2500,"targeting":{"countries":["US","CA"]},"confirm":true}'

Targeting

targeting takes countries, regions, cities (with radius), zips, ageMin/ageMax (13–65), genders (1 male, 2 female), interests, customAudiences/excludedCustomAudiences and publisherPlatforms; or raw, a Meta targeting spec used as is. Find interest and location keys with GET /ads/targeting/search?accountId&q=coffee&type=adinterest.

Custom audiences

POST /api/v1/ads/audiences
curl -X POST https://staging.postgale.com/api/v1/ads/audiences \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","adAccountId":"act_1234567890","name":"Newsletter subscribers"}'
POST /api/v1/ads/audiences/{id}/users
curl -X POST https://staging.postgale.com/api/v1/ads/audiences/23850000000000001/users \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_fb_page","emails":["[email protected]","[email protected]"]}'

Emails and phones are normalized and SHA-256 hashed by Postgale; raw values never reach Meta. Use the audience id in targeting.customAudiences. DELETE /ads/audiences/{id}?accountId= removes it.

Insights

GET /api/v1/ads/insights
curl https://staging.postgale.com/api/v1/ads/insights?accountId=acc_fb_page&objectId=120250000000000002&datePreset=last_7d&breakdowns=age,gender \
  -H 'Authorization: Bearer sk_…'

Rows carry impressions, reach, frequency, clicks, inline_link_clicks, spend, cpc, cpm, ctr and actions. objectId is an ad account (act_…), campaign, ad set or ad; since/until (YYYY-MM-DD), level, timeIncrement=1 for daily rows.

Permissions

The Facebook account must be (re)connected after the Meta app has Advanced Access to ads_management and ads_read (plus business_management, pages_manage_ads for boosting). Postgale keeps the connecting user's token encrypted for ads calls; it is never returned.

Errors

StatuscodeWhen
400confirmation_requiredA spend-capable call (create, boost, ACTIVE, budget, bid, schedule or targeting change) without confirm: true. Nothing was created or changed
400invalid_requestA field failed validation (budget below 100 minor units, lifetime budget without endTime, unknown goal…)
400not_supportedaccountId is not a Facebook Page account, or the object level cannot be updated
403approval_requiredApproval applies to the key, organization or account: ads writes are refused
403missing_permissionThe Facebook account did not grant ads_management / ads_read: reconnect it after the app has the permissions
409reconnect_requiredThe token is no longer valid: reconnect the Facebook account
429rate_limitedMeta's Marketing API rate limits (development tier is low)
502platform_errorAny other Meta failure (the message carries Meta's reason)

On this page