Skip to content

GET /v1/renders/:id

Two read endpoints. Both are free, neither ever re-renders anything.

GET https://api.galleyrender.com/v1/renders/:id
GET https://api.galleyrender.com/v1/renders?limit=25
Authorization: Bearer glr_sk_…

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.

ParameterInDescription
idpathThe 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.

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).

FieldTypeDescription
objectstringAlways render.
idstringrnd_….
statusstringqueued, processing, succeeded or failed.
templatestringResolved reference with version, e.g. invoice@1.
formatstringpdf, png or jpg.
enginestringchromium or satori.
cachedbooleanAlways false on this endpoint.
urlstring | nullFreshly signed, valid for an hour. null unless status is succeeded.
expires_atstring | nullRetention deadline for the stored object.
page_countnumber | null
billable_unitsnumber | null
byte_sizenumber | null
content_typestring | null
created_atstring
completed_atstring | null
errorobject | nullThe recorded error body when status is failed.
webhook_statusstring | nullnull if no webhook_url was given, else pending, delivered or failed. See Webhooks.
webhook_attemptsintegerDelivery attempts made so far, out of five.
batch_idstringPresent only if the render came from a batch.
Poll until it finishes shell
curl -sS https://api.galleyrender.com/v1/renders/rnd_2bk9wx4m7q1h \
  -H "Authorization: Bearer $GALLEY_API_KEY"
Poll until it finishes javascript
// 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);
Poll until it finishes python
# 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"])
Poll until it finishes — no key shell + json
# 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"
      }
    }
  }'
200 OK — still working
{
"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
}
200 OK — finished
{
"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.

200 OK — the request worked, the render did not
{
"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.

Recent renders on the account, newest first.

ParameterInDefaultDescription
limitquery25Clamped 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.

The last five renders
curl -sS 'https://api.galleyrender.com/v1/renders?limit=5' \
-H "Authorization: Bearer $GALLEY_API_KEY"
200 OK
{
"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.

TypeStatusWhen
authentication_error401Missing, unknown or revoked key.
not_found404No render with that id on this account.
404 Not Found
{
"error": {
"type": "not_found",
"message": "No render with id `rnd_nope` on this account.",
"docs_url": "https://galleyrender.com/docs/errors/not_found"
}
}