snapframe renders any URL to PDF, screenshot, or social preview image with one API call. Built for developers who need "Export to PDF" without running a browser farm. Paid plans, manual provisioning, no credit card needed.
Paste any public URL. We'll render a real screenshot or PDF, live, no signup.
Free demo: 5 renders/hour per visitor. Full API keys get much higher limits.
This demo ran on the live API. An invoice page goes in, a print-ready A4 PDF comes out in seconds.
A normal HTML invoice — the kind your app already renders for customers.
Your backend POSTs the page URL. Add ?sync=true to get the file back in the same call — no polling.
A finished PDF, ready to email or offer as a download. One render = one quota unit.

Your API key goes in the X-API-Key header. Full interactive reference: api.snapframe.mooo.com/docs (OpenAPI).
curl -X POST "https://api.snapframe.mooo.com/v1/pdf?sync=true" \ -H "X-API-Key: snap_your_key" \ -H "Content-Type: application/json" \ -d '{"url":"https://yourapp.com/invoices/2041","format":"A4"}' \ -o invoice.pdf
import requests API, H = "https://api.snapframe.mooo.com", {"X-API-Key": "snap_your_key"} pdf = requests.post(API + "/v1/pdf?sync=true", headers=H, json={"url": "https://yourapp.com/invoices/2041"}).content open("invoice.pdf", "wb").write(pdf)
const API = "https://api.snapframe.mooo.com"; const H = { "X-API-Key": "snap_your_key", "Content-Type": "application/json" }; const pdf = await fetch(API + "/v1/pdf?sync=true", { method: "POST", headers: H, body: JSON.stringify({ url: "https://yourapp.com/invoices/2041", format: "A4" }) }).then(r => r.arrayBuffer());
| Endpoint | Key parameters |
|---|---|
POST /v1/pdf | url or html (one required, max 500KB) · format: A4 / Letter · landscape · print_background · margin_top/bottom/left/right ("1cm") · display_header_footer + header_template/footer_template · wait_ms (0–15000) · ?sync=true |
POST /v1/screenshot | url or html (one required, max 500KB) · width 320–3840 · height 240–2160 · full_page · format: png / jpeg · dark_mode · wait_ms · ?sync=true |
POST /v1/og-image | url (required) · fixed 1200×630 viewport · ?sync=true |
GET /v1/jobs/{id} | status: queued → processing → done / failed |
GET /v1/results/{id} | Downloads the file. Results kept 14 days. |
GET /v1/usage | Returns plan, quota_monthly, used_this_month. |
| All render endpoints | headers (custom request headers) · cookies (for authenticated pages) · webhook_url (POSTed on async completion) · GET /v1/status (public uptime + queue depth) |
| Code | Meaning |
|---|---|
400 | Bad URL (also: SSRF-blocked private/internal address). |
401 | Missing or unknown API key. |
403 | Key is paused/disabled. |
422 | Invalid parameters (see the parameter table). |
429 | Monthly quota exceeded — or 60 requests/minute per key. |
503 | Render queue temporarily unavailable — retry. |
One render = one screenshot, PDF, or OG image. Polling, downloads, and usage checks are free.
Plans are provisioned manually after payment — request access and we'll confirm your plan within a day.
Email us and after payment is confirmed you receive an API key with your monthly quota — usually within a day.
Only finished renders: one screenshot, PDF, or OG image = one render. Polling job status, downloading results, and checking usage are free.
New renders pause with a clear "monthly quota exceeded" message until your quota resets next month (or you upgrade). Your files and stats stay available.
Two ways: give a publicly reachable URL (a signed URL works for semi-private content), or send your HTML directly in the html field — nothing is fetched, so login-walled content renders fine. External images/fonts inside your HTML must use absolute URLs.
Render results are kept for 14 days so you can download them, then automatically deleted.