Programmatic access to Google Gemini via web UI automation
Project description
๐ค Gemini Web UI Automation
Programmatic access to Google Gemini โ for free. No API key required.
Send prompts ยท Get responses ยท Model selection ยท Multi-turn conversations ยท Session persistence ยท Headless mode
โจ Features
- Free access โ Uses the Gemini web UI, no API key or billing required
- Model selection โ Switch between Fast, Thinking, and Pro models
- Async & Sync APIs โ Use
GeminiClient(async) orGeminiClientSync(sync) - Easy auth โ Log in via your regular browser cookies, no manual sign-in needed
- Session persistence โ Log in once, reuse the session across runs
- Headless mode โ Run without a display after initial setup
- Anti-detection โ System Chrome + stealth measures to bypass automation detection
- Multi-turn conversations โ Continue chats or start new ones
- Response streaming detection โ Waits for the full response automatically
๐ฆ Installation
# Clone the repo
git clone <your-repo-url>
cd gemini-api-again
# Install the package
pip install -e .
# Install Playwright browsers (first time only)
playwright install chromium
# Optional: Install cookie extraction support
pip install -e ".[cookies]"
Note: You also need Google Chrome installed on your system. The library uses your system Chrome to avoid Google's "browser not secure" detection.
# Ubuntu/Debian sudo apt install google-chrome-stable
๐ Quick Start
Option A โ Login from your browser (easiest, no manual sign-in)
Extract cookies from your regular browser โ no Playwright sign-in window needed:
# Install cookie extraction support
pip install -e ".[cookies]"
import asyncio
from gemini_webui import GeminiClient
async def main():
async with GeminiClient(headless=True, session_path="sessions/default.json") as client:
# Extract cookies from Chrome โ you must be logged into
# gemini.google.com in your Chrome browser
await client.login_from_browser(browser_name="chrome")
response = await client.send_prompt("Explain quantum computing in 3 sentences")
print(response.text)
asyncio.run(main())
Supported browsers: "chrome", "chromium", "firefox", "opera", "edge"
Option B โ Manual login via Playwright browser window
Run this once to log in and save your session. A Chrome window opens โ sign in with your Google account:
import asyncio
from gemini_webui import GeminiClient
async def setup():
async with GeminiClient(headless=False, session_path="sessions/default.json") as client:
await client.login() # Browser opens โ log in manually
# Session saved automatically on exit
asyncio.run(setup())
After that, use headless mode with the saved session:
async with GeminiClient(headless=True, session_path="sessions/default.json") as client:
await client.login()
response = await client.send_prompt("Explain quantum computing in 3 sentences")
print(response.text)
Output:
Quantum computing is a revolutionary technology that uses the principles of quantum
mechanics to process information in ways traditional computers cannot. Instead of
standard bits that represent either a 0 or a 1, quantum computers use qubits, which
can exist as both 0 and 1 simultaneously thanks to a property called superposition.
This unique ability allows them to perform complex calculations at unprecedented speeds.
Sync API (simpler, no async/await)
from gemini_webui import GeminiClientSync
with GeminiClientSync(headless=True, session_path="sessions/default.json") as client:
# Option A: From browser cookies
client.login_from_browser(browser_name="chrome")
# Option B: From saved session
# client.login()
response = client.send_prompt("What is the meaning of life?")
print(response.text)
๐ Model Selection
Switch between Gemini models โ Fast (Flash), Thinking, and Pro:
from gemini_webui import GeminiClientSync
with GeminiClientSync(headless=True, session_path="sessions/default.json") as client:
client.login()
# List available models
models = client.list_models()
print(models) # ['Fast', 'Thinking', 'Pro']
# Check current model
print(client.get_current_model()) # 'Fast'
# Switch to Pro for advanced tasks
client.set_model("pro")
response = client.send_prompt("Solve: integral of x^2 * e^x dx")
print(response.text)
# Switch to Thinking for complex reasoning
client.set_model("thinking")
response = client.send_prompt("What is 15! / (3! * 5! * 7!)?")
print(response.text)
# Switch back to Fast for quick answers
client.set_model("fast")
| Model | Alias | Description |
|---|---|---|
| Fast | "fast", "flash" |
Quick answers (Gemini Flash) |
| Thinking | "thinking" |
Solves complex problems with reasoning |
| Pro | "pro" |
Advanced math and code (Gemini Pro) |
๐ฌ Multi-Turn Conversations
from gemini_webui import GeminiClientSync
with GeminiClientSync(headless=True, session_path="sessions/default.json") as client:
client.login()
client.new_chat() # Start a fresh conversation
# First message
r1 = client.send_prompt("Tell me about Python in 2 sentences")
print(r1.text)
# Follow-up โ continues in the same conversation
r2 = client.send_prompt("What are its main advantages?")
print(r2.text)
# Start a completely new conversation
r3 = client.send_prompt("What is Rust?", new_chat=True)
print(r3.text)
๐๏ธ Configuration
client = GeminiClient(
headless=True, # Run browser in headless mode
timeout=120.0, # Response wait timeout (seconds)
session_path="sessions/default.json", # Session persistence path
browser_type="chromium", # Browser engine (ignored if use_system_chrome=True)
slow_mo=0, # Slow down actions by N ms (debugging)
request_delay=1.0, # Min seconds between prompts (avoid rate limits)
use_system_chrome=True, # Use system Chrome (avoids detection)
user_data_dir=None, # Persistent browser profile directory
)
๐ API Reference
GeminiClient (async)
| Method | Description |
|---|---|
GeminiClient(**kwargs) |
Create a new client. See Configuration for all options. |
async start() |
Start the browser |
async close() |
Close the browser and save session |
async login(session_path=None, login_timeout=120) |
Authenticate via Playwright browser window |
async login_from_browser(browser_name="chrome", session_path=None) |
Authenticate by extracting cookies from your browser |
async send_prompt(prompt, *, new_chat=False) โ GeminiResponse |
Send a prompt and get a response |
async new_chat() |
Start a new conversation |
async set_model(model) |
Switch model ("fast", "thinking", "pro") |
async get_current_model() โ str |
Get the currently selected model name |
async list_models() โ list[str] |
List available model names |
is_authenticated |
bool โ Check if logged in |
Supports async with context manager.
GeminiClientSync (sync)
Same API as GeminiClient but without async/await. Supports with context manager.
GeminiResponse
| Field | Type | Description |
|---|---|---|
text |
str |
The full response text |
prompt |
str |
The original prompt sent |
conversation_id |
str | None |
Conversation identifier from the URL |
model |
str | None |
Model name (if detectable) |
timestamp |
datetime |
When the response was received |
response = client.send_prompt("Hello")
print(response.text) # "Hi! How can I help you today?"
print(response.prompt) # "Hello"
print(response.conversation_id) # "6ea876188b87661c"
print(response.timestamp) # 2026-05-09 08:43:01
str(response) # "Hi! How can I help you today?"
Exceptions
| Exception | Description |
|---|---|
AuthenticationError |
Not logged in or session expired |
CaptchaError |
CAPTCHA challenge detected |
RateLimitError |
Google rate limiting detected |
ResponseTimeoutError |
Response not received within timeout |
SelectorNotFoundError |
UI element not found (UI may have changed) |
BrowserError |
Browser launch or runtime error |
๐ Authentication Methods
Method 1: login_from_browser() โ Extract cookies from your browser
The easiest way to authenticate. Extracts Google cookies from your regular browser and injects them into the Playwright context.
Requirements:
pip install -e ".[cookies]"
Prerequisites:
- You must be logged into
gemini.google.comin your chosen browser - Your browser must be closed (cookie files are locked while the browser is running)
Usage:
async with GeminiClient(headless=True, session_path="sessions/default.json") as client:
await client.login_from_browser(browser_name="chrome")
# Session is saved โ next time you can just use login()
Supported browsers: "chrome", "chromium", "firefox", "opera", "edge"
Method 2: login() โ Manual sign-in via Playwright window
Opens a Chrome window where you sign in manually. Works in headed mode only.
First time:
async with GeminiClient(headless=False, session_path="sessions/default.json") as client:
await client.login() # Sign in manually in the browser window
After that:
async with GeminiClient(headless=True, session_path="sessions/default.json") as client:
await client.login() # Uses saved session โ no manual sign-in needed
๐ก๏ธ Anti-Detection
Google blocks sign-in from browsers it detects as automated. This library uses several strategies to bypass that:
| Strategy | How |
|---|---|
| System Chrome | Uses your installed Chrome instead of Playwright's bundled Chromium |
| Persistent profile | Real user data directory โ looks like a normal browser |
| Stealth args | --disable-blink-features=AutomationControlled and others |
| JS patching | Removes navigator.webdriver flag |
| Realistic UA | Standard Chrome user agent string |
If you still get "This browser or app may not be secure"
- Use
login_from_browser()โ This bypasses the sign-in entirely by using your existing browser cookies - Install Google Chrome (not Chromium) โ Google is more lenient with its own browser
- Use a persistent user data directory โ Makes the browser look like a regular user's:
client = GeminiClient( headless=False, session_path="sessions/default.json", user_data_dir="/home/you/.config/gemini-automation-chrome", )
๐ง Troubleshooting
| Problem | Solution |
|---|---|
| "This browser or app may not be secure" | Use login_from_browser() instead, or see Anti-Detection |
| "No Google cookies found in chrome" | Log into gemini.google.com in your browser first, then close the browser |
| "Not logged in and running in headless mode" | Run with headless=False first, or use login_from_browser() |
| "CAPTCHA challenge detected" | Run with headless=False to solve it manually |
| "Rate limit detected" | Increase request_delay (e.g., 3.0) and wait before retrying |
| "Response did not complete within timeout" | Increase timeout (e.g., 300.0) |
| "No element found for any selector" | The Gemini UI changed โ update selectors in src/gemini_webui/selectors.py |
๐๏ธ How It Works
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Your Code โโโโโโถโ GeminiClient โโโโโโถโ Chrome Browser โ
โ โ โ โ โ (System Chrome) โ
โ send_prompt()โ โ โโโโโโโโโโ โ โ โ
โ new_chat() โ โ โ Auth โ โ โ gemini.google โ
โ login() โ โ โManager โ โ โ .com/app โ
โ โ โ โโโโโโโโโโ โ โ โ
โ โโโโโโโ โโโโโโโโโโ โโโโโโโ Response Text โ
โ โ โ โResponseโ โ โ โ
โ โ โ โParser โ โ โ โ
โ โ โ โโโโโโโโโโ โ โ โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโดโโโโโโโ
โ Session โ
โ Persistence โ
โ (cookies + โ
โ localStorage)โ
โโโโโโโโโโโโโโโ
- Browser launch โ Starts system Chrome with a persistent profile and stealth arguments
- Authentication โ Either extract cookies from your browser (
login_from_browser()) or sign in manually (login()) - Prompt sending โ Types into the contenteditable prompt area and clicks send
- Response detection โ Monitors the "Stop generating" button + content stability to detect when streaming is complete
- Text extraction โ Pulls the response text from the model's response container
โ ๏ธ Limitations
- Text prompts only โ No file/image uploads (yet)
- UI changes โ Google may update the Gemini UI, breaking selectors. The library uses fallback selector chains for resilience.
- Rate limits โ Google may throttle excessive usage. Use
request_delayto pace requests. - Terms of Service โ Automating the web UI may violate Google's ToS. Use responsibly.
๐ ๏ธ Development
# Install in development mode
pip install -e ".[dev]"
# Run linter
ruff check src/
# Format code
ruff format src/
๐ License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gemini_webui-0.1.0.tar.gz.
File metadata
- Download URL: gemini_webui-0.1.0.tar.gz
- Upload date:
- Size: 46.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
babf3c8e11b9c3aa0b3a65cfd6c9bd6a23fd6f74b1a7796a93c060eb328d831c
|
|
| MD5 |
4ccbcbbd91af31b6dd75eff90506661c
|
|
| BLAKE2b-256 |
0185565f062375b0083e3d584a9efdb1579fa1a33a09e0f80bd062b9495f58e5
|
File details
Details for the file gemini_webui-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gemini_webui-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2294b2308961e8a5cee5af3934cd0991fc582818c34366122c45992df81f7c34
|
|
| MD5 |
c2a107ea222af4812a06ed303e5cffb6
|
|
| BLAKE2b-256 |
e7237783f4901d42884a96d0af4015574464ff3bf0ffd296817af1433a2b4188
|