VerifyNow
API Docs/Integration Guide
Developer guide

API Integration Guide

Step-by-step instructions for integrating the VerifyNow API into your application. From authentication to your first API call in minutes.

API workflow explorer

Choose an industry guide

Every guide starts with a Basic path and adds Step-up checks when the event, customer or risk level requires more evidence.

Your selection is added to the URL so this workflow can be shared.

Banking & Fintech

A customer or business record ready for account-opening rules.

View industry page

Basic

Start with the core verification

Run the minimum checks needed for the normal customer journey.

  • Standard KYC Bundle

Step-up

Ask for more when risk changes

Add these checks only when the listed trigger applies.

  • Face Match — Home AffairsRemote onboarding
  • AML, PEP & Sanctions ScreeningHigher-risk customers
  • Bank Account VerificationWhen an account will receive or send funds
  • CIPC Company SearchBusiness customers

Customer

Start in your current journey

A customer starts account opening, lending, payments or payout onboarding.

Your channel

Collect the required evidence

Identity, selfie, entity, risk and bank-account details required by your policy.

VerifyNow API

Run the selected checks

Your server sends the requests below with one idempotency key per check.

Your rules

Make the business decision

Apply your RMCP, customer-risk rating and exception rules.

Your team

Keep the audit record

Keep request IDs, returned evidence and the final business decision.

API sequence

Standard KYC Bundle

Run the Standard KYC Bundle for identity and supporting trace context.

POST /verify

Face Match — Home Affairs

Compare a live selfie with the Home Affairs identity photo for stronger remote identity binding.

POST /facematch
Use when: Remote onboardingEndpoint details

AML, PEP & Sanctions Screening

Screen a person against AML, PEP, sanctions and adverse-risk sources when policy requires it.

POST /aml-screening
Use when: Higher-risk customersEndpoint details

Bank Account Verification

Verify the account holder and bank-account status before money moves.

POST /bank-account-verification
Use when: When an account will receive or send fundsEndpoint details

CIPC Company Search

Verify a business customer or counterparty against CIPC records.

POST /cipc
Use when: Business customersEndpoint details

Select calls according to your product and risk policy. More calls do not automatically create a compliant outcome.

Server-side orchestration example

JavaScript

const API_BASE = 'https://www.verifynow.co.za/api/external';

async function callVerifyNow(path, body, idempotencyKey) {
  const response = await fetch(`${API_BASE}${path}`, {
    method: 'POST',
    headers: {
      'x-api-key': process.env.VERIFYNOW_API_KEY,
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey,
    },
    body: JSON.stringify(body),
  });

  const data = await response.json();
  if (!response.ok) throw new Error(data.error || 'Verification failed');
  return data;
}

export async function runBankingFintechChecks(input) {
  const results = {};

  results.kyc = await callVerifyNow(
      '/verify',
      {
        bundle: 'kyc_bundle',
        idNumber: input.idNumber,
        mode: 'production',
      },
      `${input.customerReference}:kyc`
    );

  if (Boolean(input.selfieBase64)) {
    results.face = await callVerifyNow(
        '/facematch',
        {
          bundle: 'facematch',
          mode: 'production',
          selfie_image_base64: input.selfieBase64,
          id_number: input.idNumber,
        },
        `${input.customerReference}:face`
      );
  }

  if (input.riskLevel === 'high') {
    results.aml = await callVerifyNow(
        '/aml-screening',
        {
          mode: 'production',
          name: input.fullName,
          entity: 0,
          country: 'za',
          dataset: 'all',
        },
        `${input.customerReference}:aml`
      );
  }

  if (Boolean(input.bank)) {
    results.bank = await callVerifyNow(
        '/bank-account-verification',
        {
          type: 'Individual',
          firstName: input.firstName,
          surname: input.surname,
          identityNumber: input.idNumber,
          identityType: 'IDNumber',
          bankName: input.bank.name,
          bankAccountNumber: input.bank.accountNumber,
          bankBranchCode: input.bank.branchCode,
          bankAccountType: input.bank.accountType,
          mode: 'production',
        },
        `${input.customerReference}:bank`
      );
  }

  if (Boolean(input.companyRegistrationNumber)) {
    results.company = await callVerifyNow(
        '/cipc',
        {
          reportType: 'cipc_company_match',
          registration_number: input.companyRegistrationNumber,
          mode: 'production',
        },
        `${input.customerReference}:company`
      );
  }

  return {
    customerReference: input.customerReference,
    results,
    next: 'Apply your policy and route mismatches to manual review.',
  };
}

Request composition

How can I combine checks into one call?

VerifyNow does not accept an arbitrary array of checks in one public API request. Use a supported bundle where one exists. For a custom combination, expose one endpoint from your server and let that endpoint coordinate the required VerifyNow calls.

Basic ID + supplied-reference Face Match

Use bundle: "facematch_verified" on POST /facematch. It combines basic SA ID verification with a selfie-to-reference-image comparison in one VerifyNow call. It does not fetch a Home Affairs photo.

Selfie + Home Affairs photo Face Match

Use bundle: "facematch" on POST /facematch. It obtains the supported Home Affairs reference photo and compares the submitted selfie in one VerifyNow call.

Bank Account Verification cannot currently be added to either Face Match bundle payload. It remains a separate VerifyNow request, with its own idempotency key and normal component charge.

Give your application one endpoint

Your frontend or partner calls one endpoint that you control. Your server keeps the API key private, runs the appropriate Face Match bundle and Bank Verification, then returns one combined response.

One request from your client
POST /api/verification/combined
{
  "mode": "production",
  "idNumber": "8001015009087",
  "firstName": "Jane",
  "surname": "Doe",
  "selfieImageBase64": "data:image/jpeg;base64,...",
  "referenceImageBase64": "data:image/jpeg;base64,...",
  "bank": {
    "name": "Example Bank",
    "accountNumber": "1234567890",
    "branchCode": "250655",
    "accountType": "Cheque"
  }
}
Server-side orchestration
const VERIFYNOW_API = 'https://www.verifynow.co.za/api/external';

async function callVerifyNow(path, body, idempotencyKey) {
  const response = await fetch(`${VERIFYNOW_API}${path}`, {
    method: 'POST',
    headers: {
      'x-api-key': process.env.VERIFYNOW_API_KEY,
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey,
    },
    body: JSON.stringify(body),
  });

  const data = await response.json();
  if (!response.ok) throw new Error(data.error || 'Verification failed');
  return data;
}

export async function runCombinedVerification(input, requestId) {
  const useSuppliedReference = Boolean(input.referenceImageBase64);

  const identityAndFace = callVerifyNow(
    '/facematch',
    useSuppliedReference
      ? {
          bundle: 'facematch_verified',
          mode: input.mode,
          id_number: input.idNumber,
          selfie_image_base64: input.selfieImageBase64,
          reference_image_base64: input.referenceImageBase64,
        }
      : {
          bundle: 'facematch',
          mode: input.mode,
          id_number: input.idNumber,
          selfie_image_base64: input.selfieImageBase64,
        },
    `${requestId}:identity-face`
  );

  const bank = callVerifyNow(
    '/bank-account-verification',
    {
      type: 'Individual',
      firstName: input.firstName,
      surname: input.surname,
      identityNumber: input.idNumber,
      identityType: 'IDNumber',
      bankName: input.bank.name,
      bankAccountNumber: input.bank.accountNumber,
      bankBranchCode: input.bank.branchCode,
      bankAccountType: input.bank.accountType,
      mode: input.mode,
    },
    `${requestId}:bank`
  );

  const [identityFaceResult, bankResult] = await Promise.all([
    identityAndFace,
    bank,
  ]);

  return {
    requestId,
    checks: {
      identityFace: identityFaceResult,
      bank: bankResult,
    },
  };
}

The example runs both checks in parallel because both are required. If Bank Verification is a step-up check, run it only after the identity and face result passes your policy so an unnecessary second call is not made.

Get API Key

Sign up and get your API key from the Settings page.

Go to Settings

API Reference

View full API documentation with all endpoints.

View API Docs

Test Sandbox

Test with sandbox mode - uses 0 credits.

"mode": "sandbox"
1

Get Your API Key

Create an account and obtain credentials

  1. Sign up at verifynow.co.za
  2. Navigate to Settings in your account
  3. Find the API Key section
  4. Copy your API key (starts with vn_live_)

Security Warning

Keep your API key secret. Never expose it in client-side code or commit it to version control.

2

Set Up Your Environment

Configure environment variables

Store your API key as an environment variable:

# .env.local (or .env)
VERIFYNOW_API_KEY=vn_live_your_api_key_here
3

Make Your First API Call

Choose your preferred language

Optional ID number validation

For South African ID inputs, you can run the standard ID checksum in your application before sending a verification request. This optional pre-flight validation catches typing errors early and can help avoid unnecessary calls. It is not a separate API step.

Using cURL

curl -X POST https://www.verifynow.co.za/api/external/verify \
  -H "x-api-key: $VERIFYNOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "reportType": "said_verification",
    "idNumber": "8001015009087",
    "mode": "production"
  }'

Using JavaScript/TypeScript

const response = await fetch(
  'https://www.verifynow.co.za/api/external/verify',
  {
    method: 'POST',
    headers: {
      'x-api-key': process.env.VERIFYNOW_API_KEY,
      'Content-Type': 'application/json',
      'Idempotency-Key': crypto.randomUUID(),
    },
    body: JSON.stringify({
      reportType: 'said_verification',
      idNumber: '8001015009087',
      mode: 'production',
    }),
  }
);

const result = await response.json();

Using Python

import os
import uuid
import requests

response = requests.post(
    'https://www.verifynow.co.za/api/external/verify',
    headers={
        'x-api-key': os.environ['VERIFYNOW_API_KEY'],
        'Content-Type': 'application/json',
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={
        'reportType': 'said_verification',
        'idNumber': '8001015009087',
        'mode': 'production',
    }
)

result = response.json()

Example Response

{
  "success": true,
  "requestId": "unique-request-id",
  "user_id": "11",
  "remainingCredits": 32,
  "mode": "production",
  "reportType": "said_verification",
  "input": {
    "idNumber": "9111060123086"
  },
  "results": {
    "said_verification": {
      "Status": "Success",
      "realTimeResults": {
        "Status": "ID Number Valid",
        "Verification": {
          "Firstnames": "NALEDI LERATO",
          "Lastname": "KHUMALO",
          "Dob": "1991-11-06",
          "Age": 34,
          "Gender": "Female",
          "Citizenship": "South African",
          "DateIssued": ""
        },
        "transaction_id": "19031247"
      },
      "transaction_id": "19031247",
      "meta": {
        "environment": "production",
        "timestamp": "2026-05-26T07:54:16.776Z"
      }
    }
  }
}
4

Understand Idempotency

Prevent duplicate charges on retries

Required for Production

The Idempotency-Key header is required for all production API calls. It prevents duplicate charges.

How It Works

  1. Generate a unique key (UUID) for each unique request
  2. Send this key in the Idempotency-Key header
  3. If you retry with the same key, you get the cached response (no duplicate charge)
  4. Keys expire after 30 days

What Counts as Duplicate

ScenarioResult
Same Key + Same BodyReturns cached response (no charge)
Same Key + Different Body409 Conflict error
Different Key + Same BodyNew request (charges credits)
5

Test in Sandbox Mode

Risk-free testing before going live

Before going live, test everything with sandbox mode:

{
  "mode": "sandbox"
}

Sandbox Mode Benefits

  • Uses 0 credits
  • Returns realistic mock data
  • Works with any valid-format ID number
6

Handle Errors

Graceful error handling for production

Always handle API errors gracefully:

CodeMeaningAction
200SuccessProcess the response
400Invalid parametersCheck your request body
401Invalid API keyCheck your API key
402Insufficient creditsTop up your account
409Idempotency conflictUse a new Idempotency-Key
429Rate limitedSlow down requests
7

Go Live

Deploy to production

When ready for production:

  1. Switch to production mode:"mode": "production"
  2. Ensure you have credits - Check balance via GET /my_credits
  3. Always include Idempotency-Key for production requests
  4. Monitor your usage in the VerifyNow dashboard

Security Checklist

  • API key stored as environment variable
  • API key never exposed in client-side code
  • Idempotency-Key used for all production requests
  • Error handling implemented
  • Tested in sandbox mode first

Property Search by ID

Send a valid South African ID number to POST /property-search. Production requests need an Idempotency-Key.

A completed production search uses the current credits shown on the pricing table, including a result with found: false.

View endpoint details

Africa ID Verification

Start a job with POST /africa-verification, then fetch its result from GET /africa-verification/jobs/{jobId}.

The production POST uses the current credits shown on the pricing table. Status requests use 0 credits.

View supported countries

Consumer/Person Trace: All-in-One Endpoint

The Consumer/Person Trace endpoint (reportType: "consumer_trace") returns ALL of the following in a single call:

AddressData

Current and historical addresses (residential & postal)

EmploymentData

Employment history with employer names

ContactData

Phone numbers (cell and landline)

DefiniteMatchData

ID verification details

No need to make separate calls for address, employment, or phone data - it's all included.

Need Help?

For API support, contact us at support@verifynow.co.za

VerifyNow API overview

The VerifyNow REST API lets South African businesses run identity verification, AML/PEP screening, CIPC checks, bank account verification, face match, and vehicle lookup from their own application. Protected routes use the x-api-key header; responses are JSON and follow the schema documented for each endpoint.

Endpoints

Identity verification

POST /verify
SA ID verification, identity reports, and supported verification bundles.
POST /id-enhanced
Offline ID-photo verification with identity status fields.
POST /facematch
Supported selfie-to-reference and selfie-to-ID-photo comparisons.
POST /passive-liveness
Passive liveness assessment from a supplied face image.
POST /age-estimate
Age estimate and passive-liveness context from a supplied face image.

Compliance screening

POST /aml-screening
AML, PEP, sanctions, and supported risk-dataset screening.
POST /consumer-trace-lite
Focused identity, status, contact, and address lookup.
POST /address-lookup
Last-known recorded physical address lookup by SA ID.
POST /property-search
Property records linked to an authorised SA ID.

Business and account verification

POST /bank-account-verification
Bank account ownership and validity verification.
POST /cipc
CIPC company matching and director searches selected by reportType.

Extended verification

POST /africa-verification
Start a supported African identity verification job.
GET /africa-verification/jobs/{jobId}
Poll an Africa verification job owned by the API-key user.
POST /igaming-onboarding
iGaming onboarding pack with identity, age, face, duplicate, and optional AML checks.
POST /id-document-verify
ID document authentication with OCR and fraud signals.
POST /vehicle
Vehicle specification lookup by registration number or supported VIN mode.

Utility

GET /my_credits
Current personal or organisation API credit balance.
GET /health
Public service health response; no API key required.

Developer FAQs

How do I authenticate requests to the VerifyNow API?
Protected external routes use the x-api-key header. GET /health is public. Production POST requests also require an Idempotency-Key header for safe retries.
What response format does the API use?
External endpoints return JSON, but fields and status codes vary by service. Use the request and response schema shown for the specific endpoint.
How does sandbox mode work?
Use the same API key, endpoint, and request fields, then set mode to sandbox. Sandbox calls use mock responses and do not consume credits. Some sandbox routes enforce a short per-IP cooldown.
How should production requests be retried?
Send an Idempotency-Key with every production POST request. Reuse it only when retrying the exact same request body; use a new key for a new request.
Where can I confirm retention and hosting details?
Retention, storage, and provider boundaries vary by service. Confirm the applicable contract, DPA, sub-processor list, and service documentation instead of assuming one universal retention or residency rule.