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.
En bref
- Ce que ce guide permet
- List a location's stored owner and customer Google photos, page through them, and read how fresh the data is.
- API utilisées
- get
/api/v1/locations/{id}/media/googleLister les photos Google enregistrées d'un établissement - get
/api/v1/media/googleLister les photos Google enregistrées - get
/api/v1/mediaObtenir les médias de l'établissement - post
/api/v1/locations/{id}/mediaTéléverser des photos vers un établissement
- get
- Référence
- MediaLocations
- Prérequis
- An API key with the media:read scope
- A location id (loc_...), from the locations endpoints
- Cas d'usage typiques
- 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
Les photos Google vous parviennent de deux manières, et ce ne sont pas les mêmes données. GET /api/v1/media renvoie la médiathèque propre à Synup pour un établissement, regroupée par catégorie. Les deux endpoints de ce guide, GET /api/v1/locations/{id}/media/google et GET /api/v1/media/google, renvoient les photos que Synup a enregistrées depuis Google lui-même : celles importées par l'entreprise (owner) et celles ajoutées par les clients (ugc).
Ces deux lectures portent uniquement sur les données enregistrées. Elles n'appellent jamais Google et ne lancent aucune synchronisation, vous pouvez donc les interroger sans crainte. Chaque photo est une référence vers une URL hébergée par Google, pas une copie (une photo envoyée depuis Synup peut garder son URL source jusqu'à ce que la prochaine récupération planifiée la remplace par celle de Google), et son externalId (le nom complet de la ressource Google) est son identité : la même URL sous deux identifiants différents correspond à deux photos.
Lire les photos de l'entreprise, puis celles des clients, puis la page suivante
Les identifiants, noms et le curseur de ces exemples sont fictifs et illustratifs, non exécutables : transmettez toujours le nextCursor réel renvoyé par votre propre réponse. Commencez par les photos de l'entreprise, une par page pour que la pagination soit visible.
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"
}
}Le bloc location revient à chaque lecture d'un établissement, même sans élément, et porte la fraîcheur des photos de l'entreprise et des clients. Passons aux photos des clients.
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
}
}Ici, une liste items vide ne signifie pas « aucune photo de client ». ugc.coverage vaut unavailable : Google n'a pas répondu pour les photos de clients lors de la dernière tentative. Lisez location avant d'afficher une galerie vide à un client. Enfin, suivez nextCursor de la première page avec les mêmes filtres.
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 vaut null : c'était la dernière page. Un curseur ne fonctionne qu'avec les filtres pour lesquels il a été émis. limit accepte un entier de 1 à 100 (50 par défaut) et toute autre valeur renvoie une 400, sans ajustement. Pour tous les établissements à la fois, appelez GET /api/v1/media/google, qui accepte aussi locationId, clientId et kind ; son location vaut null uniquement sans locationId ; avec locationId, il porte la couverture de cet établissement même si items est vide. Chaque élément porte aussi la sienne.
Ce que garantissent les champs
- Un élément par photo et par établissement. Une photo partagée par plusieurs établissements apparaît une fois pour chaque établissement auquel vous avez accès. Identifiez les lignes par
associationKey, pas parid. presenceest une observation.seen_in_last_complete_scansignifie qu'une réponse de Google a inclus la photo depuis le début de la dernière analyse complète.not_seen_in_last_complete_scansignifie qu'elle n'y figurait pas. Aucune des deux ne dit que Google a supprimé quoi que ce soit, etunknownsignifie qu'il n'y a pas d'analyse complète à comparer. Une photo que vous avez envoyée après le début d'une analyse compte aussi comme vue.- Rien n'est masqué. Les photos marquées
spamouneeds_reviewdansmoderation.statussont toujours renvoyées, et les anciennes lignes sont conservées au lieu d'être filtrées : appliquez votre propre règle. - L'accès limite ce que vous voyez. Une clé limitée à certains clients ne voit que les établissements de ces clients, un établissement sans client n'est visible que des clés valables pour toute l'agence, et les autres établissements d'une photo partagée ne sont jamais renvoyés ni comptés.
- La fraîcheur est honnête. Une actualisation réussie est planifiée toutes les 72 heures par un répartiteur quotidien à 04:00 UTC. Une connexion Google absente ou une erreur apparaît comme une couverture
partialouunavailable, et une réponse 403 ou 404 de Google pour les photos de clients peut signifier indisponible, pas vide. Être éligible à une nouvelle tentative ne promet pas qu'elle s'exécute à cet instant.
Depuis un client IA (MCP)
L'outil list_google_location_media lit les mêmes données enregistrées avec les mêmes filtres (locationId, clientId, kind, limit, cursor) et renvoie la même page.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_google_location_media",
"arguments": { "locationId": "loc_456", "kind": "owner", "limit": 25 }
}
}Il n'actualise rien. fetch_gbp_media est un autre outil : il interroge Google à la demande et modifie les données enregistrées, ne l'utilisez donc que pour une nouvelle récupération.