---
title: "How ad selection works"
description: "The full path of an ad request — placement resolution, pre-targeting, limits, targeting, capping, priority tiers, campaign and banner picks, the RTB auction, and markup generation."
type: "concept"
audience: [network-manager, admin]
keywords: [ad selection, serving pipeline, priority, eCPM, weighted random, auction, default code, passback, pretargeting order, targeting order]
---

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.

# How ad selection works

Every impression is the result of a fixed elimination pipeline: the ad server starts from
all demand attached to one placement and filters it down, stage by stage, until exactly one
banner remains — or nothing does, and the placement's default code is served instead. Knowing
the order of the stages tells you *why* a given ad won and where a missing ad got dropped.

## The pipeline at a glance

```
ad request (tag / API)
  → GDPR gate (only when the network requires TCF consent)
  → resolve placement (placement, zone and site must all be active)
  → supply-side pre-targeting: Site → Zone → Placement
  → limits reached? drop exhausted advertisers / campaigns / banners
  → candidate pool = banners linked to the placement (direct links + Rules matches)
  → format filters (video type and site video requirements, native fit)
  → same-page and competitor exclusions
  → demand-side targeting: Advertiser → Campaign → Banner
  → capping: frequency / click / action / no-click / request caps
  → minimum margin
  → daisy-chaining filter, then the size filter on any-size ad units
  → selection: priority tier → campaign → banner
  → RTB auction, if a programmatic banner was picked
  → markup + macros + tracking → response (or the default code)
```

Ad serving works from a copy of your setup that each ad server keeps and refreshes. Some saves
(a limit, a price, a banner) reach the ad servers in about a second; everything else — a
frequency cap, a price adjustment rule — and any update that was missed waits
for the next full refresh, every 10 minutes by default. See
[How current the numbers are](https://epom.com/ad-server/docs/guides/delivery.md#how-current-the-numbers-are).

For the same pipeline numbered step by step, with what stops delivery and what only changes the
price, see [Delivery controls](https://epom.com/ad-server/docs/guides/delivery.md#order-of-application).

## 1. Placement resolution and the candidate pool

The ad tag (or API call) carries a placement ID. The server looks the placement up and
gathers its demand: every banner linked to it — directly, through auto-linking, or matched
by selection rules ([how linking works](https://epom.com/ad-server/docs/concepts/hierarchy-and-linking.md)). A banner enters the
pool only while it, its campaign, and its advertiser are all active; the placement itself
counts only while it, its zone, and its site are all active
(see [Serving state](https://epom.com/ad-server/docs/concepts/serving-state.md)). A direct link counts only while the linking switch
is on for **both** the banner and the placement, and a Rules match needs Rules on at the
demand side (banner, campaign or advertiser) **and** at the supply side (placement, zone or
site).

If the pool is empty, the request ends with no banner and the placement's default code
is served (see [Step 9](#9-markup-and-macros) for which entry points return it).

## 2. Supply-side pre-targeting: Site → Zone → Placement

Pre-targeting rules on the publisher side run first, broadest scope first, and fail fast:
the site's rules, then the zone's, then the placement's. If any level rejects the request
(wrong country, blocked domain, off-schedule, and so on), nothing serves — no demand is
even considered. Blocked-advertiser / blocked-campaign / blocked-banner lists set at these
levels are collected here and applied as exclusions in the later stages.

## 3. Limits and format filters

Advertisers, campaigns and banners that have exhausted an impression, click, spend, or action
[limit](https://epom.com/ad-server/docs/guides/delivery/limits.md) are removed; a site or placement limit that is reached
removes every campaign from the request. Then format compatibility trims the pool:

- a video request keeps only banners matching the requested in-stream type;
- a site that declares video creative requirements drops an uploaded video creative whose file
  does not meet them (external video, VAST tags and DSP bids are never stopped here);
- a native placement drops banners that lack the fields its template requires, and a
  non-native placement drops native banners unless it has a native adapter template.

## 4. Demand-side targeting: Advertiser → Campaign → Banner

Campaigns already shown on this page (unless the campaign is set to show several of its banners
on one page), and the competitors of a campaign that was shown, are excluded first. Targeting rules then run per candidate, top of the demand tree first:
advertiser rules, then campaign, then banner. A banner survives only if all three levels
accept the request. An entity with targeting switched off accepts everything — empty targeting
means "serve to all", not "serve to none".

## 5. Capping and minimum margin

[Frequency, click, action, no-click and request caps](https://epom.com/ad-server/docs/guides/delivery/frequency-capping.md)
are evaluated against the requesting viewer. Each cap carries its own **subject** — cookie,
server-side, or mixed — and a server-side count identifies the viewer by an explicit user ID,
then the cookie, then the device advertising ID (`ifa`) on apps and connected TV, and last a
fingerprint of IP, user agent and language. Banners and campaigns whose caps are exhausted for
*this* viewer drop out. If capping empties the pool entirely, the request ends with no banner.

Minimum margin runs after capping, so its price arithmetic is spent only on banners still in the
running: a direct banner whose gross does not clear the publisher payout by the required amount
is dropped. It skips CPC and CPA demand.

## 6. Daisy chaining and the size filter

A placement with [daisy chaining](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md) configured walks
its chain in order and serves the first entry that has not already been excluded, as long as
fewer than **Lookup depth** entries were skipped before it; past that, ordinary selection takes
over. With **View 1-N per entity** on, a filter before the walk also drops entries this viewer
has already seen enough times and pushes house ads out of the chain while anything else is left.
**Apply priorities** decides how the chain meets the priority tiers of step 7: with it on, the
chain runs inside each delivery tier enabled for it; with it off, the chain replaces the tier
order.

On a placement whose ad unit is **Any size**, a banner must also match the width and height the
page reported in the request; a banner of any other size is dropped (an unreported size counts as
0×0).

## 7. Priority tiers, then campaign, then banner

Selection proper runs in three nested picks:

1. **Priority tier.** Campaigns are grouped by delivery priority and the groups are tried
   in fixed order: **Premium → Standard High → Standard Medium → Standard Low →
   Non-guaranteed → House ads**. The first tier that can produce a banner wins; lower
   tiers are never consulted until the ones above them come up empty. House ads therefore
   serve only when everything above them did.
2. **Campaign within the tier.** How the campaign is picked depends on the network's
   campaign [optimization mode](https://epom.com/ad-server/docs/guides/delivery/optimization.md):
   - **Manual weighting** — weighted random by each campaign's weight (traffic share). House
     ads carry no weight; when every weight in the tier is zero the pick is uniform.
   - **eCPM optimization** — the campaign with the highest known or estimated eCPM for the
     request's country wins; ties are broken at random. Campaign weight is not read.
   - **eCPM optimization with burn-up period** — campaigns that are not eCPM-optimized compete on
     weight first; if the draw lands on an optimized campaign, the one with the best eCPM
     measured over its burn-up window wins, and a configurable share of traffic goes to a random
     non-winner so a losing campaign keeps getting data.
3. **Banner within the campaign.** A campaign with conversion-rate (I2C) optimization hands the
   banner pick to that optimizer first, then CTR optimization if it has that instead; otherwise,
   or when the optimizer returns no banner, a weighted random pick over the campaign's
   surviving banners uses each banner's weight.

### Why a lower-eCPM ad can win

Priority always beats price. eCPM ranking runs *inside* one priority tier only — a Premium
campaign with a modest eCPM will always outserve a Non-guaranteed campaign with a great
one. If you want pure eCPM competition, put the competing campaigns in the same priority
tier. This is the single most common cause of "the optimizer is ignoring my best payer"
reports.

### Why campaigns with equal weights get unequal impressions

A weight is a share **among the campaigns still eligible for that one request**, not a share of
the placement's traffic. Equal weights produce equal delivery only when both campaigns qualify for
the same requests. The usual reasons one gets more:

- **The other campaign drops out of many requests** — its targeting (country, device, time),
  frequency cap, limit or even pacing removes it from the pool, and every request it misses goes
  to the remaining campaigns.
- **They are not in the same priority tier.** A higher tier takes everything it can serve.
- **The network does not run Manual mode.** In the eCPM modes, campaigns taking part in eCPM
  optimization are ranked by eCPM, so their weights stop deciding between them.
- **A daisy chain on the placement** puts one advertiser first — the chain, not the weights,
  decides the order (see [daisy chaining](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md)).
- **The page carries two tags.** If a site has codes from two placements (or two networks) in
  one slot, the slot shows whatever loads, not your rotation.

To split traffic unevenly on purpose, change the **weight** on the campaigns (or on banners inside
one campaign): weights 9 and 1 give roughly 90% and 10% of the requests both can serve. A campaign
with weight **0** wins only when no competitor in its tier has a weight above 0. House ads
campaigns ignore their weights and rotate evenly.

### Where randomness enters

- Weighted random campaign pick in Manual mode, and random tie-breaks in eCPM mode.
- Weighted random banner pick inside the selected campaign — weights bias, they don't
  guarantee, so short-term shares wobble around the configured split.
- Placements with **Refresh after (sec)** set re-request on a timer, re-running the whole
  pipeline for each refresh.

## 8. The RTB auction

If the pick lands on a programmatic (OpenRTB DSP) banner, that banner is a placeholder for
an auction, not a creative. Bid requests go out to the active DSP partners whose banners are in
the tier; each response is then filtered — bids that do not match a private-auction deal are
removed, and creatives blocked by [creative scanning](https://epom.com/ad-server/docs/use-cases/creative-policy-protection.md) are removed —
and the highest remaining bid wins. What the winner pays is first price unless a second-price
setting applies, and a bid below the bid floor (or the deal floor) never wins; see
[first vs second price](https://epom.com/ad-server/docs/concepts/auction-first-vs-second-price.md). If no acceptable bid arrives, the
programmatic banners are excluded and the pick re-runs over the remaining direct demand in the
same tier, then the tiers below — so a failed auction still fills with your own ads when
possible.

## 9. Markup and macros

The winning banner's markup is generated for its type (image, HTML, native, VAST…), all
[macros](https://epom.com/ad-server/docs/reference/macros.md) are substituted with request values, and impression
tracking plus the click redirect are attached. If *nothing* survived the pipeline, the
placement's **default code** is returned — that is the moment an external fallback ad
server gets its chance. Not every entry point returns it: Standard, Non-standard, Mobile,
Video, Email, Click-through and Native placements called through the universal JS, AJAX, iframe
or standard ad API do, while a VAST request answers an empty VAST document with an `<Error>`
node, a native JSON request answers "no ads", and an Application placement answers an empty
response. See
[When the default code is returned](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md#when-the-default-code-is-returned).

> **Note:** an ad *response* is not yet an impression. Impressions are counted when the
> tracking beacon fires in the browser, which is why "deliveries" and "impressions" can
> differ in reports.

> **Corner case:** every filtering stage can silently empty the pool. A placement that
> "stopped serving" after you tightened a site-level pre-targeting rule was not unlinked —
> step 2 now rejects the traffic before any banner is looked at. Work down the pipeline
> in order when diagnosing; that is exactly what
> [Ad not serving](https://epom.com/ad-server/docs/troubleshooting/ad-not-serving.md) does.

## Related

- [Hierarchy and linking](https://epom.com/ad-server/docs/concepts/hierarchy-and-linking.md)
- [Serving state](https://epom.com/ad-server/docs/concepts/serving-state.md)
- [Add targeting rules](https://epom.com/ad-server/docs/guides/targeting/add-targeting.md)
- [Pre-targeting and blocking](https://epom.com/ad-server/docs/guides/targeting/pretargeting-blocking.md)
- [Frequency capping](https://epom.com/ad-server/docs/guides/delivery/frequency-capping.md)
- [Daisy chaining and passback](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md)
- [First vs second price](https://epom.com/ad-server/docs/concepts/auction-first-vs-second-price.md)
- [Ad not serving — diagnosis](https://epom.com/ad-server/docs/troubleshooting/ad-not-serving.md)
- [Exclude competitive campaigns](https://epom.com/ad-server/docs/guides/advertising/campaign-competitors.md)