I stopped on one sentence in the October 8 changelog entry: deep-research-pro-preview-12-2025 is deprecated and will shut down on October 23, 2026.
As an indie developer, I find that the small scripts calling an API end up scattered outside the app itself. The ones with a hard-coded model or agent name are exactly the ones I can't place six months later. With ten days left, counting where to change turned out to matter more than the change.
What I'd like to say first: the swap itself is one line, the agent parameter. The real work is not missing the places that line is hiding.
What the official notes say
This is the changelog (October 8, 2026) as I read it.
| Item | Detail |
|---|---|
| Agent being shut down | deep-research-pro-preview-12-2025 (shuts down October 23, 2026) |
| Replacement 1 | deep-research-preview-04-2026 — built for speed and efficiency, suited to streaming back to a client UI |
| Replacement 2 | deep-research-max-preview-04-2026 — maximum comprehensiveness for automated context gathering and synthesis |
| What to change | The agent parameter of interactions.create |
The Deep Research docs give a rough cost of about $1–$3 per task for the standard agent and about $3–$7 for Max, and say these are preview-rate estimates that may change. I haven't run both myself, so please read those figures as copied from the docs.
Finding every use of the old name
A hard-coded name isn't only in code. It also sits in .env files, JSON config, README samples and Apps Script notes. So I wrote a small script that scans a project by extension and prints file and line number.
#!/usr/bin/env python3
"""Find every use of the old Deep Research agent name. --apply rewrites it."""
import argparse, pathlib, sys
OLD = "deep-research-pro-preview-12-2025"
SKIP_DIRS = {".git", "node_modules", ".venv", "__pycache__", ".next"}
EXTS = {".py", ".ts", ".js", ".mjs", ".json", ".yaml", ".yml", ".toml", ".sh", ".env", ".md", ".gs", ".txt"}
def walk(root):
for p in root.rglob("*"):
if any(part in SKIP_DIRS for part in p.parts):
continue
if p.is_file() and (p.suffix in EXTS or p.name.startswith(".env")):
yield p
def main():
ap = argparse.ArgumentParser()
ap.add_argument("root")
ap.add_argument("--apply", metavar="NEW", help="replacement agent name")
a = ap.parse_args()
hits = 0
for p in walk(pathlib.Path(a.root)):
try:
text = p.read_text(encoding="utf-8")
except (UnicodeDecodeError, OSError):
continue
if OLD not in text:
continue
for n, line in enumerate(text.splitlines(), 1):
if OLD in line:
hits += 1
print(f"{p}:{n}: {line.strip()}")
if a.apply:
p.write_text(text.replace(OLD, a.apply), encoding="utf-8")
print(f"-- {hits} hits", file=sys.stderr)
return 1 if hits and not a.apply else 0
if __name__ == "__main__":
sys.exit(main())Use it in two steps: run it without arguments and read the list, then run it with --apply. It exits 1 when it finds something and 0 when it doesn't, so it can also sit at the end of CI as a guard.
I tested it on a dummy project: one old name each in a .py, a .env and a nested cfg.json, plus one inside node_modules. It reported three hits and skipped node_modules. After --apply, a rescan found zero and the .env value had changed.
I have not called the Deep Research API itself while writing this. Please check the responses in your own environment after swapping.
Choosing between the standard and Max agents
There are two targets, so I wouldn't move everything to one name mechanically. This is my own rule of thumb based on the docs.
| Kind of call | Move to | Why |
|---|---|---|
| Streams progress to a screen | Standard | The docs position it for streaming to a client UI |
| Runs overnight, report read next morning | Max | Comprehensiveness matters more than wait time |
| Old prototype with no settled purpose | Standard | Lower estimated cost per task |
"Max is the safe choice" is too quick. With an estimated cost more than double, I'd move to standard first and raise only the calls that fall short; the bill is less surprising that way.
Four things to check after the swap
Following the docs sample, a call with the new agent looks like this.
import time
from google import genai
client = genai.Client()
interaction = client.interactions.create(
agent="deep-research-preview-04-2026", # swap this line
input="what you want researched",
background=True, # required; research takes minutes
agent_config={
"type": "deep-research",
"thinking_summaries": "auto", # default is none; use auto to see progress
"collaborative_planning": False,
},
)
while True:
interaction = client.interactions.get(interaction.id)
if interaction.status == "completed":
print(interaction.steps[-1].content[0].text)
break
if interaction.status == "failed":
print("failed:", interaction.error)
break
time.sleep(10)Four checks:
background=Truewithstore=True. The docs state that background execution requiresstore=True. If your old code turned storage off, this may be where it gets rejected.- Reliance on structured output. The docs say the agent doesn't support structured outputs. If you parsed a JSON schema, switch to specifying sections in the prompt.
- Custom function-calling tools. Custom functions aren't supported; remote MCP servers are.
- Wait-time assumptions. Maximum research time is 60 minutes and most tasks finish within 20. Streaming needs both
stream=Trueandbackground=True, and a 600-second connection drop should be planned for. Revisit your polling interval and timeout.
What happens after the date
I couldn't confirm what the official docs say about behavior after the shutdown. I'd guess calls with the old name will return an error, but that is only a guess. That's why I'd run the new name through once before the deadline.
Before the day it stops, watch it pass with your own eyes. The swap is one line, but the check is the part I try not to skip.
A good first step is to run the scan once tonight, with no arguments, and count the lines it prints.