About Ssnap
Who runs the service, how it is built, how these docs are maintained, and how to reach a human.
Ssnap is a screenshot and PDF API: one HTTP endpoint that renders any public web page to
jpeg, png, webp or pdf. This page covers who is behind it and how it actually
works, because "trust the API with your production pipeline" is a fair thing to want
evidence for.
How captures are produced
Every capture is rendered by headless Chromium — a real browser engine, not an HTML-
to-image converter. That is why your @media print rules, CSS grid, web fonts,
prefers-color-scheme and client-side charts all behave the way they do in a browser.
The request path, in order:
- The API key is resolved and checked against the per-minute rate limit for that key.
- The target url is validated as publicly routable — private and internal addresses are refused. See URL restrictions.
- If
cache_ttlis set and a matching earlier capture exists, that render is returned without launching a browser. - The team's monthly quota is checked and a capture slot is reserved.
- Chromium renders the page; image formats are post-processed; the result is stored.
- You get bytes, a signed link, or a webhook.
A capture that fails mid-render releases its reserved slot, so broken renders are not billed against your quota.
Security and data handling
- API keys are stored as SHA-256 hashes. The plaintext key is shown once, at creation, and cannot be recovered afterwards — which is also why a lost key has to be replaced rather than retrieved. See API keys.
- Keys can be scoped in time. An expiry date is optional at creation and enforced on
every request; expired keys fail with
api_key_expired. - Captures are team-scoped. The cache will never serve another team's render, because team ownership is part of the lookup, not just the cache key.
- Outbound fetches are constrained. Both
urlandcallback_urlgo through the same public-host checks, so the API cannot be used to reach private infrastructure. - Webhook deliveries are signed, so your endpoint can reject anything that did not come from Ssnap. See Verify webhook signatures.
- Stored captures expire. Files are kept for a bounded retention window and then removed by a scheduled job — details in Storage and retention.
- Signed links are short-lived. A
response=urllink is valid for 24 hours and carries no credential beyond its own signature.
Platform health
ssnap.cc/status is checked live on request rather than served
from a cached dashboard, and it probes the things that actually break: database, cache,
the browser engine (by rendering a real test page), the async queue depth and the recent
capture success rate. There is a lightweight GET /up endpoint for uptime monitors.
More detail in Status and support.
About these docs
- Written and maintained by the team that builds the API, not generated from a schema.
- Every parameter range, default and error code on this site reflects the behaviour of the deployed service. Where behaviour is surprising, it is documented as such rather than smoothed over.
- Each page carries a last-updated date, taken from the commit that changed it.
- Every page is available as Markdown for machine consumption: append
.md, use the copy button, or see llms.txt and llms-full.txt. - Found something wrong or out of date? Mail it to [email protected] and it gets fixed.
Contact
| Channel | Where |
|---|---|
| [email protected] | |
| Contact form | ssnap.cc/contact |
| In-app | The feedback control in your dashboard |
| Status | ssnap.cc/status |
When reporting a failed capture, include the target url, the full parameter set, the
response status and the code from the body. That is normally enough to reproduce it
without a back-and-forth.