OpenAI-compatible reverse-engineered gateway to Google Gemini Web (chat + image gen).
Drop-in base_url replacement for any OpenAI client — no API key, reuses your browser login.
pip install git+https://github.com/Lutiancheng1/gemini-webapi-proxy.git
Generated via POST /openai/v1/images/generations · gemini-2.5-pro-image
Google's official Gemini API requires a paid API key with separate quota. This proxy is for people who already have a Gemini Web session (you log in via browser, free daily quota) and want to use that session from any OpenAI-style client — without copy-pasting prompts into the web UI.
- No API key, no payment. Uses your existing Gemini Web login (Safari cookies, env vars, or a cookies.txt).
- One server, every OpenAI client. Point OpenAI Python SDK, LobeChat,
NextChat, ChatBox, or your own scripts at
http://localhost:4982/openai. - Image gen, not just chat. Returns base64 PNG/JPEG so downstream clients don't have to wrestle with Google's CDN redirects.
- Docker one-liner. A long-running container with auto-restart and cookie refresh via the macOS host's Safari.
Disclaimer: This project is not affiliated with Google. It uses
gemini-webapi, a reverse-engineered client of the Gemini Web frontend. Usage is subject to Google's Terms of Service. You are responsible for your own usage.
- ✨ Features
- 🚀 Quick Start
- 🔌 Wiring a client
- 🖼 Image generation
- ⚙️ Configuration
- 🏗 Architecture
- 🐳 Deployment & operations
- 🛣 Roadmap
- 🧪 Development
- ❓ Troubleshooting
- 🤝 Contributing
- ⚖️ License
- 🙏 Acknowledgements
- 🪄 Drop-in OpenAI API — point any OpenAI client (Python SDK, OpenAI CLI, ChatBox, NextChat, LobeChat, Outsider Studio, …) at
http://localhost:4982/openai - 💬 Chat completions — non-streaming JSON, multi-turn, reference images
- 🖼 Image generation — base64 output only (no CDN URL leakage), text-to-image + reference-image-to-image
- 🍪 Pluggable cookie sources — desktop browser (Safari/Chrome/…), env vars, Netscape cookie file
- 🐳 One-command Docker —
docker compose up -d, auto-restart, host network bridge for the local proxy - 🔌 Multi-fallback image download — Playwright Chromium → httpx → curl_cffi → library save
- 🛡 Upstream refusal detection — Gemini "I cannot fulfill…" answers mapped to HTTP 403, not 200
- 🛠 Optional API key —
GOP_API_KEY=...enables Bearer auth; empty = off (local-only default) - 📋 Curated model list — advertises a small fixed set of
gemini-3-*chat andgemini-2.5-*-imagealiases via/openai/v1/models
You need either:
- A logged-in Gemini Web session in Safari/Chrome on macOS or Linux, or
- A
__Secure-1PSID/__Secure-1PSIDTSpair fromgemini.google.com, or - A Netscape-format
cookies.txtexported from a browser.
Pick one install path:
pip install "gemini-webapi-proxy[browser-cookie]"
gemini-webapi-proxy # starts on :4982git clone https://github.com/Lutiancheng1/gemini-webapi-proxy
cd gemini-webapi-proxy
python3 -m venv .venv
.venv/bin/pip install -e ".[browser-cookie,dev]"
.venv/bin/playwright install chromium
./start.shgit clone https://github.com/Lutiancheng1/gemini-webapi-proxy
cd gemini-webapi-proxy
bash scripts/docker-up.shscripts/docker-up.sh syncs your browser cookies into data/runtime.env
(only needed on macOS — Linux hosts can use env vars directly), then
launches the container.
In all three cases, verify the service:
curl -sS http://localhost:4982/health
# {"status":"ok","service":"gemini-webapi-proxy"}See docs/docker.md for the full deployment guide.
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:4982/openai/v1",
api_key="not-verified", # any non-empty string; or set GOP_API_KEY
)
resp = client.chat.completions.create(
model="gemini-3-flash",
messages=[{"role": "user", "content": "Reply with exactly: pong"}],
)
print(resp.choices[0].message.content)Curated chat models: gemini-3-flash, gemini-3-pro. See what your
deployment advertises with:
curl -sS http://localhost:4982/openai/v1/models | python3 -m json.toolSee docs/studio.md for connecting OpenAI-compatible desktop apps, and docs/api.md for the full API reference.
import base64, pathlib
img = client.images.generate(
model="gemini-2.5-pro-image", # stable alias — see /openai/v1/models
prompt="a still life watercolor of lemons in a bowl, soft pastel colors",
n=1,
response_format="b64_json",
)
pathlib.Path("out.png").write_bytes(base64.b64decode(img.data[0].b64_json))With a reference image:
import base64, pathlib
data_url = (
"data:image/png;base64,"
+ base64.b64encode(pathlib.Path("ref.png").read_bytes()).decode()
)
img = client.images.generate(
model="gemini-2.5-pro-image",
prompt="a portrait in the same style as the reference",
n=1, size="1024x1024",
extra_body={"image": [data_url]},
)
pathlib.Path("out.png").write_bytes(base64.b64decode(img.data[0].b64_json))The proxy always returns b64_json (never Google CDN URLs) because most
downstream clients can't fetch lh3.googleusercontent.com/... directly.
See docs/api.md and
docs/downloaders.md for the download chain.
All configuration is via environment variables. The GOP_ prefix is
recommended; a few unprefixed names (PORT, USE_BROWSER_COOKIES, …)
are kept as legacy aliases for compatibility.
| Variable | Default | Description |
|---|---|---|
GOP_PORT |
4982 |
listen port |
GOP_HOST |
0.0.0.0 |
listen address |
GOP_API_KEY |
(empty) | when set, require Authorization: Bearer <key> |
GOP_COOKIE_SOURCE |
browser |
browser / env / file |
GOP_BROWSER |
auto |
preferred browser: auto / safari / chrome / edge / brave / chromium |
GOP_COOKIE_FILE |
(empty) | path to a Netscape-format cookies.txt |
GOP_GEMINI_1PSID |
(empty) | __Secure-1PSID value (env / file modes) |
GOP_GEMINI_1PSIDTS |
(empty) | __Secure-1PSIDTS value |
GOP_GEMINI_COOKIES_RAW |
(empty) | k1=v1; k2=v2 form |
GOP_HTTP_PROXY |
(empty) | local HTTP proxy (http://127.0.0.1:7897) |
GOP_CHAT_TIMEOUT |
180 |
seconds |
GOP_IMAGE_TIMEOUT |
300 |
seconds |
GOP_INIT_TIMEOUT |
120 |
seconds |
GOP_PROBE_ON_START |
false |
probe models at startup |
GOP_LOG_FORMAT |
console |
console (dev) or json (container) |
GOP_LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING / ERROR / CRITICAL |
GOP_DATA_DIR |
./data |
where the model registry file lives |
See docs/configuration.md for the full list and detailed semantics.
┌─────────────── FastAPI (uvicorn) ───────────────┐
│ /health /ready │
│ /openai/v1/models /openai/v1/chat/completions │
│ /openai/v1/images/generations /admin/probe-models │
│ │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Cookie │ → │ GeminiClient pool│ → gemini-webapi
│ │ Source │ │ (lazy singleton) │ │
│ └─────────────┘ └──────────────────┘ │
│ │
│ ┌────────────────────────── Image download ──┐ │
│ │ Playwright Chromium → httpx → curl_cffi │ │
│ │ → gemini-webapi library save (last resort)│ │
│ └────────────────────────────────────────────┘ │
└────────────────────────────────────────────────┘
- Pluggable cookie sources (
gemini_webapi_proxy.cookies) — add a new one by subclassingBaseCookieSourceand registering it. - Pluggable image downloaders (
gemini_webapi_proxy.downloaders) — each strategy is a small class; reorder the chain inGOP_DOWNLOADER_CHAIN. - Pluggable Gemini client (
gemini_webapi_proxy.client) — current implementation wrapsgemini-webapi; future backends can implementBaseGeminiClientwithout touching the rest of the code. - Curated model registry (
gemini_webapi_proxy.client.registry) — two chat entries (gemini-3-flash,gemini-3-pro) and two stable image aliases (gemini-2.5-flash-image,gemini-2.5-pro-image). The reverse-alias index is guarded so that asking forgemini-3-procan never route to an image alias by accident.
See docs/architecture.md for the deeper data flow (cookie bootstrap → registry sync → request → RPC → downloader chain → b64 response).
- Docker on macOS — long-running container with
restart: unless-stopped, syncs Safari cookies viascripts/docker-up.sh. - Docker on Linux — set
GOP_GEMINI_1PSID/GOP_GEMINI_1PSIDTSin.env; no browser cookie source needed. - Bare-metal / venv —
./start.shorpython -m gemini_webapi_proxy. - Cookie refresh — Safari re-login →
python scripts/sync_runtime_env.py→docker compose restart. See docs/troubleshooting.md.
- Streaming chat completions (SSE) — currently
stream=truereturns 400 - Pluggable Gemini backends — official Gemini API, third-party proxies
- More cookie sources — Playwright storage state export, Firefox via
browser-cookie3 - Per-model rate limits — token-bucket per model id
- Hot-reload of model registry — watch
data/model_registry.json - Prometheus
/metricsendpoint
git clone https://github.com/Lutiancheng1/gemini-webapi-proxy
cd gemini-webapi-proxy
python3 -m venv .venv
.venv/bin/pip install -e ".[browser-cookie,dev]"
.venv/bin/playwright install chromium
.venv/bin/pytest tests/ -v
.venv/bin/ruff check
.venv/bin/ruff format
.venv/bin/mypy src/
bash scripts/e2e-image.sh # requires a real Gemini sessionSee CONTRIBUTING.md for the full contribution workflow, docs/architecture.md for the design, and examples/ for runnable code samples.
The short version:
| Symptom | Likely cause | Fix |
|---|---|---|
Failed to connect to localhost:4982 |
server not started | bash scripts/docker-up.sh or ./start.sh |
401 / AuthError from upstream |
cookie expired | re-login in Safari, re-run scripts/sync_runtime_env.py, docker compose restart |
403 "I cannot fulfill..." |
upstream safety filter | try a different prompt, or check the model list |
502 with Multiple cookies exist |
.com vs .com.hk duplicates |
already auto-filtered; if not, docker compose restart |
| 502 (b64 download) | Playwright missing | playwright install chromium (host) or rebuild Docker image |
Upstream Unknown model name: gemini-2.5-pro-image against a gemini-3-pro request |
fixed in 0.1.0 — re-pull the image and restart | docker compose pull && docker compose up -d |
Full troubleshooting matrix: docs/troubleshooting.md.
PRs welcome! Read CONTRIBUTING.md for the dev setup, conventional-commit format, and the release process. Bug reports go to GitHub Issues; security issues go to the address in SECURITY.md, not to public issues.
This project is licensed under GPL-3.0-or-later. See LICENSE.
It depends on gemini-webapi,
which is also GPL-3.0. By installing the runtime dependency, you agree to
its license.
The project is not affiliated with Google and provides no warranty. Use it only in compliance with Google's Terms of Service.
- HanaokaYuzu/Gemini-API — the upstream reverse-engineered client without which this project would not exist
- The OpenAI Python SDK team — for a clean, widely-implemented API contract that makes this kind of gateway easy to build
- All contributors and early testers