Why this shape. Targeting, pricing, and curation are folded into one round-trip — the brief drives discovery, the publisher curates against it, and
pricing_options carry firm prices the buyer commits against via pricing_option_id. We rejected a separate get_price_quote step between products and buy creation: it splits one expert decision into two underspecified ones and breaks the brief→curation contract. Iteration is buying_mode: "refine" with a typed change array — not a new task. → Design principle: the brief drives discovery./schemas/3.2.0-beta.0/media-buy/get-products-request.json
Response Schema: /schemas/3.2.0-beta.0/media-buy/get-products-response.json
Quick Start
Discover products with a natural language brief:Using Structured Filters
Use structured filters for hard product requirements such as channel, delivery type, format, currency, and reporting support. Filters are valid and have the same exclusion semantics inbrief, wholesale, and refine modes; the mode changes curation and lifecycle behavior, not whether filters apply. Concrete delivery targeting belongs in targeting_overlay; future selectable targeting belongs in required_overlay_support.
For every deterministic predicate in filters, every returned product MUST
satisfy that predicate according to the field’s documented match semantics.
Sellers MUST exclude non-matching products in all three buying modes; they MUST
NOT accept a schema-valid filter and return the unfiltered curated set, wholesale
feed, or refinement result. A filter can validly produce the same result as an
unfiltered request, so conformance is established from the returned products’
membership and field values—not merely by requiring two responses to differ.
Natural-language relevance to a brief remains a curation judgment and is not
part of this deterministic filter rule.
Request Parameters
Targeting-aware discovery
Brief versus structured targeting
Anything that can be expressed in a structured request field SHOULD be. For example, put “US only” and “ages 18–44” intargeting_overlay, not just in the
brief. Structured values use fewer tokens, can be validated by code, and do not
lose meaning through natural-language extraction. Keep the brief for campaign
goals, semantic audience descriptions, preferences, and requirements for which
AdCP has no structured field.Hard requirements remain hard wherever they appear. A seller MUST apply an
explicit hard targeting requirement stated only in a brief. When the seller’s
structured interpretation materially affects product eligibility, pricing, or
forecasting, it MUST confirm that shared interpretation once on the response;
otherwise confirmation remains a best practice:targeting_overlay already contains those values and
the seller accepts them exactly, the response omits them. Selecting any product
from the response accepts the confirmed request-level brief interpretation;
product-specific alternatives remain sparse Product targeting modifications.Only predicates supported by the structured targeting vocabulary can appear in
brief_targeting. Other hard prose requirements remain binding but must be
confirmed in the seller’s natural-language response until AdCP defines a typed
representation.Provide exact targeting once during discovery so product availability, pricing,
and forecasts already reflect the constraints that will be booked:targeting_overlay is real targeting. The seller resolves it through inherent
product scope or selectable execution, and every returned forecast is scoped to
the resulting inventory. required_overlay_support says that the buyer needs to
choose DMA and placement values on packages later; it does not request products
or packages broken out by DMA or placement.Request requirements and product support deliberately use different schemas.
The buyer request contains dimensions and required systems, never seller maxima.
A requirement value of true matches product support true or any valid
support object. An object requirement matches support true or a support object
where every requested boolean is true and every requested array is a subset of
the corresponding product array. For geo_places and geo_places_exclude,
every requested identifier system and country key must exist, and requested
place-type and catalog-version arrays must be subsets of the corresponding
product support arrays. Unrequested object fields and numeric seller limits do
not participate. Missing or unknown requirements do not match.Support guarantees the ability to select a value subject to disclosed limits;
it does not guarantee inventory or forecast every possible value. Forecasts
returned when only future support was requested describe the product’s
discovery/default scope. Rediscover with concrete targeting_overlay values for
a value-specific forecast. Fixed prices and floors remain binding uniformly
for supported selections; price guidance remains non-binding. Sellers with
value-dependent rates must return a concretely targeted configured product,
split the rate tiers into products, or withhold binding pricing until concrete
rediscovery. They cannot reinterpret a selected pricing_option_id at a new
price. If a supported selection has no current inventory, creation returns
PRODUCT_UNAVAILABLE
rather than silently substituting or repricing it.For ISO subdivisions, known values belong in
targeting_overlay.geo_regions / geo_regions_exclude and therefore scope the
configured product’s price and aggregate forecast. Values intentionally chosen
later use structured required_overlay_support independently for inclusion and
exclusion. Each country requirement uses either all_values: true or an exact
candidate values set. A matching Product overlay_support must contain every
requested country and value; product support with all_values: true satisfies
any finite requirement for that country. all_values is evaluated against the
seller’s support snapshot when the declaration is issued and does not
automatically include values introduced by a later catalog revision.
Configured discovery or refinement
with the exact overlay is the authoritative value-level preflight—there is no
separate region resolver task.If the seller can honor a structured overlay exactly, the returned product
omits Product targeting_resolution. Request-level brief confirmation appears
once on the response, not on each product. Every request-specific configured
product issued through targeting-aware discovery carries is_custom: true and
expires_at, including exact products without a resolution echo. The generic
Product schema still accepts legacy custom products without an expiry. If a
product offers a different executable structured
constraint, it contains only the changed paths. Selecting that request-scoped
product_id accepts the disclosed resolution.Property governance
targeting_overlay.property_list references a property list created via create_property_list on a property governance agent. Property lists define which publisher properties meet compliance requirements — COPPA-certified sites, sustainability-scored inventory, brand-safe publishers, etc. The legacy top-level property_list discovery filter is deprecated.To use property list filtering:- Call
get_adcp_capabilitieson a property governance agent to discover availableproperty_features - Create a property list via
create_property_listwith your feature requirements - Pass the resulting list reference in
targeting_overlay.property_listso returned products and forecasts reflect the constrained inventory
media_buy.execution.targeting.property_list capability is a
routing rollup; each returned product must also make the requested constraint
executable through inherent scope or selectable property targeting. The legacy
features.property_list_filtering capability applies only to the deprecated
top-level discovery filter. See the Property Governance overview for the full workflow.Filters Object
Filters decide which offers may be returned; they do not become package delivery targeting. Exact audience or inventory eligibility belongs intargeting_overlay. When the
buyer needs a targeting dimension but does not know its values yet, use
required_overlay_support.
During the compatibility window, sellers may translate a deprecated
targeting-like filter into its overlay equivalent. If both legacy and new forms
are present, they MUST be semantically identical; otherwise the seller rejects
the request with INVALID_REQUEST. This rule applies to every deprecated
targeting filter and the legacy top-level property_list.
Audience evidence requirements
The reusableaudience_evidence_requirements object attaches under filters, so the same contract applies to natural-language brief curation, wholesale feeds, and refine requests.
requirement_mode: "required"excludes a product when it publishes evidence but no item satisfies every constraint. Withevidence_presence: "required", a product that publishes no evidence is also excluded. Withwhen_available, a product with no evidence remains eligible.requirement_mode: "preferred"never turns a mismatch into a hard exclusion. Sellers rank products with matching evidence higher and explain the selected evidence when returning or constructing a package.- When evidence affects product inclusion or rank, the returned product MUST carry the exact matching snapshots in
audience_evidence_selections[], withdecision_useset toeligibilityorrecommendation, even if the request’sfieldsprojection omitted that field. A product admitted becausewhen_availablefound no published evidence has no selection to report. - Sellers evaluate freshness from
last_updatedat request evaluation time. They do not synthesize missing provider, confidence, methodology, documentation, or attestation data. - Accepted lists and excluded lists are evaluated together;
excluded_methodologiesandexcluded_providersalways win when the same value appears in both lists. independent_attestation_requiredrequiresaccepted_attestation_issuersand is satisfied only by one exact reference/evaluation pair. The reference must be published on the evidence, its issuer and claim type must match the buyer allowlists and seller’s sharedadcp.attestationspolicy, its subject digest must equal the evidence digest, the evaluation must cover that same reference with outcomeverified, and its action-binding digest must equal the evidence digest. Buyer issuer and claim-type constraints narrow seller policy; they never broaden it.- Buyers inspect
media_buy.audience_evidencecapabilities first. Sellers MUST NOT silently ignore unsupported hard requirement or presence modes; they return a structured unsupported/invalid-request error. A preferred mode the seller does not support may be omitted by the buyer or explicitly rejected by the seller, but is never misrepresented as applied.
targeting_overlay, demographic_targeting, or an age-verification field. If the buyer also needs exact execution, it requests that independently through the corresponding product and package targeting surfaces.
Known place IDs belong in targeting_overlay, so the returned product’s
availability, pricing, and forecast are scoped to the effective place
constraint. If the buyer will choose IDs later, required_overlay_support
asks for the country, identifier system, place type, and optional catalog
version that the product must let the buyer select:
overlay_support.geo_places is binding permission to
supply matching place IDs on packages later, subject to disclosed limits. It
does not guarantee value-specific inventory or preserve an earlier forecast
before the IDs are provided. Fixed prices and floors retain their binding
semantics for every value within declared support; price guidance remains
non-binding. If the eventual values have no current inventory, create returns
PRODUCT_UNAVAILABLE
without silent substitution or repricing. Inclusion and exclusion permission
are independent; request geo_places_exclude separately when exclusions will
be chosen later.
Placement fields
get_products returns product placement data when the seller includes placements or the buyer asks for it through fields. Placement IDs are publisher-scoped. Product placements should reference the publisher’s public adagents.json placement declarations with {publisher_domain, placement_id} when a publisher declaration exists. Seller-private placement IDs, source/origin details, and delivery-system mappings must stay out of the response.
Each returned placement may carry:
Publishers can authorize sales agents for specific publisher placements using
authorized_agents[].placement_ids or authorized_agents[].placement_tags in adagents.json. Sellers should only return publisher-referenced placements they are authorized to sell.
mode: "targetable" means the placement may be selected through
packages[].targeting_overlay.placement_selection. A placement with
mode: "included" is part of the fixed/default product and cannot be selected
independently. A request whose complete selected set exactly equals the
product’s complete included set is satisfied inherently and may be echoed on
the booked package. That exact-equality rule applies during discovery, create,
and update and does not require overlay_support.placement_selection; partial
selection does. Because placement_selection represents the complete purchased
set, a product with any included placement does not declare selectable placement
overlay support; sellers expose a separate selectable product when needed.
creative_assignments[].placement_refs only route different creatives across
placements already selected for the package; they do not purchase inventory.
Signal-targeting filter example:
Currency filtering
Usefilters.pricing_currencies when the buyer’s constraint is “only show products whose media price I can transact in.” Use budget_range.currency when the buyer is also providing a budget amount or range.
Buyers MAY send both. Sellers apply them conjunctively: budget_range.currency denominates the budget amounts, while pricing_currencies narrows which returned product pricing_options are eligible. If the two fields conflict, sellers SHOULD return zero matching products rather than reject the request solely because of the conflict. Because product-scoped signal pricing is a separate add-on surface, this filter only gates mandatory seller-applied signal charges; optional signal or vendor add-ons may still advertise other currencies, and buyers should not select unsupported add-on prices.
When combined with is_fixed_price or pricing_structures, returned product pricing_options MUST satisfy every filter: each retained option must have a requested pricing structure and its currency must be in pricing_currencies.
Currency-only filter example:
pricing_currencies: ["USD"], the seller returns the product with only its USD product-level pricing_options. If the product also has a fixed or otherwise mandatory product-scoped signal charge, that mandatory charge must either be priced in USD or have no incremental price; otherwise the product does not match the filter. A mandatory custom signal price without currency is not satisfiable for this filter unless the seller can truthfully treat it as having no incremental price. Optional signal add-ons do not affect product matching.
Budget Range Object
*At least one of
min or max must be specified.
Refine array
Therefine array is a list of change requests. Each entry declares a scope and what the buyer is asking for. At least one entry is required. The seller considers all entries together when composing the response, and replies to each via refinement_applied.
Each entry is a discriminated union on scope:
scope: “request”
scope: “product”
scope: “proposal”
refinement_applied (response)
When the seller receives arefine array, the response includes refinement_applied — an array matched by position. Each entry reports whether the ask was fulfilled:
Catalog discovery
Pass acatalog to find advertising products that can promote your catalog items. The seller matches your catalog items against its inventory and returns products where matches exist. Supports all catalog types — a product catalog finds sponsored product slots, a job catalog finds job ad products, a flight catalog finds dynamic travel ads.
The catalog field uses the same Catalog object used throughout AdCP. You can reference a synced catalog by catalog_id, provide inline items, or use selectors to filter:
Products in the response include
catalog_types (what catalog types they support) and catalog_match (which items matched).
Response
Returns an array ofproducts and optionally proposals. When the seller’s
structured interpretation of hard brief targeting materially affects product
eligibility, pricing, or forecasting, it MUST include one response-level
targeting_resolution.brief_targeting confirmation shared by the curated
result set. Otherwise confirmation remains a best practice.
Products Array
Publisher properties for non-URL inventory
publisher_properties[].publisher_domain is the domain that anchors the publisher’s adagents.json namespace. It is not required to be the URL where an ad is displayed, and it is not a placeholder for a physical venue, publication, station, screen network, or print title.
Use the same selector shape for digital and non-digital products:
- Digital properties:
publisher_domainis usually the publisher domain whoseadagents.jsondeclares the website, app, channel, or CTV property. - Print, static OOH, radio, cinema, and local TV:
publisher_domainis the operating publisher or network domain that publishes the authoritative property catalog. The actual inventory is identified byproperty_ids,property_tags, placements, collections, product metadata, and channel fields. - Aggregated networks: use
property_tagswhen the product spans many properties, such as a tagged set of venues, publications, screens, stations, or local markets.
"print" or "ooh" for publisher_domain. Put channel meaning in channels, property meaning in the referenced property declarations, and sellable-package meaning in the product itself.
For example, a product that spans tagged metro properties can use this selector inside its publisher_properties array:
Proposals Array (Optional)
Publishers may return proposals alongside products - structured media plans with budget allocations. See Proposals for details.
Each
ForecastPoint is one forecast row. Composite slices are encoded by multiple dimensions[] items on the same point, such as placement x country. Sibling points are parallel rows, not nested children. Dimension order has no meaning; buyers normalize row identity from (forecast_range_unit, budget if present, product_id if present, dimensions sorted by kind). Buyers may compare rows at the same grain, but MUST NOT sum them unless the seller documents that the returned rows form a complete, non-overlapping partition. Standard delivery reporting verifies one-dimensional marginals, not exact cross-dimensional intersections.
Pagination
pagination is valid in all get_products modes, but its meaning follows the
buying mode:
- In
briefmode, pagination bounds the seller’s curated answer to the brief. A page is not a promise that every product matching the words in the brief has been enumerated. - In
refinemode, pagination bounds the refinedproducts[]result implied by therefinearray and current filters. Proposals may accompany the page as plan metadata, butpagination.max_results,has_more,cursor, andtotal_countare scoped to the product result set, not to a separate proposal list or a combined product/proposal count. - In
wholesalemode, pagination walks the wholesale product feed. This is the exhaustive/feed-style read and is the mode that pairs with wholesale feed versioning.
Pagination is optional. When omitted, the server returns the complete result set
or a server-chosen default page. When the response includes
pagination.has_more: true, pass pagination.cursor in the next request to get
the next page using the same result-defining request context, except for the
updated pagination.cursor.
Response Metadata
filter_diagnostics
When the seller can attribute exclusions to specific filters, the response MAY include afilter_diagnostics block. This is observability — not error reporting; sellers still silently exclude unmatched products per the filter-not-fail convention. Buyers use this to triage empty/small results without depending on its presence. total_candidates and excluded_by are independently optional — sellers whose baseline candidate set size is sensitive MAY emit excluded_by without total_candidates.
incomplete array
When the seller returns usable results but cannot complete all work within thetime_budget (or due to its own internal limits), the response includes incomplete — an array declaring what is missing. incomplete is a completeness statement, not a retry classification: buyers MUST NOT infer that retrying the same request will succeed. estimated_wait, when present, lets a buyer decide whether to make a new attempt with a larger time budget.
Wholesale feed versioning
A buyer that just synced a seller’s wholesale product feed can ask “has anything changed since version X?” in one cheap call, regardless of feed size. Sellers return an opaquewholesale_feed_version on every wholesale-mode response; buyers pass it back via if_wholesale_feed_version on the next call and the seller MAY short-circuit with unchanged: true — no products payload, no per-page diff. Patterned on HTTP ETag / If-None-Match.
This is the seller-side wholesale product feed returned by get_products. It is not a sync_catalogs feed; sync_catalogs manages buyer-provided campaign input feeds on the seller account.
Unchanged response example:
Request:
test=false
- Tokens are opaque. No format, no ordering, no inspection.
- A returned
wholesale_feed_versionis scoped to the request parameters that produced it. Buyers MUST cache the version alongside the(account, buying_mode, filters, targeting_overlay, required_overlay_support, deprecated property_list, catalog)tuple used. pricing_versionis an optional finer-grained token: when present, it changes when prices move butwholesale_feed_versionchanges only when structure/metadata moves. Common for rate-card sweeps that don’t change product metadata.if_pricing_versionrequiresif_wholesale_feed_version. Pricing has no structural baseline of its own. Sendingif_pricing_versionwithoutif_wholesale_feed_versionis a schema-level error. The seller’s evaluation is two-stage: wholesale feed mismatch returns the full payload (pricing is implicitly stale); wholesale feed match with pricing mismatch also returns the full payload (so the buyer sees updatedpricing_options); both match →unchanged: true.filterscanonicalization. Sellers MUST treat thefiltersobject as canonicalized before hashing into thewholesale_feed_versionkeyspace: keys MUST be sorted lexicographically, omitted-and-default values MUST be treated identically (a missingdelivery_typekey is the same scope asdelivery_type: null), array values MUST be sorted where the filter has set semantics (e.g.,channels,format_kinds,format_option_refs,required_metrics) and preserved-order where the filter has sequence semantics (e.g.,preferred_delivery_types). Buyers that pass equivalent-but-differently-shaped filter objects MUST receive the samewholesale_feed_versionfrom the seller. This rule prevents silent stale-mirror bugs from key-order or default-elision differences between buyer SDKs. Forward-compat default: new filter fields added in 3.x minor versions MUST declare set-vs-sequence semantics in their schema (viax-canonicalization: set | sequenceor equivalent prose); absent an explicit declaration, the rule defaults to set-semantics (sort before hashing). Sellers and SDKs that drift on this default produce cache misses that consumers can’t explain.- Pagination interaction.
wholesale_feed_versiondescribes the wholesale product feed as a whole, not individual pages. Sellers MUST returnwholesale_feed_versionon every paginated page (not only the first) when they declarewholesale_feed_versioning.supported: true; sellers that do not declare versioning SHOULD do the same. When the wholesale feed mutates between pages, the new version surfaces on the next page and the buyer MUST restart pagination fromcursor: null— the partial pages they’ve already received describe a stale version. Sellers MAY alternatively snapshot the feed at the start of pagination and serve all pages from that snapshot under the original version; either implementation is conformant as long aswholesale_feed_versionon a given page is the version that page belongs to. unchanged: trueand in-progress pagination. A buyer that is mid-pagination oncursor: XMAY sendif_wholesale_feed_versionmatching the version their pages so far were drawn from. If the seller confirmsunchanged: true, the response omitsproducts[]and pagination envelope entirely; the buyer abandons their in-progress walk under that version with confidence that no further pages would have produced new data. Sellers MAY NOT use the conditional-fetch short-circuit to skip individual pages within an active pagination —unchangedis feed-versus-cached-version, not per-page.- Pre-v3.1 sellers that ignore
if_wholesale_feed_versionsimply return the full payload — semantically correct, just inefficient (same as the unchanged-server path in HTTP).
specs/wholesale-feed-webhooks.md. Wholesale feed webhooks carry the changed product payload, pricing payload, removal tombstone, or bulk-change summary; get_products remains the repair and reconciliation read.
Cache layering
Sellers publish two notional layers: a public layer (the rate-card / structural view) and per-account overlays (custom deals, account-specific rate cards). The conditional-fetch path is layer-aware viacache_scope.
Why this matters. A buyer mirroring wholesale products across N accounts at one seller doesn’t want to hold N copies of inventory that’s actually identical for every buyer. The public layer is the seller’s published rate card; most accounts at most sellers price off it directly. Premium custom deals are the exception.
Two-layer cache.
Behavior.
- Requests without
accountalways returncache_scope: "public". Buyers cache under the public key. - Requests with
accountreturncache_scope: "public"OR"account"(seller MUST declare; no default)."public": this account prices off the rate card. Buyer MAY dedupe — the version and payload are the same as the unauthenticated view. The buyer can serve subsequent requests for any account in"public"cache_scope from a single public-layer entry."account": this response carries account-specific overrides. Buyer caches under the account overlay key.
- Sellers MAY downgrade an account from
"account"back to"public"by returningcache_scope: "public"on a request that previously got"account"— buyers SHOULD interpret this as “this account no longer has overrides” and drop their account overlay.
if_wholesale_feed_version. Send the token paired with whichever scope it was returned in. The seller compares against the current version for that scope. If the buyer’s token belongs to an "account" scope but the seller responds with cache_scope: "public", that’s the downgrade signal — buyer drops the overlay.
Webhook invalidation. Wholesale feed webhook events declare applies_to.scope on *.priced and *.updated payloads. Sellers MUST apply the same account/caller authorization predicate used by get_products buying_mode: "wholesale" when deciding which subscribers receive product webhooks:
applies_to: { scope: "public" }→ invalidate the public-layer cache for the entity. All account overlays referencing that public version are also stale and SHOULD be refetched.applies_to: { scope: "account", account_ids: [...] }→ invalidate only the named accounts’ overlays. The public layer is unaffected.applies_to: { scope: "account" }withoutaccount_ids→ the seller is withholding the affected set; the per-subscriber scope filter routes the event only to subscribers whose principal is in the affected set. Receiving the event means “your overlay is stale.”
specs/wholesale-feed-webhooks.md §“Cache layering and event scoping” for the full webhook-side spec.
See schema for complete field list: get-products-response.json
Common Scenarios
Time-budgeted discovery
Declare a time budget when you need fast results and can accept partial data. The seller returns what it can within the budget and declares what is incomplete:test=false
Wholesale Product Discovery
Multi-Format Discovery
Budget and Date Filtering
Property Tag Resolution
Guaranteed Delivery Products
Standard Formats Only
Catalog-driven discovery
Usecatalog with a brand to discover advertising products that can promote your catalog items. The seller matches your items against its inventory and returns products where matches exist:
Property List Filtering
AdCP 3.0 - Property list filtering requires governance agent support.
property_list_applied is absent or false, the sales agent did not filter products. This can happen if:
- The agent doesn’t support property governance features
- The agent couldn’t access the property list
- The property list had no effect on the available inventory
Property Targeting Behavior
Products have aproperty_targeting_allowed flag that affects filtering:
property_targeting_allowed: false(default): Product is “all or nothing” - excluded unless your list contains all of its propertiesproperty_targeting_allowed: true: Product is included if there’s any intersection between its properties and your list
Refinement
After initial discovery, usebuying_mode: "refine" to iterate on specific products and proposals. The refine array is a list of change requests — each entry declares a scope and what the buyer is asking for. The seller returns updated products with revised pricing and configurations, plus refinement_applied acknowledging each ask.
See the Refinement guide for the full walkthrough: scope types, action semantics, seller responses, and common patterns. The parameter shape is defined in the Refine array section above.
Minimal example:
test=false
refineis only valid inrefinemode. Requests that include this field inbrieforwholesalemode are rejected withINVALID_REQUEST.- Filters are absolute, not deltas. Always send the full filter set you want applied.
- Proposals are actionable through status.
proposal_status: "draft"requires finalization before create;proposal_status: "committed"can be executed withcreate_media_buy(proposal_id)beforeexpires_at; absent status is legacy ready-to-buy. - Proposals are ephemeral. Proposals typically include an
expires_attimestamp. After expiration, the seller returnsPROPOSAL_EXPIRED. - Wholesale product IDs are scoped stable identifiers. A non-custom product keeps the same ID for the same logical offer within the seller and declared
cache_scope, including across reads and webhooks. Feed and pricing versions communicate mutation; retirement or replacement may end that identity. - Targeting-aware custom product IDs are ephemeral configurations. A request-specific product issued by this flow sets
is_custom: true, is usable only in the authenticated account and discovery/refinement lineage that issued it, and includesexpires_at. The generic Product schema remains compatible with earlier custom products that omitted expiry. After expiration, a seller that still recognizes the caller-authorized ID returnsPRODUCT_EXPIRED; after the seller no longer retains a tombstone—or when the ID is outside the caller’s account/lineage—it returnsPRODUCT_NOT_FOUNDwithout revealing whether another tenant has that ID.
Seller Declines
A seller that understands a well-formedbrief or refine request but deliberately declines to offer products returns the GetProductsRejected arm. This is a successful business outcome, not a malformed request or technical failure:
reason and suggestions[] MAY be sanitized to protect confidential merchandising, inventory, policy, and partner rules. Sellers MUST NOT include internal rule text, candidate product identifiers, stack traces, credentials, or private upstream details. Buyers MUST treat these values as untrusted seller-authored plain text: escape them before rendering and sanitize or isolate them before including them in an LLM prompt context.
Retry behavior belongs to the error channel, not this arm or incomplete[]. In particular, an upstream outage that leaves no usable product result is a technical failure, normally SERVICE_UNAVAILABLE with error.recovery: "transient"; buyers then follow retry_after or exponential backoff. A rejection without suggestions[] is final for that brief, and a rejection with suggestions invites a changed brief rather than an unchanged automatic retry.
The rejection arm carries no products, proposals, incomplete, filter_diagnostics, refinement_applied, cache_scope, errors, or envelope-level adcp_error. HTTP and MCP keep success markers (200, isError: false). A2A maps the transport task to completed; the AdCP artifact payload carries status: "rejected":
Error Handling
Authentication Comparison
See the difference between authenticated and unauthenticated access:- Product Count: Authenticated access returns more products, including private/custom offerings
- Pricing Information: Only authenticated requests receive detailed pricing options (CPM, CPCV, etc.)
- Targeting Details: Custom targeting capabilities may be restricted to authenticated users
- Rate Limits: Unauthenticated requests have lower rate limits
Authentication Behavior
- Without credentials: Returns limited public product results, no pricing, no custom offerings
- With credentials: Returns complete product results with pricing and custom products
Asynchronous Operations
Most product searches complete immediately, but some scenarios require asynchronous processing. When this happens, you’ll receive a status other thancompleted. A submitted response with task_id is always pollable through get_task_status (legacy tasks/get); push_notification_config adds webhook notification for background workflows.
SDK Status Handling
When Search Runs Asynchronously
Product search may require async processing in these situations:- Complex searches: Searching across multiple inventory sources or custom curation
- Needs clarification: Your brief is vague and the system needs more information
- Custom products: Bespoke product packages that require human review
Async Status Flow
- MCP
- A2A
Immediate Completion (Most Common)
Needs Clarification
When the brief is unclear, the system asks for more details:context_id:Complex Search (With Webhook and Polling)
For searches requiring deep inventory analysis, configure a webhook for terminal completion/failure notification. The returnedtask_id remains valid for polling via get_task_status (legacy tasks/get).Status Overview
Note: For the complete status list see Task Lifecycle.
Most searches complete immediately. Async processing is only needed for complex scenarios or when the system needs your input.
Next Steps
After discovering products:- Review Options: Compare products, pricing, and targeting capabilities
- Create Media Buy: Use
create_media_buyto execute campaign - Prepare Creatives: Use each returned product’s canonical
format_options[]to see format requirements - Supply Assets: Use
sync_creativesfor library-backed sellers, or inlinepackages[].creativesfor inline-only sellers
Learn More
- Product Discovery Guide - Understanding briefs and products
- Pricing Models - CPM, CPCV, CPP explained
- Brief Expectations - How to write effective briefs
- Media Products - Product structure and fields