Micro-batch synchronous match
const url = 'https://api.infiniteaudience.ai/v1/match';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"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}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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 02–05 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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Optional. Makes a retried call safe to repeat — see the endpoint description’s Retries section.
Request Bodyrequired
Section titled “Request Bodyrequired”object
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.
object
Primary email address.
Phone number in any standard format.
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.
Lowercase-hex SHA-256 hash of a normalized phone number. Same pre-hashed semantics as email_sha256.
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.
Generational suffix (e.g. JR, SR, III).
Additional raw email addresses beyond email, tried as extra match permutations.
Pre-hashed counterpart to emails.
Additional raw phone numbers beyond phone, tried as extra match permutations.
Pre-hashed counterpart to phones.
Additional addresses beyond address_1/city/state/zip, tried as extra match permutations.
object
Street address line 1.
Street address line 2 (apt, suite, etc.).
Two-letter US state code.
5- or 9-digit ZIP code.
Date of birth — any parseable date format (e.g. YYYY-MM-DD).
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 02–05 ID cannot be used for direct refresh; the row falls back to its other identity signals, if any.
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.
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.
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.
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.
Responses
Section titled “Responses”Matched records.
object
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.
object
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).
Present when the request included email_sha256, echoed back unchanged.
Present when the request included phone_sha256, echoed back unchanged.
Present when the request included full_name; the original submitted value, echoed back unchanged. Null when the request did not include one.
Echoed back when present on input, or populated from full_name parsing when the request supplied full_name without first_name/last_name.
Generational suffix (e.g. JR, SR, III). Echoed back when present on input, or populated from full_name/last_name parsing.
Additional raw emails from the request, echoed back unchanged.
Additional pre-hashed emails from the request, echoed back unchanged.
Additional raw phones from the request, echoed back unchanged.
Additional pre-hashed phones from the request, echoed back unchanged.
Additional addresses from the request, echoed back unchanged.
object
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 02–05 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.
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).
Matching strategy that produced the result (e.g. graph_name_email_match, vector_name_address_match, spatial_match). Present only on matched records.
Confidence score for the match (0–1). Present only on matched records.
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
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.
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.
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).
Total number of input records processed.
Number of records that resolved to a known identity.
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.
Match_count / record_count.
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.
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.
Present only when matching and billing succeeded but the optional, non-billable scratch-segment materialization failed.
object
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.
object
Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.
Human-readable explanation of the error.
Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.
Opaque support/debug identifier when available.
Examples
{ "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).
object
Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.
Human-readable explanation of the error.
Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.
Opaque support/debug identifier when available.
Examples
{ "error": "Unauthorized: Missing or invalid Authorization header"}{ "error": "Unauthorized", "code": "TOKEN_EXPIRED", "message": "Your session has expired. Please sign in again."}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.
object
Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.
Human-readable explanation of the error.
Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.
Opaque support/debug identifier when available.
Examples
{ "error": "Insufficient balance", "code": "BILLING_INSUFFICIENT_BALANCE", "message": "Insufficient effective balance", "required": 1, "available": 0.75, "shortfall": 0.25}{ "error": "Billing capacity unavailable", "code": "BILLING_POSTPAY_CEILING_EXCEEDED", "message": "Postpay ceiling would be exceeded", "accrued": 105, "ceiling": 100}Token is valid but lacks the required scope for this endpoint. Check the endpoint description for the required scope (discovery, purchase, or account).
object
Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.
Human-readable explanation of the error.
Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.
Opaque support/debug identifier when available.
Examples
{ "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.
object
Example
{ "code": "BILLING_NOT_READY"}A required billing provider partnership is temporarily unavailable.
object
Stable machine-readable error code (e.g. INVALID_STATUS_TRANSITION, BILLING_INSUFFICIENT_BALANCE). Always present.
Human-readable explanation of the error.
Alternate machine-readable code — present on some endpoints as an alias for error for backward compatibility.
Opaque support/debug identifier when available.
Examplegenerated
{ "error": "example", "message": "example", "code": "example", "request_id": "example"}