Playwright + FastAPI · personal/dev use only · MIT

your Gemini subscription,
as an API you can call

Playwright drives the real Gemini web app in a logged-in browser; your scripts talk to a small google-genai compatible HTTP endpoint. Point several accounts at it and they become a worker pool.

1Why this exists

You already pay for a Gemini plan, and the web app will happily generate images and answer prompts all day. But the moment a cron job, a Cloudflare Worker, or a Telegram bot wants the same thing, you are back to buying metered API credits for capability you already own.

This service sits in between: a headless browser stays logged in, and your callers speak plain HTTP. One login, many consumers, no per-image billing. Output images get their visible watermark removed automatically, and the full-resolution file is pulled from the download button rather than the scaled-down page render.

2What you get

◇Drop-in SDK compatibility

/v1beta/models/{model}:generateContent speaks the Google GenAI wire format, so google-genai works by changing only base_url.

◈Reference-image editing

/api/edit uploads a reference image and returns the edited result — the path the SDK-compatible endpoint cannot express.

❋Multi-account pool

Each worker is its own browser and its own Google account. Requests round-robin across them, so no single plan hits its cap first.

◆Admin UI

Issue and revoke a key per caller, watch each worker's 24 h success rate, replay a request, and read the history without SSHing anywhere.

3Install

Use uv or pipx — a plain pip install gives Playwright no isolated environment and the browser download fails.

uv tool install gemini-web
gemini-web install          # downloads the Chromium build Playwright drives
gemini-web login            # opens a real browser; sign in to Google, press Enter

The login step needs a desktop session (or X forwarding over SSH) because you type the password yourself. Session state lands in ~/.gemini-web/profiles/ and is reused from then on. Adding a second account is gemini-web login -w 1, a third is -w 2, and so on.

gemini-web serve --host 0.0.0.0 --port 8070

There is a systemd unit in scripts/install-service.sh for the always-on case.

4Use

# chat
curl -X POST http://localhost:8070/api/chat \
  -H 'Content-Type: application/json' \
  -H 'x-goog-api-key: gmw_<your-key>' \
  -d '{"prompt": "explain quantum mechanics"}'

# text to image
curl -X POST http://localhost:8070/api/generate \
  -H 'Content-Type: application/json' \
  -H 'x-goog-api-key: gmw_<your-key>' \
  -d '{"prompt": "a poster of Taipei 101"}'

# image to image (reference photo in, edited image out)
curl -X POST http://localhost:8070/api/edit \
  -H 'Content-Type: application/json' \
  -H 'x-goog-api-key: gmw_<your-key>' \
  -d '{"prompt": "redraw as a 3x3 sticker sheet", "reference_image": "data:image/png;base64,..."}'

Or keep your existing SDK code and repoint it:

from google import genai

client = genai.Client(
    api_key="gmw_<your-key>",
    http_options={"api_version": "v1beta", "base_url": "http://localhost:8070"},
)
print(client.models.generate_content(model="gemini-2.5-flash", contents="Hello").text)
EndpointDoes
POST /api/chatText in, text out.
POST /api/generateText to image; returns base64, watermark already removed.
POST /api/editReference image + prompt to image. The only endpoint that uploads a file.
POST /v1beta/models/{model}:generateContentGoogle GenAI compatible; also has the streaming variant.
GET /api/healthPer-worker alive / logged-in / busy plus queue depth. No account names — it is public.

Once you have issued a single key, every endpoint above requires x-goog-api-key. Before that first key exists the service stays open, so local development is not blocked — but anything reachable from the internet should have a key from day one. The browser path spends the subscription quota of whichever account is logged in, and there is no per-key rate limit to save you.

5Multi-account worker pool

Set WORKER_COUNT=N and the service starts N browsers, each on its own profile directory and therefore its own Google account. Every worker handles one request at a time, so N is both your concurrency and the number of quotas you are spreading load across. Measured cost is roughly 0.6–0.7 GB of RAM per worker.

Dispatch modeBehaviourUse when
round-robin (default)Every request advances to the next worker.You want usage spread evenly so no single plan hits its cap first.
spilloverWorker 0 takes everything; the others only wake when it is busy.You want the backup accounts left alone.

The mode is a dropdown in the admin Overview, stored in the database and kept across restarts.

One thing worth knowing before you scale up: the same Gemini web app looks different to different accounts. Menu entries vary, an account using upload for the first time gets an extra consent dialog, and finished images arrive either as a blob or behind a download button. So "one worker is broken while the rest are fine" is normal, and login is usually not the culprit. The per-worker 24 h success rate in the admin exists precisely so that this is visible at a glance instead of hiding behind a fallback.

6What is in the admin

/admin, behind a password and an HMAC session cookie:

7Optional paid fallback

The browser path is single-threaded per account, takes 30–120 s, and breaks whenever Gemini reshuffles its UI. If that is too fragile for a given caller, set GEMINI_OFFICIAL_API_KEY and the service can hand the request to the official Gemini Developer API instead: about 10 s, and metered.

The paid path always requires a valid key, so an open endpoint can never be used to run up your bill. Do watch the flip side: in fallback mode a broken worker is invisible from outside — callers still get their image, just off the metered API. That is what the success-rate column in the admin is for.

!Disclaimer — personal / experimental use only

This was built for a private homelab. It is not affiliated with, endorsed by, or supported by Google. It automates the consumer Gemini web app with a real logged-in browser session, which is not a usage pattern Google documents or blesses.