Référence API · v1
Documentation de l’API Vatlas
Une API REST, trois points d’entrée, une seule enveloppe JSON. Tout est en GET, tout est en UTF-8, et toutes les dates sont en UTC au format ISO 8601.
URL de base
https://api.vatlas.devLes chemins sont versionnés (/v1/…). Une version ne change jamais de forme de façon incompatible : un nouveau champ peut apparaître, aucun ne disparaît.
Authentification
Chaque appel porte une clé en jeton bearer dans l’en-tête Authorization. Vous pouvez en créer autant que vous voulez — elles puisent toutes dans le même quota de compte : une clé sert à savoir laquelle de vos intégrations a appelé.
curl "https://api.vatlas.dev/v1/vat/FR08000325175" \
-H "Authorization: Bearer vtl_live_xxxxxxxxxxxxxxxxxxxx"
Gardez la clé côté serveur. Elle ne doit jamais partir dans un bundle front : n’importe qui pourrait la lire et consommer votre quota. Proxifiez l’appel depuis votre backend.
Une clé absente, inconnue ou révoquée répond 401 avec l’enveloppe d’erreur standard, et n’est pas décomptée. Créez votre clé — le plan Free est gratuit et sans carte bancaire.
Enveloppe de réponse
Le succès porte toujours data et meta. L’erreur porte toujours error, et meta quand des sources ont été consultées. La forme ne change pas selon l’origine de la donnée : votre code n’a rien à brancher.
meta.origin—database,sourceouvies.meta.source—SIRENE,MF_WL,PRH,RIK,UR_VIDouVIES.meta.sourceUpdatedAt— horodatage UTC de la donnée servie.meta.checked— toutes les sources consultées, dans l’ordre.
Résoudre un numéro de TVA
GET /v1/vat/{vatNumber}
Le cœur de l’API. L’entrée est normalisée avant validation : la casse, les espaces, les points et les tirets sont ignorés, et le préfixe ISO GR de la Grèce est accepté en plus de son préfixe TVA EL. data.vatNumber revient toujours sous forme canonique.
| Paramètre | Type | Description |
|---|---|---|
vatNumber |
chemin, requis | Numéro de TVA intracommunautaire, préfixe pays inclus. FR08000325175 et fr 08 000.325-175 sont équivalents. |
Requête
curl "https://api.vatlas.dev/v1/vat/FR08000325175" \
-H "Authorization: Bearer $VATLAS_KEY"
Réponse — 200 OK
{
"data": {
"vatNumber": "FR08000325175",
"countryCode": "FR",
"nationalNumber": "08000325175",
"nationalId": "000325175",
"name": "THIERRY JANOYER",
"legalForm": "1000",
"status": "active",
"address": {
"line1": "51 RUE MARX DORMOY",
"line2": null,
"postalCode": "13004",
"city": "MARSEILLE",
"country": "FR"
}
},
"meta": {
"origin": "database",
"source": "SIRENE",
"sourceUpdatedAt": "2025-12-06T09:43:55.000Z",
"checked": ["database"]
}
}
Un nom vide n’est pas un bug. L’Allemagne, l’Espagne et les Pays-Bas ne divulguent ni raison sociale ni adresse via VIES. Pour ces pays, tant qu’aucun registre local n’est branché, seul status est significatif.
Champs de la réponse
| Champ | Type | Description |
|---|---|---|
vatNumber | string | Numéro canonique, préfixe pays inclus. |
countryCode | string | Préfixe TVA à deux lettres (EL pour la Grèce, XI pour l’Irlande du Nord). |
nationalNumber | string | La partie nationale du numéro, sans le préfixe. |
nationalId | string | null | Identifiant au registre national (SIREN, Business ID, registrikood…). Peut différer du numéro de TVA. |
name | string | Raison sociale telle que publiée par la source. Chaîne vide si la source ne la divulgue pas. |
legalForm | string | null | Code de forme juridique du registre d’origine, non harmonisé entre pays. |
status | enum | active, inactive ou unknown. |
address.line1 | string | null | Voie, ou adresse entière quand la source ne la découpe pas. |
address.line2 | string | null | Complément d’adresse. |
address.postalCode | string | null | Code postal. |
address.city | string | null | Commune. null en Lettonie, dont le registre publie un code de territoire. |
address.country | string | Code pays ISO 3166-1 alpha-2. |
Lister tous les pays et leur mode de résolution
GET /v1/countries
Renvoie les 28 préfixes — les 27 États membres plus l’Irlande du Nord — avec la source de chacun et sa stratégie : bulk pour un registre importé, on-demand pour l’API du registre, both, ou vies pour un État membre sans source locale exploitable, qui reste résolu via le repli VIES.
{
"data": [
{ "countryCode": "FR", "name": "France", "source": "SIRENE", "strategy": "bulk" },
{ "countryCode": "PL", "name": "Poland", "source": "MF_WL", "strategy": "on-demand" },
{ "countryCode": "CZ", "name": "Czechia", "source": "ARES", "strategy": "on-demand" },
{ "countryCode": "FI", "name": "Finland", "source": "PRH", "strategy": "both" },
{ "countryCode": "DE", "name": "Germany", "source": "VIES", "strategy": "vies" }
]
}
Sonde de disponibilité
GET /health
Ne répond qu’une fois un aller-retour base de données réussi, donc utilisable tel quel comme readiness probe. Cet appel ne consomme pas de quota.
{ "status": "ok" }
Codes d’erreur
Toutes les erreurs partagent la même forme :
{
"error": {
"code": "INVALID_VAT_FORMAT",
"message": "Unknown VAT country prefix \"XX\"",
"reason": "UNKNOWN_COUNTRY"
}
}
| Code | HTTP | Signification |
|---|---|---|
INVALID_VAT_FORMAT |
400 | Syntaxe ou clé de contrôle invalide ; aucune source n’a été consultée. reason précise laquelle : EMPTY, UNKNOWN_COUNTRY, BAD_FORMAT, BAD_CHECKSUM. |
MISSING_SECRET_KEY |
401 | Aucune information d’authentification Authorization: Bearer n’a été envoyée, ou l’en-tête utilisait un autre schéma. Cet appel n’est pas décompté de votre quota. |
INVALID_SECRET_KEY |
401 | La clé est inconnue ou a été révoquée. Une révocation prend effet en moins d’une minute : une clé révoquée à l’instant peut donc encore répondre. |
QUOTA_EXCEEDED |
429 | Le quota mensuel du compte est épuisé. Il se renouvelle au début du mois suivant ; X-RateLimit-Reset en donne l’instant exact. |
VAT_NOT_REGISTERED |
404 | VIES a répondu de façon définitive : le numéro n’est pas enregistré pour les échanges intracommunautaires. Ce n’est pas la même chose qu’une société inexistante — elle peut être active dans son registre national sans être enregistrée à la TVA intracommunautaire. |
COMPANY_NOT_FOUND |
404 | Plus aucune source à interroger : le numéro est valide, mais introuvable. |
SOURCE_UNAVAILABLE |
503 | Absent de la base et VIES n’a pas répondu. Ce n’est pas un résultat négatif, ce n’est jamais mis en cache : réessayez plus tard. |
NOT_FOUND |
404 | Route inconnue. |
INTERNAL_ERROR |
500 | Défaillance inattendue de notre côté. |
Quotas & limites
Le quota est mensuel et appartient au compte, pas à une clé en particulier. Chaque réponse décomptée porte l’état courant du compteur, il n’y a donc rien à estimer :
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1788220800
- Une erreur de format
400n’est jamais décomptée : le numéro est rejeté avant toute consultation. - Une
503ne l’est pas davantage — un État membre n’a pas répondu, ce n’est pas à vous de le payer. /v1/countrieset/healthne demandent pas de clé et ne coûtent pas de quota.- Quota épuisé :
429, avec la même enveloppe d’erreur.X-RateLimit-Resetindique le renouvellement.
Détail des offres sur la page tarifs.
Exemples de code
curl -s "https://api.vatlas.dev/v1/vat/FR08000325175" \
-H "Authorization: Bearer $VATLAS_KEY" \
| jq '.data.name'
// Node 20+ / Deno / navigateur (via votre backend)
const lookupVat = async (vatNumber) => {
const res = await fetch(
`https://api.vatlas.dev/v1/vat/${encodeURIComponent(vatNumber)}`,
{ headers: { 'Authorization': `Bearer ${process.env.VATLAS_KEY}` } },
)
const payload = await res.json()
if (!res.ok) throw new Error(`${payload.error.code}: ${payload.error.message}`)
return payload.data
}
const company = await lookupVat('FR08000325175')
console.log(company.name, '—', company.address.city)
import os
import requests
def lookup_vat(vat_number: str) -> dict:
res = requests.get(
f"https://api.vatlas.dev/v1/vat/{vat_number}",
headers={"Authorization": f"Bearer {os.environ['VATLAS_KEY']}"},
timeout=10,
)
payload = res.json()
if res.status_code != 200:
raise RuntimeError(payload["error"]["code"])
return payload["data"]
company = lookup_vat("FR08000325175")
print(company["name"], company["address"]["city"])
<?php
function lookupVat(string $vatNumber): array
{
$context = stream_context_create([
'http' => ['header' => 'Authorization: Bearer ' . getenv('VATLAS_KEY')],
]);
$url = 'https://api.vatlas.dev/v1/vat/' . rawurlencode($vatNumber);
$payload = json_decode(file_get_contents($url, false, $context), true);
if (isset($payload['error'])) {
throw new RuntimeException($payload['error']['code']);
}
return $payload['data'];
}
$company = lookupVat('FR08000325175');
echo $company['name'];