Generate images and text using Google Gemini via your Pro subscription — no API key required. Now with OpenAI v1 API compatibility for drop-in replacement with existing OpenAI clients.
- OpenAI v1 API Compatible: Drop-in replacement for OpenAI API clients
- CLI: Quick generation from command line
- REST API: FastAPI server for programmatic access
- MCP Server: Model Context Protocol integration for AI agents
- Auto-Auth: Uses browser cookies from your Gemini session
- Streaming: Server-Sent Events (SSE) support for chat completions
- Python 3.12+
uvpackage manager- Gemini Pro subscription
- Logged into gemini.google.com in your browser
This README assumes the repo lives at $HOME/bin/gemini-proxy for service files and examples.
mkdir -p "$HOME/bin"
cd "$HOME/bin"./setup.sh# Generate text
uv run gemini-proxy generate "Tell me a joke"
# Generate image
uv run gemini-proxy generate "A cyberpunk banana" --image --out banana.png# REST API mode (default, port 8989)
uv run python main.py --mode api
# MCP mode (for AI agents)
uv run python main.py --mode mcpThis proxy implements OpenAI-compatible endpoints, allowing you to use existing OpenAI clients and tools with Gemini models.
| Endpoint | Method | Description |
|---|---|---|
/v1/models |
GET | List available models |
/v1/chat/completions |
POST | Text generation (streaming supported) |
/v1/images/generations |
POST | Image generation |
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8989/v1",
api_key="not-needed" # Authentication via Gemini cookies
)
# Chat completion
response = client.chat.completions.create(
model="gemini-pro",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
]
)
print(response.choices[0].message.content)
# Streaming
for chunk in client.chat.completions.create(
model="gemini-pro",
messages=[{"role": "user", "content": "Tell me a story"}],
stream=True
):
print(chunk.choices[0].delta.content or "", end="")
# Image generation
response = client.images.generate(
model="gemini-pro-vision",
prompt="A futuristic cityscape at sunset",
n=1,
size="1024x1024"
)
print(response.data[0].url)# List models
curl http://localhost:8989/v1/models
# Chat completion
curl http://localhost:8989/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-pro",
"messages": [{"role": "user", "content": "Hello!"}]
}'
# Image generation
curl http://localhost:8989/v1/images/generations \
-H "Content-Type: application/json" \
-d '{
"prompt": "A cute robot",
"response_format": "url"
}'| Model ID | Type | Description |
|---|---|---|
gemini-pro |
Text | Gemini Pro for text generation |
gemini-pro-vision |
Image | Gemini Pro for image generation |
The original endpoints are still available for backward compatibility:
POST /generate-image- Generate image from promptPOST /generate-text- Generate text from promptGET /images/{filename}- Retrieve saved imageGET /prompt-tips- Get tips for better image promptsGET /health- Health check
generate_image_from_prompt- Generate image via Geminilist_generated_images- List saved imagesget_image_prompt_tips- Get tips for better prompts
| Environment Variable | Description | Default |
|---|---|---|
SECURE_1PSID |
Gemini auth cookie (optional if using browser) | - |
SECURE_1PSIDTS |
Gemini auth cookie timestamp | - |
GEMINI_COOKIE_FILE |
Path to cookies.env file | ~/.config/gemini-proxy/cookies.env |
GEMINI_OUTPUT_DIR |
Directory for saved images | ./output |
LOG_LEVEL |
Logging level (DEBUG, INFO, WARNING, ERROR) | INFO |
The proxy authenticates with Gemini using browser cookies. Cookies are loaded in this order of precedence:
- Environment variables (
SECURE_1PSID,SECURE_1PSIDTS) - Cookie file (
~/.config/gemini-proxy/cookies.env) - Browser auto-detection (requires D-Bus access)
If you're running the service via systemd and using Snap-installed Brave browser, use the automated cookie refresh:
# Install the user systemd service
./scripts/install-cookie-refresh.shThis sets up:
- A user systemd service that extracts cookies from Brave
- A timer that refreshes cookies every 12 hours
- Automatic startup on login
If automated setup doesn't work, extract cookies manually:
# Run with D-Bus access (in your desktop session)
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" \
.venv/bin/python scripts/extract_cookies.pyThe script writes to ~/.config/gemini-proxy/cookies.env.
For systems without browser access, set cookies manually:
- Open https://gemini.google.com in your browser
- Press F12 → Application tab → Cookies → gemini.google.com
- Copy
__Secure-1PSIDand__Secure-1PSIDTSvalues - Create
~/.config/gemini-proxy/cookies.env:
mkdir -p ~/.config/gemini-proxy
cat > ~/.config/gemini-proxy/cookies.env << EOF
SECURE_1PSID=your_1psid_value_here
SECURE_1PSIDTS=your_1psidts_value_here
EOF
chmod 600 ~/.config/gemini-proxy/cookies.envOr set in the systemd service file via systemctl edit gemini-proxy:
[Service]
Environment="SECURE_1PSID=your_value"
Environment="SECURE_1PSIDTS=your_value"Error: "No valid cookies available"
- Cookies have expired or were not found
- Run:
systemctl --user start gemini-cookie-refresh - Or extract cookies manually (see above)
Error: "KeyError: DBUS_SESSION_BUS_ADDRESS"
- The script needs D-Bus access for keyring decryption
- Run from your desktop session, not a plain SSH shell
- Or use manual cookie configuration
Snap Brave Browser
- Cookies are at
~/snap/brave/current/.config/BraveSoftware/Brave-Browser/Default/Cookies - The extraction script handles this automatically
Service Timer Status
# Check timer status
systemctl --user status gemini-cookie-refresh.timer
# View extraction logs
journalctl --user -u gemini-cookie-refresh.service
# Manually trigger cookie refresh
systemctl --user start gemini-cookie-refresh.serviceError: "Failed to connect to bus: No medium found"
This error means user systemd isn't available in your current shell session. This happens when:
- Running from tmux/screen started before graphical login
- SSH session without proper PAM integration
- WSL or container environments
Solutions (in order of preference):
-
Run from desktop terminal (not tmux/SSH):
./scripts/install-cookie-refresh.sh
-
Enable user lingering (allows user services to run without graphical login):
sudo loginctl enable-linger $USER # Then log out and back in, or reboot
-
Use cron as fallback (only if options 1 & 2 don't work):
# Add to crontab (crontab -e) - runs every 12 hours
0 */12 * * * DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" $HOME/bin/gemini-proxy/.venv/bin/python $HOME/bin/gemini-proxy/scripts/extract_cookies.py
**Manual extraction** (when you just need to refresh once):
```bash
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" \
.venv/bin/python scripts/extract_cookies.py
sudo systemctl restart gemini-proxy
# Install the service
sudo cp deploy/gemini-proxy.service /etc/systemd/system/
sudo sed -i "s/^User=username$/User=$USER/" /etc/systemd/system/gemini-proxy.service
sudo sed -i "s/^Group=username$/Group=$USER/" /etc/systemd/system/gemini-proxy.service
sudo systemctl daemon-reload
sudo systemctl enable gemini-proxy
sudo systemctl start gemini-proxy
# Check status
sudo systemctl status gemini-proxy
journalctl -u gemini-proxy -fAdd to your MCP client configuration:
Claude Code (~/.claude/claude_desktop_config.json):
{
"mcpServers": {
"gemini-image": {
"command": "$HOME/bin/gemini-proxy/.venv/bin/python",
"args": ["main.py", "--mode", "mcp"],
"cwd": "$HOME/bin/gemini-proxy"
}
}
}Antigravity (~/.gemini/settings.json):
{
"mcpServers": {
"gemini-image": {
"command": "$HOME/bin/gemini-proxy/.venv/bin/python",
"args": ["main.py", "--mode", "mcp"],
"cwd": "$HOME/bin/gemini-proxy"
}
}
}# Run tests
uv run pytest tests/ -v
# Lint
uv run ruff check .
# Type check
uv run mypy main.pyThis project uses bd (Beads) for issue tracking. See AGENTS.md for workflow details.
All v1 endpoints return OpenAI-compatible error responses:
{
"error": {
"message": "Invalid request: messages is required",
"type": "invalid_request_error",
"param": "messages",
"code": "missing_required_parameter"
}
}MIT