Auditer les fiches d’une entreprise avec un listings scan

Vérifiez toute entreprise sur cartes, moteurs, annuaires et assistants IA, ses nom/adresse/téléphone par annuaire ; résultats par webhook ou polling.

Un listings scan vérifie comment une entreprise figure dans le réseau d'annuaires listingsAPI et vous indique, annuaire par annuaire, si elle est présente et si son nom, son adresse et son numéro de téléphone correspondent aux informations que vous avez fournies. L'entreprise n'a pas besoin d'être un établissement de votre compte. Ce guide vous explique comment créer un scan, attendre qu'il se termine et consulter les résultats.

Utilisez-le pour :

  • auditer un prospect avant de l'intégrer, afin de commencer le premier échange par les fiches manquantes ou incorrectes ;
  • vérifier la présence d'un client et la cohérence de ses coordonnées NAP (nom, adresse, téléphone) avant et après la correction de ses fiches ;
  • permettre à un agent IA de répondre à la question « Comment cette entreprise est-elle répertoriée ? » sans ajouter l'entreprise au compte.

1. Créer le scan

POST /listings/scans reçoit les informations de l'entreprise dans un seul objet input et renvoie immédiatement le scan à l'état QUEUED. Les annuaires sont vérifiés en arrière-plan.

ChampObligatoireRemarques
nameOui150 caractères maximum.
streetOui200 caractères maximum.
cityOui100 caractères maximum.
stateIsoOui, sauf pour GBCode de l'État ou de la province, 50 caractères maximum.
postalCodeOui, sauf pour NG et AE20 caractères maximum.
countryIsoOuiCode ISO 3166-1 à 2 lettres, par exemple US.
phoneOui6 à 15 chiffres. Les formats locaux sont interprétés en fonction de countryIso, et un + en début de numéro est également accepté.
Create a scan
curl -X POST https://listingsapi.com/api/v4/listings/scans \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d @- <<'JSON'{"input": {  "name": "Joe's Pizza",  "street": "7 Carmine Street",  "city": "New York",  "stateIso": "NY",  "postalCode": "10014",  "countryIso": "US",  "phone": "2123661182"}}JSON

La réponse renvoie le scan, avec phone normalisé au format international. results reste vide tant que les annuaires n'ont pas été vérifiés ; conservez donc id.

JSON
{  "data": {    "createListingScan": {      "reused": false,      "scan": {        "id": "TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz",        "status": "QUEUED",        "source": "DIRECTORY_SEARCH",        "locationId": null,        "resultsAsOf": null,        "createdAt": "2026-11-05T07:01:08Z",        "startedAt": null,        "completedAt": null,        "business": {          "name": "Joe's Pizza",          "street": "7 Carmine Street",          "city": "New York",          "stateIso": "NY",          "postalCode": "10014",          "countryIso": "US",          "phone": "+12123661182"        },        "summary": {          "total": 0,          "found": 0,          "accurate": 0,          "inaccurate": 0,          "notFound": 0,          "pending": 0,          "unavailable": 0        },        "failure": null,        "results": []      },      "errors": []    }  }}

La réponse inclut également un objet quota, décrit dans Coûts, limites et erreurs.

2 points à vérifier avant de continuer :

  • reused. Lorsque vous demandez un scan pour une entreprise que vous avez déjà scannée au cours des 7 derniers jours, listingsAPI renvoie ce scan au lieu d'en lancer un nouveau, avec reused: true et son état actuel, généralement COMPLETED. Rien n'est décompté de votre quota et aucun nouveau webhook n'est envoyé pour ce scan.
  • errors. Même lorsqu'une requête est rejetée, le statut HTTP renvoyé est 200. Le rejet est indiqué dans data.createListingScan.errors, et scan vaut null. Voici un exemple pour un numéro de téléphone trop court :
JSON
{  "data": {    "createListingScan": {      "reused": false,      "scan": null,      "errors": [        {          "code": "SY83003",          "message": "SY83003: Invalid value for phone",          "contextInfo": [            {              "key": "field",              "value": "phone"            }          ]        }      ]    }  }}

contextInfo indique le nom du champ dont la validation a échoué. Les autres codes possibles figurent dans le tableau des erreurs.

2. Attendre les résultats

Un scan passe de QUEUED à IN_PROGRESS, puis à COMPLETED ou FAILED. La plupart se terminent en 1 à 7 minutes. Si un annuaire n'a pas répondu après environ 15 minutes, son résultat est défini sur UNAVAILABLE et le scan se termine avec les autres résultats. Utilisez un webhook si possible et ne recourez aux interrogations périodiques qu'en solution de repli.

Lorsque le scan atteint un état final, listingsAPI envoie un événement listing_scan.completed ou listing_scan.failed à l'URL de votre webhook. Son data.scan est le même objet que celui renvoyé par GET, résultats inclus ; vous n'avez donc rien à récupérer ensuite.

JSON
{  "event": "listing_scan.completed",  "timestamp": "2026-11-05T07:01:52Z",  "account_id": 12345,  "data": {    "scan": {      "id": "TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz",      "status": "COMPLETED",      "summary": { "total": 62, "found": 21, "accurate": 18, "inaccurate": 2, "notFound": 41, "pending": 0, "unavailable": 0 },      "results": [ "..." ]    }  }}

Le contenu de l'événement est abrégé ici ; l'exemple complet figure dans la référence des événements. Les événements sont signés et transmis comme tous les autres événements, avec une seule tentative et un délai maximal de 5 secondes ; vérifiez donc la signature, stockez le corps brut et accusez réception avant toute autre opération. Le récepteur présenté dans Réagir aux événements avec les webhooks effectue déjà ces opérations ; la seule modification consiste à ajouter une branche pour event.

One more branch in your receiver
# After the signature check and the endpoint.verification branch:if body.get("event") in ("listing_scan.completed", "listing_scan.failed"):  inbox.put(raw)  # commit before the 200; read data.scan from the queue  return {"received": True}, 200

Un événement est envoyé uniquement aux comptes dont les webhooks sont activés et dont l'URL est vérifiée. Si votre compte ne remplit pas ces conditions, ou si un événement n'arrive jamais, interrogez périodiquement le scan ; celui-ci est conservé pendant 90 jours dans tous les cas. Les détails concernant la signature figurent dans Signatures et vérification.

2b. Interroger périodiquement en solution de repli

GET /listings/scans/{scanId} renvoie l’analyse avec les résultats disponibles à ce stade. Pendant l’analyse, results se remplit à mesure que les annuaires répondent et summary.pending compte ceux dont la réponse est encore attendue. Interrogez toutes les 15 secondes, pas plus souvent ; chaque requête est prise en compte dans votre limite de débit.

Poll until the scan finishes
SCAN_ID="TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz" while true; doscan=$(curl -s "https://listingsapi.com/api/v4/listings/scans/$SCAN_ID" \  -H "Authorization: API $LISTINGSAPI_KEY")status=$(echo "$scan" | jq -r '.data.listingScan.status')echo "status: $status"case "$status" in  COMPLETED|FAILED)    echo "$scan" | jq '.data.listingScan | {status, summary, failure}'    break ;;  null)    echo "$scan"    break ;;esacsleep 15done
statusSignification
QUEUEDAcceptée et en attente de démarrage.
IN_PROGRESSLes annuaires sont en cours de vérification. results est partiel.
COMPLETEDChaque annuaire a une réponse. results et summary sont définitifs.
FAILEDL’analyse n’a pas pu démarrer, par exemple parce que le service était temporairement indisponible (SY83010). failure contient code et message, et results est vide. Une analyse qui échoue ne consomme pas votre quota ; vous pouvez donc la créer à nouveau.

Une analyse dont les résultats proviennent d’une analyse récente ou de l’un de vos propres établissements est COMPLETED dès sa création ; un seul appel à GET suffit donc.

3. Lire les résultats

Une analyse terminée contient un summary et une entrée dans results pour chaque annuaire vérifié, jusqu’à environ 60 selon le pays. business renvoie les données que vous avez envoyées, avec phone normalisé au format international.

JSON
{  "total": 62,  "found": 21,  "accurate": 18,  "inaccurate": 2,  "notFound": 41,  "pending": 0,  "unavailable": 0}

total est le nombre d’annuaires. found indique combien répertorient l’entreprise et se répartit entre accurate, inaccurate et les éventuelles fiches qui confirment uniquement sa présence, décrites ci-dessous. notFound indique combien ne la répertorient pas. unavailable compte les annuaires qui n’ont pas pu être vérifiés cette fois-ci, et pending n’est différent de zéro que pendant l’analyse.

Chaque résultat comporte un status pour l’annuaire :

status du résultatSignification
FOUNDL’annuaire répertorie l’entreprise. listing contient les informations qu’il affiche.
NOT_FOUNDL’annuaire a été vérifié et l’entreprise n’y figure pas.
UNAVAILABLEL’annuaire n’a pas pu être vérifié cette fois-ci. Cela ne signifie pas que l’entreprise n’y figure pas ; ne le signalez donc pas comme une absence de fiche. Vérifiez à nouveau cet annuaire plus tard.
PENDINGPas encore vérifié. Cet état n’apparaît que pendant l’analyse.

Pour un résultat FOUND, accurate indique si la fiche correspond aux données que vous avez fournies :

  • true signifie que le nom, l’adresse et le numéro de téléphone correspondent tous.
  • false signifie qu’au moins l’un d’eux ne correspond pas. Un champ absent de la fiche est considéré comme une non-correspondance ; une fiche sans numéro de téléphone est donc false.
  • null signifie que l’entreprise a été trouvée, mais qu’aucune vérification champ par champ n’a été effectuée.

accuracy explique le résultat de la comparaison avec un status et un message pour chacun des champs name, address et phone. status vaut ACCURATE, INACCURATE ou MISSING, et message vaut null lorsque le champ est exact. Voici une page Facebook dont le nom et l’adresse correspondent, mais qui ne comporte aucun numéro de téléphone :

JSON
{  "directory": {    "id": 184,    "name": "Facebook",    "url": "facebook.com"  },  "status": "FOUND",  "accurate": false,  "accuracySource": "DIRECTORY_MATCH",  "accuracy": {    "name": {      "status": "ACCURATE",      "message": null    },    "address": {      "status": "ACCURATE",      "message": null    },    "phone": {      "status": "MISSING",      "message": "No phone number on the listing"    }  },  "listing": {    "name": "Joe's Pizza",    "street": "7 Carmine St",    "city": "New York",    "state": "NY",    "postalCode": "10014",    "country": "US",    "fullAddress": "7 Carmine St, New York, NY 10014",    "phones": [],    "url": "https://www.facebook.com/177353602413952"  },  "syncStatus": null}

Certains annuaires confirment uniquement que l’entreprise existe et n’affichent pas assez de détails pour permettre une comparaison. Leurs résultats sont FOUND avec accurate: null et sont comptabilisés uniquement dans found, pas dans accurate ni dans inaccurate. Considérez ces fiches comme présentes, pas comme vérifiées :

JSON
{  "directory": {    "id": 374,    "name": "Map Quest",    "url": "www.mapquest.com"  },  "status": "FOUND",  "accurate": null,  "accuracySource": "DIRECTORY_MATCH",  "accuracy": {    "name": {      "status": "ACCURATE",      "message": null    },    "address": {      "status": "MISSING",      "message": "Not shown"    },    "phone": {      "status": "MISSING",      "message": "Not shown"    }  },  "listing": {    "name": "Joe's Pizza",    "street": "7 Carmine St",    "city": "New York",    "state": "NY",    "postalCode": "10014",    "country": null,    "fullAddress": "7 Carmine St, New York, NY 10014, US",    "phones": [],    "url": null  },  "syncStatus": null}

Un résultat NOT_FOUND ne comporte ni listing ni accuracy :

JSON
{  "directory": {    "id": 352,    "name": "YellowPages",    "url": "www.yellowpages.com"  },  "status": "NOT_FOUND",  "accurate": null,  "accuracySource": "DIRECTORY_MATCH",  "accuracy": null,  "listing": null,  "syncStatus": null}

Plusieurs annuaires ne disposent pas de leurs propres données. Uber, Waze et Lyft affichent les données de Google Maps, et Copilot affiche celles de Bing. Leurs résultats reflètent ceux de l’annuaire source ; ils reprennent donc généralement ce que Google Maps ou Bing a renvoyé et ne comportent pas de url de fiche. Comptez-les comme des entrées distinctes dans summary, mais ne les considérez pas comme une confirmation indépendante :

JSON
{  "directory": {    "id": 350,    "name": "Uber",    "url": "uber.com"  },  "status": "FOUND",  "accurate": true,  "accuracySource": "DIRECTORY_MATCH",  "accuracy": {    "name": {      "status": "ACCURATE",      "message": null    },    "address": {      "status": "ACCURATE",      "message": null    },    "phone": {      "status": "ACCURATE",      "message": null    }  },  "listing": {    "name": "Joe's Pizza",    "street": "7 Carmine Street",    "city": "New York",    "state": "NY",    "postalCode": "10014",    "country": "US",    "fullAddress": "7 Carmine St, New York, NY 10014, USA",    "phones": [      "+1 212-366-1182"    ],    "url": null  },  "syncStatus": null}

Les assistants IA tels que ChatGPT et Gemini sont également inclus. Pour eux, listing affiche ce que l’assistant sait de l’entreprise, souvent uniquement un nom, une adresse complète et un numéro de téléphone, et il n’y a pas de accuracy par champ.

Pour transformer une analyse terminée en liste de corrections, affichez chaque annuaire comportant un champ incorrect ou manquant :

Directories that need attention
curl -s https://listingsapi.com/api/v4/listings/scans/TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz \-H "Authorization: API $LISTINGSAPI_KEY" \| jq -r '.data.listingScan.results[]         | select(.accurate == false)         | .directory.name as $directory         | .accuracy         | to_entries[]         | select(.value.status != "ACCURATE")         | $directory + " " + .key + ": " + (.value.message // .value.status)'

Pour l’analyse de Joe's Pizza, cela affiche deux lignes : Facebook ne comporte aucun numéro de téléphone, et Judys Book en affiche un différent.

Text
Facebook phone: No phone number on the listingJudys Book phone: Phone number does not match

Origine des résultats

source indique comment les résultats de l'analyse ont été obtenus, et resultsAsOf indique quand les données ont été collectées.

sourceSignification
DIRECTORY_SEARCHUne nouvelle recherche dans les annuaires. resultsAsOf indique quand cette analyse s'est terminée.
RECENT_SCANUne copie d'une analyse de la même entreprise effectuée par n'importe quel utilisateur au cours des 7 derniers jours. L'opération se termine immédiatement, et resultsAsOf indique quand les données d'origine ont été collectées ; cette date peut donc être antérieure à createdAt.
LOCATION_LISTINGSL'entreprise correspond à l'un de vos établissements d'après le numéro de téléphone, le code postal et le pays ; les résultats proviennent donc des fiches réelles de cet établissement. locationId est renseigné, accuracySource vaut SYNC_STATUS au lieu de DIRECTORY_MATCH, et chaque résultat inclut le champ syncStatus de la fiche correspondante.

Coûts, limites et erreurs

Seule une nouvelle analyse DIRECTORY_SEARCH est décomptée de votre quota mensuel, qui dépend de votre forfait et est réinitialisé à chaque période de facturation. Les opérations suivantes sont gratuites :

  • relancer une analyse de la même entreprise dans un délai de 7 jours. Votre propre analyse précédente est renvoyée avec reused: true ;
  • une analyse dont les résultats peuvent être fournis par une analyse récente de la même entreprise, effectuée depuis n'importe quel compte. Il s'agit d'une copie RECENT_SCAN ;
  • les analyses qui correspondent à l'un de vos établissements (LOCATION_LISTINGS) ;
  • les analyses qui se terminent avec l'état FAILED, qui sont remboursées.

Les analyses sont conservées pendant 90 jours. Passé ce délai, GET renvoie 404 avec SY83005.

Les réponses des opérations de création et de consultation de la liste incluent un objet quota, ce qui vous permet de connaître votre consommation avant de lancer un lot. L'opération de consultation de la liste renvoie vos analyses de la plus récente à la plus ancienne, par pages de 1 à 50 résultats avec perPage, sans results.

Check your allowance
curl -s "https://listingsapi.com/api/v4/listings/scans?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY" \| jq '.data.listingScans.quota'
Champ quotaSignification
limitNombre de nouvelles analyses autorisées par votre forfait pour la période en cours.
usedNombre de nouvelles analyses utilisées jusqu'à présent.
remaininglimit moins used, sans jamais descendre en dessous de 0.
resetsAtDate de réinitialisation du quota.

Voici les erreurs que vous rencontrerez le plus souvent. L'opération de création renvoie ses erreurs dans le corps de la réponse avec le statut HTTP 200 ; les opérations de récupération et de consultation de la liste renvoient le statut HTTP habituel.

CodeContexteSignification et correction
SY83002CréationLe quota mensuel est épuisé. Le message inclut la limite et la date de réinitialisation, et quota est renvoyé. Attendez resetsAt ou réutilisez une analyse des 7 derniers jours, ce qui est gratuit.
SY83003Création, ou champ failure d'une analyseUne valeur est manquante ou invalide. contextInfo indique le nom du champ. Corrigez les données d'entrée et envoyez-les à nouveau.
SY83004CréationLes Listings scans ne sont pas disponibles pour ce countryIso.
SY83001CréationVotre forfait n'inclut pas les listings scans.
SY83005RécupérationAucune analyse ne possède cet id, ou l'analyse appartient à un autre compte ou date de plus de 90 jours. HTTP 404.
SY83010Création, ou champ failure d'une analyseLes analyses sont temporairement indisponibles. Réessayez plus tard.
SY90016CréationLa clé est en lecture seule. Utilisez une clé Write.

Une requête GET pour un id inexistant renvoie :

JSON
{  "data": {    "listingScan": null  },  "errors": [    {      "message": "SY83005: Listing scan not found"    }  ]}

Lorsqu'une analyse se termine avec l'état FAILED, la raison est indiquée dans failure, et results reste vide :

JSON
{  "status": "FAILED",  "failure": {    "code": "SY83010",    "message": "SY83010: Listing scans are temporarily unavailable, try again later",    "field": null  }}

Le format général des erreurs est décrit dans Codes d'erreur.

Pour les agents IA

Le serveur MCP hébergé expose cette fonctionnalité au moyen de 3 outils : listings_scans_create, listings_scans_get et listings_scans_list. Un assistant peut lancer une analyse pour une entreprise désignée par l'utilisateur, en consulter régulièrement l'état avec listings_scans_get et résumer les annuaires qui nécessitent une intervention. Comme avec REST, la création d'une analyse nécessite une clé Write. La configuration est décrite dans Connecter des agents IA au serveur MCP de listingsAPI.

Étapes suivantes