Flipping a Project's Shots to Final: Batched Status Writes in the ShotGrid API
A practical walkthrough of batched status updates with shotgun_api3: scoped script credentials, site-local status codes, and one batch call instead of hundreds.
09 Jan 2026, 18:14 UTC

The delivery-week status flip
It's delivery week and the producer wants every shot in the trailer project marked Final. There are a few hundred of them, all sitting at In Progress. The obvious script is a loop: find the shots, call update once per shot, done. It works — and then it waits on hundreds of sequential round trips, hammers the site with requests, and if it dies halfway through you have no clean record of which shots actually changed.
The fix is three small engineering decisions: authenticate as a scoped script instead of a person, confirm your site's status codes before writing them, and push all the writes through a single batch call. Everything below uses shotgun_api3, the long-standing Python client for ShotGrid (renamed Autodesk Flow Production Tracking in 2024 — same product, and the client and field names carry over, though it's worth checking Autodesk's current developer docs if you're on newer SDKs).
Authenticate as a script, not a person
Automated jobs should never run under someone's login. Flow Production Tracking lets you create a Script — a non-human identity with its own API key — from the site's admin Scripts page. Two things make this the right default:
- Least privilege. Attach the script to a permission group that can read Projects and read/write Shots, and nothing more. A bug in your code then can't delete versions or touch anything unrelated.
- A cleaner audit trail. Writes are attributed to the script name, so "what flipped these shots?" has an answer.
Keep the key in an environment variable, not in the repository.
Status codes are local dialects
The pipeline status field on Shots, Assets and Tasks is sg_status_list. Its values are short codes — ip for In Progress and fin for Final in a default setup — while the words artists see are display names. Sites routinely rename codes, add their own, or retire defaults, so a script that hardcodes what your last studio used can silently write the wrong state or fail outright. Read the allowed values first (the example below does this with schema_field_read), or check the status list in the admin UI.
One more gotcha: updating a Shot's status does not cascade to its tasks. If your pipeline treats "shot is Final" as "all its tasks are Final", the script has to write both.
Worked example: one batch instead of hundreds of updates
Where to run it: any machine with HTTPS access to your site and Python 3 (pip install shotgun_api3). Permissions: a Script that can read Projects and read/write Shots. Placeholders: the site URL and the project name.
# Any machine with HTTPS access to your site. Install once: pip install shotgun_api3
import os
from shotgun_api3 import Shotgun
sg = Shotgun(
"https://YOUR-STUDIO.shotgrid.autodesk.com", # your site URL
script_name=os.environ["SG_SCRIPT_NAME"],
api_key=os.environ["SG_SCRIPT_KEY"],
)
project = sg.find_one("Project", [["name", "is", "Aurora"]], ["id", "name"])
if project is None:
raise SystemExit("No project named 'Aurora' - check the exact name.")
# Dry run: read the current state before writing anything.
shots = sg.find(
"Shot",
[
["project", "is", {"type": "Project", "id": project["id"]}],
["sg_status_list", "is", "ip"],
],
["id", "code", "sg_status_list"],
)
previous = {s["id"]: s["sg_status_list"] for s in shots} # kept for rollback
print(f"{len(shots)} shots currently 'ip'")
# Confirm this site's status codes before writing anything.
schema = sg.schema_field_read("Shot", "sg_status_list")
print(schema["sg_status_list"]["valid_values"])
updates = [
{
"request_type": "update",
"entity_type": "Shot",
"entity_id": s["id"],
"data": {"sg_status_list": "fin"},
}
for s in shots
]
results = sg.batch(updates)
print(f"Batch reported {len(results)} updates")
What to check before trusting it: the count line should match what you see when you apply the same status filter in the web UI; the schema print should list the codes your site actually allows (if your client version returns a different structure, print the raw dict and look for the valid-values list); and batch returns one record per update, so its length should equal the number of shots found.
Two risks to respect. First, batch is not a transaction — if one row fails, don't assume the rest rolled back; the returned records tell you what landed, so re-run only the missing IDs. Second, fin may be wired to downstream behaviour on your site (notifications, publish rules), so prove the whole flow on a dummy shot before touching a real project.
| One update per shot | One batch call | |
|---|---|---|
| API requests | One per entity | One total |
| Mid-run failure | Loop stops; partial state is easy to lose track of | One result per row; re-run only the failures |
| Best suited to | Single-entity writes from interactive tools | Bulk status flips and migrations |
Where this approach hits its limits
These calls are synchronous: the client waits on the network for every request. That's fine for an end-of-day script and painful inside an interactive tool — if you're calling this from a panel in Maya or Houdini, keep the call off the UI thread or route it through a small internal service. Very high request volumes can also be throttled by the site; batching keeps you under that ceiling and is simply faster. If you catch yourself polling for changes on a schedule, that's the signal to look at the Event Log instead — the community shotgunEvents daemon runs your code in response to logged events, so you react to changes rather than hunting for them. Finally, every sg_ field name in an automation is a per-site decision; anything beyond the standard fields deserves a schema check at startup.
Before you run this on real shots
- Run the
findalone and eyeball the list — that's your dry run. - Print the site's valid status codes and confirm
finmeans what you think it means. - Update one dummy shot, verify the change in the web UI, then batch the rest.
- Log the returned IDs. The snippet keeps the previous statuses in
previous; since this writes state, reverting is the same batch with those saved values.
None of this is exotic — it's read-before-write, least privilege, and fewer round trips. Those three habits cover most of what goes wrong in ShotGrid automation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.