تدقيق إدراجات نشاط تجاري باستخدام listings scan

افحص ظهور أي نشاط تجاري في الخرائط ومحركات البحث والأدلة ومساعدي الذكاء الاصطناعي، ودقة اسمه وعنوانه وهاتفه لكل دليل، وتلقّ النتائج عبر webhook أو polling.

تتحقق عملية listings scan من كيفية إدراج نشاط تجاري واحد عبر شبكة الأدلة listingsAPI، وتوضح لك، لكل دليل على حدة، ما إذا كان النشاط التجاري موجودًا فيه وما إذا كان اسمه وعنوانه ورقم هاتفه تطابق البيانات التي قدمتها. لا يلزم أن يكون النشاط التجاري موقعًا في حسابك. يوضح هذا الدليل كيفية إنشاء فحص، وانتظار اكتماله، وقراءة النتائج.

استخدمه من أجل:

  • تدقيق إدراجات نشاط تجاري لعميل محتمل قبل ضمه، لتبدأ المحادثة الأولى بالإدراجات المفقودة أو غير الصحيحة؛
  • التحقق من وجود نشاط العميل التجاري واتساق بيانات NAP (الاسم والعنوان ورقم الهاتف) قبل إصلاح إدراجات نشاطه التجاري وبعده؛
  • تمكين وكيل ذكاء اصطناعي من الإجابة عن السؤال «كيف يُدرج هذا النشاط التجاري؟» دون إضافة النشاط التجاري إلى الحساب.

1. إنشاء الفحص

يستقبل POST /listings/scans بيانات النشاط التجاري في كائن input واحد، ويُعيد الفحص فورًا بالحالة QUEUED. يجري التحقق من الأدلة في الخلفية.

الحقلمطلوبملاحظات
nameنعمحتى 150 حرفًا.
streetنعمحتى 200 حرف.
cityنعمحتى 100 حرف.
stateIsoنعم، باستثناء GBرمز الولاية أو المقاطعة، حتى 50 حرفًا.
postalCodeنعم، باستثناء NG وAEحتى 20 حرفًا.
countryIsoنعمرمز ISO 3166-1 المكوّن من حرفين، مثل US.
phoneنعممن 6 إلى 15 رقمًا. تُفسَّر الصيغ المحلية في سياق countryIso، ويُقبل أيضًا وضع + في البداية.
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

تُعيد الاستجابة الفحص، مع توحيد صيغة phone إلى الصيغة الدولية. يظل results فارغًا إلى أن يجري التحقق من الأدلة، لذا احتفظ بـ 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": []    }  }}

تتضمن الاستجابة أيضًا كائن quota، الموضح في التكاليف والحدود والأخطاء.

تحقق من أمرين قبل المتابعة:

  • reused. عندما تطلب فحص نشاط تجاري فحصته خلال آخر 7 أيام، يُعيد listingsAPI ذلك الفحص بدلًا من بدء فحص جديد، مع reused: true وحالته الحالية، التي تكون عادةً COMPLETED. لا يُخصم شيء من حصتك، ولا يُرسل webhook جديد لهذا الفحص.
  • errors. يُعاد رمز HTTP 200 حتى عند رفض الطلب. يرد الرفض في data.createListingScan.errors، وتكون قيمة scan هي null. يبدو الرد على رقم هاتف أقصر من الحد المطلوب كما يلي:
JSON
{  "data": {    "createListingScan": {      "reused": false,      "scan": null,      "errors": [        {          "code": "SY83003",          "message": "SY83003: Invalid value for phone",          "contextInfo": [            {              "key": "field",              "value": "phone"            }          ]        }      ]    }  }}

يحدد contextInfo اسم الحقل الذي فشل التحقق منه. ترد الرموز الأخرى التي قد تتلقاها في جدول الأخطاء.

2. انتظار النتائج

ينتقل الفحص من QUEUED إلى IN_PROGRESS ثم إلى COMPLETED أو FAILED. تكتمل معظم الفحوص خلال 1 إلى 7 دقائق. إذا لم يستجب دليل بعد نحو 15 دقيقة، تُضبط نتيجته على UNAVAILABLE ويكتمل الفحص بالنتائج الأخرى. استخدم webhook إن أمكن، واجعل الاستعلام الدوري خيارًا احتياطيًا فقط.

عندما يصل الفحص إلى حالة نهائية، يرسل listingsAPI حدثًا واحدًا من نوع listing_scan.completed أو listing_scan.failed إلى عنوان URL الخاص بـ webhook لديك. يحتوي data.scan فيه على الكائن نفسه الذي يُعيده GET، بما في ذلك النتائج، لذا لا يلزم جلب أي شيء بعد ذلك.

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": [ "..." ]    }  }}

محتوى الطلب مختصر هنا؛ والمثال الكامل متاح في مرجع الأحداث. تُوقَّع الأحداث وتُسلَّم مثل سائر الأحداث، بمحاولة واحدة ومهلة 5 ثوانٍ، لذا تحقق من التوقيع، وخزّن جسم الطلب الخام، وأكّد الاستلام قبل القيام بأي شيء آخر. ينفذ المستقبِل الموضح في التعامل مع الأحداث باستخدام webhooks هذه الخطوات بالفعل؛ والتغيير الوحيد هو إضافة فرع آخر بناءً على 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

لا يُرسل الحدث إلا إلى الحسابات التي فُعّلت فيها webhooks ولديها عنوان URL تم التحقق منه. إذا لم يستوفِ حسابك هذين الشرطين، أو إذا لم يصل حدث، فاستعلم عن الفحص دوريًا؛ إذ يُحتفظ بالفحص لمدة 90 يومًا في كلتا الحالتين. ترد تفاصيل التوقيع في التوقيعات والتحقق.

2b. الاستعلام الدوري كخيار بديل

يعيد GET /listings/scans/{scanId} الفحص مع أي نتائج متاحة حتى الآن. أثناء تشغيل الفحص، تُضاف البيانات إلى results مع ورود إجابات الأدلة، ويحسب summary.pending عدد الأدلة التي لا تزال إجاباتها قيد الانتظار. استعلم كل 15 ثانية، ولا تستعلم بوتيرة أسرع؛ يُحتسب كل طلب ضمن حد معدل الطلبات.

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
statusالمعنى
QUEUEDتم القبول، وفي انتظار البدء.
IN_PROGRESSجارٍ التحقق من الأدلة. بيانات results جزئية.
COMPLETEDتوفرت إجابة لكل دليل. بيانات results وsummary نهائية.
FAILEDتعذّر بدء الفحص، مثلاً بسبب عدم توفر الخدمة مؤقتًا (SY83010). يحتوي failure على code وmessage، ويكون results فارغًا. لا يُحتسب الفحص الفاشل ضمن حصتك، لذا يمكنك إنشاؤه مجددًا.

يكون الفحص الذي تُستمد نتائجه من فحص حديث أو من أحد مواقعك بالحالة COMPLETED لحظة إنشائه، لذا يكفي إجراء GET مرة واحدة.

3. قراءة النتائج

يحتوي الفحص المكتمل على summary وسجل واحد في results لكل دليل تم التحقق منه، بحد أقصى يقارب 60 دليلاً حسب البلد. يعيد business المدخلات التي أرسلتها، مع توحيد تنسيق phone بالصيغة الدولية.

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

يمثل total عدد الأدلة. ويمثل found عدد الأدلة التي تدرج النشاط التجاري، وينقسم إلى accurate وinaccurate وأي إدراجات أنشطة تجارية تؤكد الوجود فقط كما هو موضح أدناه. ويمثل notFound عدد الأدلة التي لا تدرجه. يحسب unavailable عدد الأدلة التي تعذّر التحقق منها هذه المرة، ولا تكون قيمة pending غير صفرية إلا أثناء تشغيل الفحص.

تحتوي كل نتيجة على status للدليل:

status النتيجةالمعنى
FOUNDيُدرج الدليل النشاط التجاري. يحتوي listing على البيانات التي يعرضها.
NOT_FOUNDتم التحقق من الدليل، والنشاط التجاري غير موجود فيه.
UNAVAILABLEتعذّر التحقق من الدليل هذه المرة. هذا لا يعني أن النشاط التجاري غير مُدرج، لذا لا تُبلغ عنه باعتباره إدراج نشاط تجاري مفقودًا. تحقّق من ذلك الدليل مجددًا لاحقًا.
PENDINGلم يتم التحقق بعد. لا تظهر هذه الحالة إلا أثناء تشغيل الفحص.

بالنسبة إلى نتيجة FOUND، يوضح accurate ما إذا كان إدراج النشاط التجاري يطابق مدخلاتك:

  • تعني true أن الاسم والعنوان ورقم الهاتف كلها متطابقة.
  • تعني false أن واحدًا منها على الأقل غير مطابق. يُحتسب الحقل غير الوارد في إدراج النشاط التجاري على أنه غير مطابق، لذا يكون إدراج النشاط التجاري الذي لا يتضمن رقم هاتف بالحالة false.
  • تعني null أنه تم العثور على النشاط التجاري، لكن لم يتم التحقق من كل حقل على حدة.

يوضح accuracy سبب النتيجة من خلال status وmessage لكل من name وaddress وphone. تكون قيمة status هي ACCURATE أو INACCURATE أو MISSING، وتكون قيمة message هي null عندما يكون الحقل صحيحًا. إليك صفحة Facebook يتطابق فيها الاسم والعنوان، لكنها لا تتضمن رقم هاتف:

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}

تؤكد بعض الأدلة وجود النشاط التجاري فقط، ولا تعرض تفاصيل كافية للمقارنة. تُعاد نتائج هذه الأدلة بالحالة FOUND مع accurate: null، وتُحتسب ضمن found فقط، وليس ضمن accurate أو inaccurate. تعامل معها على أنها موجودة، وليس على أنها متحقَّق من صحتها:

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}

لا تحتوي نتيجة NOT_FOUND على listing ولا على 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}

لا تحتفظ عدة أدلة ببيانات خاصة بها. تعرض Uber وWaze وLyft بيانات Google Maps، ويعرض Copilot بيانات Bing. تعكس نتائجها نتائج الدليل المصدر، لذا فهي تكرر عادةً ما أعاده Google Maps أو Bing، ولا تتضمن url لإدراج النشاط التجاري. احتسبها كسجلات منفصلة في summary، لكن لا تعتبرها تأكيدًا مستقلاً:

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}

يشمل الفحص أيضًا مساعدي الذكاء الاصطناعي مثل ChatGPT وGemini. بالنسبة إليهم، يعرض listing ما يعرفه المساعد عن النشاط التجاري، وهو غالبًا مجرد اسم وعنوان كامل ورقم هاتف، ولا يوجد accuracy لكل حقل على حدة.

لتحويل فحص مكتمل إلى قائمة تصحيحات، اطبع كل دليل يحتوي على حقل خاطئ أو مفقود:

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)'

بالنسبة إلى فحص Joe's Pizza، يطبع هذا سطرين: لا يتضمن Facebook رقم هاتف، ويعرض Judys Book رقمًا مختلفًا.

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

مصادر النتائج

يوضح source كيفية الحصول على نتائج الفحص، ويشير resultsAsOf إلى وقت جمع البيانات.

sourceالمعنى
DIRECTORY_SEARCHبحث جديد في الأدلة. يشير resultsAsOf إلى وقت اكتمال هذا الفحص.
RECENT_SCANنسخة من فحص لنشاط تجاري مطابق أجراه أي شخص خلال الأيام السبعة الماضية. يكتمل الفحص فورًا، ويشير resultsAsOf إلى وقت جمع البيانات الأصلية، لذا قد يكون أقدم من createdAt.
LOCATION_LISTINGSتطابق النشاط التجاري مع أحد مواقعك من حيث رقم الهاتف والرمز البريدي والبلد، لذا تأتي النتائج من إدراجات النشاط التجاري الفعلية لذلك الموقع. تُعيَّن قيمة locationId، وتكون قيمة accuracySource هي SYNC_STATUS بدلًا من DIRECTORY_MATCH، وتتضمن كل نتيجة syncStatus الخاص بإدراج النشاط التجاري المعني.

التكاليف والحدود والأخطاء

لا يُحتسب من حصتك الشهرية إلا فحص DIRECTORY_SEARCH جديد، وتعتمد هذه الحصة على خطتك وتُعاد تهيئتها مع دورة الفوترة. الحالات التالية مجانية:

  • تكرار فحص النشاط التجاري نفسه خلال سبعة أيام. يُعاد فحصك السابق مع reused: true؛
  • فحص يمكن الحصول على نتائجه من فحص حديث للنشاط التجاري نفسه أجراه أي حساب. يكون نسخة من نوع RECENT_SCAN؛
  • الفحوصات التي تطابق أحد مواقعك (LOCATION_LISTINGS)؛
  • الفحوصات التي تنتهي بحالة FAILED، وتُعاد الحصة المحتسبة لها.

يُحتفظ بالفحوصات لمدة 90 يومًا. بعد ذلك، يُعيد GET القيمة 404 مع SY83005.

تتضمن استجابات الإنشاء والسرد كائن quota، لتتمكن من معرفة مقدار الحصة المتاح قبل بدء دفعة من الفحوصات. تُعيد عملية السرد فحوصاتك بدءًا من الأحدث، في صفحات تضم من 1 إلى 50 فحصًا مع perPage، دون 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'
حقل quotaالمعنى
limitعدد الفحوصات الجديدة التي تسمح بها خطتك في الفترة الحالية.
usedعدد الفحوصات الجديدة المستخدمة حتى الآن.
remaininglimit ناقص used، ولا تقل النتيجة عن الصفر.
resetsAtموعد إعادة تهيئة الحصة.

هذه هي الأخطاء التي ستصادفها غالبًا. تُعيد عملية الإنشاء أخطاءها في متن الاستجابة مع حالة HTTP 200؛ وتُعيد عمليتا الجلب والسرد حالة HTTP المعتادة.

الرمزموضع حدوثهالمعنى وطريقة الإصلاح
SY83002الإنشاءنُفدت الحصة الشهرية. تتضمن الرسالة الحد وموعد إعادة التهيئة، ويُعاد quota. انتظر حتى resetsAt، أو أعد استخدام فحص من الأيام السبعة الماضية دون أي تكلفة.
SY83003الإنشاء، أو failure الخاص بفحصإحدى القيم مفقودة أو غير صالحة. يحدد contextInfo اسم الحقل. صحح المدخلات وأرسلها مجددًا.
SY83004الإنشاءلا تتوفر Listings scans لقيمة countryIso تلك.
SY83001الإنشاءلا تتضمن خطتك listings scans.
SY83005الجلبلا يوجد فحص بهذا المعرّف، أو أن الفحص يخص حسابًا آخر أو مضى عليه أكثر من 90 يومًا. حالة HTTP 404.
SY83010الإنشاء، أو failure الخاص بفحصالفحوصات غير متاحة مؤقتًا. حاول مجددًا لاحقًا.
SY90016الإنشاءالمفتاح مخصص للقراءة فقط. استخدم مفتاح Write.

يُعيد طلب GET لمعرّف غير موجود ما يلي:

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

يحتوي failure على سبب انتهاء الفحص بحالة FAILED، ويظل results فارغًا:

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

ترد الصيغة العامة للأخطاء في رموز الأخطاء.

لوكلاء الذكاء الاصطناعي

يتيح خادم MCP المستضاف هذه الميزة عبر ثلاث أدوات: listings_scans_create وlistings_scans_get وlistings_scans_list. يمكن للمساعد بدء فحص لنشاط تجاري يحدده المستخدم، والاستعلام دوريًا عن الفحص باستخدام listings_scans_get، وتلخيص الأدلة التي تحتاج إلى متابعة. كما في REST، يتطلب إنشاء فحص مفتاح Write. ترد تعليمات الإعداد في ربط وكلاء الذكاء الاصطناعي بخادم MCP الخاص بـ listingsAPI.

الخطوات التالية