Gemx
Drive the Gemini web UI from Python. A play on "Gemini" — Gemx treats
gemini.google.com as if it were an API, using Playwright to enter a prompt,
submit it, and capture the structured reply.
It exists because the obvious approaches don't work: Gemini's editor is
Quill, which keeps its own document model and ignores
DOM surgery, Playwright fill(), and synthetic input events — those leave the
model empty and the turn errors with "I encountered an error." Gemx injects
text via execCommand('insertText') (the same trusted input pipeline a manual
paste uses), reads the reply from message-content .markdown, and retains the
peak streamed text because Gemini re-mounts the response node mid-stream.
Install
uv add gemx
# Playwright needs a browser the first time:
uv run playwright install chromium
CLI
# JSON (default)
gemx "List 3 NBA teams as a JSON array"
# XML
gemx --format xml "Describe the solar system as XML"
# Plain text, reading the prompt from stdin
echo "Summarize the plot of Dune in one sentence" | gemx --format txt
# Watch the browser while it works
gemx --no-headless --verbose "Hello there"
The chosen --format (json, xml, or txt) is appended to the prompt as an
instruction and drives how Gemx parses the reply.
| Option | Description |
|---|---|
-f, --format {json,xml,txt} |
Output format (default: json). |
-p, --profile-dir PATH |
Chrome profile dir (default: ~/.gemx/profile). |
--no-headless |
Show the browser window. |
--browser-channel |
Playwright browser channel to launch; defaults to chrome with --no-headless. |
--response-timeout SECONDS |
Wait for a response to start (default: 180). |
--max-retries COUNT |
Retry transient Gemini response failures up to COUNT times. Omitted means no retries. |
-v, --verbose |
Log DEBUG progress to stderr. |
Library
import asyncio
from pathlib import Path
from gemx import Gemx, GemxConfig, OutputFormat
async def main() -> None:
config = GemxConfig(profile_dir=Path("~/.gemx/profile"), log_level="INFO")
async with Gemx(config) as gemx:
data = await gemx.ask("List 3 fruits as JSON", OutputFormat.JSON)
print(data)
asyncio.run(main())
Set log_level="DEBUG" for full browser automation diagnostics, or
log_level="WARNING" to emit only problems.
Authentication
Gemx drives a real, signed-in Gemini session. Point --profile-dir at a Chrome
profile that is already logged into your Google account (run once with
--no-headless to sign in); subsequent runs reuse that profile.
Development
uv sync
uv run ruff check .
uv run mypy
uv run pylint src/gemx
uv run pytest
The browser-console debugging scripts used to discover and verify the current
Gemini selectors live in tests/scripts/.
License
MIT © ra0x3 — Stonehedge Labs
Release files for gemx 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gemx-0.2.1.tar.gz | 21.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gemx-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.3 kB
Release files / gemx-0.2.1.tar.gz
| Download URL | gemx-0.2.1.tar.gz |
|---|---|
| Size | 21.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5cd03cc42a87912b7f6dfc3ff364c06639cfa214c16dc6170d8ed66b952de300
|
|
BLAKE2b-256 checksum How to use checksums |
2fdfcef19d9a861ca74b5929bfe35169315dfc23317cdd4d578b6eb8361380ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|
Release files / gemx-0.2.1-py3-none-any.whl
| Download URL | gemx-0.2.1-py3-none-any.whl |
|---|---|
| Size | 15.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a8fb4e674f7e31e6bf607783451fa2fc19f6a79fadf0acb507ceb0b01a48b1c6
|
|
BLAKE2b-256 checksum How to use checksums |
22ce6b72e9ca09754a346c78b9f946af3362044671518f354fc080c2c955e835
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|