Overview
Pacing periods let you express a non-flat campaign flight as a sequence of dated periods, each with its own spend intensity. Use them when a campaign needs to spend more during a holiday window, less during a quiet stretch, or explicit dollar amounts in specific months. When a campaign with pacing periods executes, every selected product is automatically split into one package per period, with budget allocated according to the schedule. The campaign-level pacing schedule is the source of truth — you do not have to compute or set per-package budgets yourself.Weight mode
Express each period as a relative multiplier (
1.0 = normal, 2.5 = 150%
heavier). Scope3 distributes the campaign budget proportionally by
days × weight.Budget mode
Set explicit dollar amounts per period. Useful when finance hands you a
monthly spend plan you must hit exactly.
Concept
ApacingPeriods object has two fields:
| Field | Description |
|---|---|
mode | "weight" or "budget" |
periods[] | Ordered, non-overlapping list of dated periods |
| Field | Required when | Description |
|---|---|---|
label | Always | Human-readable name (e.g. "Memorial Day Heavy-Up") |
start | Always | Inclusive YYYY-MM-DD start date |
end | Always | Inclusive YYYY-MM-DD end date |
weight | mode = "weight" | Positive number ≤ 10 (relative intensity) |
budget | mode = "budget" | Positive dollar amount |
How to use
Weight mode
Weight mode is the most common: declare the shape, let Scope3 do the math.days × weight.
Budget mode
Budget mode pins explicit dollars to each period. The sum of period budgets must not exceedbudget.total on the campaign — anything left over stays as
unallocated headroom.
Updating a schedule
PUT /api/v2/buyer/campaigns/:id accepts the full pacingPeriods object. To
clear an existing schedule, send "pacingPeriods": null.
Live cascade for appended periods
When you append new periods to a campaign that already has paced live media buys, Scope3 automatically pushes the new period packages to those media buys via ADCP 3.0update_media_buy.new_packages. The result is returned on
the update response as pacingCascadeResult.
What gets cascaded
Only strict appends are cascaded — the newpacingPeriods.periods array
must start with the same items as the existing schedule. Insertions in the
middle, modifications of an existing period, removals, and mode changes
return pacingCascadeResult.attempted: false with a notAppendedReason and
require manually creating new media buys to apply.
Only media buys that were already paced (executed with pacingPeriods
set on the campaign at execution time) are eligible. Unpaced media buys are
reported with outcome: "skipped", reason: "unpaced" — to apply pacing,
create a new media buy.
Agent capability
Each seller agent must advertiseadd_packages in its valid_actions to
accept new packages mid-flight. Agents that don’t are reported with
outcome: "unsupported". Manual workaround: create new media buys for the
appended periods on those agents.
Response shape
pacingPeriods is not part of the update payload.
Per-media-buy pacing
Campaign-levelpacingPeriods shapes the whole campaign: every paced media
buy under it follows the same schedule. When you only want to shape one
media buy (heavy-up on a specific publisher with seasonal or event-driven
inventory, leave the rest flat), set pacingPeriods on the mediaBuys[]
entry of update_campaign instead.
The per-buy schedule replaces the campaign-level shape for that specific
buy. It uses the same mode + periods[] shape as the campaign-level
field.
Behavior by media buy state
| Media buy status | Behavior |
|---|---|
DRAFT / PENDING_APPROVAL | Schedule persisted on the buy. At execute time, packages are split by the per-buy schedule instead of the campaign-level schedule. |
ACTIVE / PAUSED, already paced, strict append | Appended period packages are sent to the seller via update_media_buy.new_packages. Requires the seller to advertise add_packages in valid_actions. |
ACTIVE / PAUSED, unpaced (live flat packages) | Rejected. Introducing period packages on top of live flat packages would double-spend. Create a new media buy with pacing periods instead. |
ACTIVE / PAUSED, paced, non-strict-append change | Rejected. Insertions, modifications, removals, and mode changes are not supported. Create a new media buy. |
"pacingPeriods": null to clear an existing per-buy schedule. The
column is cleared but any deployed period packages keep running. To stop
spend on a deployed window, cancel those packages directly.
Periods must lie inside the media buy’s flight (not the campaign’s),
must not overlap, and follow the same future-only update rules as the
campaign-level field. In response shapes, each per-buy period includes a
resolved budget computed from the sum of the buy’s product budgets.
When per-buy pacing is set on the same update_campaign call as
mediaBuys[].creative_ids (or relies on auto-sync from the campaign
manifest), the appended period packages inherit those creatives in the
same seller request. There is no separate creative-attach step.
Budget reductions
Lowering campaignbudget.total through update_campaign can also reduce
live package budgets. If the new media budget is below current live package
allocations, Scope3 proportionally reduces eligible live package budgets,
including period packages created from pacing periods. Explicit
mediaBuys[].packages[].budget values in the same request take precedence
over proportional scaling.
If Scope3 cannot apply a generated seller budget update, the campaign budget is
not lowered. This keeps campaign budget and live package allocations in sync.
Response shape
CampaignGET responses surface the schedule with resolved budgets for
every period. In weight mode, the resolved dollar amount is computed by
(days × weight) proportional split of budget.total; weights are preserved
alongside the resolved budget so you can audit the math.
unallocatedBudget is the dollar gap between mediaBudget.total and the sum
of active media buy budgets — it tells you how much room is left for new
media buys to fill the schedule.
Automatic package splitting
When a campaign with pacing periods executes:Resolve per-product budgets
Each selected product gets a budget — either set by the user or derived
from a discovery proposal.
Split per period
For every product, Scope3 creates one package per pacing period. In weight
mode, the per-period budget is
(days × weight) / sum(all weighted days) × product_budget. In budget mode,
each period’s fixed budget is split across products proportionally to
each product’s share of total product budgets.Round to last
Each period after the first uses banker’s rounding to two decimals; the
last period absorbs any rounding remainder so per-product totals match
exactly.
Validation
The campaign endpoints validatepacingPeriods against the campaign before
saving:
- Date order: every period’s
startmust be ≤end. - Inside flight: every period must lie within
flightDates.startDateandflightDates.endDate. - No overlaps: when sorted by
start, no period may begin on or before the previous period’send. - Budget sum (budget mode): the sum of period budgets must not exceed
budget.total. - Required fields per mode: weight periods need a
weight; budget periods need abudget. - Update safety: a period that has already started cannot be changed or removed; the mode cannot change while any period is in progress or past.
Best practices
- Start in weight mode. It’s easier to reason about “2× during the holiday week” than to recompute exact dollar splits when the budget moves.
- Use labels generously. Period labels show up in package metadata and in
reporting — names like
"July 4th heavy"beat"Period 2". - Keep periods aligned with media buy intent. A media buy can have any start/end inside the campaign flight, but matching its dates to a period boundary keeps reporting clean.
- Treat gaps as pauses. If you don’t want spend on July 16–18, leave that
range out of
periodsrather than settingweight: 0(which is rejected as non-positive). - Plan ahead — past periods are immutable. Once a period starts, its shape is locked. Front-load decisions you might need to revisit.
- Cap period count. The schema allows up to 52 periods; in practice, monthly or weekly granularity is plenty.
Limits
| Limit | Value |
|---|---|
| Periods per campaign | 1–52 |
weight range | (0, 10] |
budget per period | Positive dollar amount |
budget sum (budget mode) | ≤ budget.total |
| Max label length | 100 characters |
| Date format | YYYY-MM-DD (UTC, inclusive on both ends) |