Price rules, bid floors and pricing via API
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.
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, the hash-authentication parameters
(an X-Api-Key header works the same) — see the API overview 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 |
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
— the advertiser and
banner 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.dopauses them all without deleting any;enable.doresumes 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.
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.
Corner case:
delete.dowithcountry=*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 a404.
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 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.dokeepscountryPricingas the list of{"US": 2.25}pairs it always returned, which cannot say which conversion action a CPA price belongs to. ReadcountryPricingDetailsinstead: the same prices as{"country":"US","actionId":34,"actionName":"Signup","price":2.25}, where a row withoutactionIdprices every action (actionNameis left out for an action you cannot see). A CPA placement also returnsuseGlobalActionPricingandglobalActionPricing, the per-action prices for every country. The call answers404for a missing or deleted placement and403without 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.dowithuseGlobalActionPricing=true|false; a placement without fixed pricing, or priced other than CPA, is a400. The card’s Upload CSV isPOST /rest-api/placements/{placementId}/pricing/batch.do: send the file ascountryPrices(multipart) or, from a client that cannot upload files such as the AI assistant, its text ascountryPricesCsv— exactly one of the two. The layout is the console’s:Country,Pricefor CPM/CPC, a header starting with_and one column per action name (orAll) for CPA. The upload merges — countries not in the file keep their prices — and answers200withapplied/rejected(and the oldercsvErrors/csvWarnings) naming the rows it stored and skipped; a header that matches no layout is a400and 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.doandPOST /rest-api/banner/{bannerId}/pricing/batch.dotake the same choice as the placement operation above — the file ascountryPrices(multipart) or, from a client that cannot upload files such as the AI assistant, its text ascountryPricesCsv— 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.dowithpaymentModel=FIXED_PRICEbehaves 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. SwitchingpricingTypedeletes 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 samepricingTypekeeps them. The whole call is one transaction: if any part is refused or fails — e.g. apricingTypeyour role may not set, which is a403naming the permission — nothing is deleted.
Corner case — site and placement
pricing.doreplace a list only when you send it. BothPOST /rest-api/sites/{siteId}/pricing.doandPOST /rest-api/placements/{placementId}/pricing.dotake a JSON body, not form parameters — its fields are listed underPOST /rest-api/sites/{siteId}/pricing.doandPOST /rest-api/placements/{placementId}/pricing.do. AcountryPricinglist replaces the country prices that apply to every action (per-action CPA country prices are kept; an empty list removes the rest), and anadvertiserPricinglist replaces the per-advertiser shares. Leave a list out and it is kept as it is — changing only the base price orrevenueSharedoes not touch them.revenueSharemay be left out to keep the stored share, but is required (400) when the entity has none yet. Fields the chosenpaymentModeldoes not use (e.g.pricewithREVENUE_SHARE) are listed inignoredFields. Every check runs before anything is written: a malformed entry ([{}], anullprice, an unknown country or advertiser, a price outside 0–1,000,000) is a400naming the field inerrors, and a refusedpricingTypeis a403— either way nothing changes. On a placement,enablePricingOverridealone switches custom pricing off or on without touching the prices.minMarginType(NONE,ABSOLUTEorPERCENT) andminMarginValueset the minimum gap between the advertiser price and the publisher payout: they apply toFIXED_PRICEonly, 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.doon campaigns, banners, placements and sites — must be between 0 and 1,000,000. The country and action price calls also requireprice: leaving it out is a400naming it (a0country price would override the base price). An out-of-range price is a400and nothing is stored.…/pricing.doon campaigns and banners is a partial update — only the fields you send change (a banner’suseGlobalActionPricingincluded). Deleting a country price that does not exist is a404. On a campaign that is not priced CPA,actionKey/actionNameon a country price are listed inignoredFields; on a CPA banner a country price can take anactionId, and on any other banner it is listed inignoredFields.
Corner case — CSV uploads merge.
…/pricing/batch.doon 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:appliedlists every stored price as{line, country, actionId, price}(actionIdonly for a CPA action column), andrejectedlists 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, incsvErrors/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.doand…/cpm-thresholds/delete.dolikewise 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.dochanges only the fields you send —price,paymentModel,pricingType,useGlobalActionPricingand the optimization fields keep their stored values when left out (an emptypriceclears the global price). SwitchingpricingTypeneeds that pricing type’s permission, as in the console, or the call is a403and 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; sendpricein the same call to set the new type’s global price. Re-sending the currentpricingTypedeletes nothing;useGlobalActionPricingis returned by the matchingGET. The campaign’s max payout per conversion ismaxActionPriceOverrideon/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 that0is 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}.doon 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 a404, the same answer as an action that does not exist, and nothing is written. Deleting a placement or site country price byactionIdalso 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 underUK, the code the ad server resolves British traffic to. Send eitherGBorUK— setting, updating and deleting (DELETE …/pricing/GB.do) all reach the same row — but reads returnUK. Country codes are case-insensitive everywhere a price or threshold is written:usandUSare the same row, andgbis Great Britain too.