GET /v1/renders/:id
Two read endpoints. Both are free, neither ever re-renders anything.
GET https://api.galleyrender.com/v1/renders/:idGET https://api.galleyrender.com/v1/renders?limit=25Authorization: Bearer glr_sk_…GET /v1/renders/:id
Section titled “GET /v1/renders/:id”Fetches one render by its rnd_… id and signs a fresh URL every time it is called. This is
how you recover from an expired link: the signature in url lasts an hour, the stored object is
kept until expires_at, so ask for the render again rather than paying to render it again.
Request
Section titled “Request”| Parameter | In | Description |
|---|---|---|
id | path | The render id, e.g. rnd_7hq2m4x8k1bv. |
Renders are scoped to the account that made them. Somebody else’s id is a 404, not a 403.
Response
Section titled “Response”200 OK with a render object — the same shape POST /v1/render
returns, minus cached, which is always false here (this endpoint reads a stored render; it
does not perform a cache lookup).
| Field | Type | Description |
|---|---|---|
object | string | Always render. |
id | string | rnd_…. |
status | string | queued, processing, succeeded or failed. |
template | string | Resolved reference with version, e.g. invoice@1. |
format | string | pdf, png or jpg. |
engine | string | chromium or satori. |
cached | boolean | Always false on this endpoint. |
url | string | null | Freshly signed, valid for an hour. null unless status is succeeded. |
expires_at | string | null | Retention deadline for the stored object. |
page_count | number | null | |
billable_units | number | null | |
byte_size | number | null | |
content_type | string | null | |
created_at | string | |
completed_at | string | null | |
error | object | null | The recorded error body when status is failed. |
webhook_status | string | null | null if no webhook_url was given, else pending, delivered or failed. See Webhooks. |
webhook_attempts | integer | Delivery attempts made so far, out of five. |
batch_id | string | Present only if the render came from a batch. |
Example: polling a queued render
Section titled “Example: polling a queued render”curl -sS https://api.galleyrender.com/v1/renders/rnd_2bk9wx4m7q1h \
-H "Authorization: Bearer $GALLEY_API_KEY" // Node 22+. No dependencies — `fetch` is built in.
const res = await fetch("https://api.galleyrender.com/v1/renders/rnd_2bk9wx4m7q1h", {
headers: {
authorization: `Bearer ${process.env.GALLEY_API_KEY}`,
},
});
const render = await res.json();
if (!res.ok) throw new Error(render.error.message);
console.log(render.status); # Python 3.9+. Standard library only.
import json, os, urllib.request
req = urllib.request.Request(
"https://api.galleyrender.com/v1/renders/rnd_2bk9wx4m7q1h",
headers={"Authorization": f"Bearer {os.environ['GALLEY_API_KEY']}"},
)
render = json.load(urllib.request.urlopen(req))
print(render["status"]) # No API key. The first call mints a 50-render trial.
curl -sS https://mcp.galleyrender.com/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_render",
"arguments": {
"render_id": "rnd_2bk9wx4m7q1h"
}
}
}' { "object": "render", "id": "rnd_2bk9wx4m7q1h", "status": "processing", "template": "og-card@1", "format": "png", "engine": "satori", "cached": false, "url": null, "expires_at": "2026-10-16T14:07:02.551Z", "page_count": null, "billable_units": null, "byte_size": null, "content_type": "image/png", "created_at": "2026-09-16T14:07:02.551Z", "completed_at": null, "error": null}{ "object": "render", "id": "rnd_2bk9wx4m7q1h", "status": "succeeded", "template": "og-card@1", "format": "png", "engine": "satori", "cached": false, "url": "https://galley-renders.r2.cloudflarestorage.com/renders/acct_9k2pv3n8rc4t/2026/09/8e77…3a.png?X-Amz-Expires=3600&X-Amz-Signature=…", "expires_at": "2026-10-16T14:07:02.551Z", "page_count": 1, "billable_units": 1, "byte_size": 91204, "content_type": "image/png", "created_at": "2026-09-16T14:07:02.551Z", "completed_at": "2026-09-16T14:07:04.118Z", "error": null}A one-second poll interval is plenty; most queued renders finish in a few seconds. If you would
rather not poll at all, set a webhook_url.
Example: a failed render
Section titled “Example: a failed render”{ "object": "render", "id": "rnd_6m1kq9w2t7xz", "status": "failed", "template": "report-table@2", "format": "pdf", "engine": "chromium", "cached": false, "url": null, "expires_at": "2026-10-16T09:31:44.002Z", "page_count": null, "billable_units": null, "byte_size": null, "content_type": "application/pdf", "created_at": "2026-09-16T09:31:41.880Z", "completed_at": "2026-09-16T09:31:44.002Z", "error": { "type": "render_failed", "message": "Template failed to render: undefined filter: currncy", "docs_url": "https://galleyrender.com/docs/errors/render_failed", "details": { "stage": "template" } }}The HTTP status is still 200: the lookup succeeded. Read status and error, not the status
code. A failed render is never billed.
GET /v1/renders
Section titled “GET /v1/renders”Recent renders on the account, newest first.
Request
Section titled “Request”| Parameter | In | Default | Description |
|---|---|---|---|
limit | query | 25 | Clamped to 1–100. A value that is not a number falls back to 25. |
There is no cursor or offset — this is a “what happened lately” endpoint, not a full export.
Response
Section titled “Response”curl -sS 'https://api.galleyrender.com/v1/renders?limit=5' \ -H "Authorization: Bearer $GALLEY_API_KEY"{ "object": "list", "data": [ { "object": "render", "id": "rnd_2bk9wx4m7q1h", "status": "succeeded", "template": "og-card@1", "format": "png", "engine": "satori", "cached": false, "url": "https://galley-renders.r2.cloudflarestorage.com/renders/acct_9k2pv3n8rc4t/2026/09/8e77…3a.png?X-Amz-Expires=3600&X-Amz-Signature=…", "expires_at": "2026-10-16T14:07:02.551Z", "page_count": 1, "billable_units": 1, "byte_size": 91204, "content_type": "image/png", "created_at": "2026-09-16T14:07:02.551Z", "completed_at": "2026-09-16T14:07:04.118Z", "error": null }, { "object": "render", "id": "rnd_7hq2m4x8k1bv", "status": "succeeded", "template": "invoice@1", "format": "pdf", "engine": "chromium", "cached": false, "url": "https://galley-renders.r2.cloudflarestorage.com/renders/acct_9k2pv3n8rc4t/2026/09/4f1c…d0.pdf?X-Amz-Expires=3600&X-Amz-Signature=…", "expires_at": "2026-10-16T14:02:11.804Z", "page_count": 1, "billable_units": 1, "byte_size": 48213, "content_type": "application/pdf", "created_at": "2026-09-16T14:02:10.119Z", "completed_at": "2026-09-16T14:02:11.804Z", "error": null } ]}Every succeeded render in the list carries a freshly signed URL, so a list call signs one URL per
row. Ask for what you need rather than limit=100 out of habit.
Errors
Section titled “Errors”| Type | Status | When |
|---|---|---|
authentication_error | 401 | Missing, unknown or revoked key. |
not_found | 404 | No render with that id on this account. |
{ "error": { "type": "not_found", "message": "No render with id `rnd_nope` on this account.", "docs_url": "https://galleyrender.com/docs/errors/not_found" }}See also
Section titled “See also”- POST /v1/render
- Caching and signed URLs — why re-signing beats re-rendering
- Webhooks — the alternative to polling