---
title: "Optimization reference"
description: "The five stages of the ad-rotation pick with worked examples, the CTR and I2C optimizers' internals, every control of the Banners probability card and what NA means, the REST calls, the corner cases and the per-role notes — the companion to the Optimize ad rotation guide."
type: "reference"
audience: [network-manager]
keywords: [optimization reference, rotation algorithm, priority tier, weighted random, campaign lottery, banner lottery, UCB1, Thompson sampling, CTR bandit, I2C bandit, banners probability, NA, banner-probability API, weight on placement API, daisy chaining and rotation, video pod rotation]
ui_path: "Account → Publishers → site → zone → placement → Banners probability"
---

Fetch the complete documentation index at: https://epom.com/ad-server/docs/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Optimization reference

The companion to [Optimize ad rotation](https://epom.com/ad-server/docs/guides/delivery/optimization.md). The guide has the steps, the
caveats and the fix-it checklist; this page holds the algorithm exactly as ad serving runs it,
the optimizers' internals, every control of the Banners probability card, the REST calls and
the corner cases. The network-wide algorithm and its fields are on
[Configure optimization settings](https://epom.com/ad-server/docs/guides/admin/optimization-settings.md).

## How the winner is chosen

Every ad request runs these stages in order.

### 1. Build the candidate set

The placement's linked banners are narrowed by pretargeting and targeting, capping, limits,
blocked-entity rules and GDPR mediation restrictions. Anything excluded here never reaches
rotation.

### 2. Priority tier — the field is narrowed to exactly one tier

Campaigns carry a **delivery priority**. Ad serving walks the six tiers in this fixed order
and, for each one, considers *only* the campaigns of that exact priority:

**Premium → Standard High → Standard Medium → Standard Low → Non-guaranteed → House ads**

The first tier that yields a banner wins and the walk stops. A lower tier is consulted only
when everything above it produced nothing at all. So House ads serve only what nobody else
could fill, and a Premium campaign with weight 1 outserves a Non-guaranteed campaign with
weight 10 000 — every time, not usually.

> **Note:** the tier order is the order the levels are listed in, not their numeric ids.
> The ids run 1, 5, 6, 2, 3, 4, so sorting a report by the raw priority
> id will not give you serving order. Use the names.

### 3. Campaign lottery inside the tier

With Manual Weighting (the default network algorithm) the surviving campaigns of that tier
enter a **weighted random draw**: a campaign with weight 100 is twice as likely to be drawn as
one with weight 50. A campaign's weight on this placement, when it has one, stands in for its
campaign weight (see [Weights on one placement](https://epom.com/ad-server/docs/guides/delivery/optimization.md#weights-on-one-placement)). Two exceptions:

- A campaign whose weight is **0** cannot win the draw. Neither can a campaign on **House ads**
  priority: ad serving substitutes 0 for its stored weight whatever the field says.
- If that leaves the whole tier at total weight 0 — which is exactly what the House ads tier
  always looks like — the pick falls back to a **uniform** random choice. House-ads campaigns
  therefore rotate evenly and their weight value is ignored entirely.

The two eCPM algorithms replace this draw with a price ranking — see
[Configure optimization settings](https://epom.com/ad-server/docs/guides/admin/optimization-settings.md).

### 4. Banner lottery inside the chosen campaign

The chosen campaign's surviving banners enter a second weighted random draw on **banner
weight** — or, where a banner has its own weight on this placement, on that weight (see
[Weights on one placement](https://epom.com/ad-server/docs/guides/delivery/optimization.md#weights-on-one-placement)). If the campaign has I2C or CTR optimization ticked, the corresponding bandit is
asked first and the plain draw only runs when the bandit declines to answer — see
[The CTR and I2C optimizers](#the-ctr-and-i2c-optimizers).

### 5. If the drawn banner is an OpenRTB creative, the auction runs

An OpenRTB DSP banner sits in the lottery as an ordinary weighted candidate. Only once the
lottery draws it do the bid requests go out; the responses are then settled in one auction across
every OpenRTB banner eligible for this placement — first price by default, see
[First-price vs second-price auctions](https://epom.com/ad-server/docs/concepts/auction-first-vs-second-price.md). If no bid comes
back, all OpenRTB banners are excluded and stages 3–4 are re-run for the remaining direct banners.

**Programmatic demand is therefore not a separate rotation, and bid prices never compete with
direct campaigns.** A DSP only gets to bid at all when its stand-in banner wins the priority
tier and both weighted draws; a $20 CPM bid behind a Non-guaranteed campaign still loses to a
Premium house-direct banner. To let programmatic compete on price, put the OpenRTB campaign in
the same priority tier as the direct campaigns and give it the weight you want it to have.

> **Note:** rotation is a **lottery run per request, not a rota.** Weight biases the draw, it
> does not schedule it. Over a few hundred impressions the realised split wobbles around the
> configured one; over a few dozen it can look nothing like it.

### Worked examples

The worked examples — one campaign with three banners at 50 / 40 / 25 (the 43.4783 % /
34.7826 % / 21.7391 % split the Banners probability card reports), a Premium campaign taking
100 % beside a Standard Low one, equal weights giving unequal impressions, and a $20 CPM bid
that never wins — are on [How ad selection works — Common situations](https://epom.com/ad-server/docs/concepts/how-ad-selection-works.md#common-situations).
The lesson they share: **priority partitions; weight divides within a partition.** They never
combine into one score.

### Worked example — weights on one placement

Two campaigns, *Summer* and *Fall*, share every placement 1 : 1, each with two creatives —
*Summer*'s at equal weight, *Fall*'s at 60 / 40. On one publisher's placement *Summer* gets a
campaign weight of 3 and *Fall* 1. On that placement only, three requests in four go to *Summer*,
and the Banners probability card lists *Summer*'s creatives at 37.5 % each and *Fall*'s at 15 %
and 10 %; everywhere else the split stays 25 / 25 / 30 / 20. The same goes for a banner weight
on a placement: a campaign's 60 / 40 split of two creatives can be 50 / 50 on one publisher and
20 / 80 on another.

## The CTR and I2C optimizers

Both are **multi-armed bandits** that live in the ad server's memory, one model per campaign.
Neither reads a report table, and neither adjusts the banner's stored Weight.

| | CTR optimization | I2C optimization for actions |
|---|---|---|
| Optimises for | clicks per impression | conversions per impression |
| Method | UCB1 — score is `clicks ÷ impressions + √(2 · ln(campaign impressions) ÷ banner impressions)` | Thompson sampling — one draw from a Beta posterior per banner |
| Model rebuilt every | 5 seconds | 60 seconds |
| First rebuild after ad-server start | 1 minute | 1 minute |
| Prior for a banner with no conversions | — | the network's own impression-to-conversion rate over the last 14 days |
| Exploration share | `100 − CTR optimized traffic share` | `100 − I2C optimized traffic share` |
| Survives a restart | yes — counters are written to disk every 15 minutes and reloaded at startup | yes, same |

On each request for an optimized campaign the service does three things in order:

1. **Roll for exploration.** With probability `100 − <the optimizer's traffic share>` the
   request skips the model entirely and picks by plain banner weight. At a traffic share of
   **100** there is no exploration at all.
2. **Serve anything untried.** Every banner that has no impressions yet in the model is
   gathered and one is picked from them by weight, so a newly added creative is guaranteed
   traffic before the model can dismiss it.
3. **Otherwise take the top scorer.** The banner with the highest score that is still active,
   not deleted and not excluded wins. This is a **single winner, not a blend** — within one
   model snapshot an optimized campaign serves one creative until the scores move.

**What it needs before it does anything, and how long that takes.** Counters only start
accruing once the checkbox is on; a campaign with the box off collects nothing. They are
fed from the ad logs, so a live campaign starts contributing within seconds of the
checkbox being saved. The model itself is rebuilt on the cadence in the table above. In
practice the first few thousand impressions are spent giving every banner its opening
impression (step 2) and letting the exploration term shrink; the ranking only becomes stable
once each banner has enough impressions for `√(2 · ln N ÷ n)` to be small next to its measured
rate. There is no configurable minimum and no "learning finished" indicator.

Tick at most one: the campaign form **unticks the other** as soon as you tick one. If both
flags end up set anyway — through the API, say — ad serving asks I2C first and only falls
through to CTR when I2C returns nothing. Historical statistics recorded before you ticked the
box do not count.

> **Warning:** unticking CTR or I2C optimization **deletes** that campaign's learned model —
> impressions, clicks/conversions and scores. Ad serving clears it on the very next request
> the campaign takes. Toggling the box off and on again restarts learning from zero.

> **Note:** there is no per-creative "optimization coefficient" to read — compare CTR per banner
> in [Analytics](https://epom.com/ad-server/docs/guides/analytics.md) instead.

> **Note:** an optimizer never overrides a delivery control. A capped or limit-exhausted banner
> cannot be picked no matter how well it is scoring, and the probability card reports it as
> **NA** rather than 0 %.

## The Banners probability card

On a placement (**Account → Publishers → site → zone → placement**), the **Banners
probability** card lists every banner the placement can serve with its computed share. It is a
**read-only diagnostic**: it computes, it never changes weights or priorities. The stock Network
Manager has it; other roles need *Banners Probability Visibility* and view access to campaigns,
so the stock Publisher does not.

| Control | What it does |
|---|---|
| **Search** | Filters loaded rows by advertiser, campaign or banner name. At Campaign grouping the banner name is no longer searched; at Advertiser grouping only the advertiser name is. |
| **Grouping** | Banner / Campaign / Advertiser. Summing is done server-side: the grouped row sums its children's probabilities and the columns below the grouping level read `-`. |
| **Country** | Simulates a request from that country. *Ignore Country* leaves country targeting out of the calculation. |
| **Browser** | Simulates that browser. *Ignore Browser* skips browser targeting. |
| **OS** | Simulates that operating system. *Ignore OS* skips OS targeting. |
| **Priority** | *As served (highest priority only)* — the default — keeps the highest tier with a campaign still competing, as ad serving does, and lists the campaigns of lower tiers as NA. A named tier restricts the calculation to that tier. *Ignore Priority* skips tiering and spreads the shares across every tier at once. |
| **Mobile Carrier** | Mobile-site and application placements only. *Ignore Carrier* skips carrier targeting. |
| **Probability** | Shown to four decimal places. **NA** means the row cannot be given a share at all — see below. |
| **Campaign weight** / **Banner weight** | The same two per-placement columns as the Linked banners card, editable the same way, next to the share they produce. |
| **Refresh** | Re-runs the calculation against the current ad-server cache. |
| **Block** (crossed-out circle) | Writes an EXCLUDE pretargeting rule for that banner / campaign / advertiser on this placement, this site, or all sites. Needs edit rights. |

Each of Country, Browser, OS and Mobile Carrier starts at its own *Ignore …* value.

**How the number is computed.** For each surviving banner:

```
probability = campaign weight ÷ Σ campaign weights
            × banner weight   ÷ Σ banner weights of that campaign
```

where a campaign's and a banner's weights on this placement, when they have them, stand in for
their campaign and banner weights, and House-ads and zero-weight campaigns contribute 0 to both
the numerator and the sum. When no competing campaign has any weight (every one is on House ads,
or every weight is 0), ad serving picks one of them at random, so each competing campaign gets
an equal share and the rows still add up to 100 %. The card uses the network's **Campaign
optimization** algorithm from **Admin → Ad serving**, so under *eCPM with Burn-up* it shows the
burn-up split.

**On a placement with daisy chaining** the weights do not decide: the first creative (or, for a
campaign chain, the first campaign) in the chain that matches the simulated visitor takes 100 %,
split by banner weight inside that campaign, and the rest of the chain is listed as **NA**. The
card shows a fresh visitor's first request — *views per visitor* and rotation pass later requests
of the same visitor down the chain, which a simulation cannot see. A chain set to apply only to
some delivery priorities changes the card only in those priorities.

**What NA means.** The card shows `NA` whenever the stored value is negative, which happens in
these cases:

| Cause | Why |
|---|---|
| The campaign's weight is 0 | It can never win the campaign draw. |
| The campaign is on **House ads** priority | Its weight is forced to 0 in the same calculation. |
| The banner's or campaign's **limit is reached** | Added deliberately as an NA row, so an exhausted creative is visible rather than absent. |
| The advertiser or campaign is **blocked** on this placement or its site | It never serves here, so it is listed as NA rather than dropped. |
| A higher **priority tier** has a competing campaign | Shown in the default *As served* view: the lower tier only serves requests the top tier cannot take. |

**What the card does not model.**

- **Nine kinds of targeting only.** Blocked advertisers, campaigns, banners, placements and
  sites, plus Country, Browser, Operating System and Mobile Carrier. Day of week, time range,
  device, language, connection type, custom parameters, incognito and every other rule are
  **not** applied, so a banner that real targeting will reject can still show a healthy share.
- **A blocked banner has no row.** An advertiser or campaign blocked on the placement or its
  site is listed as **NA**. A banner blocked there is different: the ad server drops its link to
  the placement, so the card has no row for it. If the card is missing creatives you expect,
  check [pretargeting and blocking](https://epom.com/ad-server/docs/guides/targeting/pretargeting-blocking.md) on the placement and
  on its site before you suspect the weights.
- **The CTR / I2C bandits.** The card always reports the weight split. On a campaign with CTR or
  I2C optimization on, the real split is whatever the bandit decides — usually far more
  concentrated. The card is a forecast of the *weighted* path only.
- **eCPM Optimization (beta).** Under it campaign weight is not read by ad serving at all, yet
  the card still divides by it. Under *eCPM with Burn-up* the card follows the network algorithm
  and shows the burn-up split. Read the card as authoritative under *Manual Weighting* and *eCPM
  with Burn-up*.
- ***Ignore Priority* is not a forecast.** With the Priority filter on *Ignore Priority*, the
  calculation skips tiering and spreads probability across **every** tier at once, so a Premium
  and a House-ads campaign appear side by side with a share each. Ad serving gives the top tier
  with a competing campaign everything, which is what the default *As served* view shows. Use
  *Ignore Priority* only to compare weights across tiers.
- **Masked advertisers.** If you can see a placement but not a given advertiser, that
  advertiser's rows appear with the names masked; rows of an advertiser flagged private are
  dropped from the card altogether.
- **A video pod.** The card shows one pick; a pod's per-spot separation (below) is not modelled.
- **A minimum margin** on the placement or site *is* applied: a banner that cannot clear it
  disappears from the card exactly as it disappears from the auction — see
  [Set a minimum margin](https://epom.com/ad-server/docs/guides/delivery/pricing-bid-floor-and-margin.md#set-a-minimum-margin).

## Over the API

**Weights on one placement.** `PUT` / `DELETE
/rest-api/campaign/{campaignId}/placements/{placementId}/weight.do` and
`/rest-api/banners/{bannerId}/placements/{placementId}/weight.do` set and remove a campaign's
and a banner's weight on a placement; `GET .../placements/weights.do` on either lists them; the
linked-placement and linked-banner lists carry them as `campaignWeightOnPlacement` and
`weightOnPlacement`. Ad servers pick a change up with their next cache update for the placement
or banner. An AI assistant does the same through the `placement_weight_set` tool over
[MCP](https://epom.com/ad-server/docs/api/mcp.md).

**Banners probability.** `GET /rest-api/placements/{placementId}/banner-probability` is the
card's computation. Describe the simulated visitor with `countryCode`, `browser`,
`operatingSystem` (any operating system — `WINDOWS`, `ANDROID`, …) and `mobileCarrier`, and list
the dimensions to leave out of the match in `ignore` (`COUNTRY`, `BROWSER`, `OPERATING_SYSTEM`,
`MOBILE_CARRIER`, comma-separated; any other value is a `400` that lists these four) — the
equivalent of the card's *Ignore* choice. A dimension you neither send nor ignore is simulated as
unknown, which still fails a rule that targets it. `priority`, `group` (`BANNER`, `CAMPAIGN`,
`ADVERTISER`), `query`, `sort`, `dir`, `start` and `limit` match the card's controls. The
response is `{success, total, data}` with the rows the card shows — under Manual Weighting. The
API hands the network's optimization algorithm to the calculation and the card does not, so on
a network running **eCPM with Burn-up** the two can disagree. A banner that matches the
simulated visitor but gets no share of the rotation — zero weight, or *House ads* priority
beside other campaigns — comes back with probability `-1`, the card's **NA**. A linked banner
that does not match the visitor, is paused, or belongs to a paused or ended campaign is not part
of the simulation and is not listed at all; list the placement's banners
(`GET /rest-api/placements/{placementId}/banners.do`) to see those. It needs the *Banners
Probability Visibility* permission and view access to the placement's site (`403` otherwise),
and a missing or deleted placement is a `404`.

## Corner cases

### Daisy chaining and the chain priority

Enabling daisy chaining on a placement switches the rotation algorithm off. What survives
depends on the chain's type and on *Group by delivery priority*:

| Chain | What decides the winner |
|---|---|
| **Banner**, grouping off | The chain alone. The first link that is eligible and within the lookup depth wins, every request. Priority tiers, campaign weight, banner weight and both bandits are all bypassed. |
| **Banner**, grouping on | Priority tiers still run; inside a tier the chain covers, the chain orders the banners. Tiers it does not cover fall back to the weighted draw. |
| **Campaign**, grouping off | The tier loop is skipped and the chain picks the campaign; the banner inside that campaign is still drawn by weight (or by its bandit). |

The Banners probability card does not model any of this, so it is hidden on a placement with
daisy chaining switched on. A role without the *Daisy Chaining* permission still sees it there,
and its weight-based shares do not apply. See
[Daisy chaining & passbacks](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md).

A daisy-chain row's *Priority* is its 1-based position in the chain, not a delivery priority. A
chain row of priority 1 has nothing to do with a campaign set to *Premium*.

### House ads zeroes the weight

Choosing *House ads* sets the campaign's Weight field to 0 and disables it, and disables the
CTR and eCPM checkboxes with it: a house campaign fills leftovers and shares that tier evenly,
so weighting it is meaningless. Switching back to any other priority restores the saved weight,
or 1 if the saved value was 0. *House ads* stay at weight 0 whatever is set on a placement.

### Weights on a placement belong to the link

The banner's weight on a placement belongs to the link. Unlinking the placement drops it and
relinking starts from the banner weight. Restoring a deleted banner or placement brings each
weight back with its link, unless a weight was set on the link again in the meantime. A
campaign's weight on a placement stays while none of its banners is linked there and applies
again when one is.

The eCPM algorithms do not read campaign weights at all. The cards say so: a campaign weight the
draw does not read is greyed out with an ⓘ that names the reason — the network's *eCPM
Optimization* algorithm, *House ads* priority, or, under *eCPM with Burn-up*, the campaign's own
**eCPM Optimization** checkbox. You can still edit it; it starts to count once the reason goes
away. An optimized campaign uses the banner weight on the placement only for its exploratory
slice, like the banner weight.

Only linked rows carry weights: a banner or placement you have just ticked gets them once the
link is saved. On a network whose link tree is too large to show, the card opens on the grid,
which carries the same columns. In the grid every advertiser, campaign, banner, site, zone and
placement name opens that entity's page.

### "Show only this campaign banners on page"

With this ticked, once a campaign has won one slot on a page every further slot on the same
page is filled from the same campaign, whatever the weights say. If a campaign alias is set, the
restriction applies across every campaign sharing that alias.

### A video pod is filled one spot at a time

A placement that returns more than one ad per request runs the selection above once per spot,
and each winner is compared with the spots already in the break before it joins: the same
ISCI / Ad-ID spot never plays twice, and with the placement's *Competitive separation in a pod*
switched on a second spot from the same advertiser domain — or domain and category — is passed
over and the next winner is tried. A pod can therefore come back shorter than the placement
asks for; on a VMAP request a campaign also supplies at most one spot per break. The Banners
probability card does not model any of it. See
[Keeping competitors apart in a pod](https://epom.com/ad-server/docs/guides/inventory/placement-video.md#keeping-competitors-apart-in-a-pod).

### The eCPM Optimization Details window

The **eCPM Optimization Details** window — reachable from the **Actions** menu on a campaign or
a placement — lists the learned per-country / per-placement eCPM records and lets you delete a
selection of them. It appears only when the network algorithm is *eCPM with Burn-up* **and** the
matching permission is held. Clearing the records after a landing-page overhaul stops stale
learning from holding a campaign back.

### Prerequisites rotation cannot do without

More than one banner must actually be eligible for the same request — with a single eligible
banner there is nothing to rotate — and the banners must be **linked to the placement**:
rotation only ever considers the placement's linked set. For the CTR or I2C optimizers the
campaign must be collecting impressions and clicks (or conversions) *while the optimizer is
switched on*.

## Per-role notes

- **Network manager** — Priority, campaign and banner weight, the CTR, I2C and eCPM checkboxes
  and the Banners probability card on the stock role. The eCPM checkbox appears only while the
  network algorithm is eCPM with Burn-up, and the algorithm itself needs an administrator
  holding *Optimization Settings*.
- **Advertiser** — on the stock role, campaign and banner **Weight** are shown read-only, and the
  CTR and I2C checkboxes and Priority are not shown: the network manager sets them. The same
  goes for the campaign's and the banner's weights on a placement. See
  [What your network controls](https://epom.com/ad-server/docs/roles/advertiser.md#what-your-network-controls).
- **Publisher** — no Banners probability card on the stock role; the card also needs view access
  to campaigns, which the stock Publisher does not have.
- **Can View share** — Priority, weights and optimizer checkboxes are read-only; blocking needs
  edit rights on the site.
- **Weights on placements** — *Manage Weights on Placements*, off for every role until an
  administrator grants it; read-only shows the columns without letting you change them. On top of
  it, seeing a campaign weight needs *Manage Campaign Weight* and a banner weight *Manage Banner
  Weight* (enabled or read-only); changing one also needs both permissions enabled and edit
  rights on the advertiser. A row whose advertiser you may not view shows only its masked name:
  no weights, and the name does not open anything.

Exact grants: see [User roles](https://epom.com/ad-server/docs/guides/admin/users-and-roles.md).

## Related

- [Optimize ad rotation](https://epom.com/ad-server/docs/guides/delivery/optimization.md) — the guide
- [Configure optimization settings](https://epom.com/ad-server/docs/guides/admin/optimization-settings.md) — the network algorithm
- [How ad selection works](https://epom.com/ad-server/docs/concepts/how-ad-selection-works.md) — the full request pipeline
- [First-price vs second-price auctions](https://epom.com/ad-server/docs/concepts/auction-first-vs-second-price.md)
- [Daisy chaining & passbacks](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md)
- [Pricing](https://epom.com/ad-server/docs/guides/delivery/pricing.md) — where a campaign's own eCPM estimates and burn-up windows live
- [Limits](https://epom.com/ad-server/docs/guides/delivery/limits.md) and [Frequency capping](https://epom.com/ad-server/docs/guides/delivery/frequency-capping.md)
- [Pretargeting & blocking](https://epom.com/ad-server/docs/guides/targeting/pretargeting-blocking.md)