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. Submit JPG/JPEG, PNG, WEBP or TIFF image data up to 5 MB per image.

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.

Proof of Address Check

POST /api/external/proof-of-address accepts multipart/form-data with document and mode (sandbox or production). Send x-api-key and a unique Idempotency-Key; reuse the key only when retrying identical input. One completed production result costs 3 credits, including a decline. Technical failures are refunded. Sandbox returns fictional data without provider processing or credits; use a fictional document for testing.

Accepted documents: utility bills, bank statements and government address documents issued within three months. PDF, JPG/JPEG, PNG, WebP and TIFF are supported, including multi-page PDFs. Direct requests support up to 4 MB. For files up to 15 MB, POST JSON with action: upload, mode: production, filename, size and content_type; PUT the file to uploadUrl with the returned headers; then POST mode and document_reference set to the returned reference. References expire after 15 minutes.

Optional fields: expected_first_name, expected_last_name, expected_address, expected_country (two- or three-letter code) and document_password. Compare results.name_on_document, address and formatted_address with your supplied values. results.status preserves the automated Approved or Declined decision; results.warnings contains code and message. Matching-only findings display as Review required in dashboard/PDF reports. Unprovided expected fields are omitted from those comparisons. A completed declined check is a valid verification result, not a technical error.

Document checks cover extracted details, age and signs of alteration or inconsistent metadata. No warning reported is not a guarantee that every forensic test ran, that a document is authentic or that someone resides there. Review findings under your acceptance policy.

Documents are used for processing and retry only. Temporary uploads are deleted after processing or expiry; documents are not archived. Results are held for up to 72 hours for safe retries. Billing, security and audit metadata is retained. Establish consent or another lawful basis and provide the required privacy notice before uploading. See /privacy and /terms.

Service and sample report: https://www.verifynow.co.za/services/proof-of-address#sample-report API reference: https://www.verifynow.co.za/api-docs#proof-of-address

Proof of Address API reference and playground

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.

Driver's Licence Verification

Send the back-of-card image or complete barcode payload to POST /drivers-licence. The response includes the decoded licence fields, validation checks, categories and embedded portrait. Choose an additive bundle when the workflow also needs an identity record, Home Affairs Face Match, current selfie comparison or Passive Liveness.

Vehicle Licence Disc Scanner

Send a disc image or decoded barcode text to POST /vehicle-licence-disc. Choose a barcode or number plate comparison report. VIN comparison requires an enabled API account. Image integrations use visible field enrichment by default and can set include_visual_fields to false to skip it. allow_visual_fallback separately enables a visual-only result when the PDF417 barcode cannot be read.

Comprehensive Vehicle Check

Send a South African registrationNumber to POST /vehicle-check. Add the optional 17-character vin for the same vehicle enquiry. Plate-only requests return lookupType: registration and vehicleMatched: null. One response groups the available registration, VIN, vehicle identity, licensing, technical specifications, roadworthy details, security fields and supplementary data.

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"
      }
    }
  }
}

Passport authentication

Authenticate a South African or supported foreign passport with POST /api/external/id-document-verify and bundle id_document_verification. Upload the full passport biodata page as front_image, or front_image_base64 for JSON. Include the page containing the MRZ. A second image is optional when the document requires another side or page. Production requests use 3 credits per completed check.

Passport response and extracted fields

Read results.id_verification for the document result. This redacted excerpt comes from a completed production passport check on 10 October 2026. Identity values, dates and image bytes use placeholders. Additional fields, including image-quality scores and remainingCredits, can appear in the full response. Preserve nulls and missing fields when extraction supplies no value.

Redacted production passport response
{
  "success": true,
  "requestId": "<your-idempotency-key>",
  "results": {
    "request_id": "<verification-reference>",
    "id_verification": {
      "status": "Approved",
      "document_type": "Passport",
      "document_subtype": "PASSPORT_GENERIC",
      "document_number": "<redacted>",
      "personal_number": "<redacted>",
      "first_name": "<redacted>",
      "last_name": "<redacted>",
      "full_name": "<redacted>",
      "issuing_state": "ZAF",
      "issuing_state_name": "South Africa",
      "nationality": "ZAF",
      "date_of_birth": "<YYYY-MM-DD>",
      "expiration_date": "<YYYY-MM-DD>",
      "date_of_issue": "<YYYY-MM-DD>",
      "portrait_image": "<base64 JPEG portrait omitted>",
      "front_image": "<base64 JPEG document image omitted>",
      "back_image": null,
      "mrz": {
        "surname": "<redacted>",
        "name": "<redacted>",
        "country": "ZAF",
        "nationality": "ZAF",
        "birth_date": "<YYMMDD>",
        "expiry_date": "<YYMMDD>",
        "sex": "<redacted>",
        "document_type": "P",
        "document_number": "<redacted>",
        "optional_data": "<redacted>",
        "birth_date_hash": "9",
        "expiry_date_hash": "0",
        "document_number_hash": "3",
        "optional_data_hash": "4",
        "final_hash": "4",
        "personal_number": "<redacted>",
        "warnings": [],
        "errors": [],
        "mrz_type": "TD3",
        "mrz_string": "<two MRZ lines redacted>",
        "mrz_key": "<redacted>"
      },
      "warnings": []
    }
  }
}
Field under results.id_verificationMeaning
document_numberPassport number extracted from the document.
nationalityHolder nationality as a three-letter country code.
issuing_state / issuing_state_nameIssuing-country code and name. issuing_country is an optional request hint; the observed production response uses issuing_state.
date_of_birthDate of birth in YYYY-MM-DD format.
expiration_date / date_of_issueExpiry and issue dates in YYYY-MM-DD format, when extracted.
first_name / last_name / full_nameExtracted holder names.
portrait_imageCropped portrait as a base64 JPEG string when extracted. Check that it is populated before Face Match.
front_image / back_imageReturned document images. back_image was null for the single-page passport example.

Machine-readable zone and check digits

The mrz object contains the decoded machine-readable zone. A passport uses the TD3 layout in the observed example. Check-digit validation forms part of MRZ checking. All five check digits in the observed production passport passed a local recalculation against the returned MRZ. The response supplies the check-digit values and error/warning arrays; it has no separate mrz_valid boolean in this example.

MRZ fieldMeaning
mrz_type / mrz_stringLayout and raw MRZ text, with line breaks between the lines.
surname / nameFamily and given names read from the MRZ.
country / nationalityIssuing-country and holder-nationality codes.
birth_date / expiry_dateMRZ dates in YYMMDD format. Use the top-level document dates for YYYY-MM-DD values.
document_type / document_number / sexMRZ document type, passport number and sex marker.
optional_data / personal_numberAdditional encoded information where present. These fields can contain identity numbers.
document_number_hash / birth_date_hash / expiry_date_hashPrinted check digits for the document number, date of birth and expiry date.
optional_data_hash / final_hashOptional-data check digit where returned, and the composite check digit.
mrz_keyComposite MRZ key where returned.
errors / warningsMRZ parsing and validation findings. Read these with the document-level warnings.

Document status and authenticity signals

Treat results.id_verification.status as an open string. The endpoint passes through the document decision; an exhaustive production enum has not been confirmed for this integration. The table explains client handling for the verdicts covered by the existing API guidance. Preserve additional values for review. HTTP 200 and success: true mean the request completed; your acceptance decision must use the document status, warnings and required fields.

Returned statusClient handling
ApprovedAssess the passed document decision against your required fields and returned warnings. This was the observed production passport result.
DeclinedTreat the document as a failed check and inspect the warnings.
In ReviewSend the result to your review process. A returned review verdict is a completed response.
Other, missing or nullPreserve the raw value and send the result for review before acceptance.

Authenticity findings concern the submitted images. Document-level warnings contain a risk code, log_type severity and descriptions. The signal reference below describes documented document-check risks; the complete deployed warning-code set remains open. The observed passport returned warnings: [], mrz.errors: [] and mrz.warnings: []. The example includes image-quality scores. Separate numeric authenticity and tampering scores were absent. Treat absent forensic scores as unavailable evidence.

Signal referenceMeaning
MRZ_VALIDATION_FAILEDMRZ checksum or format validation failed.
MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAMEMRZ data and visible document text disagree.
SCREEN_CAPTURE_DETECTEDThe document image appears to have been captured from a screen.
PRINTED_COPY_DETECTEDThe image appears to show a printed copy.
PORTRAIT_MANIPULATION_DETECTEDThe document portrait appears altered or replaced.
DOCUMENT_EXPIREDThe detected document expiry date has passed.

This endpoint performs document-image checks. Issuing-authority record verification and NFC chip authentication require separate checks. A returned document decision should be considered together with your onboarding policy.

Use the passport portrait for Face Match

When the document meets your acceptance policy and portrait_image is populated, copy results.id_verification.portrait_image unchanged into reference_image_base64. Submit a current selfie as selfie_image_base64 to POST /api/external/facematch with bundle facematch_standard. This compares the two supplied images for South African and foreign passport holders. Each image has a maximum decoded size of 5 MB.

Face Match request body
{
  "bundle": "facematch_standard",
  "mode": "production",
  "reference_image_base64": "<portrait_image from the document response>",
  "selfie_image_base64": "<base64 selfie>"
}

Send x-api-key, Content-Type: application/json and a new Idempotency-Key for the Face Match operation. Reuse that key for retries with the same images. Read results.face_match.status and results.face_match.score. Face Match is a separate chargeable request. Assess selfie liveness through the separate Passive Liveness endpoint when your workflow requires it.

Request timing and retries

The initial passport request waits for processing and returns the completed result in that HTTP response. The processing call has a 55-second timeout. A concurrent retry with the same Idempotency-Key can return HTTP 202 with {"status":"pending"}. Repeat the same key and unchanged payload to retrieve a completed cached result. Processing failures and timeouts return HTTP 502. There is no passport job-polling URL or completion webhook in this endpoint.

Other request outcomes include 400 for invalid input, 401 for authentication failure, 402 for insufficient credits and 409 for an idempotency conflict or cached failure. Sandbox cooldown can return 429. The sandbox supplies mock data; use the production excerpt above to understand passport field placement.

Image transport, processing and retention

Images travel over HTTPS from your server to VerifyNow and onward to the document-processing service. HTTPS protects transport; the processing service decodes the image to analyse the document. The VerifyNow API runtime is configured for Cape Town. The processing service publishes AWS Ireland (EU) as its default region. Account-specific processing placement and cache replica/backup locations require confirmation for any contractual residency commitment.

The endpoint handles uploaded files for processing and does not write the original uploads to a document archive. Saved verification sessions are disabled for the processing request. The returned portrait and document images are included in the API response, and VerifyNow caches that complete response for up to 72 hours to support idempotent retries. This temporary cache is image retention. Cache expiry describes the active replay record; backup and operational-log retention require separate confirmation.

Supported passport issuing countries

The catalogue below lists passport-supported issuing countries and territories, including South Africa. Coverage was reviewed on 10 October 2026. Recognition depends on the document version and image quality. Use the passport issuing country when supplying the optional issuing_country hint; holder nationality is a separate response field. Catalogue entries include territories and special issuing authorities, so some codes are catalogue identifiers rather than ISO assignments.

Browse 218 passport issuing countries and territories

Catalogue reviewed 2026-10-10

218 of 218 entries

CodePassport issuing country / territory
ABKAbkhazia
AFGAfghanistan
ALBAlbania
DZAAlgeria
ANDAndorra
AGOAngola
AIAAnguilla
ATGAntigua and Barbuda
ARGArgentina
ARMArmenia
AUSAustralia
AUTAustria
AZEAzerbaijan
BHSBahamas
BHRBahrain
BGDBangladesh
BRBBarbados
BLRBelarus
BELBelgium
BLZBelize
BENBenin
BMUBermuda
BTNBhutan
BOLBolivia, Plurinational State of
BIHBosnia and Herzegovina
BWABotswana
BRABrazil
BRNBrunei Darussalam
BGRBulgaria
BFABurkina Faso
BDIBurundi
CPVCabo Verde
KHMCambodia
CMRCameroon
CANCanada
CYMCayman Islands
CAFCentral African Republic
TCDChad
CHLChile
CHNChina
COLColombia
COMComoros
COGCongo
CODCongo, The Democratic Republic of the
CRICosta Rica
HRVCroatia
CUBCuba
CYPCyprus
CZECzechia
CIVCôte d'Ivoire
DNKDenmark
DJIDjibouti
DMADominica
DOMDominican Republic
DPRDonetsk People's Republic
ECUEcuador
EGYEgypt
SLVEl Salvador
GNQEquatorial Guinea
ERIEritrea
ESTEstonia
SWZEswatini
ETHEthiopia
FROFaroe Islands
FJIFiji
FINFinland
FRAFrance
GABGabon
GMBGambia
GEOGeorgia
DEUGermany
GHAGhana
GIBGibraltar
GRCGreece
GRDGrenada
GTMGuatemala
GGYGuernsey
GINGuinea
GNBGuinea-Bissau
GUYGuyana
HTIHaiti
VATHoly See (Vatican City State)
HNDHonduras
HKGHong Kong
HUNHungary
ISLIceland
INDIndia
IDNIndonesia
IRNIran, Islamic Republic of
IRQIraq
IRLIreland
IMNIsle of Man
ISRIsrael
ITAItaly
JAMJamaica
JPNJapan
JEYJersey
JORJordan
KAZKazakhstan
KENKenya
KIRKiribati
PRKKorea, Democratic People's Republic of
KORKorea, Republic of
RKSKosovo
KWTKuwait
KGZKyrgyzstan
LAOLao People's Democratic Republic
LVALatvia
LBNLebanon
LSOLesotho
LBRLiberia
LBYLibya
LIELiechtenstein
LTULithuania
LPRLugansk People's Republic
LUXLuxembourg
MACMacao
MDGMadagascar
MWIMalawi
MYSMalaysia
MDVMaldives
MLIMali
MLTMalta
MHLMarshall Islands
MRTMauritania
MUSMauritius
MEXMexico
FSMMicronesia, Federated States of
MDAMoldova, Republic of
MCOMonaco
MNGMongolia
MNEMontenegro
MSRMontserrat
MARMorocco
MOZMozambique
MMRMyanmar
NAMNamibia
NRUNauru
NPLNepal
NLDNetherlands
NZLNew Zealand
NICNicaragua
NERNiger
NGANigeria
MKDNorth Macedonia
NORNorway
OMNOman
PAKPakistan
PLWPalau
PSEPalestine
PANPanama
PNGPapua New Guinea
PRYParaguay
PERPeru
PHLPhilippines
POLPoland
PRTPortugal
QATQatar
ROURomania
RUSRussian Federation
RWARwanda
SHNSaint Helena
KNASaint Kitts and Nevis
LCASaint Lucia
VCTSaint Vincent and the Grenadines
WSMSamoa
SMRSan Marino
STPSao Tome and Principe
SAUSaudi Arabia
SENSenegal
SRBSerbia
SYCSeychelles
SLESierra Leone
SGPSingapore
SVKSlovakia
SVNSlovenia
SLBSolomon Islands
SOMSomalia
RSLSomaliland
ZAFSouth Africa
SSDSouth Sudan
XOMSovereign Military Order of Malta
ESPSpain
LKASri Lanka
SDNSudan
SURSuriname
SWESweden
CHESwitzerland
SYRSyrian Arab Republic
TWNTaiwan
TJKTajikistan
TZATanzania, United Republic of
THAThailand
TLSTimor-Leste
TGOTogo
TONTonga
TTOTrinidad and Tobago
TUNTunisia
TKMTurkmenistan
TCATurks and Caicos Islands
TUVTuvalu
TURTürkiye
UGAUganda
UKRUkraine
AREUnited Arab Emirates
GBRUnited Kingdom
USAUnited States
URYUruguay
UZBUzbekistan
VUTVanuatu
VENVenezuela, Bolivarian Republic of
VNMViet Nam
VGBVirgin Islands, British
ESHWestern Sahara
YEMYemen
ZMBZambia
ZWEZimbabwe
ALAÅland Islands
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 72 hours

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
  • Reserved 90 / 91 inputs force negative and error paths. Sandbox test triggers
  • Repeating the same sandbox payload from the same IP returns 429 after a 2-second cooldown. Distinct reserved inputs run immediately. Playground, Postman, and curl share this lock.
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

Adverse Media Search API

Use POST /api/external/adverse-media with name, entity (0 Person or 1 Company), and mode (sandbox or production). Supply x-api-key; production also requires an Idempotency-Key of 8–160 characters.

{"name":"Jane Example 90","entity":0,"mode":"sandbox"}

Read results.articles for article evidence and results.screening for available premium screening findings. A completed search with no findings is chargeable; sandbox uses zero credits. Review potential matches against the source evidence.

Retry the same payload with the same key within 48 hours for an encrypted replay without another charge. Handle 202 and 429 using Retry-After. Names ending in 90 return no sandbox findings; 91 simulates failure. This separate endpoint does not enrol a subject.

View Adverse Media API fields and examples

/aml-screening is the one-shot screen. /aml-ongoing enrols that customer and rescreens them later. Sandbox and production ongoing responses include results.Header.DatasetVersion and results.Header.DatasetPublishedAt. VerifyNow AML is a licensed ongoing AML and ODD API. Enrol, rescreen, and withdraw identified customers with name plus identifier. A dataset webhook and email notify list changes; rescreen remains your call. Set the HTTPS URL under Settings → Ongoing AML → Dataset update webhook. Credentials and the production path are issued after the licence. Read the VerifyNow AML webhook setup.

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

Foreign passport bank verification

POST /api/external/foreign-bank-account-verification supports South African bank accounts held by foreign passport holders. Send firstName, surname, passportNumber, passportCountry (two-letter ISO country code), bankName, bankAccountNumber, bankBranchCode, bankAccountType, consentConfirmed: true, purpose and mode. Use x-api-key, Content-Type: application/json and an Idempotency-Key of 8–160 characters in both modes. A completed real-time check returns HTTP 200 with processingMode: "realtime", status: "completed", requestId, creditsUsed and result. Inspect result.outcome and result.checks to decide whether a payment needs review. This response creates no job or webhook. Foreign-passport checks use the standard bank-account verification credit price, with a fallback configuration of 6 credits. See /pricing#foreign-bank-account-verification for the configured price. Supply webhookUrl and webhookSecret together to enable asynchronous fallback. If real-time verification cannot complete and these fields are present, HTTP 202 returns a jobId for status reads and signed callbacks. Without webhook fields, fallback returns HTTP 503, code ASYNC_FALLBACK_REQUIRES_WEBHOOK and creditsUsed: 0. The asynchronous timing, job recovery and webhook examples below apply to this fallback and to Capitec Business. Sandbox uses synthetic details and zero credits. Omit webhook fields for the real-time sandbox response; include both fields to exercise the asynchronous sandbox flow. Production retries must retain the original body and Idempotency-Key. A changed body needs a new key; do not treat a job or an HTTP success alone as a positive account match. Capitec Business and Mercantile Bank company checks use POST /api/external/business-bank-account-verification, branch code 450105, and the separate business-bank price (fallback configuration: 7 credits). Their response remains HTTP 202 with a jobId. See /api-docs/foreign-bank-account-verification for the response examples and webhook recovery guide.
Foreign passport bank verification pricing

Bank job status and report recovery

This section covers asynchronous submissions and fallback jobs. Completed real-time foreign-passport responses use a requestId and return the result in the POST response.

Foreign passport and Capitec Business submissions return a jobId. Use GET /api/external/foreign-bank-account-verification/jobs/{jobId} or GET /api/external/business-bank-account-verification/jobs/{jobId} with the submitting account’s API key to read status and saved results at zero credits.

POST an empty JSON object to the job URL followed by /retry-webhook to redeliver a completed result. Provide webhookUrl and webhookSecret together to change the receiver. The playground includes both recovery actions for sandbox and production jobs. Dashboard reports include the requester, submitted details and elapsed run time, with authenticated PDFs available while the submitted details remain within their seven-day retention period.

Bank sandbox, production and webhook recovery guide