# Identity Verification API - KYC, Fraud and Risk Scores (`nabeelbaghoor/identity-verification-kyc-api`) Actor

Verify a person and score them for fraud in one call: KYC name, address, date of birth and national ID matching, synthetic identity detection, separate email, phone and address risk scores, name correlation checks, reason codes, and an accept, review or reject decision. Pay per result.

- **URL**: https://apify.com/nabeelbaghoor/identity-verification-kyc-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

$20.00 / 1,000 results

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

## Identity Verification API - KYC, Fraud and Risk Scores

Give it what you hold about a person - a name, an address, an email, a phone number, a date of birth - and get back whether those things belong together, how risky each one is, and a decision you can route on. One flat row per identity.

### What this actor does

- **Runs KYC and fraud scoring in a single call.** Name, address, date of birth and national ID matching, an identity fraud score, synthetic identity detection, and separate risk scores for the email address, the phone number and the address, all from one submission of one identity.
- **Answers the correlation question, not just the validity question.** An email address can be perfectly real and still not belong to the person claiming it. This provider scores name to address, name to phone and name to email correlation separately, which is the difference between checking that a detail exists and checking that it fits.
- **Returns a decision, not just numbers.** When the decision module is enabled, your account's own rules are applied and the row comes back as accept, review or reject. Filter to reviews and you have a work queue; filter to rejects and you have an action list.
- **Pools the reason codes.** Every module reports its own reason codes for why it scored the way it did. In practice the question is what the evaluation objected to rather than which module objected, so they are gathered into one sorted list per row, with the per-module breakdown still available in the raw column.
- **Speaks to both API generations.** This provider runs two side by side: an older one that takes an explicit list of modules, and a newer one that runs a workflow configured in your dashboard. They use different hosts, different credential schemes and different request shapes. This actor accepts one neutral identity shape and translates it into whichever one your key belongs to.
- **Handles per-account licensing gracefully.** Modules are licensed individually, and one your account does not hold comes back empty rather than as an error. That leaves its columns blank instead of breaking the row, and the complete provider response is always kept in the raw column.
- **Refuses empty records before they cost anything.** An identity with no fields filled in costs a request and can only come back empty, so it is refused locally and recorded as a refusal.
- **Names the environment in credential errors.** The sandbox is a separate host rather than a flag, and sandbox credentials are refused by production with the same message a wrong key gets. When that happens, the error here says which environment and which generation the run used.
- **Never charges for a miss.** Only rows carrying an evaluation are billed.

### Input

| Field | What it does |
| --- | --- |
| `identities` | The people to evaluate, as a JSON array of objects. |
| `platform` | Which API generation to call: modules or workflow. |
| `environment` | Production or sandbox. |
| `modules` | Which checks to run on the modules endpoint. |
| `extraModules` | Any other module names your account is licensed for. |
| `workflow` | The workflow identifier, on the workflow endpoint. |
| `decisions` | Keep only accept, review, reject, refer or resubmit rows. |
| `minFraudScore` | Keep only rows scoring at or above this. |
| `maxFraudScore` | Keep only rows scoring at or below this. |
| `maxEmailRiskScore` | Keep only rows with email risk at or below this. |
| `maxPhoneRiskScore` | Keep only rows with phone risk at or below this. |
| `maxAddressRiskScore` | Keep only rows with address risk at or below this. |
| `excludeReasonCodes` | Drop rows carrying these reason codes. |
| `requireReasonCodes` | Keep only rows carrying these reason codes. |
| `requestsPerMinute` | Pace the run under your contract's rate limit. |
| `skipNotFound` | Leave misses and refusals out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `apiKey` | Your own API key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "platform": "modules",
  "query": "jane.doe@example.com",
  "email": "jane.doe@example.com",
  "mobileNumber": "12065550100",
  "fullName": "Jane Doe",
  "referenceId": "123a456b-789c-1234-567c-9307e6e0a83f",
  "decision": "accept",
  "fraudScore": 0.167,
  "fraudModelName": "sigma",
  "syntheticScore": 0.04,
  "emailRiskScore": 0.011,
  "phoneRiskScore": 0.002,
  "addressRiskScore": 0.364,
  "nameAddressCorrelation": 0.9096,
  "namePhoneCorrelation": 0.9984,
  "nameEmailCorrelation": 0.9791,
  "kycDecision": "REFER",
  "kycConfidence": 0,
  "kycFieldValidations": {
    "firstName": 0.99,
    "surName": 0.99,
    "streetAddress": 0.99,
    "city": 0.99,
    "state": 0.99,
    "zip": 0.99,
    "mobileNumber": 0.99,
    "dob": 0.99
  },
  "reasonCodes": ["I127", "I553", "I602", "I610", "I618", "I626", "I705", "I708"],
  "modulesReturned": ["nameAddressCorrelation", "namePhoneCorrelation", "fraud", "kyc", "addressRisk", "emailRisk"],
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### What is the difference between the modules endpoint and the workflow endpoint?

They are two generations of this provider's API running side by side, not two settings of one API. The modules endpoint takes an explicit list of what to evaluate in the request itself, so the run decides what happens. The workflow endpoint names a workflow you built in the provider's dashboard, so the dashboard decides what happens and the request just supplies the person. They use different hosts and different credential schemes, and a key issued for one is refused by the other exactly like a wrong key. If you are unsure, start with the modules endpoint.

#### Which modules should I turn on?

Only the ones you need, because each is licensed and billed separately and every extra module costs money on every identity. As a starting point: `emailrisk` or `phonerisk` alone is enough to screen a signup list; add `kyc` when you need to confirm a name, address and date of birth actually belong together; add `fraud` for an overall identity risk score; add `synthetic` when fabricated identities are the specific worry; add `decision` to have your own account rules applied and get an accept, review or reject back rather than raw numbers.

#### What happens if I ask for a module my account does not have?

Its block simply does not appear in the response, so the corresponding columns come back blank. It is not an error and it does not stop the run, which is convenient but also means a column of nulls can mean either "the module found nothing" or "you are not licensed for this". The `modulesReturned` column lists which blocks actually came back, so you can tell the two apart at a glance.

#### What do the scores mean?

They run from zero to one, where higher is riskier. That is a different scale from most fraud APIs, which use zero to one hundred, so a threshold copied from elsewhere will behave very strangely here. The correlation scores read the other way round: those are confidence that two details belong to the same person, so higher is better. The filters in this actor use the provider's own scale in both cases.

#### What are reason codes and why are they pooled into one column?

Reason codes are the specific findings behind a score, such as the email domain being newly registered or the address being a mail drop. Each module reports its own list. This actor gathers them all into one sorted column per row, because the practical question when reading a result is what the evaluation objected to rather than which module raised it. If you need the per-module attribution, it is preserved in the raw column. You can also filter on them directly, which is the right tool when a score threshold is too blunt.

#### Why does correlation matter more than validity?

Because most fraud uses real details. A stolen identity has a valid name, a valid address and a valid phone number, and every one of those checks out on its own. What does not check out is the combination: the phone number has no history with that name, or the person has never been associated with that address. Correlation scoring is what catches that, and it is the reason this kind of API exists rather than just a set of separate validators.

#### Do I have to write JSON to use this?

Only if you want the full identity. The identities field takes a JSON array of objects, which is what you need for name, address and date of birth checks. But a bare string in that list is read as an email address if it contains an at sign and as a phone number otherwise, so screening a plain list of contacts works without writing any JSON at all.

#### Why do I keep getting told my API key is invalid when it is not?

Almost always the environment or the generation. The sandbox is a separate host rather than a flag, so a sandbox key is refused by production with the same message a wrong key gets, and the same applies between the two API generations. Check that the environment and platform settings match the key you pasted in. The credential error from this actor names both, so the message tells you what to compare.

#### Do I need my own API key?

Yes. This actor does not include data access. You use your own API key from Socure, which is the provider whose identity verification API this actor calls, taken from your provider dashboard and pasted into the `apiKey` field, where it is stored as an Apify secret. Your own contract, licensed modules and terms apply, and note that this provider licenses each module separately.

#### What happens when an identity returns nothing?

A negative verdict is a result, not a miss: the provider telling you a name and address do not correlate is exactly the question you asked, so that row is written and billed like any other. A miss is an identity the provider would not evaluate at all, and a refusal is an entry that never left this actor. Both are written with `found: false` and never billed. Set `skipNotFound` to true to leave them out entirely.

#### How much does a run cost?

Pricing is pay per result: you are charged for each identity evaluated into the dataset, and never for misses, for locally refused entries, for rows dropped by the filters, or for duplicates. Apify platform usage is included in the per-result price. Your own provider contract is separate and billed by them, per module.

### Keyword map

identity verification API, KYC API, know your customer API, identity fraud detection, synthetic identity detection, email risk score, phone risk score, address risk score, name address correlation, identity resolution API, AML screening, watchlist screening API, customer onboarding verification, account opening fraud, first party fraud detection, third party fraud detection, digital identity verification, age verification API, SSN verification, date of birth verification, reason codes, risk decisioning API, accept review reject decision, fintech onboarding API, bank account opening KYC, identity graph API

# Actor input Schema

## `identities` (type: `array`):

The people to evaluate, as a JSON array of objects. Each object can carry any of: firstName, surName, middleName, email, mobileNumber, physicalAddress, physicalAddress2, city, state, zip, country, dob, nationalId, userConsent, consentTimestamp, customerUserId, ipAddress. Fill in whatever you hold; which fields a given module needs depends on what your account is licensed for. A bare string is read as an email address if it contains an at sign and as a phone number otherwise, so a plain contact list works without writing JSON.

## `platform` (type: `string`):

This provider runs two API generations side by side and they are not variants of one another: different hosts, different credential schemes and different request shapes. The modules endpoint takes an explicit list of what to evaluate and is the one to use unless you know otherwise. The workflow endpoint runs a workflow you configured in the provider dashboard, so what gets evaluated is decided there rather than here. Your key works on one generation, not both.

## `environment` (type: `string`):

Production or sandbox. The sandbox is a separate host rather than a flag, and sandbox credentials are refused by production with the same message a wrong key gets, so this has to match the key you are using.

## `modules` (type: `array`):

On the modules endpoint, which checks to run. Each one is licensed and billed separately by the provider, and a module your account does not hold comes back empty rather than as an error, so its columns will simply be blank. Pick only what you actually need: every extra module costs money on every identity.

## `extraModules` (type: `array`):

Any other module names your account is licensed for, written exactly as the provider names them. This provider adds modules between releases and licenses them per account, so rather than guess at a complete list, the common ones are ticked above and anything else goes here. Names written here are sent whether or not they appear in the list above.

## `workflow` (type: `string`):

On the workflow endpoint, the identifier of the workflow to run. You will find it in your provider dashboard under the developer workbench. It must name a single active workflow in the environment you selected. Ignored on the modules endpoint.

## `decisions` (type: `array`):

Only keep rows whose decision matches one of these. This is the fastest way to turn a verification run into a queue: keep the reviews to work through by hand, or keep the rejects to act on. Matching ignores capitalisation. Leave empty to keep every decision.

## `minFraudScore` (type: `number`):

Only keep rows scoring at or above this. This provider reports its scores on a zero to one scale where higher is riskier, so a threshold like 0.7 keeps only the genuinely suspicious. Leave at zero to keep every score.

## `maxFraudScore` (type: `number`):

Only keep rows scoring at or below this on the zero to one scale. The one to use when you are building a clean list rather than a review queue. Leave at zero to keep every score.

## `maxEmailRiskScore` (type: `number`):

Only keep rows whose email risk score is at or below this, on the zero to one scale. Leave at zero to skip this check.

## `maxPhoneRiskScore` (type: `number`):

Only keep rows whose phone risk score is at or below this, on the zero to one scale. Leave at zero to skip this check.

## `maxAddressRiskScore` (type: `number`):

Only keep rows whose address risk score is at or below this, on the zero to one scale. Leave at zero to skip this check.

## `excludeReasonCodes` (type: `array`):

Drop any row whose pooled reason codes include one of these, written as the provider's codes such as I553. Reason codes are the specific findings behind a score, so this is the finer instrument when a score threshold is too blunt. Matching ignores capitalisation. Leave empty to skip this check.

## `requireReasonCodes` (type: `array`):

Keep only rows whose pooled reason codes include at least one of these. Useful for isolating a single finding across a large run. Matching ignores capitalisation. Leave empty to skip this check.

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

Pace the run so it stays under whatever rate your contract allows. Lower this if the provider starts rate limiting; raise it to finish a long list sooner.

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

Leave identities the provider returned nothing for, and entries refused before they were sent, out of the dataset. Off by default, because knowing which of your records were unusable is usually the point. Misses are never charged for either way.

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

Stop after this many rows. This is the cap on both spend and run time.

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

Your own API key for the identity verification API, taken from your provider dashboard. Keys are issued per environment and per API generation, so make sure the environment and generation above match the key you are pasting in. Stored as a secret.

## Actor input object example

```json
{
  "identities": [
    {
      "firstName": "Jane",
      "surName": "Doe",
      "email": "jane.doe@example.com",
      "mobileNumber": "12065550100"
    }
  ],
  "platform": "modules",
  "environment": "production",
  "modules": [
    "emailrisk"
  ],
  "decisions": [],
  "minFraudScore": 0,
  "maxFraudScore": 0,
  "maxEmailRiskScore": 0,
  "maxPhoneRiskScore": 0,
  "maxAddressRiskScore": 0,
  "requestsPerMinute": 120,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

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

One row per identity, carrying the decision, the fraud and synthetic scores, the per-attribute email, phone and address risk scores, the KYC field validations and correlation indices, and the pooled reason codes behind them.

# 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 = {
    "identities": [
        {
            "firstName": "Jane",
            "surName": "Doe",
            "email": "jane.doe@example.com",
            "mobileNumber": "12065550100"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/identity-verification-kyc-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 = { "identities": [{
            "firstName": "Jane",
            "surName": "Doe",
            "email": "jane.doe@example.com",
            "mobileNumber": "12065550100",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/identity-verification-kyc-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 '{
  "identities": [
    {
      "firstName": "Jane",
      "surName": "Doe",
      "email": "jane.doe@example.com",
      "mobileNumber": "12065550100"
    }
  ]
}' |
apify call nabeelbaghoor/identity-verification-kyc-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/identity-verification-kyc-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/1G83H9PFItd6cfkPe/builds/FFXMcxIObuFoF1ulA/openapi.json
