> ## Documentation Index
> Fetch the complete documentation index at: https://docs.semicola.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Frequency caps

> Cap how often a person sees your ads: the AdCP frequency_cap a seller enforces on a package, and buyer-side caps on advertisers and campaigns.

A **frequency cap** limits how often the same person is shown your ads. Semicola has two kinds, and
they are different fields:

| Kind                                        | Field                                                                                           | Who enforces it                                             |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| **Seller-side cap** (per package)           | `frequency_cap` in the package's [targeting overlay](/buy/targeting#package-targeting-overlays) | The seller, in its own ad server, on its own delivery.      |
| **Buyer-side cap** (advertiser or campaign) | `frequencyCaps` on the advertiser or campaign                                                   | Nobody yet: stored as configuration, never sent to sellers. |

<Warning>
  Buyer-side caps are one exposure budget per person across every seller running an advertiser or
  campaign. Enforcing that needs a request-time moment on each seller's inventory (real-time targeting),
  and no seller in Semicola is connected that way. So on every seller today a buyer-side cap **is never
  enforced there, not delayed and not partially applied**. Every write that sets one answers with a
  `frequencyCapsNote` saying so. For a cap a seller actually applies, use the package's
  `frequency_cap` below.
</Warning>

## Set a cap on a package

Use `update_media_buy` (or `PATCH /api/v2/buyer/media-buys/{id}`) on a draft or live buy:

```json theme={null}
{
  "mediaBuyId": "…",
  "packages": [
    {
      "packageId": "…",
      "targetingOverlay": {
        "frequency_cap": {
          "max_impressions": 3,
          "per": "households",
          "window": { "interval": 7, "unit": "days" }
        }
      }
    }
  ],
  "reason": "Limit repeat exposure on CTV"
}
```

The cap follows the AdCP `FrequencyCap` shape. Set at least one of:

| Field             | Notes                                                                                                                          |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `max_impressions` | Most impressions per person in the `window`. Needs `per` and `window`.                                                         |
| `per`             | What counts as one person: `individuals`, `households`, `devices`, `accounts`, `cookies` or `custom`.                          |
| `window`          | An AdCP duration such as `{ "interval": 7, "unit": "days" }`, or `{ "interval": 1, "unit": "campaign" }` for the whole flight. |
| `suppress`        | A cooldown between two exposures, such as `{ "interval": 60, "unit": "minutes" }`.                                             |

When both `suppress` and `max_impressions` are set, an impression is delivered only if both allow it.

The overlay is validated against the AdCP schema when you save. Like any overlay key, sending
`frequency_cap` replaces the package's current cap, and an update that leaves it out keeps it.

## The seller must support it

A cap only works if the seller enforces caps. If the seller declared which targeting it supports and
frequency capping isn't on the list, the update is refused before anything is sent: "Sales agent does not support the requested
targeting: frequency\_cap. Remove these targeting fields or use a sales agent that declares support."
A seller that declared no targeting support at all receives the cap, and whether it's honored is up to
that seller.

Ad platforms (Meta, TikTok and the others) run their own ad servers, and their storefronts in Semicola
are sandbox with no live connection yet, so a cap set there doesn't reach the platform.

## Buyer-side caps

Buyer-side caps are embedded in the advertiser and campaign; there's no separate cap resource.

| Where      | Call                                                                                                                                                |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Campaign   | `save_campaign` (create or update), `POST /api/v2/buyer/campaigns`, `PUT /api/v2/buyer/campaigns/{id}`, or `campaign.create` in the media-buy batch |
| Advertiser | `save_advertiser` (create or update) or `PUT /api/v2/buyer/advertisers/{id}`                                                                        |

```json theme={null}
{
  "frequencyCaps": [
    { "max_impressions": 3, "window": { "interval": 1, "unit": "days" } },
    { "max_impressions": 10, "window": { "interval": 7, "unit": "days" } }
  ]
}
```

| Field             | Notes                                                                                                    |
| ----------------- | -------------------------------------------------------------------------------------------------------- |
| `max_impressions` | A positive integer.                                                                                      |
| `window`          | `interval` (a positive integer) and `unit`: `minutes`, `hours`, `days` or `campaign` (the whole flight). |

The shape deliberately leaves out the seller-side fields (`per`, `suppress`): a buyer-side cap is only
about how many impressions one person gets.

* **Replace, don't merge.** Sending `frequencyCaps` replaces every stored cap on that advertiser or
  campaign; `[]` clears them; omitting the field leaves them as they are.
* **The old set is kept.** The replaced caps stay in the change record in the advertiser's or
  campaign's activity history.
* **Reads** return each cap with `id`, `targetLevel` (`ADVERTISER` or `CAMPAIGN`), `targetId`,
  `max_impressions`, `window`, `createdAt`, `updatedAt` and `archivedAt`.

Creative-level caps aren't available.

## Related

* [Targeting](/buy/targeting)
* [Update proposals](/buy/update-proposals)
