# Google-Gemini-Bild-API: Zugang Stand: 25.09.2026 ## Überblick Die Key-Images der Demo-Lifelines entstehen über die Google Gemini API, Modell `gemini-3.1-flash-image` (Produktname „Nano Banana 2"). Der Zugang läuft ausschließlich über REST mit einem API-Key aus dem Google AI Studio – kein SDK, kein OAuth, kein Google-Cloud-Projekt-Setup nötig. Genutzt wird der Zugang nur von den Entwickler-Werkzeugen unter `dev/tools/lifeline-images/`: | Datei | Aufgabe | | --- | --- | | `gemini.py` | Einziger API-Client: liest den Key, baut den Request, speichert das Bild | | `anchors.py` | Erzeugt die Altersanker je Figur (Porträt-Kette) | | `events.py` | Erzeugt ein Bild pro Lifeline-Event mit Ankern als Referenz | | `contact_sheet.py`, `montage.py` | Reine Sichtungs-Werkzeuge, ohne API-Zugriff | Die Laravel-Anwendung selbst spricht nie mit der API. Sie importiert nur fertige Dateien per `php artisan lifelines:import-images `. ## Zugangsdaten Der Key steht als `GOOGLE_API_KEY` in `backend/.env` – sonst nirgends. `.env` ist in `backend/.gitignore` ausgeschlossen, der Key liegt also nicht im Repository. ``` GOOGLE_API_KEY= ``` `gemini.py` liest die Datei direkt zeilenweise und schneidet Anführungszeichen ab; fehlt der Eintrag, bricht das Skript mit `GOOGLE_API_KEY missing in backend/.env` ab. Der Key wird nur als HTTP-Header `x-goog-api-key` gesendet, nie in eine URL, einen Log oder eine Ausgabe geschrieben. Regeln für den Umgang: - Der Key gehört nur in die lokale Entwicklungsumgebung. Auf Produktion wird er **nicht** benötigt – dort landen ausschließlich die fertigen PNG-Dateien und der Import-Befehl. - Generierte Bilder und Anker liegen unter `backend/storage/app/private/lifeline-images/` und sind ebenfalls vom Repository ausgeschlossen. - Der Key ist ein Zahlungsmittel: Wer ihn hat, generiert auf Kosten des hinterlegten Guthabens. Nicht in Tickets, Chats oder Screenshots weitergeben. - Bei Verdacht auf Weitergabe: im AI Studio löschen und neu erzeugen (siehe letzter Abschnitt). ## Endpunkt und Request Ein Aufruf ist ein POST auf `generateContent` des Bildmodells: ``` POST https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-image:generateContent Content-Type: application/json x-goog-api-key: ``` Der Body enthält einen einzigen Nutzer-Turn. Referenzbilder stehen als `inline_data` **vor** dem Text; ihre Reihenfolge ist bedeutsam, weil der Prompt sie als „Reference photo 1/2/3" anspricht: ```json { "contents": [{ "role": "user", "parts": [ { "inline_data": { "mime_type": "image/jpeg", "data": "" } }, { "text": "" } ]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": { "aspectRatio": "4:3", "imageSize": "1K" } } } ``` Parameter, die wir setzen: | Feld | Werte | Bedeutung | | --- | --- | --- | | `responseModalities` | `["IMAGE"]` | Nur Bild zurück, kein Begleittext | | `imageConfig.aspectRatio` | `4:3` bzw. `3:4` | Querformat; `3:4` für Selfies und Porträt-Anker | | `imageConfig.imageSize` | `1K` | Auflösungsstufe | | Modell-ID | Umgebungsvariable `LIFELINE_IMAGE_MODEL` | Überschreibt die Vorgabe `gemini-3.1-flash-image` | Einen `seed` verwenden wir nicht mehr; falls doch gesetzt, akzeptiert die API nur Werte bis 1.000.000. ## Antwort verarbeiten Das Bild steckt base64-kodiert im ersten Kandidaten: ``` candidates[0].content.parts[*].inlineData.data ``` Zwei Eigenheiten, die im Client berücksichtigt sind: - Das Feld heißt je nach Antwort `inlineData` oder `inline_data` – beide Schreibweisen prüfen. - Die gelieferten Daten sind faktisch JPEG, obwohl wir die Dateien mit der Endung `.png` ablegen. Beim Hochladen als Referenz wird der MIME-Typ deshalb aus den Magic Bytes bestimmt (`\x89PNG` → `image/png`, sonst `image/jpeg`) und nicht aus der Dateiendung. Kommt kein Bild zurück, steht der Grund in `candidates[0].finishReason` oder in `promptFeedback`. Der Client wiederholt den Aufruf bis zu dreimal (5 s Pause; bei HTTP 429/500/503 gestaffelt 10/20 s) und wirft danach einen Fehler mit dem Grund im Text. ## Kontingente, Abrechnung, Fehlerbilder Guthaben und Tageslimit sind zwei getrennte Grenzen. Beide haben wir beim Aufbau der Bildsets erreicht, sie sehen im Log sehr ähnlich aus: | HTTP | Meldung | Ursache | Abhilfe | | --- | --- | --- | --- | | 402 | `Your prepayment credits are depleted` | Prepaid-Guthaben aufgebraucht | Im AI Studio unter Billing aufladen; wirkt sofort | | 429 | `Quota exceeded for metric: generate_requests_per_model_per_day, limit: 100` | Tageslimit von 100 Anfragen pro Modell erreicht | Warten bis Mitternacht Pacific (ca. 9 Uhr MESZ) oder höhere Stufe beantragen | | 200 mit `finishReason: IMAGE_OTHER` | `The model could not generate the image based on the prompt provided` | Inhaltsfilter des Modells, deterministisch pro Prompt | Prompt umformulieren – Wiederholen hilft nicht | Zum Tageslimit: Es zählt Anfragen, nicht erfolgreiche Bilder. Fehlversuche und Wiederholungen zählen mit. Ein vollständiger Durchlauf für eine große Lifeline (Jonas, 252 Bilder) sprengt das Limit deshalb und muss über mehrere Tage laufen. Der aktuelle Verbrauch ist unter [ai.dev/rate-limit](https://ai.dev/rate-limit) einsehbar. Zu `IMAGE_OTHER`: Der Filter reagiert auf Formulierungen, nicht auf Zufall. In unseren Sets waren die Auslöser unter anderem Rollenangaben wie „Singer of the band" (Prominenten-Filter), oberkörperfreie Referenzbilder und Kombinationen aus erschöpften Gesichtsbeschreibungen mit Bettszenen. Vorgehen: Prompt halbieren und die Hälften einzeln testen, bis die auslösende Passage feststeht. ## Key anlegen oder rotieren 1. [aistudio.google.com](https://aistudio.google.com/) mit dem Google-Konto öffnen, das die Rechnung trägt. 2. Unter **Get API key** einen Key für das gewünschte Projekt erzeugen. Der Wert wird nur einmal vollständig angezeigt. 3. Für das Bildmodell muss im Projekt Billing aktiv sein und Guthaben vorhanden – unter [ai.studio/projects](https://ai.studio/projects) zu prüfen. Ohne Guthaben antwortet die API mit HTTP 402. 4. Key in `backend/.env` als `GOOGLE_API_KEY=` eintragen. Kein `php artisan config:clear` nötig, da nur die Python-Werkzeuge ihn lesen. 5. Prüfen mit einem einzelnen Bild, zum Beispiel: ``` cd dev/tools/lifeline-images python3 gemini.py "Ein roter Apfel auf einem weißen Tisch, Foto." --out /tmp/test.png ``` Erfolg gibt den Pfad aus; bei Problemen erscheint die HTTP-Fehlermeldung im Klartext. 6. Alten Key nach der Umstellung im AI Studio löschen. Das Tageslimit hängt an Projekt und Abrechnungsstufe, nicht am einzelnen Key. Ein neuer Key im selben Projekt umgeht das Limit also nicht.