المواقع

جميع نقاط نهاية المواقع بأمثلة cURL وPython وNode حسب ترتيب الاستخدام: السرد والبحث والجلب بالمعرّف ورمز المتجر والإنشاء والتحديث والأرشفة والصور.

ترتبط إدراجات الأنشطة التجارية والتقييمات والمنشورات والتحليلات جميعها بمعرّف موقع. يضم مورد Locations ثلاث عشرة نقطة نهاية: أربع للقراءة تعثر على المواقع، وأربع للكتابة تنشئها وتغيّرها وتؤرشفها، وخمس لإدارة الصور. يتيح مفتاح Read (قراءة) عمليات القراءة. يتطلب كل POST مفتاح Write (كتابة)؛ ويؤدي استخدام مفتاح Read في عملية كتابة إلى إرجاع HTTP 400 مع SY90016.

كل id في هذا الدليل هو معرّف Relay بترميز base64، مثل TG9jYXRpb246MTgwMDI4OQ== (يُفك ترميزه إلى Location:1800289). تقبل حزمتا SDK أيضًا القيمة الرقمية databaseId وتتوليان ترميزها لك.

نظرة سريعة على نقاط النهاية

الطريقةالمسارالوظيفةالمرجع
GET/locationsاستعراض جميع المواقع عبر صفحات، بدءًا بالأحدث.سرد جميع المواقع
GET/locations/searchالبحث عن المواقع بكلمة مفتاحية في الاسم والعنوان ومعرّف المتجر.البحث عن المواقع
GET/locations-by-idsجلب السجلات الكاملة لدفعة من المعرّفات بترميز base64.جلب المواقع باستخدام المعرّفات
GET/locations-by-store-codesجلب السجلات الكاملة باستخدام رموز متاجرك.جلب المواقع باستخدام رموز المتاجر
POST/locationsإنشاء موقع وإضافته إلى قائمة انتظار الإرسال.إنشاء موقع
POST/locations/updateتغيير أي حقل قابل للكتابة في موقع واحد.تحديث موقع
POST/locations/archiveجدولة أرشفة المواقع في نهاية دورة الفوترة.أرشفة المواقع
POST/locations/cancel_archiveإعادة المواقع المجدولة للأرشفة إلى الحالة النشطة.إلغاء الأرشفة المجدولة
POST/locations/photosإرفاق شعار أو صورة غلاف أو صور معرض باستخدام URL.رفع الصور
GET/locations/photos/requests/{requestId}الاستعلام دوريًا عن حالة عملية رفع جماعي في قائمة الانتظار.التحقق من حالة الرفع الجماعي
GET/locations/{locationId}/photosسرد جميع ملفات الوسائط المرتبطة بموقع.جلب الصور
POST/locations/photos/starتمييز الصور الإضافية بنجمة أو إزالة النجمة عنها.تمييز الصور بنجمة
POST/locations/photos/removeإلغاء إرفاق الصور الإضافية.إزالة الصور

سرد جميع المواقع

يستعرض GET /locations جميع المواقع في الحساب عبر صفحات، بدءًا بالأحدث. وهو اتصال يعتمد على مؤشرات التصفح: يحتوي edges[].node على كل سجل، ويوفّر pageInfo.endCursor قيمة المعامل after للصفحة التالية. استخدمه لمزامنة الحساب بأكمله مع نظامك.

المعاملات

الاسمالموضعمطلوبملاحظات
firstqueryلاحجم الصفحة عند التقدم إلى الأمام. القيمة الافتراضية 50. استخدمه مع after.
afterqueryلاقيمة pageInfo.endCursor للصفحة السابقة.
lastqueryلاحجم الصفحة عند الرجوع إلى الخلف. استخدمه مع before.
beforequeryلاقيمة cursor لأول عنصر edge في الصفحة التي ترجع منها إلى الخلف. لا يحتوي pageInfo على startCursor.
filterqueryلاكائن بترميز JSON لتضييق نطاق النتائج على جانب الخادم، مثل التصفية حسب حالة الموافقة. لا تقبله دالة السرد في أي من حزمتَي SDK؛ استخدم REST لجلب مجموعة فرعية مُصفّاة.
List
curl "https://listingsapi.com/api/v4/locations?first=50" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "allLocations": {      "edges": [        {          "cursor": "TG9jYXRpb246MTgwMDI4OQ==",          "node": {            "id": "TG9jYXRpb246MTgwMDI4OQ==",            "databaseId": 1800289,            "name": "Jenny Home",            "storeId": null,            "approved": "APPROVED",            "archived": false,            "archivalScheduledAt": null,            "city": "Austin",            "stateIso": "TX",            "countryIso": "US",            "subCategoryId": 1432,            "planName": "Launch",            "placeActionLinks": [],            "discoveredPlaceActionLinks": []          }        }      ],      "pageInfo": {        "hasNextPage": false,        "hasPreviousPage": false,        "endCursor": "TG9jYXRpb246MTgwMDI4OQ==",        "total": 1      }    }  }}

اقرأ pageInfo.hasNextPage وتوقف عندما تكون قيمته false. العدد على مستوى الصفحة هو pageInfo.total. قيمة id في العقدة هي المعرّف الذي تتوقعه جميع نقاط النهاية الأخرى.

البحث عن المواقع

يطابق GET /locations/search كلمة مفتاحية مع اسم الموقع وعنوان الشارع والمدينة ومعرّف المتجر. وهو اتصال يعتمد على مؤشرات التصفح، وله بنية edges[].node وpageInfo.endCursor نفسها المستخدمة في السرد. استخدمه لعنصر اختيار يعرض الاقتراحات أثناء الكتابة، أو للتأكد من وجود متجر قبل إنشائه.

المعاملات

الاسمالموضعمطلوبملاحظات
queryqueryلاالكلمة المفتاحية للبحث. احذفها لاستعراض جميع المواقع عبر صفحات دون تصفية.
firstqueryلاحجم الصفحة. القيمة الافتراضية 50.
afterqueryلاقيمة pageInfo.endCursor للصفحة السابقة.
filterqueryلاكائن تصفية بترميز JSON، كما في نقطة نهاية السرد.

تقبل حزمتا SDK أيضًا قائمة fields لقصر المطابقة على حقول محددة، مثل name فقط.

Search
curl "https://listingsapi.com/api/v4/locations/search?query=Jenny&first=10" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "searchLocations": {      "edges": [        {          "cursor": "TG9jYXRpb246MTgwMDI4OQ==",          "node": {            "id": "TG9jYXRpb246MTgwMDI4OQ==",            "databaseId": 1800289,            "name": "Jenny Home",            "storeId": null,            "street": "600 Congress Ave, Suite 200",            "city": "Austin",            "approved": "APPROVED",            "archived": false          }        }      ],      "pageInfo": {        "endCursor": "TG9jYXRpb246MTgwMDI4OQ==",        "hasNextPage": false,        "hasPreviousPage": false,        "total": 1      }    }  }}

تعني مصفوفة edges الفارغة عدم وجود أي تطابق. تحتوي العقدة على العنوان الكامل والفئة وحالة approved، لذلك لا يحتاج صف النتيجة إلى عملية جلب ثانية.

جلب المواقع حسب المعرّفات

يُرجع GET /locations-by-ids السجل الكامل لكل معرّف في دفعة من معرّفات base64 باستدعاء واحد، مما يقلل عدد الطلبات مقارنةً باستدعاء لكل موقع. ويمكنه العثور على المواقع النشطة والمؤرشفة على حد سواء. ويُرجع مصفوفة عادية، وليس اتصالًا مقسّمًا إلى صفحات. استخدمه لجلب السجلات الكاملة للمعرّفات الواردة في حمولة webhook أو المخزّنة لديك.

المعاملات

الاسمالموضعمطلوبملاحظات
idsqueryنعممصفوفة من معرّفات base64 مرمّزة بصيغة JSON. طبّق ترميز URL على القيمة بأكملها؛ يتولى curl -G --data-urlencode ذلك نيابةً عنك.
By ids
curl -G https://listingsapi.com/api/v4/locations-by-ids \--data-urlencode 'ids=["TG9jYXRpb246MTgwMDI4OQ==","TG9jYXRpb246MTgwMDI5MA=="]' \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationsByIds": [      {        "id": "TG9jYXRpb246MTgwMDI4OQ==",        "databaseId": 1800289,        "storeId": null,        "name": "Jenny Home",        "archived": false,        "archivedAt": null,        "city": "Austin",        "stateIso": "TX",        "countryIso": "US",        "subCategoryId": 1432,        "businessHours": [],        "locationPhotos": [],        "placeActionLinks": [],        "discoveredPlaceActionLinks": []      }    ]  }}

تُستبعد المعرّفات غير الموجودة في حسابك بصمت دون إصدار خطأ. قارن عدد السجلات المُرجعة بعدد المعرّفات التي أرسلتها لاكتشاف المعرّفات التي لم تعد صالحة.

جلب المواقع حسب رموز المتاجر

يُجري GET /locations-by-store-codes البحث العكسي: إذ يستخدم رموز متاجرك للوصول إلى السجلات الكاملة. ويُرجع مصفوفة عادية. استخدمه عندما يعتمد نظام نقاط البيع أو سجل أصحاب الامتياز لديك على رمز المتجر لتعريف المواقع، ولم يسبق لك تخزين معرّفاتنا.

المعاملات

الاسمالموضعمطلوبملاحظات
storeCodesqueryنعممصفوفة من السلاسل النصية لرموز المتاجر مرمّزة بصيغة JSON. طبّق ترميز URL على القيمة بأكملها.
By store codes
curl -G https://listingsapi.com/api/v4/locations-by-store-codes \--data-urlencode 'storeCodes=["ACME01","ACME02"]' \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationsByStoreCodes": [      {        "id": "TG9jYXRpb246MTgwMDI4OQ==",        "databaseId": 1800289,        "storeId": "ACME01",        "name": "Jenny Home",        "archived": false,        "city": "Austin",        "countryIso": "US",        "planName": "Launch"      }    ]  }}

طابق قيمة storeId في كل كائن مع سجلّك. لا تظهر الرموز التي ليس لها تطابق. لا يكون للموقع رمز متجر إلا إذا ضُبط storeId عند الإنشاء أو التحديث.

إنشاء موقع

يُنشئ POST /locations موقعًا ويضعه في قائمة الانتظار لإرساله إلى كل منصة نشر ضمن خطتك، أو إلى enabledSiteIds فقط عند تمرير ذلك الحقل. تُدرج جميع الحقول ضمن input. هناك 10 حقول إلزامية؛ أما بقية الحقول فهي اختيارية، وتعرض منصات النشر أي حقل تغفله فارغًا.

المعاملات

الاسمالموضعمطلوبملاحظات
input.namebodyنعماسم النشاط التجاري، من 2 إلى 150 حرفًا.
input.descriptionbodyنعم200 حرف على الأقل (SY10005). تستخدمه منصات النشر كنص لإدراج النشاط التجاري.
input.countryIsobodyنعمرمز بلد مدعوم (SY10036). من البلدان.
input.citybodyنعممطلوب عندما تكون قيمة hasCity للبلد هي true (SY10126). وعندما تكون القيمة false، تُستبعد المدينة التي ترسلها.
input.subCategoryIdbodyنعمقيمة databaseId رقمية من الفئات الفرعية.
input.streetbodyنعممن 5 إلى 100 حرف (SY10011).
input.stateIsobodyنعممطلوب عندما تكون قيمة stateRequired للبلد هي true، ويجب أن يكون إحدى قيم states لذلك البلد (SY10010). أغفله عندما تكون قيمة stateRequired هي false.
input.phonebodyنعميُتحقّق من صحته وفقًا للبلد (SY10015).
input.postalCodebodyلااختياري، لكن أرسله كلما كانت قيمة hasPostalCode للبلد هي true. يجب أن تتطابق القيمة التي ترسلها مع التنسيق المعتمد للبلد (SY10014).
input.bizUrlbodyنعميُنشر بوصفه الموقع الإلكتروني. ترفض بعض منصات النشر إدراج النشاط التجاري دون موقع إلكتروني.
input.storeIdbodyلارمز متجرك الخاص، ويجب أن يكون فريدًا على مستوى الحساب.
input.businessHoursbodyلا7 عناصر من { day, type, slots }. تكون قيمة type إما OPEN أو CLOSED؛ ويكون slots فارغًا في يوم الإغلاق.
input.placeActionLinksbodyلاروابط الإجراءات في Google: placeActionType وuri وisPreferred الاختياري. لا حاجة إلى action عند الإنشاء. راجع روابط إجراءات الموقع.
input.enabledSiteIdsbodyلاالنشر على مجموعة فرعية من مواقع خطتك. المعرّفات من مواقع الخطة.
input.clientMutationIdbodyلاتُرجع القيمة دون تغيير لربط الطلب بالاستجابة. لا تمنع عمليات الكتابة المكررة.

بالإضافة إلى الحقول الاختيارية الواردة في الصفحة المرجعية: فئات Google GCID، وعناوين URL لوسائل التواصل الاجتماعي، والإحداثيات، وبيانات الاتصال بالمالك، وطرق الدفع، ومنطقة الخدمة، وغيرها.

Create
curl -X POST https://listingsapi.com/api/v4/locations \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "name": "Acme Kitchen",    "description": "Acme Kitchen is a family-owned neighborhood restaurant in downtown New York serving wood-fired pizza, hand-rolled pasta, and a short seasonal menu built around produce from nearby growers. Our dining room seats forty, takeout and delivery run until close, and the bar pours regional wine and beer on tap.",    "storeId": "ACME01",    "street": "123 Jump Street",    "city": "New York",    "stateIso": "NY",    "postalCode": "10013",    "countryIso": "US",    "phone": "6443859313",    "bizUrl": "https://acmekitchen.example.com",    "subCategoryId": 1432,    "businessHours": [      { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] },      { "day": "SUNDAY", "type": "CLOSED", "slots": [] }    ],    "placeActionLinks": [      { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true }    ]  }}'
JSON
{  "data": {    "createLocation": {      "clientMutationId": null,      "success": true,      "errors": null,      "location": {        "id": "TG9jYXRpb246MTgwMDI5MA==",        "databaseId": 1800290,        "storeId": "ACME01",        "name": "Acme Kitchen",        "city": "New York",        "countryIso": "US",        "status": "PENDING",        "placeActionLinks": [          { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true, "name": null, "submissionStatus": null }        ],        "discoveredPlaceActionLinks": []      }    }  }}

احتفظ بكلا المعرّفين: فالمعرّف id بصيغة base64 هو ما تحتاجه كل عملية كتابة لاحقة، أما databaseId فهو الرقم الذي تراه في لوحات التحكم وإشعارات webhook. تبدأ قيمة status بـ PENDING وتتقدم دون استدعاء إضافي لواجهة API. وتظل قيمتا name وsubmissionStatus لرابط إجراء الموقع مساويتين لـ null حتى يُزامَن مع Google. يتحقّق locations.add() في Python من صحة الحقول الإلزامية محليًا ويُصدر ValidationError قبل أي استدعاء عبر الشبكة؛ أما locations.create(input) فيقبل قاموس camelCase خامًا بدلًا من ذلك.

تحديث موقع

POST /locations/update هو تحديث جزئي: input.id هو الحقل الوحيد المطلوب، ولا تتغير إلا الحقول التي ترسلها. يمكن للاستدعاء نفسه تصحيح رقم هاتف أو إعادة كتابة الجدول الأسبوعي. يقبل كل من locations.update() في Python وupdateLocation() في Node كائن camelCase واحدًا.

المعاملات

الاسمالموضعمطلوبملاحظات
input.idbodyنعممعرّف الموقع بترميز base64. الحقل هو id، وليس locationId.
input.phone، input.bizUrl، input.name، حقول العنوانbodyلاأي حقل قابل للكتابة من استدعاء الإنشاء.
input.businessHoursbodyلاأرسل الأيام السبعة كلها؛ يُرفض إرسال جزء من الأسبوع. أضف عناصر day: "SPECIAL" مع specialDate لتجاوز ساعات العمل في العطلات، إلى جانب الجدول الأسبوعي، ولا ترسلها أبدًا بدلًا منه.
input.moreHoursbodyلاجداول ثانوية مثل جدول التوصيل، كل منها { hoursTypeId, moreHour }. لا يوجد specialDate هنا.
input.temporarilyClosedbodyلايوقف true نشر ساعات العمل أثناء إغلاق الموقع.
input.primaryGbpSiteCategoryId, input.additionalGbpSiteCategoryIdsbodyلافئات Google بصيغة GCIDs، مثل gcid:restaurant. أرسل countryIso معها.
input.placeActionLinksbodyلاإلحاق أو إدراج مع تحديث الموجود: يقبل كل عنصر action بالقيمة add (الافتراضية) أو delete. تبقى الروابط التي لا تذكرها دون تغيير.
input.submissionDisabledSiteIdsbodyلامعرّفات المواقع الإلكترونية المراد استثناؤها من الإرسال.

بالإضافة إلى الحقول الاختيارية المدرجة في الصفحة المرجعية.

Update
curl -X POST https://listingsapi.com/api/v4/locations/update \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "id": "TG9jYXRpb246MTgwMDI4OQ==",    "phone": "5125550199",    "bizUrl": "https://acmekitchen.example.com",    "primaryGbpSiteCategoryId": "gcid:restaurant",    "businessHours": [      { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] },      { "day": "SUNDAY", "type": "CLOSED", "slots": [] }    ]  }}'
JSON
{  "data": {    "updateLocation": {      "clientMutationId": null,      "success": true,      "errors": null,      "location": {        "id": "TG9jYXRpb246MTgwMDI4OQ==",        "databaseId": 1800289,        "name": "Jenny Home",        "phone": "5125550199",        "status": "APPROVED",        "placeActionLinks": [          { "placeActionType": "FOOD_ORDERING", "uri": "https://order.example.com", "isPreferred": true, "name": null, "submissionStatus": null }        ],        "discoveredPlaceActionLinks": []      }    }  }}

تُرجع الاستجابة الموقع بعد تحديثه لتتمكن من التأكد من تطبيق القيم الجديدة. يُرجع كل تحديث مجموعة placeActionLinks الحالية الكاملة للموقع. لتغيير عنوان URL لأحد الروابط، أرسل delete للرابط القديم وadd للرابط الجديد في الطلب نفسه.

أرشفة المواقع

يجدول POST /locations/archive موقعًا واحدًا أو أكثر للأرشفة. الأرشفة ليست فورية: ينتقل كل موقع إلى ARCHIVE_SCHEDULED، ويظل نشطًا وخاضعًا للفوترة حتى نهاية دورة الفوترة الحالية، ثم يُؤرشف عند إنشاء الفاتورة التالية. استخدمه لإنهاء خدمة الفروع المغلقة دفعة واحدة.

المعاملات

الاسمالموضعمطلوبملاحظات
input.locationIdsbodyنعممصفوفة معرّفات بترميز base64 للمواقع المراد أرشفتها.
input.clientMutationIdbodyلاتُرجع القيمة دون تغيير. ولا تمنع عمليات الكتابة المكررة.
Archive
curl -X POST https://listingsapi.com/api/v4/locations/archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="]  }}'
JSON
{  "data": {    "archiveLocations": {      "clientMutationId": null,      "bulkEditTaskId": null,      "errors": null,      "success": null,      "result": [        { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null },        { "locationId": "TG9jYXRpb246MTgwMDI5MA==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null }      ]    }  }}

تكون قيمة success في المستوى الأعلى هي null؛ اقرأ بدلًا منها قيمة success الخاصة بكل عنصر في result[]، حتى لا يمنع معرّف غير صالح واحد معالجة بقية الدفعة.

result[].statusالمعنى
ARCHIVE_SCHEDULEDأُضيف إلى قائمة الانتظار. يظل الموقع نشطًا وخاضعًا للفوترة حتى نهاية دورة الفوترة.
ARCHIVEDسبق أن أُرشف باستدعاء سابق أو في دورة سابقة.

أثناء جدولة الأرشفة، يُضبط archivalScheduledAt الخاص بالموقع في نتائج القوائم والبحث. لا يمكن تحديث موقع مؤرشف حتى يُعاد تنشيطه.

إلغاء الأرشفة المجدولة

يعيد POST /locations/cancel_archive المواقع الموجودة في ARCHIVE_SCHEDULED إلى ACTIVE، وتستمر الفوترة. لا يعمل هذا الاستدعاء إلا ما دامت دورة الفوترة مفتوحة؛ وبمجرد اكتمال أرشفة موقع، يُرجع الاستدعاء 404 لذلك الموقع.

المعاملات

الاسمالموضعمطلوبملاحظات
input.locationIdsbodyنعممصفوفة معرّفات بترميز base64 للمواقع الموجودة حاليًا في ARCHIVE_SCHEDULED.
input.clientMutationIdbodyلاتُرجع القيمة دون تغيير.
Cancel archive
curl -X POST https://listingsapi.com/api/v4/locations/cancel_archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="] } }'
JSON
{  "data": {    "cancelLocationsArchive": {      "clientMutationId": null,      "bulkEditTaskId": null,      "errors": null,      "success": null,      "result": [        { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ACTIVE", "success": true, "errors": null }      ]    }  }}

يؤكد كل عنصر في result[] عودة الموقع إلى ACTIVE. تتطلب كلتا حزمتَي SDK وسيطين موضعيين إضافيين، هما نوع التحديد ومعرّف changedBy، وترسلانهما إلى جانب locationIds؛ أما جسم طلب REST فلا يحتاج إلا إلى المعرّفات.

رفع صور إلى موقع

يُرفق POST /locations/photos الصور باستخدام عنوان URL متاح للعامة: تستضيف أنت الصورة، وتجلبها واجهة API وتخزّنها. يحتفظ الموقع بصورة واحدة من نوع LOGO، وصورة واحدة من نوع COVER، وأي عدد من الصور من نوع ADDITIONAL. تؤدي إعادة رفع شعار أو صورة غلاف إلى استبدال الصورة الموجودة.

المعاملات

الاسمالموضعمطلوبملاحظات
input.locationIdbodyنعممعرّف الموقع بترميز base64.
input.photosbodyنعممصفوفة من كائنات الصور؛ يجب ألا تكون فارغة.
input.photos[].photobodyنعمعنوان URL متاح للعامة لصورة بصيغة JPEG أو PNG. الحقل هو photo، وليس url.
input.photos[].typebodyلاLOGO أو COVER أو ADDITIONAL. القيمة الافتراضية هي صورة إضافية.
input.photos[].starredbodyلاميّز الصورة بنجمة عند وصولها. للصور الإضافية فقط، بحد أقصى 4 صور لكل موقع.
input.photos[].sourcebodyلاتسمية نصية حرة تسجّل مصدر الصورة.
Upload
curl -X POST https://listingsapi.com/api/v4/locations/photos \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "photos": [      { "photo": "https://cdn.example.com/jenny-home/logo.png", "type": "LOGO" },      { "photo": "https://cdn.example.com/jenny-home/storefront.jpg", "type": "ADDITIONAL" }    ]  }}'
JSON
{  "data": {    "addLocationPhotos": {      "clientMutationId": null,      "success": true,      "request": {},      "photos": [        { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "databaseId": 5878931, "type": "LOGO", "starred": false,          "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.png" },        { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": false,          "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg" }      ],      "errors": null    }  }}

احتفظ بقيمة id لكل صورة؛ إذ تقبلها نقطتا النهاية للتمييز بنجمة والإزالة. هذه هي بنية الاستجابة التي تتضمن النتائج مباشرةً لدفعة صغيرة. أما الدفعة الكبيرة فتُضاف إلى قائمة الانتظار: تُرجع photos فارغة وتُضبط request.requestId، وتستعلم دوريًا من نقطة النهاية التالية باستخدام ذلك المعرّف. تُحدَّد العتبة ضمن إعدادات الخادم، لذا اختر مسار التنفيذ بناءً على وجود request.requestId.

التحقق من حالة رفع الصور دفعة واحدة

يستعلم GET /locations/photos/requests/{requestId} دوريًا عن عملية رفع في قائمة الانتظار حتى تصل إلى حالة نهائية، ثم يعرض النتيجة لكل صورة. استخدمه لتحديث شريط التقدم وعرض الملفات التي رُفضت بالضبط وأسباب رفضها.

المعاملات

الاسمالموضعمطلوبملاحظات
requestIdpathنعممعرّف UUID الوارد في request.requestId ضمن استجابة عملية رفع في قائمة الانتظار. استعلم دوريًا فقط عن المعرّفات التي أعادتها عملية رفع حديثة.
Upload status
curl https://listingsapi.com/api/v4/locations/photos/requests/e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationPhotosUploadStatus": {      "requestId": "e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a",      "status": "SUCCESS",      "photos": [        { "mediaId": "TWVkaWFGaWxlOjkxODI3", "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "SUCCESS", "errorMessage": null },        { "mediaId": null, "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ERROR", "errorMessage": "Image dimensions below the minimum 250x250." }      ]    }  }}
status للمهمةالمعنى
PROCESSINGلا تزال المعالجة جارية. استعلم مرة أخرى بعد بضع ثوانٍ.
SUCCESSانتهت المعالجة. قد تكون معالجة بعض الصور قد فشلت رغم ذلك، لذا اقرأ photos[].status وphotos[].errorMessage.
ERRORفشلت المهمة نفسها. افحص photos[].errorMessage.

تشير حالة المهمة فقط إلى انتهاء المعالجة، ولا تعني أن كل صورة أُضيفت بنجاح. انتظر بضع ثوانٍ بين الاستعلامات؛ فتكرار الاستعلام هنا دون انتظار يؤدي سريعًا إلى بلوغ 429.

جلب صور موقع

يعيد GET /locations/{locationId}/photos جميع ملفات الوسائط المرتبطة بموقع في مصفوفة بسيطة: الشعار والغلاف والصور الإضافية، ويُرفق بكل ملف url المتاح للعامة وthumbnailUrl والفئة والعَلَم starred وviewsCount طوال مدة وجود الملف. استدعه قبل الرفع لمعرفة أي خانة تفتقر إلى صورة، ولجمع المعرّفات اللازمة لاستدعاءات التمييز بنجمة والإزالة.

المعاملات

الاسمالموضعمطلوبملاحظات
locationIdpathنعمالمعرّف بترميز base64. يُقبل أيضًا databaseId الرقمي الخام. إذا لم تكن القيمة بأيٍّ من هذين الشكلين، فستُعاد استجابة HTTP 200 مع SY90002: Invalid Id في errors.
List photos
curl https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/photos \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "mediaFilesOfLocation": [      { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "categoryName": "Logo", "categoryId": "4", "fileType": "image", "starred": false, "viewsCount": 0,        "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.jpg",        "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_f47d62a6.jpg" },      { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "categoryName": "Additional", "categoryId": "12", "fileType": "image", "starred": true, "viewsCount": 143,        "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg",        "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_ef4ffb12.jpg" }    ]  }}

تكون قيمة categoryName هي Logo أو Cover أو Additional. استخدم thumbnailUrl للعرض الشبكي وurl للعرض بالحجم الكامل. إذا لم يكن للموقع أي وسائط، فستُعاد مصفوفة فارغة، وليس خطأً.

تمييز صور الموقع بنجمة

يحدّد POST /locations/photos/star صورًا إضافية لمنحها أولوية العرض على منصات النشر التي تدعم الصور المميزة. لا يمكن تمييز سوى صور ADDITIONAL بنجمة، بحد أقصى 4 صور لكل موقع. تعمل إزالة النجمة بالطريقة نفسها: فالعَلَم starred في جسم الطلب هو الذي يحدّد الإجراء.

المعاملات

الاسمالموضعمطلوبملاحظات
input.locationIdbodyنعممعرّف الموقع بترميز base64.
input.photoIdsbodyنعممعرّفات الوسائط الواردة في استجابة جلب الصور أو الرفع.
input.starredbodyنعمtrue للتمييز بنجمة، وfalse لإزالة النجمة. هذا العَلَم، وليس المسار، هو الذي يحدّد الإجراء؛ إذ يعمل /star و/unstar بالطريقة نفسها.
Star
curl -X POST https://listingsapi.com/api/v4/locations/photos/star \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="],    "starred": true  }}'
JSON
{  "data": {    "starUnstarLocationPhotos": {      "success": true,      "photos": [        { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": true }      ],      "error": null    }  }}

مفتاح الاستجابة هو starUnstarLocationPhotos في كلا المسارين، ويحتوي على error واحد، وليس مصفوفة errors. تؤدي إضافة صورة خامسة مميزة بنجمة إلى فشل الاستدعاء بأكمله مع SY15012؛ إذ يحتسب التحقق الصور المميزة بنجمة بالفعل بالإضافة إلى الصور الواردة في هذا الطلب.

إزالة صور من موقع

يفصل POST /locations/photos/remove الصور الإضافية عن الموقع باستخدام معرّف الوسائط. استخدمه لإزالة صور واجهة المتجر القديمة أو التراجع عن عملية استيراد جماعي غير صحيحة. لا يمكن إزالة سوى صور ADDITIONAL؛ ولاستبدال شعار أو غلاف، ارفع صورة جديدة بقيمة type الخاصة به.

المعاملات

الاسمالموضعمطلوبملاحظات
input.locationIdbodyنعممعرّف الموقع بترميز base64.
input.photoIdsbodyنعممعرّفات الوسائط المطلوب فصلها، والواردة في استجابة جلب الصور أو الرفع.
Remove
curl -X POST https://listingsapi.com/api/v4/locations/photos/remove \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="]  }}'
JSON
{  "data": {    "removeLocationPhotos": {      "clientMutationId": null,      "removedCount": 1,      "errors": null    }  }}

يعرض removedCount عدد الملفات التي فُصلت فعليًا، لذا فإن 0 مع errors: null يعني أن أيًا من تلك المعرّفات لم يكن مرتبطًا بهذا الموقع. تفصل الإزالة الملف عن الموقع؛ ولا تحذفه بالضرورة من مكتبة الوسائط لديك.

الأخطاء التي ستواجهها

الرمزالمعنىالحل
SY10005description مفقود أو يقل طوله عن 200 حرف عند الإنشاء.اكتب وصفًا أكثر تفصيلًا.
SY10011street مفقود أو يقل طوله عن 5 أحرف.أرسل street.
SY10010stateIso مفقود أو غير صالح للبلد.أرسل رمز ولاية من البلدان.
SY10036countryIso ليس بلدًا مدعومًا.استخدم رمزًا من البلدان.
SY10126city مفقود لبلد يتطلبه.أرسل city.
SY15012أكثر من 4 صور مميزة بنجمة في موقع واحد.أزل النجمة عن صورة أولًا، أو احذف starred من طلب الرفع.
SY90002معرّف غير صالح في مسار الصور.مرّر المعرّف بترميز base64 أو الرقم المجرد، وليس بصيغة أخرى.
SY90016استُخدم مفتاح Read في عملية كتابة.استخدم مفتاح Write.
HTTP 404إلغاء أرشفة موقع لم يعد في حالة ARCHIVE_SCHEDULED.اكتملت أرشفة الموقع؛ استخدم خطوات إعادة التنشيط بدلًا من ذلك.
HTTP 429تم تجاوز حد معدل الطلبات.انتظر المدة المحددة في Retry-After. راجع حدود معدل الطلبات.

قد تُعيد عمليات الكتابة أيضًا استجابة HTTP 200 مع success: false والتفاصيل في data.<operation>.errors. تتوفر القائمة الكاملة في رموز الأخطاء.

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