Ubicaciones

Ubicaciones: ejemplos cURL, Python y Node en orden de uso: listar, buscar, consultar por id/código de tienda, crear, actualizar, archivar y fotos.

Las fichas, las reseñas, las publicaciones y las analíticas están vinculadas a un ID de ubicación. El recurso de ubicaciones tiene trece endpoints: cuatro de lectura para encontrar ubicaciones, cuatro de escritura para crearlas, modificarlas y archivarlas, y cinco para gestionar fotos. Una clave Read (lectura) cubre las operaciones de lectura. Cada POST necesita una clave Write (escritura); usar una clave Read en una operación de escritura devuelve HTTP 400 con SY90016.

Todos los id de esta guía son id de Relay en base64, por ejemplo, TG9jYXRpb246MTgwMDI4OQ== (se decodifica como Location:1800289). Ambos SDK también aceptan el databaseId numérico y lo codifican por ti.

Resumen de endpoints

MétodoRutaQué haceReferencia
GET/locationsRecorre todas las ubicaciones por páginas, empezando por las más recientes.Listar todas las ubicaciones
GET/locations/searchBusca ubicaciones por palabra clave en el nombre, la dirección y el id de tienda.Buscar ubicaciones
GET/locations-by-idsObtiene los registros completos de un lote de id en base64.Obtener ubicaciones por ID
GET/locations-by-store-codesObtiene los registros completos a partir de tus propios códigos de tienda.Obtener ubicaciones por códigos de tienda
POST/locationsCrea una ubicación y la pone en cola para su envío.Crear una ubicación
POST/locations/updateModifica cualquier campo editable de una ubicación.Actualizar una ubicación
POST/locations/archivePrograma el archivado de ubicaciones para el final del ciclo de facturación.Archivar ubicaciones
POST/locations/cancel_archiveDevuelve al estado activo las ubicaciones cuyo archivado está programado.Cancelar el archivado programado
POST/locations/photosAdjunta un logotipo, una foto de portada o fotos de galería mediante URL.Subir fotos
GET/locations/photos/requests/{requestId}Consulta periódicamente el estado de una subida masiva en cola.Consultar el estado de una subida masiva
GET/locations/{locationId}/photosLista todos los archivos multimedia de una ubicación.Obtener fotos
POST/locations/photos/starMarca o desmarca fotos adicionales como destacadas.Marcar fotos como destacadas
POST/locations/photos/removeDesvincula fotos adicionales.Eliminar fotos

Listar todas las ubicaciones

GET /locations recorre por páginas todas las ubicaciones de la cuenta, empezando por las más recientes. Es una conexión con paginación por cursor: edges[].node contiene cada registro y pageInfo.endCursor proporciona el valor del parámetro after para la página siguiente. Úsalo para sincronizar toda la cuenta con tu propio sistema.

Parámetros

NombreEnObligatorioNotas
firstqueryNoTamaño de página al avanzar. El valor predeterminado es 50. Combínalo con after.
afterqueryNoEl pageInfo.endCursor de la página anterior.
lastqueryNoTamaño de página al retroceder. Combínalo con before.
beforequeryNoEl cursor del primer edge de la página desde la que retrocedes. pageInfo no tiene startCursor.
filterqueryNoObjeto codificado en JSON para acotar el conjunto en el servidor, por ejemplo, por estado de aprobación. El método de listado de ninguno de los dos SDK lo acepta; llama a REST para obtener un subconjunto filtrado.
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      }    }  }}

Lee pageInfo.hasNextPage y detente cuando sea false. El recuento de la página es pageInfo.total. El id del nodo es el identificador que esperan todos los demás endpoints.

Buscar ubicaciones

GET /locations/search busca coincidencias de una palabra clave en el nombre de la ubicación, la dirección postal, la ciudad y el id de tienda. Es una conexión con paginación por cursor con la misma estructura de edges[].node y pageInfo.endCursor que el listado. Úsalo para un selector con búsqueda mientras escribes o para confirmar que una tienda existe antes de crearla.

Parámetros

NombreEnObligatorioNotas
queryqueryNoLa palabra clave de búsqueda. Omítela para recorrer por páginas todas las ubicaciones sin filtrar.
firstqueryNoTamaño de página. El valor predeterminado es 50.
afterqueryNoEl pageInfo.endCursor de la página anterior.
filterqueryNoObjeto de filtro codificado en JSON, como en el endpoint de listado.

Ambos SDK también aceptan una lista fields para restringir la búsqueda de coincidencias a campos específicos, por ejemplo, solo 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      }    }  }}

Un array edges vacío significa que no hubo coincidencias. El nodo contiene la dirección completa, la categoría y el estado approved, por lo que una fila de resultados no requiere una segunda consulta.

Obtener ubicaciones por IDs

GET /locations-by-ids devuelve el registro completo de cada ubicación de un lote de ids en base64 en una sola llamada, con menos solicitudes que si hicieras una llamada por ubicación. Se resuelven tanto las ubicaciones activas como las archivadas. Devuelve un array simple, no una conexión. Úsalo para obtener los registros completos a partir de los ids de la carga útil de un webhook o de tu propio almacén de datos.

Parámetros

NombreEnObligatorioNotas
idsquerySíArray de ids en base64 codificado en JSON. Aplica la codificación URL al valor completo; curl -G --data-urlencode lo hace por ti.
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": []      }    ]  }}

Los ids que no existen en tu cuenta se omiten silenciosamente en lugar de generar un error. Compara la cantidad devuelta con la que enviaste para detectar ids obsoletos.

Obtener ubicaciones por códigos de tienda

GET /locations-by-store-codes realiza la búsqueda inversa: resuelve tus propios códigos de tienda para obtener los registros completos. Devuelve un array simple. Úsalo cuando tu sistema de punto de venta o tu registro de franquicias identifica las ubicaciones por código de tienda y nunca hayas almacenado nuestros ids.

Parámetros

NombreEnObligatorioNotas
storeCodesquerySíArray de cadenas de códigos de tienda codificado en JSON. Aplica la codificación URL al valor completo.
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"      }    ]  }}

Relaciona el storeId de cada objeto con la entrada correspondiente de tu registro. Los códigos sin coincidencias no se incluyen. Una ubicación solo tiene un código de tienda si se estableció storeId al crearla o actualizarla.

Crear una ubicación

POST /locations crea una ubicación y la pone en cola para enviarla a todas las plataformas de publicación de tu plan, o solo a enabledSiteIds cuando incluyes ese campo. Todos los campos van dentro de input. Hay 10 campos obligatorios; los demás son opcionales y las plataformas de publicación muestran en blanco los que omitas.

Parámetros

NombreEnObligatorioNotas
input.namebodySíNombre del negocio, de 2 a 150 caracteres.
input.descriptionbodySíMínimo de 200 caracteres (SY10005). Las plataformas de publicación lo usan como texto de la ficha.
input.countryIsobodySíUn código de país admitido (SY10036). Se obtiene de países.
input.citybodySíObligatorio cuando el valor de hasCity del país es true (SY10126). Cuando es false, se descarta la ciudad que envíes.
input.subCategoryIdbodySídatabaseId numérico de subcategorías.
input.streetbodySíDe 5 a 100 caracteres (SY10011).
input.stateIsobodySíObligatorio cuando el valor de stateRequired del país es true, y debe ser uno de los valores de states de ese país (SY10010). Omítelo cuando stateRequired sea false.
input.phonebodySíSe valida según el país (SY10015).
input.postalCodebodyNoOpcional, pero envíalo siempre que el valor de hasPostalCode del país sea true. El valor que envíes debe ajustarse al formato del país (SY10014).
input.bizUrlbodySíSe publica como sitio web. Algunas plataformas de publicación rechazan las fichas que no tienen uno.
input.storeIdbodyNoTu propio código de tienda, único en toda la cuenta.
input.businessHoursbodyNo7 entradas de { day, type, slots }. type es OPEN o CLOSED; un día en que el negocio está cerrado tiene slots vacío.
input.placeActionLinksbodyNoEnlaces de acción de Google: placeActionType, uri y, opcionalmente, isPreferred. No se necesita action al crear la ubicación. Consulta Enlaces de acción del lugar.
input.enabledSiteIdsbodyNoPublica en un subconjunto de los sitios de tu plan. Los ids se obtienen de sitios del plan.
input.clientMutationIdbodyNoSe devuelve sin cambios para correlacionar la solicitud y la respuesta. No evita escrituras duplicadas.

Además de los campos opcionales que figuran en la página de referencia: categorías GCID de Google, URLs de redes sociales, coordenadas, contacto del propietario, métodos de pago, área de servicio y más.

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

Conserva ambos ids: el id en base64 es el que necesita cada escritura posterior, y databaseId es el número que ves en los paneles y los webhooks. status comienza como PENDING y avanza sin otra llamada a la API. Los valores de name y submissionStatus de un enlace de acción del lugar permanecen en null hasta que el enlace se sincroniza con Google. locations.add() de Python valida los campos obligatorios localmente y lanza ValidationError antes de cualquier llamada de red; locations.create(input), en cambio, acepta un dict camelCase sin procesar.

Actualizar una ubicación

POST /locations/update es una actualización parcial: input.id es el único campo obligatorio y solo cambian los campos que envías. La misma llamada permite corregir un número de teléfono o reescribir el horario semanal. Tanto locations.update() de Python como updateLocation() de Node aceptan un único objeto camelCase.

Parámetros

NombreEnObligatorioNotas
input.idbodySíEl id en base64 de la ubicación. El campo es id, no locationId.
input.phone, input.bizUrl, input.name, campos de direcciónbodyNoCualquier campo que admita escritura de la llamada de creación.
input.businessHoursbodyNoEnvía los 7 días; se rechaza una semana parcial. Añade entradas day: "SPECIAL" con un specialDate para las excepciones de días festivos, junto con la semana, nunca en su lugar.
input.moreHoursbodyNoHorarios secundarios, como el de entrega, cada uno { hoursTypeId, moreHour }. Aquí no se admite specialDate.
input.temporarilyClosedbodyNotrue detiene la publicación de horarios mientras la ubicación está cerrada.
input.primaryGbpSiteCategoryId, input.additionalGbpSiteCategoryIdsbodyNoCategorías de Google como GCIDs, por ejemplo, gcid:restaurant. Envía countryIso junto con ellas.
input.placeActionLinksbodyNoAñadir o insertar o actualizar: cada elemento acepta action add (predeterminado) o delete. Los enlaces que no menciones se mantienen sin cambios.
input.submissionDisabledSiteIdsbodyNoIds de sitios que se excluirán del envío.

Además de los campos opcionales indicados en la página de referencia.

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

La respuesta devuelve la ubicación actualizada para que puedas confirmar que se aplicaron los nuevos valores. Cada actualización devuelve el conjunto completo y actual de placeActionLinks de la ubicación. Para cambiar la URL de un enlace, envía un delete para el enlace anterior y un add para el nuevo en la misma solicitud.

Archivar ubicaciones

POST /locations/archive programa el archivado de una o más ubicaciones. El archivado no es inmediato: cada ubicación pasa a ARCHIVE_SCHEDULED, permanece activa y se sigue facturando hasta que termina el ciclo de facturación actual, y se archiva cuando se genera la siguiente factura. Úsalo para dar de baja en lote las sucursales cerradas.

Parámetros

NombreEnObligatorioNotas
input.locationIdsbodySíArray de ids en base64 que se van a archivar.
input.clientMutationIdbodyNoSe devuelve sin cambios. No impide las escrituras duplicadas.
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 }      ]    }  }}

El success de nivel superior es null; lee en su lugar el success propio de cada entrada de result[], de modo que un id incorrecto no bloquee el resto del lote.

result[].statusSignificado
ARCHIVE_SCHEDULEDEn cola. La ubicación permanece activa y se sigue facturando hasta que termina el ciclo de facturación.
ARCHIVEDYa se archivó mediante una llamada o un ciclo anterior.

Mientras el archivado está programado, se establece el archivalScheduledAt de la ubicación en los resultados de las consultas de lista y búsqueda. Una ubicación archivada no se puede actualizar hasta que se reactive.

Cancelar el archivado programado

POST /locations/cancel_archive devuelve las ubicaciones que están en ARCHIVE_SCHEDULED a ACTIVE, y la facturación continúa. Solo funciona mientras el ciclo de facturación sigue abierto; una vez que una ubicación está completamente archivada, esta llamada devuelve 404 para ella.

Parámetros

NombreEnObligatorioNotas
input.locationIdsbodySíArray de ids en base64 que actualmente están en ARCHIVE_SCHEDULED.
input.clientMutationIdbodyNoSe devuelve sin cambios.
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 }      ]    }  }}

Cada entrada de result[] confirma que la ubicación ha vuelto a ACTIVE. Ambos SDK requieren 2 argumentos posicionales adicionales, un tipo de selección y un identificador changedBy, que envían junto con locationIds; el cuerpo de la solicitud REST solo necesita los ids.

Subir fotos a una ubicación

POST /locations/photos adjunta imágenes mediante una URL pública: tú alojas la imagen y la API la obtiene y la almacena. Una ubicación conserva un LOGO, un COVER y cualquier número de fotos ADDITIONAL. Volver a subir un logotipo o una portada reemplaza el elemento existente.

Parámetros

NombreEnObligatorioNotas
input.locationIdbodySíEl id en base64 de la ubicación.
input.photosbodySíArray de objetos de imagen; no puede estar vacío.
input.photos[].photobodySíUna URL pública de una imagen JPEG o PNG. El campo es photo, no url.
input.photos[].typebodyNoLOGO, COVER o ADDITIONAL. De forma predeterminada, se trata de una foto adicional.
input.photos[].starredbodyNoMarca la foto con una estrella al recibirla. Solo se aplica a fotos adicionales, con un máximo de 4 por ubicación.
input.photos[].sourcebodyNoEtiqueta de texto libre que registra la procedencia de la imagen.
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    }  }}

Conserva el id de cada foto; los endpoints para marcar con una estrella y eliminar lo aceptan. Esta es la estructura de la respuesta directa para un lote pequeño. En cambio, un lote grande se pone en cola: photos se devuelve vacío y se establece request.requestId, y debes consultar periódicamente el siguiente endpoint con ese id. El umbral se configura en el servidor, así que elige la rama de ejecución según esté presente o no request.requestId.

Consultar el estado de una carga masiva de fotos

GET /locations/photos/requests/{requestId} consulta periódicamente una carga en cola hasta que finaliza y, a continuación, informa del resultado de cada imagen. Úsalo para actualizar una barra de progreso y mostrar exactamente qué archivos se rechazaron y por qué.

Parámetros

NombreEnObligatorioNotas
requestIdpathSíEl UUID de request.requestId en la respuesta de una carga en cola. Consulta únicamente los identificadores devueltos por una carga reciente.
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 del trabajoSignificado
PROCESSINGSigue en curso. Vuelve a consultar al cabo de unos segundos.
SUCCESSEl procesamiento ha finalizado. Algunas fotos pueden haber fallado, así que consulta photos[].status y photos[].errorMessage.
ERROREl trabajo en sí ha fallado. Examina photos[].errorMessage.

El estado del trabajo solo indica que el procesamiento ha finalizado, no que todas las imágenes se hayan cargado correctamente. Deja pasar unos segundos entre consultas; un bucle sin pausas puede hacer que alcances 429 rápidamente.

Obtener las fotos de una ubicación

GET /locations/{locationId}/photos devuelve todos los archivos multimedia de una ubicación en un array simple: logotipo, portada e imágenes adicionales, cada uno con su url público, thumbnailUrl, categoría, indicador starred y viewsCount acumulado desde su creación. Llámalo antes de una carga para ver qué espacio falta por cubrir y recopilar los identificadores para las llamadas de marcado como destacado y eliminación.

Parámetros

NombreEnObligatorioNotas
locationIdpathSíEl identificador en base64. También se acepta el databaseId numérico sin codificar. Un valor que no tenga ninguno de esos formatos da lugar a una respuesta HTTP 200 con SY90002: Invalid Id en 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 es Logo, Cover o Additional. Usa thumbnailUrl para una cuadrícula y url para la vista a tamaño completo. Una ubicación sin archivos multimedia devuelve un array vacío, no un error.

Marcar fotos de una ubicación como destacadas

POST /locations/photos/star marca fotos adicionales para darles una posición prioritaria en las plataformas de publicación que admiten imágenes destacadas. Solo se pueden marcar como destacadas las fotos ADDITIONAL, con un máximo de 4 por ubicación. Quitar la marca de destacada funciona de la misma manera: el indicador starred del cuerpo determina qué ocurre.

Parámetros

NombreEnObligatorioNotas
input.locationIdbodySíEl identificador en base64 de la ubicación.
input.photoIdsbodySíIdentificadores de archivos multimedia de la respuesta de get-photos o de la carga.
input.starredbodySítrue para marcar como destacada, false para quitar la marca. Este indicador, no la ruta, determina la acción; /star y /unstar se comportan de la misma manera.
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    }  }}

La clave de respuesta es starUnstarLocationPhotos en ambas rutas y contiene un único error, no un array errors. Una quinta foto destacada hace que toda la llamada falle con SY15012; la comprobación cuenta las fotos que ya están marcadas como destacadas más las de esta solicitud.

Eliminar fotos de una ubicación

POST /locations/photos/remove desvincula fotos adicionales mediante su identificador de archivo multimedia. Úsalo para retirar imágenes desactualizadas de la fachada del establecimiento o deshacer una importación masiva incorrecta. Solo se pueden eliminar las fotos ADDITIONAL; para sustituir un logotipo o una portada, carga una nueva imagen con ese type.

Parámetros

NombreEnObligatorioNotas
input.locationIdbodySíEl identificador en base64 de la ubicación.
input.photoIdsbodySíIdentificadores de los archivos multimedia que se van a desvincular, obtenidos de la respuesta de get-photos o de la carga.
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 indica cuántos archivos se desvincularon realmente, por lo que 0 con errors: null significa que ninguno de esos identificadores estaba vinculado a esta ubicación. La eliminación desvincula el archivo; no necesariamente lo borra de tu biblioteca multimedia.

Errores que verás

CódigoSignificadoSolución
SY10005Falta description o tiene menos de 200 caracteres al crear.Escribe una descripción más completa.
SY10011Falta street o tiene menos de 5 caracteres.Envía street.
SY10010Falta stateIso o no es válido para el país.Envía un código de estado de países.
SY10036countryIso no es un país admitido.Usa un código de países.
SY10126Falta city para un país que lo requiere.Envía city.
SY15012Más de 4 fotos destacadas en una ubicación.Primero quita la marca de destacada de una foto o elimina starred de la carga.
SY90002Identificador no válido en la ruta de fotos.Proporciona el identificador en base64 o el número sin codificar, no otro formato.
SY90016Se usó una clave Read en una operación de escritura.Usa una clave Write.
HTTP 404Cancelación del archivado de una ubicación que ya no está en estado ARCHIVE_SCHEDULED.La ubicación está completamente archivada; usa el flujo de reactivación en su lugar.
HTTP 429Se ha superado el límite de solicitudes.Espera el tiempo indicado por Retry-After. Consulta límites de solicitudes.

Las operaciones de escritura también pueden devolver HTTP 200 con success: false y el detalle en data.<operation>.errors. La lista completa está en códigos de error.

Próximos pasos