GuideAnleitungen/Rezepte/Read stored Google photos
How do I read a location's Google Business Profile photos through the API?

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.

Auf einen Blick

Was diese Anleitung leistet
List a location's stored owner and customer Google photos, page through them, and read how fresh the data is.
Verwendete APIs
Referenz
MediaLocations
Voraussetzungen
  • An API key with the media:read scope
  • A location id (loc_...), from the locations endpoints
Typische Anwendungsfälle
  • 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

Google-Fotos erreichen Sie auf zwei Wegen, und es sind nicht dieselben Daten. GET /api/v1/media liefert die eigene Mediathek von Synup für einen Standort, nach Kategorie gruppiert. Die beiden Endpunkte in dieser Anleitung, GET /api/v1/locations/{id}/media/google und GET /api/v1/media/google, liefern die Fotos, die Synup von Google selbst gespeichert hat: die vom Unternehmen hochgeladenen (owner) und die von Kunden hinzugefügten (ugc).

Beide Abrufe lesen nur Gespeichertes. Sie rufen Google nie auf und starten keine Synchronisierung, Sie können sie also gefahrlos wiederholt abfragen. Jedes Foto ist ein Verweis auf eine bei Google gehostete URL, keine Kopie (ein von Synup gesendetes Foto kann seine Quell-URL behalten, bis der nächste geplante Abruf sie durch die von Google ersetzt), und seine externalId (der vollständige Google-Ressourcenname) ist seine Identität: dieselbe URL unter zwei verschiedenen IDs sind zwei Fotos.

Erst Unternehmensfotos lesen, dann Kundenfotos, dann die nächste Seite

IDs, Namen und der Cursor in diesen Beispielen sind frei erfunden und nur zur Veranschaulichung, nicht ausführbar: Übergeben Sie immer den tatsächlichen nextCursor aus Ihrer eigenen Antwort. Beginnen Sie mit den eigenen Fotos des Unternehmens, eines pro Seite, damit der Seitenwechsel sichtbar wird.

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"
  }
}

Der Block location kommt bei jedem Standortabruf zurück, auch ohne Einträge, und enthält die Aktualität von Unternehmens- und Kundenfotos. Jetzt die Kundenfotos.

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
  }
}

Eine leere Liste items bedeutet hier nicht „keine Kundenfotos“. ugc.coverage ist unavailable, Google hat beim letzten Versuch also nicht für Kundenfotos geantwortet. Lesen Sie location, bevor Sie einem Kunden eine leere Galerie zeigen. Folgen Sie zuletzt nextCursor von der ersten Seite, mit denselben Filtern.

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 ist null, das war also die letzte Seite. Ein Cursor funktioniert nur mit den Filtern, für die er ausgestellt wurde. limit nimmt eine ganze Zahl von 1 bis 100 an (Standard 50), jeder andere Wert ergibt ein 400 und wird nicht angepasst. Für alle Standorte auf einmal rufen Sie GET /api/v1/media/google auf, das auch locationId, clientId und kind annimmt; sein location ist nur null, wenn Sie kein locationId übergeben; mit locationId enthält es die Abdeckung dieses Standorts, auch wenn items leer ist. Jeder Eintrag trägt zusätzlich seinen eigenen.

Was die Felder zusichern

  • Ein Eintrag pro Foto und Standort. Ein Foto, das mehrere Standorte teilen, erscheint einmal für jeden Standort, auf den Sie zugreifen dürfen. Identifizieren Sie Zeilen über associationKey, nicht über id.
  • presence ist eine Beobachtung. seen_in_last_complete_scan heißt, dass eine Antwort von Google das Foto enthielt, seit der letzte vollständige Scan begann. not_seen_in_last_complete_scan heißt, dass es in diesem Scan nicht enthalten war. Keines von beiden besagt, dass Google etwas gelöscht hat, und unknown heißt, dass es keinen vollständigen Scan zum Vergleichen gibt. Ein Foto, das Sie nach Beginn eines Scans gesendet haben, zählt ebenfalls als gesehen.
  • Nichts wird ausgeblendet. Als spam oder needs_review in moderation.status markierte Fotos werden weiterhin zurückgegeben, und alte Zeilen bleiben erhalten statt gefiltert zu werden. Wenden Sie Ihre eigene Regel an.
  • Zugriff begrenzt, was Sie sehen. Ein auf bestimmte Kunden beschränkter Schlüssel sieht nur deren Standorte, ein Standort ohne Kunde ist nur für agenturweite Schlüssel sichtbar, und die anderen Standorte eines geteilten Fotos werden nie zurückgegeben oder gezählt.
  • Die Aktualität ist ehrlich. Eine erfolgreiche Aktualisierung wird alle 72 Stunden von einem täglichen Dispatcher um 04:00 UTC eingeplant. Eine fehlende Google-Verbindung oder ein Fehler erscheint als Abdeckung partial oder unavailable, und ein 403 oder 404 von Google für Kundenfotos kann nicht verfügbar bedeuten, nicht leer. Dass ein erneuter Versuch zulässig ist, verspricht nicht, dass er in diesem Moment läuft.

Aus einem KI-Client (MCP)

Das Tool list_google_location_media liest dieselben gespeicherten Daten mit denselben Filtern (locationId, clientId, kind, limit, cursor) und liefert dieselbe Seite.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_google_location_media",
    "arguments": { "locationId": "loc_456", "kind": "owner", "limit": 25 }
  }
}

Es aktualisiert nichts. fetch_gbp_media ist ein anderes Tool: Es ruft Google auf Anfrage ab und ändert gespeicherte Daten, nutzen Sie es also nur, wenn Sie einen neuen Abruf wollen.