Poll for results
Poll a task's status until it settles, then read the result, from a server with an API key or from a browser with the task's pat_ token.
A create call returns a task id, not the file. Poll GET /tasks/{id}/status
until status leaves pending, then read GET /tasks/{id}/result. Both are
served from a cache and are the calls meant for a loop. GET /tasks/{id} is
the full record and is not.
The SDK's generate() does this for you. Poll yourself when you want control
over the loop, or when a browser does the waiting.
const API = "https://api.mynth.io";
const headers = { Authorization: `Bearer ${process.env.MYNTH_API_KEY}` };
async function waitForTask(taskId: string, { timeoutMs = 30 * 60_000 } = {}) {
const deadline = Date.now() + timeoutMs;
let delay = 2_000;
while (Date.now() < deadline) {
const res = await fetch(`${API}/tasks/${taskId}/status`, { headers });
if (!res.ok && res.status < 500) throw new Error(`status ${res.status}`);
if (res.ok) {
const { data } = await res.json();
if (data.status !== "pending") {
const result = await fetch(`${API}/tasks/${taskId}/result`, { headers });
return (await result.json()).data; // { id, type, status, result }
}
}
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, 10_000);
}
throw new Error(`Task ${taskId} still pending after ${timeoutMs} ms`);
}Start near 2 seconds and back off to about 10. Most images finish in seconds.
Video takes minutes, so give it a longer timeout. Retry 5xx and network
errors. A 4xx fails the same way every time.
When the loop returns:
status: "completed": check status on each item in result.images or
result.videos, because a completed task can hold failed items. See
tasks.status: "failed": /result does not include errors. Read
GET /tasks/{id} for the codes.Copy the file when you see completed if you need it for more than 7 days
and did not name a destination.
POST /image/generate, POST /image/remove-background,
POST /image/upscale, and POST /video/generate return
data.public_access_token, a pat_ token for that one task, when the request
sets generate_public_access_token: true. It works as a bearer token on the status and result calls
for that task and nowhere else, and it expires one hour after it is issued.
Only those two paths accept cross-origin requests.
Create the task on your server and hand the browser only the id and the token:
// server
import Mynth from "@mynthio/sdk";
const mynth = new Mynth();
const pending = await mynth.image.generateAsync({
model: "black-forest-labs/flux.2-pro",
prompt: "A lighthouse at dusk, film grain",
generate_public_access_token: true,
});
return Response.json({ taskId: pending.id, token: pending.access.publicAccessToken });// browser
async function pollInBrowser(taskId: string, token: string) {
const headers = { Authorization: `Bearer ${token}` };
for (;;) {
const res = await fetch(`https://api.mynth.io/tasks/${taskId}/status`, { headers });
if (res.status === 401) throw new Error("Token expired or invalid. Ask the server again.");
const { data } = await res.json();
if (data.status !== "pending") {
const result = await fetch(`https://api.mynth.io/tasks/${taskId}/result`, { headers });
return (await result.json()).data;
}
await new Promise((resolve) => setTimeout(resolve, 3_000));
}
}500 public_access_token_failed with the task's id in error.task_id (in
the SDK, MynthAPIError.taskId). Don't resend: poll that task from the
server.401 token_expired. A long video render can
outlive the hour, so use a webhook or server-side
polling for those.generate_public_access_token: true no token is issued. Leave it
out when only your server polls or you use webhooks.Never send the mak_ key to the browser. The pat_ token can read one task's status and result
and do nothing else.
npx @mynthio/cli task wait tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP --jsontask wait prints the finished task. A failed task prints too, with a
non-zero exit code: 5 for restricted_content and 1 for anything else.
The default timeout is 30 minutes. Change it with --timeout <seconds>.