Skip to content

Audiences

Build, list, read and delete custom audiences. Audience creation is asynchronous: the create call returns immediately with a new id, and you poll the get endpoint until the audience reaches its completed lifecycle status. To run a completed audience on a recurring data window - rebuilt and optionally re-exported every cycle - see Schedules.

All audience endpoints are authenticated and JSON-only. Send Authorization: Bearer <token> and Accept: application/json on every call (plus Content-Type: application/json on the POST create). Reads use the read rate bucket (120 requests/min per caller); the create and delete use the write bucket (30 requests/min per caller).

Create Audience

POST /api/v2/analyses/audiences/create

Creates a custom audience for the authenticated user’s company from one or two datasets. Two datasets are combined with an operator. An optional crossvisitation block narrows the audience to people who visited selected points of interest; an optional crosspurchase block runs the built audience against the affinity purchase data. The audience is queued asynchronously; poll GET /api/v2/analyses/audiences/{id} for its status and results_count.

Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min). Optional: Idempotency-Key header (see below).

The build counts toward your company’s monthly data-scan limit (see Usage). When the limit has been reached and is enforced, the call is refused with a 422 whose message names the limit and the date it resets, before anything is created or queued.

Body

FieldTypeRequiredDescription
namestringYesAudience name (max 255 chars).
operatorstringConditionalHow to combine two datasets: AND, OR, or NOTIN. Required when datasets holds two blocks; rejected (422) when it holds one. Fetch the accepted values from GET /api/v2/analyses/reference/common/operators.
project_idintegerNoA project id owned by your company to file the audience under.
datasetsobject[]YesOne or two dataset blocks (array of size 1 or 2).
crossvisitationobjectNoOptional POI crossvisitation analysis. Permission-gated - see below.
crosspurchaseobjectNoOptional cross purchase analysis run on the built audience. Permission-gated - see below.
analysesobjectNoThe frequency analyses to run as the audience builds - frequency (with its frequency_day_part sub-option), apps_frequency and web_frequency, all booleans - the same toggles as the Audience Manager and what the activation preview depends on. Permission-gated, each tied to its dataset type - see below.
datastreamsobject[]NoData stream visualizations to generate as the audience builds - see below.

An audience holds one or two datasets. A single dataset takes no operator; two datasets require an operator (AND, OR, or NOTIN). Each dataset is validated and company-scoped identically. A second dataset that is present but missing its type is rejected with 422.

Each datasets[i] block always carries a type, and (except for Cohorts and Demographics) a start_date and end_date. The remaining fields depend on the type. The type is one of the dataset types your company is entitled to, fetchable from GET /api/v2/analyses/reference/common/dataset-types.

Common to each datasets[i]:

FieldTypeRequiredDescription
typestringYesThe dataset type. Fetch the types your company is entitled to from GET /api/v2/analyses/reference/common/dataset-types - a type not enabled for your account is rejected with 422.
start_datestring (Y-m-d)Yes for all except Cohorts and DemographicsWindow start date.
end_datestring (Y-m-d)Yes for all except Cohorts and DemographicsWindow end date.
signal_providersstring[]Yes (all types except Cohorts and Demographics)BID values identifying the signal source: a list of distinct strings, at most 200. Fetch valid values for the dataset type from GET /api/v2/analyses/reference/common/signal-providers. A provider already used on one of your organization’s audiences of the same dataset type is also accepted, as long as it is still a signal provider. Any other value is rejected with 422 naming it.
languagesstring[]NoLanguage codes. Fetch valid values from GET /api/v2/analyses/reference/common/languages.
locationobjectcountries required (all types except Cohorts, AffinityTransactions and ProfileAttributes){ countries[], states[], cities[], dmas[], zipcodes[] } (string arrays). countries come from GET .../reference/common/countries; states cascade via GET .../reference/common/states; cities cascade via GET .../reference/common/cities; dmas (POI and Origin only) come from GET .../reference/common/dmas.

Every key in a dataset block must be one the block’s type defines (the common keys above plus the type’s own fields below). Any other key - a misspelling, a key from another type, or a filter the type does not have - is rejected with 422 naming the key, never silently ignored. The same applies inside location.

WebDomain fields:

start_date must fall within the last 45 days of available data: web data lands with a ~48-hour delay, so the window ends two days ago (UTC). Older visitation data is archived and unreadable - an earlier start_date is rejected with 422. The window rolls forward one day at UTC midnight.

FieldTypeRequiredDescription
iab_category_codesinteger[]One of iab_category_codes / web_domainsIAB category ids. Fetch valid values from GET .../reference/web/iab-categories (and .../web/iab-subcategories).
web_domainsstring[]One of iab_category_codes / web_domainsWeb domains to target. Domains must exist in the catalog - fetch valid values from GET .../reference/web/domains; a domain outside the catalog is rejected with 422.
web_ref_domainsstring[]NoReferrer domains to target. Fetch valid values from GET .../reference/web/ref-domains.
device_typesstring[]NoWeb device type names. Fetch valid values from GET .../reference/web/device-types.
device_makesstring[]NoWeb device make names. Fetch valid values from GET .../reference/web/device-makes.
device_osesstring[]NoWeb device OS names. Fetch valid values from GET .../reference/web/device-oses.
browsersstring[]NoWeb browser names. Fetch valid values from GET .../reference/web/browsers.
max_devices_per_ipintegerNoMatch strictness: how many devices may share one household/IP. 1 (very strict) to 5 (more reach). Defaults to 3 (recommended).

CTV fields:

FieldTypeRequiredDescription
ctv_vendor_namesstring[]NoCTV vendor names. Fetch valid values from GET .../reference/ctv/vendors.
ctv_content_type_namesstring[]NoCTV content type names. Fetch valid values from GET .../reference/ctv/content-types.
ctv_content_genre_namesstring[]NoCTV content genre names. Fetch valid values from GET .../reference/ctv/content-genres.
ctv_channel_namesstring[]NoCTV channel names. Fetch valid values from GET .../reference/ctv/channel-names.
iab_codesarrayNoIAB codes. Fetch valid values from GET .../reference/web/iab-categories.
ctv_device_typesstring[]NoCTV device type names. Fetch valid values from GET .../reference/ctv/device-types.
ctv_device_makesstring[]NoCTV device make names. Fetch valid values from GET .../reference/ctv/device-makes.
ctv_device_osesstring[]NoCTV device OS names. Fetch valid values from GET .../reference/ctv/device-oses.
ctv_connection_typesstring[]NoCTV connection type names. Fetch valid values from GET .../reference/ctv/connection-types.
ctv_ispsstring[]NoCTV ISP names. Fetch valid values from GET .../reference/ctv/isps.
ctv_seriesstring[]NoCTV series names. Fetch valid values from GET .../reference/ctv/series.

Cohorts fields:

FieldTypeRequiredDescription
cohort_idintegerYesA completed cohort id owned by your company. Fetch valid ids from GET .../reference/cohorts/get-cohorts, or create one from your own cloud file via Create Cohort. (No dates or location.)

Apps fields:

FieldTypeRequiredDescription
app_idsinteger[]At least one of categories / taxonomies / bundle_ids / app_idsApp ids to target. Fetch valid values from GET .../reference/apps/bundle-ids.
bundle_idsstring[]At least one of categories / taxonomies / bundle_ids / app_idsApp bundle ids to target. Fetch valid values from GET .../reference/apps/bundle-ids.
categoriesarrayAt least one of categories / taxonomies / bundle_ids / app_idsApp category ids. Fetch valid values from GET .../reference/apps/categories.
taxonomiesarrayAt least one of categories / taxonomies / bundle_ids / app_idsApp taxonomy ids. Fetch valid values from GET .../reference/apps/taxonomies.
device_osstring[]NoApp device OS values. Fetch valid values from GET .../reference/apps/os.
refineobjectNoPost-build refinement: keep only the most active devices. Permission-gated, single-dataset audiences only - see Refine.

Apps location supports countries only: sending location.states, location.cities, location.dmas, or location.zipcodes on an Apps dataset is rejected with 422.

POI fields:

Targets people by the points of interest they physically visited. A POI dataset picks its places in one of two modes, set by poi_selection_mode: by categories, brands and locations (categorization, the default), or by the attributes scheduled at locations (attributes, see POI attributes). The POI catalogs cascade: segments -> categories (?segments[]) -> brands (?categories[]) -> locations (?brands[]). Unknown POI category, brand and location ids are rejected with 422, and so is a location whose brand is not available to your company.

FieldTypeRequiredDescription
categoriesinteger[]At least one of categories / analysisdata / locations in categorization mode, absent or empty in attributes modePOI category ids, as a FLAT list of integers ([12, 13]). An object here, such as [{"id": 12, "brands": [3]}], is rejected with 422. Fetch valid values from GET .../reference/poi/categories (cascades from .../poi/segments). With no analysisdata (and no locations to take brands from), each category expands to the brands available to your company; a category with none is rejected with 422.
analysisdatainteger[]At least one of categories / analysisdata / locations in categorization mode, absent or empty in attributes modePOI brand ids. This is the brand field: there is no brands key on a POI block, and brands are never nested inside categories. Fetch valid values from GET .../reference/poi/brands.
locationsarrayAt least one of categories / analysisdata / locations in categorization mode, absent or empty in attributes modePOI location identifiers, interpreted per location_identifier_type. Fetch candidates from GET .../reference/poi/locations.
location_identifier_typestringNoHow locations are interpreted: none, location_id, external_id, store_id, placekey, h3_index, or h3_index_integer.
poi_selection_modestringNoHow the dataset picks its places. categorization (the default) reads categories, analysisdata and locations. attributes reads poi_attributes, see POI attributes.
poi_attributesarrayYes in attributes mode, absent or empty otherwiseThe attribute values to build from, as [{ attribute_id, value_ids[] }] with at least one value id per attribute. Fetch attribute ids from GET .../reference/poi/attributes and value ids from GET .../reference/poi/attribute-values.
location.countriesstring[]YesCountry codes (at least one). From GET .../reference/common/countries.
location.statesstring[]NoState codes. Cascade via GET .../reference/common/states.
location.citiesstring[]NoCity names. Cascade via GET .../reference/common/cities.
location.dmasstring[]NoDMA market labels. From GET .../reference/common/dmas.
location.zipcodesstring[]NoZIP codes. Cascade from cities via GET .../reference/common/zipcodes (paginated).
signal_providersstring[]YesBID values identifying the signal source (at least one). From GET .../reference/common/signal-providers. A provider already used on one of your organization’s audiences of the same dataset type is also accepted, as long as it is still a signal provider.
time_limitsobjectNo{ process: boolean, start: 0-23, end: 0-23 }. Hour-of-day window applied only when process is true.
day_limitsobjectNo{ process: boolean, days: integer[] }. Day-of-week filter applied only when process is true; days use ISO numbering (1 = Monday .. 7 = Sunday).
distance_limitsobjectNo{ process: boolean, distance: number, unit: "miles" | "kilometers" }. Radius applied only when process is true.
refineobjectNoPost-build refinement: keep only the most active devices. Permission-gated, single-dataset audiences only - see Refine.
filtersstring[]NoQuality filters, none by default: any of anomalous_devices, anomalous_pois, strict_gps - see POI filters.

POI attributes

In attributes mode a POI dataset is built from attributes scheduled at locations instead of from categories, brands and locations. Read the attribute ids from GET .../reference/poi/attributes, then the value ids of each attribute from GET .../reference/poi/attribute-values, and send them in poi_attributes. The dataset is built from the locations where any of the selected values is scheduled. Only visits inside each location’s scheduled event windows count.

Leave categories, analysisdata and locations out in this mode: they come from the locations the values are scheduled at. The selected values must be scheduled at one location or more, with at least one scheduled event between start_date and end_date. The dates can cover at most five years (1,827 days), and poi_attributes takes up to 50 attributes with up to 500 value ids each.

{
  "type": "POI",
  "start_date": "2026-09-01",
  "end_date": "2026-09-28",
  "signal_providers": ["BID001"],
  "location": { "countries": ["USA"] },
  "poi_selection_mode": "attributes",
  "poi_attributes": [
    { "attribute_id": 3, "value_ids": [9] }
  ]
}

POI filters

A POI dataset takes the Audience Manager’s POI quality filters as filters. None apply unless you send them. The Audience Manager turns on anomalous_devices and anomalous_pois by default, so send both to match an audience built there.

ValueWhat it removes
anomalous_devicesDevices on Intuizi’s list of known bad devices.
anomalous_poisEvery visit to locations flagged as anomalous.
strict_gpsEvery visit at a spot where more than 500 devices from one signal provider were seen at that location in the same month: city-center defaults and rounded coordinates that a provider reports for many devices at once.

Use strict_gps for address-level lists such as offices and small businesses, where a single spot shared by hundreds of devices cannot be the location’s own visitors. Do not use it for stores, malls or venues: real crowds also gather on one spot there, and it removes them too.

The filters apply to every POI dataset in the audience, not to its other dataset types. When both datasets are POI, send the same filters on both, or the request is rejected with 422. Unknown values are rejected with 422, and so is filters on any other dataset type or next to an AffinityTransactions dataset, where they would not apply.

{
  "type": "POI",
  "start_date": "2026-06-01",
  "end_date": "2026-08-31",
  "signal_providers": ["BID001"],
  "categories": [101],
  "location": { "countries": ["USA"] },
  "filters": ["anomalous_devices", "anomalous_pois", "strict_gps"]
}

AffinityTransactions fields:

Targets people by what they actually bought. The purchase catalogs cascade: categories -> sub-categories (?categories[]) -> brands (?categories[], ?subcategories[]). Unknown category, sub-category, brand and demographic values are rejected with 422.

Affinity purchase data is United States only. location.countries is optional here and is always stored as ["USA"]; sending any other country code is rejected with 422, as is location.dmas (the type reads states, cities and postal codes only).

Affinity purchase data lands one week at a time, and a completed week is only fully loaded about eleven days after it ends. end_date therefore cannot run past the most recent fully-settled week - the latest Sunday that is at least 11 days before today (UTC) - and a later date is rejected with 422. This boundary advances by one week automatically each Thursday, so the newest selectable end date moves forward on its own; there is no fixed cut-off to track.

FieldTypeRequiredDescription
categoriesinteger[]At least one of categories / brandsAffinity purchase category ids. Fetch valid values from GET .../reference/affinity-transactions/categories.
brandsstring[]At least one of categories / brandsAffinity brand id strings. Fetch valid values from GET .../reference/affinity-transactions/brands.
subcategoriesstring[]NoSub-category strings (merchant category descriptions) cascading from categories. Fetch valid values from GET .../reference/affinity-transactions/subcategories. Picked without any brands, they are resolved to the brands they cover.
incomesstring[]NoIncome bands. Fetch valid values from GET .../reference/affinity-transactions/incomes.
agesstring[]NoAge bands. Fetch valid values from GET .../reference/affinity-transactions/ages.
gendersstring[]NoGender values. Fetch valid values from GET .../reference/affinity-transactions/genders.
ethnicitiesstring[]NoEthnicity values. Fetch valid values from GET .../reference/affinity-transactions/ethnicities.
channelsstring[]NoPurchase channel: B&M (in-store) and/or ONLINE.
spend_minnumberNoMinimum total spend (USD) across the date window.
spend_maxnumberNoMaximum total spend (USD) across the date window.
txn_minnumberNoMinimum number of transactions across the date window.
txn_maxnumberNoMaximum number of transactions across the date window.
analysis_spend_frequencybooleanNoAlso compute the spend distribution for the built audience. Defaults to false.
analysis_transactions_frequencybooleanNoAlso compute the transaction-count distribution for the built audience. Defaults to false.
max_devices_per_ipintegerNoMatch strictness: how many devices each shopper may expand to. 1 (very strict) to 5 (more reach). Defaults to 3 (recommended).

An AffinityTransactions dataset block:

{
  "type": "AffinityTransactions",
  "start_date": "2025-01-01",
  "end_date": "2025-03-31",
  "signal_providers": ["BID001"],
  "categories": [1],
  "subcategories": ["Eating Places, Restaurants"],
  "brands": ["501"],
  "incomes": ["100k-150k"],
  "channels": ["B&M"],
  "spend_min": 50,
  "txn_min": 2,
  "max_devices_per_ip": 3,
  "location": { "states": ["CA"] }
}

Demographics fields:

Targets people by their household demographic attributes. The four dictionaries are flat - no cascade - and at least one of them must carry a selection. Unknown values are rejected with 422.

A Demographics dataset reads one monthly vintage of the demographics data, the latest complete one when the audience or estimate runs. The audience’s dataset entry and the estimate name that month in vintage (YYYY-MM), and its numbers read as “latest monthly vintage, as of YYYY-MM”. An audience or estimate made before 2026-10-24 carries no vintage: it read every monthly vintage at once, so its numbers are about 3 to 4 times larger.

This type carries no date window and no signal providers: sending start_date, end_date or signal_providers on a Demographics dataset is rejected with 422. Its location supports countries only, so location.states, location.cities, location.zipcodes and location.dmas are rejected too.

FieldTypeRequiredDescription
gendersstring[]At least one of genders / ages / marital_statuses / incomesGender values. Fetch valid values from GET .../reference/demographics/genders.
agesstring[]At least one of genders / ages / marital_statuses / incomesAge ranges. Fetch valid values from GET .../reference/demographics/ages.
marital_statusesstring[]At least one of genders / ages / marital_statuses / incomesMarital status values. Fetch valid values from GET .../reference/demographics/marital-statuses.
incomesstring[]At least one of genders / ages / marital_statuses / incomesIncome ranges. Fetch valid values from GET .../reference/demographics/incomes.
location.countriesstring[]YesCountry codes (at least one). From GET .../reference/common/countries.

A Demographics dataset block:

{
  "type": "Demographics",
  "genders": ["F"],
  "ages": ["25-34", "35-44"],
  "incomes": ["100k-150k"],
  "location": { "countries": ["USA"] }
}

Deidentified fields:

Delivers the deidentified signals themselves for a country and date window, rather than a targeted device list - so instead of filters it takes fields: which signal fields the delivery carries.

fields selects output columns only. It does not filter rows: there is no device, OS or platform filter on this type (fields.userDetails: ["deviceos"] adds the OS column to the delivery, it does not restrict the delivery to an OS). A filter-shaped key such as deviceos is rejected with 422.

fields is an object keyed by group (geoLocation, advertising, userDetails, ipDetails, privacy, general). Every group is optional - send only the groups you want, or omit fields entirely for the default shape. Fetch the fields and the group each belongs to from GET .../reference/deidentified/fields; an unknown field, or a field sent under a group it does not belong to, is rejected with 422.

This type is delivered on its own: an audience whose dataset is Deidentified takes exactly one dataset block, so pairing it with a second one (and therefore an operator) is rejected with 422.

Its location supports countries only, so location.states, location.cities, location.zipcodes and location.dmas are rejected. When location.countries includes USA, the date window may cover at most two weeks per delivery; a longer window is rejected with 422.

The window can always start on the first day of the previous month or later. A start_date before the earliest day the Audience Manager offers in any time zone is rejected with 422, and the message names that day. Your Account Manager can enable a longer look-back for your account.

FieldTypeRequiredDescription
fieldsobjectNoWhich signal fields the delivery carries, keyed by group. Fetch the fields and their groups from GET .../reference/deidentified/fields.
signal_providersstring[]YesBID values identifying the signal source (at least one). From GET .../reference/common/signal-providers. A provider already used on one of your organization’s audiences of the same dataset type is also accepted, as long as it is still a signal provider.
start_datestring (Y-m-d)YesWindow start, inside the look-back described above.
end_datestring (Y-m-d)YesWindow end. At most two weeks after start_date when location.countries includes USA.
location.countriesstring[]YesCountry codes (at least one). From GET .../reference/common/countries.

A Deidentified dataset block. Its dates only show the format: send a window inside the look-back described above.

{
  "type": "Deidentified",
  "start_date": "2026-10-01",
  "end_date": "2026-10-12",
  "signal_providers": ["BID001"],
  "fields": {
    "geoLocation": ["latitude", "longitude"],
    "ipDetails": ["ipisp"],
    "general": ["d_utc"]
  },
  "location": { "countries": ["USA"] }
}

ProfileAttributes fields:

Targets people by the profile attributes they carry. An attribute is a category, a key under that category, and the values of that key to match; the values of one attribute are alternatives. Send the attributes in one of two shapes:

  • profile_attributes: a list of attributes combined with AND - a device must match every one.
  • profile_attribute_groups: 1 to 5 groups, combined with AND. A group with match: "all" requires every attribute in it, a group with match: "any" at least one. Use it for “all of these, and at least one of those”.

A block takes one of the two, never both (422). A single group with match: "all" builds exactly the same audience as profile_attributes.

The attribute catalogs cascade: categories -> keys (?category_ids[]) -> values (?category_ids[], ?key). Each attribute is checked at all three levels: an unknown category, a key that does not belong to the attribute’s category, or a value that does not belong to its category and key is rejected with 422 naming the exact field: datasets.<i>.profile_attributes.<n>.<field> or datasets.<i>.profile_attribute_groups.<g>.attributes.<n>.<field>. Indexes count from 0.

A profile_attributes row takes no group or match: sending either is rejected with 422, so use profile_attribute_groups to group attributes. An attribute inside a group takes only category_id, key, and value_ids.

location is not applied to this dataset type. Profile attribute data carries no geography, so the builder discards the whole location block. location.countries is therefore optional - it is still accepted, for backward compatibility with integrations written when it was required, but it never filters anything. location.states, location.cities, location.zipcodes and location.dmas are rejected outright.

Profile attribute data is delivered quarterly. start_date and end_date must fall inside the delivered window published by GET .../reference/profile-attributes/recency-limits; a date outside it is rejected with 422. The stored window is expanded out to whole quarters - start_date to the first day of its quarter and end_date to the last day of its quarter.

FieldTypeRequiredDescription
profile_attributesobject[]One of the twoAttributes (at least one), combined with AND.
profile_attributes[].category_idintegerYesAn attribute category id. Fetch valid values from GET .../reference/profile-attributes/categories.
profile_attributes[].keystringYesAn attribute key under that category. Fetch valid values from GET .../reference/profile-attributes/keys with ?category_ids[].
profile_attributes[].value_idsinteger[]YesValue ids of that key (at least one). Fetch valid values from GET .../reference/profile-attributes/values with ?category_ids[] and ?key.
profile_attribute_groupsobject[]One of the two1 to 5 attribute groups, combined with AND.
profile_attribute_groups[].matchstringYesall (every attribute in the group must match) or any (at least one must).
profile_attribute_groups[].attributesobject[]YesThe group’s attributes (at least one), each with category_id, key and value_ids exactly like a profile_attributes row.
signal_providersstring[]YesBID values identifying the signal source (at least one). From GET .../reference/common/signal-providers. A provider already used on one of your organization’s audiences of the same dataset type is also accepted, as long as it is still a signal provider.
start_datestring (Y-m-d)YesWindow start, inside the delivered window.
end_datestring (Y-m-d)YesWindow end, inside the delivered window.
location.countriesstring[]NoAccepted but never applied - this dataset has no geography. Safe to omit.

A ProfileAttributes dataset block (no location needed):

{
  "type": "ProfileAttributes",
  "start_date": "2026-04-01",
  "end_date": "2026-06-30",
  "signal_providers": ["BID001"],
  "profile_attributes": [
    { "category_id": 1, "key": "auto_intent", "value_ids": [10, 12] },
    { "category_id": 2, "key": "credit_band", "value_ids": [30] }
  ]
}

The same dataset with groups - both attributes of the first group required, and at least one of the second group’s:

{
  "type": "ProfileAttributes",
  "start_date": "2026-04-01",
  "end_date": "2026-06-30",
  "signal_providers": ["BID001"],
  "profile_attribute_groups": [
    { "match": "all", "attributes": [
      { "category_id": 1, "key": "auto_intent", "value_ids": [10, 12] },
      { "category_id": 2, "key": "credit_band", "value_ids": [30] }
    ] },
    { "match": "any", "attributes": [
      { "category_id": 3, "key": "hunting", "value_ids": [41] },
      { "category_id": 3, "key": "fishing", "value_ids": [42] }
    ] }
  ]
}

Origin fields:

Targets people by their home location - where a device originates - rather than by the places it visited. Each device’s home is resolved once per week and placed by point-in-polygon against the ZIP code and DMA boundaries, so every filter below narrows that resolved home location.

Its only filters are geographic: beyond the common signal_providers, dates and location, it defines no key of its own.

Its data is weekly - a device’s home is resolved once per week, and there is no day in the data - so a sub-week window cannot exist. Any dates are accepted, and the window is widened to the whole Monday-to-Sunday weeks it touches: Tuesday to Friday builds, and is stored as, that full week.

Origin data exists for a limited set of countries. Fetch the covered set from GET .../reference/common/countries?datasetType=Origin; a country outside it is rejected with 422.

FieldTypeRequiredDescription
signal_providersstring[]YesBID values identifying the signal source (at least one). From GET .../reference/common/signal-providers?dataType=Origin.
start_datestring (Y-m-d)YesWindow start. Widened back to the Monday of its week.
end_datestring (Y-m-d)YesWindow end, on or after start_date. Widened forward to the Sunday of its week.
location.countriesstring[]YesCountry codes (at least one) with Origin coverage. From GET .../reference/common/countries?datasetType=Origin.
location.statesstring[]NoState codes. From GET .../reference/common/states.
location.citiesstring[]NoCity names. From GET .../reference/common/cities.
location.dmasstring[]NoDMA labels exactly as returned by GET .../reference/common/dmas - the label string, never a numeric code.
location.zipcodesstring[]NoZIP codes. From GET .../reference/common/zipcodes.

An Origin dataset block:

{
  "type": "Origin",
  "start_date": "2026-08-31",
  "end_date": "2026-09-13",
  "signal_providers": ["BID001"],
  "location": {
    "countries": ["USA"],
    "states": ["CA"],
    "dmas": ["803 - Los Angeles, CA"]
  }
}

Refine

The optional per-dataset refine object refines the audience after it is built: devices are ranked by a behavioral metric and only a subset is kept - the top X% (percentile), everyone at or above a minimum event count (threshold), or the top N devices (rank). The refined audience keeps its full data shape; results_count and every downstream activation reflect the refined device set.

Refine is available on POI and Apps datasets, and only for single-dataset audiences - a refine block on any other dataset type, or on either dataset of a two-dataset audience, is rejected with 422.

Refine is a gated feature. It requires additional permissions that your Account Manager can enable for your account. If a refine block is present and the feature is not enabled, the request is rejected with 403 (never silently ignored). Omitting the block entirely requires nothing.

FieldTypeRequiredDescription
typestringYesHow the subset is selected: percentile (keep the top X% by the metric), threshold (keep devices with at least value events), or rank (keep the top value devices).
metricstringYesThe behavioral metric to rank devices by. POI: visit_count or visit_days. Apps: app_usage_count or app_usage_days.
valuenumberYesMeaning depends on type: the percentage to keep for percentile (greater than 0 and less than 100), the minimum event count for threshold, or the number of devices for rank (whole number, at least 1).
fraud_cutoff_percentileintegerNoDrop outlier devices above this activity percentile (1-100, e.g. 99) before the cut, so bot-like devices do not crowd out the subset you keep.
max_valueintegerNoUpper bound on the metric, threshold type only - keeps devices between value and max_value events. Rejected with 422 on the other types.

A refined POI dataset block:

{
  "type": "POI",
  "start_date": "2025-01-01",
  "end_date": "2025-03-31",
  "signal_providers": ["BID001"],
  "categories": [101],
  "location": { "countries": ["USA"] },
  "refine": {
    "type": "percentile",
    "metric": "visit_count",
    "value": 10,
    "fraud_cutoff_percentile": 99
  }
}

Crossvisitation

The optional top-level crossvisitation object narrows the audience to people who physically visited selected points of interest (POIs) within a date window and optional time-of-day and distance limits. It sits alongside datasets in the body and is available only when the request contains a POI dataset - a crossvisitation block on any other dataset type is rejected. When the block is present it carries the same requirements as a POI dataset: a date window (start_date / end_date), at least one country, and exactly one category. Like the frequency analyses, it is not available for some dataset combinations (see below).

Crossvisitation is a gated feature. It requires additional permissions that your Account Manager can enable for your account. If a crossvisitation block is present and the feature is not enabled, the request is rejected with 403 (never silently ignored). Omitting the block entirely requires nothing.

POI id sources. Pick the POI values from the read-only Dataset Types - POI catalogs: categories from GET .../reference/poi/categories, brands from GET .../reference/poi/brands, and locations from GET .../reference/poi/locations. (Creating and managing the underlying POI data itself is the separate My POI Data surface.)

FieldTypeRequiredDescription
start_datestring (Y-m-d)YesStart of the visitation window.
end_datestring (Y-m-d)YesEnd of the visitation window.
poi_levelbooleanNoResolve at the individual-POI level rather than the parent brand/category.
filtersstring[]NoQuality filters: any of anomalous_devices, gps_only, anomalous_pois.
categoriesinteger[]Yes (exactly one)A single POI category id (see above). The block requires exactly one category.
brandsinteger[]NoPOI brand ids (see above). With no brands (and no locations to take them from), the category expands to the brands available to your company; a category with none is rejected with 422.
locationsarrayNoPOI location identifiers, interpreted per location_identifier_type.
location_identifier_typestringNoHow locations are interpreted: none, location_id, external_id, store_id, placekey, h3_index, or h3_index_integer.
locationobjectcountries requiredGeographic scope { countries[], states[], cities[], dmas[], zipcodes[] } - countries needs at least one entry, the rest are optional. countries come from GET .../reference/common/countries.
time_limitsobjectNo{ process: boolean, start: 0-23, end: 0-23 }. Hour-of-day window applied only when process is true.
day_limitsobjectNo{ process: boolean, days: integer[] }. Day-of-week filter applied only when process is true; days use ISO numbering (1 = Monday .. 7 = Sunday).
distance_limitsobjectNo{ process: boolean, distance: number, unit: "miles" | "kilometers" }. Radius applied only when process is true.

Frequency analyses

The optional top-level analyses object runs the frequency analyses as the audience builds - the same toggles the Audience Manager offers. Each one stores, for every device in the audience, the number of distinct days it was seen on its dataset:

FieldTypeRequiredDescription
frequencybooleanNotrue runs the Visitation Frequency analysis: distinct days a device was seen at the POI dataset’s points of interest. Requires a POI dataset.
frequency_day_partbooleanNoThe Visitation Frequency by Day part sub-option: true buckets the Visitation Frequency by hour of the day instead of by visit day. Requires frequency: true, and is not available when the request holds an Apps dataset. A day-part audience cannot be previewed (hours are not visit counts); its activation applies the Day Part filter instead.
apps_frequencybooleanNotrue runs the Apps Frequency analysis: distinct days a device used the Apps dataset’s apps. Requires an Apps dataset.
web_frequencybooleanNotrue runs the Web Frequency analysis: distinct days a device visited the WebDomain dataset’s domains. Requires a WebDomain dataset; the activation preview supports it on single-dataset WebDomain audiences.

Send the flavour that matches your dataset when you may later want to activate only the devices seen on a minimum number of days (“2+ visits”). The analysis stores the distinct-day histogram that Preview Activation sums and that the freq_min / freq_max filter of Create Activation applies. An audience built without a frequency analysis cannot be previewed or filtered by frequency, and none can be added after the build. A two-dataset audience may carry more than one flavour, exactly as in the Audience Manager, but the preview only resolves an audience that carries exactly one.

The frequency analyses are a gated feature. They require additional permissions that your Account Manager can enable for your account. If any of them is true and the feature is not enabled, the request is rejected with 403 (never silently ignored). Omitting the object, or sending false, requires nothing.

Each flavour needs its dataset. frequency needs a POI block, apps_frequency an Apps block and web_frequency a WebDomain block: a flavour sent without its dataset is rejected with 422 naming it, and so is frequency_day_part without frequency or next to an Apps block. Any other key under analyses is rejected with 422 naming it.

Some datasets turn an analysis off. Cross visitation, Visitation Frequency and Apps Frequency are not available for some dataset types, or for some combinations of two datasets and an operator. The Audience Manager disables them there, and the API rejects them with 422 on crossvisitation, analyses.frequency or analyses.apps_frequency.

{
  "name": "Coffee shop visitors - January",
  "datasets": [
    {
      "type": "POI",
      "start_date": "2026-01-01",
      "end_date": "2026-01-31",
      "signal_providers": ["BID001"],
      "categories": [101],
      "location": { "countries": ["USA"] }
    }
  ],
  "analyses": { "frequency": true }
}

Data Stream Visualizations

An audience can generate a set of charts while it builds - visitation patterns, dwell time, demographics, CTV and web breakdowns and so on - which are then viewed in the Intuizi console on the audience’s Data Streams tab.

Opt in with the optional top-level datastreams array. It takes the same shape as the one on activations, so a client that already speaks one speaks both:

FieldTypeRequiredDescription
datastreams[].idintegerYesThe data stream id, from the reference catalog below.
datastreams[].visualizing_statusbooleanNotrue to generate this stream’s visualizations. Omitted or false means the stream is listed but not generated.
{
  "name": "Airport visitors - September",
  "datasets": [ { "type": "POI", "start_date": "2026-09-01", "end_date": "2026-09-10" } ],
  "datastreams": [
    { "id": 21, "visualizing_status": true },
    { "id": 14, "visualizing_status": true }
  ]
}

The rendered charts are not returned by this API. There is no endpoint that serves a visualization payload and no PDF export. datastreams controls what gets built, and the results are read in the console. While the charts are drawn, the audience reads 109 Visualizing data streams, before 105 and 104 Completed, so keep polling through it (see Status Codes).

Where the ids come from. List what you may use, scoped to the dataset you are about to build:

GET /api/v2/analyses/reference/common/datastream-visualizations?dataset_type=POI

The list is scoped to the caller: every public stream, plus the private ones assigned to your company. Streams without an active visualization are never listed. Each item carries id, slug, name, dataset_types and visual_count.

Mismatches are rejected, not dropped. A stream that does not apply to this audience’s dataset types is a 422 naming the stream and the types it does support - it is not silently ignored, because a stream built against the wrong dataset produces a broken chart rather than an empty one. An id you are not permitted to use is reported the same way as one that does not exist.

Cross Purchase

The optional top-level crosspurchase object runs the built audience against the affinity purchase data: the audience’s devices are matched into purchase transactions within your date window, scoped to the target categories, sub-categories and brands you select. It sits alongside datasets in the body. Because it runs on the finished audience, it is available with any dataset combination - one or two datasets, any type. The analysis results are surfaced in the Audience Manager alongside the audience.

Cross purchase is a gated feature. It requires additional permissions that your Account Manager can enable for your account. If a crosspurchase block is present and the feature is not enabled, the request is rejected with 403 (never silently ignored). Omitting the block entirely requires nothing.

Target value sources. Pick the target values from the read-only Transactions catalogs: target_categories from GET .../reference/affinity-transactions/categories, target_subcategories from GET .../reference/affinity-transactions/subcategories, and target_brands from GET .../reference/affinity-transactions/brands.

FieldTypeRequiredDescription
start_datestring (Y-m-d)YesStart of the purchase window.
end_datestring (Y-m-d)YesEnd of the purchase window. The range may cover at most 2 calendar months, and cannot run past the most recent fully-settled purchase week - the latest Sunday at least 11 days before today (UTC), which advances each Thursday. A later date is rejected with 422.
target_categoriesinteger[]At least one of target_categories / target_subcategories / target_brandsAffinity category ids to scope the analysis to.
target_subcategoriesstring[]At least one of target_categories / target_subcategories / target_brandsAffinity sub-category strings (verbatim values from the reference read).
target_brandsstring[]At least one of target_categories / target_subcategories / target_brandsAffinity brand id strings.

A crosspurchase block on a request body:

{
  "name": "Coffee visitors vs dining purchases",
  "datasets": [ { "type": "POI", "...": "..." } ],
  "crosspurchase": {
    "start_date": "2025-01-01",
    "end_date": "2025-02-28",
    "target_categories": [1],
    "target_brands": ["501"]
  }
}
Collect the ids and values these filters expect from the Dataset Types endpoints first, then pass them in here. Only WebDomain, CTV, POI and Origin read the full location block (POI and Origin also take location.dmas); Apps, Demographics, Deidentified and ProfileAttributes read location.countries only; AffinityTransactions is United States only and reads location.states, location.cities and location.zipcodes; Cohorts takes neither dates nor location, and Demographics takes no dates.

WebDomain windows roll: start_date must fall within the last 45 days of available data at the moment you send the request - substitute a current window for the dates below.

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/create" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: <UNIQUE_KEY>" \
  -d '{
    "name": "US auto-intenders on tech sites",
    "datasets": [
      {
        "type": "WebDomain",
        "start_date": "2026-07-01",
        "end_date": "2026-07-28",
        "signal_providers": ["BID001"],
        "iab_category_codes": [1, 4],
        "web_domains": ["example.com"],
        "device_types": ["Mobile"],
        "browsers": ["Chrome"],
        "languages": ["en"],
        "location": { "countries": ["USA"] }
      }
    ]
  }'

Response

The create response is the freshly queued audience (status Initiating, results_count 0). credits_charged is the credits the build was charged, or null unless your organization is billed in credits. The build’s price is known before you create it, from Quote Audience.

{
  "status": "success",
  "code": 201,
  "message": "Resource created successfully.",
  "data": [
    {
      "id": 88,
      "name": "US auto-intenders on tech sites",
      "status": { "id": 100, "name": "Initiating" },
      "is_cohort": false,
      "is_lookalike": false,
      "results_count": 0,
      "credits_charged": 4,
      "created_at": "2025-01-01 12:00:00"
    }
  ]
}

Idempotency

The optional Idempotency-Key header makes a create safe to retry. The key is caller-generated: any unique string up to 255 characters (a UUID v4 is typical), one per logical create - reuse it only when retrying that same request. The first request with a given key runs normally and the successful (2xx) response is cached per caller. A retry with the same key and the same body replays the stored response verbatim with an Idempotency-Replayed: true header, so no duplicate audience is created. A retry with the same key but a different body returns 409 Conflict. See Idempotency for the full replay semantics and the eight endpoints that honor the header.

List Audiences

GET /api/v2/analyses/audiences/index

Lists the audiences owned by the authenticated user’s company, most recent first, paginated.

Auth: bearer token + Accept: application/json. Rate limit: read bucket (120 requests/min).

Query parameters

FieldTypeRequiredDescription
per_pageintegerNoItems per page. Defaults to 25, capped at 100.
pageintegerNoPage number (standard pagination).
searchstringNoOptional free-text filter on the audience name (case-insensitive contains).
curl "https://console.intuizi.com/api/v2/analyses/audiences/index?per_page=25" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json"

Response

{
  "status": "success",
  "code": 200,
  "message": "Resources fetched successfully.",
  "data": {
    "items": [
      {
        "id": 88,
        "name": "Coffee Buyers NYC",
        "status": { "id": 104, "name": "Completed" },
        "is_cohort": false,
        "is_lookalike": false,
        "results_count": 482311,
        "credits_charged": null,
        "is_activation_allowed": true,
        "eligibility": {
          "allowed": true,
          "reasons": [],
          "metrics": {"unique_eids": 482311, "unique_scids": null, "eid_scid_ratio": null, "is_affinity": false}
        },
        "totals": {"uniques": 482311, "visits": 1203440, "avg_uniques": 16077, "avg_visits": 40114},
        "source_audience": null,
        "created_by": { "name": "Jane Doe", "email": "jane.doe@acme.example" },
        "project": { "id": 4, "name": "Retail 2025" },
        "operator": "AND",
        "dataset": [
          { "analysis_type": "WebDomain", "start_date": "01/01/2025", "end_date": "03/31/2025" },
          { "analysis_type": "Apps", "start_date": "01/01/2025", "end_date": "03/31/2025" }
        ],
        "created_at": "2025-01-01 12:00:00",
        "updated_at": "2025-01-10 09:30:00"
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page": 25,
      "total": 1,
      "last_page": 1
    }
  }
}

Get Audience

GET /api/v2/analyses/audiences/{id}

Retrieves a single audience by id.

The same audience is also readable at GET /api/v2/my-data/audiences/{id} for backward compatibility. The two responses differ by two fields: this /analyses/audiences/{id} read includes the operator field (the two-dataset combination mode) and totals (the flat metric whitelist), while the /my-data/audiences/{id} read has neither - it returns eligibility but not totals. When the build recorded a month of the demographics data, this read also carries demographics_stream_vintage, and dataset[].vintage on a Demographics entry. The /my-data/audiences/{id} read carries neither.

Auth: bearer token + Accept: application/json. Rate limit: read bucket (120 requests/min).

Path parameters

FieldTypeRequiredDescription
idintegerYesThe audience id to fetch.
curl "https://console.intuizi.com/api/v2/analyses/audiences/88" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json"

Response

created_by is always an object (empty {} when no creator resolves), never null. is_lookalike is true when this audience is the result of a Lookalike Model run - false for every ordinarily-built audience. is_activation_allowed is the verdict of the activation eligibility engine - the same gates the console UI, the activation endpoints and the scheduler apply:

Gate (reasons[].code)Rule
minimum_devicesat least 500 unique devices (1,000 for lookalike outputs)
affinity_device_coverageaudiences containing AffinityTransactions data: unique EIDs must exceed 2x unique SCIDs
scid_count_unavailableAffinity audience built before SCID totals were stored - duplicate and rebuild it
audience_expireda notice, not a block: standard audiences whose latest dataset end date is older than 90 days cannot be delivered as MAIDs or IPs (mapping keys are retained for 90 days). blocks_identifiers = ["MAID", "IP"]; activation with an EID / SCID / HEM pricing model is allowed, a MAID or IP pricing model is rejected with 422. Affinity, lookalikes and cohorts are exempt

When blocked, eligibility.reasons[] carries the code, a human message, the threshold used and the observed aggregate counts (for Affinity: unique_eids, unique_scids, eid_scid_ratio, required_unique_eids). eligibility.notices[] has the same shape plus blocks_identifiers: rules that only apply to some output identifiers. They leave allowed true; the activation create rejects a pricing model whose identifiers fall in blocks_identifiers. eligibility.metrics always returns unique_eids, unique_scids (null when the audience has no Affinity data), eid_scid_ratio and is_affinity. totals is the same metric whitelist the console shows (keys vary by dataset type, e.g. uniques, scids, transactions, amount, visits, signals, unique_eips). Only aggregates are returned - never raw SCIDs or device identifiers. dataset is a trimmed summary with one entry per dataset block (one or two entries). A Cohorts entry also carries cohort, the cohort it reads. A POI entry built in attributes mode also carries poi_selection_mode and poi_attributes, the attribute pick it was built from, in the shape POI attributes describes. A POI entry built from categories, brands and locations carries neither. A Demographics entry also carries vintage, the monthly vintage it read (YYYY-MM). operator carries the combination mode for a two-dataset audience (AND, OR, or NOTIN), and is null for a single-dataset audience.

demographics_stream_vintage names the month an audience’s demographics data stream reads (YYYY-MM). It is the month the build recorded, so an audience that is not a Demographics audience carries it too, and a Demographics audience shows the same month in dataset[].vintage. The field is read-only. When the build recorded no month, as for an audience made before 2026-10-24, both are absent.

"dataset": [
  { "analysis_type": "Demographics", "start_date": "", "end_date": "", "vintage": "2026-09" }
],
"demographics_stream_vintage": "2026-09",

Two fields say what an audience was built from, so you can follow it by id instead of by name:

  • source_audience - the seed audience a Lookalike Model was built from (the source_audience_id it was created with). null for every other audience.
  • dataset[].cohort - on a Cohorts entry, the cohort it reads. Get Cohort then says how that cohort was created (source) and, for one built from an audience, which audience (source_audience).

Each is { "id", "name" }, or null when the audience or cohort it points to has been deleted or does not belong to your company - so an id you get here is always one you can fetch.

"source_audience": null,
"operator": "AND",
"dataset": [
  { "analysis_type": "Cohorts", "start_date": "", "end_date": "", "cohort": { "id": 42, "name": "Loyalty program members" } },
  { "analysis_type": "CTV", "start_date": "01/27/2026", "end_date": "07/27/2026" }
]

A POI entry in attributes mode:

"dataset": [
  {
    "analysis_type": "POI",
    "start_date": "09/01/2026",
    "end_date": "09/28/2026",
    "poi_selection_mode": "attributes",
    "poi_attributes": [{ "attribute_id": 3, "value_ids": [9] }]
  }
]

The read returns the datasets and operator only. A crossvisitation block or an analyses object sent on create is not echoed back here - they live outside the public dataset shape and do not round-trip on read.

credits_charged is the credits the audience’s run was charged, after any refund: a run that fails, is cancelled, or completes with no results reads 0. It is null unless your organization is billed in credits, and for a run from before it was.

recipe_hash and normalized_payload are the canonical recipe stamped at create time: the same hash a prior Estimate Audience Size call returned for the identical payload, and the normalized body it was computed from. Both are null for audiences created in the console UI or before this field existed.

{
  "status": "success",
  "code": 200,
  "message": "Resource fetched successfully.",
  "data": [
    {
      "id": 88,
      "name": "Coffee Buyers NYC",
      "status": { "id": 104, "name": "Completed" },
      "is_cohort": false,
      "is_lookalike": false,
      "results_count": 482311,
      "credits_charged": null,
      "is_activation_allowed": true,
      "eligibility": {
        "allowed": true,
        "reasons": [],
        "metrics": {"unique_eids": 482311, "unique_scids": null, "eid_scid_ratio": null, "is_affinity": false}
      },
      "totals": {"uniques": 482311, "visits": 1203440, "avg_uniques": 16077, "avg_visits": 40114},
      "source_audience": null,
      "created_by": { "name": "Jane Doe", "email": "jane.doe@acme.example" },
      "project": { "id": 4, "name": "Retail 2025" },
      "operator": "AND",
      "dataset": [
        { "analysis_type": "WebDomain", "start_date": "01/01/2025", "end_date": "03/31/2025" },
        { "analysis_type": "Apps", "start_date": "01/01/2025", "end_date": "03/31/2025" }
      ],
      "recipe_hash": null,
      "normalized_payload": null,
      "created_at": "2025-01-01 12:00:00",
      "updated_at": "2025-01-10 09:30:00"
    }
  ]
}

Estimate Audience Size

POST /api/v2/analyses/audiences/estimate

Answers “how many devices would this audience hold?” without creating an audience. The request body is identical to Create Audience - same fields, same validation, same 422 responses - and the estimate runs the same build pipeline, so the numbers match what a create with this payload would produce. Nothing appears in the Audience Manager and no export, cohort, schedule, or activation is triggered.

The estimate is asynchronous: this call answers at once with status: "pending", and how long the answer takes depends on the datasets. Read it with GET /api/v2/analyses/audiences/estimate/{id} until status is completed, blocked, or failed. Add wait to that read and it waits up to 15 seconds for the answer instead of answering at once.

An estimate is metered on the bytes it scans, which count toward your company’s monthly data-scan limit, reported under the estimate operation type on Usage. When the limit has been reached the call is refused with the same 422 a create receives, before anything is queued.

The response carries recipe_hash: a canonical hash of the datasets + operator recipe. Creating an audience with the identical payload stamps the same hash on the audience (returned by Get Audience), proving the created audience matches the estimated recipe. The name and project_id are not part of the hash - renaming does not invalidate an estimate.

Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min). Optional: Idempotency-Key header (see below).

Body

The body of Create Audience, unchanged: name and datasets required, operator required for two dataset blocks, optional project_id and analyses (checked exactly as on create; an estimate never runs an analysis, and the flags change neither the estimate nor its recipe_hash). Every dataset block rule on this page applies verbatim.

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/estimate" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Coffee Buyers NYC (probe)",
    "datasets": [{ "type": "Cohorts", "cohort_id": 7 }]
  }'

Response

201 with the queued estimate. estimate is null until the run completes.

{
  "status": "success",
  "code": 201,
  "message": "Resource created successfully.",
  "data": [
    {
      "id": 12,
      "status": "pending",
      "name": "Coffee Buyers NYC (probe)",
      "recipe_hash": "sha256:5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
      "normalized_payload": {
        "name": "Coffee Buyers NYC (probe)",
        "operator": "Single",
        "datasets": [{ "cohort_id": 7, "type": "Cohorts" }]
      },
      "estimate": null,
      "status_message": null,
      "info": null,
      "project_id": null,
      "created_at": "2026-08-24 12:00:00",
      "updated_at": "2026-08-24 12:00:00"
    }
  ]
}

Get Audience Estimate

GET /api/v2/analyses/audiences/estimate/{id}

Retrieves one size estimate with its status and, once completed, the numbers.

status walks pending -> processing -> one of three terminal states: completed (the estimate object holds the numbers), blocked (the recipe cannot be answered - estimate.blocked is { code, reason }, e.g. OUT_OF_COVERAGE with the available date window), or failed (status_message explains).

With wait, the read waits for the answer: it answers as soon as status is completed, blocked, or failed, and at the latest wait seconds after the request arrived, with the estimate as it is then. A pending or processing answer means the estimate is still running: read it again. At most 5 calls of your organization wait at once (reads with wait and the MCP estimate tools together), and at most 25 across all organizations. Another call answers at once, as it would without wait. Only one call waits per estimate: a newer call for the same estimate takes the wait over, and the older one answers at once. So a read that answers pending well before its wait has passed could not wait: pause before the next read, as you would between polls (for example 5 seconds).

On completed, estimate carries: uniques (approximate distinct device count - the same counting method real audience totals use, with a standard error of about 2.3%), visits / signals / unique_eips where the dataset types produce them, the full totals row a create would have shown, providers (signal providers present in the result), as_of (when the numbers were computed), query_metrics (runtime and data scanned), method and exact (how the count was produced), vintage (a Demographics dataset only: the monthly vintage its numbers come from, YYYY-MM), and the echoed canonical recipe_hash.

info is null, or a sentence about what the number leaves out. When a POI dataset of the recipe is in attributes mode, the estimate does not account for the event time windows of the selected attributes, and the built audience will be materially smaller. See POI attributes.

Auth: bearer token + Accept: application/json. Rate limit: read bucket (120 requests/min).

Path parameters

FieldTypeRequiredDescription
idintegerYesThe estimate id from Estimate Audience Size.

Query parameters

FieldTypeRequiredDescription
waitintegerNoSeconds the read may wait for the answer, from 1 to 15, counted from the moment the request arrives. Any other value is refused (422). Without it, the read answers at once.
curl "https://console.intuizi.com/api/v2/analyses/audiences/estimate/12?wait=15" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json"

Response

{
  "status": "success",
  "code": 200,
  "message": "Resource fetched successfully.",
  "data": [
    {
      "id": 12,
      "status": "completed",
      "name": "Coffee Buyers NYC (probe)",
      "recipe_hash": "sha256:5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
      "normalized_payload": {
        "name": "Coffee Buyers NYC (probe)",
        "operator": "Single",
        "datasets": [{ "cohort_id": 7, "type": "Cohorts" }]
      },
      "estimate": {
        "uniques": 482311,
        "visits": 1203440,
        "signals": null,
        "unique_eips": null,
        "number_of_days": 30,
        "method": "approx_distinct",
        "exact": false,
        "as_of": "2026-08-24T12:34:56Z",
        "providers": ["<provider-bid>"],
        "query_metrics": { "total_queries": 9, "total_data_scanned_bytes": 1073741824, "total_runtime_ms": 412000 },
        "canonical_hash": "sha256:5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
        "totals": { "uniques": 482311, "visits": 1203440 },
        "blocked": null
      },
      "status_message": null,
      "info": null,
      "project_id": null,
      "created_at": "2026-08-24 12:00:00",
      "updated_at": "2026-08-24 12:07:00"
    }
  ]
}

A blocked estimate keeps status: "blocked" and explains itself:

{
  "estimate": {
    "uniques": null,
    "blocked": {
      "code": "OUT_OF_COVERAGE",
      "reason": "No POI signal data is available for 2020-01-01..2020-02-01. POI data is available 2024-01 to 2026-07. Adjust the date range to overlap the available window."
    }
  }
}

Quote Audience

POST /api/v2/analyses/audiences/quote

Returns the price in credits of the audience a payload would create, without creating it. The body is identical to Create Audience, with the same fields, the same validation, and the same 422 responses. Nothing is created, nothing is queued, and the quote is free: it counts toward neither your build budget nor your data-scan limit.

credits is what Create Audience charges for the same payload, as long as the credit rates stay the same. Quote before a create to show the price and the credits left after the run.

Only for an organization billed in credits. Any other organization receives 403 before the body is validated. See Credits.

Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min).

Body

The body of Create Audience, unchanged. Every dataset block rule on this page applies verbatim.

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/quote" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Mobile gamers US",
    "datasets": [{
      "type": "Apps",
      "start_date": "2026-09-01",
      "end_date": "2026-09-30",
      "signal_providers": ["BID001"],
      "categories": [1],
      "location": { "countries": ["USA"] }
    }]
  }'

Response

A month of one Apps category comes to less than one credit, so the audience costs the minimum of 1 and its line carries no credits.

{
  "status": "success",
  "code": 200,
  "message": "Resource fetched successfully.",
  "data": [
    {
      "run_type": "audience",
      "credits": 1,
      "minimum": 1,
      "minimum_applies": true,
      "lines": [
        { "dataset": "Apps", "days": 30, "credits": null }
      ],
      "not_charged": [],
      "available": 4905,
      "available_after": 4904
    }
  ]
}

Response fields

FieldTypeDescription
run_typestringaudience.
creditsintegerThe price of the run in credits.
minimumintegerThe least a run of this type costs.
minimum_appliesbooleantrue when the datasets add up to less than minimum, so the run costs minimum.
linesarrayThe price split over the run’s datasets: dataset (the dataset type, or CrossPurchase for a Cross Purchase), days (the length of its date window, null for a dataset without one), and credits. The credits of the lines add up to the price, and they are null when minimum_applies is true.
not_chargedarrayDataset types of the run that carry no credits.
availableintegerYour organization’s credits now.
available_afterintegerYour organization’s credits after this run.

Delete Audience

POST /api/v2/analyses/audiences/delete-by-id

Soft-deletes an audience and queues the downstream cleanup.

Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min).

Body

FieldTypeRequiredDescription
idintegerYesThe audience id to delete. Must be owned by your company.
curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/delete-by-id" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "id": 88 }'

Response

{
  "status": "success",
  "code": 200,
  "message": "Resource deleted successfully.",
  "data": []
}

Create Lookalike Audience

POST /api/v2/analyses/audiences/create-lookalike

Builds a Lookalike Model from a completed seed audience. The result is a new audience (is_lookalike: true) that goes through the normal polling lifecycle - poll GET /api/v2/analyses/audiences/{id} for its status. Lookalike Models are a gated feature: accounts without the capability receive 403 - it requires additional permissions which need to be approved by your Account Manager. The seed audience must be a Completed audience owned by your company, must not itself be a lookalike, and must hold at least 1,000 devices by default.

Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min by default). Optional: Idempotency-Key header (see Idempotency).

Body

FieldTypeRequiredDescription
namestringYesName of the Lookalike Model and its result audience.
source_audience_idintegerYesThe seed audience id this model learns from. Must be owned by your company, Completed, not itself a lookalike, and hold at least 1,000 devices by default.
configobjectYesModel configuration - see the fields below.
config.target_sizeintegerYesHow many devices the output audience should contain. 1 to 4,000,000 by default.
config.geo.countriesstring[]YesCountries the candidate universe is restricted to (at least one). Candidates come from Origin data, which covers only some countries, and the create accepts any country code: a run in which none of the countries has Origin data fails after it is created, and when only some do, the others are left out of the model without an error. Start from the Origin list, GET .../reference/common/countries?datasetType=Origin.
config.geo.statesstring[]NoStates the candidate universe is restricted to.
config.signalsstring[]YesData families the model learns from (at least one): poi, apps, demographics, transactions, profile_attributes. They do not all describe the same thing - see Data families below. (web and ctv are withdrawn; requests naming them are rejected with 422.)
config.exclude_seed_devicesbooleanYesWhen true, the seed audience’s own devices are removed from the output audience.
config.contrast_audience_idintegerNoA second completed audience used as negative examples the model contrasts against. Must belong to your company, be Completed, not be a lookalike, and differ from source_audience_id. Omit for an automatic geo-matched sample.
config.expand_eidsbooleanYesWhen true, each output device is expanded across its linked identifiers.

Data families

The families differ in what they observe and over what period. Both matter when you read a model’s results, so they are worth knowing before you choose.

FamilyWhat it observesGrainWindow
poiPhysical visits to placesDeviceLast full calendar month
appsMobile app usageDeviceLast full calendar month
demographicsAge, income, gender, marital statusDeviceCurrent state, no window
transactionsCard transaction behaviourDeviceOne finalized calendar month
profile_attributesHousehold/person profile of the device’s ONE primary person, read from two catalogs at once: the AA consumer layer (household income and net worth, investments, home ownership, dwelling, education, generation, marital status, household composition, occupation) and the Vision Marketing layer (age band, household size, income band, home ownership and value, education, children, lifestyle flags) - one person per deviceDeviceCurrent state, latest quarterly profile refresh
webWeb browsing (withdrawn; models built before 2026-08-29 still report it)HouseholdTrailing 7 complete days

web is household-derived, which is the one asymmetry to keep in mind. Browsing is observed against a household’s IP address and attributed to the devices in that household, capped at the few devices most associated with the address. A device’s web activity therefore means “browsing seen at this device’s household”, not “browsing done by this device”. Every other family is observed on the device itself.

web also reads a trailing 7 days rather than a calendar month, because browsing is a recency signal. Two families in one model can describe different periods.

Selecting more families is not automatically better. A family only helps when the seed audience’s own devices are covered by it, and coverage varies a great deal between audiences. The completed model reports how much of each cohort each family actually reached, so you can see what a given run drew on.

Reading a completed model back returns a feature_families object recording exactly that:

"feature_families": {
  "registry_version": "2026-08-27.1",
  "included": [
    {
      "name": "web",
      "period": "2026-08-21..2026-08-27",
      "coverage": {
        "seed": 28.85,
        "candidates": 60.2,
        "devices_with_signal": 978047
      }
    }
  ]
}

period is the exact date range the family read, which is what makes a run reproducible for families whose window moves. Families measured over a fixed calendar month omit it - the table above already says what window each family uses.

coverage reports the share of your seed devices and of the candidates (the pool the model scored) that carried the family’s signal. The percentages are the useful part when a model underperforms: a family that reached only a small share of your seed had little to learn from, whatever its coverage of the wider candidate pool. feature_families is null for audiences that are not lookalike models, and for models created before this was recorded.

The read also names the seed the model was built from, so you can read the seed back with Get Audience - for example to see which datasets and date windows it holds:

"is_lookalike": true,
"source_audience": { "id": 14988, "name": "Coffee Buyers NYC" }

| notification | boolean | No | Whether to send an email on completion. Defaults to true. |

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/create-lookalike" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: <UNIQUE_KEY>" \
  -d '{
    "name": "API Lookalike",
    "source_audience_id": 14988,
    "config": {
      "target_size": 100000,
      "geo": { "countries": ["USA"], "states": [] },
      "signals": ["poi", "apps"],
      "exclude_seed_devices": true,
      "contrast_audience_id": null,
      "expand_eids": false
    }
  }'

Response

The create response is the freshly queued result audience (status Initiating). credits_charged is the credits the run was charged, or null unless your organization is billed in credits. The price is known before you create it, from Quote Lookalike.

{
  "status": "success",
  "code": 201,
  "message": "Resource created successfully.",
  "data": [
    {
      "id": 15021,
      "name": "API Lookalike",
      "status": { "id": 100, "name": "Initiating" },
      "is_lookalike": true,
      "source_audience_id": 14988,
      "credits_charged": 120
    }
  ]
}

Cancel Lookalike

POST /api/v2/analyses/audiences/cancel-lookalike

Requests cooperative cancellation of an in-flight Lookalike Model run: the job checks the cancellation flag at its next checkpoint and stops there rather than being killed immediately. A finished run cannot be cancelled. Until the run reaches that checkpoint the audience keeps reading 108 Modeling. When it stops, the audience ends at 400 Error, which is final and never reaches 104, and an audience.failed webhook is sent. Audience Manager shows the status as Cancelled on request. A cancel that arrives once the result is already being published is ignored, and the run completes at 104.

Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min by default).

Body

FieldTypeRequiredDescription
idintegerYesThe lookalike audience id to cancel - the id returned by Create Lookalike Audience.
curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/cancel-lookalike" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "id": 15021 }'

Response

{
  "status": "success",
  "code": 200,
  "message": "Cancellation requested.",
  "data": []
}

Quote Lookalike

POST /api/v2/analyses/audiences/quote-lookalike

Returns the price in credits of the lookalike a payload would build, without building it. The body is identical to Create Lookalike Audience, with the same validation and the same 403 and 422 responses. A lookalike costs the same for any size. Nothing is created, and the quote is free.

Only for an organization billed in credits. Any other organization receives 403 before the body is validated. See Credits.

Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min).

Body

The body of Create Lookalike Audience, unchanged.

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/quote-lookalike" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "API Lookalike",
    "source_audience_id": 14988,
    "config": {
      "target_size": 100000,
      "geo": { "countries": ["USA"] },
      "signals": ["poi", "apps"],
      "exclude_seed_devices": true,
      "expand_eids": false
    }
  }'

Response

The fields of Quote Audience, with run_type lookalike and no lines.

{
  "status": "success",
  "code": 200,
  "message": "Resource fetched successfully.",
  "data": [
    {
      "run_type": "lookalike",
      "credits": 120,
      "minimum": 25,
      "minimum_applies": false,
      "lines": [],
      "not_charged": [],
      "available": 1000,
      "available_after": 880
    }
  ]
}