Drive ChatGPT and Gemini from Python — no API keys, no billing, just the free web UI.
Project description
Drive ChatGPT and Gemini from Python — no API keys, no billing, just the free web UI.
ChatGPT and Gemini are incredibly capable — but their official APIs are expensive, and for many tasks you simply don't need them. If you want to run OCR on an image, generate artwork, extract text from a screenshot, or just ask a quick question in a script, paying per-token for API access is overkill when the free web UI can do the same thing.
Hermex lets you drive ChatGPT and Gemini from Python just like a human would: it opens a real Chrome browser, types your message, uploads your files, waits for the response, and hands it back to you as a Python object. No API keys, no billing, no rate-limit tiers.
from hermex import ChatGPT
response = ChatGPT.simple_query("What does this receipt say?", attachments=["receipt.jpg"])
print(response.text)
It uses undetected-chromedriver under the hood to avoid bot detection, and reuses a persistent browser profile so your login session survives across runs.
Installation
pip install hermex
Requires Python 3.11+ and Google Chrome 130+.
First-time setup
Hermex reuses a persistent Chrome profile so you only need to log in once:
from hermex import Gemini
Gemini.setup() # opens a browser — log in, browse briefly, then close the window
After setup, all future runs reuse the saved session automatically. Repeat this if your session expires.
Guest mode (no login) works for basic text queries on Gemini but file upload requires a logged-in session. ChatGPT works without login for all features including file upload.
Usage
Single query
from hermex import Gemini, ChatGPT
# Gemini
gemini = Gemini()
gemini.open_url()
response = gemini.query("Summarize the history of the internet.")
print(response.text)
gemini.close()
# ChatGPT
chatgpt = ChatGPT()
chatgpt.open_url()
response = chatgpt.query("Summarize the history of the internet.")
print(response.text)
chatgpt.close()
Attaching files
response = gemini.query(
"Describe what's in this image.",
attachments=["photo.jpg"],
)
print(response.text)
Supported formats: .jpg, .jpeg, .png, .gif, .webp, .pdf, .csv, .txt, .json. Each platform exposes its allowed types via Gemini.SUPPORTED_ATTACHMENTS and ChatGPT.SUPPORTED_ATTACHMENTS.
One-shot query
from hermex import Gemini, ChatGPT
response = Gemini.simple_query("What is the capital of France?")
print(response.text)
response = ChatGPT.simple_query("What is the capital of France?")
print(response.text)
# With an attachment
response = Gemini.simple_query("Describe this image.", attachments=["photo.jpg"])
print(response.text)
AssistantMessage object
query() and get_last_response() return an AssistantMessage dataclass:
@dataclass
class AssistantMessage:
text: str | None # plain text (or markdown if get_markdown=True)
image: Path | None # path to downloaded image, or None
API reference
Both Gemini and ChatGPT share the same interface — all methods below apply to both unless noted.
| Method | Description |
|---|---|
open_url(url, timeout) |
Open the chat interface in the browser |
send_message(message, submit, attachments, paste, fake_typing, typing_delay) |
Type and optionally submit a message |
query(message, timeout, attachments, paste, get_markdown, remove_watermark) |
Send a message, wait for the response, and return it |
get_last_response(get_markdown, remove_watermark) |
Retrieve the most recent response |
wait_until_idle(timeout) |
Block until the chatbot finishes generating |
get_state() |
Return the current UI state (State.IDLE, GENERATING, TYPING, UPLOADING) |
simple_query(prompt, attachments, timeout) |
Class method — open, query, close in one call |
short_wait() |
Sleep ~7 seconds |
long_wait() |
Sleep ~5 minutes |
refresh_page() |
Reload the current page |
close() |
Close the browser |
setup() |
One-time login setup (class method) |
See the full documentation for detailed guides on Gemini and ChatGPT.
Constructor options
Gemini(
chrome_version=None, # auto-detected from installed Chrome
download_dir=Path("."), # where generated images are saved
headless=False,
typing_delay=0.025, # seconds between keystrokes
disable_web_security=True,
)
# ChatGPT accepts the same parameters
Watermark removal
Gemini watermarks its generated images. Pass remove_watermark=True to strip it:
response = gemini.query("Generate an image of a sunset.", remove_watermark=True)
Notes
- Bot detection is mitigated through per-character typing delays, fake typing before paste, a persistent browser profile, and a spoofed user agent. Avoid running headless for sensitive sessions.
- Browser profile and session data are stored in the platform data directory (
~/Library/Application Support/hermexon macOS). - This library relies on browser automation and may break if the UI changes. Use responsibly and be aware of each platform's terms of service.
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 hermex-0.2.0.tar.gz.
File metadata
- Download URL: hermex-0.2.0.tar.gz
- Upload date:
- Size: 29.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b467c4da59b000eca844512ac3c42bbc4135fedbaee90ca209469a562a68090a
|
|
| MD5 |
63c251d83d459a765883481f6ab6346a
|
|
| BLAKE2b-256 |
a1f06b19332b1ca22f67851a32187718e09499dc017d175a09ccf6994c641d08
|
Provenance
The following attestation bundles were made for hermex-0.2.0.tar.gz:
Publisher:
publish.yml on pseudo-usama/hermex
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hermex-0.2.0.tar.gz -
Subject digest:
b467c4da59b000eca844512ac3c42bbc4135fedbaee90ca209469a562a68090a - Sigstore transparency entry: 1485935034
- Sigstore integration time:
-
Permalink:
pseudo-usama/hermex@063c0a99d17898bdb903f62f214fdb8664495604 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/pseudo-usama
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@063c0a99d17898bdb903f62f214fdb8664495604 -
Trigger Event:
push
-
Statement type:
File details
Details for the file hermex-0.2.0-py3-none-any.whl.
File metadata
- Download URL: hermex-0.2.0-py3-none-any.whl
- Upload date:
- Size: 29.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a74a7d48558352ee439091c4984f677d4176a7cc1803acfa3f3d0fbd141306b0
|
|
| MD5 |
8895813c79797259e0519797487f575b
|
|
| BLAKE2b-256 |
4fec2d26ac058d064c744fdd23c48adc290a40a9d31af6c17af44c46c484299f
|
Provenance
The following attestation bundles were made for hermex-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on pseudo-usama/hermex
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hermex-0.2.0-py3-none-any.whl -
Subject digest:
a74a7d48558352ee439091c4984f677d4176a7cc1803acfa3f3d0fbd141306b0 - Sigstore transparency entry: 1485935049
- Sigstore integration time:
-
Permalink:
pseudo-usama/hermex@063c0a99d17898bdb903f62f214fdb8664495604 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/pseudo-usama
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@063c0a99d17898bdb903f62f214fdb8664495604 -
Trigger Event:
push
-
Statement type: