Craftkitdocs

Download a render

Stream a succeeded render's PDF straight from storage, authenticated with your project API key.

Stream a succeeded render's PDF straight from storage, authenticated with your project API key. Use this when you don't want to hand out a public bucket URL — the bytes are served behind your bearer token.

GET /v1/renders/:id/download

:id is the render id (UUIDv7) within the authenticated project.

Quick Start

curl

curl https://api.craftkit.dev/v1/renders/0193c2c3/download \
  -H "Authorization: Bearer $CRAFTKIT_API_KEY" \
  --output render.pdf

Node.js

import { writeFile } from 'node:fs/promises';

const res = await fetch('https://api.craftkit.dev/v1/renders/0193c2c3/download', {
  headers: { Authorization: `Bearer ${process.env.CRAFTKIT_API_KEY}` },
});
if (!res.ok) throw new Error(`download failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await writeFile('render.pdf', bytes);

Python

import os, requests

res = requests.get(
    "https://api.craftkit.dev/v1/renders/0193c2c3/download",
    headers={"Authorization": f"Bearer {os.environ['CRAFTKIT_API_KEY']}"},
)
res.raise_for_status()
with open("render.pdf", "wb") as f:
    f.write(res.content)

Path parameters

Field Type Description Default
id string Render id (UUIDv7). Must belong to the API key's project and have status: "succeeded".

Response — 200 OK

The response body is the raw PDF. There is no JSON envelope on success.

Header Value
Content-Type application/pdf
Content-Length Byte length of the PDF (lets you detect truncation)
Content-Disposition attachment; filename="<id>.pdf"

The asset is buffered server-side before the response is sent, so a storage read error surfaces as a clean 500 rather than a truncated 200. Always check res.ok (or raise_for_status()) before writing the bytes to disk.

When to use this vs downloadUrl

GET /v1/renders/:id returns a downloadUrl. Its value depends on deployment config:

Config downloadUrl returned by GET /v1/renders/:id This route
S3_PUBLIC_URL set (e.g. a public R2/MinIO bucket) A permanent direct public CDN URL to the object — no auth, no TTL Still works; the equivalent authenticated fetch if you prefer not to expose the public URL
S3_PUBLIC_URL not set This authenticated route's URL (…/v1/renders/:id/download) The only way to fetch the PDF — there is no public object URL

In short: when no public bucket is configured, downloadUrl points back at this route, and a bearer token is required to fetch the bytes.

Errors

HTTP Code Meaning Fix
401 unauthorized Missing, invalid, or revoked API key Send a valid Authorization: Bearer key
403 forbidden Key's project no longer exists Use a key from an active project
404 not_found No render with that id in this key's project Check the id and the API key's project scope
409 conflict Render hasn't succeeded yet, or has no stored asset (message includes the current status) Poll GET /v1/renders/:id until status is succeeded, then retry
500 internal The PDF could not be read from storage Transient — retry; if it persists, contact support

See Errors for the envelope shape and full code list.