Ads

Facebook advertising, on the same connection as Pages. These are platform-specific routes rather than a normalized surface: campaign structure has no equivalent on the other providers Adeli covers.

Every endpoint needs a Facebook connection whose authorization includes the ads permissions. A connection made before those were requested keeps working for Pages and answers 403 ads_permission_required here, with the missing scopes in error.details.missingScopes; the fix is another authorization, not a repair.

Campaign and metric reads are live. Adeli stores no campaign structure and no metrics, so every figure is as fresh as that response. The one exception is the list of ad accounts, which is reused for up to 15 minutes.

When Meta throttles a request, the response is 429 rate_limited with a Retry-After header, error.details.retryAfterSeconds (Meta's own estimate when it gives one, otherwise a minute) and error.details.limit: app when the app's hourly call budget for this connection is spent, ad_account when that ad account's own budget is.

When Meta refuses a request and explains why, the response is 422 provider_rejected with Meta's explanation in error.message — for example that a Business portfolio has reached its limit of ad accounts.

GET/api/v1/ads/accountsList ad accounts

Every ad account the connection can reach, both directly assigned and owned by a Business portfolio the authorizing person administers. businessId is null for a directly-assigned account.

The list is reused for up to 15 minutes; fetchedAt is when the oldest part of it was read from Meta. Pass refresh=true after granting access to a new ad account to see it immediately. Other endpoints re-list on their own when given an ad account id the stored list does not contain.

Query parameters

accountIduuid
A connected Facebook account. Defaults to the profile's.
profileIduuid
Defaults to your key's profile.
refreshboolean
true to ask Meta again instead of using the stored list.

POST/api/v1/ads/accountsCreate an ad account

Creates an ad account in a Business portfolio the authorizing person administers. Meta has no API for personal ad accounts. The currency and time zone cannot be changed afterwards, and a portfolio may only hold a limited number of ad accounts — commonly one until it has spend history — which Meta reports as 422 provider_rejected.

An Idempotency-Key header is required, since a retry would be a second ad account: the same key replays 200 with the same account, 409 ad_account_create_failed if it failed, or 409 ad_account_create_in_progress. A new account is 201, and the account list is refreshed to include it.

Body

businessIdstringRequired
A portfolio from GET `/api/v1/ads/businesses`.
namestringRequired
Up to 100 characters.
timezonestringRequired
An IANA zone Adeli maps to Meta's time zone id, such as America/New_York or Europe/London.
currencystring
ISO 4217. Defaults to USD.

GET/api/v1/ads/businessesList Business portfolios

The portfolios the authorizing person administers — the ones an ad account can be created in.

GET/api/v1/ads/accounts/{adAccountId}Ad account details

The account as listed, plus details: accountStatus, disableReason, amountSpent and spendCap in whole currency units (spendCap is null when there is none), and hasPaymentMethod — null when Meta withholds it, which it does unless the person has the MANAGE task on the account. Always read live.

PATCH/api/v1/ads/accounts/{adAccountId}Update an ad account

Renames an account or sets its spending limit — Meta's hard stop, counted from the moment it is set: once the account has spent that much, all its ads stop delivering. Returns the same shape as the details endpoint.

Body

namestring
Up to 100 characters.
spendCapnumber | null
Whole currency units; null removes the limit.

Payment methods and closing an account have no API; do those in Meta.

GET/api/v1/ads/treeCampaign tree

The Campaign → Ad set → Ad hierarchy for one ad account, with spend, impressions, reach, clicks, ctr, cpc and cpm rolled up at every level. An object that did not deliver in the range reports zeroes rather than being omitted.

Query parameters

adAccountIdstringRequired
An ad account id, in Meta's act_<digits> form.
sincedate
YYYY-MM-DD. Defaults to 28 days ago.
untildate
YYYY-MM-DD. Defaults to today. Ranges over 730 days are rejected.
accountIduuid
A connected Facebook account.
profileIduuid
Defaults to your key's profile.

GET/api/v1/ads/insightsDaily insights

One point per day for an ad account, or for a single campaign, ad set or ad when objectId is given.

Query parameters

adAccountIdstringRequired
An ad account id.
objectIdstring
A campaign, ad set or ad id. Defaults to the whole account.
sincedate
YYYY-MM-DD. Defaults to 28 days ago.
untildate
YYYY-MM-DD. Defaults to today.

POST/api/v1/ads/campaignsCreate a campaign

Promotes a post the connected Page already published, creating a campaign, ad set, creative and ad. Everything is created paused. Nothing delivers and nothing spends until you activate it, which is a separate call.

An Idempotency-Key header is required. This is the only endpoint in the API where a retry has a cost, so the same key replays the original result rather than creating a second campaign: 200 with the same object ids when the campaign was built, 409 campaign_create_failed with the original reason when it failed, and 409 campaign_create_in_progress while it is still being created. To retry a failed create, send a new key.

Body

adAccountIdstringRequired
The ad account to create in.
pagePostIdstringRequired
The post id alone; the Page half of Meta's story id is server-derived.
dailyBudgetnumberRequired
Whole currency units, as Meta expects: 25 means 25.00.
startDatedateRequired
YYYY-MM-DD.
endDatedateRequired
YYYY-MM-DD, on or after startDate.
campaignNamestringRequired
Shown in Ads Manager.

Responds 201 with the created object ids, Adeli's campaign record (its id is the campaignRecordId below), and a preview when Meta returns one: Meta's body markup, plus the url, width and height of the facebook.com preview page it wraps. Embed url in an iframe that allows scripts; the page renders blank without them. If a step fails, Adeli deletes what it already created and responds 502 campaign_create_failed with the failing step and any orphanedObjectIds it could not remove — those are paused, so they cost nothing, but they are named so you can find them in Ads Manager.

DELETE/api/v1/ads/campaigns/{campaignId}Delete a campaign

Deletes a campaign together with its ad sets and ads, which stops any delivery immediately. It cannot be undone. The campaign must belong to adAccountId; otherwise the response is 404 campaign_not_found and nothing is deleted. Responds 204. Adeli's record of a campaign it created is marked deleted.

Query parameters

adAccountIdstringRequired
The ad account the campaign belongs to.
accountIduuid
A connected Facebook account. Defaults to the profile's.

PUT/api/v1/ads/statusActivate, pause or resume

Sets an ad's or ad set's delivery status. Activating an ad is what starts spending.

Body

adAccountIdstringRequired
The ad account the object belongs to.
objectIdstringRequired
An ad or ad set id.
statusstringRequired
ACTIVE or PAUSED.
campaignRecordIduuid
Keeps Adeli's campaign record in step when the object came from POST `/api/v1/ads/campaigns`.

GET/api/v1/ads/leadsLead forms and submissions

Without formId, the lead generation forms belonging to the connected Page. With it, that form's submissions, including the field data and the campaign, ad set and ad each lead came from.

Adeli does not store lead data. Submissions are read from Facebook when you ask for them and are not retained after the response.

Query parameters

formIdstring
A lead form id. Omit to list the Page's forms.
accountIduuid
A connected Facebook account.
profileIduuid
Defaults to your key's profile.