Skip to main content

Overview

Exact age targeting reaches a storefront in one of two ways, depending on the kind of storefront. Storefronts on the Storefront-built or Agent-supplied path (see Storefront agents in discovery) take the exact range from the AdCP 3.2 targeting_overlay.demographics.age field on a get_products request:
A storefront reads the structured field only from a request served at AdCP 3.2. When you set demographic targeting on a campaign, Apostra’s proposal requests send it to these storefronts as this field. Built-in platform adapters and external sellers receive the brief only. These storefronts take hard age targeting only from the structured field. A decision model reads every brief and decides whether it requires or excludes an age range, says whether to reach people whose age is unknown, or only describes the audience. To make that decision, the storefront sends the brief text to Cloudflare Workers AI, which runs the Clef decision model; no other request data is sent. Ages count however they are written, in digits, in words, or in another language. A brief that states a hard age requirement is refused rather than guessed at, so move the range into the structured field, or say in the brief that it is a preference: A brief that restates the structured range as a requirement is refused in the same way: keep hard targeting in the structured field only. A request served at AdCP 3.1 has no structured age field, so until these storefronts negotiate AdCP 3.2 publicly, a 3.1 caller can express age only as a preference in the brief. If the brief cannot be read at that moment, the storefront returns a retryable SERVICE_UNAVAILABLE rather than searching past requirements it may state; retry the request. Built-in platform adapters (the platforms listed below) currently read one closed age range from the brief:
The adapter copies the two numbers from the brief. No path invents an age range, translates a generation label such as “Gen Z” into ages, or widens the request to fit a platform. This applies during product discovery and media-buy creation. It does not change targeting on an existing media buy.

Platform support

Each storefront must prove that it can execute the requested range exactly. ages 21-35 therefore works on Meta and Spotify. ages 25-44 also works on Google Ads because it is the exact union of two closed native buckets. A range that cuts through a bucket, or a closed range such as 65-99 projected onto Google’s open-ended 65+ bucket, is never approximated.

Snap: Australia minimum age

Snap prohibits reaching people under 16 when a media buy targets Australia exclusively. If a Snap media buy targets Australia only and the brief has not requested an age range, Apostra automatically applies an 18+ audience before sending the buy to Snap — so if you target Australia and don’t say 18+, Snap will get an 18+ audience. This only applies when Australia is the sole targeted country; combining Australia with another country does not trigger it. If you explicitly request ages 13-17 on an Australia-only Snap buy, the media buy is rejected before it reaches Snap rather than having that request silently overridden.

Requests that need clarification

A Storefront-built or Agent-supplied storefront rejects a structured age it cannot read exactly, with field: "targeting_overlay.demographics.age":
  • INVALID_REQUEST when include_unknown is missing, a bound is not a whole number, no bound is given, min is below 18, min is greater than max, or max is above 120.
  • UNSUPPORTED_FEATURE when one bound is omitted (an open-ended range such as 21+), or when accepted_bases or accepted_verification_methods is sent.
A built-in adapter asks for clarification on an ambiguous, excluded, or open-ended age range in the brief. An exact but unsupported age range receives a counter-pitch instead: a composition storefront returns products that fit the rest of the brief and includes an additive limitations entry. The proposal is composed from inventory without age targeting; fixed-age inventory is excluded rather than silently delivering to a different age audience. Its message says this explicitly, requested preserves the requested range, excluded_ranges lists fixed ranges omitted from the counter-pitch, and supported_ranges lists the exact ranges the seller can execute (or is empty when none are declared). The storefront never broadens, approximates, or inverts the age request.
For a built-in adapter, rewrite the brief as one explicit, closed, supported adult range when it contains:
  • An open endpoint, such as ages 21+, under 35, or up to 35.
  • More than one or a disjoint range, such as ages 21-24 or 35-44.
  • An exclusion, such as exclude people ages 21-35.
  • A minimum below 18.
  • Conflicting or ambiguous bounds.
  • A descriptive label without numbers, such as young adults or Gen Z.
This fail-closed response is expected. It prevents a storefront or model from silently choosing a different audience.

Unknown ages on Meta and Google Ads

Meta and Google Ads can deliver to people whose age is unknown. A constrained-age request excludes them unless it explicitly says to include them. In the structured field, set include_unknown: true. In a brief, say so directly:
You can also state that they must be excluded. The choice is stored with the product, written to the provider targeting object, and checked on readback. Meta may omit the unknown-age field when its effective value is false. In that case, readback still preserves your explicit exclusion under the requested controls and reports an exact match. A provider value of true never satisfies an exclusion or an age-constrained request that did not opt in. On Google Ads, every constrained request writes positive criteria for the selected age buckets and negative criteria for every omitted bucket. This includes a negative AGE_RANGE_UNDETERMINED criterion unless the brief explicitly includes unknown ages; an include-unknown choice instead writes it as a positive criterion. Exact readback verifies the complete positive and negative set. Other platforms reject an include-unknown request unless their row above says otherwise.

Read the applied targeting

A returned product with verified age targeting includes the applied range at:
Use the returned product for media-buy creation. The storefront checks the stored product declaration again before publishing it or writing provider state. On Meta, requested and provider controls also show whether unknown ages were included.
Signal-based path: Age targeting through product-level signals (described in this guide) is carried by the selected product via ext.scope3_product_targeting.demographics. It is not a targeting_overlay field; the paths are separate.AdCP 3.2 overlay path: overlaySupport.demographics is the permission signal for demographic overlays, and demographicTargeting is the companion product-scoped compiler contract for supported age bounds and unknown-age handling. save_media_buy rejects targetingOverlay.demographics before dispatch while the application seller transport remains on AdCP 3.1. Treat these fields as discovery/readback data for now, and use signal targeting or omit demographics when staging a media buy.

Seller impact

Sellers using a built-in adapter do not need a new configuration field. A custom or upstream sales agent must return the exact ext.scope3_product_targeting.demographics.age declaration or its product is filtered from that brief. As an alternative, an ad-server-backed seller can map existing ad-server targeting to a first-class age signal: The product declaration does not have to come from a user-level age signal. A wholesale product can declare that its inventory itself represents the exact age cohort—for example, from survey evidence or modeled audience composition—by including determinationMethod and the responsible dataProvider beside its age range. Chef can then select that inventory directly without attaching an age signal. Descriptive copy such as “great for young adults” is not enough; the structured bounds and provenance must be present.
Start from the selected ad-server targeting candidate’s complete adapterConfig; the abbreviated object above only illustrates the age fields. The age meaning comes exclusively from the age signal type and its range. A signal named “Adults 25-54” without those fields is not treated as an age signal. Signal Manager supports three semantic types:
  • generic is ordinary buyer-selectable targeting.
  • age is buyer-selectable targeting with a required closed adult range and determination method.
  • property is inventory addressing only and never enters the buyer-selectable signal catalog.
Every age signal also declares one determination method:
  • survey_based: the person reported their age in a survey.
  • assumptive: the provider inferred or modeled the age.
  • user_provided: the person supplied their age, without identity verification.
  • user_verified: the supplied age was verified against an identity source.
dataProvider separately names who supplied the dataset. It may name the seller, a first-party registration system, or a third-party data provider; the provider name does not substitute for the determination method. When a request asks for the same closed range, the storefront can select products from that signal’s source and automatically applies the signal on the buy. A different or partially overlapping bucket is not accepted, and signal-backed age buckets do not include unknown ages. Product declarations and mapped age signals describe targeting that will be applied, not the full range the source might be capable of creating. For example, a product or signal declaring ages 18-65 does not prove that ages 25-54 will be applied. The source must return a product or explicit signal declaring the requested range exactly.

Restrict acceptable age determination

Until AdCP 3.2 supplies native provenance fields, a buyer can add the temporary Apostra extension below to the same get_products request:
When this allow-list is present, the storefront only returns a product whose exact age mapping declares one of those methods. Missing, unknown, or disallowed provenance fails closed; it is never treated as approval. A product with age targeting baked into its inventory metadata declares the same fact as ext.scope3_product_targeting.demographics.age.determinationMethod.