[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-10712":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":25,"hasPages":23,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":42,"readmeContent":43,"aiSummary":44,"trendingCount":16,"starSnapshotCount":16,"syncStatus":45,"lastSyncTime":46,"discoverSource":47},10712,"Gemini-API","HanaokaYuzu\u002FGemini-API","HanaokaYuzu","✨ Reverse-engineered Python API for Google Gemini web app","https:\u002F\u002Fpypi.org\u002Fproject\u002Fgemini-webapi\u002F",null,"Python",3188,496,18,28,0,1,70,350,52,30.09,"GNU Affero General Public License v3.0",false,"master",true,[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"ai","api","async","bard","chatbot","gemini","generative-ai","google","google-gemini","image-generation","imagefx","llm","nano-banana","python","reverse-engineering","2026-06-12 02:02:25","\u003Cp align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FHanaokaYuzu\u002FGemini-API\u002Fmaster\u002Fassets\u002Fbanner.png\" width=\"55%\" alt=\"Gemini Banner\" align=\"center\">\n\u003C\u002Fp>\n\u003Cp align=\"center\">\n    \u003Ca href=\"https:\u002F\u002Fpypi.org\u002Fproject\u002Fgemini-webapi\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fpypi\u002Fv\u002Fgemini-webapi\" alt=\"PyPI\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fpepy.tech\u002Fproject\u002Fgemini-webapi\">\n        \u003Cimg src=\"https:\u002F\u002Fstatic.pepy.tech\u002Fbadge\u002Fgemini-webapi\" alt=\"Downloads\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FHanaokaYuzu\u002FGemini-API\u002Fnetwork\u002Fdependencies\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Flibrariesio\u002Fgithub\u002FHanaokaYuzu\u002FGemini-API\" alt=\"Dependencies\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FHanaokaYuzu\u002FGemini-API\u002Fblob\u002Fmaster\u002FLICENSE\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002FHanaokaYuzu\u002FGemini-API\" alt=\"License\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fpsf\u002Fblack\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fcode%20style-black-000000.svg\" alt=\"Code style\">\u003C\u002Fa>\n\u003C\u002Fp>\n\u003Cp align=\"center\">\n    \u003Ca href=\"https:\u002F\u002Fstar-history.com\u002F#HanaokaYuzu\u002FGemini-API\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002FHanaokaYuzu\u002FGemini-API?style=social\" alt=\"GitHub stars\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FHanaokaYuzu\u002FGemini-API\u002Fissues\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fissues\u002FHanaokaYuzu\u002FGemini-API?style=social&logo=github\" alt=\"GitHub issues\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FHanaokaYuzu\u002FGemini-API\u002Factions\u002Fworkflows\u002Fpypi-publish.yml\">\n        \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002FHanaokaYuzu\u002FGemini-API\u002Factions\u002Fworkflows\u002Fpypi-publish.yml\u002Fbadge.svg\" alt=\"CI\">\u003C\u002Fa>\n\u003C\u002Fp>\n\n# \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FHanaokaYuzu\u002FGemini-API\u002Fmaster\u002Fassets\u002Flogo.svg\" width=\"35px\" alt=\"Gemini Icon\" \u002F> Gemini-API\n\nA reverse-engineered asynchronous Python wrapper for the [Google Gemini](https:\u002F\u002Fgemini.google.com) web app (formerly Bard).\n\n## Features\n\n- **Persistent Cookies** - Automatically refreshes cookies in background. Optimized for always-on services.\n- **Image Generation** - Natively supports generating and editing images with natural language.\n- **Video & Audio Generation** - Supports generating videos and audio\u002Fmusic content natively.\n- **Deep Research** - Full deep research workflow with plan creation, status polling, and result retrieval.\n- **System Prompt** - Supports customizing the model's system prompt with [Gemini Gems](https:\u002F\u002Fgemini.google.com\u002Fgems\u002Fview).\n- **Extension Support** - Supports generating content with [Gemini extensions](https:\u002F\u002Fgemini.google.com\u002Fextensions), such as YouTube and Gmail.\n- **Classified Outputs** - Categorizes text, thoughts, images, videos, and audio in the response.\n- **Streaming Mode** - Supports stream generation, yielding partial outputs as they are generated.\n- **CLI Tool** - Standalone command-line interface for quick interactions.\n- **Official Flavor** - Provides a simple and elegant interface inspired by [Google Generative AI](https:\u002F\u002Fai.google.dev\u002Ftutorials\u002Fpython_quickstart)'s official API.\n- **Asynchronous** - Utilizes `asyncio` to run generation tasks and return outputs efficiently.\n\n## Table of Contents\n\n- [Features](#features)\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n- [Authentication](#authentication)\n- [Usage](#usage)\n  - [Initialization](#initialization)\n  - [Generate Content](#generate-content)\n  - [Generate Content with Files](#generate-content-with-files)\n  - [Conversations Across Multiple Turns](#conversations-across-multiple-turns)\n  - [Continue Previous Conversations](#continue-previous-conversations)\n  - [Read Conversation History](#read-conversation-history)\n  - [Delete Previous Conversations from Gemini History](#delete-previous-conversations-from-gemini-history)\n  - [Temporary Mode](#temporary-mode)\n  - [Streaming Mode](#streaming-mode)\n  - [Select Language Model](#select-language-model)\n  - [List Available Models](#list-available-models)\n  - [Apply System Prompt with Gemini Gems](#apply-system-prompt-with-gemini-gems)\n  - [Manage Custom Gems](#manage-custom-gems)\n    - [Create a Custom Gem](#create-a-custom-gem)\n    - [Update an Existing Gem](#update-an-existing-gem)\n    - [Delete a Custom Gem](#delete-a-custom-gem)\n  - [Retrieve Model's Thought Process](#retrieve-models-thought-process)\n  - [Retrieve Images in Response](#retrieve-images-in-response)\n  - [Generate and Edit Images](#generate-and-edit-images)\n  - [Retrieve Videos and Audio](#retrieve-videos-and-audio)\n  - [Generate Content with Gemini Extensions](#generate-content-with-gemini-extensions)\n  - [Check and Switch to Other Reply Candidates](#check-and-switch-to-other-reply-candidates)\n  - [Deep Research](#deep-research)\n  - [Logging Configuration](#logging-configuration)\n- [CLI Tool](#cli-tool)\n  - [Cookie Setup](#cookie-setup)\n  - [CLI Commands](#cli-commands)\n  - [Deep Research Workflow](#deep-research-workflow)\n- [References](#references)\n- [Stargazers](#stargazers)\n\n## Installation\n\n> [!NOTE]\n>\n> This package requires Python 3.10 or higher.\n\nInstall or update the package with pip.\n\n```sh\npip install -U gemini_webapi\n```\n\nOptionally, the package offers a way to automatically import cookies from your local browser via optional dependency `browser-cookie3`. To enable this feature, install `gemini_webapi[browser]` instead. Supported platforms and browsers can be found [here](https:\u002F\u002Fgithub.com\u002Fborisbabic\u002Fbrowser_cookie3?tab=readme-ov-file#contribute).\n\n```sh\npip install -U gemini_webapi[browser]\n```\n\n## Authentication\n\n> [!TIP]\n>\n> If `browser-cookie3` is installed, you can skip this step and go directly to the [usage](#usage) section. Just make sure you are logged in to \u003Chttps:\u002F\u002Fgemini.google.com> in your browser.\n\n- Go to \u003Chttps:\u002F\u002Fgemini.google.com> and log in with your Google account\n- Press F12 to open the web inspector, go to the `Network` tab, and refresh the page\n- Click any request and copy the cookie values of `__Secure-1PSID` and `__Secure-1PSIDTS`\n\n> [!NOTE]\n>\n> If your application is deployed in a containerized environment (e.g. Docker), you may want to persist the cookies with a volume to avoid re-authentication every time the container rebuilds. You can set `GEMINI_COOKIE_PATH` environment variable to specify the path where auto-refreshed cookies are stored. Make sure the path is writable by the application.\n>\n> Here's part of a sample `docker-compose.yml` file:\n\n```yaml\nservices:\n    main:\n        environment:\n            GEMINI_COOKIE_PATH: \u002Ftmp\u002Fgemini_webapi\n        volumes:\n            - .\u002Fgemini_cookies:\u002Ftmp\u002Fgemini_webapi\n```\n\n> [!NOTE]\n>\n> The API's auto-cookie-refreshing feature doesn't require `browser-cookie3` and is enabled by default. It allows you to keep the API service running without worrying about cookie expiration.\n>\n> This feature may require you to log in to your Google account again in the browser. This is expected behavior and won't affect the API's functionality.\n>\n> To avoid this, it's recommended to get cookies from a separate browser session and close it as soon as possible for best utilization (e.g. a fresh login in the browser's private mode). More details can be found [here](https:\u002F\u002Fgithub.com\u002FHanaokaYuzu\u002FGemini-API\u002Fissues\u002F6).\n\n## Usage\n\n### Initialization\n\nImport the required packages and initialize a client with your cookies from the previous step. After successful initialization, the API will automatically refresh `__Secure-1PSIDTS` in the background as long as the process is alive.\n\n```python\nimport asyncio\nfrom gemini_webapi import GeminiClient\n\n# Replace \"COOKIE VALUE HERE\" with your actual cookie values.\n# Leave Secure_1PSIDTS empty if it's not available for your account.\nSecure_1PSID = \"COOKIE VALUE HERE\"\nSecure_1PSIDTS = \"COOKIE VALUE HERE\"\n\nasync def main():\n    # If browser-cookie3 is installed, simply use `client = GeminiClient()`\n    client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxy=None)\n    await client.init(timeout=30, auto_close=False, close_delay=300, auto_refresh=True)\n\nasyncio.run(main())\n```\n\n> [!TIP]\n>\n> `auto_close` and `close_delay` are optional arguments for automatically closing the client after a certain period of inactivity. This feature is disabled by default. In an always-on service like a chatbot, it's recommended to set `auto_close` to `True` with a reasonable `close_delay` value for better resource management.\n\n### Generate Content\n\nAsk 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.\n\n```python\nasync def main():\n    response = await client.generate_content(\"Hello World!\")\n    print(response.text)\n\nasyncio.run(main())\n```\n\n> [!TIP]\n>\n> Simply use `print(response)` to get the same output if you just want to see the response text.\n\n### Generate Content with Files\n\nGemini 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 a text prompt.\n\n```python\nasync def main():\n    response = await client.generate_content(\n            \"Introduce the contents of these two files. Is there any connection between them?\",\n            files=[\"assets\u002Fsample.pdf\", Path(\"assets\u002Fbanner.png\")],\n        )\n    print(response.text)\n\nasyncio.run(main())\n```\n\n### Conversations Across Multiple Turns\n\nIf you want to keep the conversation continuous, use `GeminiClient.start_chat` to create a `gemini_webapi.ChatSession` object and send messages through it. The conversation history will be handled automatically and updated after each turn.\n\n```python\nasync def main():\n    chat = client.start_chat()\n    response1 = await chat.send_message(\n        \"Introduce the contents of these two files. Is there any connection between them?\",\n        files=[\"assets\u002Fsample.pdf\", Path(\"assets\u002Fbanner.png\")],\n    )\n    print(response1.text)\n    response2 = await chat.send_message(\n        \"Use image generation tool to modify the banner with another font and design.\"\n    )\n    print(response2.text, response2.images, sep=\"\\n\\n----------------------------------\\n\\n\")\n\nasyncio.run(main())\n```\n\n> [!TIP]\n>\n> Same as `GeminiClient.generate_content`, `ChatSession.send_message` also accepts `image` as an optional argument.\n\n### Continue Previous Conversations\n\nTo manually retrieve previous conversations, you can pass a previous `ChatSession`'s metadata to `GeminiClient.start_chat` when creating a new `ChatSession`. Alternatively, you can persist previous metadata to a file or database if you need to access it after the current Python process has exited.\n\n```python\nasync def main():\n    # Start a new chat session\n    chat = client.start_chat()\n    response = await chat.send_message(\"Fine weather today\")\n\n    # Save chat's metadata\n    previous_session = chat.metadata\n\n    # Load the previous conversation\n    previous_chat = client.start_chat(metadata=previous_session)\n    response = await previous_chat.send_message(\"What was my previous message?\")\n    print(response)\n\nasyncio.run(main())\n```\n\n### Read Conversation History\n\nYou can read the conversation history of a specific chat by calling `GeminiClient.read_chat` with the chat ID. It returns a `ChatHistory` object containing a list of `ChatTurn` objects ordered from newest to oldest.\n\n```python\nasync def main():\n    chat = client.start_chat()\n    await chat.send_message(\"What is the capital of France?\")\n\n    # Read the chat history\n    history = await client.read_chat(chat.cid)\n    if history:\n        for turn in history.turns:\n            print(f\"[{turn.role.upper()}] {turn.text}\")\n            print(\"\\n----------------------------------\\n\")\n\nasyncio.run(main())\n```\n\nTo list all recent chats, use `GeminiClient.list_chats`:\n\n```python\nasync def main():\n    chats = client.list_chats()\n    if chats:\n        for chat_info in chats:\n            print(f\"{chat_info.cid}: {chat_info.title}\")\n\nasyncio.run(main())\n```\n\n### Delete Previous Conversations from Gemini History\n\nYou can delete a specific chat from Gemini history on the server by calling `GeminiClient.delete_chat` with the chat ID.\n\n```python\nasync def main():\n    # Start a new chat session\n    chat = client.start_chat()\n    await chat.send_message(\"This is a temporary conversation.\")\n\n    # Delete the chat\n    await client.delete_chat(chat.cid)\n    print(f\"Chat deleted: {chat.cid}\")\n\nasyncio.run(main())\n```\n\n### Temporary Mode\n\nYou can start a temporary chat by passing `temporary=True` to `GeminiClient.generate_content` or `ChatSession.send_message`. Temporary chats won't be saved in Gemini history.\n\n```python\nasync def main():\n    response = await client.generate_content(\"Hello World!\", temporary=True)\n    print(response.text, \"\\n\\n----------------------------------\\n\\n\")\n\n    chat = client.start_chat()\n    await chat.send_message(\"Fine weather today\", temporary=False)\n    response2 = await chat.send_message(\"What's my last message?\", temporary=True)\n    print(response2.text)\n\nasyncio.run(main())\n```\n\n### Streaming Mode\n\nFor longer responses, you can use streaming mode to receive partial outputs as they are generated. This provides a more responsive user experience, especially for real-time applications like chatbots.\n\nThe `generate_content_stream` method yields `ModelOutput` objects where the `text_delta` attribute contains only the **new characters** received since the last yield, making it easy to display incremental updates.\n\n```python\nasync def main():\n    async for chunk in client.generate_content_stream(\n        \"What's the difference between 'await' and 'async for'?\"\n    ):\n        print(chunk.text_delta, end=\"\", flush=True)\n\n    print()\n\nasyncio.run(main())\n```\n\n> [!TIP]\n>\n> Streaming mode accepts the same arguments as `generate_content`. You can also use streaming mode in multi-turn conversations with `ChatSession.send_message_stream`.\n\n### Select Language Model\n\nYou can specify which language model to use by passing the `model` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. The default value is `unspecified`.\n\nAvailable models are discovered **dynamically** at init time based on your account tier. The `Model` enum provides convenient shortcuts.\n\n```python\nfrom gemini_webapi.constants import Model\n\nasync def main():\n    response1 = await client.generate_content(\n        \"What's your language model version? Reply with the version number only.\",\n        model=Model.BASIC_FLASH,\n    )\n    print(f\"Model version ({Model.BASIC_FLASH.model_name}): {response1.text}\")\n\n    chat = client.start_chat(model=\"gemini-3-pro\")\n    response2 = await chat.send_message(\"What's your language model version? Reply with the version number only.\")\n    print(f\"Model version (gemini-3-pro): {response2.text}\")\n\nasyncio.run(main())\n```\n\nYou can also pass custom model header strings directly to access models that are not listed above.\n\n```python\n# \"model_name\" and \"model_header\" keys must be present\ncustom_model = {\n    \"model_name\": \"xxx\",\n    \"model_header\": {\n        \"x-goog-ext-525001261-jspb\": \"[1,null,null,null,'e6fa609c3fa255c0',null,null,null,[4]]\"\n    },\n}\n\nresponse = await client.generate_content(\n    \"What's your model version?\",\n    model=custom_model\n)\n```\n\n### List Available Models\n\nThe client dynamically discovers which models are available for your account at initialization. Use `GeminiClient.list_models` to see all available models and their details.\n\n```python\nasync def main():\n    await client.init()  # Make sure the client is initialized first\n    models = client.list_models()\n    if models:\n        for model in models:\n            print(f\"{model.display_name}: {model.model_name}\")\n\nasyncio.run(main())\n```\n\n### Apply System Prompt with Gemini Gems\n\nSystem prompts can be applied to conversations via [Gemini Gems](https:\u002F\u002Fgemini.google.com\u002Fgems\u002Fview). To use a gem, you can pass the `gem` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. `gem` can be either a gem ID string or a `gemini_webapi.Gem` object. Only one gem can be applied to a single conversation.\n\n> [!TIP]\n>\n> There are some system predefined gems that are not shown to users by default (and therefore may not work properly). Use `client.fetch_gems(include_hidden=True)` to include them in the fetch result.\n\n```python\nasync def main():\n    # Fetch all gems for the current account, including both predefined and user-created ones\n    await client.fetch_gems(include_hidden=False)\n\n    # Once fetched, gems will be cached in `GeminiClient.gems`\n    gems = client.gems\n\n    # Get the gem you want to use\n    system_gems = gems.filter(predefined=True)\n    coding_partner = system_gems.get(id=\"coding-partner\")\n\n    response1 = await client.generate_content(\n        \"What's your system prompt?\",\n        gem=coding_partner,\n    )\n    print(response1.text)\n\n    # Another example with a user-created custom gem\n    # Gem ids are consistent strings. Store them somewhere to avoid fetching gems every time\n    your_gem = gems.get(name=\"Your Gem Name\")\n    your_gem_id = your_gem.id\n    chat = client.start_chat(gem=your_gem_id)\n    response2 = await chat.send_message(\"What's your system prompt?\")\n    print(response2)\n```\n\n### Manage Custom Gems\n\nYou can create, update, and delete your custom gems programmatically with the API. Note that predefined system gems cannot be modified or deleted.\n\n#### Create a Custom Gem\n\nCreate a new custom gem with a name, system prompt (instructions), and optional description:\n\n```python\nasync def main():\n    # Create a new custom gem\n    new_gem = await client.create_gem(\n        name=\"Python Tutor\",\n        prompt=\"You are a helpful Python programming tutor.\",\n        description=\"A specialized gem for Python programming\"\n    )\n\n    print(f\"Custom gem created: {new_gem}\")\n\n    # Use the newly created gem in a conversation\n    response = await client.generate_content(\n        \"Explain how list comprehensions work in Python\",\n        gem=new_gem\n    )\n    print(response.text)\n\nasyncio.run(main())\n```\n\n#### Update an Existing Gem\n\n> [!NOTE]\n>\n> When updating a gem, you must provide all parameters (name, prompt, description) even if you only want to change one of them.\n\n```python\nasync def main():\n    # Get a custom gem (assuming you have one named \"Python Tutor\")\n    await client.fetch_gems()\n    python_tutor = client.gems.get(name=\"Python Tutor\")\n\n    # Update the gem with new instructions\n    updated_gem = await client.update_gem(\n        gem=python_tutor,  # Can also pass gem ID string\n        name=\"Advanced Python Tutor\",\n        prompt=\"You are an expert Python programming tutor.\",\n        description=\"An advanced Python programming assistant\"\n    )\n\n    print(f\"Custom gem updated: {updated_gem}\")\n\nasyncio.run(main())\n```\n\n#### Delete a Custom Gem\n\n```python\nasync def main():\n    # Get the gem to delete\n    await client.fetch_gems()\n    gem_to_delete = client.gems.get(name=\"Advanced Python Tutor\")\n\n    # Delete the gem\n    await client.delete_gem(gem_to_delete)  # Can also pass gem ID string\n    print(f\"Custom gem deleted: {gem_to_delete.name}\")\n\nasyncio.run(main())\n```\n\n### Retrieve Model's Thought Process\n\nWhen using models with thinking capabilities, the model's thought process will be populated in `ModelOutput.thoughts`.\n\n```python\nasync def main():\n    response = await client.generate_content(\n            \"What's 1+1?\", model=\"gemini-3-pro\"\n        )\n    print(response.thoughts)\n    print(response.text)\n\nasyncio.run(main())\n```\n\n### Retrieve Images in Response\n\nImages 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.\n\n```python\nasync def main():\n    response = await client.generate_content(\"Send me some pictures of cats\")\n    for image in response.images:\n        print(image, \"\\n\\n----------------------------------\\n\\n\")\n\nasyncio.run(main())\n```\n\n### Generate and Edit Images\n\nYou can ask Gemini to generate and edit images with Nano Banana, Google's latest image model, using natural language.\n\n> [!IMPORTANT]\n>\n> Google has some limitations on Gemini's image generation feature, so availability may vary by region\u002Faccount. Here's a summary copied from [official documentation](https:\u002F\u002Fsupport.google.com\u002Fgemini\u002Fanswer\u002F14286560) (as of Sep 10, 2025):\n>\n> > This feature's availability in any specific Gemini app is also limited to the supported languages and countries of that app.\n> >\n> > For now, this feature isn't available to users under 18.\n> >\n> > To use this feature, you must be signed in to Gemini Apps.\n\nYou can save images returned from Gemini locally by calling `Image.save()`. Optionally, you can specify the file path and file name by passing `path` and `filename` arguments to the function. This works for both `WebImage` and `GeneratedImage`.\n\n```python\nasync def main():\n    response = await client.generate_content(\"Generate some pictures of cats\")\n    for i, image in enumerate(response.images):\n        await image.save(path=\"temp\u002F\", filename=f\"cat_{i}.png\", verbose=True)\n        print(image, \"\\n\\n----------------------------------\\n\\n\")\n\nasyncio.run(main())\n```\n\n> [!NOTE]\n>\n> By default, when asked to send images (like in the previous example), Gemini will send images fetched from the web instead of generating images with an AI model, unless you specifically ask it to \"generate\" images in your prompt. In this package, web images and generated images are treated differently as `WebImage` and `GeneratedImage`, and are automatically categorized in the output.\n\n### Retrieve Videos and Audio\n\nGemini can generate videos and audio\u002Fmusic content. These are returned as `GeneratedVideo` and `GeneratedMedia` objects in `ModelOutput.videos` and `ModelOutput.media` respectively. You can save them to disk just like images.\n\n> [!NOTE]\n>\n> You may need an active subscription to access Gemini's video and audio generation features.\n\n```python\nasync def main():\n    response = await client.generate_content(\"Generate a short video of a cat playing\")\n\n    # Save generated videos\n    for video in response.videos:\n        result = await video.save(path=\"temp\u002F\", verbose=True)\n        print(f\"Video saved: {result}\")\n\n    # Save generated media (audio\u002Fmusic)\n    for media in response.media:\n        result = await media.save(path=\"temp\u002F\", verbose=True)\n        print(f\"Media saved: {result}\")\n\nasyncio.run(main())\n```\n\n> [!NOTE]\n>\n> `GeneratedMedia.save()` accepts a `download_type` parameter: `\"audio\"`, `\"video\"`, or `\"both\"` (default). Generated video\u002Faudio may take time to render — the save method will poll automatically until the content is ready.\n\n### Generate Content with Gemini Extensions\n\n> [!IMPORTANT]\n>\n> To access Gemini extensions in the API, you must activate them on the [Gemini website](https:\u002F\u002Fgemini.google.com\u002Fextensions) first. As with image generation, Google also has limitations on the availability of Gemini extensions. Here's a summary copied from [official documentation](https:\u002F\u002Fsupport.google.com\u002Fgemini\u002Fanswer\u002F13695044) (as of March 19, 2025):\n>\n> > To connect apps to Gemini, you must have​​​​ Gemini Apps Activity on.\n> >\n> > To use this feature, you must be signed in to Gemini Apps.\n> >\n> > Important: If you're under 18, Google Workspace and Maps apps currently only work with English prompts in Gemini.\n\nAfter activating extensions for your account, you can access them in your prompts either in natural language or by starting your prompt with \"@\" followed by the extension keyword.\n\n```python\nasync def main():\n    response1 = await client.generate_content(\"@Gmail What's the latest message in my mailbox?\")\n    print(response1, \"\\n\\n----------------------------------\\n\\n\")\n\n    response2 = await client.generate_content(\"@Youtube What's the latest activity of Taylor Swift?\")\n    print(response2, \"\\n\\n----------------------------------\\n\\n\")\n\nasyncio.run(main())\n```\n\n> [!NOTE]\n>\n> For region availability, your Google account's **preferred language** only needs to be set to one of the three supported languages listed above. You can change your language settings [here](https:\u002F\u002Fmyaccount.google.com\u002Flanguage).\n\n### Check and Switch to Other Reply Candidates\n\nA response from Gemini sometimes contains multiple reply candidates with different generated content. You can check all candidates and choose one to continue the conversation. By default, the first candidate is chosen.\n\n```python\nasync def main():\n    # Start a conversation and list all reply candidates\n    chat = client.start_chat()\n    response = await chat.send_message(\"Recommend a science fiction book for me.\")\n    for candidate in response.candidates:\n        print(candidate, \"\\n\\n----------------------------------\\n\\n\")\n\n    if len(response.candidates) > 1:\n        # Control the ongoing conversation flow by choosing candidate manually\n        new_candidate = chat.choose_candidate(index=1)  # Choose the second candidate here\n        followup_response = await chat.send_message(\"Tell me more about it.\")  # Will generate content based on the chosen candidate\n        print(new_candidate, followup_response, sep=\"\\n\\n----------------------------------\\n\\n\")\n    else:\n        print(\"Only one candidate available.\")\n\nasyncio.run(main())\n```\n\n### Deep Research\n\nGemini's deep research feature is an autonomous research agent that browses the web, analyzes sources, and produces a comprehensive report. You can access it programmatically through the API.\n\n> [!NOTE]\n>\n> You may need an active subscription to access Gemini's deep research feature.\n\n**Quick one-call method:**\n\n```python\nasync def main():\n    result = await client.deep_research(\n        \"Compare the top 3 cloud providers and their AI offerings\",\n        poll_interval=10.0,\n        timeout=600.0,\n    )\n    print(f\"Done: {result.done}\")\n    print(result.text)\n\nasyncio.run(main())\n```\n\n**Step-by-step workflow** for more control:\n\n```python\nasync def main():\n    # Step 1: Create a research plan\n    plan = await client.create_deep_research_plan(\n        \"What are the latest advancements in quantum computing?\"\n    )\n    print(f\"Title: {plan.title}\")\n    print(f\"ETA: {plan.eta_text}\")\n    for step in plan.steps:\n        print(f\"  - {step}\")\n\n    # Step 2: Start the research\n    await client.start_deep_research(plan)\n\n    # Step 3: Poll for completion\n    result = await client.wait_for_deep_research(\n        plan,\n        poll_interval=10.0,\n        timeout=600.0,\n        on_status=lambda s: print(f\"Status: {s.state}\"),\n    )\n\n    print(result.text)\n\nasyncio.run(main())\n```\n\n### Logging Configuration\n\nThis package uses [loguru](https:\u002F\u002Floguru.readthedocs.io\u002Fen\u002Fstable\u002F) for logging and exposes a function `set_log_level` to control the log level. You can set the log level to one of the following values: `DEBUG`, `INFO`, `WARNING`, `ERROR`, and `CRITICAL`. The default value is `INFO`.\n\n```python\nfrom gemini_webapi import set_log_level\n\nset_log_level(\"DEBUG\")\n```\n\n> [!NOTE]\n>\n> Calling `set_log_level` for the first time will **globally** remove all existing loguru handlers. You may want to configure logging directly with loguru to avoid this issue and have more advanced control over logging behaviors.\n\n## CLI Tool\n\nA standalone CLI (`cli.py`) is included for interacting with Gemini from the terminal. It supports single-turn questions, multi-turn chat, deep research, image download, and account diagnostics.\n\n### Cookie Setup\n\nExport your cookies from [gemini.google.com](https:\u002F\u002Fgemini.google.com) and save them as a JSON file. The CLI supports multiple formats:\n\n```json\n{ \"__Secure-1PSID\": \"value...\", \"__Secure-1PSIDTS\": \"value...\" }\n```\n\nYou can also use a browser cookie extension export (array-of-objects format is supported).\n\n> [!NOTE]\n>\n> The CLI automatically persists updated cookies back to the JSON file after each run. Use `--no-persist` to disable this behavior.\n\n### CLI Commands\n\n**Global options** (placed before the subcommand):\n\n```sh\n--cookies-json PATH    Path to cookies JSON file (required)\n--proxy URL            Proxy URL (or uses HTTPS_PROXY env)\n--model NAME           Model name (see 'models' command)\n--verbose              Enable debug logging\n--no-persist           Don't update cookies file after run\n--request-timeout SEC  HTTP timeout in seconds (default: 300)\n```\n\n**Available commands:**\n\n```sh\n# Ask a single question (streams by default)\npython cli.py --cookies-json cookies.json ask \"What is quantum computing?\"\n\n# Ask with image input\npython cli.py --cookies-json cookies.json ask --image photo.jpg \"Describe this\"\n\n# Non-streaming mode\npython cli.py --cookies-json cookies.json ask --no-stream \"Hello\"\n\n# Continue a conversation (chat ID from previous output)\npython cli.py --cookies-json cookies.json reply c_abc123 \"Tell me more\"\n\n# List your chat history\npython cli.py --cookies-json cookies.json list\n\n# Read a specific chat conversation\npython cli.py --cookies-json cookies.json read c_abc123\n\n# List available models\npython cli.py --cookies-json cookies.json models\n\n# Download a generated image\npython cli.py --cookies-json cookies.json download \"https:\u002F\u002F...\" -o output.png\n\n# Account diagnostics (check feature availability)\npython cli.py --cookies-json cookies.json inspect\n```\n\n### Deep Research Workflow\n\nThe CLI supports Gemini's Deep Research feature — an autonomous research agent that browses the web, analyzes sources, and produces a comprehensive report.\n\n```sh\n# 1. Submit a research task\npython cli.py --cookies-json cookies.json research send --prompt \"AI chip competition 2025\"\n\n# 2. Check progress (use the chat ID from step 1)\npython cli.py --cookies-json cookies.json research check c_abc123\n\n# 3. Fetch the full result\npython cli.py --cookies-json cookies.json research get c_abc123\n\n# 4. Save result to a file\npython cli.py --cookies-json cookies.json research get c_abc123 --output report.md\n```\n\n## References\n\n[Google AI Studio](https:\u002F\u002Fai.google.dev\u002Ftutorials\u002Fai-studio_quickstart)\n\n[acheong08\u002FBard](https:\u002F\u002Fgithub.com\u002Facheong08\u002FBard)\n\n## Stargazers\n\n\u003Cp align=\"center\">\n    \u003Ca href=\"https:\u002F\u002Fstar-history.com\u002F#HanaokaYuzu\u002FGemini-API\">\n        \u003Cimg src=\"https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=HanaokaYuzu\u002FGemini-API&type=Date\" width=\"75%\" alt=\"Star History Chart\">\u003C\u002Fa>\n\u003C\u002Fp>\n","Gemini-API 是一个反向工程的 Python API，用于与 Google Gemini（原 Bard）网页应用进行交互。该项目的核心功能包括自动刷新持久化 Cookie、图像生成与编辑、视频及音频内容生成、深度研究工作流支持以及自定义系统提示等。它还支持 Gemini 扩展，如 YouTube 和 Gmail，并能对输出结果进行分类处理。此外，Gemini-API 提供了异步操作模式和命令行工具，便于开发者集成到需要持续在线服务的应用场景中。该库适用于需要利用先进生成式 AI 功能来创建或增强应用程序的开发人员。",2,"2026-06-11 03:29:50","top_topic"]