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.

5 min readSume
All posts

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.

From TanStack's Polling, UseQueryOptions, and Important Defaults pages and the job status schema in the Sume API reference, read 2026-09-27.
Option or fieldFromWhat it does
refetchIntervalTanStack QueryMilliseconds, or a function of the query; false stops polling. Default false
refetchIntervalInBackgroundTanStack QueryDefault false: polling pauses while the tab is in the background
retryTanStack QueryFailed queries retry 3 times on the client by default, with exponential backoff
terminalSume job statusTrue when the job is completed, failed, or canceled and polling can stop
next_poll_after_secondsSume job statusSuggested minimum delay before the next poll; null once terminal
result_readySume job statusTrue 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: once result_ready is true, have your backend read GET /v1/jobs/{id}/result and hand the component the media URL.
  • failed or canceled: the result route answers 409 job_not_completed for these, so your backend reads the error from GET /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

All Integrations posts

Written by Sume