This is a storefront-operator capability, and it’s opt-in per source: it
only applies to sources you’ve explicitly enabled it on. A source that is simply
unpriced is not asked for a feed — only sources whose ad server can’t expose
pricing use this path.
What you supply
One row per ad-server selector × country. Column order and header capitalization don’t matter; CSV and XLSX both work.Copy the CSV template
Use the embedded template below. Copy the header and sample rows into a CSV
or XLSX file, then replace them with your ad-unit or placement ids,
countries, CPM percentiles, dates, and monthly availability.
Required columns
| Column | Meaning |
|---|---|
ad_server_selector | The ad server’s stable selector id. For GAM this is the numeric Ad Unit ID (GAM → Inventory → Ad units) or a placement id. |
country | A country name (United States, Germany, …), an ISO-3166 alpha-2 code (US, GB, AU), or GLOBAL. One row per selector per country. |
currency | A real ISO-4217 currency code (e.g. USD, EUR, GBP). Reserved or placeholder codes such as ZZZ are rejected. |
pricing_model | cpm. |
p25_cpm, p50_cpm, p75_cpm | Your clearing-price distribution: the 25th, 50th (median), and 75th percentile CPMs. Non-decreasing (p25 ≤ p50 ≤ p75). p25/p50 may be 0 (common for run-of-network display that clears at $0 part of the time); p75 must be greater than 0 so the row has a sellable price. Scientific notation (e.g. 7.7e-7) is accepted. Values above 1,000 are rejected as likely unit mistakes or fat fingers; contact Scope3 if a CPM above that limit is intentional. |
effective_start_date, effective_end_date | The window this pricing is valid for, ISO YYYY-MM-DD (end after start). |
source_version | Any label for the file version, e.g. 2026-Q3. |
Optional columns
| Column | Meaning |
|---|---|
selector_type | ad_unit or placement. If omitted, the upload assumes ad_unit. |
selector_name | Human name of the ad unit, for verification (e.g. Run of Site — Display). |
monthly_impressions | Monthly available impressions for this selector and country, whole number. (available_impressions / avails are also accepted.) Values above 100,000,000,000 per selector/country/month are rejected as likely unit mistakes; split the row or contact Scope3 if that volume is intentional. |
How pricing is used
You give the platform a price distribution per country, not a single number:- The floor is derived from
p25— the platform will not sell below your 25th-percentile clearing price. Wherep25is0(run-of-network display that clears at $0 part of the time), the floor is0and the product sells at open auction, guided byp50/p75. p50andp75are guidance for pricing above the floor. The platform does not anchor the sell price on a percentile — the buyer’s agent chooses a price within your distribution.- Your per-country prices and availability stay per-country. Your ad units map to the buyer-facing products (a product can span several ad units), and each product carries a per-country forecast: the available impressions and the expected CPM range for each country you priced. A buyer targeting the US sees your US price and avails; one targeting the UK sees the UK’s — without splitting your catalog into a separate product per country.
Example feed
Copy this template into a CSV file:Uploading
All examples use the storefront base URL:1. Enable the module on your source
2. Preview
Submit rows (or paste CSV) withoutconfirmed to see exactly what will be
accepted or rejected before anything is written:
acceptedRowCount, rejectedRowCount, and a per-row
rejected list — each with the row, the column, and why (for example, a
missing p50_cpm, a fake currency code, a p25 > p50 ordering error, an
implausible outlier, or a non-ISO date).
3. Commit
When the preview is clean, re-submit withconfirmed: true. A commit is
rejected if any row is still rejected — fix and re-preview first. Each commit
fully replaces the prior feed for the source, so a selector you drop from the
new file stops being sold. A successful commit refreshes the source’s storefront
products automatically; no separate catalog refresh is required.
The response’s catalogRefresh fields report whether cache invalidation and the
immediate warm completed. A refresh failure does not roll back an accepted feed.
.xlsx or .csv file can be uploaded directly to
POST /inventory-sources/$SOURCE_ID/wholesale-pricing/upload as multipart
form-data (field file, plus confirmed=true to commit).