DOCUMENTATION · V1

Votre premier lookup.

Une clé test, un SIREN et une réponse JSON versionnée. Ce quickstart décrit uniquement les routes implémentées et contract-testées aujourd’hui.

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.

HTTPDéfilement horizontal si nécessaireFaire défiler le code
Authorization: Bearer ent_test_votre_prefixe_votre_secret

Les 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

GET/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.

cURLDéfilement horizontal si nécessaireFaire défiler le code
export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl "$ENTREPRISE_ORIGIN/v1/companies/552100554" \
  -H "Authorization: Bearer $ENTREPRISE_API_KEY" \
  -H "Accept: application/json"
JavaScript · Node.jsDéfilement horizontal si nécessaireFaire défiler le code
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.
PythonDéfilement horizontal si nécessaireFaire défiler le code
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"]
GET/v1/search

Recherche 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.

cURL · RechercheDéfilement horizontal si nécessaireFaire défiler le code
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

GET/v1/companies/{siren}/establishments
GET/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.

cURL · ÉtablissementsDéfilement horizontal si nécessaireFaire défiler le code
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.

GET/v1/companies/{siren}/history
GET/v1/companies/{siren}/diff?from_revision_id=…
cURLDéfilement horizontal si nécessaireFaire défiler le code
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.

EndpointlimitAncrage du curseur
GET /v1/search1 – 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}/history1 – 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}/establishments1 – 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.
Prévoyez le 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.

Le même événement n’a pas le même statut sur les deux surfaces.

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.

cURL · PaginationDéfilement horizontal si nécessaireFaire défiler le code
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"
JavaScript · PaginationDéfilement horizontal si nécessaireFaire défiler le code
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.

Deux enveloppes d’erreur coexistent.

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/*

JSON · v1Défilement horizontal si nécessaireFaire défiler le code
{
  "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.

JSON · v2Défilement horizontal si nécessaireFaire défiler le code
{
  "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.

CodeHTTPPortéeCe que cela signifieQue faire
invalid_api_key401ToutesClé 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_scope403ToutesLa 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_unavailable503ToutesLe service d’authentification est indisponible ; l’API refuse de servir sans lui.Fail-closed volontaire. Backoff exponentiel.
rate_limit_exceeded429ToutesLimite de débit de la clé API atteinte.Respectez Retry-After et RateLimit-Reset.
authentication_rate_limit_exceeded429ToutesTrop 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_exceeded429ToutesQuota 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_unavailable503ToutesLes contrôles de débit sont indisponibles ; l’API refuse de servir sans eux.Fail-closed volontaire. Backoff exponentiel.
metering_unavailable503ToutesLa comptabilisation d’usage est indisponible ; aucun appel n’est servi non compté.Fail-closed volontaire. Backoff exponentiel.
invalid_siren400EntreprisesLe SIREN ne contient pas exactement 9 chiffres.Validez côté client avant l’appel ; retirez les espaces.
invalid_siret400ÉtablissementsLe SIRET ne contient pas exactement 14 chiffres.Validez côté client ; un SIRET est un SIREN suivi de 5 chiffres.
invalid_query400Rechercheq 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_filter400Recherchestatus, postal_code, commune ou naf_code invalide.status accepte active, closed ou all uniquement.
invalid_status400ÉtablissementsLe filtre status n’est pas active, closed ou all.Corrigez la valeur ; ne réessayez pas à l’identique.
invalid_limit400Paginéeslimit hors des bornes de l’endpoint.Voir les bornes par endpoint dans la section pagination.
invalid_cursor400PaginéesCurseur 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_id400Historique et diffrevision_id inconnu ou mal formé.Utilisez un revision_id renvoyé par /history, jamais un identifiant de snapshot interne.
invalid_idempotency_key400WarmupsEn-tête Idempotency-Key absent ou mal formé.Fournissez une clé stable par intention de requête.
company_not_found404EntreprisesAucun snapshot public pour ce SIREN.Absence de donnée publiable, pas une panne. Ne réessayez pas en boucle.
establishment_not_found404ÉtablissementsAucun établissement public pour ce SIRET.Absence de donnée publiable, pas une panne. Ne réessayez pas en boucle.
rnb_not_found404RNBAucune représentation RNB scellée pour ce SIREN.Absence de donnée, pas une panne.
warmup_not_found404WarmupsL’identifiant de warmup est inconnu ou expiré.Relancez un warmup plutôt que de sonder l’identifiant.
cursor_stale409PaginéesLa 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_unsupported409Historique et diffLa 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_reused409WarmupsLa 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_unavailable503Entreprises et établissementsDépendance de données indisponible.Backoff exponentiel.
search_unavailable503RechercheLe corpus de recherche est temporairement indisponible.Backoff exponentiel ; le lookup par SIREN reste disponible.
establishment_data_not_ready503ÉtablissementsAucune 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_snapshot503EntreprisesLe 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_unavailable503ToutesUne dépendance interne requise est indisponible.Backoff exponentiel.
warmup_unavailable503WarmupsLe service de warmup est indisponible.Backoff exponentiel.
internal_error500ToutesErreur 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.

HTTPDéclaré par Pappers ?QuandComportement client attendu
403Oui, sur 3 des 24 opérationsScope 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.
429Non, jamaisDébit ou quota dépassé.Branche à ajouter : distinguez rate_limit_exceeded de quota_exceeded.
500Non, jamaisErreur inattendue.Branche à ajouter ; conservez le request_id.
451Non, jamaisRoute 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.
501Non, jamaisRoute 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êteSignification
RateLimit-LimitNombre de requêtes autorisées sur la fenêtre courante.
RateLimit-RemainingRequêtes restantes sur cette fenêtre.
RateLimit-ResetHorodatage 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-AfterDé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.

MIGRATION PAPPERS · V2 · PREVIEW

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
Clé API

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.

RouteCouvert aujourd’huiLimites connuesStatut
GET /v2/entrepriseClé 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/rechercheRecherche 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-jetonsSolde 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_effectifsFrontiè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_physiqueRé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.

La présence d’une clé ne prouve pas la présence d’une donnée.

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)

  • siren
  • siren_formate
  • denomination
  • nom_entreprise
  • categorie_juridique
  • date_creation
  • objet_social
  • entreprise_cessee
  • diffusable
  • numero_tva_intracommunautaire
  • siege
  • etablissements
  • derniere_mise_a_jour_sirene
  • derniere_mise_a_jour_rne
  • dernier_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

ChampPourquoi cela compte
capitalCapital social — souvent affiché ou utilisé en scoring interne.
code_nafCode d’activité — fréquemment utilisé pour segmenter ou router.
representantsDirigeants — cœur des usages KYC et onboarding.
numero_rcsImmatriculation RCS — attendue sur les documents contractuels.
financesChiffre d’affaires et résultat — pas encore projetés.
publications_bodaccAnnonces légales — pas encore projetées.
procedures_collectivesProcédures — critique pour le risque, jamais devinée.
beneficiaires_effectifsBé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_rne
  • annee_effectif
  • annee_tranche_effectif
  • association
  • associe_unique
  • beneficiaires_effectifs
  • capital
  • capital_actuel_si_variable
  • capital_formate
  • code_greffe
  • code_naf
  • code_naf_2025
  • code_nafa
  • comptes
  • conventions_collectives
  • date_cessation
  • date_cessation_formate
  • date_cloture_exercice
  • date_cloture_exercice_exceptionnelle
  • date_creation_formate
  • date_debut_activite
  • date_debut_premiere_activite
  • date_immatriculation_rcs
  • date_immatriculation_rne
  • date_premiere_immatriculation_rcs
  • date_radiation_rcs
  • date_radiation_rne
  • date_reouverture
  • depots_actes
  • derniere_mise_a_jour_rcs
  • derniers_statuts
  • devise_capital
  • domaine_activite
  • duree_personne_morale
  • economie_sociale_solidaire
  • effectif
  • effectif_max
  • effectif_min
  • entreprise_employeuse
  • extrait_immatriculation
  • finances
  • finances_consolidees
  • forme_exercice
  • forme_juridique
  • formes_exercice
  • greffe
  • libelle_code_naf
  • nom
  • numero_rcs
  • opposition_utilisation_commerciale
  • personne_morale
  • prenom
  • prenoms
  • procedure_collective_en_cours
  • procedure_collective_existe
  • procedures_collectives
  • prochaine_date_cloture_exercice
  • prochaine_date_cloture_exercice_formate
  • publications_bodacc
  • qualite_artisan
  • representants
  • rnm
  • sexe
  • sigle
  • societe_a_mission
  • statut_consolide
  • statut_rcs
  • statut_rne
  • tranche_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.

JSON · GET /v2/entrepriseDéfilement horizontal si nécessaireFaire défiler le code
{
  "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.

cURLDéfilement horizontal si nécessaireFaire défiler le code
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"
cURL · RechercheDéfilement horizontal si nécessaireFaire défiler le code
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"
cURL · CréditsDéfilement horizontal si nécessaireFaire défiler le code
export ENTREPRISE_ORIGIN="${ENTREPRISE_ORIGIN:-http://localhost:8088}"
curl "$ENTREPRISE_ORIGIN/v2/suivi-jetons" \
  -H "api-key: $ENTREPRISE_API_KEY" \
  -H "Accept: application/json"
JavaScript · Node.jsDéfilement horizontal si nécessaireFaire défiler le code
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();
PythonDéfilement horizontal si nécessaireFaire défiler le code
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.

NiveauCe que cela garantitDécision de migration
ExactRoute, 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.
EquivalentMê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.
DerivedValeur 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.
RestrictedDonné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.
ProprietaryScore, 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.