Ad Campaigns

Update a campaign

Changed on

Changes a campaign's settings, such as its name, budget, bidding or tracking URLs.

Send at least one of budget, bidStrategy, portfolioBidStrategyId, targetImpressionShare, manualCpc, networkSettings, trackingUrlTemplate, finalUrlSuffix, sharedBudgetId, name or platformSpecificData. An unsupported field is always an error, never a silent drop.

Body fieldMetaGoogleOthers
bidStrategyYesYes501
bidAmount, roasAverageFloor400 (ad-set level)Yes400
portfolioBidStrategyId400Yes400
targetImpressionShare400Search only400
manualCpc400Search and Display400
networkSettings400Search only400
trackingUrlTemplate, finalUrlSuffix400Yes400
sharedBudgetId400Yes400
budget (CBO; ABO returns 409)YesDaily onlyOpenAI and Reddit: daily or lifetime; others 501
nameYes501Reddit: Yes; others 501
platformSpecificData.spendCapYes400400
accountId (empty campaigns)Yes--

Meta budget edits check the live campaign budget, so an older local ABO stamp cannot block a CBO campaign. A successful edit repairs local ad budget fields. A live ABO campaign still returns 409 with the ad-set budget endpoint.

On Google: LOWEST_COST_WITHOUT_CAP = Maximize Conversions, COST_CAP + bidAmount = Target CPA, LOWEST_COST_WITH_MIN_ROAS + roasAverageFloor = Target ROAS, LOWEST_COST_WITH_BID_CAP + bidAmount = Maximize Clicks with a CPC ceiling; portfolioBidStrategyId attaches a portfolio strategy instead (exclusive with bidStrategy). targetImpressionShare switches the campaign to Target impression share and manualCpc: { maxCpc } to Manual CPC (every ad group of the campaign gets maxCpc as its bid, in the same mutate); each is exclusive with every other strategy field. networkSettings, trackingUrlTemplate and finalUrlSuffix go out in one campaign update, each field sent written on its own leaf, so an omitted one keeps its current value; an empty string clears a URL field. Setting the standard triplet on a campaign that is currently on a PORTFOLIO strategy is rejected: detach it in Google Ads first, since it is shared across campaigns.

Google budget updates read the current budget before mutation. Shared budgets return 409 unless allowSharedBudgetUpdate=true is explicitly supplied, because the change affects every campaign using that budget. A budget counts as shared when Google flags it as shared or when more than one campaign uses it (older budgets can serve several campaigns without the flag). Unknown sharing state also returns 409.

sharedBudgetId (Google) moves the campaign onto a shared budget from GET /v1/ads/shared-budgets, which also needs allowSharedBudgetUpdate: true (409 otherwise) because the campaign then splits that budget with every campaign on it; budget cannot ride along. sharedBudgetId: null moves it back onto a new budget of its own, sized by budget (required, daily); the new budget and the switch go out in one atomic Google mutate. A campaign that already has its own budget returns 409 for null. The budget a campaign leaves is not removed. The response carries the budget the campaign now uses.

OpenAI Ads campaigns carry exactly one spend cap: budget.type daily or lifetime replaces whichever cap the campaign had, with a minimum of 1 in the ad account's currency (422 below it). Lifetime can switch to daily, but OpenAI never switches a daily cap back to lifetime (422).

Reddit Ads: budget changes the amount of a campaign budget (Reddit campaign budget optimization); a campaign whose ad groups carry the budget returns 409 or 422, use PUT /v1/ads/ad-sets/{adSetId} there. Reddit cannot switch a published budget between daily and lifetime, so budget.type must match the current one (422 otherwise). bidStrategy is set per ad group on Reddit (PUT /v1/ads/ad-sets/{adSetId}).

accountId forwards the update straight to Meta for a campaign with zero ads, which would otherwise 404; the response then carries updated: 0.

put/v1/ads/campaigns/{campaignId}

Request

  • Base URL: https://zernio.com/api
  • URL: https://zernio.com/api/v1/ads/campaigns/{campaignId}
  • Auth: HTTP bearer

Path parameters

campaignIdstring required

Platform campaign ID

Request body

platform'facebook' | 'instagram' | 'tiktok' | 'linkedin' | 'pinterest' | 'google' | 'twitter' | 'openai' | 'reddit' required

Required: platform campaign IDs are not globally unique.

accountIdstring

Meta only. Zernio SocialAccount id owning the ad account. Needed only for an EMPTY campaign (zero ads); ignored otherwise.

bidStrategy'LOWEST_COST_WITHOUT_CAP' | 'LOWEST_COST_WITH_BID_CAP' | 'COST_CAP' | 'LOWEST_COST_WITH_MIN_ROAS'

Meta bid strategy. Same enum applies at campaign and ad-set level; ad-set value (when set) overrides campaign-level. Cross-field rules:

  • LOWEST_COST_WITHOUT_CAP (default): auto-bid, forbids bidAmount and roasAverageFloor.
  • LOWEST_COST_WITH_BID_CAP / COST_CAP: require bidAmount (whole currency units).
  • LOWEST_COST_WITH_MIN_ROAS: requires roasAverageFloor (decimal multiplier, 2.0 = 2.0x). Source: facebook-business-sdk-codegen api_specs/specs/enum_types.json (AdSet_bid_strategy, Campaign_bid_strategy).
bidAmountnumber

Google only. Whole currency units (USD: 12 = $12.00). Max CPC for LOWEST_COST_WITH_BID_CAP, CPA target for COST_CAP; required for both.

roasAverageFloornumber

Google only. Decimal ROAS multiplier (2.0 = 2.0x), required for LOWEST_COST_WITH_MIN_ROAS.

portfolioBidStrategyIdstring

Google only. Attach an existing portfolio bid strategy (numeric id from GET /v1/ads/bid-strategies) instead of setting bidStrategy. Exclusive with bidStrategy.

allowSharedBudgetUpdateboolean

Google only. Explicitly allow changing a shared campaign budget (flagged as shared by Google, or used by more than one campaign), affecting every campaign that uses it. Does not bypass an unknown sharing state. Also required to move a campaign onto a shared budget with sharedBudgetId.

trackingUrlTemplatestring

Google only. campaign.tracking_url_template; an empty string clears it.

finalUrlSuffixstring

Google only. campaign.final_url_suffix; an empty string clears it.

sharedBudgetIdstring nullable

Google only. Move the campaign onto this shared budget (id from GET /v1/ads/shared-budgets), or null to move it back onto a budget of its own sized by budget.

namestring

Meta and Reddit. Rename the campaign.

Response

Campaign updated

updatedinteger

Local Ad documents mirrored. 0 on the empty-campaign path.

budgetLevel'campaign'
bidStrategy'LOWEST_COST_WITHOUT_CAP' | 'LOWEST_COST_WITH_BID_CAP' | 'COST_CAP' | 'LOWEST_COST_WITH_MIN_ROAS'

Meta bid strategy. Same enum applies at campaign and ad-set level; ad-set value (when set) overrides campaign-level. Cross-field rules:

  • LOWEST_COST_WITHOUT_CAP (default): auto-bid, forbids bidAmount and roasAverageFloor.
  • LOWEST_COST_WITH_BID_CAP / COST_CAP: require bidAmount (whole currency units).
  • LOWEST_COST_WITH_MIN_ROAS: requires roasAverageFloor (decimal multiplier, 2.0 = 2.0x). Source: facebook-business-sdk-codegen api_specs/specs/enum_types.json (AdSet_bid_strategy, Campaign_bid_strategy).
bidAmountnumber
roasAverageFloornumber
portfolioBidStrategyIdstring

Google only. Echoed back, but NOT mirrored onto local Ad documents (no column for it yet).

trackingUrlTemplatestring
finalUrlSuffixstring
sharedBudgetIdstring nullable

Google only. Echoed back when the campaign moved budgets; budget is then the budget it now uses.

platformSpecificDataobject

Changes

    • info

      added the new linkedin enum value to the request property

    • info

      added the new openai enum value to the request property

    • info

      added the new pinterest enum value to the request property

    • info

      added the new reddit enum value to the request property

    • info

      added the new tiktok enum value to the request property

    • info

      added the new twitter enum value to the request property

    • info

      added the new optional request property

    • info

      added the new optional request property

    • info

      added the new optional request property

    • info

      added the new optional request property

    • info

      added the new optional request property

    • info

      added the new optional request property

    • info

      added the optional property to the response with the status

    • info

      added the optional property to the response with the status

    • info

      added the optional property to the response with the status

    • info

      added the optional property to the response with the status

    • info

      added the optional property to the response with the status

    • info

      added the optional property to the response with the status