VerSys
API keys

API Reference

Programmatic access to VAT, IBAN, card, EORI, company registry and bank account verification. All responses are JSON.

Overview

The base URL for all endpoints is:

https://www.versys.io/api

All requests must use HTTPS. Request bodies must use Content-Type: application/json.

Authentication

Pass your API key in the Authorization header:

Authorization: Bearer vsk_your_api_key_here

API keys are created in your account dashboard under My Account → API Keys. Each key is tied to your account and deducts from your credit balance when VIES lookups are performed.

Format-only checks (VAT format, IBAN, card Luhn, EORI format) are free and do not require an API key or account. VIES live EU VAT verification and EORI live registry lookups require authentication and deduct credits.
GB VAT numbers: Format and checksum validation works for all GB numbers. Live verification uses the HMRC VAT registry when HMRC credentials are configured. If HMRC is not configured for the current environment, vies_checked will be false and vies_error will read "UK VAT verification coming soon".

Credits & rate limits

OperationCost
VAT format + checksum checkFree
IBAN format + checksum checkFree
Card Luhn checkFree
BIN / issuer lookupFree
VIES live lookup (fresh)2 credits
VIES cached result (<24 h old)1 credit
EORI format checkFree
EORI live registry lookup1 credit

Free accounts receive 40 credits/month. Pro accounts receive 400 credits/month.

Unauthenticated format checks are limited to 100 requests per hour per IP address.

The current credit_balance is returned in every authenticated response.

Error codes

HTTPerrorMeaning
400MissingFieldA required field is absent or the wrong type
400BatchTooLargeMore than 100 items in a batch request
401UnauthorizedNo valid API key or session cookie (VIES requires auth)
402InsufficientCreditsNot enough credits for this request
422InvalidFormat / InvalidChecksum / InvalidCountryInput failed validation
429RateLimitExceededHourly anonymous limit reached — sign in for higher limits
503ViesUnavailableVIES or a member state registry is temporarily offline
500ServerErrorUnexpected server error

All error responses include an error code string and an error_detail human-readable message.

VAT — single check

POST /api/check Free +2 credits with VIES

Validate a VAT number and optionally verify it against the EU VIES registry.

Request body

FieldTypeRequiredDescription
vatstringYesVAT number with 2-letter country prefix, e.g. DE129273398
viesbooleanNoIf true, perform a VIES live lookup (requires authentication)
vies_forcebooleanNoBypass the 24-hour cache and force a fresh VIES request

Example — format check (no auth needed)

curl https://www.versys.io/api/check \
  -H "Content-Type: application/json" \
  -d '{"vat": "DE129273398"}'

Example — VIES live lookup

curl https://www.versys.io/api/check \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{"vat": "FR40303265045", "vies": true}'

Response fields

FieldTypeDescription
validbooleanPasses format and checksum validation
vatstringNormalised VAT number (uppercase, no spaces)
vat_formattedstringFormatted with spaces for readability
country_codestringISO 3166-1 alpha-2 country code
country_namestringFull country name
numberstringLocal number without the country prefix
format_validbooleanCountry-specific format check result
vies_checkedbooleantrue if a live VIES/HMRC lookup was performed; false if format-only or lookup unavailable
vies_validboolean|nullWhether the number is currently registered; null if not checked
vies_namestring|nullRegistered business name (some countries withhold this for privacy)
vies_addressstring|nullRegistered address (some countries withhold this for privacy)
vies_request_datestringDate the lookup was performed (YYYY-MM-DD)
vies_from_cachebooleantrue if the result was served from the 24-hour cache (costs 1 credit instead of 2)
vies_sourcestring"VIES" for EU numbers; "HMRC" for GB numbers (when available)
vies_errorstring|nullHuman-readable reason if the lookup was not performed
credit_balanceintegerRemaining credits after this request (authenticated only)

VAT — batch check

POST /api/batch Free +credits with VIES

Validate up to 100 VAT numbers in a single request.

Request body

FieldTypeRequiredDescription
vatsstring[]YesArray of VAT numbers (max 100)
viesbooleanNoPerform VIES lookup on all valid numbers (requires auth)
vies_forcebooleanNoBypass the 24-hour cache

Example

curl https://www.versys.io/api/batch \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "vats": ["DE129273398", "FR40303265045", "GB980780684"],
    "vies": true
  }'

Response

Returns {"count": N, "results": [...], "credit_balance": N}. Each item in results has the same fields as the single check response, in the same order as the input array.

VAT compliance check

POST /api/compliance/check +credits with VIES

Check a client's VAT number, compare the submitted name with registry data, and return reverse-charge guidance for your invoice workflow.

If you are authenticated and have saved your business country and VAT number in your profile, the API will use those values automatically when supplier_country or supplier_vat are omitted.

Request body

FieldTypeRequiredDescription
client_namestringYesYour client's legal or trading name
client_vatstringYesYour client's VAT number
supplier_countrystringConditionallyYour 2-letter business country code. Optional if saved in your authenticated profile.
supplier_vatstringNoYour own VAT number. Optional if saved in your authenticated profile.
with_viesbooleanNoIf true, include a live VIES/HMRC validation of the client VAT number
vies_forcebooleanNoBypass the normal cache for the live lookup

Example

curl https://www.versys.io/api/compliance/check \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme GmbH",
    "client_vat": "DE129273398",
    "with_vies": true
  }'

Response fields

FieldTypeDescription
client_namestringThe submitted client name
client_vatstringThe submitted client VAT number
supplier_countrystringYour effective business country used for the reverse-charge analysis
supplier_vatstring|nullYour effective VAT number, if supplied or available in your profile
name_matchobjectComparison between the submitted client name and the official registry name, where available
reverse_chargeobjectHigh-level reverse-charge guidance based on supplier and client countries
vies_checkedboolean|nullWhether a live registry lookup was performed
vies_notestring|nullExplanation if a live lookup was not requested or not available
credit_balanceintegerRemaining credits after the lookup (authenticated requests only)

IBAN — single check

POST /api/iban/check Free

Validate an IBAN using the ISO 13616 checksum and look up bank details where available.

Request body

FieldTypeRequiredDescription
ibanstringYesIBAN with or without spaces, e.g. DE89 3704 0044 0532 0130 00

Example

curl https://www.versys.io/api/iban/check \
  -H "Content-Type: application/json" \
  -d '{"iban": "DE89370400440532013000"}'

Response fields

FieldTypeDescription
validbooleanPasses format and ISO 13616 checksum
ibanstringNormalised IBAN (uppercase, no spaces)
iban_formattedstringGrouped in blocks of 4 characters
country_codestring2-letter country code of the account
country_namestringFull country name
in_sepa_zonebooleanWhether the country participates in SEPA
checksum_digitsstringThe 2-digit ISO checksum
bank_codestringBank identifier portion of the BBAN
branch_codestring|nullBranch code (where applicable)
account_codestringAccount number portion of the BBAN
bicstring|nullBIC/SWIFT code (where available in registry)

IBAN — batch check

POST /api/iban/batch Free

Validate up to 100 IBANs in a single request.

Request body

FieldTypeRequiredDescription
ibansstring[]YesArray of IBANs (max 100)

Example

curl https://www.versys.io/api/iban/batch \
  -H "Content-Type: application/json" \
  -d '{
    "ibans": ["DE89370400440532013000", "GB82WEST12345698765432"]
  }'

Returns {"count": N, "results": [...]} where each item has the same fields as the single check.

Card — single check

POST /api/card/check Free

Validate a payment card number using the Luhn algorithm and look up issuer details by BIN.

Request body

FieldTypeRequiredDescription
card_numberstringYesCard number — digits only or with spaces/dashes, e.g. 4111 1111 1111 1111

Example

curl https://www.versys.io/api/card/check \
  -H "Content-Type: application/json" \
  -d '{"card_number": "4111111111111111"}'

Response fields

FieldTypeDescription
validbooleanPasses Luhn checksum
luhn_validbooleanExplicit Luhn result
card_maskedstringNumber with middle digits replaced by *
last4stringLast 4 digits
lengthintegerTotal number of digits
brandstringDetected card brand (Visa, Mastercard, Amex, etc.)
binstring8-digit BIN (Bank Identification Number)
bin_lookupobjectIssuer details: bank_name, scheme, card_type, country_code, country_name, prepaid

Card — batch check

POST /api/card/batch Free

Validate up to 100 card numbers in a single request.

Request body

FieldTypeRequiredDescription
cardsstring[]YesArray of card numbers (max 100)

Example

curl https://www.versys.io/api/card/batch \
  -H "Content-Type: application/json" \
  -d '{
    "cards": ["4111111111111111", "5500005555555559"]
  }'

Returns {"count": N, "results": [...]} where each item has the same fields as the single check.

BIN lookup

POST /api/bin/lookup Free

Look up issuer information for a BIN (Bank Identification Number) or full card number. The first 6–8 digits are used; the rest are ignored.

Request body

FieldTypeRequiredDescription
binstringYes*First 6–8 digits of the card
card_numberstringYes*Full card number (BIN extracted automatically)

* Provide either bin or card_number.

Example

curl https://www.versys.io/api/bin/lookup \
  -H "Content-Type: application/json" \
  -d '{"bin": "41111111"}'

Response fields

FieldDescription
bin_lookup_foundtrue if the BIN was found in the registry
bank_nameIssuing bank name
schemeCard scheme (visa, mastercard, amex, etc.)
brandProduct brand (e.g. Visa Classic, Mastercard World)
card_typedebit, credit or prepaid
country_codeIssuing country
country_nameIssuing country full name
prepaidboolean or null

EORI — single check

POST /api/eori/check Free +1 credit with lookup

Validate an EORI (Economic Operators Registration and Identification) number and optionally look it up against the EU customs registry or the UK HMRC registry.

Format validation is always free. Live registry lookups require authentication and cost 1 credit. Results are cached for 30 days — a cached hit still costs 1 credit and returns lookup_from_cache: true.

Request body

FieldTypeRequiredDescription
eoristringYesEORI number with 2-letter country prefix, e.g. GB123456789012 or DE123456789012345
lookupbooleanNoIf true, perform a live registry lookup (requires authentication)
lookup_forcebooleanNoBypass the 30-day cache and force a fresh registry request

Example — format check (no auth needed)

curl https://www.versys.io/api/eori/check \
  -H "Content-Type: application/json" \
  -d '{"eori": "GB123456789012"}'

Example — live registry lookup

curl https://www.versys.io/api/eori/check \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{"eori": "DE123456789012345", "lookup": true}'

Response fields

FieldTypeDescription
validbooleanPasses EORI format validation
eoristringNormalised EORI (uppercase, no spaces)
eori_formattedstringFormatted with a space after the country prefix
country_codestring2-letter country prefix
country_namestringFull country name
format_validbooleanCountry-specific format pattern check
known_countrybooleanfalse for country codes that do not issue EORI numbers (e.g. CH, NO)
lookup_checkedbooleantrue if a registry lookup was performed
lookup_validboolean|nullWhether the EORI exists in the registry; null if not checked
lookup_sourcestring|null"eu_soap" for EU-27 numbers; "hmrc" for GB/XI numbers
lookup_namestring|nullRegistered operator name (when operator has consented to disclosure)
lookup_addressstring|nullRegistered street address
lookup_citystring|nullRegistered city
lookup_postcodestring|nullRegistered postcode
lookup_countrystring|nullCountry from the registry record
lookup_datestring|nullDate of the registry response (ISO 8601)
lookup_from_cachebooleantrue if the result was served from the 30-day cache
lookup_errorstring|nullHuman-readable reason if the lookup could not be completed
credit_balanceintegerRemaining credits (authenticated requests only)

EORI — batch check

POST /api/eori/batch Free

Validate up to 100 EORI numbers in a single request. Requires authentication. Format validation only — live registry lookup is not available in batch mode.

Request body

FieldTypeRequiredDescription
eoristring[]YesArray of EORI numbers (max 100)

Example

curl https://www.versys.io/api/eori/batch \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "eori": ["GB123456789012", "DE123456789012345", "FR12345678901234"]
  }'

Returns {"count": N, "results": [...]} where each item has the same fields as the single check response (without lookup fields). Each result also includes an index field matching its position in the input array.

Company — single lookup

POST /api/company/lookup Free +2 credits with VIES

Validate a VAT number format, look up company data from GLEIF and national registries (free), and optionally add a live VIES EU registration check (costs credits).

Format validation is always free. Registry lookup ("lookup": true) requires authentication but costs no credits — it queries GLEIF, OpenCorporates, and country-specific registries. VIES verification ("vies": true) requires authentication and costs 2 credits live or 1 if cached. Both flags can be combined.

Request body

FieldTypeRequiredDescription
vatstringYesVAT number with 2-letter country prefix, e.g. DE123475223 or GB123456789
lookupbooleanNoIf true, query GLEIF, OpenCorporates, and national registries (free, requires auth)
viesbooleanNoIf true, run a live EU VIES check (2 credits live, 1 if cached; requires auth)
namestringNoExpected company name — used for name-consistency check and to improve GLEIF/OpenCorporates matching

Example — format check (no auth needed)

curl https://www.versys.io/api/company/lookup \
  -H "Content-Type: application/json" \
  -d '{"vat": "DE123475223"}'

Example — registry lookup (GLEIF + LEI, free)

curl https://www.versys.io/api/company/lookup \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{"vat": "DE123475223", "lookup": true, "name": "Deutsche Telekom AG"}'

Example — registry + VIES (full check)

curl https://www.versys.io/api/company/lookup \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{"vat": "DE123475223", "lookup": true, "vies": true}'

Response fields

FieldTypeDescription
validbooleanVAT number passes format validation
format_validbooleanCountry-specific format check result
vatstringNormalised VAT number (uppercase, no spaces)
vat_formattedstringCountry-formatted VAT number
country_codestring2-letter ISO country code
country_namestringFull country name
vies_validboolean|nullVIES registry status; null if lookup not performed
vies_namestring|nullCompany name from VIES (may be withheld — e.g. Germany returns "---")
vies_addressstring|nullAddress from VIES (may be withheld)
vies_checkedbooleantrue if a VIES check was performed
vies_cachedbooleantrue if the VIES result came from cache
registry_enrichedbooleantrue if at least one registry source returned data
registry_sourcesstring[]List of sources that contributed data, e.g. ["gleif", "opencorporates"]
registry_namestring|nullLegal name from the highest-priority registry source
registry_addressstring|nullRegistered address (street)
registry_citystring|nullRegistered city
registry_postcodestring|nullPostcode
registry_statusstring|nullactive, inactive, dissolved, or unknown
registry_numberstring|nullCompanies House / Handelsregister / equivalent registration number
registry_leistring|nullLegal Entity Identifier (from GLEIF when available)
registry_urlstring|nullLink to the company's registry record
registry_jurisdictionstring|nullOpenCorporates jurisdiction code
name_matchobject|nullName consistency result — see below
credit_balanceintegerRemaining credits (authenticated requests only)

name_match object

Present when a name hint was supplied and registry data was found, or when both VIES and registry names are available.

FieldTypeDescription
levelstringexact, strong, partial, or weak
scorenumber0–1 similarity score
submittedstring|nullThe name you supplied
registeredstring|nullThe name found in the registry

Registry source priority

When multiple sources return data, fields are merged in this priority order (highest first):

  1. Country-specific APIs: HMRC (GB), Companies House (GB), Brønnøysund Register (NO), CVR (DK), SIRENE (FR)
  2. GLEIF (global LEI database — strong coverage for large companies)
  3. OpenCorporates (broad coverage for SMEs)

Company — batch lookup

POST /api/company/batch Auth required

Look up up to 100 VAT numbers in a single request. Format validation is always performed. Pass "lookup": true for free GLEIF/registry enrichment, and/or "vies": true for VIES verification (credits charged per item with VIES).

Request body

FieldTypeRequiredDescription
vatsstring[]YesArray of VAT numbers (max 100)
lookupbooleanNoIf true, run GLEIF + registry enrichment for each (free)
viesbooleanNoIf true, run VIES verification for each (2 credits per item)

Example

curl https://www.versys.io/api/company/batch \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "vats": ["DE123475223", "GB123456789", "FR12345678901"],
    "lookup": true
  }'

Returns {"count": N, "results": [...]} where each item has the same fields as the single company lookup response.

Bank account check

POST /api/domestic/check Free

Validate a domestic bank account number using country-specific format and checksum rules. Supported countries: GB, DE, FR, ES, IT, BE, NL, NO.

If you have an IBAN, use /api/iban/check instead — domestic account details are extracted and validated automatically from the IBAN.

Request body

Always include country plus the country-specific fields shown below.

FieldTypeRequiredDescription
countrystringYes2-letter ISO country code, e.g. GB
GB (United Kingdom)
sort_codestringYes6-digit sort code, dashes optional (e.g. 20-00-00)
accountstringYes8-digit account number
DE (Germany)
blzstringYes8-digit Bankleitzahl (BLZ)
accountstringYes1–10 digit Kontonummer
FR (France)
bankstringYes5-digit bank code
branchstringYes5-digit branch code (guichet)
accountstringYes11-character account number (alphanum)
rib_keystringYes2-digit RIB check key
ES (Spain)
entitystringYes4-digit entity code
branchstringYes4-digit branch code (oficina)
dc1stringYes1-digit check digit (validates entity + branch)
dc2stringYes1-digit check digit (validates account)
accountstringYes10-digit account number
IT (Italy)
abistringYes5-digit ABI bank code
cabstringYes5-digit CAB branch code
cinstringYes1-letter CIN check character
accountstringYes12-character account number (alphanum)
BE (Belgium)
accountstringYes12-digit Belgian account number, dashes optional (BBB-AAAAAAA-CC)
NL (Netherlands)
accountstringYesUp to 10-digit account number
NO (Norway)
accountstringYes11-digit Norwegian account number

Example — GB sort code + account

curl https://www.versys.io/api/domestic/check \
  -H "Content-Type: application/json" \
  -d '{
    "country": "GB",
    "sort_code": "20-00-00",
    "account": "55779911"
  }'

Example — DE BLZ + account

curl https://www.versys.io/api/domestic/check \
  -H "Content-Type: application/json" \
  -d '{
    "country": "DE",
    "blz": "37040044",
    "account": "532013000"
  }'

Response fields

FieldTypeDescription
validbooleantrue if the account passes all applicable checks
format_validbooleanLength and character format is correct
checksum_validboolean|nullCheck digit / Mod-11 result; null if checksum not available for the country
country_codestring2-letter country code
country_namestringFull country name
validation_methodstringAlgorithm used, e.g. mod11, rib_key, format_only
account_formattedstringCanonical formatted account number (country-specific)
errorstring|nullInvalidFormat or InvalidCheckDigit on failure
error_detailstring|nullHuman-readable explanation of the error

ViDA — e-Invoicing mandate check

POST /api/vida/check Free

Returns the e-Invoicing mandate status and technical requirements for a given buyer country, supply type and transaction direction. No authentication or credits required.

Data is sourced from national tax authority publications and the OpenPeppol network. Requirements change — always verify with the relevant authority before invoicing. The response includes a data_reviewed date.

Request body

FieldTypeRequiredDescription
buyer_countrystringYes2-letter ISO country code of the buyer, e.g. DE, IT, FR
supplier_countrystringNo2-letter ISO country code of the supplier. Used to detect cross-border scenarios and generate relevant notes.
supply_typestringNob2b (default), b2g (government buyer), or b2c (consumer)

Example — B2B invoice, GB supplier → DE buyer

curl https://www.versys.io/api/vida/check \
  -H "Content-Type: application/json" \
  -d '{"buyer_country": "DE", "supplier_country": "GB", "supply_type": "b2b"}'

Example — B2G invoice to Italian public authority

curl https://www.versys.io/api/vida/check \
  -H "Content-Type: application/json" \
  -d '{"buyer_country": "IT", "supply_type": "b2g"}'

Response fields

FieldTypeDescription
buyer_countrystring2-letter country code of the buyer
buyer_country_namestringFull country name
supplier_countrystring|nullSupplier country code if provided
supply_typestringb2b, b2g, or b2c
mandate_statusstringactive — mandatory now; planned — upcoming mandate; none — no mandate
einvoice_requiredbooleantrue when the mandate is currently active
einvoice_plannedbooleantrue when a mandate is confirmed but not yet in force
mandate_sincestring|nullISO date when the mandate came (or comes) into force
mandate_scopestring|nullHuman-readable description of what the mandate covers
platformstring|nullName of the required platform or gateway (e.g. SDI, KSeF, Peppol)
formatstring|nullRequired invoice format or standard (e.g. FatturaPA, XRechnung, Peppol BIS Billing 3.0)
peppol_supportedbooleantrue if the country supports or uses the Peppol network
real_time_reportingbooleantrue if near-real-time invoice reporting to the tax authority is required
clearance_modelbooleantrue if invoices must be cleared through the tax authority gateway before reaching the buyer (Italy, Romania, Poland)
authoritystring|nullName of the relevant tax / regulatory authority
authority_urlstring|nullURL of the authority's e-Invoicing guidance page
notesstring|nullAdditional context about the mandate
cross_borderbooleantrue when supplier and buyer countries differ
cross_border_notestring|nullRelevant note for cross-border transactions (e.g. French e-reporting obligations)
data_reviewedstringISO date when the mandate data was last reviewed

Countries covered

Active or planned B2B mandates: IT, RO, HU, GR, PL, DE, BE, ES, FR. B2G mandate data: GB, NL, SE, NO, DK, FI, AT, IE, PT and all remaining EU-27 (via Directive 2014/55/EU). For countries not in the detailed table the API returns mandate_status: "none" for B2B/B2C or mandate_status: "active" for B2G if the country is an EU member state.

PDF report generation

POST /api/pdf Auth required

Generate a tamper-evident PDF report for any completed check. Every PDF contains a SHA-256 report ID and a versys.io/verify/{id} URL. Any recipient can open that URL to see the original check result — if any field in the PDF differs from what the verify page shows, the document has been altered.

The hash covers the check outcome (valid/invalid, names, addresses, numbers) not just the metadata. Changing any substantive field in the PDF produces a visible mismatch on the verify page.

Request body

FieldTypeDescription
kindstringvat | iban | card | compliance | supplier | eori | company
scopestringsingle (default) or batch
resultobjectThe full response object from the corresponding check endpoint (single)
resultsarrayArray of result objects (batch)
generated_atstringDisplay timestamp (optional — defaults to server time)

Example

curl https://www.versys.io/api/pdf \
  -H "Authorization: Bearer vsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "vat",
    "scope": "single",
    "result": { "vat": "DE129273398", "valid": true, "vies_valid": true, ... }
  }' \
  --output report.pdf

Returns a application/pdf binary. The filename is set via Content-Disposition.

Verify a report

Every PDF footer contains: Verify: versys.io/verify/{hash}. GET /verify/{hash} returns an HTML page showing the stored result fields. No authentication required to verify.