---
title: "Replace campaign-level negative keywords"
method: PUT
path: "/v1/ads/campaigns/{campaignId}/negative-keywords"
tags: ["Ad Campaigns"]
---

# Replace campaign-level negative keywords

`PUT /v1/ads/campaigns/{campaignId}/negative-keywords`

Replaces the FULL set of campaign-level negative keywords (C.270): the desired
list is diffed against what Google already has, and the difference is applied
as one `create`/`remove` mutate. Send an empty array to clear every campaign
negative.

The platform is always discovered from the campaign itself; a non-Google
campaign returns 501 rather than 404, whether or not `platform` was sent.

## Path parameters

- `campaignId` string, required

## Request body

- object
  - `platform` 'facebook' | 'instagram' | 'tiktok' | 'linkedin' | 'pinterest' | 'google' | 'twitter' | 'openai' — Optional and NOT authoritative: the resolved campaign's own platform decides 200 vs 501, never this hint.
  - `keywords` KeywordEntry[], required
    - union — A Google Search keyword: a bare string (BROAD match), or an object naming the match type.
      - string — Keyword text; defaults to BROAD match
      - object
        - `text` string, required
        - `matchType` 'exact' | 'phrase' | 'broad'

## Response `200`

Campaign-level negative keywords replaced

- object
  - `created` integer — Negative criteria newly created on Google
  - `removed` integer — Negative criteria removed from Google
  - `keywords` object[] — The full negative-keyword set after the replace
    - `criterionId` string
    - `text` string
    - `matchType` 'exact' | 'phrase' | 'broad'

## Other responses

- `400` — Invalid request
- `401` — Unauthorized
- `404` — Campaign not found
- `429` — Google Ads operations budget exhausted; retry later
- `501` — Only available on Google Ads campaigns

## Changes

- **2026-09-25** `2c04683ce694` — 4 info
  - added the optional property `details/adAccountId` to the response with the `400` status
  - added the optional property `details/createdObjects` to the response with the `400` status
  - added the optional property `details/stage` to the response with the `400` status
  - added the optional property `details/unconfirmedWrite` to the response with the `400` status
- **2026-09-16** `3e6ddf2a99ea` — 1 info
  - added the optional property `details/budgetScope` to the response with the `400` status
- **2026-09-15** `0dba7d004d75` — 2 info
  - added the optional property `details/quotaExhausted` to the response with the `400` status
  - added the optional property `details/quotaScope` to the response with the `400` status
- **2026-09-08** `d7a8fa118ef0` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/ads/campaigns/:campaignId/negative-keywords/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/b2325332041a?raw)
