---
title: "Price rules, bid floors and pricing via API"
description: "Price adjustment rules on advertisers, campaigns and banners, country bid floors on placements, sites and banners, self-serve CPM floor rates, and the corner cases of entity pricing and CSV pricing uploads."
type: "reference"
audience: [network-manager, admin]
keywords: [api, price adjustment, bid floor, floor cpm, pricing, country pricing, csv, revenue share, minimum margin, curl]
---

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.

# Price rules, bid floors and pricing via API

Price adjustment rules, country bid floors and CPM floor rates over REST, plus the corner cases of
entity pricing — country and action prices, CSV uploads, site and placement payment models.
Examples append [`$AUTH`](https://epom.com/ad-server/docs/api.md#the-auth-shorthand), the hash-authentication parameters
(an `X-Api-Key` header works the same) — see the [API overview](https://epom.com/ad-server/docs/api.md) for authentication.

## Who this is for

Developers who script delivery control for a network — network managers, and
publisher or advertiser users whose role holds the permissions named below. The calls behave the
same for every role; what differs is which entities and dimensions your role may change.

## Price adjustment rules

Rules that multiply or override the gross price when a condition matches, on three levels —
replace `{level}` with `banner`, `campaign` or `advertiser`. Ten operations per level:

```
GET    /rest-api/price-adjustment-rules/{level}/{entityId}/list.do
POST   /rest-api/price-adjustment-rules/{level}/{entityId}/create.do
GET    /rest-api/price-adjustment-rules/{level}/rule/{ruleId}.do
POST   /rest-api/price-adjustment-rules/{level}/rule/{ruleId}/update.do
DELETE /rest-api/price-adjustment-rules/{level}/rule/{ruleId}/delete.do
GET    /rest-api/price-adjustment-rules/{level}/condition-subjects.do
GET    /rest-api/price-adjustment-rules/{level}/condition-operators.do
GET    /rest-api/price-adjustment-rules/{level}/action-types.do
POST   /rest-api/price-adjustment-rules/{level}/{entityId}/enable.do
POST   /rest-api/price-adjustment-rules/{level}/{entityId}/disable.do
```

A rule is a condition plus an action:

| Parameter | Values today |
|---|---|
| `conditionSubject` | `PLACEMENT` — the only subject currently offered |
| `conditionOperator` | `IS` — the only operator currently offered |
| `conditionValue` | The ID of the entity to match |
| `actionType` | `MULTIPLY` (gross × `actionValue`) or `SET` (gross := `actionValue`) |
| `actionValue` | The number the action applies |

```bash
curl -X POST "https://<your-network-domain>/rest-api/price-adjustment-rules/campaign/2001/create.do?$AUTH" \
  -d "conditionSubject=PLACEMENT" \
  -d "conditionOperator=IS" \
  -d "conditionValue=4001" \
  -d "actionType=MULTIPLY" \
  -d "actionValue=1.5"
```

All fields, with descriptions and an example request:
[`POST /rest-api/price-adjustment-rules/campaign/{entityId}/create.do`](https://epom.com/ad-server/docs/api/campaign-price-adjustment-rules.md#post-rest-apiprice-adjustment-rulescampaignentityidcreatedo)
— the [advertiser](https://epom.com/ad-server/docs/api/advertiser-price-adjustment-rules.md) and
[banner](https://epom.com/ad-server/docs/api/banner-price-adjustment-rules.md) levels have the same operations.

The three discovery endpoints exist so a client can render the pickers without hardcoding those
lists — worth calling if you build a UI on top, since the subject and operator sets are
expected to grow.

> **Corner case:** creating a rule auto-enables price adjusting on the entity. Rules
> cascade at serve time in the order advertiser → campaign → banner, so a banner-level
> rule is applied **last** and effectively wins. Rules only load for serving while the
> entity's switch is on — `disable.do` pauses them all without deleting any; `enable.do`
> resumes them. Both need *Manage Price Adjusting* and update access to the owning
> advertiser.

## Bid floors

Country-level minimum CPMs on placements, sites and banners:

| Method | Path | Purpose |
|---|---|---|
| GET | `/rest-api/bid-floor/placement/{placementId}.do` | Read placement bid floors |
| POST | `/rest-api/bid-floor/placement/{placementId}/update.do` | Set (`country`* = ISO alpha-2 code or `*` for the global default, `bidFloor`*) |
| POST | `/rest-api/bid-floor/placement/{placementId}/delete.do` | Remove (`country`*) |
| GET / POST / POST | `/rest-api/bid-floor/site/{siteId}.do`, `…/update.do`, `…/delete.do` | The same three for sites |
| GET / POST / POST | `/rest-api/bid-floor/banner/{bannerId}.do`, `…/update.do`, `…/delete.do` | Same for banners (the OpenRTB DSP creative's floor) |

All three answer with HTTP status codes — 404 for a missing or deleted entity (the message names
it, e.g. `Placement 4001 not found.`) or bid floor, 403 without update access or *Bid Floor
Management* (the message names which), 400 for an unknown country or a floor outside
0–1,000,000. The site and placement bodies keep their `success:false` and `error` text. Every
write on the three is recorded in the action log.

```bash
curl -X POST "https://<your-network-domain>/rest-api/bid-floor/placement/4001/update.do?$AUTH" \
  -d "country=US" -d "bidFloor=0.50"
```

All fields, with descriptions and an example request:
[`POST /rest-api/bid-floor/placement/{placementId}/update.do`](https://epom.com/ad-server/docs/api/pricing.md#post-rest-apibid-floorplacementplacementidupdatedo).

> **Corner case:** `delete.do` with `country=*` does not delete the global floor — it **sets it to
> zero** (the answer says *Global bid floor cannot be removed. The bid floor has been set to 0
> instead.*) and leaves the country entries as they are. Pass a specific country code to remove one
> country's entry; a country with no entry, or an entity with no bid floor at all, is a `404`.

Two more endpoints under the same tag serve self-serve CPM floor rates rather than placement
bid floors: `GET /rest-api/floorCPMs.do` lists floor rates filtered by campaign type, operating
system, world region and country codes, and `GET /rest-api/floorCPMs/suggest.do` computes a
suggested rate for one advertiser, adjusted by that advertiser's
`selfServeCpmFloorAdjustment` multiplier. Country codes in `suggest.do`'s `geo` parameter may
be prefixed with `-` to exclude rather than include.

Entity pricing itself (CPM/CPC/CPA rates, country pricing) lives on the entity
endpoints — see the pricing rows in [Entities CRUD](https://epom.com/ad-server/docs/api/entities-crud.md) and the
`…/pricing.do` and `…/pricing/{countryCode}.do` paths under banner, campaign, placement and
site, and the `…/pricing/batch.do` CSV upload under banner, campaign and placement.
`GET /rest-api/campaign/{campaignId}/pricing.do` marks the answer with `pricingSet`: a campaign
whose pricing was never set answers `{"campaignId":2001,"pricingSet":false}` instead of a `404`.
`GET /rest-api/banner/{bannerId}/pricing.do` of a CPA banner lists the prices set through
`bannerPricing/actionPricing.do` as `globalActionPricing` (`actionName`, `actionKey`, `price`),
the campaign read's name, leaving out deleted actions. `DELETE /rest-api/campaign/{campaignId}/pricing.do`
answers `{"success":true}`, also when there was no pricing to remove.

> **Corner case — which action a placement country price is for.**
> `GET /rest-api/placements/{placementId}/pricing.do` keeps `countryPricing` as the list of
> `{"US": 2.25}` pairs it always returned, which cannot say which conversion action a CPA price
> belongs to. Read `countryPricingDetails` instead: the same prices as
> `{"country":"US","actionId":34,"actionName":"Signup","price":2.25}`, where a row without
> `actionId` prices every action (`actionName` is left out for an action you cannot see). A CPA
> placement also returns `useGlobalActionPricing` and `globalActionPricing`, the per-action prices
> for every country. The call answers `404` for a missing or deleted placement and `403` without
> a share on its site.

> **Corner case — placement "Use general pricing" and CSV upload.** The CPA checkbox of the
> placement Pricing card is `POST /rest-api/placements/{placementId}/pricing/use-global-action-pricing.do`
> with `useGlobalActionPricing=true|false`; a placement without fixed pricing, or priced other than
> CPA, is a `400`. The card's **Upload CSV** is `POST /rest-api/placements/{placementId}/pricing/batch.do`:
> send the file as `countryPrices` (multipart) or, from a client that cannot upload files such as
> the AI assistant, its text as `countryPricesCsv` — exactly one of the two. The layout is the
> console's: `Country,Price` for CPM/CPC, a header starting with `_` and one column per action name
> (or `All`) for CPA. The upload merges — countries not in the file keep their prices — and
> answers `200` with `applied` / `rejected` (and the older `csvErrors` / `csvWarnings`) naming the
> rows it stored and skipped; a header that matches
> no layout is a `400` and changes nothing. Both calls need the placement to have fixed pricing
> already, *Manage Site Payment Model* enabled and update access to the site; the upload also needs
> *Manage Pricing*.

> **Corner case — campaign and banner pricing CSV take text too.**
> `POST /rest-api/campaign/{campaignId}/pricing/batch.do` and
> `POST /rest-api/banner/{bannerId}/pricing/batch.do` take the same choice as the placement
> operation above — the file as `countryPrices` (multipart) or, from a client that cannot upload
> files such as the AI assistant, its text as `countryPricesCsv` — exactly one of the two (see
> below for how both merge and report results).

> **Corner case — site fixed pricing.** `POST /rest-api/sites/{siteId}/pricing.do` with
> `paymentModel=FIXED_PRICE` behaves like the site's Pricing card in the console: it never
> touches the pricing of the site's placements, and a placement with prices of its own keeps
> them. Switching `pricingType` deletes the site's country and action prices (as confirming a
> pricing-type change in the console does) and clears the global price unless you send one;
> keeping the same `pricingType` keeps them. The whole call is one transaction: if any part is
> refused or fails — e.g. a `pricingType` your role may not set, which is a `403` naming the
> permission — nothing is deleted.

> **Corner case — site and placement `pricing.do` replace a list only when you send it.**
> Both `POST /rest-api/sites/{siteId}/pricing.do` and `POST /rest-api/placements/{placementId}/pricing.do`
> take a **JSON body**, not form parameters — its fields are listed under [`POST /rest-api/sites/{siteId}/pricing.do`](https://epom.com/ad-server/docs/api/sites.md#post-rest-apisitessiteidpricingdo)
> and [`POST /rest-api/placements/{placementId}/pricing.do`](https://epom.com/ad-server/docs/api/placements.md#post-rest-apiplacementsplacementidpricingdo).
> A `countryPricing` list replaces the country prices that apply to every action (per-action CPA
> country prices are kept; an empty list removes the rest), and an `advertiserPricing` list replaces
> the per-advertiser shares. Leave a list out and it is kept as it is — changing only the base
> price or `revenueShare` does not touch them. `revenueShare` may be left out to keep the stored
> share, but is required (`400`) when the entity has none yet. Fields the chosen `paymentModel`
> does not use (e.g. `price` with `REVENUE_SHARE`) are listed in `ignoredFields`. Every check runs
> before anything is written: a malformed entry (`[{}]`, a `null` price, an unknown country or
> advertiser, a price outside 0–1,000,000) is a `400` naming the field in `errors`, and a refused
> `pricingType` is a `403` — either way nothing changes. On a placement, `enablePricingOverride`
> alone switches custom pricing off or on without touching the prices. `minMarginType` (`NONE`,
> `ABSOLUTE` or `PERCENT`) and `minMarginValue` set the minimum gap between the advertiser price
> and the publisher payout: they apply to `FIXED_PRICE` only, are enforced on CPM links only and
> need *Manage Minimum Margin* enabled; leave them out to keep the stored setting.

> **Corner case — prices are required and bounded.** Every price you write — `…/pricing.do`,
> `…/pricing/{countryCode}.do`, `…/campaignPricing/actionPricing.do`, `…/bannerPricing/actionPricing.do`
> on campaigns, banners, placements and sites — must be between 0 and 1,000,000. The country and
> action price calls also require `price`: leaving it out is a `400` naming it (a `0` country price
> would override the base price). An out-of-range price is a `400`
> and nothing is stored. `…/pricing.do` on campaigns and banners is a partial update — only the
> fields you send change (a banner's `useGlobalActionPricing` included). Deleting a country price
> that does not exist is a `404`. On a campaign that is not priced CPA, `actionKey` / `actionName`
> on a country price are listed in `ignoredFields`; on a CPA banner a country price can take an
> `actionId`, and on any other banner it is listed in `ignoredFields`.

> **Corner case — CSV uploads merge.** `…/pricing/batch.do` on campaigns, banners and placements
> adds and updates the countries in the file and keeps the prices of every country the file leaves
> out. The answer splits the file by line: `applied` lists every stored price as
> `{line, country, actionId, price}` (`actionId` only for a CPA action column), and `rejected`
> lists every line — or part of a line — that was not, as `{line, reason}`; line 1 is the header.
> A rejected row changes nothing, and the rest of the file is still applied. The same rows also
> appear, as text, in `csvErrors` / `csvWarnings` (`Line #3: …`), kept for older clients. In a semicolon- or
> tab-separated file a price may use a decimal comma (`1,50`).
> `…/pricing/FIXED_PRICE/remove.do` and `…/cpm-thresholds/delete.do` likewise remove the
> country prices or country thresholds together with the site-level record, or nothing.

> **Corner case — campaign pricing is a partial update.** `POST /rest-api/campaign/{campaignId}/pricing.do`
> changes only the fields you send — `price`, `paymentModel`, `pricingType`,
> `useGlobalActionPricing` and the optimization fields keep their stored values when left out
> (an empty `price` clears the global price). Switching `pricingType` needs that pricing type's
> permission, as in the console, or the call is a `403` and nothing changes. A switch that goes
> through deletes the campaign's country and action prices and its global price, exactly as
> confirming a pricing-type change on the console's Pricing card does; send `price` in the same
> call to set the new type's global price. Re-sending the current `pricingType` deletes nothing;
> `useGlobalActionPricing` is returned by the matching `GET`. The campaign's max payout per
> conversion is `maxActionPriceOverride` on `/rest-api/campaign/{campaignId}/update.do`: omit it
> to keep the ceiling, send it empty to remove it (as clearing the console field does), and note
> that `0` is a ceiling of zero, not "no ceiling".

> **Corner case — pricing an action you cannot see.** The calls that price by conversion action —
> `…/campaignPricing/actionPricing.do`, `…/bannerPricing/actionPricing.do`, and
> `…/pricing/{countryCode}.do` on campaigns (`actionKey` / `actionName`), placements and sites
> (`actionId`) — accept only an action you would see in the console's action list: every action
> with *Actions* view at *All* level, otherwise the ones shared with you. Any other action is a
> `404`, the same answer as an action that does not exist, and nothing is written. Deleting a
> placement or site country price by `actionId` also accepts a deleted action you can see, so a
> price left behind on it can still be removed.

> **Corner case — Great Britain is `UK`.** Country prices and CPM thresholds for Great
> Britain are stored under `UK`, the code the ad server resolves British traffic to. Send
> either `GB` or `UK` — setting, updating and deleting (`DELETE …/pricing/GB.do`) all reach
> the same row — but reads return `UK`. Country codes are case-insensitive everywhere a price
> or threshold is written: `us` and `US` are the same row, and `gb` is Great Britain too.

## Related

- [Targeting, capping and limits via API](https://epom.com/ad-server/docs/api/targeting-capping-limits.md) — the overview
- [API overview](https://epom.com/ad-server/docs/api.md)
- [Targeting rules via API](https://epom.com/ad-server/docs/api/targeting-api.md)
- [Frequency capping via API](https://epom.com/ad-server/docs/api/capping-api.md)
- [Delivery limits via API](https://epom.com/ad-server/docs/api/limits-api.md)
- [Entities CRUD](https://epom.com/ad-server/docs/api/entities-crud.md)
- [Generated reference: pricing](https://epom.com/ad-server/docs/api/pricing.md)
- [Price adjustment rules (UI guide)](https://epom.com/ad-server/docs/guides/delivery/price-adjustment-rules.md)