Skip to main content

Gemini Banner

PyPI License Code style

Gemini Icon Gemini-API Extended

📦 Fork Notice: This is an extended fork of gemini-webapi with additional features. All credit for the core functionality goes to the original author UZQueen.

A reverse-engineered asynchronous python wrapper for Google Gemini web app (formerly Bard).

✨ Extended Features

This fork adds the following capabilities:

  • 🗑️ Delete Chat - Programmatically delete chat conversations to avoid leaving history traces

For the original features, see below.

Features

  • Persistent Cookies - Automatically refreshes cookies in background. Optimized for always-on services.
  • Image Generation - Natively supports generating and editing images with natural language.
  • System Prompt - Supports customizing model's system prompt with Gemini Gems.
  • Extension Support - Supports generating contents with Gemini extensions on, like YouTube and Gmail.
  • Classified Outputs - Categorizes texts, thoughts, web images and AI generated images in the response.
  • Official Flavor - Provides a simple and elegant interface inspired by Google Generative AI's official API.
  • Asynchronous - Utilizes asyncio to run generating tasks and return outputs efficiently.

Table of Contents

Installation

Install/update the package with pip.

pip install -U gemini-webapi-extended

Optionally, package offers a way to automatically import cookies from your local browser. To enable this feature, install browser-cookie3 as well. Supported platforms and browsers can be found here.

pip install -U browser-cookie3

Authentication

  • Go to https://gemini.google.com and login with your Google account
  • Press F12 for web inspector, go to Network tab and refresh the page
  • Click any request and copy cookie values of __Secure-1PSID and __Secure-1PSIDTS
services:
    main:
        environment:
            GEMINI_COOKIE_PATH: /tmp/gemini_webapi
        volumes:
            - ./gemini_cookies:/tmp/gemini_webapi

Usage

Initialization

Import required packages and initialize a client with your cookies obtained from the previous step. After a successful initialization, the API will automatically refresh __Secure-1PSIDTS in background as long as the process is alive.

import asyncio
from gemini_webapi import GeminiClient

# Replace "COOKIE VALUE HERE" with your actual cookie values.
# Leave Secure_1PSIDTS empty if it's not available for your account.
Secure_1PSID = "COOKIE VALUE HERE"
Secure_1PSIDTS = "COOKIE VALUE HERE"

async def main():
    # If browser-cookie3 is installed, simply use `client = GeminiClient()`
    client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxy=None)
    await client.init(timeout=30, auto_close=False, close_delay=300, auto_refresh=True)

asyncio.run(main())

Generate contents

Ask a single-turn question by calling GeminiClient.generate_content, which returns a gemini_webapi.ModelOutput object containing the generated text, images, thoughts, and conversation metadata.

async def main():
    response = await client.generate_content("Hello World!")
    print(response.text)

asyncio.run(main())

Generate contents with files

Gemini supports file input, including images and documents. Optionally, you can pass files as a list of paths in str or pathlib.Path to GeminiClient.generate_content together with text prompt.

async def main():
    response = await client.generate_content(
            "Introduce the contents of these two files. Is there any connection between them?",
            files=["assets/sample.pdf", Path("assets/banner.png")],
        )
    print(response.text)

asyncio.run(main())

Conversations across multiple turns

If you want to keep conversation continuous, please use GeminiClient.start_chat to create a gemini_webapi.ChatSession object and send messages through it. The conversation history will be automatically handled and get updated after each turn.

async def main():
    chat = client.start_chat()
    response1 = await chat.send_message(
        "Introduce the contents of these two files. Is there any connection between them?",
        files=["assets/sample.pdf", Path("assets/banner.png")],
    )
    print(response1.text)
    response2 = await chat.send_message(
        "Use image generation tool to modify the banner with another font and design."
    )
    print(response2.text, response2.images, sep="\n\n----------------------------------\n\n")

asyncio.run(main())

Continue previous conversations

To manually retrieve previous conversations, you can pass previous ChatSession's metadata to GeminiClient.start_chat when creating a new ChatSession. Alternatively, you can persist previous metadata to a file or db if you need to access them after the current Python process has exited.

async def main():
    # Start a new chat session
    chat = client.start_chat()
    response = await chat.send_message("Fine weather today")

    # Save chat's metadata
    previous_session = chat.metadata

    # Load the previous conversation
    previous_chat = client.start_chat(metadata=previous_session)
    response = await previous_chat.send_message("What was my previous message?")
    print(response)

asyncio.run(main())

Select language model

You can specify which language model to use by passing model argument to GeminiClient.generate_content or GeminiClient.start_chat. The default value is unspecified.

Currently available models (as of November 20, 2025):

  • unspecified - Default model
  • gemini-3.0-pro - Gemini 3.0 Pro
  • gemini-2.5-pro - Gemini 2.5 Pro
  • gemini-2.5-flash - Gemini 2.5 Flash
from gemini_webapi.constants import Model

async def main():
    response1 = await client.generate_content(
        "What's you language model version? Reply version number only.",
        model=Model.G_2_5_FLASH,
    )
    print(f"Model version ({Model.G_2_5_FLASH.model_name}): {response1.text}")

    chat = client.start_chat(model="gemini-2.5-pro")
    response2 = await chat.send_message("What's you language model version? Reply version number only.")
    print(f"Model version (gemini-2.5-pro): {response2.text}")

asyncio.run(main())

You can also pass custom model header strings directly to access models which are not listed above.

# "model_name" and "model_header" keys must be present
custom_model = {
    "model_name": "xxx",
    "model_header": {
        "x-goog-ext-525001261-jspb": "[1,null,null,null,'e6fa609c3fa255c0',null,null,null,[4]]"
    },
}

response = await client.generate_content(
    "What's your model version?",
    model=custom_model
)

Apply system prompt with Gemini Gems

System prompt can be applied to conversations via Gemini Gems. To use a gem, you can pass gem argument to GeminiClient.generate_content or GeminiClient.start_chat. gem can be either a string of gem id or a gemini_webapi.Gem object. Only one gem can be applied to a single conversation.

async def main():
    # Fetch all gems for the current account, including both predefined and user-created ones
    await client.fetch_gems(include_hidden=False)

    # Once fetched, gems will be cached in `GeminiClient.gems`
    gems = client.gems

    # Get the gem you want to use
    system_gems = gems.filter(predefined=True)
    coding_partner = system_gems.get(id="coding-partner")

    response1 = await client.generate_content(
        "what's your system prompt?",
        model=Model.G_2_5_FLASH,
        gem=coding_partner,
    )
    print(response1.text)

    # Another example with a user-created custom gem
    # Gem ids are consistent strings. Store them somewhere to avoid fetching gems every time
    your_gem = gems.get(name="Your Gem Name")
    your_gem_id = your_gem.id
    chat = client.start_chat(gem=your_gem_id)
    response2 = await chat.send_message("what's your system prompt?")
    print(response2)

Manage Custom Gems

You can create, update, and delete your custom gems programmatically with the API. Note that predefined system gems cannot be modified or deleted.

Create a custom gem

Create a new custom gem with a name, system prompt (instructions), and optional description:

async def main():
    # Create a new custom gem
    new_gem = await client.create_gem(
        name="Python Tutor",
        prompt="You are a helpful Python programming tutor.",
        description="A specialized gem for Python programming"
    )

    print(f"Custom gem created: {new_gem}")

    # Use the newly created gem in a conversation
    response = await client.generate_content(
        "Explain how list comprehensions work in Python",
        gem=new_gem
    )
    print(response.text)

asyncio.run(main())

Update an existing gem

async def main():
    # Get a custom gem (assuming you have one named "Python Tutor")
    await client.fetch_gems()
    python_tutor = client.gems.get(name="Python Tutor")

    # Update the gem with new instructions
    updated_gem = await client.update_gem(
        gem=python_tutor,  # Can also pass gem ID string
        name="Advanced Python Tutor",
        prompt="You are an expert Python programming tutor.",
        description="An advanced Python programming assistant"
    )

    print(f"Custom gem updated: {updated_gem}")

asyncio.run(main())

Delete a custom gem

async def main():
    # Get the gem to delete
    await client.fetch_gems()
    gem_to_delete = client.gems.get(name="Advanced Python Tutor")

    # Delete the gem
    await client.delete_gem(gem_to_delete)  # Can also pass gem ID string
    print(f"Custom gem deleted: {gem_to_delete.name}")

asyncio.run(main())

Retrieve model's thought process

When using models with thinking capabilities, the model's thought process will be populated in ModelOutput.thoughts.

async def main():
    response = await client.generate_content(
            "What's 1+1?", model="gemini-2.5-pro"
        )
    print(response.thoughts)
    print(response.text)

asyncio.run(main())

Retrieve images in response

Images in the API's output are stored as a list of gemini_webapi.Image objects. You can access the image title, URL, and description by calling Image.title, Image.url and Image.alt respectively.

async def main():
    response = await client.generate_content("Send me some pictures of cats")
    for image in response.images:
        print(image, "\n\n----------------------------------\n")

asyncio.run(main())

Generate and edit images

You can ask Gemini to generate and edit images with Nano Banana, Google's latest image model, simply by natural language.

You can save images returned from Gemini to local by calling Image.save(). Optionally, you can specify the file path and file name by passing path and filename arguments to the function and skip images with invalid file names by passing skip_invalid_filename=True. Works for both WebImage and GeneratedImage.

async def main():
    response = await client.generate_content("Generate some pictures of cats")
    for i, image in enumerate(response.images):
        await image.save(path="temp/", filename=f"cat_{i}.png", verbose=True)
        print(image, "\n\n----------------------------------\n")

asyncio.run(main())

Generate contents with Gemini extensions

After activating extensions for your account, you can access them in your prompts either by natural language or by starting your prompt with "@" followed by the extension keyword.

async def main():
    response1 = await client.generate_content("@Gmail What's the latest message in my mailbox?")
    print(response1, "\n\n----------------------------------\n")

    response2 = await client.generate_content("@Youtube What's the latest activity of Taylor Swift?")
    print(response2, "\n\n----------------------------------\n")

asyncio.run(main())

Check and switch to other reply candidates

A response from Gemini sometimes contains multiple reply candidates with different generated contents. You can check all candidates and choose one to continue the conversation. By default, the first candidate will be chosen.

async def main():
    # Start a conversation and list all reply candidates
    chat = client.start_chat()
    response = await chat.send_message("Recommend a science fiction book for me.")
    for candidate in response.candidates:
        print(candidate, "\n\n----------------------------------\n")

    if len(response.candidates) > 1:
        # Control the ongoing conversation flow by choosing candidate manually
        new_candidate = chat.choose_candidate(index=1)  # Choose the second candidate here
        followup_response = await chat.send_message("Tell me more about it.")  # Will generate contents based on the chosen candidate
        print(new_candidate, followup_response, sep="\n\n----------------------------------\n\n")
    else:
        print("Only one candidate available.")

asyncio.run(main())

Logging Configuration

This package uses loguru for logging, and exposes a function set_log_level to control log level. You can set log level to one of the following values: DEBUG, INFO, WARNING, ERROR and CRITICAL. The default value is INFO.

from gemini_webapi import set_log_level

set_log_level("DEBUG")

References

Google AI Studio

acheong08/Bard

Stargazers

Star History Chart

Metadata

Release files for gemini-webapi-extended 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gemini-webapi-extended 1.0.2
File Size Uploaded Provenance
gemini_webapi_extended-1.0.2.tar.gz 262.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for gemini-webapi-extended 1.0.2
File Interpreter ABI Platform
gemini_webapi_extended-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 321.0 kB

Release files / gemini_webapi_extended-1.0.2.tar.gz

Download URL gemini_webapi_extended-1.0.2.tar.gz
Size 262.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c860af728000c2ced4e111f223ab0e5bcee06fb45ad598975411d159f7692103
BLAKE2b-256 checksum
How to use checksums
d4a623a58df05dcea2617fe88b887bc7dc80ce4e8f260d4ac96aa657eca7aee2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release files / gemini_webapi_extended-1.0.2-py3-none-any.whl

Download URL gemini_webapi_extended-1.0.2-py3-none-any.whl
Size 58.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
999f4d3bc4c7da56e7c57f6abfaf0e6d68950be19d55f2380fbfe3993606368d
BLAKE2b-256 checksum
How to use checksums
ad36f71698f1ff075d435f1f986e342b398c91a4fea619c5f21daeb4ec3a891b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Security for gemini-webapi-extended 1.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.2 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page