Skip to main content
Add a webhook object to any capture request and Screenshotly sends an HTTP request to your endpoint when the capture finishes. Webhooks are available on the Basic plan and above, on both POST /api/screenshot and POST /api/jobs.
Without a secret, deliveries are not signed and your endpoint cannot tell a real delivery from a forged one. Always set a secret and verify it.

Events

Synchronous requests report failures in the HTTP response, so they never send screenshot.failed.

Payloads

Every delivery is a JSON body with event, timestamp, and data:
screenshot.completed
screenshot.failed
  • size is in bytes and duration in milliseconds.
  • storageUrl is a signed download link valid for 24 hours. Async jobs always include it. Synchronous captures include it only when the request also sets "storage": { "enabled": true }; otherwise the file is only in the HTTP response.
  • The link may answer with a redirect to object storage, so download it with a client that follows redirects (for example curl -L).
  • The file itself is deleted at the end of the account’s retention period (1–30 days), even if the link has not expired.

Request headers

Delivery and retries

  • Deliveries are queued durably and sent by a worker, so they arrive shortly after the capture finishes, not inside the capture request.
  • Any 2xx response within 10 seconds counts as delivered. Redirects are not followed and count as failures, so point webhook.url at the final address.
  • A failed delivery is retried up to 5 attempts in total, after 5, 15, 45, and 135 seconds.
  • For async jobs, GET /api/jobs/{id} shows each delivery’s status, attempts, and deliveredAt.
A delivery can arrive more than once, for example if your endpoint processed the request but responded too slowly. Make handlers idempotent: key on the job or on data.url plus timestamp, and return 2xx quickly before doing slow work.

Verify the signature

When secret is set, X-Screenshotly-Signature is sha256= followed by the hex-encoded HMAC-SHA256 of the raw request body, keyed with your secret. Compute it over the exact bytes you received, before parsing JSON, and compare in constant time.
X-Screenshotly-Timestamp is not part of the signed content. It tells you when the event was created, but it does not protect against replays by itself, which is another reason to make handlers idempotent.

Usage webhooks

The account owner can set one Usage notification webhook URL in Account controls. It receives usage.warning and usage.limit events, at most once per type per billing month, with this payload:
Usage webhooks use the same delivery queue and retry schedule, but they are not signed because Account controls has no secret field. Treat them as a prompt to check the dashboard, not as an authenticated instruction.