{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Tce3stUlHN0L"
      },
      "source": [
        "##### Copyright 2026 Google LLC."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "cellView": "form",
        "id": "tuOe1ymfHZPu"
      },
      "outputs": [],
      "source": [
        "# @title Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GAsiP4mohC2_"
      },
      "source": [
        "# Gemini API: JSON Mode Quickstart\n\n<a target=\"_blank\" href=\"https://colab.research.google.com/github/google-gemini/cookbook/blob/main/quickstarts/JSON_mode.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" height=30/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lF6sWVRGQ_bi"
      },
      "source": [
        "The Gemini API can be used to generate a JSON output if you set the schema that you would like to use.\n\nTwo methods are available. You can either set the desired output in the prompt or supply a schema to the model separately."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d0b5fbd931e5"
      },
      "source": [
        "> **Note:** This notebook uses the [Interactions API](https://ai.google.dev/gemini-api/docs/interactions), the latest way to interact with Gemini models. Looking for the `generateContent` version? Check the [archive branch](https://github.com/google-gemini/cookbook/blob/archive/generate-content-api/quickstarts/JSON_mode.ipynb)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "857d8bf104ed"
      },
      "source": [
        "### Install dependencies"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qLuL9m7KhvxR"
      },
      "outputs": [],
      "source": [
        "%pip install -U -q \"google-genai>=2.9.0\"  # 2.0 for Interactions API"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "B-axqBTM8Lbd"
      },
      "source": [
        "### Configure your API key\n",
        "\n",
        "To run the following cell, your API key must be stored in a Colab Secret named `GEMINI_API_KEY`. If you don't already have an API key, or you're not sure how to create a Colab Secret, see [Authentication](https://github.com/google-gemini/cookbook/blob/main/quickstarts/Authentication.ipynb) for a walkthrough."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "id": "d6lYXRcjthKV"
      },
      "outputs": [],
      "source": [
        "from google.colab import userdata\nfrom google import genai\n\nGEMINI_API_KEY = userdata.get('GEMINI_API_KEY')\nclient = genai.Client(api_key=GEMINI_API_KEY)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hD3qXcOTRD3z"
      },
      "source": [
        "## Set your constrained output in the prompt\n",
        "\n",
        "For this first example just describe the schema you want in the prompt itself, and ask the model to return JSON using `response_format`.\n",
        "\n",
        "The `response_format` parameter with `{\"type\": \"text\", \"mime_type\": \"application/json\"}` tells the model to output **only valid JSON** — without it, the model might wrap the JSON in markdown code blocks or add explanatory text around it."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "id": "K8ezjNb0RJ6Y"
      },
      "outputs": [],
      "source": [
        "prompt = \"\"\"\n",
        "    List a few popular cookie recipes using this JSON schema:\n",
        "\n",
        "    Recipe = {'recipe_name': str}\n",
        "    Return: list[Recipe]\n",
        "\"\"\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4071a6143d31"
      },
      "source": [
        "Select the model you want to use in this guide:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "id": "ggudoxK8RMlb"
      },
      "outputs": [],
      "source": [
        "MODEL_ID = \"gemini-3.7-flash\" # @param [\"gemini-3.7-flash\", \"gemini-2.5-pro\", \"gemini-3.1-pro-preview\"] {\"allow-input\":true, isTemplate: true}\n",
        "\n",
        "raw_interaction = client.interactions.create(\n",
        "    model=MODEL_ID,\n",
        "    input=prompt,\n",
        "    response_format={\"type\": \"text\", \"mime_type\": \"application/json\"},\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9TqoNg3VSMYB"
      },
      "source": [
        "Parse the string to JSON:\n> The response from `interactions.create` contains a list of `steps`. For text responses, access the output via `interaction.steps[-1].content[0].text`.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {
        "id": "b99ee66972f5"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[{'recipe_name': 'Chocolate Chip Cookies'}, {'recipe_name': 'Oatmeal Raisin Cookies'}, {'recipe_name': 'Peanut Butter Cookies'}, {'recipe_name': 'Sugar Cookies'}, {'recipe_name': 'Snickerdoodles'}]\n"
          ]
        }
      ],
      "source": [
        "import json\n\nresponse = json.loads(raw_interaction.steps[-1].content[0].text)\nprint(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1092c669169a"
      },
      "source": [
        "For readability serialize and print it:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {
        "id": "WLDPREpmSMu5"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[\n",
            "    {\n",
            "        \"recipe_name\": \"Chocolate Chip Cookies\"\n",
            "    },\n",
            "    {\n",
            "        \"recipe_name\": \"Oatmeal Raisin Cookies\"\n",
            "    },\n",
            "    {\n",
            "        \"recipe_name\": \"Peanut Butter Cookies\"\n",
            "    },\n",
            "    {\n",
            "        \"recipe_name\": \"Sugar Cookies\"\n",
            "    },\n",
            "    {\n",
            "        \"recipe_name\": \"Snickerdoodles\"\n",
            "    }\n",
            "]\n"
          ]
        }
      ],
      "source": [
        "print(json.dumps(response, indent=4))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K9nIks0R-tIa"
      },
      "source": [
        "## Supply the schema to the model directly\n",
        "\n",
        "Another option is to pass a schema directly via the `response_format` parameter. The model output will then follow that schema exactly.\n",
        "\n",
        "You can define your schema as a Python class using [Pydantic](https://docs.pydantic.dev/latest/) (or `typing_extensions.TypedDict`) and use `.model_json_schema()` to generate the JSON Schema automatically. This is much cleaner than writing raw JSON Schema by hand:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "id": "JiIxKaLl4R0f"
      },
      "outputs": [],
      "source": [
        "from pydantic import BaseModel, Field\n",
        "from typing import List, Optional\n",
        "\n",
        "class Ingredient(BaseModel):\n",
        "    \"\"\"A single ingredient in a recipe.\"\"\"\n",
        "    name: str = Field(description=\"Name of the ingredient.\")\n",
        "    quantity: str = Field(description=\"Quantity of the ingredient, including units.\")\n",
        "\n",
        "class Recipe(BaseModel):\n",
        "    \"\"\"A recipe with name, description, ingredients, and instructions.\"\"\"\n",
        "    recipe_name: str = Field(description=\"The name of the recipe.\")\n",
        "    recipe_description: str = Field(description=\"A one-sentence description of the recipe.\")\n",
        "    ingredients: List[Ingredient]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vBlWzt6M-2oM"
      },
      "source": [
        "Use `.model_json_schema()` to automatically generate the JSON Schema from your Pydantic model. This is the recommended approach — it avoids writing long JSON Schema dictionaries by hand:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "id": "8oe-tL8MDGtx"
      },
      "outputs": [],
      "source": [
        "# Preview the generated schema\n",
        "import json\n",
        "print(json.dumps(Recipe.model_json_schema(), indent=2))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "984d505b692a"
      },
      "outputs": [],
      "source": [
        "result = client.interactions.create(\n",
        "    model=MODEL_ID,\n",
        "    input=\"List a few imaginative cookie recipes along with a one-sentence description as if you were a gourmet restaurant and their main ingredients\",\n",
        "    response_format={\n",
        "        \"type\": \"text\",\n",
        "        \"mime_type\": \"application/json\",\n",
        "        \"schema\": Recipe.model_json_schema(),\n",
        "    },\n",
        ")\n",
        "\n",
        "# Parse and validate the response directly into a Pydantic object\n",
        "recipes = json.loads(result.steps[-1].content[0].text)\n",
        "print(json.dumps(recipes, indent=4))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "efc175ffb1db"
      },
      "source": [
        "You can also validate the response directly into your Pydantic model using `model_validate_json()` for type safety:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "79e3b1b33e4e"
      },
      "outputs": [],
      "source": [
        "# Validate into Pydantic objects\n",
        "from pydantic import TypeAdapter\n",
        "\n",
        "recipes_list = TypeAdapter(List[Recipe]).validate_json(result.steps[-1].content[0].text)\n",
        "for r in recipes_list:\n",
        "    print(f\"🍪 {r.recipe_name}: {r.recipe_description}\")\n",
        "    for ing in r.ingredients:\n",
        "        print(f\"   - {ing.quantity} {ing.name}\")\n",
        "    print()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "id": "slYcVAcqaDQY"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[\n",
            "    {\n",
            "        \"recipe_name\": \"Lavender & Wildflower Honey Shortbread\",\n",
            "        \"recipe_description\": \"A delicate, buttery shortbread infused with hand-harvested culinary lavender and finished with a glaze of artisanal wildflower honey.\",\n",
            "        \"recipe_ingredients\": [\n",
            "            \"European-style butter\",\n",
            "            \"all-purpose flour\",\n",
            "            \"granulated sugar\",\n",
            "            \"dried lavender buds\",\n",
            "            \"wildflower honey\",\n",
            "            \"Maldon sea salt\"\n",
            "        ]\n",
            "    },\n",
            "    {\n",
            "        \"recipe_name\": \"Umami Miso & White Chocolate Chunk\",\n",
            "        \"recipe_description\": \"A nuanced exploration of sweet and savory notes blending fermented white miso with velvety white chocolate and toasted macadamia nuts.\",\n",
            "        \"recipe_ingredients\": [\n",
            "            \"White miso paste\",\n",
            "            \"white chocolate chunks\",\n",
            "            \"toasted macadamia nuts\",\n",
            "            \"brown butter\",\n",
            "            \"flour\",\n",
            "            \"cane sugar\"\n",
            "        ]\n",
            "    },\n",
            "    {\n",
            "        \"recipe_name\": \"Smoked Sea Salt & Midnight Rye\",\n",
            "        \"recipe_description\": \"An earthy, deep-cocoa profile achieved with stone-ground rye flour and 70% dark chocolate, punctuated by crystals of hickory-smoked sea salt.\",\n",
            "        \"recipe_ingredients\": [\n",
            "            \"Stone-ground rye flour\",\n",
            "            \"70% dark chocolate\",\n",
            "            \"Dutch-process cocoa powder\",\n",
            "            \"unsalted butter\",\n",
            "            \"muscovado sugar\",\n",
            "            \"hickory-smoked sea salt\"\n",
            "        ]\n",
            "    }\n",
            "]\n"
          ]
        }
      ],
      "source": [
        "# You can also use TypedDict if you prefer not to depend on Pydantic:\n",
        "import typing_extensions as typing\n",
        "\n",
        "class SimpleRecipe(typing.TypedDict):\n",
        "    recipe_name: str\n",
        "    recipe_description: str"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "239af844c650"
      },
      "source": [
        "> **Tip:** The Pydantic approach with `model_json_schema()` is the recommended method — it keeps your schema in sync with your code, provides built-in validation, and avoids manual JSON Schema errors."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5ef9efdb62d3"
      },
      "source": [
        "## Next Steps\n",
        "### Useful API references:\n",
        "\n",
        "Check the [structured output](https://ai.google.dev/gemini-api/docs/interactions/structured-output) documentation for more details, including:\n",
        "* **Streaming** structured outputs\n",
        "* **Structured outputs with tools** (Google Search, Code Execution, etc.) — available with Gemini 3 models\n",
        "* **JSON Schema support** — supported types and properties\n",
        "* **Structured outputs vs function calling** — when to use which\n",
        "\n",
        "### Related examples\n",
        "\n",
        "* The constrained output is used in the [Text summarization](../examples/json_capabilities/Text_Summarization.ipynb) example to provide the model a format to summarize a story (genre, characters, etc...)\n",
        "* The [Object detection](../examples/json_capabilities/Object_detection.ipynb) example shows how to detect the position of specific objects in an image\n",
        "* Check all the [JSON](../examples/json_capabilities/) examples."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "JSON_mode.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}