# Consumer Identity Data API - Person, Household, Business (`nabeelbaghoor/consumer-identity-data-api`) Actor

Resolve names, postal addresses, emails, phone numbers and hashed keys to person, household, place, residency, business and entity documents, with the demographic and contact bundles your contract covers. Batch up to 100 records per call, or look a key up without sending any personal data.

- **URL**: https://apify.com/nabeelbaghoor/consumer-identity-data-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$15.00 / 1,000 documents

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Consumer Identity Data API - Person, Household, Business

Turn the details you hold about somebody into the documents an identity graph holds about them. One flat row per document, with everything your contract covers attached.

### What this actor does

- **Resolves six kinds of document, not one kind of record.** People, households, places, residencies, businesses and entities are separate classes at this provider, and they answer different questions. The individual, the group living at an address, the address itself, the household at that place, the company, and the resolved identity across sources are each their own request, and each is available here.
- **Lets you look somebody up without sending anything about them.** Lookup mode takes a key you already hold, a persistent link, a hashed email or a name and address representation hashed at your end, and fetches the document by exact key. No personal detail leaves your side, which is often the difference between a workable integration and one legal will not sign off.
- **Validates every record before it can spoil a batch.** Requests travel a hundred at a time, and this provider fails the whole batch when one record breaks a rule rather than skipping the bad field. Every documented rule, postcode length, state abbreviation, digit counts on the three phone parts, coordinate ranges, the four pairs of fields that cannot travel together, is checked here first. A record that would have failed is reported as its own row with the reason, and the other ninety-nine still go.
- **Batches a hundred at a time and pairs the answers back up.** Position is kept from request to response, so every row says which of your records produced it.
- **Asks for more than one match when you want the alternatives.** A record can resolve to up to ten documents, best first, each with its rank. The run says plainly that each of those is a separate transaction at the provider, because a limit of ten across a hundred records is a thousand transactions.
- **Says when a name only partly matched.** An email or phone match where part of the name also lined up is reported by the provider as a partial name match, and it ranks above a match on the contact detail alone. That distinction is its own column, and its own filter.
- **Handles the two-part authorisation this provider actually uses.** A bearer token says who you are; a role and a tenant say which contracted view you get. Both are inputs, both are echoed into the run log, and a refusal explains that the pair is the usual cause rather than the credential.
- **Refuses to guess at your bundles.** No two accounts are contracted for the same data, so nothing here assumes a bundle exists. The document is written through exactly as sent, and the bundles that actually came back are listed in their own column.
- **Charges nothing for a miss.** Only rows carrying a document are billed.

### Input

| Field | What it does |
| --- | --- |
| `mode` | Match from details, or look up by an exact key. |
| `documentClass` | People, households, places, residencies, businesses, entities. |
| `records` | One object per record, using this provider's own parameter names. |
| `emails` / `phones` | Shortcuts for records that are only an email or only a number. |
| `lookupDocumentClass` / `lookupType` / `lookupValues` | The key lookup. |
| `clientId` / `apiKey` | Your own OAuth client id and secret. |
| `tokenUrl` / `scope` / `baseUrl` | Overrides for a tenant on its own hosts. |
| `role` / `tenant` | The contracted pair that decides which view you get. |
| `bundles` | Named bundles to return. Empty means everything contracted. |
| `limit` | Documents per record, up to ten, best first. |
| `persistedDocumentsOnly` | Stored documents only, or allow calculated ones. |
| `matchLevel` | The full match cascade, or strict. |
| `strictValidation` | Fail a record with an invalid field, or blank the field. |
| `geoCoderLevel` | How precisely to geocode. |
| `skipPostal` | Skip postal correction before matching. |
| `docIdOnly` | Return nothing but the document id. |
| `onlyFullNameMatches` | Drop partial name matches. |
| `requireDocId` | Drop documents with no id. |
| `skipNotFound` | Leave misses out of the dataset. |
| `maxResults` | Row ceiling, and therefore the cost ceiling. |
| `requestsPerMinute` | Pacing, to match your contracted throttle rate. |

### Example output

```json
{
  "documentClass": "people",
  "mode": "match",
  "requestIndex": 1,
  "bundlesRequested": ["postalContact", "matchMetadata"],
  "lookupType": null,
  "lookupValue": null,
  "inputSummary": "name=mary robinson zipCode=60515",
  "found": true,
  "rank": 1,
  "docId": "000000US00Yc6Bd5-a08a4FHSWN9JW34aCS88m",
  "nameMatchIntegrity": "full",
  "bundlesReturned": ["matchMetadata", "postalContact"],
  "notFoundReason": null,
  "document": {
    "docId": "000000US00Yc6Bd5-a08a4FHSWN9JW34aCS88m",
    "matchMetadata": { "nameMatchIntegrity": "full" },
    "postalContact": { "city": "Downers Grove", "state": "IL", "zipCode": "60515" }
  }
}
```

The `document` column carries whatever your contract covers. The example shows two bundles because that is what the example account asked for; yours will differ.

### FAQ

#### What is the difference between match and lookup?

Match takes ordinary identity details, a name, an address, an email, a phone number, and runs them through a cascade of matching steps until something resolves. Lookup takes a key that already identifies a document, a persistent link, a document id, or a hash of a name and address representation, and fetches that document by exact key with no matching involved. Match answers "who is this"; lookup answers "give me this one again".

#### How do I look somebody up without sending their personal details?

Build the entity representation at your end, lowercase the name and address into a single string, hash it, and send the hash as the lookup value with the matching hash type. The provider resolves against hashes of the same representations. The same works for an email using MD5 or SHA256. Nothing identifying crosses the network, which is why this route exists.

#### Why does one bad record matter so much?

Because requests travel in batches of up to a hundred and this provider validates strictly: a nine-character postcode with a letter in it, or a phone number with dashes still in it, produces a validation error for the request rather than a quietly ignored field. This actor applies every rule the provider documents before sending, so a malformed record is reported on its own and the batch it would have spoiled still goes out.

#### What are the role and the tenant, and do I need them?

They are the two values that say which contracted view of the data your request runs under. If your credentials are mapped to exactly one of each, the provider infers them and you can leave both empty. If your account carries more than one, they are required, and leaving them out produces a refusal that reads like a permissions problem rather than a missing parameter. Both are issued by the provider, not chosen by you.

#### Which document class should I ask for?

Ask for what you want back. A person document describes the individual. A household document describes the group of people at an address, which is the right class for anything addressed to a dwelling rather than a name. A place document describes the address itself. A residency joins a household to a place. A business document describes a company. An entity document is the resolved identity across the sources behind all of them. Asking for the wrong class returns nothing rather than an error, so it is worth being deliberate.

#### What does a partial name match mean?

The provider's default cascade includes steps that match on an email address or a phone number alone. When part of the name also lines up on such a match, it is reported as partial rather than as a bare contact-detail match, and it ranks higher. It is not a full name match: some of the name matched and some did not. Use the strict match level to skip those steps entirely, or the partial name filter to drop them after the fact.

#### Why does asking for ten documents per record cost so much more?

Because each document in a multi-document result is billed by the provider as its own transaction, and is written here as its own row. A hundred records at a limit of ten is up to a thousand of each. The run logs the arithmetic before it starts. The limit does not loosen what counts as a match; it only lets through more of the matches that already qualified.

#### Can I run this without credentials?

No, and the actor will tell you so rather than failing. Every call uses your own OAuth client id and secret, and access to this data is contracted: the provider approves a production key against a stated use before issuing one. Credentials also expire every six months, so a pair that used to work may simply have aged out.

### Keyword map

consumer identity data API, identity graph resolution, identity resolution API, person household match API, email to identity append, hashed email lookup, persistent identity link, address hygiene and match, household data API, consumer demographics API, entity resolution API, batch identity match, PII-free identity lookup, business firmographic match, identity data enrichment.

# Actor input Schema

## `mode` (type: `string`):

Match takes identity details and finds the best document for them. Lookup takes a key you already hold, such as a persistent link or a hashed email, and fetches its document exactly. Lookup never sends personal details over the wire, which is the reason to prefer it when you have the keys.

## `documentClass` (type: `string`):

In match mode, which kind of document to resolve to. The class decides what comes back: a person document carries the individual, a household document carries the address group they belong to, a place document carries the address itself. Ask for the one you actually want, because the wrong class returns nothing rather than an error.

## `records` (type: `array`):

In match mode, one object per record. Recognised keys are firstName, middleName, lastName, generationalSuffix, name, businessName, streetAddress, primaryNumber, preDirectional, street, streetSuffix, postDirectional, unitDesignator, secondaryNumber, city, state, zipCode, email, emailMD5, phone, areaCode, exchange, subscriberNumber, latitude, longitude, sumLastFourSSN and yearOfBirth. Do not mix a parsed and an unparsed form of the same thing in one record.

## `emails` (type: `array`):

In match mode, a shortcut for records that are nothing but an email address, one per line. An email alone resolves to entity, household, person and place documents at this provider, and a name alongside it produces a stronger match.

## `phones` (type: `array`):

In match mode, a shortcut for records that are nothing but a phone number, one per line. Punctuation is stripped for you; the provider wants ten digits for a United States number.

## `lookupDocumentClass` (type: `string`):

In lookup mode, which kind of document the key should resolve to.

## `lookupType` (type: `string`):

In lookup mode, what the values are. An entity representation is the lowercased name and address written as one string. The hash types are built from that same representation, so nothing personal leaves your side. The link types are the provider's own persistent identifiers.

## `lookupValues` (type: `array`):

In lookup mode, the values to fetch, one per line. They all have to be of the kind chosen above.

## `clientId` (type: `string`):

The OAuth client id issued to your account by this provider. It is half of the credential pair and is stored encrypted.

## `apiKey` (type: `string`):

The OAuth client secret issued to your account by this provider. Bring your own credentials: nothing is shared between runs and the secret is never written to the dataset. Credentials at this provider expire every six months, so a pair that used to work may simply have aged out.

## `tokenUrl` (type: `string`):

Where to exchange the client id and secret for a bearer token. The default is the provider's published endpoint. Change it only if your credentials were issued on a dedicated authorisation server, in which case the value is the token\_endpoint from your own OpenID configuration document.

## `scope` (type: `string`):

An optional scope to request with the token. Leave empty unless your account was told to send one.

## `baseUrl` (type: `string`):

The host serving the data. Change it only if your account is served from a region with its own hostname.

## `role` (type: `string`):

The contracted role to run this request under. Required whenever your account is mapped to more than one. A missing or wrong role is refused in a way that reads like a permissions problem, so set it if you have one.

## `tenant` (type: `string`):

The contracted tenant to run this request under. Required whenever your account is mapped to more than one.

## `bundles` (type: `array`):

Which named data bundles to ask for, one per line, for example postalContact or basicDemographics. Leave empty to receive every bundle your contract covers. Asking for fewer returns a smaller document; it does not unlock bundles you are not contracted for.

## `limit` (type: `integer`):

In match mode, how many matching documents to accept per record, best first, up to ten. This does not loosen what counts as a match; it only allows more of them through when they exist. Each document is a separate transaction at the provider and a separate row here.

## `persistedDocumentsOnly` (type: `boolean`):

On by default, meaning only documents that already exist are returned. Turn it off to allow documents calculated at request time as well. The provider refuses that together with more than one document per record, so if both are set the document limit wins and the run says so.

## `matchLevel` (type: `string`):

Default runs the whole match cascade, including phone only, email only and name plus postcode steps. Strict skips those three, which trades recall for certainty about who the document belongs to.

## `strictValidation` (type: `boolean`):

On by default at the provider: a malformed postcode or email produces a validation error for that request. Turn it off to have the provider blank the invalid field and match on what is left. This actor checks the documented rules before sending either way, so a whole batch is never lost to one bad record.

## `geoCoderLevel` (type: `string`):

The most precise level at which to attempt geocoding for the bundles that carry coordinates. Coarser levels resolve more often; finer levels resolve more exactly.

## `skipPostal` (type: `boolean`):

Sends the address to matching exactly as given, without running it through postal correction first. Leave off unless your addresses are already standardised, because the correction step usually improves the match.

## `docIdOnly` (type: `boolean`):

Asks for nothing but the document id. Useful for a cheap existence check or for building a key list to look up later.

## `onlyFullNameMatches` (type: `boolean`):

This provider reports a partial name match when only part of the name lined up alongside an email or a phone number. Turn this on to keep only documents where the whole name matched. Only meaningful when the match metadata bundle is in your contract.

## `requireDocId` (type: `boolean`):

Drops documents with no document id, which is the case for some calculated documents.

## `skipNotFound` (type: `boolean`):

Records that resolved to nothing are written as rows with the reason, so you can see what happened to every input. Turn this on to keep only the ones that resolved. Misses are never charged for either way.

## `maxResults` (type: `integer`):

Stop after this many rows. Rows are charged individually, so this is the ceiling on what a run can cost.

## `requestsPerMinute` (type: `integer`):

How fast to call the provider. This provider sets a throttle rate per account as part of the contract, so lower this to match yours rather than relying on the retries.

## Actor input object example

```json
{
  "mode": "match",
  "documentClass": "people",
  "records": [],
  "emails": [],
  "phones": [],
  "lookupDocumentClass": "people",
  "lookupType": "er",
  "lookupValues": [],
  "tokenUrl": "https://login.acxiom.com/oauth2/v1/token",
  "baseUrl": "https://api.acxiom.com",
  "bundles": [],
  "limit": 1,
  "persistedDocumentsOnly": true,
  "matchLevel": "default",
  "strictValidation": true,
  "geoCoderLevel": "defaultLevel",
  "skipPostal": false,
  "docIdOnly": false,
  "onlyFullNameMatches": false,
  "requireDocId": false,
  "skipNotFound": false,
  "maxResults": 100,
  "requestsPerMinute": 60
}
```

# Actor output Schema

## `results` (type: `string`):

One row per document returned, carrying the document class, the document id, the name match integrity, the rank within a multi-document match, the bundles present, and the whole document as sent by the provider.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "records": [],
    "emails": [],
    "phones": [],
    "lookupValues": [],
    "bundles": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/consumer-identity-data-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "records": [],
    "emails": [],
    "phones": [],
    "lookupValues": [],
    "bundles": [],
}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/consumer-identity-data-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "records": [],
  "emails": [],
  "phones": [],
  "lookupValues": [],
  "bundles": []
}' |
apify call nabeelbaghoor/consumer-identity-data-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/consumer-identity-data-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/6PZ8ap5zrXfBKnXIt/builds/TLeEOvEV6DDLTNpJl/openapi.json
