1. Authentification
Envoyez votre clé dans le header Bearer. Les clés test et live sont isolées et peuvent être révoquées séparément.
Authorization: Bearer ent_test_votre_prefixe_votre_secretLes routes de compatibilité /v2/* utilisent à la place le header api-key, pour que le code écrit contre Pappers continue de fonctionner. Ne mettez jamais une clé dans l’URL : elle finirait dans les journaux des intermédiaires.
2. Récupérer une entreprise
/v1/companies/{siren}La réponse est un objet { meta, data }. data porte l’entreprise, meta porte la provenance et la fraîcheur. Un 206 signale un snapshot incomplet : les champs indisponibles restent nuls, aucune valeur n’est inventée.
export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl "$ENTREPRISE_ORIGIN/v1/companies/552100554" \
-H "Authorization: Bearer $ENTREPRISE_API_KEY" \
-H "Accept: application/json"const entrepriseOrigin = process.env.ENTREPRISE_ORIGIN ?? "http://localhost:8088";
const response = await fetch(`${entrepriseOrigin}/v1/companies/552100554`, {
headers: {
Authorization: `Bearer ${process.env.ENTREPRISE_API_KEY}`,
Accept: "application/json",
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code} (request ${error.request_id})`);
}
const { meta, data } = await response.json();
// meta.data_as_of et meta.stale décident si ce snapshot convient à votre usage.import os
import requests
entreprise_origin = os.environ.get("ENTREPRISE_ORIGIN", "http://localhost:8088")
response = requests.get(
f"{entreprise_origin}/v1/companies/552100554",
headers={
"Authorization": f"Bearer {os.environ['ENTREPRISE_API_KEY']}",
"Accept": "application/json",
},
timeout=10,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']} (request {error['request_id']})")
body = response.json()
meta, data = body["meta"], body["data"]3. Rechercher une entreprise
/v1/searchRecherche sur le corpus SIRENE local et versionné, sans appel fournisseur synchrone. q est obligatoire et doit faire 2 à 100 caractères. Filtres disponibles : status (active, closed ou all), postal_code, commune et naf_code. La réponse est paginée par curseur — voir Pagination.
export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl --get "$ENTREPRISE_ORIGIN/v1/search" \
-H "Authorization: Bearer $ENTREPRISE_API_KEY" \
--data-urlencode "q=TOTALENERGIES" \
--data-urlencode "status=active" \
--data-urlencode "limit=20"4. Lister les établissements
/v1/companies/{siren}/establishments/v1/establishments/{siret}Collection SIRENE immuable, paginée par clé SIRET. status vaut active par défaut. Tant qu’aucune collection complète n’a été publiée pour un SIREN, l’endpoint répond 503 establishment_data_not_ready plutôt que de servir une liste partielle silencieuse.
export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl --get "$ENTREPRISE_ORIGIN/v1/companies/552100554/establishments" \
-H "Authorization: Bearer $ENTREPRISE_API_KEY" \
--data-urlencode "status=active" \
--data-urlencode "limit=50"5. Parcourir et comparer les révisions
L’historique vient uniquement de notre corpus PostgreSQL et ne déclenche aucun appel fournisseur. Utilisez les revision_id publics — jamais les identifiants internes de snapshot.
/v1/companies/{siren}/history/v1/companies/{siren}/diff?from_revision_id=…export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl "$ENTREPRISE_ORIGIN/v1/companies/552100554/diff?from_revision_id=$REVISION_ID" \
-H "Authorization: Bearer $ENTREPRISE_API_KEY" \
-H "Accept: application/json"Le diff est une liste déterministe d’opérations add, remove et replace adressées par JSON Pointer. Un history_gap signale une période inconnue, restreinte, masquée ou une frontière de schéma.
Paginer un jeu de résultats
La pagination est par curseur, jamais par numéro de page : un offset se décale dès que le corpus change sous vos pieds. Chaque réponse paginée expose pagination.next_cursor. Passez-le tel quel à l’appel suivant ; null signifie qu’il n’y a plus de page.
Le curseur est opaque et signé (HMAC), valide 24 heures, et lié à la requête qui l’a produit. Le décoder, le fabriquer ou changer les filtres en cours de traversée renvoie un 400 invalid_cursor.
| Endpoint | limit | Ancrage du curseur |
|---|---|---|
GET /v1/search | 1 – 50 (défaut 20) | Génération exacte du corpus. Un curseur reste utilisable tant que sa génération est conservée ; une génération retirée renvoie cursor_stale. |
GET /v1/companies/{siren}/history | 1 – 50 (défaut 20) | Plus récent d’abord, ancré sur la dernière révision vue à la première page : une publication ultérieure ne décale pas la traversée. |
GET /v1/companies/{siren}/establishments | 1 – 200 (défaut 50) | Pagination par clé SIRET, liée à la collection et à l’observation qui ont produit la première page. |
- Endpoint
GET /v1/search- limit
- 1 – 50 (défaut 20)
- Ancrage du curseur
- Génération exacte du corpus. Un curseur reste utilisable tant que sa génération est conservée ; une génération retirée renvoie cursor_stale.
- Endpoint
GET /v1/companies/{siren}/history- limit
- 1 – 50 (défaut 20)
- Ancrage du curseur
- Plus récent d’abord, ancré sur la dernière révision vue à la première page : une publication ultérieure ne décale pas la traversée.
- Endpoint
GET /v1/companies/{siren}/establishments- limit
- 1 – 200 (défaut 50)
- Ancrage du curseur
- Pagination par clé SIRET, liée à la collection et à l’observation qui ont produit la première page.
409 cursor_stale.Une traversée est ancrée à la génération de données vue à la première page. Si cette génération est retirée pendant que vous paginez, l’API renvoie 409 plutôt que de vous servir un mélange silencieux de deux versions.
Les deux réflexes habituels sont faux ici. Le rejouer tel quel avec le même curseur boucle indéfiniment, puisque le curseur restera invalide. L’abandonner comme une erreur fatale perd silencieusement la fin du jeu de résultats. Le comportement attendu est de jeter les résultats partiels et de reprendre à la première page — c’est pour cela que la boucle ci-dessous vide son accumulateur.
Un curseur périmé renvoie 409 cursor_stale sur /v1/search, mais 400 sur /v2/recherche, avec le message « The cursor corpus changed; restart pagination ». Un client qui décide « 4xx sauf 409 = ma faute, ne pas rejouer » abandonnera silencieusement la traversée sur la surface de compatibilité, là où il la reprendrait correctement sur /v1.
Si vous migrez de /v2/recherche vers /v1/search, ou si vous appelez les deux, traitez la péremption de curseur par son sens et non par son code HTTP.
export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
# Première page
curl --get "$ENTREPRISE_ORIGIN/v1/search" \
-H "Authorization: Bearer $ENTREPRISE_API_KEY" \
--data-urlencode "q=boulangerie" \
--data-urlencode "limit=50"
# Page suivante : réutilisez next_cursor tel quel, sans le décoder
curl --get "$ENTREPRISE_ORIGIN/v1/search" \
-H "Authorization: Bearer $ENTREPRISE_API_KEY" \
--data-urlencode "cursor=$NEXT_CURSOR"let cursor = null;
const companies = [];
do {
const url = new URL("/v1/search", entrepriseOrigin);
url.searchParams.set("q", "boulangerie");
url.searchParams.set("limit", "50");
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
// Le corpus a changé pendant la traversée : reprenez à la première page.
if (response.status === 409) { cursor = null; companies.length = 0; continue; }
if (!response.ok) throw new Error(`API error: ${response.status}`);
const page = await response.json();
companies.push(...page.data);
cursor = page.pagination.next_cursor;
} while (cursor);Gérer les erreurs
Toute erreur porte un code stable, un message lisible et un request_id. Traitez le code, jamais le message : le texte peut être reformulé, le code non. Conservez le request_id dans vos journaux — c’est ce que nous demanderons.
Les routes /v1/* et les routes de compatibilité /v2/* ne renvoient pas la même forme. Un gestionnaire écrit pour l’une lit undefined sur l’autre — sans lever d’exception, donc sans que vos tests le remarquent. C’est le piège de migration le plus coûteux de cette page, parce que les chemins d’erreur sont précisément ceux que personne n’exerce avant la mise en production.
Enveloppe /v1/*
{
"error": {
"code": "company_not_found",
"message": "no public company snapshot is available for this siren",
"request_id": "req_8r2Q…"
}
}Enveloppe /v2/* (compatibilité Pappers)
Forme héritée de Pappers, conservée volontairement pour que votre code existant continue de la lire.
{
"error": "Not Found",
"message": "Company not found",
"statusCode": 404
}Catalogue complet des codes
Les 31 codes que l’API /v1 peut renvoyer, sans exception. La colonne « Portée » indique les endpoints concernés : Toutes signifie que le code provient du middleware commun et peut donc apparaître sur n’importe quel appel, y compris ceux que vous n’avez pas encore écrits.
| Code | HTTP | Portée | Ce que cela signifie | Que faire |
|---|---|---|---|---|
invalid_api_key | 401 | Toutes | Clé absente, révoquée, ou mauvais environnement (test contre live). | Vérifiez le header. Ne réessayez pas : la réponse ne changera pas. |
insufficient_scope | 403 | Toutes | La clé est valide mais ne porte pas le scope requis par l’opération. | Erreur de configuration, pas de disponibilité : corrigez les scopes de la clé. |
authentication_unavailable | 503 | Toutes | Le service d’authentification est indisponible ; l’API refuse de servir sans lui. | Fail-closed volontaire. Backoff exponentiel. |
rate_limit_exceeded | 429 | Toutes | Limite de débit de la clé API atteinte. | Respectez Retry-After et RateLimit-Reset. |
authentication_rate_limit_exceeded | 429 | Toutes | Trop de tentatives d’authentification depuis cette adresse, avant même la validation de la clé. | Signale en général une clé erronée rejouée en boucle : corrigez-la avant de réessayer. |
quota_exceeded | 429 | Toutes | Quota mensuel du projet épuisé — distinct du rate limit. | Retry-After ne vous aidera pas : le quota se réinitialise au cycle de facturation. |
rate_limiter_unavailable | 503 | Toutes | Les contrôles de débit sont indisponibles ; l’API refuse de servir sans eux. | Fail-closed volontaire. Backoff exponentiel. |
metering_unavailable | 503 | Toutes | La comptabilisation d’usage est indisponible ; aucun appel n’est servi non compté. | Fail-closed volontaire. Backoff exponentiel. |
invalid_siren | 400 | Entreprises | Le SIREN ne contient pas exactement 9 chiffres. | Validez côté client avant l’appel ; retirez les espaces. |
invalid_siret | 400 | Établissements | Le SIRET ne contient pas exactement 14 chiffres. | Validez côté client ; un SIRET est un SIREN suivi de 5 chiffres. |
invalid_query | 400 | Recherche | q absent, hors de 2–100 caractères, non normalisable, ou paramètre inconnu/dupliqué. | Corrigez la requête ; ne réessayez pas à l’identique. |
invalid_filter | 400 | Recherche | status, postal_code, commune ou naf_code invalide. | status accepte active, closed ou all uniquement. |
invalid_status | 400 | Établissements | Le filtre status n’est pas active, closed ou all. | Corrigez la valeur ; ne réessayez pas à l’identique. |
invalid_limit | 400 | Paginées | limit hors des bornes de l’endpoint. | Voir les bornes par endpoint dans la section pagination. |
invalid_cursor | 400 | Paginées | Curseur illisible, expiré (24 h) ou ne correspondant pas à la requête. | Ne fabriquez ni ne modifiez un curseur : reprenez à la première page. |
invalid_revision_id | 400 | Historique et diff | revision_id inconnu ou mal formé. | Utilisez un revision_id renvoyé par /history, jamais un identifiant de snapshot interne. |
invalid_idempotency_key | 400 | Warmups | En-tête Idempotency-Key absent ou mal formé. | Fournissez une clé stable par intention de requête. |
company_not_found | 404 | Entreprises | Aucun snapshot public pour ce SIREN. | Absence de donnée publiable, pas une panne. Ne réessayez pas en boucle. |
establishment_not_found | 404 | Établissements | Aucun établissement public pour ce SIRET. | Absence de donnée publiable, pas une panne. Ne réessayez pas en boucle. |
rnb_not_found | 404 | RNB | Aucune représentation RNB scellée pour ce SIREN. | Absence de donnée, pas une panne. |
warmup_not_found | 404 | Warmups | L’identifiant de warmup est inconnu ou expiré. | Relancez un warmup plutôt que de sonder l’identifiant. |
cursor_stale | 409 | Paginées | La génération de données a changé pendant votre traversée. | Jetez les résultats partiels et reprenez à la première page. |
revision_schema_unsupported | 409 | Historique et diff | La révision demandée précède une frontière de schéma public. | Traitez comme une limite d’historique, pas comme une erreur transitoire. |
idempotency_key_reused | 409 | Warmups | La clé d’idempotence a déjà servi pour une requête différente. | Une clé par intention : n’en rejouez pas une avec un corps différent. |
temporarily_unavailable | 503 | Entreprises et établissements | Dépendance de données indisponible. | Backoff exponentiel. |
search_unavailable | 503 | Recherche | Le corpus de recherche est temporairement indisponible. | Backoff exponentiel ; le lookup par SIREN reste disponible. |
establishment_data_not_ready | 503 | Établissements | Aucune collection d’établissements complète n’a encore été publiée pour ce SIREN. | État d’ingestion, pas une panne : réessayez plus tard, sans boucle serrée. |
invalid_snapshot | 503 | Entreprises | Le snapshot courant est illisible ; l’API préfère échouer que servir une donnée douteuse. | Signalez-le avec le request_id : cela ne se corrige pas côté client. |
dependency_unavailable | 503 | Toutes | Une dépendance interne requise est indisponible. | Backoff exponentiel. |
warmup_unavailable | 503 | Warmups | Le service de warmup est indisponible. | Backoff exponentiel. |
internal_error | 500 | Toutes | Erreur inattendue. | Conservez le request_id et signalez-le. |
- Code
invalid_api_key- HTTP
- 401
- Portée
- Toutes
- Ce que cela signifie
- Clé absente, révoquée, ou mauvais environnement (test contre live).
- Que faire
- Vérifiez le header. Ne réessayez pas : la réponse ne changera pas.
- Code
insufficient_scope- HTTP
- 403
- Portée
- Toutes
- Ce que cela signifie
- La clé est valide mais ne porte pas le scope requis par l’opération.
- Que faire
- Erreur de configuration, pas de disponibilité : corrigez les scopes de la clé.
- Code
authentication_unavailable- HTTP
- 503
- Portée
- Toutes
- Ce que cela signifie
- Le service d’authentification est indisponible ; l’API refuse de servir sans lui.
- Que faire
- Fail-closed volontaire. Backoff exponentiel.
- Code
rate_limit_exceeded- HTTP
- 429
- Portée
- Toutes
- Ce que cela signifie
- Limite de débit de la clé API atteinte.
- Que faire
- Respectez Retry-After et RateLimit-Reset.
- Code
authentication_rate_limit_exceeded- HTTP
- 429
- Portée
- Toutes
- Ce que cela signifie
- Trop de tentatives d’authentification depuis cette adresse, avant même la validation de la clé.
- Que faire
- Signale en général une clé erronée rejouée en boucle : corrigez-la avant de réessayer.
- Code
quota_exceeded- HTTP
- 429
- Portée
- Toutes
- Ce que cela signifie
- Quota mensuel du projet épuisé — distinct du rate limit.
- Que faire
- Retry-After ne vous aidera pas : le quota se réinitialise au cycle de facturation.
- Code
rate_limiter_unavailable- HTTP
- 503
- Portée
- Toutes
- Ce que cela signifie
- Les contrôles de débit sont indisponibles ; l’API refuse de servir sans eux.
- Que faire
- Fail-closed volontaire. Backoff exponentiel.
- Code
metering_unavailable- HTTP
- 503
- Portée
- Toutes
- Ce que cela signifie
- La comptabilisation d’usage est indisponible ; aucun appel n’est servi non compté.
- Que faire
- Fail-closed volontaire. Backoff exponentiel.
- Code
invalid_siren- HTTP
- 400
- Portée
- Entreprises
- Ce que cela signifie
- Le SIREN ne contient pas exactement 9 chiffres.
- Que faire
- Validez côté client avant l’appel ; retirez les espaces.
- Code
invalid_siret- HTTP
- 400
- Portée
- Établissements
- Ce que cela signifie
- Le SIRET ne contient pas exactement 14 chiffres.
- Que faire
- Validez côté client ; un SIRET est un SIREN suivi de 5 chiffres.
- Code
invalid_query- HTTP
- 400
- Portée
- Recherche
- Ce que cela signifie
- q absent, hors de 2–100 caractères, non normalisable, ou paramètre inconnu/dupliqué.
- Que faire
- Corrigez la requête ; ne réessayez pas à l’identique.
- Code
invalid_filter- HTTP
- 400
- Portée
- Recherche
- Ce que cela signifie
- status, postal_code, commune ou naf_code invalide.
- Que faire
- status accepte active, closed ou all uniquement.
- Code
invalid_status- HTTP
- 400
- Portée
- Établissements
- Ce que cela signifie
- Le filtre status n’est pas active, closed ou all.
- Que faire
- Corrigez la valeur ; ne réessayez pas à l’identique.
- Code
invalid_limit- HTTP
- 400
- Portée
- Paginées
- Ce que cela signifie
- limit hors des bornes de l’endpoint.
- Que faire
- Voir les bornes par endpoint dans la section pagination.
- Code
invalid_cursor- HTTP
- 400
- Portée
- Paginées
- Ce que cela signifie
- Curseur illisible, expiré (24 h) ou ne correspondant pas à la requête.
- Que faire
- Ne fabriquez ni ne modifiez un curseur : reprenez à la première page.
- Code
invalid_revision_id- HTTP
- 400
- Portée
- Historique et diff
- Ce que cela signifie
- revision_id inconnu ou mal formé.
- Que faire
- Utilisez un revision_id renvoyé par /history, jamais un identifiant de snapshot interne.
- Code
invalid_idempotency_key- HTTP
- 400
- Portée
- Warmups
- Ce que cela signifie
- En-tête Idempotency-Key absent ou mal formé.
- Que faire
- Fournissez une clé stable par intention de requête.
- Code
company_not_found- HTTP
- 404
- Portée
- Entreprises
- Ce que cela signifie
- Aucun snapshot public pour ce SIREN.
- Que faire
- Absence de donnée publiable, pas une panne. Ne réessayez pas en boucle.
- Code
establishment_not_found- HTTP
- 404
- Portée
- Établissements
- Ce que cela signifie
- Aucun établissement public pour ce SIRET.
- Que faire
- Absence de donnée publiable, pas une panne. Ne réessayez pas en boucle.
- Code
rnb_not_found- HTTP
- 404
- Portée
- RNB
- Ce que cela signifie
- Aucune représentation RNB scellée pour ce SIREN.
- Que faire
- Absence de donnée, pas une panne.
- Code
warmup_not_found- HTTP
- 404
- Portée
- Warmups
- Ce que cela signifie
- L’identifiant de warmup est inconnu ou expiré.
- Que faire
- Relancez un warmup plutôt que de sonder l’identifiant.
- Code
cursor_stale- HTTP
- 409
- Portée
- Paginées
- Ce que cela signifie
- La génération de données a changé pendant votre traversée.
- Que faire
- Jetez les résultats partiels et reprenez à la première page.
- Code
revision_schema_unsupported- HTTP
- 409
- Portée
- Historique et diff
- Ce que cela signifie
- La révision demandée précède une frontière de schéma public.
- Que faire
- Traitez comme une limite d’historique, pas comme une erreur transitoire.
- Code
idempotency_key_reused- HTTP
- 409
- Portée
- Warmups
- Ce que cela signifie
- La clé d’idempotence a déjà servi pour une requête différente.
- Que faire
- Une clé par intention : n’en rejouez pas une avec un corps différent.
- Code
temporarily_unavailable- HTTP
- 503
- Portée
- Entreprises et établissements
- Ce que cela signifie
- Dépendance de données indisponible.
- Que faire
- Backoff exponentiel.
- Code
search_unavailable- HTTP
- 503
- Portée
- Recherche
- Ce que cela signifie
- Le corpus de recherche est temporairement indisponible.
- Que faire
- Backoff exponentiel ; le lookup par SIREN reste disponible.
- Code
establishment_data_not_ready- HTTP
- 503
- Portée
- Établissements
- Ce que cela signifie
- Aucune collection d’établissements complète n’a encore été publiée pour ce SIREN.
- Que faire
- État d’ingestion, pas une panne : réessayez plus tard, sans boucle serrée.
- Code
invalid_snapshot- HTTP
- 503
- Portée
- Entreprises
- Ce que cela signifie
- Le snapshot courant est illisible ; l’API préfère échouer que servir une donnée douteuse.
- Que faire
- Signalez-le avec le request_id : cela ne se corrige pas côté client.
- Code
dependency_unavailable- HTTP
- 503
- Portée
- Toutes
- Ce que cela signifie
- Une dépendance interne requise est indisponible.
- Que faire
- Backoff exponentiel.
- Code
warmup_unavailable- HTTP
- 503
- Portée
- Warmups
- Ce que cela signifie
- Le service de warmup est indisponible.
- Que faire
- Backoff exponentiel.
- Code
internal_error- HTTP
- 500
- Portée
- Toutes
- Ce que cela signifie
- Erreur inattendue.
- Que faire
- Conservez le request_id et signalez-le.
Les 4xx décrivent votre requête et ne se résolvent pas en réessayant à l’identique. Les 503 sont transitoires et méritent un backoff exponentiel. Le 409 est le seul cas qui demande de recommencer une traversée depuis le début.
Statuts que votre client Pappers n’attend pas
Comparé au contrat Pappers v2.20.0, qui déclare 24 opérations, la façade de compatibilité renvoie des statuts qu’un client écrit contre Pappers ne gère probablement pas. Deux situations différentes, à ne pas confondre.
| HTTP | Déclaré par Pappers ? | Quand | Comportement client attendu |
|---|---|---|---|
| 403 | Oui, sur 3 des 24 opérations | Scope manquant sur la clé. Nous l’émettons depuis le middleware commun, donc sur n’importe quelle route. | Votre branche 403 existe peut-être déjà, mais elle doit couvrir toutes les routes, pas seulement les trois d’origine. |
| 429 | Non, jamais | Débit ou quota dépassé. | Branche à ajouter : distinguez rate_limit_exceeded de quota_exceeded. |
| 500 | Non, jamais | Erreur inattendue. | Branche à ajouter ; conservez le request_id. |
| 451 | Non, jamais | Route bénéficiaires effectifs : frontière juridique explicite, authentifiée et non facturée. | Retirez l’appel de votre intégration ; aucune donnée ne sera servie sur cette route. |
| 501 | Non, jamais | Route de conformité personne physique : non implémentée, sans couverture implicite. | Traitez comme définitivement indisponible, pas comme une panne transitoire. |
- HTTP
- 403
- Déclaré par Pappers ?
- Oui, sur 3 des 24 opérations
- Quand
- Scope manquant sur la clé. Nous l’émettons depuis le middleware commun, donc sur n’importe quelle route.
- Comportement client attendu
- Votre branche 403 existe peut-être déjà, mais elle doit couvrir toutes les routes, pas seulement les trois d’origine.
- HTTP
- 429
- Déclaré par Pappers ?
- Non, jamais
- Quand
- Débit ou quota dépassé.
- Comportement client attendu
- Branche à ajouter : distinguez rate_limit_exceeded de quota_exceeded.
- HTTP
- 500
- Déclaré par Pappers ?
- Non, jamais
- Quand
- Erreur inattendue.
- Comportement client attendu
- Branche à ajouter ; conservez le request_id.
- HTTP
- 451
- Déclaré par Pappers ?
- Non, jamais
- Quand
- Route bénéficiaires effectifs : frontière juridique explicite, authentifiée et non facturée.
- Comportement client attendu
- Retirez l’appel de votre intégration ; aucune donnée ne sera servie sur cette route.
- HTTP
- 501
- Déclaré par Pappers ?
- Non, jamais
- Quand
- Route de conformité personne physique : non implémentée, sans couverture implicite.
- Comportement client attendu
- Traitez comme définitivement indisponible, pas comme une panne transitoire.
Déclarations Pappers comptées dans contracts/pappers/inventory-v2.20.0.json ; comportement Entreprise.dev lu dans les handlers. Aucun appel live à Pappers.
Limites de débit et quota
Deux mécanismes distincts s’appliquent, et ils échouent différemment. La limite de débit est court terme et par clé ; le quota est mensuel et par projet. Les deux renvoient 429, mais avec un code différent — c’est le code qui vous dit lequel.
| En-tête | Signification |
|---|---|
RateLimit-Limit | Nombre de requêtes autorisées sur la fenêtre courante. |
RateLimit-Remaining | Requêtes restantes sur cette fenêtre. |
RateLimit-Reset | Horodatage Unix absolu de la réinitialisation, en secondes depuis l’epoch — et non un délai. Le brouillon IETF impose un délai en secondes ; nous émettons un epoch. Soustrayez l’heure courante, ou utilisez Retry-After. Divergence suivie par l’issue #109 : la valeur changera d’unité. |
Retry-After | Délai d’attente conseillé. Présent sur les 429 de débit et certains 503. |
- En-tête
RateLimit-Limit- Signification
- Nombre de requêtes autorisées sur la fenêtre courante.
- En-tête
RateLimit-Remaining- Signification
- Requêtes restantes sur cette fenêtre.
- En-tête
RateLimit-Reset- Signification
- Horodatage Unix absolu de la réinitialisation, en secondes depuis l’epoch — et non un délai. Le brouillon IETF impose un délai en secondes ; nous émettons un epoch. Soustrayez l’heure courante, ou utilisez Retry-After. Divergence suivie par l’issue #109 : la valeur changera d’unité.
- En-tête
Retry-After- Signification
- Délai d’attente conseillé. Présent sur les 429 de débit et certains 503.
Un 429 quota_exceeded ne se résout pas en attendant Retry-After : le quota se réinitialise au cycle de facturation. La consommation est visible dans la console développeur, et GET /v2/suivi-jetons expose le solde sans être elle-même facturée.
Les lectures répétées d’une même entreprise sont bon marché : les réponses de lookup, d’historique et d’établissements portent un ETag. Renvoyez-le en If-None-Match et l’API répond 304 Not Modified sans corps. La recherche paginée est délibérément exclue de ce mécanisme : une page multi-entreprises ne doit pas survivre à une restriction de diffusion.
Vérifier la fraîcheur
meta.data_as_of, meta.stale, meta.completeness et meta.sources permettent de décider si votre workflow accepte le snapshot ou attend une mise à jour.
Préparez le changement de base URL. Vérifiez chaque opération.
La façade de preview sert localement /v2/entreprise, /v2/recherche et/v2/suivi-jetons, avec des réponses explicites pour les opérations sensibles hors périmètre. Une compatibilité n’est annoncée Exact qu’après comparaison live de la route, de ses options et de sa projection de données ; les écarts restent visibles au lieu d’être masqués.
- Avant · Pappers
https://api.pappers.fr/v2- Après · Entreprise.dev (preview locale)
$ENTREPRISE_ORIGIN/v2Défaut des exemples : http://localhost:8088
Gardez le header api-key. Le paramètre historique api_token reste prévu pour la transition, mais évitez-le : une clé placée dans l’URL peut finir dans l’historique ou les journaux d’un intermédiaire.
Aucune opération n’est encore publiée Exact. Ne basculez en production qu’après validation de vos routes et champs dans la matrice versionnée.
Couverture disponible en preview
Ces routes sont contract-testées, mais restent compatibles seulement en partie tant que leurs limites ci-dessous subsistent.
| Route | Couvert aujourd’hui | Limites connues | Statut |
|---|---|---|---|
GET /v2/entreprise | Clé api-key, SIREN ou SIRET, forme racine et principales erreurs couvertes par les fixtures. | Projection de données encore partielle ; les options non prises en charge sont rejetées. | Compatible partiel · Preview |
GET /v2/recherche | Recherche q, pages jusqu’à 400 résultats, curseur opaque et principales erreurs couvertes. | Sous-ensemble de filtres ; q obligatoire ; total au-delà de 400 seulement borné. | Compatible partiel · Preview |
GET /v2/suivi-jetons | Solde local authentifié, isolé par projet et environnement, sans facturation de la requête. | Projection Derived : la sémantique exacte Pappers attend encore une fixture live de succès. | Derived · Preview |
GET /v2/recherche-beneficiaires · GET /v2/document/declaration_beneficiaires_effectifs | Frontière explicite, authentifiée et non facturée : réponse 451 sans acquisition ni lecture de données. | Bénéficiaires effectifs entièrement hors V1, en attente d’une revue juridique séparée. | Restricted · Hors V1 |
GET /v2/conformite/personne_physique | Réponse 501 explicite, authentifiée et non facturée ; aucune couverture implicite. | Nécessite une source sanctions/PEP autorisée et une politique de conservation approuvée. | Source requise |
- Route
GET /v2/entreprise- Couvert aujourd’hui
- Clé api-key, SIREN ou SIRET, forme racine et principales erreurs couvertes par les fixtures.
- Limites connues
- Projection de données encore partielle ; les options non prises en charge sont rejetées.
- Statut
- Compatible partiel · Preview
- Route
GET /v2/recherche- Couvert aujourd’hui
- Recherche q, pages jusqu’à 400 résultats, curseur opaque et principales erreurs couvertes.
- Limites connues
- Sous-ensemble de filtres ; q obligatoire ; total au-delà de 400 seulement borné.
- Statut
- Compatible partiel · Preview
- Route
GET /v2/suivi-jetons- Couvert aujourd’hui
- Solde local authentifié, isolé par projet et environnement, sans facturation de la requête.
- Limites connues
- Projection Derived : la sémantique exacte Pappers attend encore une fixture live de succès.
- Statut
- Derived · Preview
- Route
GET /v2/recherche-beneficiaires · GET /v2/document/declaration_beneficiaires_effectifs- Couvert aujourd’hui
- Frontière explicite, authentifiée et non facturée : réponse 451 sans acquisition ni lecture de données.
- Limites connues
- Bénéficiaires effectifs entièrement hors V1, en attente d’une revue juridique séparée.
- Statut
- Restricted · Hors V1
- Route
GET /v2/conformite/personne_physique- Couvert aujourd’hui
- Réponse 501 explicite, authentifiée et non facturée ; aucune couverture implicite.
- Limites connues
- Nécessite une source sanctions/PEP autorisée et une politique de conservation approuvée.
- Statut
- Source requise
Quels champs portent réellement une donnée
GET /v2/entreprise renvoie 84 clés racine avec les noms attendus par un client Pappers. Aujourd’hui, 15 sont mappées — elles peuvent porter une valeur, mais plusieurs restent null quand la source n’a rien pour cette entreprise — et 69 sont systématiquement null.
Le mapper initialise les 84 clés à null puis renseigne les 15 ci-dessous. Un code qui teste "capital" in response passera et lira null. Testez la valeur, pas la clé.
Renseignés (15)
sirensiren_formatedenominationnom_entreprisecategorie_juridiquedate_creationobjet_socialentreprise_cesseediffusablenumero_tva_intracommunautairesiegeetablissementsderniere_mise_a_jour_sirenederniere_mise_a_jour_rnedernier_traitement
etablissements est remplacé par etablissement lorsque vous interrogez un SIRET plutôt qu’un SIREN.
Les absences qui bloquent le plus souvent une migration
| Champ | Pourquoi cela compte |
|---|---|
capital | Capital social — souvent affiché ou utilisé en scoring interne. |
code_naf | Code d’activité — fréquemment utilisé pour segmenter ou router. |
representants | Dirigeants — cœur des usages KYC et onboarding. |
numero_rcs | Immatriculation RCS — attendue sur les documents contractuels. |
finances | Chiffre d’affaires et résultat — pas encore projetés. |
publications_bodacc | Annonces légales — pas encore projetées. |
procedures_collectives | Procédures — critique pour le risque, jamais devinée. |
beneficiaires_effectifs | Bénéficiaires effectifs — exclus par décision juridique, pas par manque de données. |
- Champ
capital- Pourquoi cela compte
- Capital social — souvent affiché ou utilisé en scoring interne.
- Champ
code_naf- Pourquoi cela compte
- Code d’activité — fréquemment utilisé pour segmenter ou router.
- Champ
representants- Pourquoi cela compte
- Dirigeants — cœur des usages KYC et onboarding.
- Champ
numero_rcs- Pourquoi cela compte
- Immatriculation RCS — attendue sur les documents contractuels.
- Champ
finances- Pourquoi cela compte
- Chiffre d’affaires et résultat — pas encore projetés.
- Champ
publications_bodacc- Pourquoi cela compte
- Annonces légales — pas encore projetées.
- Champ
procedures_collectives- Pourquoi cela compte
- Procédures — critique pour le risque, jamais devinée.
- Champ
beneficiaires_effectifs- Pourquoi cela compte
- Bénéficiaires effectifs — exclus par décision juridique, pas par manque de données.
Toujours null aujourd’hui (69)
Listés en entier pour que vous puissiez chercher le champ dont dépend votre intégration. Le nom est stable : quand la donnée arrivera, elle arrivera sous ce nom, sans rupture de contrat.
Afficher les 69 champs toujours nuls
activites_rneannee_effectifannee_tranche_effectifassociationassocie_uniquebeneficiaires_effectifscapitalcapital_actuel_si_variablecapital_formatecode_greffecode_nafcode_naf_2025code_nafacomptesconventions_collectivesdate_cessationdate_cessation_formatedate_cloture_exercicedate_cloture_exercice_exceptionnelledate_creation_formatedate_debut_activitedate_debut_premiere_activitedate_immatriculation_rcsdate_immatriculation_rnedate_premiere_immatriculation_rcsdate_radiation_rcsdate_radiation_rnedate_reouverturedepots_actesderniere_mise_a_jour_rcsderniers_statutsdevise_capitaldomaine_activiteduree_personne_moraleeconomie_sociale_solidaireeffectifeffectif_maxeffectif_minentreprise_employeuseextrait_immatriculationfinancesfinances_consolideesforme_exerciceforme_juridiqueformes_exercicegreffelibelle_code_nafnomnumero_rcsopposition_utilisation_commercialepersonne_moraleprenomprenomsprocedure_collective_en_coursprocedure_collective_existeprocedures_collectivesprochaine_date_cloture_exerciceprochaine_date_cloture_exercice_formatepublications_bodaccqualite_artisanrepresentantsrnmsexesiglesociete_a_missionstatut_consolidestatut_rcsstatut_rnetranche_effectif
Deux causes différentes se cachent derrière ces absences. La plupart sont un travail d’ingestion et de projection encore à faire. beneficiaires_effectifs est un cas distinct : l’accès est juridiquement restreint et son absence est une décision, pas un retard.
Une réponse réelle, telle qu’elle arrive aujourd’hui
Cet exemple n’est pas une illustration flatteuse : il est généré à partir de la même liste que la section ci-dessus, donc il montre exactement les 15 champs renseignés et les 69 champs nuls que vous recevrez.
{
"activites_rne": null,
"annee_effectif": null,
"annee_tranche_effectif": null,
"association": null,
"associe_unique": null,
"beneficiaires_effectifs": null,
"capital": null,
"capital_actuel_si_variable": null,
"capital_formate": null,
"categorie_juridique": "5710",
"code_greffe": null,
"code_naf": null,
"code_naf_2025": null,
"code_nafa": null,
"comptes": null,
"conventions_collectives": null,
"date_cessation": null,
"date_cessation_formate": null,
"date_cloture_exercice": null,
"date_cloture_exercice_exceptionnelle": null,
"date_creation": "1922-02-25",
"date_creation_formate": null,
"date_debut_activite": null,
"date_debut_premiere_activite": null,
"date_immatriculation_rcs": null,
"date_immatriculation_rne": null,
"date_premiere_immatriculation_rcs": null,
"date_radiation_rcs": null,
"date_radiation_rne": null,
"date_reouverture": null,
"denomination": "RENAULT SAS",
"depots_actes": null,
"dernier_traitement": "2026-08-02",
"derniere_mise_a_jour_rcs": null,
"derniere_mise_a_jour_rne": "2026-08-01",
"derniere_mise_a_jour_sirene": "2026-08-02",
"derniers_statuts": null,
"devise_capital": null,
"diffusable": true,
"domaine_activite": null,
"duree_personne_morale": null,
"economie_sociale_solidaire": null,
"effectif": null,
"effectif_max": null,
"effectif_min": null,
"entreprise_cessee": false,
"entreprise_employeuse": null,
"etablissements": [
{
"siren": "552100554",
"siret": "55210055400858",
"siege": true,
"entreprise_cessee": false
}
],
"extrait_immatriculation": null,
"finances": null,
"finances_consolidees": null,
"forme_exercice": null,
"forme_juridique": null,
"formes_exercice": null,
"greffe": null,
"libelle_code_naf": null,
"nom": null,
"nom_entreprise": "RENAULT SAS",
"numero_rcs": null,
"numero_tva_intracommunautaire": "FR76552100554",
"objet_social": "Construction de véhicules automobiles",
"opposition_utilisation_commerciale": null,
"personne_morale": null,
"prenom": null,
"prenoms": null,
"procedure_collective_en_cours": null,
"procedure_collective_existe": null,
"procedures_collectives": null,
"prochaine_date_cloture_exercice": null,
"prochaine_date_cloture_exercice_formate": null,
"publications_bodacc": null,
"qualite_artisan": null,
"representants": null,
"rnm": null,
"sexe": null,
"siege": {
"siret": "55210055400858",
"adresse_ligne_1": "122 AVENUE DU GENERAL LECLERC",
"ville": "BOULOGNE-BILLANCOURT",
"code_postal": "92100",
"code_naf": "29.10Z"
},
"sigle": null,
"siren": "552100554",
"siren_formate": "552 100 554",
"societe_a_mission": null,
"statut_consolide": null,
"statut_rcs": null,
"statut_rne": null,
"tranche_effectif": null
}Exemple dérivé du contrat et du code du mapper, non capturé sur un appel réel.
export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl "$ENTREPRISE_ORIGIN/v2/entreprise?siren=552100554" \
-H "api-key: $ENTREPRISE_API_KEY" \
-H "Accept: application/json"export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl --get "$ENTREPRISE_ORIGIN/v2/recherche" \
-H "api-key: $ENTREPRISE_API_KEY" \
-H "Accept: application/json" \
--data-urlencode "q=TOTALENERGIES" \
--data-urlencode "par_page=20"export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl "$ENTREPRISE_ORIGIN/v2/suivi-jetons" \
-H "api-key: $ENTREPRISE_API_KEY" \
-H "Accept: application/json"const entrepriseOrigin = process.env.ENTREPRISE_ORIGIN ?? "http://localhost:8088";
const response = await fetch(
`${entrepriseOrigin}/v2/entreprise?siren=552100554`,
{ headers: { "api-key": process.env.ENTREPRISE_API_KEY } },
);
if (!response.ok) throw new Error(`API error: ${response.status}`);
const entreprise = await response.json();import os
import requests
entreprise_origin = os.environ.get("ENTREPRISE_ORIGIN", "http://localhost:8088")
response = requests.get(
f"{entreprise_origin}/v2/entreprise",
params={"siren": "552100554"},
headers={"api-key": os.environ["ENTREPRISE_API_KEY"]},
timeout=10,
)
response.raise_for_status()
entreprise = response.json()Lisez le niveau avant de basculer
Le niveau s’applique à une route ou un champ précis, jamais à toute l’API par défaut.
| Niveau | Ce que cela garantit | Décision de migration |
|---|---|---|
| Exact | Route, paramètres, erreurs et projection de données comparés live puis verrouillés par des fixtures versionnées. | Bascule directe après votre test de non-régression. |
| Equivalent | Même donnée ou même usage, mais normalisation, ordre ou provenance peuvent différer. | Validez les valeurs métier, pas un diff JSON octet par octet. |
| Derived | Valeur calculée localement à partir de données traçables, sans prétendre reproduire une valeur propriétaire à l’identique. | Validez la formule et la provenance avant de remplacer le champ Pappers correspondant. |
| Restricted | Donnée soumise à une base légale, une habilitation ou un contrôle d’accès dédié. | Absente du profil public ; demande d’accès séparée obligatoire. |
| Proprietary | Score, jeton ou enrichissement propre à Pappers, sans source publique reproductible. | Non imité ; utilisez une alternative Entreprise.dev explicitement documentée. |
- Niveau
- Exact
- Ce que cela garantit
- Route, paramètres, erreurs et projection de données comparés live puis verrouillés par des fixtures versionnées.
- Décision de migration
- Bascule directe après votre test de non-régression.
- Niveau
- Equivalent
- Ce que cela garantit
- Même donnée ou même usage, mais normalisation, ordre ou provenance peuvent différer.
- Décision de migration
- Validez les valeurs métier, pas un diff JSON octet par octet.
- Niveau
- Derived
- Ce que cela garantit
- Valeur calculée localement à partir de données traçables, sans prétendre reproduire une valeur propriétaire à l’identique.
- Décision de migration
- Validez la formule et la provenance avant de remplacer le champ Pappers correspondant.
- Niveau
- Restricted
- Ce que cela garantit
- Donnée soumise à une base légale, une habilitation ou un contrôle d’accès dédié.
- Décision de migration
- Absente du profil public ; demande d’accès séparée obligatoire.
- Niveau
- Proprietary
- Ce que cela garantit
- Score, jeton ou enrichissement propre à Pappers, sans source publique reproductible.
- Décision de migration
- Non imité ; utilisez une alternative Entreprise.dev explicitement documentée.