> ## 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.

# Pacing periods

> Shape a campaign flight into dated periods with weights or budgets; each staged buy splits into one package per period at launch.

A flat flight spends evenly from start to end. **Pacing periods** shape it instead: a heavy launch
week, a quiet middle, a holiday push. You describe the flight as dated periods, each with a relative
`weight` or an explicit `budget`, and at launch every staged media buy splits into one package per
period.

## The schedule

```json theme={null}
{
  "pacingPeriods": {
    "mode": "weight",
    "periods": [
      { "label": "Launch", "start": "2026-11-01", "end": "2026-11-07", "weight": 3 },
      { "label": "Sustain", "start": "2026-11-08", "end": "2026-11-21", "weight": 1 },
      { "label": "Holiday push", "start": "2026-11-22", "end": "2026-11-30", "weight": 2 }
    ]
  }
}
```

| Field          | Notes                                                                                |
| -------------- | ------------------------------------------------------------------------------------ |
| `mode`         | `weight` (relative intensity) or `budget` (explicit amounts). One mode per schedule. |
| `label`        | Up to 100 characters. Error messages name the period by its label.                   |
| `start`, `end` | Inclusive `YYYY-MM-DD` dates in UTC.                                                 |
| `weight`       | Weight mode: above 0 and at most 10.                                                 |
| `budget`       | Budget mode: a positive amount in the campaign currency.                             |

A schedule has 1–52 periods. Periods must sit inside the flight and can't overlap. Gaps are allowed:
leave a pause out of the periods rather than weighting it 0. In budget mode the period budgets can't
add up to more than the campaign budget.

### How budgets resolve

* **Weight mode** splits the budget by days × weight. In the example, Launch gets 7 × 3 = 21 shares,
  Sustain 14 × 1 = 14 and Holiday push 9 × 2 = 18.
* **Budget mode** uses the amounts as written.

Amounts are rounded to cents, and the last period absorbs the rounding. Reads return every period with
its resolved `budget`.

## Set it on a campaign

Pass `pacingPeriods` to `save_campaign` on create, or on an update with `expectedRevision`. An update
sends the **full** schedule; `null` clears it. The result's `pacingApplicationNote` says how many draft
buys will use the schedule ("… will use this schedule the next time the campaign launches; live media
buys are unaffected."). Over REST, pass the same field on `POST /api/v2/buyer/campaigns` and `PUT /api/v2/buyer/campaigns/{id}`.

## Per media buy

`update_media_buy` (or `PATCH /api/v2/buyer/media-buys/{id}`) also takes `pacingPeriods`, replacing
the campaign's shape for that one buy. It's stored on a draft or pending buy and used at launch.

## At launch

Each staged buy splits into **one package per period per product**. Each package covers its period's
dates and carries `pacingPeriod` (`index`, one-based, and `label`). In weight mode a product's budget
splits by days × weight; in budget mode each period's budget is shared across products in proportion
to their budgets.

A campaign launched mid-flight skips periods that have already ended, and their share goes to the
periods still ahead, so each product keeps its full budget.

## Changing a schedule after it starts

A period that has started is locked:

* it can't be changed or removed ("… has already started, so it can't be changed or removed."), and
  the schedule can't be cleared;
* the mode can't change once any period has started;
* every period can't be in the past: a schedule needs at least one period still ahead.

### Appending periods to a live campaign

On a live campaign, adding periods to the end of the schedule, with the existing ones unchanged and in
order, is pushed to its paced buys as new packages. The update returns a `pacingCascadeResult`:

| Field                                                                               | Notes                                                                                                                           |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `attempted`                                                                         | Whether the change was a strict append.                                                                                         |
| `notAppendedReason`                                                                 | Why not: `mode_changed`, `periods_removed`, `periods_modified_or_inserted`, `no_new_periods`, …                                 |
| `totalMediaBuys`, `updatedCount`, `skippedCount`, `failedCount`, `unsupportedCount` | Counts across the campaign's buys.                                                                                              |
| `results[]`                                                                         | Per buy: `mediaBuyId`, `agentName`, `outcome` (`updated`, `skipped`, `failed` or `unsupported`), `addedPackageCount`, `reason`. |

A buy whose seller doesn't support adding packages to a live buy is `unsupported`. In weight mode, an
appended period keeps the buy's current spend rate per weighted day.

## Related

* [Media buys and packages](/buy/media-buys-and-packages)
* [Campaigns](/buy/campaign)
