Streaming interactions
When creating an Interaction, you can set stream: true to incrementally stream the response using server-sent events (SSE).
Python
from google import genai
client = genai.Client()
stream = client.interactions.create(
model="gemini-3-flash-preview",
input="Count to from 1 to 25.",
stream=True,
)
for event in stream:
if event.event_type == "step.delta":
if event.delta.type == "text":
print(event.delta.text, end="", flush=True)
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const stream = await client.interactions.create({
model: "gemini-3-flash-preview",
input: "Count to from 1 to 25.",
stream: true,
});
for await (const event of stream) {
if (event.event_type === "step.delta") {
if (event.delta.type === "text") {
process.stdout.write(event.delta.text);
}
}
}
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-H "Api-Revision: 2026-05-20" \
--no-buffer \
-d '{
"model": "gemini-3-flash-preview",
"input": "Count to from 1 to 25.",
"stream": true
}'
event: interaction.created
data: {"interaction":{"id":"v1_...","status":"in_progress","object":"interaction","model":"gemini-3-flash-preview"},"event_type":"interaction.created"}
event: interaction.status_update
data: {"interaction_id":"v1_...","status":"in_progress","event_type":"interaction.status_update"}
event: step.start
data: {"index":0,"step":{"type":"thought"},"event_type":"step.start"}
event: step.delta
data: {"index":0,"delta":{"signature":"...","type":"thought_signature"},"event_type":"step.delta"}
event: step.stop
data: {"index":0,"event_type":"step.stop"}
event: step.start
data: {"index":1,"step":{"type":"model_output"},"event_type":"step.start"}
event: step.delta
data: {"index":1,"delta":{"text":"1, 2, 3, 4, 5, 6, ","type":"text"},"event_type":"step.delta"}
event: step.delta
data: {"index":1,"delta":{"text":"7, 8, 9, 10, 11, 12, 13,","type":"text"},"event_type":"step.delta"}
...
event: step.stop
data: {"index":1,"event_type":"step.stop"}
event: interaction.completed
data: {"interaction":{"id":"v1_...","status":"completed","usage":{"total_tokens":346,"total_input_tokens":11,"input_tokens_by_modality":[{"modality":"text","tokens":11}],"total_cached_tokens":0,"total_output_tokens":90,"total_tool_use_tokens":0,"total_thought_tokens":245},"created":"2026-05-12T18:44:51Z","updated":"2026-05-12T18:44:51Z","service_tier":"standard","object":"interaction","model":"gemini-3-flash-preview"},"event_type":"interaction.completed"}
event: done
data: [DONE]
Event types
Each server-sent event includes a named event_type and associated JSON data. The Interactions API uses a symmetric streaming model where all content—text, tool calls, thinking—flows through a consistent step-based event.
Each stream follows this event flow:
interaction.created: The interaction is created, includes metadata (ID, model, status).- A series of steps, each consisting of:
- A
step.startevent, indicating the step type (e.g.,model_output,thought,function_call). - One or more
step.deltaevents with incremental data for that step. - A
step.stopevent marking the step as complete.
- A
- An
interaction.completedevent with finalusagestatistics.
When you set stream: false, the API returns a single interaction object with a steps array. Each element in steps is the fully assembled version of one step.start → step.delta(s) → step.stop cycle.
interaction.created
Sent when the interaction is first created. Contains the interaction ID, model, and initial status.
event: interaction.created
data: {"interaction": {"id": "...", "model": "gemini-3-flash-preview", "status": "in_progress", "object": "interaction"}, "event_type": "interaction.created"}
interaction.status_update
Signals an interaction-level status transition. May appear between steps.
event: interaction.status_update
data: {"interaction_id": "...", "status": "in_progress", "event_type": "interaction.status_update"}
step.start
Marks the beginning of a new step. Contains the step type and index. The step type determines which delta types to expect and how the step appears in a non-streaming response:
| Step Type | Expected Delta Types | Description |
|---|---|---|
model_output |
text, image, audio |
The model's final response content. |
thought |
thought_signature, thought_summary |
Chain-of-thought reasoning. summary is only present when thinking_summaries is enabled. |
function_call |
arguments_delta |
A request for the client to execute a function. Sets interaction status to requires_action. |
| Server-side tools | Varies by tool | Tools executed by the API (e.g., google_search_call, google_search_result, code_execution_call, code_execution_result). |
See the Interactions API reference for the full list.
event: step.start
data: {"index": 0, "step": {"type": "model_output"}, "event_type": "step.start"}
For function calls, the step includes the function name, id and empty arguments {}
event: step.start
data: {"index": 0, "step": {"type": "function_call", "id":"un6k8t18", "name": "get_weather", "arguments":{}}, "event_type": "step.start"}
step.delta
Incremental data for the current step. The delta object contains a type field that determines its shape.
Examples:
text: Incremental text token from a model_output step:
event: step.delta
data: {"index": 0, "delta": {"type": "text", "text": "Hello, my name is Phil"}, "event_type": "step.delta"}
event: step.delta
data: {"index": 0, "delta": {"type": "text", "text": ", and I live in Germany." }, "event_type": "step.delta"}
image: Base64-encoded image data from a model_output step:
event: step.delta
data: {"index": 0, "delta": {"type": "image", "mime_type": "image/jpeg", "data": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCg..."}, "event_type": "step.delta"}
thought_summary: Thinking summary content from a thought step:
event: step.delta
data: {"index": 0, "delta": {"type": "thought_summary", "content": {"type": "text", "text": "I need to find the GCD..."}}, "event_type": "step.delta"}
arguments_delta: (Partial) JSON string for function call arguments. Must be accumulated across deltas:
event: step.delta
data: {"index": 0, "delta": {"type": "arguments_delta", "arguments": "{\"location\": \"San Francisco, CA\"}"}, "event_type": "step.delta"}
These are some of the most common delta types. For the complete list of all delta types, see the Interactions API reference.
step.stop
Marks the end of a step. Contains the step index.
event: step.stop
data: {"index": 0, "event_type": "step.stop"}
interaction.completed
Sent when the interaction is finished. Contains the final interaction object with usage statistics. In non-streaming mode, this is the top-level response object itself. Does not include steps in the response.
event: interaction.completed
data: {"interaction": {"id": "v1_abc123", "status": "completed", "usage": {"total_input_tokens": 7, "total_output_tokens": 12, "total_tokens": 19}}, "event_type": "interaction.completed"}
error
Sent when an error occurs during the interaction. Contains an error object with a message and code.
event: error
data: {"error":{"message":"Deadline expired before operation could complete.","code":"gateway_timeout"},"event_type":"error"}
Streaming with tools
The Interactions API supports streaming with both client-side tools (function calling) and server-side tools (Google Search, Code Execution, etc.) in a single request. During streaming, tool invocations appear as typed steps in the event stream. For function calls, the step.start event delivers the function name, and step.delta events stream the arguments as JSON strings (arguments_delta). You must accumulate these deltas to get the full arguments. Server-side tools like Google Search are executed automatically by the API, producing google_search_call and google_search_result steps.
Streaming with function calling
To perform function calling with streaming, the client must handle a multi-turn conversation:
1. Turn 1 (Function Request): Call interactions.create with stream: true and your defined tools. The API will stream a function_call step. You must accumulate the incremental argument JSON strings (arguments_delta) from step.delta events until the interaction completes with the status requires_action.
2. Turn 2 (Sending Result): Call interactions.create again, passing the previous_interaction_id (matching the ID of the first interaction) and sending a function_result block within the input array. This resumes the stream, allowing the model to generate its final response.
Python
from google import genai
client = genai.Client()
weather_tool = {
"type": "function",
"name": "get_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
# Turn 1: Request function call
stream = client.interactions.create(
model="gemini-3-flash-preview",
tools=[weather_tool],
input="What is the weather in Paris right now?",
stream=True,
)
first_interaction_id = None
func_call_id = None
func_call_name = None
func_args_accumulated = ""
for event in stream:
if event.event_type == "interaction.created":
first_interaction_id = event.interaction.id
elif event.event_type == "step.start":
step = event.step
if step.type == "function_call":
func_call_id = step.id
func_call_name = step.name
elif event.event_type == "step.delta":
if event.delta.type == "arguments_delta":
func_args_accumulated += event.delta.arguments
# Turn 2: Execute tool and send the result back to resume stream
if func_call_id:
# Execute weather_tool using accumulated arguments
# args = json.loads(func_args_accumulated)
dummy_result = {
"content": [{"type": "text", "text": '{"weather": "Sunny and 22°C"}'}]
}
stream2 = client.interactions.create(
model="gemini-3-flash-preview",
previous_interaction_id=first_interaction_id,
input=[{
"type": "function_result",
"name": func_call_name,
"call_id": func_call_id,
"result": dummy_result
}],
stream=True,
)
for event in stream2:
if event.event_type == "step.delta":
if event.delta.type == "text":
print(event.delta.text, end="", flush=True)
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const weatherTool = {
type: "function",
name: "get_weather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA"
}
},
required: ["location"]
}
};
// Turn 1: Request function call
const stream = await client.interactions.create({
model: "gemini-3-flash-preview",
tools: [weatherTool],
input: "What is the weather in Paris right now?",
stream: true,
});
let firstInteractionId = null;
let funcCallId = null;
let funcCallName = null;
let funcArgsAccumulated = "";
for await (const event of stream) {
if (event.event_type === "interaction.created") {
firstInteractionId = event.interaction.id;
} else if (event.event_type === "step.start") {
const step = event.step;
if (step.type === "function_call") {
funcCallId = step.id;
funcCallName = step.name;
}
} else if (event.event_type === "step.delta") {
if (event.delta.type === "arguments_delta") {
funcArgsAccumulated += event.delta.arguments;
}
}
}
// Turn 2: Execute tool and send the result back to resume stream
if (funcCallId && firstInteractionId && funcCallName) {
// const args = JSON.parse(funcArgsAccumulated);
const dummyResult = {
content: [{ type: "text", text: '{"weather": "Sunny and 22°C"}' }]
};
const stream2 = await client.interactions.create({
model: "gemini-3-flash-preview",
previous_interaction_id: firstInteractionId,
input: [{
type: "function_result",
name: funcCallName,
call_id: funcCallId,
result: dummyResult
}],
stream: true,
});
for await (const event of stream2) {
if (event.event_type === "step.delta") {
if (event.delta.type === "text") {
process.stdout.write(event.delta.text);
}
}
}
}
REST
Turn 1: Request function call
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-H "Api-Revision: 2026-05-20" \
--no-buffer \
-d '{
"model": "gemini-3-flash-preview",
"input": "What is the weather in Paris right now?",
"stream": true,
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
]
}'
Turn 2: Send the function result using the previous_interaction_id and call_id from Turn 1
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-H "Api-Revision: 2026-05-20" \
--no-buffer \
-d '{
"model": "gemini-3-flash-preview",
"previous_interaction_id": "v1_ChdGUVFJYXBXVUdLVEF4TjhQ...",
"stream": true,
"input": [
{
"type": "function_result",
"name": "get_weather",
"call_id": "CALL_ID",
"result": {
"content": [
{
"type": "text",
"text": "{\"weather\": \"Sunny and 22°C\"}"
}
]
}
}
]
}'
Streaming with multiple tools
The following example uses both a function tool and google_search in one request:
Python
from google import genai
client = genai.Client()
tools = [
{"type": "google_search"},
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
]
stream = client.interactions.create(
model="gemini-3-flash-preview",
tools=tools,
input="Search what it the largest mountain in Europe and what the weather is there right now?",
stream=True,
)
for event in stream:
if event.event_type == "step.start":
step = event.step
print(f"\n--- Step {event.index}: {step.type} ---")
# Show details for tool steps
if step.type == "google_search_call":
print(f" Search ID: {step.id}")
elif step.type == "google_search_result":
print(f" Result for: {step.call_id}")
elif step.type == "function_call":
print(f" Function: {step.name}({step.arguments})")
elif event.event_type == "step.delta":
if event.delta.type == "text":
print(event.delta.text, end="", flush=True)
elif event.delta.type == "google_search_call":
print(f" Queries: {event.delta.arguments}")
elif event.delta.type == "arguments_delta":
print(f" Args chunk: {event.delta.arguments}", end="", flush=True)
elif event.event_type == "interaction.completed":
print(f"\n\nStatus: {event.interaction.status}")
if event.interaction.status == "requires_action":
print("Action required: provide function call results to continue.")
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const tools = [
{ type: "google_search" },
{
type: "function",
name: "get_weather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA"
}
},
required: ["location"]
}
}
];
const stream = await client.interactions.create({
model: "gemini-3-flash-preview",
tools: tools,
input: "Search what it the largest mountain in Europe and what the weather is there right now?",
stream: true,
});
for await (const event of stream) {
if (event.event_type === "step.start") {
const step = event.step;
console.log(`\n--- Step ${event.index}: ${step.type} ---`);
// Show details for tool steps
if (step.type === "google_search_call") {
console.log(` Search ID: ${step.id}`);
} else if (step.type === "google_search_result") {
console.log(` Result for: ${step.call_id}`);
} else if (step.type === "function_call") {
console.log(` Function: ${step.name}(${JSON.stringify(step.arguments)})`);
}
} else if (event.event_type === "step.delta") {
if (event.delta.type === "text") {
process.stdout.write(event.delta.text);
} else if (event.delta.type === "google_search_call") {
console.log(` Queries: ${JSON.stringify(event.delta.arguments?.queries)}`);
} else if (event.step.type === "google_search_result") {
console.log(` Result for: ${event.step.call_id}`);
} else if (event.delta.type === "arguments_delta") {
process.stdout.write(` Args chunk: ${event.delta.arguments}`);
}
} else if (event.event_type === "interaction.completed") {
console.log(`\n\nStatus: ${event.interaction.status}`);
if (event.interaction.status === "requires_action") {
console.log("Action required: provide function call results to continue.");
}
}
}
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-H "Api-Revision: 2026-05-20" \
--no-buffer \
-d '{
"model": "gemini-3-flash-preview",
"input": "Search what it the largest mountain in Europe and what the weather is there right now?",
"stream": true,
"tools": [
{ "type": "google_search" },
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
]
}'
event: interaction.created
data: {"interaction":{"id":"v1_...","status":"in_progress","object":"interaction","model":"gemini-3-flash-preview"},"event_type":"interaction.created"}
event: interaction.status_update
data: {"interaction_id":"v1_...","status":"in_progress","event_type":"interaction.status_update"}
event: step.start
data: {"index":0,"step":{"id":"mkutnkgn","signature":"","type":"google_search_call"},"event_type":"step.start"}
event: step.delta
data: {"index":0,"delta":{"signature":"...","type":"google_search_call","arguments":{"queries":["largest mountain in Europe"]}},"event_type":"step.delta"}
event: step.stop
data: {"index":0,"event_type":"step.stop"}
event: step.start
data: {"index":1,"step":{"call_id":"mkutnkgn","signature":"","type":"google_search_result"},"event_type":"step.start"}
event: step.delta
data: {"index":1,"delta":{"signature":"...","type":"google_search_result","is_error":false},"event_type":"step.delta"}
event: step.stop
data: {"index":1,"event_type":"step.stop"}
event: step.start
data: {"index":2,"step":{"type":"thought"},"event_type":"step.start"}
event: step.delta
data: {"index":2,"delta":{"signature":"...","type":"thought_signature"},"event_type":"step.delta"}
event: step.stop
data: {"index":2,"event_type":"step.stop"}
event: step.start
data: {"index":3,"step":{"id":"ktr5aysg","type":"function_call","name":"get_weather","arguments":{}},"event_type":"step.start"}
event: step.delta
data: {"index":3,"delta":{"arguments":"{\"location\":\"Mount Elbrus, Russia\"}","type":"arguments_delta"},"event_type":"step.delta"}
event: step.stop
data: {"index":3,"event_type":"step.stop"}
event: interaction.completed
data: {"interaction":{"id":"v1_...","status":"requires_action","usage":{"total_tokens":299,"total_input_tokens":138,"input_tokens_by_modality":[{"modality":"text","tokens":138}],"total_cached_tokens":0,"total_output_tokens":20,"total_tool_use_tokens":0,"total_thought_tokens":141},"created":"2026-05-12T17:24:26Z","updated":"2026-05-12T17:24:26Z","service_tier":"standard","object":"interaction","model":"gemini-3-flash-preview"},"event_type":"interaction.completed"}
event: done
data: [DONE]
Streaming with thinking
When the model uses thinking, you'll receive thought steps with two distinct delta types: thought_summary (incremental text or image summary content) and thought_signature (an encrypted representation of the model's internal reasoning, sent as the last delta before step.stop). If thinking_summaries is enabled, thought_summary deltas stream a summary of the model's reasoning. For more details on thinking, see the Thinking guide.
Python
from google import genai
client = genai.Client()
stream = client.interactions.create(
model="gemini-3-flash-preview",
input="What is the greatest common divisor of 1071 and 462?",
generation_config={
"thinking_summaries": "auto"
},
stream=True,
)
for event in stream:
if event.event_type == "step.start":
print(f"\n--- Step: {event.step.type} ---")
elif event.event_type == "step.delta":
if event.delta.type == "thought_summary":
if event.delta.content.type == "text":
print(event.delta.content.text, end="", flush=True)
elif event.delta.type == "text":
print(event.delta.text, end="", flush=True)
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const stream = await client.interactions.create({
model: "gemini-3-flash-preview",
input: "What is the greatest common divisor of 1071 and 462?",
generation_config: {
thinking_summaries: "auto",
},
stream: true,
});
for await (const event of stream) {
if (event.event_type === "step.start") {
console.log(`\n--- Step: ${event.step.type} ---`);
} else if (event.event_type === "step.delta") {
if (event.delta.type === "thought_summary") {
if (event.delta.content.type === "text") {
process.stdout.write(event.delta.content.text);
}
} else if (event.delta.type === "text") {
process.stdout.write(event.delta.text);
}
}
}
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-H "Api-Revision: 2026-05-20" \
--no-buffer \
-d '{
"model": "gemini-3-flash-preview",
"input": "What is the greatest common divisor of 1071 and 462?",
"stream": true,
"generation_config": {
"thinking_summaries": "auto"
}
}'
event: interaction.created
data: {"interaction":{"id":"v1_...","status":"in_progress","object":"interaction","model":"gemini-3-flash-preview"},"event_type":"interaction.created"}
event: interaction.status_update
data: {"interaction_id":"v1_...","status":"in_progress","event_type":"interaction.status_update"}
event: step.start
data: {"index":0,"step":{"type":"thought"},"event_type":"step.start"}
event: step.delta
data: {"index":0,"delta":{"content":{"text":"**Implementing Euclidean Algorithm**\n\nI've just worked through a detailed example applying the Euclidean algorithm to find the GCD of 1071 and 462, confirming its step-by-step nature. The calculations went smoothly, tracking the remainders until zero. My focus is now solidifying the implementation logic, ensuring accuracy and considering potential edge cases. I'll translate this example into code.\n\n\n","type":"text"},"type":"thought_summary"},"event_type":"step.delta"}
event: step.delta
data: {"index":0,"delta":{"signature":"...","type":"thought_signature"},"event_type":"step.delta"}
event: step.stop
data: {"index":0,"event_type":"step.stop"}
event: step.start
data: {"index":1,"step":{"type":"model_output"},"event_type":"step.start"}
...
Streaming with agents
The Interactions API supports agents like Deep Research. Agents use background=True and return results asynchronously, but you can also stream agent interactions to receive progress updates and intermediate steps as they happen. For more details, see the Deep Research guide.
Python
from google import genai
client = genai.Client()
stream = client.interactions.create(
agent="deep-research-preview-04-2026",
input="Research the latest advances in quantum computing.",
stream=True,
background=True,
agent_config={
"type": "deep-research",
"thinking_summaries": "auto"
}
)
for event in stream:
if event.event_type == "step.start":
print(f"\n--- Step: {event.step.type} ---")
elif event.event_type == "step.delta":
if event.delta.type == "text":
print(event.delta.text, end="", flush=True)
elif event.delta.type == "thought_summary":
if event.delta.content.type == "text":
print(event.delta.content.text, end="", flush=True)
elif event.event_type == "interaction.completed":
print(f"\n\nTotal Tokens: {event.interaction.usage.total_tokens}")
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const stream = await client.interactions.create({
agent: "deep-research-preview-04-2026",
input: "Research the latest advances in quantum computing.",
stream: true,
background: true,
agent_config: {
type: "deep-research",
thinking_summaries: "auto"
}
});
for await (const event of stream) {
if (event.event_type === "step.start") {
console.log(`\n--- Step: ${event.step.type} ---`);
} else if (event.event_type === "step.delta") {
if (event.delta.type === "text") {
process.stdout.write(event.delta.text);
} else if (event.delta.type === "thought_summary") {
if (event.delta.content.type === "text") {
process.stdout.write(event.delta.content.text);
}
}
} else if (event.event_type === "interaction.completed") {
console.log(`\n\nTotal Tokens: ${event.interaction.usage.total_tokens}`);
}
}
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-H "Api-Revision: 2026-05-20" \
--no-buffer \
-d '{
"agent": "deep-research-preview-04-2026",
"input": "Research the latest advances in quantum computing.",
"stream": true,
"background": true,
"agent_config": {
"type": "deep-research",
"thinking_summaries": "auto"
}
}'
event: interaction.created
data: {"interaction":{"id":"v1_...","status":"in_progress","object":"interaction","agent":"deep-research-preview-04-2026"},"event_type":"interaction.created"}
event: interaction.status_update
data: {"interaction_id":"v1_...","status":"in_progress","event_type":"interaction.status_update"}
event: step.start
data: {"index":0,"step":{"type":"thought"},"event_type":"step.start"}
event: step.delta
data: {"index":0,"delta":{"content":{"text":"***Generating research plan***\n\nTo best answer your request, I'm starting by constructing a comprehensive research plan. This will outline the key areas I need to investigate and the strategy I'll use to connect them."},"type":"thought_summary"},"event_type":"step.delta"}
... (additional thought steps) ...
event: step.stop
data: {"index":0,"event_type":"step.stop"}
event: step.start
data: {"index":1,"step":{"type":"model_output"},"event_type":"step.start"}
event: step.delta
data: {"index":1,"delta":{"text":"# The Quantum Inflection Point: Exhaustive Analysis of Hardware, Algorithms, and Market Dynamics in 2026\n\n## Executive Summary\n\n..."},"event_type":"step.delta"}
event: step.stop
data: {"index":1,"event_type":"step.stop"}
event: interaction.completed
data: {"interaction":{"id":"v1_...","status":"completed","usage":{"total_tokens":1117031,"total_input_tokens":428865,"total_output_tokens":22294,"total_thought_tokens":26213},"created":"2026-05-12T17:24:27Z","updated":"2026-05-12T17:24:27Z","object":"interaction","agent":"deep-research-preview-04-2026"},"event_type":"interaction.completed"}
event: done
data: [DONE]
Streaming image generation
The Interactions API supports streaming multiple output modalities simultaneously. By requesting both text and image in the response_format, you can receive interleaved text and generated images in the same stream.
The following example uses gemini-3.1-flash-image-preview (Nano Banana 2) to search for information and generate a story with interleaved illustrations.
Python
from google import genai
client = genai.Client()
stream = client.interactions.create(
model="gemini-3.1-flash-image-preview",
tools=[{"type": "google_search", "search_types": ["web_search", "image_search"]}],
input="Search for the history of the Colosseum and write a short illustrated story about a gladiator named Marcus. Interleave text and generated images.",
response_format=[
{"type": "text"},
{"type": "image"}
],
stream=True,
)
for event in stream:
if event.event_type == "step.delta":
if event.delta.type == "text":
print(event.delta.text, end="", flush=True)
elif event.delta.type == "image":
print(f"\n[Image chunk: {len(event.delta.data)} bytes]", end="", flush=True)
JavaScript
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const stream = await client.interactions.create({
model: "gemini-3.1-flash-image-preview",
tools: [{ type: "google_search", search_types: ["web_search", "image_search"] }],
input: "Search for the history of the Colosseum and write a short illustrated story about a gladiator named Marcus. Interleave text and generated images.",
response_format: [
{ type: "text" },
{ type: "image" }
],
stream: true,
});
for await (const event of stream) {
if (event.event_type === "step.delta") {
if (event.delta.type === "text") {
process.stdout.write(event.delta.text);
} else if (event.delta.type === "image") {
console.log(`\n[Image chunk: ${event.delta.data.length} bytes]`);
}
}
}
REST
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-H "Api-Revision: 2026-05-20" \
--no-buffer \
-d '{
"model": "gemini-3.1-flash-image-preview",
"input": "Search for the history of the Colosseum and write a short illustrated story about a gladiator named Marcus. Interleave text and generated images.",
"stream": true,
"tools": [
{ "type": "google_search",
"search_types": ["web_search", "image_search"]
}
],
"generation_config": {
"thinking_summaries": "auto"
},
"response_format": [
{ "type": "text" }, { "type": "image"}
]
}'
event: interaction.created
data: {"interaction":{"id":"v1_...","status":"in_progress","object":"interaction","model":"gemini-3.1-flash-image-preview"},"event_type":"interaction.created"}
event: interaction.status_update
data: {"interaction_id":"v1_...","status":"in_progress","event_type":"interaction.status_update"}
event: step.start
data: {"index":0,"step":{"type":"model_output"},"event_type":"step.start"}
event: step.delta
data: {"index":0,"delta":{"text":"Here is a short illustrated story about the Colosseum...\n\n### Part 1: The New Flavian Amphitheater\n\n...","type":"text"},"event_type":"step.delta"}
...
event: step.stop
data: {"index":0,"event_type":"step.stop"}
event: step.start
data: {"index":1,"step":{"type":"thought"},"event_type":"step.start"}
event: step.delta
data: {"index":1,"delta":{"signature":"...","type":"thought_signature"},"event_type":"step.delta"}
event: step.stop
data: {"index":1,"event_type":"step.stop"}
event: step.start
data: {"index":2,"step":{"type":"model_output"},"event_type":"step.start"}
event: step.delta
data: {"index":2,"delta":{"mime_type":"image/jpeg","data":"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCg...","type":"image"},"event_type":"step.delta"}
event: step.delta
data: {"index":2,"delta":{"text":"### Part 2: The Hypogeum and the Wait\n\n...","type":"text"},"event_type":"step.delta"}
...
event: step.stop
data: {"index":2,"event_type":"step.stop"}
event: step.start
data: {"index":3,"step":{"type":"thought"},"event_type":"step.start"}
event: step.delta
data: {"index":3,"delta":{"signature":"...","type":"thought_signature"},"event_type":"step.delta"}
event: step.stop
data: {"index":3,"event_type":"step.stop"}
event: step.start
data: {"index":4,"step":{"type":"model_output"},"event_type":"step.start"}
event: step.delta
data: {"index":4,"delta":{"mime_type":"image/jpeg","data":"/9j/4AAQSkZJRgABAQAAAQABAAD/...","type":"image"},"event_type":"step.delta"}
event: step.delta
data: {"index":4,"delta":{"text":"### Part 3: The Moment of Spectacle\n\n...","type":"text"},"event_type":"step.delta"}
...
event: step.stop
data: {"index":4,"event_type":"step.stop"}
event: interaction.completed
data: {"interaction":{"id":"v1_...","status":"completed","usage":{"total_tokens":6128,"total_input_tokens":29,"total_output_tokens":6099,"output_tokens_by_modality":[{"modality":"image","tokens":4480}]}},"event_type":"interaction.completed"}
event: done
data: [DONE]
Handling unknown events
In accordance with the API's versioning policy, new event types and delta types may be added over time. Your code should handle unknown event types gracefully—log and skip any events you don't recognize rather than throwing an error.
What's next
- Learn more about the Interactions API.
- Explore Function calling with tools.
- Learn about Thinking for enhanced reasoning.
- Try the Deep Research Agent for long-running tasks.
- See the Interactions API reference for all event types and delta types.