Read a Location's Google Photos from Stored Data
List the Google Business Profile photos Synup has stored for a location, owner and customer, with freshness and paging, without calling Google.
De un vistazo
- Qué logra esta guía
- List a location's stored owner and customer Google photos, page through them, and read how fresh the data is.
- APIs utilizadas
- get
/api/v1/locations/{id}/media/googleLista las fotos de Google almacenadas de una ubicación - get
/api/v1/media/googleLista las fotos de Google almacenadas - get
/api/v1/mediaObtener medios de la ubicación - post
/api/v1/locations/{id}/mediaSubir fotos a una ubicación
- get
- Referencia
- MediaLocations
- Requisitos previos
- An API key with the media:read scope
- A location id (loc_...), from the locations endpoints
- Casos de uso habituales
- Showing a location's Google photos in your own dashboard
- Checking which photos customers added to a listing
- Telling a stale photo list from a complete one
Las fotos de Google te llegan de dos maneras, y no son los mismos datos. GET /api/v1/media devuelve la biblioteca multimedia propia de Synup para una ubicación, agrupada por categoría. Los dos endpoints de esta guía, GET /api/v1/locations/{id}/media/google y GET /api/v1/media/google, devuelven las fotos que Synup ha almacenado desde el propio Google: las que subió el negocio (owner) y las que añadieron los clientes (ugc).
Ambas lecturas solo consultan lo almacenado. Nunca llaman a Google ni inician una sincronización, así que puedes consultarlas con frecuencia. Cada foto es una referencia a una URL alojada en Google, no una copia (una foto que enviaste desde Synup puede conservar su URL de origen hasta que la próxima descarga programada la sustituya por la de Google), y su externalId (el nombre completo del recurso en Google) es su identidad: la misma URL con dos ids distintos son dos fotos.
Lee las fotos del negocio, luego las de clientes y después la página siguiente
Los ids, nombres y el cursor de estos ejemplos son ficticios e ilustrativos, no ejecutables: pasa siempre el nextCursor real que devuelva tu propia respuesta. Empieza por las fotos del propio negocio, una por página para que se vea la paginación.
curl "https://ai.synup.com/api/v1/locations/loc_456/media/google?kind=owner&limit=1" \
-H "Authorization: Bearer $SYNUP_API_KEY"{
"data": {
"items": [
{
"id": "9b2e7c14-5d3a-4f6e-8a21-c0d7f3a1b645",
"associationKey": "9b2e7c14-5d3a-4f6e-8a21-c0d7f3a1b645:loc_456",
"externalId": "accounts/102938475610293847561/locations/5647382910563748291/media/AF1QipOwnerLobby01",
"url": "https://lh3.googleusercontent.com/p/AF1QipOwnerLobby01",
"category": "INTERIOR",
"format": "PHOTO",
"kind": "owner",
"lastSeenAt": "2026-10-12T04:01:58.000Z",
"presence": "seen_in_last_complete_scan",
"moderation": { "status": null },
"location": { "id": "loc_456", "name": "Acme Dental, Downtown", "clientId": "cli_123" },
"freshness": { "coverage": "complete", "outcome": "complete", "lastAttemptAt": "2026-10-12T04:02:11.000Z", "lastSuccessAt": "2026-10-12T04:02:11.000Z", "completedScanAt": "2026-10-12T04:01:58.000Z" }
}
],
"location": {
"id": "loc_456", "name": "Acme Dental, Downtown", "clientId": "cli_123",
"owner": { "coverage": "complete", "outcome": "complete", "lastAttemptAt": "2026-10-12T04:02:11.000Z", "lastSuccessAt": "2026-10-12T04:02:11.000Z", "completedScanAt": "2026-10-12T04:01:58.000Z" },
"ugc": { "coverage": "unavailable", "outcome": "unavailable", "lastAttemptAt": "2026-10-12T04:02:12.000Z", "lastSuccessAt": null, "completedScanAt": null }
},
"limit": 1,
"nextCursor": "NEXT_CURSOR_FROM_THIS_RESPONSE"
}
}El bloque location vuelve en cada lectura de una ubicación, incluso sin elementos, y trae la actualización de las fotos del negocio y de clientes. Ahora, las fotos de clientes.
curl "https://ai.synup.com/api/v1/locations/loc_456/media/google?kind=ugc" \
-H "Authorization: Bearer $SYNUP_API_KEY"{
"data": {
"items": [],
"location": {
"id": "loc_456", "name": "Acme Dental, Downtown", "clientId": "cli_123",
"owner": { "coverage": "complete", "outcome": "complete", "lastAttemptAt": "2026-10-12T04:02:11.000Z", "lastSuccessAt": "2026-10-12T04:02:11.000Z", "completedScanAt": "2026-10-12T04:01:58.000Z" },
"ugc": { "coverage": "unavailable", "outcome": "unavailable", "lastAttemptAt": "2026-10-12T04:02:12.000Z", "lastSuccessAt": null, "completedScanAt": null }
},
"limit": 50,
"nextCursor": null
}
}Aquí una lista items vacía no significa "sin fotos de clientes". ugc.coverage es unavailable, así que Google no respondió por las fotos de clientes en el último intento. Lee location antes de mostrar una galería vacía a un cliente. Por último, sigue nextCursor de la primera página con los mismos filtros.
curl "https://ai.synup.com/api/v1/locations/loc_456/media/google?kind=owner&limit=1&cursor=NEXT_CURSOR_FROM_THIS_RESPONSE" \
-H "Authorization: Bearer $SYNUP_API_KEY"{
"data": {
"items": [
{
"id": "c5d0e3a7-81b4-4a29-b6f2-3e9a7d1c0f58",
"associationKey": "c5d0e3a7-81b4-4a29-b6f2-3e9a7d1c0f58:loc_456",
"externalId": "accounts/102938475610293847561/locations/5647382910563748291/media/AF1QipOwnerFront02",
"url": "https://lh3.googleusercontent.com/p/AF1QipOwnerFront02",
"category": "EXTERIOR",
"format": "PHOTO",
"kind": "owner",
"lastSeenAt": "2026-10-12T04:01:58.000Z",
"presence": "seen_in_last_complete_scan",
"moderation": { "status": null },
"location": { "id": "loc_456", "name": "Acme Dental, Downtown", "clientId": "cli_123" },
"freshness": { "coverage": "complete", "outcome": "complete", "lastAttemptAt": "2026-10-12T04:02:11.000Z", "lastSuccessAt": "2026-10-12T04:02:11.000Z", "completedScanAt": "2026-10-12T04:01:58.000Z" }
}
],
"location": { "id": "loc_456", "...": "same location block as the first page" },
"limit": 1,
"nextCursor": null
}
}nextCursor es null, así que era la última página. Un cursor solo sirve con los filtros con que se emitió. limit acepta un entero de 1 a 100 (50 por defecto) y cualquier otro valor da un 400, no se ajusta. Para todas las ubicaciones a la vez, llama a GET /api/v1/media/google, que también acepta locationId, clientId y kind; su location es null solo cuando no envías locationId; con locationId trae la cobertura de esa ubicación aunque items esté vacío. Cada elemento trae además la suya.
Qué garantizan los campos
- Un elemento por foto y ubicación. Una foto compartida por varias ubicaciones aparece una vez por cada ubicación a la que tienes acceso. Identifica las filas con
associationKey, no conid. presencees una observación.seen_in_last_complete_scansignifica que una respuesta de Google incluyó la foto desde que empezó el último análisis completo.not_seen_in_last_complete_scansignifica que no estaba en ese análisis. Ninguno dice que Google borrara nada, yunknownsignifica que no hay un análisis completo con el que comparar. Una foto que enviaste después de que empezara un análisis también cuenta como vista.- No se oculta nada. Las fotos marcadas
spamoneeds_reviewenmoderation.statusse siguen devolviendo, y las filas antiguas se conservan en lugar de filtrarse, así que aplica tu propia política. - El acceso limita lo que ves. Una clave limitada a ciertos clientes solo ve las ubicaciones de esos clientes, una ubicación sin cliente solo la ven las claves de toda la agencia, y las otras ubicaciones de una foto compartida nunca se devuelven ni se cuentan.
- La actualización es honesta. Cada 72 horas se programa una actualización correcta, mediante un despachador diario a las 04:00 UTC. Una conexión de Google ausente o un error aparece como cobertura
partialounavailable, y que Google responda 403 o 404 para las fotos de clientes puede significar no disponible, no vacío. Poder reintentar no promete que el reintento se ejecute en ese momento.
Desde un cliente de IA (MCP)
La herramienta list_google_location_media lee los mismos datos almacenados con los mismos filtros (locationId, clientId, kind, limit, cursor) y devuelve la misma página.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_google_location_media",
"arguments": { "locationId": "loc_456", "kind": "owner", "limit": 25 }
}
}No actualiza nada. fetch_gbp_media es otra herramienta: consulta Google bajo demanda y cambia los datos almacenados, así que úsala solo cuando quieras una descarga nueva.