REST API¶
Base URL https://verify.orvel.dev. Interactive OpenAPI UI: /api. Machine-readable schema:
/openapi.json.
Authentication¶
X-API-Key: <key> works too. Without a key the verification endpoints still answer, limited to 20 requests per
minute per IP. Get a key on the Pricing page.
Endpoints¶
| Method | Path | Tool |
|---|---|---|
| GET | /v1/vat/{vat} |
check_vat. Query: country_code, requester_vat. |
| GET | /v1/siren/{q} |
lookup_siren. q = SIREN, SIRET or name. |
| GET | /v1/siret/{siret}/normalize |
normalize_siret |
| GET | /v1/iban/{iban} |
check_iban. Query: bic. |
| POST | /v1/address/normalize |
normalize_address_eu. Body: address, country_code, postal_code, city. |
| GET | /v1/phone/{phone} |
normalize_phone. Query: region. |
| GET | /v1/email/{email} |
check_email. Query: mx (default true). |
| GET | /v1/eori/{eori} |
check_eori |
| GET | /v1/statuses |
What each status value means. Free. |
| GET | /v1/usage |
Your monthly counter. Free. |
| GET | /v1/plans |
Plan catalog and checkout links. Free. |
| GET | /health |
Version and counter-store state. Free. |
All of them return the envelope. Arguments are echoed in input.
Examples¶
# VAT, with audit proof
curl -H "Authorization: Bearer $EV_KEY" \
"https://verify.orvel.dev/v1/vat/FR03552081317?requester_vat=FR40303265045"
# French business by name
curl -H "Authorization: Bearer $EV_KEY" \
"https://verify.orvel.dev/v1/siren/electricite%20de%20france"
# IBAN plus the BIC the customer typed
curl -H "Authorization: Bearer $EV_KEY" \
"https://verify.orvel.dev/v1/iban/DE89370400440532013000?bic=COBADEFFXXX"
# Address
curl -X POST -H "Authorization: Bearer $EV_KEY" -H "Content-Type: application/json" \
-d '{"address":"22 avenue de wagram","postal_code":"75008","city":"Paris","country_code":"FR"}' \
https://verify.orvel.dev/v1/address/normalize
# Email without the DNS lookup
curl -H "Authorization: Bearer $EV_KEY" \
"https://verify.orvel.dev/v1/email/bob@example.com?mx=false"
Response headers¶
| Header | Meaning |
|---|---|
X-Plan |
Plan behind the key, or anonymous. |
X-Usage-Used, X-Usage-Quota, X-Usage-Remaining |
Monthly counter after this call. |
Retry-After |
Only with upstream_unavailable: seconds to wait. |
Errors¶
Errors are JSON, never the envelope:
{ "code": "quota_exceeded", "message": "Monthly quota of 500 calls reached for plan 'free'. Upgrade at /docs/pricing/", "used": 500, "quota": 500 }
| Code | HTTP | Meaning |
|---|---|---|
invalid_input |
400 | A required argument was empty. |
validation_error |
422 | The request body does not match the schema; see details. |
invalid_api_key |
401 | Unknown, expired or cancelled key. |
api_key_required |
401 | The endpoint needs a key (/v1/usage, portal). |
quota_exceeded |
402 | Monthly quota used up. Upgrade or wait for the reset. |
rate_limited |
429 | Anonymous limit reached; use a key. |
A wrong identifier is not an error: it is 200 with status: "invalid". A register being down is also 200,
with status: "upstream_unavailable".
Billing rules¶
One successful call = one unit against the monthly quota. Not counted: upstream_unavailable, 4xx, 5xx,
/v1/usage, /v1/plans, /v1/statuses, /health. Counters reset on the first of the month (UTC).