Synchronous captures are convenient for interactive use. Async jobs are better for long pages, full-page captures, authenticated dashboards, and batch pipelines.

Queue a job

json
{ "url": "https://example.com/dashboard", "async": true, "webhook_url": "https://example.com/webhooks/screenframed", "device": "browser-macos", "background_preset": "midnight"}

Response:

json
{ "job_id": "job_01K...", "status": "pending"}

Poll status

bash
curl "https://screenframed.com/v1/jobs/job_01K..." \ -H "Authorization: Bearer $SCREENFRAMED_API_KEY"

Completed jobs include the same render payload shape used by synchronous captures:

json
{ "id": "job_01K...", "status": "completed", "result": { "id": "cap_01K...", "url": "https://cdn.screenframed.com/r/cap_01K....png", "width": 1600, "height": 900, "format": "png", "credits_used": 3 }, "error": null, "webhook_url": "https://example.com/webhooks/screenframed", "webhook_status": "delivered", "created_at": "2026-04-27 15:00:00", "completed_at": "2026-04-27 15:00:04"}

CLI polling

bash
screenframed capture https://example.com --asyncscreenframed jobs status job_01K... --watch

Webhook payloads

Success:

json
{ "job_id": "job_01K...", "status": "completed", "result": { "id": "cap_01K...", "url": "https://cdn.screenframed.com/r/cap_01K....png" }}

Failure:

json
{ "job_id": "job_01K...", "status": "failed", "error": "Render failed: target page did not finish loading"}

Operational guidance

Async capture readiness
Use stable input payloads

Keep the same URL and styling payload for retryable jobs so cached renders can be reused.

Store the job id

Persist job_id before returning from your own request handler.

Make webhooks idempotent

Treat duplicate webhook deliveries as possible and upsert by job_id.

Ask a question... ⌘I