Skip to main content

Overview

A frequency cap limits how many times a single user can be exposed to your advertising within a rolling time window. Caps are enforced by Scope3 across every publisher running a given advertiser, campaign, or creative — so a user who sees the cap-worth of impressions on one publisher will not see another impression on a different publisher in the same window. Scope3 stores caps as buyer-side configuration. They are distinct from any frequency_cap a publisher may apply on its own inventory and travel with the buyer’s request as part of the targeting overlay.
Buyer-side caps are about your exposure budget for a user — they are enforced even when you buy across publishers that have no view of each other’s delivery.
Cross-publisher enforcement on a given seller’s inventory requires that seller to be connected to real-time targeting — see Real-time targeting (TMP). On inventory that isn’t TMP-connected — Meta, TikTok, Snap, and other closed-platform integrations — this cap is never enforced there, not delayed and not partially applied. These sellers run their own ad server end-to-end, so Scope3 never gets a request-time moment to check the shared counter this cap relies on. Use each platform’s own native cap instead (targeting_overlay.frequency_cap on the package — a different field from the one on this page) for that inventory; see the ad platform compatibility guides for what each platform’s native cap supports.
Closed-platform publishers (TikTok, Meta, Snap, Pinterest, Reddit, Spotify, Amazon, Google, LinkedIn, and other platform-adapter integrations) can never be TMP-connected. These platforms run their own ad server and auction end-to-end — Scope3 talks to their campaign-management API, not a real-time bidstream, so there is no request-time moment for a frequencyCaps cap to act on. This isn’t a setup gap that can be fixed later; it’s a structural property of how these platforms are integrated.If you want a hard, per-publisher frequency cap on one of these platforms — e.g. TikTok’s own native ad-group frequency setting — set frequency_cap in the product-level targetingOverlay when you add the product (add_discovery_products / create_media_buys), not the campaign-level frequencyCaps field described on this page. The two are unrelated fields: frequencyCaps is Scope3’s own buyer-side exposure budget, and targetingOverlay.frequency_cap is the publisher-side setting that ships in the request the publisher actually receives.

Concept

Every cap config is a triple of: window.unit accepts these values: max_impressions is a positive integer.

Buyer-side vs publisher-side

The schema deliberately omits the publisher-side fields from the AdCP FrequencyCap shape (suppress, suppress_minutes, per). Those are suppression instructions that publishers act on inside their own ad server. Buyer-side caps care only about how many impressions you’ll spend on a single user — Scope3 picks the reach unit, not you.

Cascade across levels

Caps are applied at the level you set them on:
  • Advertiser-level caps apply to every campaign on the advertiser.
  • Campaign-level caps apply to every media buy and creative inside that campaign.
  • Creative-level caps apply to that single creative across every campaign it runs in.
When more than one cap matches a request, all caps must be satisfied for the impression to serve. The strictest cap wins.

How to use

Frequency caps are not a standalone REST resource — they are embedded into the parent advertiser, campaign, or creative endpoints. To change the caps on an entity, send the full array you want stored. PUT replaces the full array.

On campaign create

This sets two caps that both apply: a user sees at most 3 impressions per day and at most 10 impressions per week.

On campaign update

Sending frequencyCaps on update replaces all previously stored caps for that target. To clear caps entirely, send an empty array. Omit the field to leave existing caps untouched.

On the advertiser

This puts a brand-wide ceiling: no user sees more than 20 impressions for this advertiser in any 7-day window, regardless of which campaign serves them.

On a creative

Useful for “first-impression” creatives that should run at most once per user per day.

Response shape

GET responses on advertiser, campaign, or creative include the caps with their full server-assigned metadata:

Behavior

1

Replace, don't merge

replaceForTarget archives every existing cap for the (customer_id, target_level, target_id) triple, then inserts the new set in a single transaction. There is no partial update.
2

Archive, don't delete

Old caps stay in the database with archived_at set. They are excluded from API responses but preserved for audit and reporting.
3

All caps must pass

When multiple caps apply to a request, the impression serves only if every cap allows it. There is no priority or override.
4

Cap counts at the configured level

A CAMPAIGN cap counts impressions on that campaign only. An ADVERTISER cap counts impressions across every campaign on that advertiser.

Best practices

  • Layer caps from broad to narrow. Set an advertiser-level lifetime cap for brand exposure, then add tighter campaign-level caps for short flights.
  • Keep windows in human units. { interval: 1, unit: "days" } is easier to reason about than { interval: 1440, unit: "minutes" }.
  • Use campaign unit for lifetime caps. Don’t try to express “for the entire campaign” as a multi-week days window — use unit: "campaign".
  • Empty array clears caps. To remove all caps from a target, send "frequencyCaps": []. Omitting the field leaves them in place.
  • Don’t fight the publisher. Publisher-side suppression in target_overlay is a separate feature; if you need to enforce that a user doesn’t see your ad in a given session, work with the agent rather than layering more buyer-side caps.

Limits