Ad Sets
Ad set budgets, schedule, optimization goal, and post-launch delivery edits
Ad Sets
The ad set is where delivery is decided: who sees the ad, when, how Meta optimizes, and (on ABO campaigns) how much is spent. POST /v1/ads/create builds one for you, and everything below edits it afterwards without recreating the ad.
| Setting | Owned by | Editable after launch |
|---|---|---|
| Budget (ABO) | Ad set | Yes, PUT /v1/ads/ad-sets/{adSetId} |
| Status | Ad set | Yes, on the same endpoint or /status |
Bid strategy, bidAmount, roasAverageFloor | Ad set | Yes, see Bid strategy |
| Targeting | Ad set | Yes, via PUT /v1/ads/{adId} with targeting (the edit lands on the ad's ad set) |
Optimization goal, billing event, schedule, promotedObject | Ad set | Yes, platformSpecificData |
| Budget (CBO) | Campaign | Yes, on the campaign endpoint |
Update budget and status (ABO)
PUT /v1/ads/ad-sets/{adSetId} when budgetLevel === 'adset'. Supply budget and/or status, at least one is required. Returns 409 with code: "BUDGET_LEVEL_MISMATCH" if the parent campaign is CBO.
await zernio.adcampaigns.updateAdSet({
path: { adSetId: 'ADSET_ID' },
body: {
platform: 'facebook',
budget: { amount: 75, type: 'daily' },
status: 'active',
},
});client.ad_campaigns.update_ad_set(
ad_set_id="ADSET_ID",
platform="facebook",
budget={"amount": 75, "type": "daily"},
status="active",
)curl -X PUT "https://zernio.com/api/v1/ads/ad-sets/ADSET_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "facebook",
"budget": { "amount": 75, "type": "daily" },
"status": "active"
}'Budget amounts are whole currency units of the ad account (USD: 75 = $75.00). Read currency off the campaign node in /v1/ads/tree before you show or send a number, budgets are never normalized to a single currency.
Status-only toggles
For pause/resume without touching the budget, PUT /v1/ads/ad-sets/{adSetId}/status mirrors PUT /v1/ads/campaigns/{campaignId}/status. See Duplicate & Lifecycle for the campaign-level and bulk variants.
Post-launch delivery edits
Beyond budget, status, name and bid strategy, the Meta-only delivery settings live under platformSpecificData on PUT /v1/ads/ad-sets/{adSetId}:
await zernio.adcampaigns.updateAdSet({
path: { adSetId: 'ADSET_ID' },
body: {
platform: 'facebook',
platformSpecificData: {
optimizationGoal: 'OFFSITE_CONVERSIONS',
billingEvent: 'IMPRESSIONS',
startDate: '2026-08-01T09:00:00Z',
endDate: '2026-08-31T23:59:00Z',
promotedObject: { pixelId: '1729525464415281', customEventType: 'PURCHASE' },
},
},
});client.ad_campaigns.update_ad_set(
ad_set_id="ADSET_ID",
platform="facebook",
platform_specific_data={
"optimizationGoal": "OFFSITE_CONVERSIONS",
"billingEvent": "IMPRESSIONS",
"startDate": "2026-08-01T09:00:00Z",
"endDate": "2026-08-31T23:59:00Z",
"promotedObject": {"pixelId": "1729525464415281", "customEventType": "PURCHASE"},
},
)curl -X PUT "https://zernio.com/api/v1/ads/ad-sets/ADSET_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "facebook",
"platformSpecificData": {
"optimizationGoal": "OFFSITE_CONVERSIONS",
"billingEvent": "IMPRESSIONS",
"startDate": "2026-08-01T09:00:00Z",
"endDate": "2026-08-31T23:59:00Z",
"promotedObject": { "pixelId": "1729525464415281", "customEventType": "PURCHASE" }
}
}'| Field | Notes |
|---|---|
optimizationGoal | Forwarded verbatim. Meta validates it against the campaign objective and rejects incompatible pairs. |
billingEvent | What Meta charges for (IMPRESSIONS, LINK_CLICKS, THRUPLAY, ...). On create it's a top-level field of POST /v1/ads/create, consistent with the top-level optimizationGoal / promotedObject / startDate there. |
startDate / endDate | ISO 8601. Setting endDate schedules the stop; see below for clearing it. |
promotedObject | The full object, replaced wholesale. Accepted keys are documented under Conversion campaigns. |
platformSpecificData is strict. An unknown or misspelled key returns a 400 naming the path (platformSpecificData.optimisationGoal) instead of being silently dropped. A non-Meta account also returns 400. The same applies to promotedObject: a typo used to change what the ad set optimized toward with no warning, now it fails loudly.
Clearing an end date. Meta clears end_time when it receives 0, so pass endDate: null and Zernio sends the sentinel for you. The ad set then runs until you pause it.
Meta validates combinations on its side, and its error surfaces verbatim: switching an OUTCOME_TRAFFIC ad set to OFFSITE_CONVERSIONS without a pixel on the promotedObject returns Meta's own message, not a generic failure.
Read an ad set live
GET /v1/ads/ad-sets/{adSetId} reads the ad set straight from Meta rather than from Zernio's synced copy. Use it when you need the current state right now, including the learning phase.
const { data } = await zernio.adcampaigns.getAdSetDetails({
path: { adSetId: 'ADSET_ID' },
query: { accountId: 'ACCOUNT_ID' },
});
console.log(data.adSet.learning_stage_info?.status);data = client.ad_campaigns.get_ad_set_details(
ad_set_id="ADSET_ID",
account_id="ACCOUNT_ID",
)
print(data.ad_set.learning_stage_info)curl "https://zernio.com/api/v1/ads/ad-sets/ADSET_ID?accountId=ACCOUNT_ID" \
-H "Authorization: Bearer YOUR_API_KEY"The default projection includes learning_stage_info; pass fields to override it with your own Meta field list.
Learning phase
learning_stage_info is Meta's own verdict on whether the ad set has exited the learning phase, forwarded verbatim:
status | Meaning |
|---|---|
LEARNING | Still learning. Delivery and cost are unstable, and a significant edit restarts the clock. |
SUCCESS | Exited learning. |
FAIL | Learning limited: the ad set never gathered ~50 optimization events in a week. Broaden targeting, raise the budget, or consolidate ad sets. |
WAIVING | Meta waived the requirement for this ad set. |
last_sig_edit_ts is the last significant edit, in Unix seconds. It can be absent on a SUCCESS entry.
Absence is not health. Meta omits learning_stage_info.status on paused ad sets and on many older active ones (they come back with only attribution_windows). Treat a missing status as unknown, not as "exited learning".
Zernio also captures the learning stage during the regular ads sync, so the ad documents returned by GET /v1/ads and GET /v1/ads/tree carry it without an extra live call. The live read above is for when you need it fresher than the last sync pass.
Add an ad to an existing ad set
Pass adSetId on POST /v1/ads/create to attach one more ad to an ad set Meta has already optimized on, inheriting its budget, targeting, schedule and goal. See Creatives.
Duplicate an ad set
POST /v1/ads/ad-sets/{adSetId}/duplicate copies the ad set (and by default its ads and creatives), optionally into a different campaign. See Duplicate & Lifecycle.