Next.js 16.4 GET route handler: poll a Sume job at request time
With Cache Components, Next.js 16.4 runs GET handlers at request time. A Sume job status route stays live if it sends no-store and follows the poll hint.

A GET route that proxies a Sume job status stays live in Next.js 16.4 as long as it reads something per request, such as a query parameter or a header. The Route Handlers page (version 16.4.0) says that with Cache Components, GET handlers run at request time by default and prerendering stops when the handler touches request.body or headers(). Your browser can then poll the route and see fresh status.
This post shows a status route that forwards the poll, passes through the wait hint Sume returns, and avoids the one mistake that serves a stale processing state.
What the 16.4 docs say about GET
Before Cache Components, a GET handler with no dynamic input could be prerendered under force-static. The docs still list that opt-in, and they say other methods are never cached. In 16.4 the recommended mode runs GET at request time unless you opt in.
Do not add use cache inside the handler body; the docs say it cannot sit directly there. If you cache a helper, never cache the Sume status call.
| Method | Cached by default | How to opt in |
|---|---|---|
| GET | No | export const dynamic = 'force-static' |
| POST | Never | Not possible |
| GET with Cache Components | Runs at request time | use cache in a helper, not the handler body |
GET that reads headers() | Prerender stops | Nothing to do |
What the Sume status payload gives you
A Sume job envelope returns status (queued, processing, completed, failed, canceled), terminal, result_ready and next_poll_after_seconds. Pass the wait hint to the browser instead of picking your own interval.
Poll only until terminal is true. A timeout on your side does not cancel the job, and you should not submit again; a retry of the submit uses the same Idempotency-Key.
A status route
The route reads the job id from the URL and forwards one request. The Sume client sends the key in x-api-key; do not add an Authorization header as well.
export async function GET(request: Request) {
const id = new URL(request.url).searchParams.get("job");
const key = process.env.SUME_API_KEY;
if (!id || !key) return Response.json({ error: "bad request" }, { status: 400 });
const res = await fetch(`https://api.sume.com/v1/jobs/${encodeURIComponent(id)}`, {
headers: { "x-api-key": key },
cache: "no-store",
});
const body = await res.json();
return Response.json(body, {
status: res.status,
headers: { "cache-control": "no-store" },
});
}Limits to plan for
Status reads can hit 429 rate_limited; treat that as poll backpressure and read retry-after. For long jobs, prefer a webhook and use this route only as a fallback for the page that shows progress.
- Read
next_poll_after_secondsand return it to the client. - Send
cache-control: no-storeso a CDN never stores aprocessingresponse. - Never expose the API key to browser code.
Choose the polling client
A browser that polls your route should wait for the value your route returns, not a fixed 2 seconds. Return next_poll_after_seconds in your own response and have the client schedule its next call from it. If the job is queued, Sume may ask for a longer interval than when it is processing, and a long queue is exactly when a fixed fast loop hurts.
Stop on terminal. Then, if result_ready is true, make a second request for the result so that your status route stays small. Media URLs on media.sume.com are durable, so the result page can be cached after the job ends without a staleness problem for the file itself.
- Do not poll from many tabs; one poll per job per user is enough.
- Do not retry a failed status read faster than
retry-afterallows. - Show
queuedas waiting, not as an error; a full concurrency limit still accepts the job.
Sources
Related posts
More in Developers
- Omni Flash API errors: which to retry and which to fix
A retry policy for Gemini Omni Flash 1.1 calls on Sume: 400, 401, 402, 409, 429 rate_limited, 429 queue_full and 503 each mapped to retry, wait or fix.
- Omni job status names: pending vs queued, cancelled vs canceled
Sume's /v1/videos poll uses pending, in_progress, completed, failed and cancelled; /v1/jobs uses queued, processing and canceled. Mapping table for Omni code.
- Replace videos.create_and_poll with a requests helper on Sume
The OpenAI Python SDK's create_and_poll and download_content have no Sume twin. Here is a 25-line requests helper with the same call shape and a safe retry key.
- OpenRouter video client on Sume: swap the base URL, change webhooks
A client written for OpenRouter /videos works on Sume after you change the base URL, key and model ids. The webhook body and signature are different.
Written by Sume