Changelog
Every change to the API that a caller can notice, newest first. Changes to this site, to billing or to anything behind the endpoint are not here; if your code cannot tell it happened, it is not a change to the API.
https://screenshotbuddy.io/api/v1, which is why the version in the path has not moved.
What we promise
The error codes and the status each one answers with are a
published contract. Renaming a code, or changing which situation it covers, is a breaking change: an
integration that retries on render_timeout and gives up on
blocked_host would silently start doing the wrong thing. That
waits for a version bump in the path.
Adding a code is not a breaking change, so treat one you do not recognise as the status it arrived with. The
human message next to it is free to be reworded, made more specific or translated at any time,
which is why nothing should ever branch on it.
New request parameters and new response headers arrive the same way: added, never repurposed. The OpenAPI document is generated from the same definitions the API validates against, so it is the machine-readable version of this page's present tense.
3 August 2026
The API went from one endpoint with one way in to the surface described by the rest of this documentation. Everything below landed on the same day and none of it changes an answer a caller was already getting.
A Postman collection
GET https://screenshotbuddy.io/api/v1/postman.json answers with the API as a Postman collection, built from the same
definitions the OpenAPI document is built from. It carries every
endpoint with every parameter it accepts, takes its token from one collection variable, and arrives with the
optional parameters listed but switched off. It needs no token itself, so importing it is something you can do
before you have one. Importing the collection.
Signed URLs for direct embedding
GET https://screenshotbuddy.io/api/v1/snap/signed takes the same capture parameters as /snap plus a
tokenId, an optional expires and an HMAC-SHA256 signature over them, so
a capture can go straight into an <img> tag without a token travelling to the browser. Every
token gained a signing secret of its own, revealed on demand under
Settings » API tokens, and a signed request is metered,
billed, cached and rate limited exactly as the bearer request it stands in for.
How to sign a URL.
Response caching and request dedup
Asking for the same capture twice now answers from the rendering already made for your account, and a hit costs
no credit. Responses say which it was with X-Cache: HIT or MISS and carry an
ETag that If-None-Match turns into a 304; cache=0 opts out
and cacheTtl chooses the lifetime. Identical requests that arrive at the same moment are rendered
once and answered twice. How caching works.
Credit and request metadata headers
Every API response now carries X-Request-Id, and every authenticated one adds
X-Credits-Limit, X-Credits-Remaining and X-Credits-Reset, so running low
is something you read off a successful response rather than discover on a failed one. The request id is the
same value as the request_id in an error
envelope and in our logs, which makes it the thing to quote in a support request.
What the headers mean.
The capture options the renderer already supported
/snap accepted a URL and little else. It now takes format, quality,
width, height, scale, delay, selector, the
four clip fields, omitBackground, waitForSelector and
waitUntil for screenshots, and paperFormat, landscape and the four
margins for PDFs. Options that contradict each other are refused rather than quietly ignored: naming a
selector, a clip and fullPage together is three different answers to
where the capture starts. Screenshot options and
PDF options.
Token permissions, expiry and rotation
A token now carries the two permissions the product actually has, screenshot and pdf,
checked before any credit is spent: asking for a PDF with a screenshot only token answers 403 and
missing_ability. Tokens can also be given an expiry when you
create them and rotated in place, which mints a replacement and deletes the old value in one step. Tokens that
existed before this were given both permissions, so none of them changed behaviour.
Permissions, expiry and rotation.
A usage endpoint and machine-readable error codes
GET https://screenshotbuddy.io/api/v1/usage reports your remaining, used and granted credits, your plan, when the
period resets and the rate limit in effect. It answers 200 whatever state the account is in and
has a throttle of its own, so polling it never eats into the budget for renders.
The usage endpoint.
Alongside it, every non-2xx answer under api/* became one envelope: a stable code, a
human message, an optional errors map and a request_id. Two failures
that used to share a status and differ only in their English now differ in their code, so a client can tell a
blocked host from a malformed URL without reading a sentence.
Every error code.
See the current surface, not the history
The playground runs the API as it stands today, with every option this page introduced, and hands you the matching code.