---
title: "Update a campaign"
method: PUT
path: "/v1/ads/campaigns/{campaignId}"
tags: ["Ad Campaigns"]
---

# Update a campaign

`PUT /v1/ads/campaigns/{campaignId}`

Campaign-level edits. Send at least one of `budget`, `bidStrategy`,
`portfolioBidStrategyId`, `name` or `platformSpecificData`. An unsupported
field is always an error, never a silent drop.

| Body field | Meta | Google | Others |
|---|---|---|---|
| `bidStrategy` | Yes | Yes | 501 |
| `bidAmount`, `roasAverageFloor` | 400 (ad-set level) | Yes | 400 |
| `portfolioBidStrategyId` | 400 | Yes | 400 |
| `budget` (CBO; ABO returns 409) | Yes | Daily only | 501 |
| `name` | Yes | 501 | 501 |
| `platformSpecificData.spendCap` | Yes | 400 | 400 |
| `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`). 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. Unknown sharing state also returns 409.

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

## Path parameters

- `campaignId` string, required

## Request body

- object
  - `platform` 'facebook' | 'instagram' | 'google', required — Required: platform campaign IDs are not globally unique.
  - `accountId` string — **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`).
  - `bidAmount` number — **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.
  - `roasAverageFloor` number — **Google only.** Decimal ROAS multiplier (2.0 = 2.0x), required for LOWEST_COST_WITH_MIN_ROAS.
  - `portfolioBidStrategyId` string — **Google only.** Attach an existing portfolio bid strategy (numeric id from GET /v1/ads/bid-strategies) instead of setting bidStrategy. Exclusive with bidStrategy.
  - `allowSharedBudgetUpdate` boolean — Google only. Explicitly allow changing a shared campaign budget, affecting every campaign that uses it. Does not bypass an unknown sharing state.
  - `budget` object — Meta CBO or Google daily campaign budget, in whole currency units.
    - `amount` number, required — Budget amount in the ad account's currency
    - `type` 'daily' | 'lifetime', required
  - `name` string — **Meta only.** Rename the campaign.
  - `platformSpecificData` object — **Meta only.** Platform implied by the `platform` body param, same convention as POST /v1/ads/create.
    - `spendCap` number, nullable — Campaign lifetime spend cap, in the ad account's currency (Meta `spend_cap`). Pass null to remove the cap; 0 is rejected by Meta.

## Response `200`

Campaign updated

- object — Echoes back only the fields you sent, plus `updated`.
  - `updated` integer — Local Ad documents mirrored. 0 on the empty-campaign path.
  - `budget` AdCampaignBudget — Budget amount in the ad account's native currency (see the campaign's `currency` field for the code).
    - `amount` number, required
    - `type` 'daily' | 'lifetime', required
    - `amountMicros` string — Google only. Exact decimal micros; DAILY uses amount_micros and CUSTOM_PERIOD uses total_amount_micros.
    - `explicitlyShared` boolean, nullable — Google only. True for a shared budget; null when unavailable. Shared writes require allowSharedBudgetUpdate=true; unknown sharing status cannot be overridden.
    - `resourceName` string, nullable — Google only. campaign_budget.resource_name, or null when unavailable.
    - `deliveryMethod` string, nullable — Google only. campaign_budget.delivery_method, typically STANDARD, or null when unavailable.
  - `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`).
  - `bidAmount` number
  - `roasAverageFloor` number
  - `portfolioBidStrategyId` string — Google only. Echoed back, but NOT mirrored onto local Ad documents (no column for it yet).
  - `platformSpecificData` object

## Other responses

- `400` — Invalid input, or a field the resolved platform does not support at the campaign level (see the support table)
- `401` — Unauthorized
- `403` — Returned with code `ads_allowance_exceeded` when the team has no payment method on file and has reached the 500 free live ads: add a card to resume.
- `404` — The account or requested resource was not found or is not accessible. An account ID may have been disconnected and removed. Read GET /v1/accounts for current account IDs.
- `409` — Meta campaign is ABO, or the Google budget is shared without allowSharedBudgetUpdate=true, or sharing state cannot be verified. The account may also be inactive or need reconnection (code ads_connection_required). Reconnect it and read GET /v1/accounts for its current ID before retrying.
- `501` — Operation not supported on this platform

## Changes

- **2026-09-16** `3e6ddf2a99ea` — 1 info
  - added the optional property `details/budgetScope` to the response with the `404` status
- **2026-09-15** `0dba7d004d75` — 2 info
  - added the optional property `details/quotaExhausted` to the response with the `404` status
  - added the optional property `details/quotaScope` to the response with the `404` status
- **2026-09-10** `e70ed06e7150` — 1 info
  - added the media type `application/json` for the response with the status `404`
- **2026-09-09** `222e3a38a382` — 3 breaking, 2 info
  - the `budget` response's property type changed from `object` to no type for status `200`
  - removed the required property `budget/amount` from the response with the `200` status
  - removed the required property `budget/type` from the response with the `200` status
  - added the new optional request property `allowSharedBudgetUpdate`
  - …1 more
- **2026-09-08** `d7a8fa118ef0` — 2 info
  - added the new optional request property `portfolioBidStrategyId`
  - added the optional property `portfolioBidStrategyId` to the response with the `200` status

[Full history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/ads/campaigns/:campaignId/put.md)

---

[API](https://skmtc.dev/zernio/apis/zernio-api.md) · [All operations](https://skmtc.dev/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zernio/apis/zernio-api/revisions/106013ac73b8?raw)
