๐ค gemi โ the simplest Python library for Google Gemini web app
Project description
๐ค gemi
The simplest Python library for Google Gemini โ no API key, no asyncio, just your browser cookies.
from gemi import Gemini
g = Gemini()
print(g.ask("What is the capital of France?"))
โจ Features
- No
asynciorequired โ everything works in regular Python scripts - Zero-config auth โ reads cookies from env vars,
.env, JSON file, or browser - Simple
ask()โ returns aResponsewith.text,.images,.thoughts - Streaming โ
g.stream()yields text as it arrives - Multi-turn chat โ
g.chat()remembers context across messages - Gems โ create, list, update, and delete system prompt presets
- Chat management โ list, read, and delete your Gemini history
- Deep Research โ
g.research("topic")โ full web-browsed report - Image generation โ
g.generate_image("a cat")+.save_images() - CLI tool โ
gemi ask "What is 2+2?" - Async support โ
async_ask(),async_stream()available too
๐ฆ Installation
pip install gemi
With automatic browser cookie detection:
pip install "gemi[browser]"
Requires Python 3.10+
๐ Authentication
You need your Gemini session cookies. There are several ways to provide them:
Option A โ Environment Variables (recommended)
export GEMINI_PSID="your __Secure-1PSID value"
export GEMINI_PSIDTS="your __Secure-1PSIDTS value"
Or in a .env file in your project directory:
GEMINI_PSID=your_value_here
GEMINI_PSIDTS=your_value_here
Then just use Gemini() โ it auto-discovers them.
Option B โ Direct
g = Gemini(psid="...", psidts="...")
Option C โ JSON Cookie File
g = Gemini(cookies_json="cookies.json")
Supports browser export formats (array of objects, flat dict, {"cookies": [...]} wrapper).
Option D โ Auto Browser (needs [browser] extra)
pip install "gemi[browser]"
Just be logged in to gemini.google.com in your browser.
How to get your cookies
- Go to gemini.google.com and log in
- Press
F12โ Network tab โ refresh the page - Click any request โ Cookies tab
- Copy
__Secure-1PSIDand__Secure-1PSIDTS
๐ Usage
Ask a question
from gemi import Gemini
g = Gemini()
response = g.ask("What is the capital of France?")
print(response.text) # "Paris"
print(response) # same โ str(response) returns the text
Streaming
for chunk in g.stream("Write a poem about the ocean"):
print(chunk, end="", flush=True)
print()
Multi-turn Chat
chat = g.chat(model="flash")
chat.say("My name is Alex and I love astronomy.")
reply = chat.say("What books would you recommend for me?")
print(reply)
# View conversation history
for role, text in chat.history:
print(f"[{role}] {text[:80]}")
# Save to file
chat.save("conversation.txt")
# Keep the chat ID to resume later
print(chat.chat_id) # โ "c_abc123..."
Resume a Previous Chat
chat = g.resume_chat("c_abc123")
chat.say("Continue from where we left off.")
Attach Files (Images, PDFs, Documents)
response = g.ask(
"Summarize this document and describe the image",
files=["report.pdf", "diagram.png"]
)
print(response.text)
Choose a Model
g.ask("Hello!", model="flash")
g.ask("Solve this math problem: ...", model="thinking")
g.ask("Write a detailed essay", model="pro")
| Alias | Model |
|---|---|
flash |
gemini-3-flash |
pro |
gemini-3-pro |
thinking |
gemini-3-flash-thinking |
flash-plus |
gemini-3-flash-plus |
pro-plus |
gemini-3-pro-plus |
thinking-plus |
gemini-3-flash-thinking-plus |
flash-advanced |
gemini-3-flash-advanced |
pro-advanced |
gemini-3-pro-advanced |
thinking-advanced |
gemini-3-flash-thinking-advanced |
Thinking Models
response = g.ask("What is 17 ร 23?", model="thinking")
print(response.thoughts) # reasoning steps
print(response.text) # final answer
Images
# Get web images in a response
response = g.ask("Show me pictures of the Eiffel Tower")
for img in response.web_images:
print(img.title, img.url)
# Generate AI images
response = g.generate_image("a futuristic city at sunset")
paths = response.save_images("./generated/")
print(paths) # ['/path/to/generated/gemi_0.png']
# One-liner: generate and auto-save
g.generate_image("a cute robot", save_to="./robots/")
Chat Management
# List recent conversations
for chat in g.list_chats():
print(chat["id"], chat["title"])
# Read a chat's full history
turns = g.read_chat("c_abc123")
for turn in turns:
print(f"[{turn['role']}] {turn['text'][:80]}")
# Delete one chat
g.delete_chat("c_abc123")
# Delete multiple chats at once
results = g.delete_chats(["c_abc", "c_def", "c_ghi"])
print(results) # {'c_abc': True, 'c_def': True, 'c_ghi': True}
Gems (System Prompt Presets)
# List all gems
for gem in g.get_gems():
kind = "system" if gem["predefined"] else "custom"
print(f"[{kind}] {gem['name']} โ {gem['id']}")
# Find a specific gem by name
gem = g.get_gem(name="Coding partner")
# Use a gem in a one-shot ask
response = g.ask("Review my code", gem=gem["id"])
# Use a gem in a multi-turn chat
chat = g.chat(gem=gem["id"])
chat.say("Help me write a Python function")
# Create a custom gem
my_gem = g.create_gem(
name="Python Teacher",
prompt="You are a patient Python teacher. Explain with simple examples.",
description="For learning Python",
)
print(my_gem["id"])
# Update a gem
g.update_gem(
gem_id=my_gem["id"],
name="Advanced Python Teacher",
prompt="You are an expert Python engineer. Focus on best practices.",
)
# Delete a gem
g.delete_gem(my_gem["id"])
Deep Research
# One-liner โ returns the full report as a string
report = g.research("Impact of large language models on software engineering")
print(report)
# With progress callback and save to file
def show_progress(status):
print(f" [{status['state']}]", end="\r", flush=True)
report = g.research(
"Climate change adaptation strategies 2025",
timeout=600,
on_status=show_progress,
)
with open("report.md", "w") as f:
f.write(report)
List Available Models
for m in g.list_models():
status = "โ" if m["available"] else "โ"
print(f"[{status}] {m['name']}: {m['display_name']}")
# Or just get available names
print(g.models)
Temporary Conversations (not saved to history)
response = g.ask("Secret question", temporary=True)
Context Manager
with Gemini() as g:
print(g.ask("Hello!"))
# client closes cleanly
Async Usage (for async code / Jupyter notebooks)
import asyncio
from gemi import Gemini
async def main():
g = Gemini()
async with g:
response = await g.async_ask("Hello!")
print(response.text)
async for chunk in g.async_stream("Tell me a story"):
print(chunk, end="", flush=True)
asyncio.run(main())
Account Diagnostics
info = g.inspect()
print("Deep Research:", info["deep_research_available"])
print("Status:", info["account_status"])
๐ฅ๏ธ CLI
After installing you get the gemi command:
# Ask a question
gemi ask "What is quantum entanglement?"
# Stream a response
gemi stream "Write a haiku about code"
# Interactive chat REPL
gemi chat
# Resume an existing chat
gemi chat c_abc123
# List recent conversations
gemi chats
# Read a chat's message history
gemi read c_abc123
# Delete one or more chats
gemi delete c_abc123 c_def456
# List available models
gemi models
# List gems
gemi gems
# Create a custom gem
gemi gem-create --name "Chef" --prompt "You are a master chef."
# Update a gem
gemi gem-update GEM_ID --name "Chef Pro" --prompt "Updated prompt"
# Delete a gem
gemi gem-delete GEM_ID
# Run a deep research task
gemi research "AI trends in 2025" --output report.md
# Check account feature availability
gemi inspect
CLI Authentication
# Via environment variables (recommended)
export GEMINI_PSID="..."
export GEMINI_PSIDTS="..."
gemi ask "Hello"
# Via JSON cookie file
gemi --cookies cookies.json ask "Hello"
# Via direct flags
gemi --psid "..." --psidts "..." ask "Hello"
# Via proxy
gemi --proxy http://127.0.0.1:8080 ask "Hello"
# Enable debug output
GEMINI_DEBUG=1 gemi ask "Hello"
๐ Project Structure
gemi/
โโโ src/
โ โโโ gemi/
โ โโโ __init__.py # Public API
โ โโโ client.py # Gemini class (main entry point)
โ โโโ chat.py # Chat class (multi-turn conversations)
โ โโโ response.py # Response wrapper
โ โโโ cookies.py # Cookie discovery from multiple sources
โ โโโ exceptions.py # Re-exported exceptions
โ โโโ cli.py # CLI (installed as `gemi` command)
โโโ pyproject.toml
โโโ README.md
โโโ LICENSE
โ Notes
- Uses browser session cookies โ unofficial reverse-engineered API
- Cookie tokens expire; re-export them if you get an
AuthError - Deep Research and image generation depend on your Google account type and region
- Some features require Gemini Advanced (paid subscription)
๐ License
Apache License 2.0. See LICENSE.
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 gemi-0.1.0.tar.gz.
File metadata
- Download URL: gemi-0.1.0.tar.gz
- Upload date:
- Size: 77.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
851e53f4e2c8c9bfd587bf90a712b538fef8dff526ad1d510be39914bee49e57
|
|
| MD5 |
2a0d9a1a4a49c37308e3edccfe327049
|
|
| BLAKE2b-256 |
3eebbc01a3d5cf60d46f7e3a6322238ea164c3672406f438a617b70016e51d58
|
File details
Details for the file gemi-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gemi-0.1.0-py3-none-any.whl
- Upload date:
- Size: 85.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26f8e2a24c53e7614ddecbf5593ef9c8a89ca7adfb3e375c80b528db106eb221
|
|
| MD5 |
818d645ee0f7c528b5c3285a2937853e
|
|
| BLAKE2b-256 |
d265a8252a529feefd58671c0ffccb11baad9dd4d5a02feff67ff7e0296348a4
|