# Unofficial Gemini API

> Use this skill whenever the user mentions Google's Gemini in conjunction with unofficial, reverse-engineered, web-based, cookie-based, or free access methods. Also triggers for any implementation of Gemini using browser cookies, __Secure-1PSID authentication, or when the user wants to access Gemini's web API programmatically without using the official Google AI SDK. This includes requests for "free Gemini", "unofficial Gemini API", "Gemini without API key", "access Gemini via web", or similar patterns.

- Skill: `k1064190/unofficial-gemini-api` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add k1064190/unofficial-gemini-api`
- Raw SKILL.md: https://api.skillmd.com/api/skills/k1064190/unofficial-gemini-api/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: k1064190 (https://skillmd.com/u/k1064190)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/k1064190/unofficial-gemini-api

---


# Unofficial Gemini API (gemini_webapi)

When implementing code that uses Google's Gemini through unofficial means (reverse-engineered web API), use the `gemini_webapi` library from HanaokaYuzu/Gemini-API.

## Quick Reference

```python
from gemini_webapi import GeminiClient
import asyncio

# Authentication (see section below for details)
client = GeminiClient(secure_1psid, secure_1psidts)
await client.init()

# Single generation
response = await client.generate_content("Your prompt here")
print(response.text)

# Multi-turn conversation
chat = client.start_chat()
response = await chat.send_message("Hello!")
print(response.text)
```

## Authentication

### Option 1: Environment Variables (Recommended)

Store cookies in a `.env` file:

```bash
# .env
GEMINI_SECURE_1PSID=your_cookie_value_here
GEMINI_SECURE_1PSIDTS=your_cookie_value_here
```

Then load in Python:

```python
import os
from dotenv import load_dotenv
from gemini_webapi import GeminiClient
import asyncio

load_dotenv()

async def main():
    client = GeminiClient(
        secure_1psid=os.getenv("GEMINI_SECURE_1PSID"),
        secure_1psidts=os.getenv("GEMINI_SECURE_1PSIDTS")
    )
    await client.init()

asyncio.run(main())
```

### Option 2: Cookie File (Netscape format)

Export cookies from your browser in Netscape format using an extension like
"Get cookies.txt LOCALLY" or "Cookie-Editor" (choose Netscape export).

The file uses tab-separated fields: `domain`, `include_subdomains`, `path`, `secure`, `expires`, `name`, `value`:

```
# Netscape HTTP Cookie File
.google.com	TRUE	/	TRUE	1809084518	__Secure-1PSID	your_value_here
.google.com	TRUE	/	TRUE	1809084518	__Secure-1PSIDTS	your_value_here
```

Load from file:

```python
def load_cookies_from_netscape(filepath="cookie.txt"):
    """Load __Secure-1PSID and __Secure-1PSIDTS from a Netscape-format cookie.txt.

    Args:
        filepath (str): Path to the Netscape-format cookie file. Defaults to "cookie.txt".
    Returns:
        dict: Cookie name-value pairs for __Secure-1PSID and __Secure-1PSIDTS.
    """
    cookies = {}
    with open(filepath) as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            parts = line.split('\t')
            if len(parts) >= 7:
                name, value = parts[5], parts[6]
                if name in ("__Secure-1PSID", "__Secure-1PSIDTS"):
                    cookies[name] = value
    return cookies

cookies = load_cookies_from_netscape()
client = GeminiClient(cookies.get("__Secure-1PSID"), cookies.get("__Secure-1PSIDTS"))
```

### Option 3: Browser Cookie Auto-Import (Simplest)

Install with browser support:

```bash
pip install -U gemini_webapi[browser]
```

Then just initialize - cookies are automatically imported:

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    # Make sure you're logged in to https://gemini.google.com
    client = GeminiClient()
    await client.init()

asyncio.run(main())
```

### Getting Cookies Manually

If you need to get cookies manually:

1. Go to https://gemini.google.com and log in
2. Press F12 to open DevTools, go to the **Network** tab
3. Refresh the page
4. Click any request and look in the **Request Headers** > **Cookie**
5. Copy the values for `__Secure-1PSID` and `__Secure-1PSIDTS`

## Installation

```bash
pip install -U gemini_webapi
# Or for browser auto-cookie support:
pip install -U gemini_webapi[browser]
```

## Core Usage Patterns

### 1. Single Prompt Generation

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    response = await client.generate_content("Explain quantum computing")
    print(response.text)

asyncio.run(main())
```

### 2. Multi-Turn Conversation

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    chat = client.start_chat()

    response1 = await chat.send_message("What's the capital of France?")
    print(response1.text)

    response2 = await chat.send_message("What about Germany?")
    print(response2.text)

asyncio.run(main())
```

### 3. Streaming Mode (for real-time output)

`text_delta` contains only the new characters since the last yield. Also works in multi-turn via `chat.send_message_stream()`.

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # Single-turn streaming
    async for chunk in client.generate_content_stream("Tell me a long story"):
        print(chunk.text_delta, end="", flush=True)
    print()

    # Multi-turn streaming
    chat = client.start_chat()
    async for chunk in chat.send_message_stream("Continue the story"):
        print(chunk.text_delta, end="", flush=True)
    print()

asyncio.run(main())
```

### 4. File Upload (Images, Documents)

```python
from pathlib import Path
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    response = await client.generate_content(
        "What's in this image?",
        files=["screenshot.png", "document.pdf"]
    )
    print(response.text)

asyncio.run(main())
```

### 5. Select Model

Available models (as of 1.21.0): `gemini-3.1-pro`, `gemini-3.0-flash`, `gemini-3.0-flash-thinking`. `model` works on both `generate_content` and `start_chat`.

```python
from gemini_webapi import GeminiClient
from gemini_webapi.constants import Model
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # Via generate_content
    response = await client.generate_content(
        "Solve: What is the derivative of x^2?",
        model=Model.G_3_0_FLASH  # or string: "gemini-3.0-flash"
    )
    print(response.text)

    # Via start_chat
    chat = client.start_chat(model="gemini-3.1-pro")
    response2 = await chat.send_message("What is 247 * 313?")
    print(response2.text)

    # Custom model with raw header (for unlisted models)
    custom_model = {
        "model_name": "xxx",
        "model_header": {
            "x-goog-ext-525001261-jspb": "[1,null,null,null,'e6fa609c3fa255c0',null,null,null,[4]]"
        },
    }
    response3 = await client.generate_content("Hello", model=custom_model)

asyncio.run(main())
```

### 6. Using Gems (System Prompts)

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # NOTE: fetch_gems() is currently broken in gemini_webapi 1.21.0 due to a Google API
    # response format change (see https://github.com/HanaokaYuzu/Gemini-API/issues/279).
    # Awaiting upstream fix. The code below is correct; the failure is server-side.
    await client.fetch_gems(include_hidden=False)

    # Use a predefined gem
    coding_gem = client.gems.filter(predefined=True).get(id="coding-partner")
    response = await client.generate_content(
        "Help me debug this code",
        gem=coding_gem
    )
    print(response.text)

asyncio.run(main())
```

### 7. Image Generation

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # Ask to generate images (not just fetch web images)
    response = await client.generate_content("Generate 3 pictures of cats")

    # Save generated images
    for i, image in enumerate(response.images):
        await image.save(path="output/", filename=f"cat_{i}.png")
        print(f"Saved: {image.title}")

asyncio.run(main())
```

### 8. Using Extensions (@gmail, @youtube, etc.)

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # Must activate extensions on Gemini website first
    response = await client.generate_content("@gmail Summarize my latest email")
    print(response.text)

asyncio.run(main())
```

### 9. Retrieve Model Thoughts

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    response = await client.generate_content(
        "What is 247 * 313?",
        model="gemini-3.1-pro"
    )

    print("Thoughts:", response.thoughts)
    print("Answer:", response.text)

asyncio.run(main())
```

### 10. Persistent Chat (Save/Restore)

```python
from gemini_webapi import GeminiClient
import json
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # Start a chat
    chat = client.start_chat()
    await chat.send_message("My name is Alice")

    # Save chat metadata (in-memory or persist to file)
    previous_session = chat.metadata

    # To persist across processes, serialize as JSON:
    # with open("chat_state.json", "w") as f:
    #     json.dump(list(previous_session), f)
    # with open("chat_state.json") as f:
    #     previous_session = tuple(json.load(f))

    # Restore the conversation
    restored_chat = client.start_chat(metadata=previous_session)
    response = await restored_chat.send_message("What's my name?")
    print(response.text)  # Should remember "Alice"

asyncio.run(main())
```

### 11. Temporary Mode (No history saved)

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # This won't be saved in Gemini history
    response = await client.generate_content(
        "Secret message",
        temporary=True
    )
    print(response.text)

asyncio.run(main())
```

### 12. Managing Custom Gems

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # Create a custom gem
    new_gem = await client.create_gem(
        name="My Assistant",
        prompt="You are a helpful coding assistant specialized in Python.",
        description="Python coding helper"
    )
    print(f"Created gem: {new_gem.id}")

    # Use the custom gem
    response = await client.generate_content(
        "Write a Fibonacci function",
        gem=new_gem.id
    )
    print(response.text)

    # Update the gem
    updated = await client.update_gem(
        gem=new_gem,
        name="My Python Assistant",
        prompt="You are an expert Python tutor.",
        description="Advanced Python help"
    )

    # Delete the gem when done
    await client.delete_gem(updated)

asyncio.run(main())
```

### 13. Multiple Reply Candidates

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    chat = client.start_chat()
    response = await chat.send_message("Recommend a sci-fi book")

    # Check all candidates
    for i, candidate in enumerate(response.candidates):
        print(f"Candidate {i+1}:")
        print(candidate.text)
        print("---")

    # Choose a specific candidate to continue with
    if len(response.candidates) > 1:
        chat.choose_candidate(index=1)
        followup = await chat.send_message("Tell me more about that one")
        print(followup.text)

asyncio.run(main())
```

### 14. Read/Delete Chat History

```python
from gemini_webapi import GeminiClient
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init()

    # Start a chat
    chat = client.start_chat()
    await chat.send_message("What's 2+2?")

    # Read chat history
    history = await client.read_chat(chat.cid)
    for turn in history:
        print(f"User: {turn.user_prompt}")
        print(f"Assistant: {turn.assistant_response}")
        print("---")

    # Delete the chat
    await client.delete_chat(chat.cid)
    print(f"Deleted chat: {chat.cid}")

asyncio.run(main())
```

### 15. Async Chatbot Example

```python
from gemini_webapi import GeminiClient
import asyncio
from typing import Optional

class GeminiBot:
    def __init__(self, secure_1psid: str, secure_1psidts: str):
        self.client = GeminiClient(secure_1psid, secure_1psidts)
        self.chat: Optional[object] = None

    async def start(self):
        await self.client.init()
        self.chat = self.client.start_chat()

    async def message(self, prompt: str) -> str:
        if not self.chat:
            await self.start()
        response = await self.chat.send_message(prompt)
        return response.text

# Usage
async def main():
    bot = GeminiBot(secure_1psid, secure_1psidts)
    answer = await bot.message("Hello!")
    print(answer)

asyncio.run(main())
```

## Advanced Configuration

### Docker Cookie Persistence

```yaml
# docker-compose.yml
services:
  gemini-bot:
    environment:
      - GEMINI_COOKIE_PATH=/tmp/gemini_webapi
    volumes:
      - ./gemini_cookies:/tmp/gemini_webapi
```

### init() Parameters

```python
async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    await client.init(
        timeout=30,          # connection timeout in seconds
        auto_close=True,     # close client after inactivity (good for chatbots)
        close_delay=300,     # inactivity threshold in seconds
        auto_refresh=True,   # auto-refresh __Secure-1PSIDTS in background (default True)
    )

asyncio.run(main())
```

### Logging

```python
from gemini_webapi import set_log_level

set_log_level("DEBUG")  # DEBUG, INFO, WARNING, ERROR, CRITICAL
```

## Important Notes

1. **Python 3.10+ is required**

2. **Cookies auto-refresh** - The library automatically refreshes `__Secure-1PSIDTS` in the background as long as the process is running.

3. **Image generation availability** - Varies by region/account. Google restricts this feature in some regions.

4. **Extensions** - Must be activated on the Gemini website first before using via API.

5. **Rate limiting** - This uses the web interface, so be mindful of rate limits to avoid being flagged.

6. **Terms of Service** - This is an unofficial, reverse-engineered API. Use at your own risk and in accordance with Google's terms of service.

## Response Object Structure

When calling `generate_content()` or `send_message()`, you get a `ModelOutput` object with:

- `text`: The main response text
- `thoughts`: Model's internal thought process (if available)
- `images`: List of `Image` objects (web or generated)
- `candidates`: List of alternative response candidates
- `files`: Uploaded files referenced in the response

## Error Handling

```python
from gemini_webapi import GeminiClient
from gemini_webapi.exceptions import GeminiAPIError
import asyncio

async def main():
    client = GeminiClient(secure_1psid, secure_1psidts)
    try:
        await client.init(timeout=30)
        response = await client.generate_content("Hello")
        print(response.text)
    except GeminiAPIError as e:
        print(f"Gemini API error: {e}")
    except asyncio.TimeoutError:
        print("Connection timed out")

asyncio.run(main())
```

