{
"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
}