Référence API
Accès programmatique aux vérifications TVA, IBAN, carte, EORI, registre d’entreprise et compte bancaire. Toutes les réponses sont au format 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.
vies_checked will be
false and vies_error will read
"UK VAT verification coming soon".
Credits & rate limits
| Operation | Cost |
|---|---|
| VAT format + checksum check | Free |
| IBAN format + checksum check | Free |
| Card Luhn check | Free |
| BIN / issuer lookup | Free |
| VIES live lookup (fresh) | 2 credits |
| VIES cached result (<24 h old) | 1 credit |
| EORI format check | Free |
| EORI live registry lookup | 1 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
| HTTP | error | Meaning |
|---|---|---|
| 400 | MissingField | A required field is absent or the wrong type |
| 400 | BatchTooLarge | More than 100 items in a batch request |
| 401 | Unauthorized | No valid API key or session cookie (VIES requires auth) |
| 402 | InsufficientCredits | Not enough credits for this request |
| 422 | InvalidFormat / InvalidChecksum / InvalidCountry | Input failed validation |
| 429 | RateLimitExceeded | Hourly anonymous limit reached — sign in for higher limits |
| 503 | ViesUnavailable | VIES or a member state registry is temporarily offline |
| 500 | ServerError | Unexpected server error |
All error responses include an error code string and an error_detail human-readable message.
VAT — single check
Validate a VAT number and optionally verify it against the EU VIES registry.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
vat | string | Yes | VAT number with 2-letter country prefix, e.g. DE129273398 |
vies | boolean | No | If true, perform a VIES live lookup (requires authentication) |
vies_force | boolean | No | Bypass 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
| Field | Type | Description |
|---|---|---|
valid | boolean | Passes format and checksum validation |
vat | string | Normalised VAT number (uppercase, no spaces) |
vat_formatted | string | Formatted with spaces for readability |
country_code | string | ISO 3166-1 alpha-2 country code |
country_name | string | Full country name |
number | string | Local number without the country prefix |
format_valid | boolean | Country-specific format check result |
vies_checked | boolean | true if a live VIES/HMRC lookup was performed; false if format-only or lookup unavailable |
vies_valid | boolean|null | Whether the number is currently registered; null if not checked |
vies_name | string|null | Registered business name (some countries withhold this for privacy) |
vies_address | string|null | Registered address (some countries withhold this for privacy) |
vies_request_date | string | Date the lookup was performed (YYYY-MM-DD) |
vies_from_cache | boolean | true if the result was served from the 24-hour cache (costs 1 credit instead of 2) |
vies_source | string | "VIES" for EU numbers; "HMRC" for GB numbers (when available) |
vies_error | string|null | Human-readable reason if the lookup was not performed |
credit_balance | integer | Remaining credits after this request (authenticated only) |
VAT — batch check
Validate up to 100 VAT numbers in a single request.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
vats | string[] | Yes | Array of VAT numbers (max 100) |
vies | boolean | No | Perform VIES lookup on all valid numbers (requires auth) |
vies_force | boolean | No | Bypass 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
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
| Field | Type | Required | Description |
|---|---|---|---|
client_name | string | Yes | Your client's legal or trading name |
client_vat | string | Yes | Your client's VAT number |
supplier_country | string | Conditionally | Your 2-letter business country code. Optional if saved in your authenticated profile. |
supplier_vat | string | No | Your own VAT number. Optional if saved in your authenticated profile. |
with_vies | boolean | No | If true, include a live VIES/HMRC validation of the client VAT number |
vies_force | boolean | No | Bypass 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
| Field | Type | Description |
|---|---|---|
client_name | string | The submitted client name |
client_vat | string | The submitted client VAT number |
supplier_country | string | Your effective business country used for the reverse-charge analysis |
supplier_vat | string|null | Your effective VAT number, if supplied or available in your profile |
name_match | object | Comparison between the submitted client name and the official registry name, where available |
reverse_charge | object | High-level reverse-charge guidance based on supplier and client countries |
vies_checked | boolean|null | Whether a live registry lookup was performed |
vies_note | string|null | Explanation if a live lookup was not requested or not available |
credit_balance | integer | Remaining credits after the lookup (authenticated requests only) |
IBAN — single check
Validate an IBAN using the ISO 13616 checksum and look up bank details where available.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
iban | string | Yes | IBAN 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
| Field | Type | Description |
|---|---|---|
valid | boolean | Passes format and ISO 13616 checksum |
iban | string | Normalised IBAN (uppercase, no spaces) |
iban_formatted | string | Grouped in blocks of 4 characters |
country_code | string | 2-letter country code of the account |
country_name | string | Full country name |
in_sepa_zone | boolean | Whether the country participates in SEPA |
checksum_digits | string | The 2-digit ISO checksum |
bank_code | string | Bank identifier portion of the BBAN |
branch_code | string|null | Branch code (where applicable) |
account_code | string | Account number portion of the BBAN |
bic | string|null | BIC/SWIFT code (where available in registry) |
IBAN — batch check
Validate up to 100 IBANs in a single request.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
ibans | string[] | Yes | Array 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
Validate a payment card number using the Luhn algorithm and look up issuer details by BIN.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
card_number | string | Yes | Card 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
| Field | Type | Description |
|---|---|---|
valid | boolean | Passes Luhn checksum |
luhn_valid | boolean | Explicit Luhn result |
card_masked | string | Number with middle digits replaced by * |
last4 | string | Last 4 digits |
length | integer | Total number of digits |
brand | string | Detected card brand (Visa, Mastercard, Amex, etc.) |
bin | string | 8-digit BIN (Bank Identification Number) |
bin_lookup | object | Issuer details: bank_name, scheme, card_type, country_code, country_name, prepaid |
Card — batch check
Validate up to 100 card numbers in a single request.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
cards | string[] | Yes | Array 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
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
| Field | Type | Required | Description |
|---|---|---|---|
bin | string | Yes* | First 6–8 digits of the card |
card_number | string | Yes* | 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
| Field | Description |
|---|---|
bin_lookup_found | true if the BIN was found in the registry |
bank_name | Issuing bank name |
scheme | Card scheme (visa, mastercard, amex, etc.) |
brand | Product brand (e.g. Visa Classic, Mastercard World) |
card_type | debit, credit or prepaid |
country_code | Issuing country |
country_name | Issuing country full name |
prepaid | boolean or null |
EORI — single check
Validate an EORI (Economic Operators Registration and Identification) number and optionally look it up against the EU customs registry or the UK HMRC registry.
lookup_from_cache: true.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
eori | string | Yes | EORI number with 2-letter country prefix, e.g. GB123456789012 or DE123456789012345 |
lookup | boolean | No | If true, perform a live registry lookup (requires authentication) |
lookup_force | boolean | No | Bypass 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
| Field | Type | Description |
|---|---|---|
valid | boolean | Passes EORI format validation |
eori | string | Normalised EORI (uppercase, no spaces) |
eori_formatted | string | Formatted with a space after the country prefix |
country_code | string | 2-letter country prefix |
country_name | string | Full country name |
format_valid | boolean | Country-specific format pattern check |
known_country | boolean | false for country codes that do not issue EORI numbers (e.g. CH, NO) |
lookup_checked | boolean | true if a registry lookup was performed |
lookup_valid | boolean|null | Whether the EORI exists in the registry; null if not checked |
lookup_source | string|null | "eu_soap" for EU-27 numbers; "hmrc" for GB/XI numbers |
lookup_name | string|null | Registered operator name (when operator has consented to disclosure) |
lookup_address | string|null | Registered street address |
lookup_city | string|null | Registered city |
lookup_postcode | string|null | Registered postcode |
lookup_country | string|null | Country from the registry record |
lookup_date | string|null | Date of the registry response (ISO 8601) |
lookup_from_cache | boolean | true if the result was served from the 30-day cache |
lookup_error | string|null | Human-readable reason if the lookup could not be completed |
credit_balance | integer | Remaining credits (authenticated requests only) |
EORI — batch check
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
| Field | Type | Required | Description |
|---|---|---|---|
eori | string[] | Yes | Array 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
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).
"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
| Field | Type | Required | Description |
|---|---|---|---|
vat | string | Yes | VAT number with 2-letter country prefix, e.g. DE123475223 or GB123456789 |
lookup | boolean | No | If true, query GLEIF, OpenCorporates, and national registries (free, requires auth) |
vies | boolean | No | If true, run a live EU VIES check (2 credits live, 1 if cached; requires auth) |
name | string | No | Expected 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
| Field | Type | Description |
|---|---|---|
valid | boolean | VAT number passes format validation |
format_valid | boolean | Country-specific format check result |
vat | string | Normalised VAT number (uppercase, no spaces) |
vat_formatted | string | Country-formatted VAT number |
country_code | string | 2-letter ISO country code |
country_name | string | Full country name |
vies_valid | boolean|null | VIES registry status; null if lookup not performed |
vies_name | string|null | Company name from VIES (may be withheld — e.g. Germany returns "---") |
vies_address | string|null | Address from VIES (may be withheld) |
vies_checked | boolean | true if a VIES check was performed |
vies_cached | boolean | true if the VIES result came from cache |
registry_enriched | boolean | true if at least one registry source returned data |
registry_sources | string[] | List of sources that contributed data, e.g. ["gleif", "opencorporates"] |
registry_name | string|null | Legal name from the highest-priority registry source |
registry_address | string|null | Registered address (street) |
registry_city | string|null | Registered city |
registry_postcode | string|null | Postcode |
registry_status | string|null | active, inactive, dissolved, or unknown |
registry_number | string|null | Companies House / Handelsregister / equivalent registration number |
registry_lei | string|null | Legal Entity Identifier (from GLEIF when available) |
registry_url | string|null | Link to the company's registry record |
registry_jurisdiction | string|null | OpenCorporates jurisdiction code |
name_match | object|null | Name consistency result — see below |
credit_balance | integer | Remaining 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.
| Field | Type | Description |
|---|---|---|
level | string | exact, strong, partial, or weak |
score | number | 0–1 similarity score |
submitted | string|null | The name you supplied |
registered | string|null | The name found in the registry |
Registry source priority
When multiple sources return data, fields are merged in this priority order (highest first):
- Country-specific APIs: HMRC (GB), Companies House (GB), Brønnøysund Register (NO), CVR (DK), SIRENE (FR)
- GLEIF (global LEI database — strong coverage for large companies)
- OpenCorporates (broad coverage for SMEs)
Company — batch lookup
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
| Field | Type | Required | Description |
|---|---|---|---|
vats | string[] | Yes | Array of VAT numbers (max 100) |
lookup | boolean | No | If true, run GLEIF + registry enrichment for each (free) |
vies | boolean | No | If 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
Validate a domestic bank account number using country-specific format and checksum rules. Supported countries: GB, DE, FR, ES, IT, BE, NL, NO.
/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.
| Field | Type | Required | Description |
|---|---|---|---|
country | string | Yes | 2-letter ISO country code, e.g. GB |
| GB (United Kingdom) | |||
sort_code | string | Yes | 6-digit sort code, dashes optional (e.g. 20-00-00) |
account | string | Yes | 8-digit account number |
| DE (Germany) | |||
blz | string | Yes | 8-digit Bankleitzahl (BLZ) |
account | string | Yes | 1–10 digit Kontonummer |
| FR (France) | |||
bank | string | Yes | 5-digit bank code |
branch | string | Yes | 5-digit branch code (guichet) |
account | string | Yes | 11-character account number (alphanum) |
rib_key | string | Yes | 2-digit RIB check key |
| ES (Spain) | |||
entity | string | Yes | 4-digit entity code |
branch | string | Yes | 4-digit branch code (oficina) |
dc1 | string | Yes | 1-digit check digit (validates entity + branch) |
dc2 | string | Yes | 1-digit check digit (validates account) |
account | string | Yes | 10-digit account number |
| IT (Italy) | |||
abi | string | Yes | 5-digit ABI bank code |
cab | string | Yes | 5-digit CAB branch code |
cin | string | Yes | 1-letter CIN check character |
account | string | Yes | 12-character account number (alphanum) |
| BE (Belgium) | |||
account | string | Yes | 12-digit Belgian account number, dashes optional (BBB-AAAAAAA-CC) |
| NL (Netherlands) | |||
account | string | Yes | Up to 10-digit account number |
| NO (Norway) | |||
account | string | Yes | 11-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
| Field | Type | Description |
|---|---|---|
valid | boolean | true if the account passes all applicable checks |
format_valid | boolean | Length and character format is correct |
checksum_valid | boolean|null | Check digit / Mod-11 result; null if checksum not available for the country |
country_code | string | 2-letter country code |
country_name | string | Full country name |
validation_method | string | Algorithm used, e.g. mod11, rib_key, format_only |
account_formatted | string | Canonical formatted account number (country-specific) |
error | string|null | InvalidFormat or InvalidCheckDigit on failure |
error_detail | string|null | Human-readable explanation of the error |
ViDA — e-Invoicing mandate check
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_reviewed date.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
buyer_country | string | Yes | 2-letter ISO country code of the buyer, e.g. DE, IT, FR |
supplier_country | string | No | 2-letter ISO country code of the supplier. Used to detect cross-border scenarios and generate relevant notes. |
supply_type | string | No | b2b (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
| Field | Type | Description |
|---|---|---|
buyer_country | string | 2-letter country code of the buyer |
buyer_country_name | string | Full country name |
supplier_country | string|null | Supplier country code if provided |
supply_type | string | b2b, b2g, or b2c |
mandate_status | string | active — mandatory now; planned — upcoming mandate; none — no mandate |
einvoice_required | boolean | true when the mandate is currently active |
einvoice_planned | boolean | true when a mandate is confirmed but not yet in force |
mandate_since | string|null | ISO date when the mandate came (or comes) into force |
mandate_scope | string|null | Human-readable description of what the mandate covers |
platform | string|null | Name of the required platform or gateway (e.g. SDI, KSeF, Peppol) |
format | string|null | Required invoice format or standard (e.g. FatturaPA, XRechnung, Peppol BIS Billing 3.0) |
peppol_supported | boolean | true if the country supports or uses the Peppol network |
real_time_reporting | boolean | true if near-real-time invoice reporting to the tax authority is required |
clearance_model | boolean | true if invoices must be cleared through the tax authority gateway before reaching the buyer (Italy, Romania, Poland) |
authority | string|null | Name of the relevant tax / regulatory authority |
authority_url | string|null | URL of the authority's e-Invoicing guidance page |
notes | string|null | Additional context about the mandate |
cross_border | boolean | true when supplier and buyer countries differ |
cross_border_note | string|null | Relevant note for cross-border transactions (e.g. French e-reporting obligations) |
data_reviewed | string | ISO 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
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.
Request body
| Field | Type | Description |
|---|---|---|
kind | string | vat | iban | card | compliance | supplier | eori | company |
scope | string | single (default) or batch |
result | object | The full response object from the corresponding check endpoint (single) |
results | array | Array of result objects (batch) |
generated_at | string | Display 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.