Keyword: async screenshot API ยท Updated July 30, 2026
Async Screenshot API with Webhooks: Queue Long Captures
Some screenshot jobs should not hold an HTTP client open while Chromium renders a page. A product preview queue, report generator, marketplace audit, or review workflow usually needs a job id immediately, then a callback when the image is ready. SnapshotFlow supports that pattern with async=true, signed webhooks, and a polling fallback.
Why revisit async screenshot workflows now?
Webhook reliability is getting fresh attention because more teams are wiring APIs into automation, agents, and internal queues. GitHub's current REST API guidance tells integrators to prefer webhooks over polling where possible and to use queues to avoid concurrency pressure. Stripe's webhook docs warn that duplicate events can occur and recommend asynchronous processing for handlers that may see delivery spikes.
Those are not screenshot-specific claims, but they are directly relevant to screenshot capture. A browser render is heavier and less predictable than a metadata lookup. If your caller only needs to register the work, send it to an async endpoint and process the completion event separately.
When async mode is the better fit
| Use async when | Use a normal screenshot request when |
|---|---|
| The caller should continue immediately after enqueueing a capture. | The user is waiting for a preview image in the current screen. |
| Your workflow already has a webhook receiver, queue, or background worker. | You are running a local script and can save the binary response directly. |
You need a stable job_id for audit logs, retry checks, or support tickets. | You only need one PNG, JPEG, WebP, or PDF now. |
A missed callback can be recovered by polling GET /jobs/:id. | You cannot expose a public HTTPS webhook endpoint. |
1. Submit the screenshot job
Call GET /screenshot with async=true. In production, webhook_url must be a public HTTPS URL; SnapshotFlow validates webhook URLs with the same private-network protection used for screenshot targets.
The API returns 202 Accepted with the job id and current status. If you supplied external_identifier, SnapshotFlow echoes it in the response so your app can confirm the correlation value without parsing the submitted URL.
2. Receive and verify the webhook
When the capture finishes, SnapshotFlow sends a JSON POST to your webhook URL. Authenticated accounts can create a webhook signing secret; when a secret exists, each delivery includes X-SnapshotFlow-Signature in the format t=<unix-seconds>,sha256=<hex>. Verify it against the raw request body before trusting the payload.
Minimal Express receiver
This example uses the official Node SDK helper. The important parts are raw-body verification, job_id deduplication, and returning 2xx only after your app has accepted the event.
3. Use the Node SDK when you prefer typed calls
The SnapshotFlow Node SDK maps camelCase options to the API's snake_case query parameters, starts async jobs with takeAsync(), checks status with getJob(), and can block until completion with waitForJob().
Operational advice and edge cases
- Keep a polling fallback. SnapshotFlow jobs are stored in memory and expire after one hour. Webhook delivery is best-effort with one retry for transient failures, so poll
GET /jobs/:idif a missed callback would block downstream work. - Deduplicate by SnapshotFlow's job id. Use
job_idorX-SnapshotFlow-Referenceas the idempotency key. Useexternal_identifierto connect the capture to your own record, such as a report id or queue id. - Treat signed URLs as handoff URLs. The webhook payload includes
result.storagePath. Copy the file to your durable storage if your business process needs long-term evidence. - Classify failures correctly. A failed capture webhook can include
error.codevalues such asQUOTA_EXCEEDED,BLOCKED,TIMEOUT,INVALID_URL, orNAVIGATION_FAILED. If you setwebhook_errors=false, SnapshotFlow omits the error block from failure payloads. - Use final HTTPS URLs. In production, webhook URLs must use HTTPS. Avoid redirects and placeholder endpoints; webhooks should hit the exact receiver path your app owns.
- Avoid credential leakage. Send the SnapshotFlow API key in
X-Api-Key. Keep webhook secrets in your server-side secret manager, not in a browser or client-side workflow.
FAQ
Does async mode make screenshots faster?
It makes the caller return faster. The browser still has to render the page, wait for the configured load condition, capture the output, and save the result before the webhook fires.
Can I use async mode without a webhook?
Yes. Submit async=true without webhook_url, keep the returned job_id, and poll GET /jobs/:id. This is useful for agents and backend jobs that already have a polling loop.
What happens if the webhook receiver returns 500?
SnapshotFlow treats 408, 429, 5xx, network errors, and delivery timeouts as retryable and makes one delayed retry. Other 3xx and 4xx responses are treated as permanent receiver-side failures, so fix the endpoint and use the job polling fallback if needed.
Sources and further reading
- GitHub REST API best practices: avoid polling, queue concurrent work, and handle rate limits
- GitHub webhook redelivery guide: failed delivery handling and duplicate-safe redelivery logic
- Stripe webhook docs: duplicate events, asynchronous handlers, HTTPS, and secret rotation
- Svix webhook verification guide: raw body, stable message id, timestamp, HMAC, and constant-time comparison
- Mambu webhook best practices, updated April 2026: async receiver design, idempotency, HTTPS, and status-code handling
- HookCap: Webhook Best Practices: Retry Logic, Idempotency, and Error Handling (April 2, 2026)
Build the callback path before scaling captures
Start with one URL, one webhook receiver, raw-body verification, and a polling fallback. Add queues, storage, and notifications after that path is observable.
Read async API docs