Publisher-Declared, Buyer-Selected Model
How It Works
- Publishers declare pricing options in their products via
pricing_optionsarray (each with uniquepricing_option_id) - Buyers discover available options through
get_products - Buyers select a specific option when creating a media buy via
pricing_option_id - Delivery is measured according to the declared
delivery_measurementprovider
Key Benefits
- Flexibility: Publishers can offer multiple pricing models for the same inventory
- Currency Support: Publishers specify supported currencies; buyers must match
- Market Standards: Each channel (TV, video, display, performance) can use its natural pricing unit
- Clear Expectations: Both parties agree on pricing before campaign launch
Measurement & Source of Truth
Measurement Provider as Source of Truth
The product declares the measurement provider, and the buyer accepts that provider as the source of truth for the buy. Publishers specify their measurement provider in the product:- Ad Servers: Google Ad Manager, Freewheel, SpringServe
- Attention Metrics: Adelaide, Lumen, TVision
- Third-Party Verification: IAS, DoubleVerify, Scope3
- TV/Audio Measurement: Nielsen, Comscore, iSpot.tv, Triton Digital
- DOOH: Geopath, Vistar, Place Exchange
Measurement Terms and Performance Standards
Beyond declaring a provider, sellers can publish structured terms on products — and buyers can negotiate them at buy creation. These are two separate concerns:measurement_terms— Who counts the billing metric and what happens when thresholds are breached (makegoods)performance_standards— What rate thresholds apply (viewability, IVT, completion, brand safety, attention)
billing_measurement— Which vendor’s count of the billing metric (determined bypricing_model) governs invoicing.max_variance_percentdefines the threshold at which the non-billing party’s count divergence triggers resolution.makegood_policy— Closed menu of remedies available when any performance standard or billing variance is breached. Three remedy types:additional_delivery(extend/add impressions, like-for-like),credit(toward future buys),invoice_adjustment(reduce current invoice). Seller proposes from this menu; buyer accepts or disputes.
performance_standards array specifies:
metric— What is measured:viewability,ivt,completion_rate,brand_safety,attention_scorethreshold— Rate as a decimal (0-1). Whether this is a floor or ceiling depends on the metric: viewability, completion_rate, brand_safety, attention_score are floors (must exceed); ivt is a ceiling (must not exceed).standard— Required for viewability ("mrc"or"groupm"). Omit for other metrics.vendor— Who measures it, identified by domain. When agreed on a confirmed package, creatives MUST include tracker assets from specified vendors.metric_id— Optional. Which of the vendor’s metrics carries the value judged againstthreshold, in the vendor’s own vocabulary (themetric_idin itsmeasurement.metrics[]catalog and in delivery’svendor_metric_values[]). Allowed only forivt,brand_safety, andattention_score.viewabilityandcompletion_rateare judged against their delivery scalars (viewabilityunderstandard; a vendor-attested completion rate carriescompletion_source: vendor_attested).
metric_id says which one the standard means:
metric_id is present:
- Evidence. The standard is judged only against
vendor_metric_values[]entries from the same vendor (equaldomainandbrand_id) with the samemetric_id. Values for the vendor’s other metrics are not evidence for it. If several rows match because theirqualifiers differ, only the row whose qualifier matches the package’s vendor-scopecommitted_metricsentry counts; if that does not leave exactly one row, the standard is not judged. - Scale.
metric_idMUST name a metric the vendor reports as a 0–1 fraction, on the same scale asthreshold. A reported value outside 0–1 is not evidence and MUST NOT be converted (for example from a percent). - Direction.
metric_idMUST name a metric that runs the same way asmetric: forivt, the share of invalid traffic (judged as a ceiling); forbrand_safety, the share of safe impressions; forattention_score, higher is better (both judged as floors). An inverted metric such as an unsafe-impression rate cannot carry abrand_safetystandard. - Denominator. The judged value is the vendor’s reported value as-is. The denominators in the
performance-standard-metricenum descriptions do not apply. - Reporting. A package whose standard carries
metric_idSHOULD also carry a matching vendor-scopecommitted_metricsentry, somissing_metricsaudits the value. - Versions. The field is new in 3.3. Senders MUST omit
metric_idtoward counterparties pinned below 3.3: canonical 3.2 shapes reject it, and legacy shapes ignore it, so a standard sent that way does not bind the metric.
metric_id is absent on an ivt, brand_safety, or attention_score standard, nothing says which vendor metric carries it. Parties MUST NOT infer one from the vendor’s catalog or metric naming, and treat the standard as not judged from vendor_metric_values.
Negotiation flow:
- Seller publishes
measurement_termsandperformance_standardson the product (defaults) - Buyer proposes overrides on the package request at
create_media_buy - Seller accepts (echoes on confirmed package), rejects (
TERMS_REJECTED), or adjusts (returns modified terms) - If a threshold is breached, seller proposes a remedy from the agreed
makegood_policy; buyer accepts or disputes
Cancellation Policy
Guaranteed products can declare acancellation_policy with the minimum notice period required before cancellation takes effect:
percent_remaining— Percentage of remaining committed spend (requiresrate, e.g.,0.5for 50%)full_commitment— Buyer owes the full committed budget regardless of deliveryfixed_fee— Flat monetary amount (requiresamountin the buy’s currency)none— No cancellation fee (cancellation with notice is free)
measurement_terms, cancellation policy is not a negotiation surface — the seller declares it and the buyer accepts it by creating a media buy against the product. A guaranteed buy canceled without sufficient notice incurs the declared cancellation fee.
Best Practices
For Publishers:- Clearly identify your measurement provider (ad server and any third-party verification)
- Explain your measurement methodology in plain language
- For DOOH, specify your audience measurement source (e.g., Geopath, venue sensors)
- Review the measurement provider before committing budget
- Ensure the provider meets your campaign requirements
- Negotiate audit rights in contracts if needed
Supported Pricing Models
CPM (Cost Per Mille)
Cost per 1,000 impressions - Traditional display advertising pricing. Use Cases: Display, native, banner advertising Example:vCPM (Viewable Cost Per Mille)
Cost per 1,000 viewable impressions - Payment only for impressions meeting MRC viewability standard. Use Cases: Display, native, video advertising with viewability guarantee Viewability Standard: MRC (Media Rating Council) standard requires:- Display ads: 50% of pixels in-view for at least 1 continuous second
- Video ads: 50% of pixels in-view for at least 2 continuous seconds
delivery_measurement field. Common providers include IAS (Integral Ad Science), DoubleVerify, MOAT, and Google Active View.
CPCV (Cost Per Completed View)
Cost per 100% video/audio completion - Payment only for fully completed views. Use Cases: Video campaigns, audio ads, pre-roll video Example:CPV (Cost Per View)
Cost per view at threshold - Payment when viewer reaches publisher-defined threshold. Use Cases: Video campaigns with shorter completion requirements Example:view_threshold: Decimal from 0.0 to 1.0 (e.g., 0.5 = 50% completion)
CPP (Cost Per Point)
Cost per Gross Rating Point - Traditional TV/radio buying metric. Use Cases: Connected TV, linear TV, radio, audio streaming Example:demographic: Target demographic (e.g., “A18-49”, “W25-54”, “M35+”)min_points: Minimum GRP commitment required
grps: Total Gross Rating Points deliveredreach: Unique individuals reachedfrequency: Average frequency per individual
- Nielsen DAR/TV: Industry-standard TV measurement
- Comscore: Campaign Ratings for CTV
- iSpot.tv: Advanced TV analytics
- Triton Digital: Audio/streaming measurement
CPC (Cost Per Click)
Cost per click - Performance-based pricing for engagement. Use Cases: Direct response campaigns, search ads, social advertising Example:CPA (Cost Per Acquisition)
Cost per conversion event - Advertiser pays when a defined conversion occurs. Use Cases: Retail media (pay per order), lead generation, app install campaigns, commerce media Example:event_type fires. The pricing option declares what event triggers billing — this is independent of optimization_goals, which controls delivery optimization.
Parameters:
event_type(required): The conversion event that triggers billing. Uses the standard event type enum (e.g.,purchase,lead,app_install,add_to_cart,subscribefor paid subscriptions,followfor free durable opt-ins).event_source_id(optional): When present, only events from this specific source count toward billing. Must match an event source configured viasync_event_sources. When omitted, any event of the specifiedevent_typecounts.
event_type (what triggers billing) is independent of the package’s optimization_goals (what the platform optimizes delivery toward). For example, a package can use CPA pricing on lead events while setting an event goal with event_sources containing purchase events and target: { kind: "per_ad_spend", value: 4.0 } — billing fires on leads, but delivery is optimized for downstream purchase return.
Refunds and adjustments: Refund handling and conversion adjustment policies are commercial terms between buyer and seller. The protocol does not govern clawbacks or billing credits for refunded conversions.
Note: CPA replaces the need for separate “CPO” (Cost Per Order) or “CPL” (Cost Per Lead) pricing models. A seller can offer multiple CPA options with different event types, event sources, and prices on the same product.
Revenue Share
Percentage of settled attributed value - Advertiser pays a fixed commission rate on the portion of attributed conversion value eligible under the parties’ agreement. Use Cases: Affiliate networks, content commerce, performance partnerships, and other outcome-priced mediacommission_rate is a decimal proportion: 0.04 means 4%. Revenue share uses contingent pricing, not fixed-unit or auction pricing, so the option does not carry fixed_price, floor_price, price_guidance, or max_bid, and the package request does not carry bid_price.
Billing formula:
commissionable_value is the settled value eligible for commission. It is distinct from conversion_value, which remains the total attributed value for analytics. The difference may reflect excluded tax or shipping, discounts, ineligible items, returns, or cancellations. commission_basis_description tells operators how the authoritative party calculates the eligible amount.
Settlement flow:
- The buyer registers the declared
event_source_idthroughsync_event_sources. - The package’s
measurement_terms.billing_measurementnames the authoritative party and commission-lockmeasurement_window. - Provisional delivery may report conversion and commissionable values while the return window is open.
adjustedorwindow_updatereports replace earlier values as returns and cancellations settle.- A final row reports
commissionable_value, calculatedspend,is_final: true, andfinalized_at.
budget is the maximum commission payable—not attributed sales. Sellers must not invoice above that budget without an accepted budget update.
When billing is buyer- or vendor-attested, report_usage carries media_buy_id, pricing_option_id, commissionable_value, and vendor_cost; the seller verifies the formula against the selected pricing option.
Revenue share is channel-independent. Affiliate is the immediate use case, but sellers may offer this pricing model on other channels where the same commercial contract applies.
Flat Rate
Fixed cost - Single payment regardless of delivery volume. Use Cases: Sponsorships, takeovers, exclusive placements, branded content, DOOH fixed slots Example (sponsorship):parameters.type: "dooh"):
sov_percentage: Guaranteed time share as a percentage (0-100). Compare it with reporteddooh_metrics.sov_achievedby dividingsov_percentageby 100.slot_span: Number of consecutive loop slots in this seller-packaged offer. It guarantees contiguity, but not position unlessloop_positionis present.loop_position: Seller-defined loop-position label, such asany,first,last, oradjacent_to_content_break. The vocabulary is open; buyers accept unknown values as opaque, with no position guarantee they understand.loop_duration_seconds: Deprecated compatibility copy of the placement’sdooh_placement_attributes.loop_duration_seconds. New integrations read the placement-level value; when both are present they must agree. Offers with different loop durations use distinct placements or products rather than pricing-option-specific copies.min_plays_per_hour: Minimum guaranteed plays per hourvenue_package: Named collection of screensduration_hours: Duration of the slot in hours (e.g., 24 for a full-day takeover)daypart: Named daypart (e.g.,morning_commute,evening_rush)estimated_impressions: Audience impression estimate (informational, not a delivery guarantee)
parameters — the fixed price covers the placement without DOOH-specific constraints.
Share of voice is always duration-weighted. For a loop with segment durations
of 10, 10, 20, and 40 seconds, reserving the first two consecutive segments is
20 / 80 = 25% SOV, not 2 / 4 = 50%. On equal-duration loops, time share and
slot-count share produce the same result. Delivery reports use the corresponding
0.0-1.0 scale, so a contracted sov_percentage of 25 is reconciled against
dooh_metrics.sov_achieved of 0.25.
Time (Cost Per Time Unit)
Cost per time unit - Rate scales with campaign duration, enabling self-serve sponsorships. Use Cases: Homepage takeovers, section sponsorships, premium placements where price depends on booking duration Example:fixed_price x number of time_units in the campaign flight. For example, a 3-day campaign at $50,000/day = $150,000 total.
Parameters:
time_unit(required):"hour","day","week", or"month"min_duration(optional): Minimum booking duration in time unitsmax_duration(optional): Maximum booking duration in time units
Comparison with Flat Rate:
Digital Out-of-Home (DOOH) Pricing
DOOH advertising uses existing pricing models—typically CPM or flat_rate—with optional parameters to describe the inventory allocation.Basic Concepts
- CPM for DOOH: Priced per thousand impressions, where impressions are calculated based on venue traffic (e.g., Geopath data)
- Flat rate for DOOH: Fixed cost for specific duration or allocation (hourly, daily, or exclusive takeover)
Simple Example: Billboard Takeover
DOOH parameters (optional)
For flat_rate DOOH inventory, publishers may include aparameters object with "type": "dooh". See the flat_rate pricing model section above for a complete example and full field list.
Note: DOOH measurement and buying practices vary by market. Publishers should clearly explain their measurement methodology and inventory allocation in the product description and delivery_measurement field.
Price Breakdown
When buyer and seller have negotiated discounts and commissions, theprice_breakdown object discloses how fixed_price was derived from the rate card. This is optional — sellers who don’t need breakdowns omit it entirely, with zero overhead to existing implementations.
price_breakdown is a planning-layer construct. It exists on pricing options and confirmed packages within AdCP — it does not propagate into impression-level OpenRTB bid requests or responses.
Structure
list_price— the rate card or base price before any adjustmentsadjustments— ordered list of adjustments, each categorized bykindfixed_price(on the parent pricing option) — must equal the result of applying allfeeanddiscountadjustments sequentially tolist_price. A breakdown from the operator’s buying platform also appliesoperator_feeadjustments; see Operator fees
price_breakdown and its adjustments to support forward compatibility. A reader that meets an unrecognized adjustment kind SHOULD skip the invariant check rather than report a mismatch.
Adjustment Kinds
Adjustments fall into five kinds based on their economic effect:
Fees increase what the buyer pays. Ad serving fees, data/targeting surcharges, and brand safety verification costs are additive to the base price. Without fee adjustments, these would be opaque within
list_price.
Discounts reduce what the buyer pays. Volume discounts, negotiated rates, and early booking discounts all lower both the buyer’s cost and the publisher’s revenue.
Commissions are revenue splits. The buyer’s price and budget don’t change — commission determines how the payment is divided between the intermediary (e.g., agency) and the publisher. Budgets are always managed inclusive of commissions. Multiple commission adjustments compound sequentially, like discounts — e.g., a 15% agency commission followed by a 5% trading desk fee means the publisher receives budget × 0.85 × 0.95. Use commission only when the seller has agreed to fund the split; a fee the advertiser pays on top of the seller’s price is an operator fee.
Settlement adjustments are applied at invoicing or payment time (e.g., a cash discount for early payment). They don’t affect the committed price or budget. Retroactive rebates and performance incentives are out of scope for price_breakdown and would be handled through reconciliation workflows.
Operator fees are kept by the account’s operator out of the advertiser’s budget — for example, an agency’s 2% insurance fee on every media buy. The advertiser pays it, the operator keeps it, and the seller never sees it. See Operator fees.
Beneficiary
Each adjustment may include an optionalbeneficiary field identifying who receives the adjustment’s value. This is most useful for commission adjustments in multi-intermediary chains:
operator_fee, beneficiary defaults to the account’s operator. Set it only when another party receives the fee.
Invariant
The invariant is:list_price with all fee, discount, and operator_fee adjustments applied sequentially equals the buyer’s effective price — fixed_price when the parent object carries it. Commission and settlement adjustments don’t participate — they’re disclosed for transparency. If no fee, discount, or operator fee adjustments are present, fixed_price must equal list_price.
This invariant applies only when fixed_price is present on the parent object. For auction-based packages, price_breakdown is informational — see On Confirmed Packages.
Fee, discount, and operator fee adjustments compound in array order using these formulas:
price_adjustments, to that option’s price precision, which can be finer. The invariant holds after rounding.
Example with mixed adjustments:
Rate vs. Amount
Each adjustment must include exactly one ofrate or amount:
rate— decimal proportion, strictly between 0 and 1 exclusive (0 < rate < 1), e.g., 0.15 for 15%amount— positive number (> 0) in the pricing option’s currency
Budgets and Commission
Budgets are always denominated at thefixed_price level, inclusive of commissions. A buying platform’s budget toward the advertiser also includes operator fees; see Operator fees. If a buyer commits €10,000 at an €11.90 CPM, that €10,000 is the buyer’s cost. The agency takes their commission from that amount; the publisher receives the remainder.
This means a buyer agent comparing rates across sellers can use fixed_price directly — it’s the actual cost per unit, regardless of the underlying commission structure.
On Confirmed Packages
Theprice_breakdown field also appears on the package response (confirmed line item). On a seller’s response it is seller-populated, and buyer agents should treat it as read-only. A buying platform reporting the package to the advertiser may append operator_fee adjustments; see Operator fees.
Fixed-price packages echo the pricing option’s breakdown:
price_breakdown to disclose commission and settlement terms against the clearing price. For auction packages, list_price is set to the clearing price since there is no rate card derivation to disclose. The discount invariant does not apply — the breakdown is informational only:
Operator fees
An account’soperator, typically an agency buying for a brand, often keeps a fee out of the advertiser’s budget on every media buy. Neither fee nor commission fits: a fee is paid to the seller, and a commission does not change the buyer’s price. An operator_fee raises the price the advertiser pays and leaves the seller’s revenue unchanged. A buying platform’s or trading desk’s own fee that the advertiser pays on top of the seller’s price is also an operator_fee, with beneficiary naming the platform.
Who adds it. Operator fees appear only in breakdowns the operator’s buying platform returns to the operator and the advertiser it buys for. The seller never sees them, so sellers MUST NOT include operator_fee in a breakdown or eligible_adjustments they author.
Order and the seller’s price. operator_fee adjustments MUST follow every fee and discount adjustment. The running price just before the first operator_fee is the seller’s price. Commission and settlement adjustments in the same breakdown apply to the seller’s price and budget, not the advertiser’s. Several operator fees compound in array order.
Rate basis. Like a fee, an operator fee’s rate is a markup on the running price. A contract that sets the fee as a share g of the advertiser’s gross spend converts to rate = g / (1 − g) (2% of gross is a rate of about 0.0204), or uses amount.
A buying platform reporting a package to the advertiser echoes the seller’s breakdown and appends the operator fee:
budget at the advertiser’s price. The advertiser’s €10,200 funds a €10,000 media buy at the seller’s €8.50 (seller budget = advertiser budget × seller price ÷ advertiser price), and the operator keeps €200. The platform SHOULD report delivered spend to the advertiser on the same basis. beneficiary is omitted because the fee goes to the account’s operator. A brand operating its own account has no operator fee to declare.
A flat per-campaign fee or retainer is not a per-unit adjustment and is out of scope. Whether the advertiser sees the operator fee is a matter for the platform’s permission model and the operator’s contract with the advertiser, not the protocol.
Selection-dependent price adjustments
Experimental (
media_buy.price_adjustments): sellers that implement price_adjustments declare the feature id in experimental_features, and the shape may change in 3.x with the notice described in Experimental status. The rule that a seller MUST NOT return a table to callers below 3.3 applies in addition.Two supported paths
A seller can quote a price in either of two equally supported ways:- A fully priced offer. The seller returns a pricing option, or a configured product or proposal, whose
fixed_priceis the price for what was quoted. It publishes no table. The price applies only to that quote: the buyer cannot derive the price of a different selection, date range, or volume from it and re-discovers or refines to learn it. - A fixed price plus an adjustment table. A fixed-price
cpm,vcpm,cpc,cpcv,cpv,cpp, orflat_rateoption declares an orderedprice_adjustmentstable, so the buyer can compute the price locally for the selections and dates the table covers.
Rows and composition
fixed_price stays the base rate: it is what the buyer pays when no row applies, and it is already net of any negotiated discount in the option’s own price_breakdown. This is distinct from eligible_adjustments (which kinds of adjustment are possible) and from price_breakdown (how a negotiated price was derived): price_adjustments is the seller’s declared, binding function from selections and dates to price.
Every row is a fee (adds) or a discount (subtracts), carrying exactly one of:
- a
rate: a proportion of the running total at that step. A feeratecan be 1 or more, so an index of 2.5 is a feerateof 1.5; - an
amount: a fixed amount in the option’s currency per pricing unit.
f is a fee rate of f − 1 when f > 1, a discount rate of 1 − f when f < 1, and no row when f = 1. The rate form needs no second kind, one rule covers percentage fees, discounts, and index cards, and a row means the same thing wherever it sits in the table.
Each row has a dimension. format_option and placement_selection rows match values the buyer selects; always rows need no selection and apply whenever their window allows (month, market, and audience indices).
1000.00 → 1100.00 (month index 1.10) → 1045.00 (market index 0.95) → 836.00 (20-second index 0.80) → 1045.00 (audience index 1.25) → 1254.00 (first-in-break fee of 20% of the running total, 209.00). The accepted price_breakdown lists the five rows in that order: fee 0.10, discount 0.05, discount 0.20, fee 0.25, fee 0.20.
Rate-side spot-length indexing is expressible through price_adjustments (a row keyed to the spot’s format option). Impression-side :30 equivalization and demo-bound CPM remain open and are tracked for 3.4 in #8001.
Eligible dimensions
Selection rows price only values that come from a seller-published catalog on the same product, so the seller can enumerate them in advance. The dimensions in this version are placement and format option (which carries spot length):
Matching is full-identity equality after resolving both sides to the product’s declared entry: a legacy placement ref and a placement identity for the same placement match, a seller-inline placement’s
seller_agent.agent_url compares under the URL canonicalization rules rather than byte equality, and the same format option ID under a different scope does not. Only values the buyer actually selects match a selection row. Omitting the selection, or choosing the product default, matches no row.
Property and collection are out of this version. property_list and collection_list are buyer-owned references (agent_url plus list_id): the buyer creates and changes the list, so the seller has no catalog entry to anchor a row to and cannot publish a price for a value it cannot enumerate. A row keyed to a list would be priced by the buyer’s own list contents. A seller that prices property or collection differently quotes them as a fully priced offer for the properties or collections the buyer names, or sells them as separately priced products. A later minor version can add property by seller-side id, or collection by collection_id, once a row can name a value the seller publishes.
Position in break is modelled as a seller-published placement (the example above prices first_in_break that way) until the booking half of #8009 lands; the reporting side is a separate contract (Position pricing is outside this reporting contract). Activation method, measurement vendor, and reporting tier have no selectable anchor yet; an unknown dimension is a schema error and later minor versions add values as anchors appear.
Validity windows
A row may carryvalid_from (inclusive) and valid_until (exclusive), both date-times with an offset, so a seller whose month and market indices change monthly publishes one row per window. A row without them always applies. The window is evaluated at the purchase’s start_time.
A purchase resolves to one price, so a flight whose [start_time, end_time) crosses a valid_from or valid_until of a row that would otherwise apply is rejected with INVALID_REQUEST naming the flight. The buyer splits the flight into one purchase per window. A flight may end exactly on a valid_until. Resolving each installment or each day of one purchase at its own rate would need a price per installment in the accepted snapshot, which this version does not define.
Row windows are separate from quote expiry below: a window says when a rate is in force, expiry says how long the seller holds the quote.
Resolution
Sellers and buyers MUST resolve a purchase’s price in this order:- Select rows. A row applies when its window contains the purchase’s
start_timeand its selector matches. Among rows of one selection dimension, only the first applicable row in declared order fires, so a seller can list the same value once per window and the matching window wins.alwaysrows each fire. Sellers who want multi-select purchases priced at the highest applicable row list it first; summing the matched values of one dimension is a recorded limit of 3.3, not supported in this version. - Apply in declared order to one running total. Start the running total at
fixed_price. For each firing row, in the order the seller declared it, araterow adds (fee) or subtracts (discount)round(running × rate)and anamountrow adds or subtracts itsamount; the result is the new running total. Because each row applies to the running total, a 10% fee followed by a 10% fee gives× 1.21, not× 1.20; a fixed amount followed by a rate is scaled by the rate; and the same rows declared in another order can resolve to a different price. The seller’s declared order is part of the quote. - Precision. The option’s price precision is the greatest of the currency’s ISO 4217 minor-unit exponent; 4 for
cpm,vcpm,cpc,cpcv,cpv, andcppoptions; and the fewest decimal places that exactly representfixed_priceand each rowamount. Precision is a property of the numeric values, not of how they were written:0.0100and0.01are the same value. Every step rounds half away from zero to price precision, so the resolved price is the exact running total and is not rounded again; rounding to currency minor units belongs to later spend and invoice calculation. Implementations MUST use exact decimal or integer arithmetic. - Never negative. The total MUST NOT fall below zero at the end. Sellers MUST verify at publication that no permitted selection resolves below zero; a purchase that would is rejected with
INVALID_REQUEST, never clamped.
amount rows last, which covers the Dutch example above; they differ when two percentage rows both apply, and when an amount row is declared before a rate row.
Two consequences follow from one-row-per-dimension. A multi-select purchase pays the first applicable row’s rate on all of its volume, so a seller that wants each selected value priced separately sells separate packages or publishes fully resolved per-selection options. And a row that prices a placement already in the product’s default set would make choosing it explicitly cost more than the default, so sellers should price only values that add to the default.
Quotes stay bound to their terms
A price, whether a fully priced offer or resolved from a table, is bound to what it was quoted for: its scope, applicable dates, buyer commitments, delivery terms, and the account it was quoted to. For a table:- Account. A table applies only to the account it was returned for. A buyer MUST NOT apply a table returned for one account to another account. A response with
cache_scope: "public"was returned for every account that prices off the public rate card. - Expiry. A table expires with the quote it came in: the proposal’s or offer’s
expires_at, or, for a reusable product, the validity of the discovery response it was returned in (seepricing_versionandcache_scopeonget_products). After expiry the buyer re-discovers and uses the new table; it MUST NOT price a new purchase from the expired one. - Without a table. A fully priced offer carries the same bounds. Its price applies only to what was quoted, and the buyer MUST NOT extrapolate it to other selections or dates.
Resolved price and accepted terms
- When
pricingis omitted from a purchase, the seller computes the resolved price. When the buyer suppliespricing, the seller MUST reject anyfixed_priceorprice_breakdownthat differs from the resolved result withINVALID_REQUESTnaming the pricing field, and SHOULD carry the resolved value and breakdown aserror-details/price-adjustments-mismatch.json(details.resolved_fixed_price,details.resolved_price_breakdown), so the buyer can see which row it disagrees on. It MUST NOT silently reprice the request. - In the accepted purchase
pricing, the seller MUST returnfixed_priceas the resolved rate. When any row fired, the seller MUST also returnprice_breakdown: itslist_priceis the declared option’slist_pricewhen the option declares aprice_breakdown, and otherwise the option’s declaredfixed_price; itsadjustmentsare the declared breakdown’s adjustments followed by one entry per fired row, in declared order. A rate row is recorded with itsratewhen that rate is below 1, and as its currency delta inamountotherwise (a fee rate of 1 or more); an amount row is recorded as itsamount; a row whose delta rounds to zero contributes nothing and is omitted.list_pricefolded throughadjustmentsequalsfixed_price, so the invariant above covers selection-dependent pricing. A verifier re-resolves the table of the acceptedpricing_option_idover the purchase’s selections and flight and compares. - The accepted
pricing_option_idpins the table. Any change to the table, or to which selections or dates it matches, is a change to binding price and mints a newpricing_option_id. So the acceptedpricing_option_id, whichcommercial_terms.terms_digestcovers, identifies exactly one immutable table, unfired rows and rows for other windows included. Changes the agreement already permits (a different format option or placement within the acceptedchange_terms[]) are priced from the table of the acceptedpricing_option_idwithout a re-quote. Both parties retain that table from discovery, and the seller MUST price from it, not from a later table the option’s successor carries. Quote expiry does not apply once the option is accepted: the accepted ID still names the table. - The table grants no change rights. Pinning the table does not make any selection or date change allowed. Which changes the buyer may make come only from the accepted
change_terms[](DR-0016): a change they do not permit returnsACTION_NOT_ALLOWEDwhatever the table would price it at, and a change that exceeds the accepted commercial envelope returnsREQUOTE_REQUIRED. A seller MUST NOT read a row for an unused value as consent to that change, and a buyer MUST NOT read it as an entitlement. - The table is not embedded in the accepted snapshot. The canonical schemas that carry accepted terms (
canonical-pricing-option.json,product-purchase.json, and the commercial terms that contain them) are closed, and a minor release does not add properties to closed schemas. Embedding the table in the snapshot, so the terms are self-contained without the retained discovery copy, is a 4.0 item tracked with #8501.
price_adjustments to a caller whose adcp_version is below 3.3, because an older buyer would read fixed_price as the whole price and be repriced at create; for those callers it publishes fully priced per-selection options or omits the option. A consumer that does not recognize a row’s dimension treats the option as unpurchasable. The fixed_price is net of the option’s declared discounts. Auction floors (floor_price) do not take adjustments, and price_adjustments requires fixed_price. Availability and forecasts stay seller-computed: the price resolves locally but feasibility does not.
A seller can instead publish a fully priced pricing option per selection combination, or split the inventory into separately priced products. Both remain valid with equal standing; price_adjustments is for sellers whose dimensions are orthogonal enough that a combination catalog would explode and who are willing to publish the coefficients.
Eligible Adjustments
Publishers can declare which adjustment kinds apply to a pricing option usingeligible_adjustments. This tells buyer agents upfront whether discounts, commissions, or settlement terms are available — before any negotiation begins. Sellers never declare operator_fee, which only a buying platform applies.
eligible_adjustments is present, the buyer knows which kinds of adjustments to expect or request. When absent, no adjustments are pre-declared — the buyer should check price_breakdown if present for any adjustments that were already applied.
This field pairs with price_breakdown: eligible_adjustments signals what is possible, while price_breakdown shows what was applied.
Multi-Currency Support
Publishers can offer the same product in multiple currencies:Fixed vs. Auction Pricing
Fixed Pricing (fixed_price present)
- Publisher sets a fixed price
- Price is guaranteed and predictable
- Common for guaranteed inventory
- Include
fixed_pricefield
Auction Pricing (fixed_price absent)
- Final price determined through auction
- Publisher provides
floor_price(hard minimum) and optionalprice_guidance(percentile hints) - Common for non-guaranteed inventory
- Buyer expresses execution intent explicitly with
bidding.bid_amountorbidding.max_bid
bidding is the buyer’s execution instruction. The pricing option supplies the auction unit and must use the media-buy currency. Every package budget, minimum-spend target, and canonical bidding amount uses that single denomination; split packages requiring another currency into a separate media buy. In 3.2, pricing-option max_bid and package bid_price remain only as deprecated migration fields. A seller-offered boolean no longer determines whether buyer input is a manual bid, auction ceiling, or average outcome-cost control.
Auction Example:
Buyer Selection Process
Each package specifies its own pricing option, which determines pricing model and auction unit. Its declared currency must match the single media-buy currency:- Package selects
pricing_option_idfrom product (e.g., “cpcv_usd_auction”) - Pricing option determines pricing model, auction unit, and fixed vs auction; its currency must match the media buy
- Package
budgetand all canonicalbiddingamounts use the media-buy currency - Auction-based pricing uses explicit
bidding.bid_amountfor a manual auction bid orbidding.max_bidfor a hard per-auction ceiling, expressed per the pricing option’s unit - Sellers reject currency-mismatched packages rather than converting amounts; buyers split them into separate media buys
Reporting Metrics by Pricing Model
Different pricing models report different primary metrics:Example: Multi-Model CTV Product
A publisher offering Connected TV inventory with multiple pricing options:Negotiated and Premium Pricing
Premium sellers — particularly in OOH, CTV sponsorships, and direct deals — often finalize pricing through negotiation rather than publishing fixed rates. AdCP supports this workflow through the existing brief-to-proposal path without requiring a separate quote or price-on-request mechanism.Sellers are not required to publish wholesale prices
Product discovery (get_products) does not require sellers to disclose wholesale or clearing prices. Sellers may publish products with limited or no public pricing detail — for example, a CTV sponsorship product with audience and format information but no pricing_options. The product is discoverable, but the buyer must submit a brief to receive a proposal with actual pricing.
Selective brief response
Sellers control which briefs they respond to and what pricing they offer. When a seller receives a brief for inventory they choose not to transact on, the protocol-native options are:- Omit the product from the proposal entirely — the buyer sees no proposal for that inventory, signaling unavailability for this request
- Return
PROPOSAL_NOT_COMMITTED— an explicit signal that the seller reviewed the brief but is not offering a committable proposal at this time
The existing flow covers negotiated deals
The standard path for negotiated-price inventory is:- Seller publishes products with descriptive metadata (audience, formats, properties) but without wholesale pricing
- Buyer submits a brief via
get_productsdescribing their campaign goals and budget parameters - Seller returns a proposal with negotiated pricing tailored to the specific brief — or declines to propose
- Buyer refines or accepts the proposal, then passes the
proposal_idintocreate_media_buy
Best Practices
For Publishers
- Offer relevant pricing models - Match pricing to your inventory type and buyer expectations
- Set appropriate minimums - Use
min_spend_per_packageto ensure campaign viability - Provide price guidance - For auction pricing, give realistic floor and percentile data
- Consider multi-currency - Support currencies of your target markets
- Document parameters - Clearly explain thresholds, demographics, and action types
For Buyers
- Select appropriate model - Choose pricing that aligns with campaign objectives
- Match currency - Ensure you select a currency the publisher supports
- Set realistic budgets - Account for minimum spend requirements
- Align goals with pricing - Set delivery goals that match your pricing model
- Monitor relevant metrics - Focus on the metrics that matter for your pricing model
Related Documentation
- Media Products - Product model reference
- Creating Media Buys - How to select pricing when buying
- Delivery Reporting - Understanding metrics by pricing model
- Glossary - Pricing and metric definitions