{"openapi":"3.1.0","info":{"title":"ScreenshotBuddy API","version":"1.0.0","description":"Render a web page as an image or a PDF, and read where the account stands. A successful render is the image or the PDF itself, so treat it as binary and write it to a file; every error is JSON in one envelope, carrying a machine readable `code` to branch on, a human `message`, a `request_id` to quote at support, and, for a validation failure, an `errors` object. Every render costs one credit, and a failed render is refunded."},"servers":[{"url":"https://screenshotbuddy.io/api/v1","description":"The base URL of the API."}],"security":[{"bearerToken":[]}],"components":{"securitySchemes":{"bearerToken":{"type":"http","scheme":"bearer","description":"An API token from your dashboard, sent as `Authorization: Bearer <token>`."}},"schemas":{"Error":{"type":"object","description":"The one shape every error answer takes.","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"The machine readable reason the request was refused. Branch on this rather than on the message, which is written for a person and may be reworded at any time.","enum":["unauthenticated","invalid_signature","signed_url_expired","email_unverified","missing_ability","no_active_plan","credit_limit_reached","validation_failed","invalid_url","blocked_host","target_unreachable","rate_limited","render_failed","server_error","upstream_error","upstream_quota_exceeded","render_timeout","not_found","method_not_allowed"]},"message":{"type":"string","description":"The reason in words, for a person to read. Not a stable contract."},"errors":{"type":"object","description":"Present only on a validation shaped failure. Keyed by the query parameter at fault, with the messages for that parameter.","additionalProperties":{"type":"array","items":{"type":"string"}}},"request_id":{"type":"string","format":"uuid","description":"Identifies this one answer, and is in the server log for the request that produced it. Quote it in a support request."}}}}},"paths":{"/snap":{"get":{"operationId":"snap","summary":"Render a web page as an image or a PDF.","description":"Renders the page at `url` and returns the file. Sending `pdf=true` switches the endpoint to PDF mode, which accepts a different set of parameters: the `x-modes` list on each parameter says which modes accept it, and a parameter sent to a mode that does not accept it is rejected with a 422 rather than ignored.\n\nAn identical repeat of a request is answered with the rendering already made for it, for no credit, marked `X-Cache: HIT`. Every rendered answer carries an `ETag`; sending it back on the next request as `If-None-Match` is answered `304` with no body when the entry is unchanged, which costs neither a credit nor the download. Send `cache=0` to render the page again instead, and `cacheTtl` to set how long a rendering is kept.","security":[{"bearerToken":[]}],"parameters":[{"name":"url","in":"query","required":true,"description":"The URL of the page to render. It must start with `http://` or `https://`, be at most `2048` characters long, and point at a publicly reachable host. Private, loopback and internal addresses are refused.","schema":{"type":"string"},"x-modes":["screenshot","pdf"]},{"name":"fullPage","in":"query","required":false,"description":"Whether to capture the entire scrollable page instead of just the viewport. Defaults to `false`. Screenshots only; sending it with `pdf` is rejected, because a PDF always prints the whole document.","schema":{"type":"boolean","default":false},"x-modes":["screenshot"]},{"name":"pdf","in":"query","required":false,"description":"Set this to `true` to render a PDF instead of an image. It changes which other parameters are accepted, so see [creating PDFs](https://screenshotbuddy.io/documentation/creating-pdfs) for the options that belong to that mode. Defaults to `false`.","schema":{"type":"boolean","default":false},"x-modes":["screenshot","pdf"]},{"name":"landscape","in":"query","required":false,"description":"Whether to use landscape orientation. Defaults to `false` (portrait). PDFs only; sending it without `pdf` is rejected. A screenshot is shaped by `width` and `height` instead.","schema":{"type":"boolean","default":false},"x-modes":["pdf"]},{"name":"format","in":"query","required":false,"description":"The image format to return. Possible values are `png`, `jpeg` and `webp`. Defaults to `png`. Screenshots only; sending it with `pdf` is rejected. Use `paperFormat` to set the paper size of a PDF.","schema":{"type":"string","enum":["png","jpeg","webp"],"default":"png"},"x-modes":["screenshot"]},{"name":"quality","in":"query","required":false,"description":"The quality of the image, between `1` and `100`. It applies to lossy formats only, so `format` has to be `jpeg` or `webp`; sending it with `png` is rejected. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":1,"maximum":100},"x-modes":["screenshot"]},{"name":"paperFormat","in":"query","required":false,"description":"The paper format. Possible values are `letter`, `legal`, `tabloid`, `ledger`, and `a0` through `a6`. Defaults to `a4`. PDFs only; sending it without `pdf` is rejected. Use `format` to set the image format of a screenshot.","schema":{"type":"string","enum":["letter","legal","tabloid","ledger","a0","a1","a2","a3","a4","a5","a6"],"default":"a4"},"x-modes":["pdf"]},{"name":"width","in":"query","required":false,"description":"The viewport width of a screenshot, or the paper width of a PDF, between `1` and `10000`. Must be used together with `height`. For a PDF it overrides `paperFormat` and is read in the unit set by `marginUnit`.","schema":{"type":"number","minimum":1,"maximum":10000},"x-modes":["screenshot","pdf"],"x-requires-together":["height"]},{"name":"height","in":"query","required":false,"description":"The viewport height of a screenshot, or the paper height of a PDF, between `1` and `10000`. Must be used together with `width`. For a PDF it overrides `paperFormat` and is read in the unit set by `marginUnit`.","schema":{"type":"number","minimum":1,"maximum":10000},"x-modes":["screenshot","pdf"],"x-requires-together":["width"]},{"name":"marginTop","in":"query","required":false,"description":"Top margin, between `0` and `1000`, in the unit set by `marginUnit`. All four margins (`marginTop`, `marginRight`, `marginBottom`, `marginLeft`) have to be set together. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginRight","marginBottom","marginLeft"]},{"name":"marginRight","in":"query","required":false,"description":"Right margin, between `0` and `1000`, in the unit set by `marginUnit`. Set it together with the other three margins. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginTop","marginBottom","marginLeft"]},{"name":"marginBottom","in":"query","required":false,"description":"Bottom margin, between `0` and `1000`, in the unit set by `marginUnit`. Set it together with the other three margins. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginTop","marginRight","marginLeft"]},{"name":"marginLeft","in":"query","required":false,"description":"Left margin, between `0` and `1000`, in the unit set by `marginUnit`. Set it together with the other three margins. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginTop","marginRight","marginBottom"]},{"name":"marginUnit","in":"query","required":false,"description":"The unit for the margins and for a custom paper size. Possible values are `mm`, `cm`, `in` and `px`. Defaults to `mm`. PDFs only; sending it without `pdf` is rejected, because a screenshot's `width` and `height` are viewport pixels.","schema":{"type":"string","enum":["mm","cm","in","px"],"default":"mm"},"x-modes":["pdf"]},{"name":"scale","in":"query","required":false,"description":"The scale of the rendering, between `1` and `3` for screenshots and between `0.1` and `2` for PDFs. Screenshots take whole numbers only, because the browser renders at whole device scale factors; PDFs take fractions. Defaults to `1`. A value outside the range of the mode you are in is rejected.","schema":{"type":"number","minimum":0.1,"maximum":3,"default":1},"x-modes":["screenshot","pdf"]},{"name":"delay","in":"query","required":false,"description":"How long to wait before capturing, in milliseconds, between `0` and `10000`. Useful for pages that animate on load. Screenshots only; sending it with `pdf` is rejected, because the PDF renderer has no way to wait a fixed amount of time before it prints.","schema":{"type":"integer","minimum":0,"maximum":10000},"x-modes":["screenshot"]},{"name":"selector","in":"query","required":false,"description":"A CSS selector. Only the first element that matches it is captured, instead of the page. At most `512` characters, and it may not contain single quotes, backslashes or control characters, so write attribute selectors with double quotes: `a[href=\"/pricing\"]`. It chooses what to capture, so it cannot be combined with a clip region or with `fullPage`. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"string"},"x-modes":["screenshot"]},{"name":"clipX","in":"query","required":false,"description":"The distance from the left edge of the page to the region to capture, in pixels, between `0` and `10000`. All four clip parameters (`clipX`, `clipY`, `clipWidth`, `clipHeight`) have to be set together. A clip region chooses what to capture, so it cannot be combined with `selector` or with `fullPage`. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":0,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipY","clipWidth","clipHeight"]},{"name":"clipY","in":"query","required":false,"description":"The distance from the top edge of the page to the region to capture, in pixels, between `0` and `10000`. Set it together with the other three clip parameters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":0,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipX","clipWidth","clipHeight"]},{"name":"clipWidth","in":"query","required":false,"description":"The width of the region to capture, in pixels, between `1` and `10000`. Set it together with the other three clip parameters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":1,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipX","clipY","clipHeight"]},{"name":"clipHeight","in":"query","required":false,"description":"The height of the region to capture, in pixels, between `1` and `10000`. Set it together with the other three clip parameters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":1,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipX","clipY","clipWidth"]},{"name":"omitBackground","in":"query","required":false,"description":"Whether to render the page background transparent. Defaults to `false`. The format has to be able to hold transparency, so `png` or `webp`; sending it with `jpeg` is rejected rather than answered with a black background. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"boolean","default":false},"x-modes":["screenshot"]},{"name":"waitForSelector","in":"query","required":false,"description":"A CSS selector to wait for before capturing. The render continues once an element matching it exists. At most `512` characters, and under the same character restriction as `selector`: no single quotes, backslashes or control characters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"string"},"x-modes":["screenshot"]},{"name":"waitUntil","in":"query","required":false,"description":"The load event to wait for before capturing. Possible values are `load`, `domcontentloaded`, `networkidle0` (no network connections for half a second) and `networkidle2` (at most two). Defaults to `networkidle2`. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"string","enum":["load","domcontentloaded","networkidle0","networkidle2"],"default":"networkidle2"},"x-modes":["screenshot"]},{"name":"cache","in":"query","required":false,"description":"Whether an identical repeat of this request may be answered with the rendering we already made, and whether this rendering is kept for the next one. Defaults to `true`. A cached answer costs no credit and says so with `X-Cache: HIT`; send `cache=0` to render the page again, which costs a credit as any render does. Entries belong to your own account. See [caching](https://screenshotbuddy.io/documentation/caching) for the whole picture.","schema":{"type":"boolean","default":true},"x-modes":["screenshot","pdf"]},{"name":"cacheTtl","in":"query","required":false,"description":"How long the rendering is kept, in seconds, between `60` and `2592000` (thirty days). Defaults to `86400`. Sending it with `cache=0` is rejected, because there is no lifetime to set on a rendering that is not being kept.","schema":{"type":"integer","minimum":60,"maximum":2592000,"default":86400},"x-modes":["screenshot","pdf"]}],"responses":{"200":{"description":"The rendered file. The body is the image or the PDF itself rather than JSON, so write it to a file instead of parsing it. The Content-Type names the format that was rendered.","x-retryable":false,"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"X-Credits-Limit":{"description":"Credits the current period was granted. Sent on every answer to a request that authenticated.","schema":{"type":"integer"}},"X-Credits-Remaining":{"description":"Credits left to spend, counted after this request was accounted for, so a render that succeeded is already subtracted and a render that failed is already refunded. Floored at zero.","schema":{"type":"integer"}},"X-Credits-Reset":{"description":"When the credits are granted again, as a Unix timestamp in seconds.","schema":{"type":"integer"}},"X-Cache":{"description":"Whether this answer came out of the cache. `HIT` means nothing was rendered and no credit was spent; `MISS` means the page was rendered for this request and the result was kept for the next one. Absent when the request sent `cache=0`, which declines the cache rather than missing it.","schema":{"type":"string","enum":["HIT","MISS"]}},"ETag":{"description":"The quoted digest of the body. Send it back as `If-None-Match` on the same request and an unchanged entry is answered `304` with no body, costing neither a credit nor the download.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"The number of requests allowed per minute for this account.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"The number of requests left in the current minute.","schema":{"type":"integer"}}},"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}},"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"The request carried no bearer token, or a token that is unknown or revoked.","x-retryable":false,"x-error-codes":["unauthenticated"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"unauthenticated","message":"Unauthenticated.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"402":{"description":"The account has no active plan, or it has used every credit in the current period. The code says which of the two it is.","x-retryable":false,"x-error-codes":["no_active_plan","credit_limit_reached"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"X-Credits-Limit":{"description":"Credits the current period was granted. Sent on every answer to a request that authenticated.","schema":{"type":"integer"}},"X-Credits-Remaining":{"description":"Credits left to spend, counted after this request was accounted for, so a render that succeeded is already subtracted and a render that failed is already refunded. Floored at zero.","schema":{"type":"integer"}},"X-Credits-Reset":{"description":"When the credits are granted again, as a Unix timestamp in seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"credit_limit_reached","message":"You have reached the credit limit of your plan.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"403":{"description":"The email address of the account has not been verified yet, or the token is not allowed to ask for the mode the request is in. The code says which of the two it is: a token carries a permission per mode, and one narrowed to screenshots cannot print a PDF.","x-retryable":false,"x-error-codes":["email_unverified","missing_ability"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"email_unverified","message":"You must verify your email address.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"422":{"description":"A query parameter did not validate, or the target URL could not be reached or loaded (\"The target URL could not be reached or loaded. Check that it is publicly available and try again.\"). The errors object names the parameter at fault. No credit is charged.","x-retryable":false,"x-error-codes":["validation_failed","invalid_url","blocked_host","target_unreachable"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"invalid_url","message":"The url query parameter is required.","errors":{"url":["The url query parameter is required."]},"request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"429":{"description":"The rate limit of the account was exceeded. Wait for the number of seconds in the Retry-After header before sending the request again.","x-retryable":true,"x-error-codes":["rate_limited"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"The number of requests allowed per minute for this account.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"The number of requests left in the current minute.","schema":{"type":"integer"}},"Retry-After":{"description":"The number of seconds to wait before sending the request again.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"rate_limited","message":"Too Many Attempts.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"500":{"description":"The render failed for a reason we did not recognise. The credit is refunded.","x-retryable":false,"x-error-codes":["render_failed","server_error"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"render_failed","message":"Server error while taking screenshot.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"502":{"description":"The rendering service failed to process the request. The credit is refunded and the request is worth sending again.","x-retryable":true,"x-error-codes":["upstream_error","upstream_quota_exceeded"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Retry-After":{"description":"The number of seconds to wait before sending the request again.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"upstream_error","message":"The rendering service failed to process this request. Please try again shortly.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"504":{"description":"The render did not finish within its time budget. The credit is refunded and the request is worth sending again.","x-retryable":true,"x-error-codes":["render_timeout"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Retry-After":{"description":"The number of seconds to wait before sending the request again.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"render_timeout","message":"The render did not complete in time. Please try again.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}}}}},"/snap/signed":{"get":{"operationId":"snapSigned","summary":"Render a web page from a URL that carries its own proof of authorisation.","description":"The same render as `/snap`, authenticated by an HMAC in the query string instead of by a bearer token, so the URL can be put straight in an `<img src>` or behind a CDN without the token going with it. Every parameter of `/snap` applies unchanged, and so does everything after authentication: the token's permissions, the credit it costs, the cache and the account's rate limit.\n\nSign the canonical form of the query with the signing secret of the token, which is shown once under Settings, API tokens: drop `signature`, sort the remaining parameters bytewise by name, percent encode both halves of every pair, join them with `&`, prefix the result with `snap-signed-v1` and a newline, and take the lowercase hex HMAC-SHA256 of that with the secret as the key. Every parameter is covered, so anything appended to the URL afterwards invalidates it. The [signed URLs](https://screenshotbuddy.io/documentation/signed-urls) page has the algorithm spelled out with a worked example and reference implementations.\n\nThis operation takes no bearer token: sending one changes nothing, because the signature is what authenticates.","security":[],"parameters":[{"name":"url","in":"query","required":true,"description":"The URL of the page to render. It must start with `http://` or `https://`, be at most `2048` characters long, and point at a publicly reachable host. Private, loopback and internal addresses are refused.","schema":{"type":"string"},"x-modes":["screenshot","pdf"]},{"name":"fullPage","in":"query","required":false,"description":"Whether to capture the entire scrollable page instead of just the viewport. Defaults to `false`. Screenshots only; sending it with `pdf` is rejected, because a PDF always prints the whole document.","schema":{"type":"boolean","default":false},"x-modes":["screenshot"]},{"name":"pdf","in":"query","required":false,"description":"Set this to `true` to render a PDF instead of an image. It changes which other parameters are accepted, so see [creating PDFs](https://screenshotbuddy.io/documentation/creating-pdfs) for the options that belong to that mode. Defaults to `false`.","schema":{"type":"boolean","default":false},"x-modes":["screenshot","pdf"]},{"name":"landscape","in":"query","required":false,"description":"Whether to use landscape orientation. Defaults to `false` (portrait). PDFs only; sending it without `pdf` is rejected. A screenshot is shaped by `width` and `height` instead.","schema":{"type":"boolean","default":false},"x-modes":["pdf"]},{"name":"format","in":"query","required":false,"description":"The image format to return. Possible values are `png`, `jpeg` and `webp`. Defaults to `png`. Screenshots only; sending it with `pdf` is rejected. Use `paperFormat` to set the paper size of a PDF.","schema":{"type":"string","enum":["png","jpeg","webp"],"default":"png"},"x-modes":["screenshot"]},{"name":"quality","in":"query","required":false,"description":"The quality of the image, between `1` and `100`. It applies to lossy formats only, so `format` has to be `jpeg` or `webp`; sending it with `png` is rejected. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":1,"maximum":100},"x-modes":["screenshot"]},{"name":"paperFormat","in":"query","required":false,"description":"The paper format. Possible values are `letter`, `legal`, `tabloid`, `ledger`, and `a0` through `a6`. Defaults to `a4`. PDFs only; sending it without `pdf` is rejected. Use `format` to set the image format of a screenshot.","schema":{"type":"string","enum":["letter","legal","tabloid","ledger","a0","a1","a2","a3","a4","a5","a6"],"default":"a4"},"x-modes":["pdf"]},{"name":"width","in":"query","required":false,"description":"The viewport width of a screenshot, or the paper width of a PDF, between `1` and `10000`. Must be used together with `height`. For a PDF it overrides `paperFormat` and is read in the unit set by `marginUnit`.","schema":{"type":"number","minimum":1,"maximum":10000},"x-modes":["screenshot","pdf"],"x-requires-together":["height"]},{"name":"height","in":"query","required":false,"description":"The viewport height of a screenshot, or the paper height of a PDF, between `1` and `10000`. Must be used together with `width`. For a PDF it overrides `paperFormat` and is read in the unit set by `marginUnit`.","schema":{"type":"number","minimum":1,"maximum":10000},"x-modes":["screenshot","pdf"],"x-requires-together":["width"]},{"name":"marginTop","in":"query","required":false,"description":"Top margin, between `0` and `1000`, in the unit set by `marginUnit`. All four margins (`marginTop`, `marginRight`, `marginBottom`, `marginLeft`) have to be set together. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginRight","marginBottom","marginLeft"]},{"name":"marginRight","in":"query","required":false,"description":"Right margin, between `0` and `1000`, in the unit set by `marginUnit`. Set it together with the other three margins. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginTop","marginBottom","marginLeft"]},{"name":"marginBottom","in":"query","required":false,"description":"Bottom margin, between `0` and `1000`, in the unit set by `marginUnit`. Set it together with the other three margins. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginTop","marginRight","marginLeft"]},{"name":"marginLeft","in":"query","required":false,"description":"Left margin, between `0` and `1000`, in the unit set by `marginUnit`. Set it together with the other three margins. PDFs only; sending a margin without `pdf` is rejected.","schema":{"type":"number","minimum":0,"maximum":1000},"x-modes":["pdf"],"x-requires-together":["marginTop","marginRight","marginBottom"]},{"name":"marginUnit","in":"query","required":false,"description":"The unit for the margins and for a custom paper size. Possible values are `mm`, `cm`, `in` and `px`. Defaults to `mm`. PDFs only; sending it without `pdf` is rejected, because a screenshot's `width` and `height` are viewport pixels.","schema":{"type":"string","enum":["mm","cm","in","px"],"default":"mm"},"x-modes":["pdf"]},{"name":"scale","in":"query","required":false,"description":"The scale of the rendering, between `1` and `3` for screenshots and between `0.1` and `2` for PDFs. Screenshots take whole numbers only, because the browser renders at whole device scale factors; PDFs take fractions. Defaults to `1`. A value outside the range of the mode you are in is rejected.","schema":{"type":"number","minimum":0.1,"maximum":3,"default":1},"x-modes":["screenshot","pdf"]},{"name":"delay","in":"query","required":false,"description":"How long to wait before capturing, in milliseconds, between `0` and `10000`. Useful for pages that animate on load. Screenshots only; sending it with `pdf` is rejected, because the PDF renderer has no way to wait a fixed amount of time before it prints.","schema":{"type":"integer","minimum":0,"maximum":10000},"x-modes":["screenshot"]},{"name":"selector","in":"query","required":false,"description":"A CSS selector. Only the first element that matches it is captured, instead of the page. At most `512` characters, and it may not contain single quotes, backslashes or control characters, so write attribute selectors with double quotes: `a[href=\"/pricing\"]`. It chooses what to capture, so it cannot be combined with a clip region or with `fullPage`. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"string"},"x-modes":["screenshot"]},{"name":"clipX","in":"query","required":false,"description":"The distance from the left edge of the page to the region to capture, in pixels, between `0` and `10000`. All four clip parameters (`clipX`, `clipY`, `clipWidth`, `clipHeight`) have to be set together. A clip region chooses what to capture, so it cannot be combined with `selector` or with `fullPage`. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":0,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipY","clipWidth","clipHeight"]},{"name":"clipY","in":"query","required":false,"description":"The distance from the top edge of the page to the region to capture, in pixels, between `0` and `10000`. Set it together with the other three clip parameters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":0,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipX","clipWidth","clipHeight"]},{"name":"clipWidth","in":"query","required":false,"description":"The width of the region to capture, in pixels, between `1` and `10000`. Set it together with the other three clip parameters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":1,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipX","clipY","clipHeight"]},{"name":"clipHeight","in":"query","required":false,"description":"The height of the region to capture, in pixels, between `1` and `10000`. Set it together with the other three clip parameters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"integer","minimum":1,"maximum":10000},"x-modes":["screenshot"],"x-requires-together":["clipX","clipY","clipWidth"]},{"name":"omitBackground","in":"query","required":false,"description":"Whether to render the page background transparent. Defaults to `false`. The format has to be able to hold transparency, so `png` or `webp`; sending it with `jpeg` is rejected rather than answered with a black background. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"boolean","default":false},"x-modes":["screenshot"]},{"name":"waitForSelector","in":"query","required":false,"description":"A CSS selector to wait for before capturing. The render continues once an element matching it exists. At most `512` characters, and under the same character restriction as `selector`: no single quotes, backslashes or control characters. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"string"},"x-modes":["screenshot"]},{"name":"waitUntil","in":"query","required":false,"description":"The load event to wait for before capturing. Possible values are `load`, `domcontentloaded`, `networkidle0` (no network connections for half a second) and `networkidle2` (at most two). Defaults to `networkidle2`. Screenshots only; sending it with `pdf` is rejected.","schema":{"type":"string","enum":["load","domcontentloaded","networkidle0","networkidle2"],"default":"networkidle2"},"x-modes":["screenshot"]},{"name":"cache","in":"query","required":false,"description":"Whether an identical repeat of this request may be answered with the rendering we already made, and whether this rendering is kept for the next one. Defaults to `true`. A cached answer costs no credit and says so with `X-Cache: HIT`; send `cache=0` to render the page again, which costs a credit as any render does. Entries belong to your own account. See [caching](https://screenshotbuddy.io/documentation/caching) for the whole picture.","schema":{"type":"boolean","default":true},"x-modes":["screenshot","pdf"]},{"name":"cacheTtl","in":"query","required":false,"description":"How long the rendering is kept, in seconds, between `60` and `2592000` (thirty days). Defaults to `86400`. Sending it with `cache=0` is rejected, because there is no lifetime to set on a rendering that is not being kept.","schema":{"type":"integer","minimum":60,"maximum":2592000,"default":86400},"x-modes":["screenshot","pdf"]},{"name":"tokenId","in":"query","required":true,"description":"The id of the API token this URL is signed for. It is the number in front of the pipe in the token itself, and it is shown next to every token under Settings, API tokens. It is not a secret, and it is covered by the signature, so it cannot be swapped for another account's. See [signed URLs](https://screenshotbuddy.io/documentation/signed-urls).","schema":{"type":"integer"},"x-modes":["screenshot","pdf"]},{"name":"expires","in":"query","required":false,"description":"The Unix timestamp, in seconds, after which this URL stops working. Optional; a URL without one keeps working until the token is revoked or rotated. It is covered by the signature, so it cannot be pushed back without signing again, and a URL past it is refused with `signed_url_expired` rather than rendered.","schema":{"type":"integer"},"x-modes":["screenshot","pdf"]},{"name":"signature","in":"query","required":true,"description":"The HMAC-SHA256 of the canonical query, keyed with the signing secret of the token, in lowercase hex. Exactly 64 characters. Every other query parameter is covered by it, so anything appended to the URL, a tracking parameter included, invalidates it. The [signed URLs](https://screenshotbuddy.io/documentation/signed-urls) page spells out the canonicalisation step by step.","schema":{"type":"string"},"x-modes":["screenshot","pdf"]}],"responses":{"200":{"description":"The rendered file. The body is the image or the PDF itself rather than JSON, so write it to a file instead of parsing it. The Content-Type names the format that was rendered.","x-retryable":false,"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}},"X-Credits-Limit":{"description":"Credits the current period was granted. Sent on every answer to a request that authenticated.","schema":{"type":"integer"}},"X-Credits-Remaining":{"description":"Credits left to spend, counted after this request was accounted for, so a render that succeeded is already subtracted and a render that failed is already refunded. Floored at zero.","schema":{"type":"integer"}},"X-Credits-Reset":{"description":"When the credits are granted again, as a Unix timestamp in seconds.","schema":{"type":"integer"}},"X-Cache":{"description":"Whether this answer came out of the cache. `HIT` means nothing was rendered and no credit was spent; `MISS` means the page was rendered for this request and the result was kept for the next one. Absent when the request sent `cache=0`, which declines the cache rather than missing it.","schema":{"type":"string","enum":["HIT","MISS"]}},"ETag":{"description":"The quoted digest of the body. Send it back as `If-None-Match` on the same request and an unchanged entry is answered `304` with no body, costing neither a credit nor the download.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"The number of requests allowed per minute for this account.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"The number of requests left in the current minute.","schema":{"type":"integer"}}},"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}},"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"The signature did not verify, or the URL has passed the `expires` it was signed with. The code says which: `signed_url_expired` is fixed by signing a fresh URL, `invalid_signature` by fixing how the URL is built. Every other way a signed request can fail to authenticate, an unknown token id and a revoked token included, answers `invalid_signature` as well, so that the endpoint cannot be used to find out which tokens exist.","x-retryable":false,"x-error-codes":["invalid_signature","signed_url_expired"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"invalid_signature","message":"This signed URL is not valid. Check that the signature covers every query parameter, and that the token it names still exists.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"402":{"description":"The account has no active plan, or it has used every credit in the current period. The code says which of the two it is.","x-retryable":false,"x-error-codes":["no_active_plan","credit_limit_reached"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}},"X-Credits-Limit":{"description":"Credits the current period was granted. Sent on every answer to a request that authenticated.","schema":{"type":"integer"}},"X-Credits-Remaining":{"description":"Credits left to spend, counted after this request was accounted for, so a render that succeeded is already subtracted and a render that failed is already refunded. Floored at zero.","schema":{"type":"integer"}},"X-Credits-Reset":{"description":"When the credits are granted again, as a Unix timestamp in seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"credit_limit_reached","message":"You have reached the credit limit of your plan.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"403":{"description":"The email address of the account has not been verified yet, or the token is not allowed to ask for the mode the request is in. The code says which of the two it is: a token carries a permission per mode, and one narrowed to screenshots cannot print a PDF.","x-retryable":false,"x-error-codes":["email_unverified","missing_ability"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"email_unverified","message":"You must verify your email address.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"422":{"description":"A query parameter did not validate, or the target URL could not be reached or loaded (\"The target URL could not be reached or loaded. Check that it is publicly available and try again.\"). The errors object names the parameter at fault. No credit is charged.","x-retryable":false,"x-error-codes":["validation_failed","invalid_url","blocked_host","target_unreachable"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"invalid_url","message":"The url query parameter is required.","errors":{"url":["The url query parameter is required."]},"request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"429":{"description":"The rate limit of the account was exceeded, or more than 30 signatures failed to verify from this address in a minute. Wait for the number of seconds in the Retry-After header before sending the request again.","x-retryable":true,"x-error-codes":["rate_limited"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"The number of requests allowed per minute for this account.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"The number of requests left in the current minute.","schema":{"type":"integer"}},"Retry-After":{"description":"The number of seconds to wait before sending the request again.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"rate_limited","message":"Too Many Attempts.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"500":{"description":"The render failed for a reason we did not recognise. The credit is refunded.","x-retryable":false,"x-error-codes":["render_failed","server_error"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"render_failed","message":"Server error while taking screenshot.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"502":{"description":"The rendering service failed to process the request. The credit is refunded and the request is worth sending again.","x-retryable":true,"x-error-codes":["upstream_error","upstream_quota_exceeded"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}},"Retry-After":{"description":"The number of seconds to wait before sending the request again.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"upstream_error","message":"The rendering service failed to process this request. Please try again shortly.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"504":{"description":"The render did not finish within its time budget. The credit is refunded and the request is worth sending again.","x-retryable":true,"x-error-codes":["render_timeout"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Cache-Control":{"description":"On a rendered answer, `public` with a `max-age` of the rendering's own lifetime, shortened to what is left of `expires` when the URL carries one, so nothing in front of us serves an image from a URL that no longer authenticates. On every refusal, and on a request that sent `cache=0`, `no-store`: a cached refusal would keep being served after the reason for it was fixed.","schema":{"type":"string"}},"Retry-After":{"description":"The number of seconds to wait before sending the request again.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"render_timeout","message":"The render did not complete in time. Please try again.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}}}}},"/usage":{"get":{"operationId":"usage","summary":"Report the credits, plan, period and rate limit of the account.","description":"Says what the account currently has, so a caller can find out where it stands before spending a credit. It reports state rather than refusing on it: an account with no plan, or with every credit spent, is described with a 200 and the numbers that say so, where `/snap` answers 402. It runs under a throttle of its own of 60 requests per minute, which is separate from the render limit, so polling this endpoint never eats into what `/snap` will accept.","security":[{"bearerToken":[]}],"responses":{"200":{"description":"The current state of the account. Answered even when the account is on no plan or has spent every credit.","headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"X-Credits-Limit":{"description":"Credits the current period was granted. Sent on every answer to a request that authenticated.","schema":{"type":"integer"}},"X-Credits-Remaining":{"description":"Credits left to spend, counted after this request was accounted for, so a render that succeeded is already subtracted and a render that failed is already refunded. Floored at zero.","schema":{"type":"integer"}},"X-Credits-Reset":{"description":"When the credits are granted again, as a Unix timestamp in seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["credits","plan","period","rate_limit"],"properties":{"credits":{"type":"object","description":"The credits of the current period.","required":["remaining","used","total"],"properties":{"remaining":{"type":"integer","description":"Credits left to spend. Floored at zero, so an overspent period reads as empty rather than as a negative allowance."},"used":{"type":"integer","description":"Credits spent in the current period."},"total":{"type":"integer","description":"Credits the period was granted."}}},"plan":{"type":["object","null"],"description":"The plan the account is on, or null when it is on none.","properties":{"name":{"type":"string","description":"The plan as it is written for a person."},"slug":{"type":"string","description":"The stable identifier of the plan."}}},"period":{"type":["object","null"],"description":"The current billing period, or null when the account has none yet.","properties":{"started_at":{"type":"string","format":"date-time","description":"When the current period began."},"resets_at":{"type":"string","format":"date-time","description":"When the credits are granted again."}}},"rate_limit":{"type":"object","description":"The render limit of the account.","required":["requests_per_minute"],"properties":{"requests_per_minute":{"type":"integer","description":"How many requests to /snap the account may make per minute. This is the account's own limit, whatever plan or override it came from."}}}}},"example":{"credits":{"remaining":8432,"used":1568,"total":10000},"plan":{"name":"Business","slug":"business"},"period":{"started_at":"2026-08-01T00:00:00+00:00","resets_at":"2026-09-01T00:00:00+00:00"},"rate_limit":{"requests_per_minute":40}}}}},"401":{"description":"The request carried no bearer token, or a token that is unknown or revoked.","x-retryable":false,"x-error-codes":["unauthenticated"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"unauthenticated","message":"Unauthenticated.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"403":{"description":"The email address of the account has not been verified yet.","x-retryable":false,"x-error-codes":["email_unverified"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"email_unverified","message":"You must verify your email address.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}},"429":{"description":"More than 60 requests to this endpoint in a minute. This throttle belongs to the usage endpoint alone: going over it does not spend, or depend on, the per-minute budget of /snap.","x-retryable":true,"x-error-codes":["rate_limited"],"headers":{"X-Request-Id":{"description":"Identifies this one answer, and is in the server log for the request that produced it. An error envelope carries the same value in its `request_id`, so a rendered file and a refusal are quoted at support the same way.","schema":{"type":"string","format":"uuid"}},"Retry-After":{"description":"The number of seconds to wait before sending the request again.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"code":"rate_limited","message":"Too Many Attempts.","request_id":"1f3c0e6a-6a5d-4a4c-9a3f-0a3f7d1c2b84"}}}}}}}}}