Skip to main content
AdCP supports multiple pricing models to accommodate different advertising channels and business objectives. Publishers declare which pricing models they support, and buyers select from available options.

Publisher-Declared, Buyer-Selected Model

How It Works

  1. Publishers declare pricing options in their products via pricing_options array (each with unique pricing_option_id)
  2. Buyers discover available options through get_products
  3. Buyers select a specific option when creating a media buy via pricing_option_id
  4. Delivery is measured according to the declared delivery_measurement provider

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:
Common Measurement Providers:
  • 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
By accepting the product, buyers agree to use the declared measurement provider as the authoritative source for delivery metrics.

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)
Measurement terms fields:
  • billing_measurement — Which vendor’s count of the billing metric (determined by pricing_model) governs invoicing. max_variance_percent defines 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 fields: Each entry in the performance_standards array specifies:
  • metric — What is measured: viewability, ivt, completion_rate, brand_safety, attention_score
  • threshold — 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 against threshold, in the vendor’s own vocabulary (the metric_id in its measurement.metrics[] catalog and in delivery’s vendor_metric_values[]). Allowed only for ivt, brand_safety, and attention_score. viewability and completion_rate are judged against their delivery scalars (viewability under standard; a vendor-attested completion rate carries completion_source: vendor_attested).
Verification vendors usually report several metrics per category: general, sophisticated, and combined invalid traffic rates, or several brand-safety rates. metric_id says which one the standard means:
When metric_id is present:
  • Evidence. The standard is judged only against vendor_metric_values[] entries from the same vendor (equal domain and brand_id) with the same metric_id. Values for the vendor’s other metrics are not evidence for it. If several rows match because their qualifiers differ, only the row whose qualifier matches the package’s vendor-scope committed_metrics entry counts; if that does not leave exactly one row, the standard is not judged.
  • Scale. metric_id MUST name a metric the vendor reports as a 0–1 fraction, on the same scale as threshold. A reported value outside 0–1 is not evidence and MUST NOT be converted (for example from a percent).
  • Direction. metric_id MUST name a metric that runs the same way as metric: for ivt, the share of invalid traffic (judged as a ceiling); for brand_safety, the share of safe impressions; for attention_score, higher is better (both judged as floors). An inverted metric such as an unsafe-impression rate cannot carry a brand_safety standard.
  • Denominator. The judged value is the vendor’s reported value as-is. The denominators in the performance-standard-metric enum descriptions do not apply.
  • Reporting. A package whose standard carries metric_id SHOULD also carry a matching vendor-scope committed_metrics entry, so missing_metrics audits the value.
  • Versions. The field is new in 3.3. Senders MUST omit metric_id toward 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.
When 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:
  1. Seller publishes measurement_terms and performance_standards on the product (defaults)
  2. Buyer proposes overrides on the package request at create_media_buy
  3. Seller accepts (echoes on confirmed package), rejects (TERMS_REJECTED), or adjusts (returns modified terms)
  4. If a threshold is breached, seller proposes a remedy from the agreed makegood_policy; buyer accepts or disputes
When the buyer omits these fields, the product’s defaults apply.

Cancellation Policy

Guaranteed products can declare a cancellation_policy with the minimum notice period required before cancellation takes effect:
Cancellation fee types:
  • percent_remaining — Percentage of remaining committed spend (requires rate, e.g., 0.5 for 50%)
  • full_commitment — Buyer owes the full committed budget regardless of delivery
  • fixed_fee — Flat monetary amount (requires amount in the buy’s currency)
  • none — No cancellation fee (cancellation with notice is free)
Unlike 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)
For Buyers:
  • 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:
Billing: Charged per 1,000 ad impressions served

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
Example:
Billing: Charged per 1,000 viewable impressions (impressions meeting MRC viewability threshold). Viewability is measured by the declared measurement provider. Measurement: When available, publishers declare their viewability measurement provider in the optional 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:
Billing: Charged only when viewer completes 100% of the video/audio ad. Completion is measured by the declared measurement provider.

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:
Billing: Charged when viewer reaches threshold (e.g., 50% completion, 30 seconds) Parameters:
  • 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:
Billing: Charged per rating point delivered to target demographic Parameters:
  • demographic: Target demographic (e.g., “A18-49”, “W25-54”, “M35+”)
  • min_points: Minimum GRP commitment required
Metrics Reported:
  • grps: Total Gross Rating Points delivered
  • reach: Unique individuals reached
  • frequency: Average frequency per individual
Measurement Requirements: CPP pricing requires certified demographic measurement. Publishers should declare their measurement provider:
Common Measurement Providers for CPP:
  • Nielsen DAR/TV: Industry-standard TV measurement
  • Comscore: Campaign Ratings for CTV
  • iSpot.tv: Advanced TV analytics
  • Triton Digital: Audio/streaming measurement
Buyers should verify the measurement provider meets their campaign requirements before accepting CPP deals.

CPC (Cost Per Click)

Cost per click - Performance-based pricing for engagement. Use Cases: Direct response campaigns, search ads, social advertising Example:
Billing: Charged only when user clicks the ad

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:
Billing: Charged a fixed price when the specified 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, subscribe for paid subscriptions, follow for 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 via sync_event_sources. When omitted, any event of the specified event_type counts.
Example (different rates by event source):
Pricing vs. optimization: The CPA pricing option’s 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 media
commission_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:
  1. The buyer registers the declared event_source_id through sync_event_sources.
  2. The package’s measurement_terms.billing_measurement names the authoritative party and commission-lock measurement_window.
  3. Provisional delivery may report conversion and commissionable values while the return window is open.
  4. adjusted or window_update reports replace earlier values as returns and cancellations settle.
  5. A final row reports commissionable_value, calculated spend, is_final: true, and finalized_at.
For revenue share, package 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):
Example (DOOH slot with parameters):
Billing: Fixed cost for the entire campaign period regardless of delivery volume. DOOH parameters (parameters.type: "dooh"):
  • sov_percentage: Guaranteed time share as a percentage (0-100). Compare it with reported dooh_metrics.sov_achieved by dividing sov_percentage by 100.
  • slot_span: Number of consecutive loop slots in this seller-packaged offer. It guarantees contiguity, but not position unless loop_position is present.
  • loop_position: Seller-defined loop-position label, such as any, first, last, or adjacent_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’s dooh_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 hour
  • venue_package: Named collection of screens
  • duration_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)
Sponsorship flat_rate options omit 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:
Billing: Cost = 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 units
  • max_duration (optional): Maximum booking duration in time units
Time Unit Calculation: 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 a parameters 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, the price_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 adjustments
  • adjustments — ordered list of adjustments, each categorized by kind
  • fixed_price (on the parent pricing option) — must equal the result of applying all fee and discount adjustments sequentially to list_price. A breakdown from the operator’s buying platform also applies operator_fee adjustments; see Operator fees
Implementations should ignore unrecognized fields in 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 optional beneficiary field identifying who receives the adjustment’s value. This is most useful for commission adjustments in multi-intermediary chains:
The value can be a sellers.json domain, an AdCP account ID, or a human-readable party name. For 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:
Operator fees use the fee formulas. All monetary values are rounded to the precision of the currency (e.g., 2 decimal places for EUR/USD) at each step, or, for an option that declares price_adjustments, to that option’s price precision, which can be finer. The invariant holds after rounding. Example with mixed adjustments:
For the structure example above: 14.00 × (1 − 0.15) = 11.90 ✓ (no fee adjustments in that example)

Rate vs. Amount

Each adjustment must include exactly one of rate 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 the fixed_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

The price_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:
Auction-based packages use 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’s operator, 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:
The platform states 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_price is 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, or flat_rate option declares an ordered price_adjustments table, so the buyer can compute the price locally for the selections and dates the table covers.
Declaring a table is optional and a fully priced offer is not a fallback. Whether a table is present tells the buyer whether it can compute the price itself. Many sellers, broadcast included, price the combination of property, dates, and format against avails and their own yield rules, and a seller managing yield this way SHOULD NOT be expected to publish a table or the method behind its prices. A table suits sellers whose rate really is a base rate with coefficients: Dutch linear TV rate cards multiply a net base price by month, spot-length, target-audience, and market indices.

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 fee rate can be 1 or more, so an index of 2.5 is a fee rate of 1.5;
  • an amount: a fixed amount in the option’s currency per pricing unit.
There is no separate index row kind. A Dutch index card factor 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).
A buyer booking a 20-second spot in first position for a flight in the second half of November pays, applying each row to the running total in declared order: 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 carry valid_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:
  1. Select rows. A row applies when its window contains the purchase’s start_time and 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. always rows 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.
  2. 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, a rate row adds (fee) or subtracts (discount) round(running × rate) and an amount row adds or subtracts its amount; 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.
  3. 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, and cpp options; and the fewest decimal places that exactly represent fixed_price and each row amount. Precision is a property of the numeric values, not of how they were written: 0.0100 and 0.01 are 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.
  4. 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.
This replaces the draft’s earlier model, which applied index factors multiplicatively and then added percentage premiums to the indexed price without compounding them. The two give the same price for a table that declares its index rows before at most one percentage row and its 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 (see pricing_version and cache_scope on get_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 pricing is omitted from a purchase, the seller computes the resolved price. When the buyer supplies pricing, the seller MUST reject any fixed_price or price_breakdown that differs from the resolved result with INVALID_REQUEST naming the pricing field, and SHOULD carry the resolved value and breakdown as error-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 return fixed_price as the resolved rate. When any row fired, the seller MUST also return price_breakdown: its list_price is the declared option’s list_price when the option declares a price_breakdown, and otherwise the option’s declared fixed_price; its adjustments are the declared breakdown’s adjustments followed by one entry per fired row, in declared order. A rate row is recorded with its rate when that rate is below 1, and as its currency delta in amount otherwise (a fee rate of 1 or more); an amount row is recorded as its amount; a row whose delta rounds to zero contributes nothing and is omitted. list_price folded through adjustments equals fixed_price, so the invariant above covers selection-dependent pricing. A verifier re-resolves the table of the accepted pricing_option_id over the purchase’s selections and flight and compares.
  • The accepted pricing_option_id pins the table. Any change to the table, or to which selections or dates it matches, is a change to binding price and mints a new pricing_option_id. So the accepted pricing_option_id, which commercial_terms.terms_digest covers, 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 accepted change_terms[]) are priced from the table of the accepted pricing_option_id without 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 returns ACTION_NOT_ALLOWED whatever the table would price it at, and a change that exceeds the accepted commercial envelope returns REQUOTE_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.
A seller MUST NOT return an option carrying 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 using eligible_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.
When 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:
Buyer Responsibility: Buyers must select a currency that the publisher supports.

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_price field

Auction Pricing (fixed_price absent)

  • Final price determined through auction
  • Publisher provides floor_price (hard minimum) and optional price_guidance (percentile hints)
  • Common for non-guaranteed inventory
  • Buyer expresses execution intent explicitly with bidding.bid_amount or bidding.max_bid
The pricing option is the seller’s commercial offer; 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:
How it works:
  1. Package selects pricing_option_id from product (e.g., “cpcv_usd_auction”)
  2. Pricing option determines pricing model, auction unit, and fixed vs auction; its currency must match the media buy
  3. Package budget and all canonical bidding amounts use the media-buy currency
  4. Auction-based pricing uses explicit bidding.bid_amount for a manual auction bid or bidding.max_bid for a hard per-auction ceiling, expressed per the pricing option’s unit
  5. 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:
A buyer could choose CPP pricing if they’re planning TV buys, CPCV if optimizing for engagement, or CPM for reach-based campaigns.

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
Neither requires the seller to disclose why they declined. This is the protocol-native equivalent of “price not available without negotiation.”

The existing flow covers negotiated deals

The standard path for negotiated-price inventory is:
  1. Seller publishes products with descriptive metadata (audience, formats, properties) but without wholesale pricing
  2. Buyer submits a brief via get_products describing their campaign goals and budget parameters
  3. Seller returns a proposal with negotiated pricing tailored to the specific brief — or declines to propose
  4. Buyer refines or accepts the proposal, then passes the proposal_id into create_media_buy
This mirrors how premium direct deals work today: the seller sees the brief, decides whether to engage, and offers terms specific to the opportunity.

Best Practices

For Publishers

  1. Offer relevant pricing models - Match pricing to your inventory type and buyer expectations
  2. Set appropriate minimums - Use min_spend_per_package to ensure campaign viability
  3. Provide price guidance - For auction pricing, give realistic floor and percentile data
  4. Consider multi-currency - Support currencies of your target markets
  5. Document parameters - Clearly explain thresholds, demographics, and action types

For Buyers

  1. Select appropriate model - Choose pricing that aligns with campaign objectives
  2. Match currency - Ensure you select a currency the publisher supports
  3. Set realistic budgets - Account for minimum spend requirements
  4. Align goals with pricing - Set delivery goals that match your pricing model
  5. Monitor relevant metrics - Focus on the metrics that matter for your pricing model