Shopify video sources are empty until READY: poll before attaching
On Shopify's Video object, sources and duration stay empty until status is READY. Poll the file before you attach a Sume clip.

A Shopify video is not usable the moment fileCreate returns. On the Admin GraphQL Video object, sources is empty and duration is null unless the video's status is READY, so a pipeline that uploads a Sume clip and immediately reads its playback URLs gets nothing. Poll until the file reports READY, and keep that wait separate from your Sume job polling.
Shopify details come from the Video object, fileCreate and fileUpdate references, read 2026-10-02. Sume details come from Jobs and results.
What exactly is empty before READY?
The Video object lists sources (the playable renditions), preview (a preview image), duration in milliseconds, filename and status. Shopify's text says sources is empty and duration is null until status is READY. The page does not list which formats or resolutions Shopify produces, so do not hard-code a rendition list from another source.
fileCreate says the same thing from the file side: files process asynchronously, fileStatus tracks that, and READY means processing succeeded.
Which steps need READY?
Reading playable sources and duration needs it. So does editing: fileUpdate requires the file to be in a ready state, and the mutation locks the file during updates. Shopify's page also says videos and 3D models allow only alt text and product-reference updates, and you cannot change originalSource and previewImageSource in one call, so a video poster swap is a separate step. See the poster post for that.
| Step | Needs READY | Source |
|---|---|---|
| Read sources or duration | Yes, empty or null before | Video object |
| fileUpdate on the file | Yes, file must be ready | fileUpdate |
| Show the status | No, fileStatus is readable at any time | fileCreate |
| Attach to a product | Not stated on the pages read | Test on a draft product |
How do I poll without re-uploading?
Back off between checks and give up with an error that says what to do next. The loop below reads fileStatus and duration through the generic node query and doubles the delay up to 30 seconds. The important line is the last one: a timeout is a reason to look again later, not a reason to create the file a second time and pay for another Sume render.
import os, time, requests
GQL = (f"https://{os.environ['SHOP']}/admin/api/"
f"{os.environ['SHOPIFY_API_VERSION']}/graphql.json")
Q = """query($id: ID!) {
node(id: $id) {
... on File { fileStatus }
... on Video { duration }
}
}"""
def wait_ready(file_id, tries=8):
delay = 2
for _ in range(tries):
r = requests.post(GQL, json={"query": Q, "variables": {"id": file_id}},
headers={"X-Shopify-Access-Token": os.environ["SHOPIFY_TOKEN"]},
timeout=30)
r.raise_for_status()
node = r.json()["data"]["node"]
if node["fileStatus"] == "READY":
return node.get("duration") # milliseconds, null until READY
time.sleep(delay)
delay = min(delay * 2, 30)
raise TimeoutError(f"{file_id} not READY; do not re-upload, check it again")What if the file never becomes READY?
Shopify's pages say nothing about how long processing takes or which failure statuses exist besides READY, so do not hard-code a time limit from guesswork. Pick a ceiling that suits your pipeline, such as eight checks with growing delays as in the sketch, then stop and surface the file id for a person to look at.
Do not create the file again as a retry. That leaves an orphan on the Files page and, if the upload came from a Sume render you paid for, wastes nothing on the Sume side but time. If the video is truly stuck, regenerate only after you know the cause, and use a new Idempotency-Key for the new render. Generation admission describes how queued and processing jobs count against your accepted-job capacity, so a stuck pipeline that keeps submitting can also fill your queue.
Is this the same as polling Sume jobs?
The pattern is the same, the systems are not. Sume job statuses are queued, processing, completed, failed and canceled; the last three are terminal, and the docs tell you to use exponential backoff and not to resubmit a paid request because a local process timed out. Shopify's gate is READY. Run both waits in order: Sume job to completed, upload, then Shopify file to READY.
Store both ids. When a run dies half-way, the Sume job id lets you read the finished clip again without resubmitting, and the Shopify file id lets you resume the wait.
Sources
Related posts
More in Integrations
- Slack Events API retries 3 times: keep one Sume run per event
Slack retries a missed Events API ack three times and can disable your subscriptions. Ack in 3 seconds and let an Idempotency-Key keep one Sume run per event.
- smolagents MCPClient: give a CodeAgent Sume's remote tools
Connect a smolagents CodeAgent to Sume's hosted MCP with MCPClient and the streamable-http transport, send an API key header, and wait on jobs correctly.
- Snap Marketing API ai_content_source: the AI declaration on Media
Snap added an ai_content_source attribute to the Media entity in its Marketing API on August 28, 2026. What the changelog says, and how to record it.
- Snapchat Ads API: set ai_content_source USER_AI_GEN on an AI video
Snap's create-media call has an optional ai_content_source field: USER_AI_GEN or SNAP_AI_GENERATED. Set it when you upload a Sume clip, then wait for READY.
Written by Sume