Skip to content

Micro-batch synchronous match

POST
/v1/match
curl --request POST \
--url https://api.infiniteaudience.ai/v1/match \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "records": [ { "email": "[email protected]", "phone": "example", "email_sha256": "example", "phone_sha256": "example", "full_name": "example", "first_name": "example", "middle_name": "example", "last_name": "example", "name_suffix": "example", "emails": [ "[email protected]" ], "emails_sha256": [ "example" ], "phones": [ "example" ], "phones_sha256": [ "example" ], "addresses": [ { "address_1": "example", "address_2": "example", "city": "example", "state": "example", "zip": "example" } ], "address_1": "example", "address_2": "example", "city": "example", "state": "example", "zip": "example", "dob": "example", "iag_person_id": "example" } ], "field_list": [ "example" ], "template_id": "example", "match_level": [ "I" ], "create_scratch_segment": false }'

Synchronous inline matching for small record sets (up to 100 records). Submit your records and the list of attributes you want appended, and receive matched results immediately.

Person IDs: A real match returns a tier 01 iag_person_id: a stable, non-reversible ID salted for your organization. The same real identity is consistent inside your organization and different in every other organization. An unmatched row may receive a derived tier 0205 ID from its own submitted signals. That derived value is a continuity label—not a graph match, licensed identity, refresh key, or resolved audience member. Use the match metadata and match_count, not ID presence alone, to identify real matches.

Billing: Usage is emitted per matched record only (unmatched records are free). The platform reserves a conservative ceiling and durably queues finalized usage for billing rather than debiting a local balance field. amount_charged is the conservative USD estimate accepted for provider delivery; it is not a finalized invoice amount.

Records array: one output record per input record — matched or not. Unmatched rows carry match_level/match_type/match_confidence: null and a best-effort iag_person_id derived from the row’s own identity signals (or null if none qualified) — only real matches are billed. Use match_count vs record_count to distinguish real matches from derived-only rows.

For datasets larger than 100 records, use POST /v1/match/file instead. Upload, analysis, and async matching are free until egress. The first successful delivery involving that file-match run emits its one aggregate match usage set together with normal delivery usage.

Retries: pass an Idempotency-Key header to make a retried call safe to repeat without a duplicate charge. The same key with an identical body returns the original response verbatim, at zero additional charge (honored for 24 hours); the same key with a different body is rejected with 409 IDEMPOTENCY_KEY_CONFLICT. Without this header, every call reserves and charges independently — recommended for any client that may retry on timeout. Requires ‘purchase’ scope.

Idempotency-Key
string
>= 8 characters <= 160 characters /^[A-Za-z0-9][A-Za-z0-9._:-]{7,159}$/

Optional. Makes a retried call safe to repeat — see the endpoint description’s Retries section.

Media typeapplication/json
object
records
required

Records to match (max 100). Each object should include at least one identity signal: email (or email_sha256), phone (or phone_sha256), full_name (or first_name+last_name), or address attributes (address_1, city, state, zip). All attributes are optional — include only what you have. Any extra attributes you include are echoed back unchanged.

Array<object>
>= 1 items <= 100 items
object
email

Primary email address.

string format: email
phone

Phone number in any standard format.

string
email_sha256

Lowercase-hex SHA-256 hash of a normalized email address, for clients whose own systems never expose raw email addresses (e.g. data co-op / clean-room partners using the same hashing convention). The platform passes this through without re-hashing. Do not submit an already-hashed value in email — it would be hashed a second time and never match.

string
phone_sha256

Lowercase-hex SHA-256 hash of a normalized phone number. Same pre-hashed semantics as email_sha256.

string
full_name

Optional convenience field. When provided (and first_name / last_name are absent), the engine parses this into first_name, middle_name, last_name, and name_suffix automatically. The parsed components are used for matching; the original full_name value is echoed back unchanged.

string
first_name
string
middle_name
string
last_name
string
name_suffix

Generational suffix (e.g. JR, SR, III).

string
emails

Additional raw email addresses beyond email, tried as extra match permutations.

Array<string>
emails_sha256

Pre-hashed counterpart to emails.

Array<string>
phones

Additional raw phone numbers beyond phone, tried as extra match permutations.

Array<string>
phones_sha256

Pre-hashed counterpart to phones.

Array<string>
addresses

Additional addresses beyond address_1/city/state/zip, tried as extra match permutations.

Array<object>
object
address_1
string
address_2
string
city
string
state
string
zip
string
address_1

Street address line 1.

string
address_2

Street address line 2 (apt, suite, etc.).

string
city
string
state

Two-letter US state code.

string
zip

5- or 9-digit ZIP code.

string
dob

Date of birth — any parseable date format (e.g. YYYY-MM-DD).

string
iag_person_id

A previously-issued tier 01 person ID from this organization (format CLIENTCODE_TIER_HASH, e.g. returned by a prior call to this endpoint or a matched-segment delivery). A valid ID can go directly to refresh without repeating identity matching. A foreign, malformed, or derived tier 0205 ID cannot be used for direct refresh; the row falls back to its other identity signals, if any.

string
key
additional properties
any
field_list

Enrichment attribute names to append on match — every attribute must be a valid audience attribute (see GET /v1/catalog/fields). Mutually exclusive with template_id. If neither is provided, the Standard IAG attribute set (all attributes with product_usage containing audience) is used. Individual-level attributes (e.g. age, gender) come back null per row when that row’s match_level doesn’t qualify for individual-level data (see match_level below) — this is expected, not an error. Request only the attributes valid for the match_level(s) you accept if you want to avoid nulls.

Array<string>
template_id

Pre-built enrichment bundle ID. Only standard_iag is valid (see templates[] in GET /v1/catalog/fields). Mutually exclusive with field_list. If neither is provided, the Standard IAG attribute set is used.

string
match_level

Filter results to specific match levels. I = individual, H = household, D = digital (email/phone only match), S = spatial (nearby-address proximity match), A = address-level. Defaults to all 5 levels (['I', 'H', 'D', 'S', 'A']) if omitted. Restricting this also restricts which attributes come back populated — see field_list above.

Array<string>
Allowed values: I H D S A
create_scratch_segment

When true, materialize the real matched identities from this completed response as a reusable 24-hour matched scratch segment. This reuses the already-paid result and does not run matching or billing again. The response’s scratch_segment is null when no identities matched.

boolean

Matched records.

Media typeapplication/json
object
records

One output record per input record — matched or not. Matched rows carry a resolved iag_person_id, match_level/match_type/match_confidence, and (when requested) enrichment attributes. Unmatched rows carry match_level/match_type/match_confidence: null and a best-effort derived iag_person_id (see iag_person_id below) computed from the row’s own identity signals, or null if none qualified. Use match_count vs record_count to distinguish real matches from derived-only rows — only real matches are billed. Input attributes are echoed back unchanged; resolved enrichment attributes are added alongside them.

Array<object>
object
row_id

Correlates a matched output row back to its position in the submitted records array (1-indexed as a string when the input didn’t supply one).

string
email
string format: email
phone
string
email_sha256

Present when the request included email_sha256, echoed back unchanged.

string
phone_sha256

Present when the request included phone_sha256, echoed back unchanged.

string
full_name

Present when the request included full_name; the original submitted value, echoed back unchanged. Null when the request did not include one.

string | null
first_name
string
middle_name

Echoed back when present on input, or populated from full_name parsing when the request supplied full_name without first_name/last_name.

string
last_name
string
name_suffix

Generational suffix (e.g. JR, SR, III). Echoed back when present on input, or populated from full_name/last_name parsing.

string
address_1
string
address_2
string
city
string
state
string
zip
string
dob
string
emails

Additional raw emails from the request, echoed back unchanged.

Array<string>
emails_sha256

Additional pre-hashed emails from the request, echoed back unchanged.

Array<string>
phones

Additional raw phones from the request, echoed back unchanged.

Array<string>
phones_sha256

Additional pre-hashed phones from the request, echoed back unchanged.

Array<string>
addresses

Additional addresses from the request, echoed back unchanged.

Array<object>
object
key
additional properties
any
iag_person_id

Org-scoped, non-reversible identifier — never a raw internal id. The same real identity receives a consistent tier-01 ID within one organization and a different ID in every other organization. Format: CLIENTCODE_TIER_HASH. TIER is 01 for a real graph match (see match_level/match_type/match_confidence) or 0205 for a best-effort id deterministically derived from the row’s own identity signals when it did NOT match (name+address, email, name+phone, or name+zip, in that priority order). null when the row is unmatched and no identity signal qualified for derivation. A tier-01 id may be resubmitted as input (see iag_person_id on the request schema above) to re-resolve the same match; derived (tier 02–05) ids cannot be.

string | null
match_level

Granularity of the match. I = individual, H = household, A = address-level, S = spatial (nearby-address proximity match), D = digital (email/phone only match). Present only on matched records (null, not a derived value, on unmatched rows).

string
Allowed values: I H A S D
match_type

Matching strategy that produced the result (e.g. graph_name_email_match, vector_name_address_match, spatial_match). Present only on matched records.

string
match_confidence

Confidence score for the match (0–1). Present only on matched records.

number
enrichment

Requested enrichment attributes keyed by column name (e.g. acs_housing_units, usda_median_hh_income_2023). Only present when field_list or template_id was provided and the record matched.

object
key
additional properties
any
resolution_action

Customer-facing resolution outcome for this row, orthogonal to license_action below. resolved = an organic PII match (no id submitted for this row). refreshed = the caller submitted a valid iag_person_id for this row (direct Refresh) and the platform cannot confirm this identity’s enrichment is unchanged since it was last delivered — whether or not this call was also billed (see license_action). current = same as refreshed, but the platform’s data-release registry (see GET /v1/data-release) confirms that the identity remains on the same refresh-major release as its last delivery. The response contains current-mart values for the fields requested in this call; the requested field set may differ from an earlier call. unmatched = no real match, never licensed. identity_unavailable = a previously valid seeded id no longer resolves to deliverable data (deletion, suppression, retirement, or a split graph identity — the underlying reason is deliberately never distinguished here); the record otherwise looks exactly like an ordinary unmatched row. The platform’s full vocabulary also defines invalid, reserved for a reject-the-row policy this API does not yet implement — it is not returned today, but a client should not treat its future appearance as a breaking change.

string
Allowed values: resolved refreshed current unmatched identity_unavailable
license_action

License effect of this row’s resolution, independent of resolution_action — a row can simultaneously be resolution_action: resolved and license_action: renewed. none = no license period was started or renewed this call (already active, unmatched, or identity_unavailable). started = this identity had no prior confirmed license claim and one was started. renewed = a prior confirmed claim existed (even if expired) and the period was renewed.

string
Allowed values: none started renewed
license_expires_at

ISO-8601 timestamp the identity’s current license period expires. Populated when reusing an existing active license (license_action: none) and for a row starting or renewing a period in this same call (license_action: started or renewed — a fixed 12 months from now). Null when no license period applies (e.g. an unmatched or identity_unavailable row).

string | null
key
additional properties
any
record_count

Total number of input records processed.

integer
match_count

Number of records that resolved to a known identity.

integer
billable_identity_count

Number of unique real identities charged for this operation, deduplicated by internal identity rather than row count. Duplicate rows resolving to the same identity are counted once; a resubmitted iag_person_id for an identity with an active license contributes zero (free Refresh). May be less than or equal to match_count, and is the value actually billed — match_count describes matching, not billing.

integer
match_rate

Match_count / record_count.

number
amount_charged

Conservative platform estimate in USD for usage accepted for delivery (e.g. 0.30 means $0.30 estimated). This legacy-named field is not a local credit debit or finalized invoice amount; unmatched records emit no usage.

number
run_id

Unique identifier for this match run. Pass to GET /v1/match/runs/{runId} to retrieve full details or audit this call later. Requires ‘discovery’ scope to read.

string
scratch_segment
One of:
object
segment_id
required
string
name
required
string
record_count
required
integer
>= 1
ephemeral
required
boolean
expires_at
required
string format: date-time
scratch_segment_error

Present only when matching and billing succeeded but the optional, non-billable scratch-segment materialization failed.

object
code
required
string
Allowed value: SCRATCH_SEGMENT_CREATION_FAILED
message
required
string
Example
{
"records": [
{
"match_level": "I",
"resolution_action": "resolved",
"license_action": "none"
}
],
"scratch_segment": {
"ephemeral": true
},
"scratch_segment_error": {
"code": "SCRATCH_SEGMENT_CREATION_FAILED"
}
}

Invalid request — malformed body, missing required attribute, or failed validation. See error and message for details.

Media typeapplication/json
object
error
required

Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.

string
message

Human-readable explanation of the error.

string
code

Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.

string
request_id

Opaque support/debug identifier when available.

string
key
additional properties
any
Examples
Examplevalidation_error
{
"error": "Bad Request",
"code": "MISSING_SEGMENTS",
"message": "segment_ids is required for filter audiences."
}

Missing or invalid Bearer token. Obtain one via POST /v1/auth/token. When a token was supplied but rejected, code distinguishes TOKEN_EXPIRED (the token’s lifetime has passed — request a new one via POST /v1/auth/token and retry) from TOKEN_INVALID (malformed or revoked — re-authenticate).

Media typeapplication/json
object
error
required

Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.

string
message

Human-readable explanation of the error.

string
code

Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.

string
request_id

Opaque support/debug identifier when available.

string
key
additional properties
any
Examples
{
"error": "Unauthorized: Missing or invalid Authorization header"
}

The request cannot proceed on billing grounds. BILLING_INSUFFICIENT_BALANCE applies only to prepay accounts and includes required, available, and shortfall in USD. BILLING_POSTPAY_CEILING_EXCEEDED applies to an account ceiling; BILLING_CONSUMER_POSTPAY_CEILING_EXCEEDED applies to an agency child’s routed ceiling. Both ceiling responses include projected accrued and configured ceiling in USD.

Media typeapplication/json
object
error
required

Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.

string
message

Human-readable explanation of the error.

string
code

Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.

string
request_id

Opaque support/debug identifier when available.

string
code
string
Allowed values: BILLING_INSUFFICIENT_BALANCE BILLING_POSTPAY_CEILING_EXCEEDED BILLING_CONSUMER_POSTPAY_CEILING_EXCEEDED
shortfall
number
required
number
available
number
accrued
number
ceiling
number
Examples
{
"error": "Insufficient balance",
"code": "BILLING_INSUFFICIENT_BALANCE",
"message": "Insufficient effective balance",
"required": 1,
"available": 0.75,
"shortfall": 0.25
}

Token is valid but lacks the required scope for this endpoint. Check the endpoint description for the required scope (discovery, purchase, or account).

Media typeapplication/json
object
error
required

Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.

string
message

Human-readable explanation of the error.

string
code

Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.

string
request_id

Opaque support/debug identifier when available.

string
key
additional properties
any
Examples
Examplemissing_scope
{
"error": "SCOPE_REQUIRED",
"message": "This endpoint requires the purchase scope."
}

BILLING_NOT_READY — the billing account/route isn’t active. IDEMPOTENCY_KEY_CONFLICT — the Idempotency-Key header was already used with a different request body. IDEMPOTENCY_REQUEST_IN_PROGRESS — another request with this key is in flight; retry after the seconds in Retry-After.

Media typeapplication/json
object
error
required
string
code
required
string
Allowed values: BILLING_NOT_READY IDEMPOTENCY_KEY_CONFLICT IDEMPOTENCY_REQUEST_IN_PROGRESS
message
required
string
Example
{
"code": "BILLING_NOT_READY"
}

A required billing provider partnership is temporarily unavailable.

Media typeapplication/json
object
error
required

Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.

string
message

Human-readable explanation of the error.

string
code

Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.

string
request_id

Opaque support/debug identifier when available.

string
key
additional properties
any
Examplegenerated
{
"error": "example",
"message": "example",
"code": "example",
"request_id": "example"
}