React Query polling: stop refetchInterval when a job is done
Poll a video job with React Query's refetchInterval: call your backend, wait the delay the API suggests, and return false once the job is terminal.

To poll with React Query (TanStack Query), set refetchInterval on useQuery: a number of milliseconds, or a function that receives the query and returns the next delay, or false to stop. For an AI video job, return false once the job is terminal and otherwise wait the delay the API suggests, and point queryFn at your own backend, never at the video API, because the API key must stay on the server.
TanStack facts come from its Polling guide, UseQueryOptions reference, and Important Defaults; Sume facts come from Jobs and results, Authentication, and the Sume API reference. All were read on 2026-09-27. Sume has no React package: the browser calls your backend, and your backend calls Sume over HTTPS.
Why poll my backend instead of the video API?
Because the key cannot go to the browser. Sume's docs say browser and mobile clients should call your backend, which attaches the API key, and that keys never belong in frontend JavaScript or mobile apps. CORS error calling the Sume API from a browser covers what happens otherwise. Your status route validates the request and checks that the signed-in user owns the job, then reads GET /v1/jobs/{id}/status with the key and returns the payload's data object. A job started with POST /v1/videos can be read there too.
How do I stop polling when the job is done?
The status payload answers both questions the interval function asks: terminal says whether to stop, and next_poll_after_seconds says how long to wait; How to poll a video generation job status API covers the rest of the payload. TanStack's Polling guide uses the same shape for a job: returning false clears the interval timer, and polling resumes by itself if the function later returns a number again.
| Option or field | From | What it does |
|---|---|---|
refetchInterval | TanStack Query | Milliseconds, or a function of the query; false stops polling. Default false |
refetchIntervalInBackground | TanStack Query | Default false: polling pauses while the tab is in the background |
retry | TanStack Query | Failed queries retry 3 times on the client by default, with exponential backoff |
terminal | Sume job status | True when the job is completed, failed, or canceled and polling can stop |
next_poll_after_seconds | Sume job status | Suggested minimum delay before the next poll; null once terminal |
result_ready | Sume job status | True only when /result can return the result |
import { useQuery } from "@tanstack/react-query";
type JobStatus = {
sume_status: "queued" | "processing" | "completed" | "failed" | "canceled";
terminal: boolean;
result_ready: boolean;
next_poll_after_seconds: number | null;
};
export function useVideoJob(jobId: string) {
return useQuery({
queryKey: ["video-job", jobId],
queryFn: async (): Promise<JobStatus> => {
const res = await fetch(`/api/video-jobs/${jobId}`); // your backend
if (!res.ok) throw new Error(`status read failed: ${res.status}`);
return res.json();
},
refetchInterval: (query) => {
const job = query.state.data;
if (job?.terminal) return false; // completed, failed or canceled
return (job?.next_poll_after_seconds ?? 5) * 1000;
},
});
}What should the component do when the job ends?
Branch on sume_status once terminal is true:
completed: onceresult_readyis true, have your backend readGET /v1/jobs/{id}/resultand hand the component the media URL.failedorcanceled: the result route answers409 job_not_completedfor these, so your backend reads the error fromGET /v1/jobs/{id}instead.- In both cases the interval function has already returned
false, so no further polls go out.
What happens when the tab is hidden or a poll fails?
Polling pauses while the tab is in the background unless you set refetchIntervalInBackground: true. The job does not pause: a client that stops watching does not cancel it, and it keeps running and billing. The next poll picks it up.
A failed queryFn is retried before the query reports an error, and a 429 or 5xx on a status read means the read failed, not the work. Every tab polls through your server's key, and reads have their own per-minute budget per key, forty times the write budget. Honoring next_poll_after_seconds and pausing hidden tabs keep that traffic down.
Can I use long polling, SSE, or WebSockets instead?
Not against the Sume API. There is no SSE or WebSocket transport on the Developer API today, and GET /v1/jobs/:id/events is a pull snapshot, not a stream. The closest thing is sync mode on the submit, or its alias subscribe: one wait of at most 30 seconds, which video jobs routinely outlast. For push, send a webhook_url with the job (callback_url on POST /v1/videos) so Sume notifies your server when it ends, and let your server tell the browser however your app already does. AI video generation API progress updates covers what to show meanwhile.
Sources
Related posts
More in Integrations
- Roo Code MCP server: add Sume with type streamable-http
Add a remote MCP server to Roo Code: a streamable-http entry for Sume's hosted MCP, an API-key header, and paid tools kept out of alwaysAllow.
- How to run a Lambda function on a schedule with EventBridge
Use EventBridge Scheduler to invoke a Lambda function on a cron or rate schedule. For a daily AI video, key the run to the scheduled time and return.
- Shopify product video AI API with products/create webhooks
Answer Shopify's products/create webhook within five seconds, run a Sume Format from a queue, then upload the MP4 to Shopify with a staged upload.
- Slack bot to generate video: a slash command with Sume's API
Ack Slack's slash command within 3000 ms, submit POST /v1/videos with a callback_url, then post the URL to response_url when Sume's webhook lands.
Written by Sume