Long-running calls
Submit, wait, check back, and stop at a deadline you choose.
Most calls finish within the 30-second wait. Some take longer: a crawl of many pages, a large search. This guide is a complete loop that sends a call, waits, checks back and stops at a deadline you choose, in Python and Node.js.
The loop
Both versions do the same five things:
Send the call with
Prefer: wait=30, so a fast one finishes right away.Send an
Idempotency-Key, so that if the request has to be sent again, it returns the same job.If the job is still running, wait
poll_after_ms, then fetchpoll_url.Wait twice as long each time, up to 10 seconds between checks.
Stop at your deadline. The job keeps running, and you can fetch it later with its
job_id.
import osimport timeimport uuidimport requestsAPI = "https://data.upscrape.com"HEADERS = {"Authorization": f"Bearer {os.environ['UPSCRAPE_API_KEY']}"}def run(capability, input, deadline_seconds=300): # One key per piece of work: a retry after a lost answer gets the same job. key = str(uuid.uuid4()) response = requests.post( f"{API}/execute", headers={**HEADERS, "Prefer": "wait=30", "Idempotency-Key": key}, json={"capability": capability, "input": input}, ) job = response.json() if "error" in job and "job_id" not in job: raise RuntimeError(job["error"]["code"]) deadline = time.monotonic() + deadline_seconds delay = job.get("poll_after_ms", 2000) / 1000 while job["state"] not in ("completed", "failed"): if time.monotonic() > deadline: raise TimeoutError(f"job {job['job_id']} is still {job['state']}") time.sleep(delay) delay = min(delay * 2, 10) job = requests.get(job["poll_url"], headers=HEADERS).json() if job["state"] == "failed": raise RuntimeError(job["error"]["code"]) return job["results"][0]["data"]data = run("web.crawl", {"url": "https://docs.stripe.com/payments", "max_pages": 10})print(len(data["pages"]), "pages")
import { randomUUID } from "node:crypto";const API = "https://data.upscrape.com";const headers = { Authorization: `Bearer ${process.env.UPSCRAPE_API_KEY}` };const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));async function run(capability, input, deadlineMs = 300_000) { // One key per piece of work: a retry after a lost answer gets the same job. const response = await fetch(`${API}/execute`, { method: "POST", headers: { ...headers, "Content-Type": "application/json", Prefer: "wait=30", "Idempotency-Key": randomUUID(), }, body: JSON.stringify({ capability, input }), }); let job = await response.json(); if (job.error && !job.job_id) throw new Error(job.error.code); const deadline = Date.now() + deadlineMs; let delay = job.poll_after_ms ?? 2000; while (job.state !== "completed" && job.state !== "failed") { if (Date.now() > deadline) throw new Error(`job ${job.job_id} is still ${job.state}`); await sleep(delay); delay = Math.min(delay * 2, 10_000); job = await (await fetch(job.poll_url, { headers })).json(); } if (job.state === "failed") throw new Error(job.error.code); return job.results[0].data;}const data = await run("web.crawl", { url: "https://docs.stripe.com/payments", max_pages: 10 });console.log(data.pages.length, "pages");
Both print 10 pages; we ran them on 16 October 2026.
Make it survive a restart
In a real app, a process can stop while it waits. Store the job_id with your own record as soon as you have it. After a restart, fetch the stored job instead of sending the call again: a second call would start, and pay for, a second job.
If you don't know whether the first request arrived, send it again with the same Idempotency-Key. That returns the first job, whatever state it is in.
Many calls at once
To run a batch, send every call without Prefer, so each answers 202 at once, and store the job_ids. Then check on them together. Your account runs a set number of calls at once, and the rest wait in line; Credits and limits gives the numbers.
Over MCP
An AI agent's calls work the same way: a slow call hands back a job_id, and the agent fetches it with upscrape_get_job_result. MCP jobs and results covers large results too.
Something missing or unclear? Ask the team that builds the endpoints.