{"openapi":"3.0.3","info":{"title":"proofito","version":"1.1.0","description":"Document-rendering API: URL screenshots, PDFs, and OG social images from one engine. Every /v1/* endpoint takes `Authorization: Bearer <key>` (keys are self-serve via Google login at https://astraeatech.dev). Free allows 1,000 metered requests/month, Pro 50,000; past the quota the API returns 429. Errors share one JSON envelope: `{\"error\": {\"code\": \"...\", \"message\": \"...\"}}`.","contact":{"email":"admin@astraeatech.dev"}},"servers":[{"url":"https://api.astraeatech.dev/proofito","description":"Production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Renders","description":"Screenshot / PDF / OG rendering (authenticated, metered, cached)."},{"name":"Usage","description":"Key usage reporting (authenticated, never metered)."},{"name":"Meta","description":"Liveness and machine-readable descriptions (public)."}],"paths":{"/v1/screenshot":{"get":{"summary":"Screenshot a URL","description":"Render an http(s) URL in real Chromium and return a PNG or JPEG screenshot.","tags":["Renders"],"parameters":[{"name":"url","in":"query","required":true,"description":"Absolute http(s) URL to render. Localhost, cloud-metadata, and non-http(s) targets are rejected (400 invalid_url).","schema":{"type":"string","format":"uri"}},{"name":"width","in":"query","description":"Viewport width in CSS pixels.","schema":{"type":"integer","minimum":1,"maximum":3840,"default":1200}},{"name":"height","in":"query","description":"Viewport height in CSS pixels.","schema":{"type":"integer","minimum":1,"maximum":2160,"default":800}},{"name":"full_page","in":"query","description":"Capture the full scrollable page instead of the viewport (true/false/1/0). Pages taller than 16000px are rejected with guidance (422 `render_failed`); cannot combine with `selector` or `clip_*`.","schema":{"type":"boolean","default":false}},{"name":"format","in":"query","description":"Image format (case-insensitive).","schema":{"type":"string","enum":["png","jpeg"],"default":"png"}},{"name":"device_scale","in":"query","description":"Device pixel ratio (1-3); 2-3 produce retina captures.","schema":{"type":"integer","minimum":1,"maximum":3,"default":1}},{"name":"quality","in":"query","description":"JPEG quality 1-100 (`format=jpeg` only; absent leaves Chromium's default).","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"wait_ms","in":"query","description":"Extra settle time after load in milliseconds (on top of the fixed settle).","schema":{"type":"integer","minimum":0,"maximum":10000,"default":0}},{"name":"timeout_ms","in":"query","description":"Per-request render cap in milliseconds (can only tighten the worker default).","schema":{"type":"integer","minimum":1000,"maximum":60000}},{"name":"selector","in":"query","description":"CSS selector to capture instead of the viewport (exclusive with `clip_*`, viewport-mode only).","schema":{"type":"string","maxLength":512}},{"name":"clip_x","in":"query","description":"Clip rectangle left edge in CSS pixels (all four `clip_*` must be given together).","schema":{"type":"integer","minimum":0,"maximum":20000}},{"name":"clip_y","in":"query","description":"Clip rectangle top edge in CSS pixels.","schema":{"type":"integer","minimum":0,"maximum":20000}},{"name":"clip_w","in":"query","description":"Clip rectangle width in CSS pixels.","schema":{"type":"integer","minimum":1,"maximum":20000}},{"name":"clip_h","in":"query","description":"Clip rectangle height in CSS pixels.","schema":{"type":"integer","minimum":1,"maximum":20000}},{"name":"block","in":"query","description":"Comma-separated request-blocking presets: ads, trackers, fonts, media (case-insensitive).","schema":{"type":"string"}},{"name":"ttl","in":"query","description":"Freshness window in seconds; 0 disables all caching for the request.","schema":{"type":"integer","minimum":0,"maximum":604800,"default":86400}}],"responses":{"200":{"description":"Rendered image bytes.","headers":{"X-Cache":{"description":"`HIT` when served from the render cache, `MISS` on a fresh render.","schema":{"type":"string","enum":["HIT","MISS"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}},"Cache-Control":{"description":"`public, max-age=<ttl>` on cached renders, `no-store` when `ttl=0`.","schema":{"type":"string"}},"X-Cache-Key":{"description":"R2 object key of this render (pass to `DELETE /v1/cache` to purge it; omitted when `ttl=0`).","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Metered requests left this month after this request.","schema":{"type":"integer"}}},"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_url | invalid_option\", ...}}` — bad url or out-of-range option.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_api_key\", ...}}` — missing, malformed, unknown, or revoked key; the only response that is never metered.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Error envelope `{\"error\": {\"code\": \"render_failed | render_timeout\", ...}}` — Chromium launch, navigation, or timeout failure inside the render.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope `{\"error\": {\"code\": \"quota_exceeded\", ...}}` — plan monthly cap reached (Free 1,000, Pro 50,000 metered requests); resets the first of next month UTC.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Always 0 on a 429.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Screenshot inline HTML","description":"Render an inline HTML document in real Chromium and return a PNG or JPEG screenshot.","tags":["Renders"],"requestBody":{"required":true,"description":"JSON object with the HTML source plus screenshot options. Bodies over 256 KB are rejected (413).","content":{"application/json":{"schema":{"type":"object","required":["html"],"properties":{"html":{"type":"string","description":"Full HTML document to capture (non-empty)."},"width":{"type":"integer","minimum":1,"maximum":3840,"default":1200,"description":"Viewport width in CSS pixels."},"height":{"type":"integer","minimum":1,"maximum":2160,"default":800,"description":"Viewport height in CSS pixels."},"full_page":{"type":"boolean","default":false,"description":"Capture the full scrollable page (pages taller than 16000px are rejected with guidance, 422)."},"format":{"type":"string","enum":["png","jpeg"],"default":"png","description":"Image format (case-insensitive)."},"device_scale":{"type":"integer","minimum":1,"maximum":3,"default":1,"description":"Device pixel ratio."},"quality":{"type":"integer","minimum":1,"maximum":100,"description":"JPEG quality (`format=jpeg` only)."},"wait_ms":{"type":"integer","minimum":0,"maximum":10000,"default":0,"description":"Extra settle time in milliseconds."},"timeout_ms":{"type":"integer","minimum":1000,"maximum":60000,"description":"Per-request render cap in milliseconds."},"selector":{"type":"string","maxLength":512,"description":"CSS selector to capture."},"clip_x":{"type":"integer","minimum":0,"maximum":20000,"description":"Clip rectangle left edge."},"clip_y":{"type":"integer","minimum":0,"maximum":20000,"description":"Clip rectangle top edge."},"clip_w":{"type":"integer","minimum":1,"maximum":20000,"description":"Clip rectangle width."},"clip_h":{"type":"integer","minimum":1,"maximum":20000,"description":"Clip rectangle height."},"block":{"type":"string","description":"Comma-separated blocking presets: ads, trackers, fonts, media (case-insensitive)."},"ttl":{"type":"integer","minimum":0,"maximum":604800,"default":86400,"description":"Freshness window in seconds; 0 disables caching."}}}}}},"responses":{"200":{"description":"Rendered image bytes.","headers":{"X-Cache":{"description":"`HIT` when served from the render cache, `MISS` on a fresh render.","schema":{"type":"string","enum":["HIT","MISS"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}},"Cache-Control":{"description":"`public, max-age=<ttl>` on cached renders, `no-store` when `ttl=0`.","schema":{"type":"string"}},"X-Cache-Key":{"description":"R2 object key of this render (pass to `DELETE /v1/cache` to purge it; omitted when `ttl=0`).","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Metered requests left this month after this request.","schema":{"type":"integer"}}},"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_body | invalid_option\", ...}}` — malformed JSON, missing html, or out-of-range option.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_api_key\", ...}}` — missing, malformed, unknown, or revoked key; the only response that is never metered.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Error envelope `{\"error\": {\"code\": \"payload_too_large\", ...}}` — request body (or inline html) exceeds 256 KB.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Error envelope `{\"error\": {\"code\": \"unsupported_media_type\", ...}}` — Content-Type is not application/json.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Error envelope `{\"error\": {\"code\": \"render_failed | render_timeout\", ...}}` — Chromium launch, navigation, or timeout failure inside the render.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope `{\"error\": {\"code\": \"quota_exceeded\", ...}}` — plan monthly cap reached (Free 1,000, Pro 50,000 metered requests); resets the first of next month UTC.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Always 0 on a 429.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/pdf":{"get":{"summary":"Render a URL to PDF","description":"Print an http(s) URL to PDF with Chromium's print pipeline.","tags":["Renders"],"parameters":[{"name":"url","in":"query","required":true,"description":"Absolute http(s) URL to render. Localhost, cloud-metadata, and non-http(s) targets are rejected (400 invalid_url).","schema":{"type":"string","format":"uri"}},{"name":"format","in":"query","description":"Paper size (case-insensitive).","schema":{"type":"string","enum":["A4","Letter"],"default":"A4"}},{"name":"margin","in":"query","description":"Uniform page margin in millimetres (every side a per-side margin does not override).","schema":{"type":"integer","minimum":0,"maximum":100,"default":10}},{"name":"margin_top","in":"query","description":"Top page margin in millimetres (overrides `margin` for that side).","schema":{"type":"integer","minimum":0,"maximum":100}},{"name":"margin_right","in":"query","description":"Right page margin in millimetres (overrides `margin` for that side).","schema":{"type":"integer","minimum":0,"maximum":100}},{"name":"margin_bottom","in":"query","description":"Bottom page margin in millimetres (overrides `margin` for that side).","schema":{"type":"integer","minimum":0,"maximum":100}},{"name":"margin_left","in":"query","description":"Left page margin in millimetres (overrides `margin` for that side).","schema":{"type":"integer","minimum":0,"maximum":100}},{"name":"landscape","in":"query","description":"Print in landscape orientation.","schema":{"type":"boolean","default":false}},{"name":"scale","in":"query","description":"Print scale factor (0.1-2.0).","schema":{"type":"number","minimum":0.1,"maximum":2,"default":1}},{"name":"display_header_footer","in":"query","description":"Show the header/footer templates on every page (`displayHeaderFooter` is accepted as an alias).","schema":{"type":"boolean","default":false}},{"name":"header_template","in":"query","description":"Self-contained HTML for the page header (requires `display_header_footer=true`; `headerTemplate` is accepted as an alias; use the date/title/url/pageNumber/totalPages classes to inject print values).","schema":{"type":"string","maxLength":32768}},{"name":"footer_template","in":"query","description":"Self-contained HTML for the page footer (requires `display_header_footer=true`; `footerTemplate` is accepted as an alias).","schema":{"type":"string","maxLength":32768}},{"name":"print_bg","in":"query","description":"Keep background graphics (`printBackground` is accepted as an alias).","schema":{"type":"boolean","default":true}},{"name":"timeout_ms","in":"query","description":"Per-request render cap in milliseconds (can only tighten the worker default).","schema":{"type":"integer","minimum":1000,"maximum":60000}},{"name":"ttl","in":"query","description":"Freshness window in seconds; 0 disables all caching for the request.","schema":{"type":"integer","minimum":0,"maximum":604800,"default":86400}}],"responses":{"200":{"description":"Rendered PDF bytes.","headers":{"X-Cache":{"description":"`HIT` when served from the render cache, `MISS` on a fresh render.","schema":{"type":"string","enum":["HIT","MISS"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}},"Cache-Control":{"description":"`public, max-age=<ttl>` on cached renders, `no-store` when `ttl=0`.","schema":{"type":"string"}},"X-Cache-Key":{"description":"R2 object key of this render (pass to `DELETE /v1/cache` to purge it; omitted when `ttl=0`).","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Metered requests left this month after this request.","schema":{"type":"integer"}}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_url | invalid_option\", ...}}` — bad url or out-of-range option.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_api_key\", ...}}` — missing, malformed, unknown, or revoked key; the only response that is never metered.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Error envelope `{\"error\": {\"code\": \"render_failed | render_timeout\", ...}}` — Chromium launch, navigation, or timeout failure inside the render.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope `{\"error\": {\"code\": \"quota_exceeded\", ...}}` — plan monthly cap reached (Free 1,000, Pro 50,000 metered requests); resets the first of next month UTC.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Always 0 on a 429.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Render inline HTML to PDF","description":"Print an inline HTML document to PDF with Chromium's print pipeline.","tags":["Renders"],"requestBody":{"required":true,"description":"JSON object with the HTML source plus print options. Bodies over 256 KB are rejected (413).","content":{"application/json":{"schema":{"type":"object","required":["html"],"properties":{"html":{"type":"string","description":"Full HTML document to print (non-empty)."},"format":{"type":"string","enum":["A4","Letter"],"default":"A4","description":"Paper size (case-insensitive)."},"margin":{"type":"integer","minimum":0,"maximum":100,"default":10,"description":"Uniform page margin in millimetres."},"margin_top":{"type":"integer","minimum":0,"maximum":100,"description":"Top margin (overrides `margin`)."},"margin_right":{"type":"integer","minimum":0,"maximum":100,"description":"Right margin (overrides `margin`)."},"margin_bottom":{"type":"integer","minimum":0,"maximum":100,"description":"Bottom margin (overrides `margin`)."},"margin_left":{"type":"integer","minimum":0,"maximum":100,"description":"Left margin (overrides `margin`)."},"landscape":{"type":"boolean","default":false,"description":"Print in landscape orientation."},"scale":{"type":"number","minimum":0.1,"maximum":2,"default":1,"description":"Print scale factor."},"display_header_footer":{"type":"boolean","default":false,"description":"Show the header/footer templates."},"displayHeaderFooter":{"type":"boolean","description":"Alias of `display_header_footer` (used only when `display_header_footer` is absent)."},"header_template":{"type":"string","maxLength":32768,"description":"Self-contained HTML page header."},"headerTemplate":{"type":"string","maxLength":32768,"description":"Alias of `header_template`."},"footer_template":{"type":"string","maxLength":32768,"description":"Self-contained HTML page footer."},"footerTemplate":{"type":"string","maxLength":32768,"description":"Alias of `footer_template`."},"print_bg":{"type":"boolean","default":true,"description":"Keep background graphics."},"printBackground":{"type":"boolean","description":"Alias of `print_bg` (used only when `print_bg` is absent)."},"timeout_ms":{"type":"integer","minimum":1000,"maximum":60000,"description":"Per-request render cap in milliseconds."},"ttl":{"type":"integer","minimum":0,"maximum":604800,"default":86400,"description":"Freshness window in seconds; 0 disables caching."}}}}}},"responses":{"200":{"description":"Rendered PDF bytes.","headers":{"X-Cache":{"description":"`HIT` when served from the render cache, `MISS` on a fresh render.","schema":{"type":"string","enum":["HIT","MISS"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}},"Cache-Control":{"description":"`public, max-age=<ttl>` on cached renders, `no-store` when `ttl=0`.","schema":{"type":"string"}},"X-Cache-Key":{"description":"R2 object key of this render (pass to `DELETE /v1/cache` to purge it; omitted when `ttl=0`).","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Metered requests left this month after this request.","schema":{"type":"integer"}}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_body | invalid_option\", ...}}` — malformed JSON, missing html, or out-of-range option.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_api_key\", ...}}` — missing, malformed, unknown, or revoked key; the only response that is never metered.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Error envelope `{\"error\": {\"code\": \"payload_too_large\", ...}}` — request body (or inline html) exceeds 256 KB.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Error envelope `{\"error\": {\"code\": \"unsupported_media_type\", ...}}` — Content-Type is not application/json.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Error envelope `{\"error\": {\"code\": \"render_failed | render_timeout\", ...}}` — Chromium launch, navigation, or timeout failure inside the render.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope `{\"error\": {\"code\": \"quota_exceeded\", ...}}` — plan monthly cap reached (Free 1,000, Pro 50,000 metered requests); resets the first of next month UTC.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Always 0 on a 429.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/og":{"get":{"summary":"Render an OG social image","description":"Return a social image (default 1200x630 JPEG, quality 90). Two modes: pass `title` for the built-in template, or `url` to capture a page.","tags":["Renders"],"parameters":[{"name":"title","in":"query","description":"Headline for the built-in template (template mode). Required unless `url` is given.","schema":{"type":"string","maxLength":300}},{"name":"subtitle","in":"query","description":"Sub-headline for the built-in template (template mode only).","schema":{"type":"string","maxLength":300}},{"name":"url","in":"query","description":"Page to capture (url mode). Required when `title` is absent.","schema":{"type":"string","format":"uri"}},{"name":"theme","in":"query","description":"Template theme (template mode; case-insensitive).","schema":{"type":"string","enum":["light","dark"],"default":"light"}},{"name":"layout","in":"query","description":"Template layout (template mode; case-insensitive).","schema":{"type":"string","enum":["classic","minimal"],"default":"classic"}},{"name":"accent","in":"query","description":"Brand accent as a 6-digit hex color, e.g. `#4f46e5` (template mode).","schema":{"type":"string"}},{"name":"logo_url","in":"query","description":"Logo image URL, fetched worker-side and embedded (template mode; must be an image under 256 KB).","schema":{"type":"string","format":"uri"}},{"name":"format","in":"query","description":"Image format (case-insensitive).","schema":{"type":"string","enum":["png","jpeg"],"default":"jpeg"}},{"name":"width","in":"query","description":"Capture width in CSS pixels.","schema":{"type":"integer","minimum":600,"maximum":2400,"default":1200}},{"name":"height","in":"query","description":"Capture height in CSS pixels.","schema":{"type":"integer","minimum":300,"maximum":1260,"default":630}},{"name":"timeout_ms","in":"query","description":"Per-request render cap in milliseconds (can only tighten the worker default).","schema":{"type":"integer","minimum":1000,"maximum":60000}},{"name":"ttl","in":"query","description":"Freshness window in seconds; 0 disables all caching for the request.","schema":{"type":"integer","minimum":0,"maximum":604800,"default":86400}}],"responses":{"200":{"description":"Rendered image bytes.","headers":{"X-Cache":{"description":"`HIT` when served from the render cache, `MISS` on a fresh render.","schema":{"type":"string","enum":["HIT","MISS"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}},"Cache-Control":{"description":"`public, max-age=<ttl>` on cached renders, `no-store` when `ttl=0`.","schema":{"type":"string"}},"X-Cache-Key":{"description":"R2 object key of this render (pass to `DELETE /v1/cache` to purge it; omitted when `ttl=0`).","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Metered requests left this month after this request.","schema":{"type":"integer"}}},"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_url | invalid_option\", ...}}` — neither title nor url, bad url, or over-long text.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_api_key\", ...}}` — missing, malformed, unknown, or revoked key; the only response that is never metered.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Error envelope `{\"error\": {\"code\": \"render_failed | render_timeout\", ...}}` — Chromium launch, navigation, or timeout failure inside the render.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-Response-Ms":{"description":"Worker time spent on the request in milliseconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error envelope `{\"error\": {\"code\": \"quota_exceeded\", ...}}` — plan monthly cap reached (Free 1,000, Pro 50,000 metered requests); resets the first of next month UTC.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}},"X-RateLimit-Limit":{"description":"Monthly metered-request cap for the key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Always 0 on a 429.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/usage":{"get":{"summary":"Report key usage","description":"Metered totals, per-endpoint daily series, and current quota position for the caller's key. Deliberately unmetered.","tags":["Usage"],"parameters":[{"name":"days","in":"query","description":"Trailing window in days.","schema":{"type":"integer","minimum":1,"maximum":365,"default":30}}],"responses":{"200":{"description":"Usage report for the key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageReport"}}}},"400":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_option\", ...}}` — days outside 1-365.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_api_key\", ...}}` — missing, malformed, unknown, or revoked key; the only response that is never metered.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/cache":{"delete":{"summary":"Purge one cached render","description":"Delete a single cached render by its `X-Cache-Key` response header. Deliberately unmetered; the next identical render simply re-renders.","tags":["Usage"],"parameters":[{"name":"key","in":"query","required":true,"description":"R2 object key from an `X-Cache-Key` header (`cache/<64 hex chars>.<png|jpg|pdf>`).","schema":{"type":"string"}}],"responses":{"200":{"description":"Whether an object was deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CachePurge"}}}},"400":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_option\", ...}}` — missing or malformed key.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error envelope `{\"error\": {\"code\": \"invalid_api_key\", ...}}` — missing, malformed, unknown, or revoked key; the only response that is never metered.","headers":{"X-Cache":{"description":"Always `NONE` on errors.","schema":{"type":"string","enum":["NONE"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/health":{"get":{"summary":"Liveness probe","description":"Unauthenticated, unmetered liveness check.","tags":["Meta"],"security":[{}],"responses":{"200":{"description":"Worker is alive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/v1/openapi.json":{"get":{"summary":"OpenAPI document","description":"This machine-readable API description (unauthenticated, unmetered).","tags":["Meta"],"security":[{}],"responses":{"200":{"description":"OpenAPI 3.0.3 document.","content":{"application/json":{"schema":{"type":"object","description":"OpenAPI 3.0.3 document."}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"sk_live_<32 hex>","description":"API key minted on your profile (https://proofito.astraeatech.dev/profile). Send as `Authorization: Bearer <key>`."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}},"UsageReport":{"type":"object","required":["totals","daily"],"properties":{"totals":{"$ref":"#/components/schemas/UsageTotals"},"daily":{"type":"array","items":{"$ref":"#/components/schemas/DailyUsage"}},"quota":{"$ref":"#/components/schemas/UsageQuota"}}},"UsageTotals":{"type":"object","required":["screenshot","pdf","og","cache_hit","error"],"properties":{"screenshot":{"type":"integer","description":"Screenshot renders (all statuses)."},"pdf":{"type":"integer","description":"PDF renders (all statuses)."},"og":{"type":"integer","description":"OG renders (all statuses)."},"cache_hit":{"type":"integer","description":"Cache hits across endpoints."},"error":{"type":"integer","description":"Errors across endpoints."}}},"DailyUsage":{"type":"object","required":["date","count","screenshot","pdf","og"],"properties":{"date":{"type":"string","format":"date","description":"UTC day (days with no events are omitted)."},"count":{"type":"integer","description":"Total events that day."},"screenshot":{"type":"integer","description":"Screenshot events that day."},"pdf":{"type":"integer","description":"PDF events that day."},"og":{"type":"integer","description":"OG events that day."}}},"UsageQuota":{"type":"object","required":["plan","scope","limit","used","remaining","reset_at"],"properties":{"plan":{"type":"string","enum":["free","pro"],"description":"Plan the quota is counted under."},"scope":{"type":"string","enum":["key","account"],"description":"Whether the quota is per-key or shared across the account."},"limit":{"type":"integer","description":"Monthly metered-request cap."},"used":{"type":"integer","description":"Metered requests used this month."},"remaining":{"type":"integer","description":"Metered requests left this month."},"reset_at":{"type":"string","format":"date-time","description":"ISO instant of the next UTC-month reset."}}},"CachePurge":{"type":"object","required":["purged"],"properties":{"purged":{"type":"boolean","description":"True when a cached object was deleted; false when the key did not exist."}}},"Health":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}}