Skip to main content
PUT /api/v2/buyer/campaigns/:campaignId For managed campaigns, the mediaBuys[] array lets you update, cancel, or delete individual media buys and their packages in a single call. Tracked tracked projections are read-only; their only accepted compatibility update is a refresh request that reads from the seller.

Request

Parameters

Always confirm optimization_goals with the buyer before changing them — silent goal changes break optimization continuity.
Retrieve package IDs with Get campaign products, optionally filtered by mediaBuyId. When the buyer names a package by its period or its dates (“the Week 6 package”, “the display package ending 2026-08-11”), use Get media buy packages instead: it is scoped to one media buy and returns each package’s pacingPeriod and flight window, which is what resolves a description to exactly one id. Package IDs are opaque, so do not derive one by parsing the trailing number on another. Do not submit a real update to test whether an ID is valid: the API validates package ownership before dispatch and returns VALIDATION_ERROR for unknown IDs.

Refresh a tracked campaign

The compatibility refresh request is:
This request prioritizes a fresh account read and reconciles the projection. It does not change the connected provider account. Any other update shape for a tracked campaign is rejected.

Reducing campaign budgets

Lower an executed campaign’s spend through this campaign update endpoint. Do not archive and rebuild a media buy just to reduce its budget. Every budget is gross (fee-inclusive), so the comparison is direct: live media buys can never allocate more than budget.total. Lowering budget.total below what live media buys have already allocated is rejected with INSUFFICIENT_MEDIA_BUDGET — the error names the new total and the committed allocation so you know what to shrink or cancel first. Lowering into headroom (new total at or above the current allocation) succeeds on its own. To reduce the campaign and its live media buys together, put both changes in one request: the new budget.total plus explicit mediaBuys[].packages[].budget reductions. The request is validated against the projected post-update allocation and applied atomically — if a package reduction cannot be applied, the campaign-level budget.total is not lowered.
Lower into headroom — budget change alone
Reduce campaign budget and live packages in one call

Propagating geo targeting to live media buys

Changing constraints.geo_countries or constraints.geo_regions on a campaign also pushes the new value to every already-deployed package on that campaign’s media buys — it is not limited to filtering future product discovery. The new value replaces each package’s existing declaration for that field.
Add a country to a live campaign's targeting
The response’s top-level geoConstraintPropagationResults array reports one entry per media buy with deployed packages:
  • applied — the seller accepted the new value.
  • unchanged — every package on that media buy already matched the requested value; nothing was sent.
  • rejected — the seller (or its declared capabilities) does not support changing this field on an already-live buy. reason explains why. The campaign’s own stored constraints still updates for future product discovery even when a live media buy rejects propagation.
geoConstraintPropagationResults is present whenever the request changes geo_countries or geo_regions — as an empty array ([]) when the campaign has no media buy with deployed packages yet (e.g. a DRAFT-only campaign; there is nothing to propagate to). It is omitted entirely only when the request does not touch geo_countries or geo_regions at all. You cannot clear a field to “no value at all” this way: geo_countries: [] (or geo_regions: []) is rejected for any live package that currently has a non-empty value, since there is no way to declare “no countries” to a seller — only a non-empty replacement is supported. A package that you also update explicitly via mediaBuys[].packages[].targetingOverlay in the same request keeps that explicit value for the field(s) it sets; the propagated campaign-level value is skipped for exactly those fields on that package, not the whole package. This behavior currently covers geo_countries/geo_regions only. Other constraints fields (language, device_type, device_platform, geo_metros, geo_postal_areas) are not propagated to live media buys. Only one update to a given campaign can be applied at a time. A second request that arrives while an earlier one on the same campaign is still being applied — even one that doesn’t touch geo_countries/geo_regions — gets 409 CONFLICT with a message telling you to retry shortly, rather than risk silently reverting a geo_countries/geo_regions change that already took effect. This is a brief window, not a queue: a non-geo_countries/ geo_regions request only holds the conflict window for its own write, not for however long a concurrent geo propagation takes to reach the seller.

Updating a package’s flight dates

Change an individual package’s flight start or end date without canceling and recreating it — useful when one package needs a narrower or extended window than the rest of the media buy. startTime/endTime must fall within the media buy’s own date range.
Extend one package's flight end date
Only the packages you name are changed — sibling packages on the same media buy keep their existing flight windows. When a package has no stored flight dates and its seller has not returned valid_actions or available_actions for the buy, the request is rejected as unsupported rather than forwarded speculatively. Wait for seller capabilities or confirm package date support with the seller first.

Response

When the request includes pacingPeriods, the response also carries a pacingCascadeResult block at the top level alongside campaign, summarizing the per-media-buy outcome of pushing appended periods to live media buys. See the Pacing periods guide for that shape, append-only rules, and unsupported-agent fallback. When the request changes constraints.geo_countries or constraints.geo_regions and the campaign has live media buys, the response also carries a geoConstraintPropagationResults array — see Propagating geo targeting to live media buys. When one or more media buy updates require seller approval (e.g. a seller-managed storefront), the server returns 202 Accepted with a proposals array instead of the campaign object:

Errors

  • 400 VALIDATION_ERROR — a creative_ids entry is not linked to the campaign or does not match a format the media buy’s products accept (the field is not silently filtered); or creative_ids was supplied with cancel/delete.
  • 422 CAPABILITY_NOT_SUPPORTED — a package flight-date update cannot be safely sent because the package has no stored dates and the seller has not declared available actions.
  • 409 INSUFFICIENT_MEDIA_BUDGET — the requested budget.total is below the projected live media buy allocation, even after applying the package budget reductions included in the same request. The error names the new total and the committed allocation.
  • 409 PRICING_NOT_CONFIGURED — the budget change involves media buys whose fee terms cannot be determined, so the projected allocation cannot be computed. details.unpricedBuyIds names the buys; resolve pricing for them first.
  • 409 CONFLICT — another update to the same campaign was still being applied when this request arrived. Retry the request.
  • 404 NOT_FOUND — campaign or referenced mediaBuyId not found.
See Errors for the full error contract.

Get campaign

Read the current resource first

Get media buy status

Poll live ADCP status

Pacing periods

Append-only pacing cascade

Campaign overview

Fields, lifecycle, and concepts