POST/api/v1/renders
Render one PDF from HTML and a row of data. Responds with the PDF bytes.
- •html is a template; data fills its placeholders. page_settings takes the app page-setup keys: page_size (a4, a5, a3, letter, legal, tabloid), orientation, margins (top/right/bottom/left in mm), page_numbers, scale, print_background. Unknown keys are rejected with 400.
- •Billed as one document against your monthly allowance. On the free plan the PDF carries the SheetRender watermark footer.
- •HTML over 2 MB is rejected with 413 — both the template you send and the document it renders to.
Request body
{
"html": "<h1>Invoice {{ invoice_no }}</h1><p>{{ client }}</p>",
"data": { "invoice_no": "1043", "client": "Acme Ltd" },
"page_settings": { "page_size": "a4", "margins": { "top": 18, "right": 18, "bottom": 18, "left": 18 } }
}
Response
200 application/pdf
Content-Disposition: inline; filename="render.pdf"
<PDF bytes>
Example
curl -X POST https://sheetrender.com/api/v1/renders \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"html":"<h1>Invoice {{ invoice_no }}</h1>","data":{"invoice_no":"1043"}}' \
--output invoice.pdf
GET/api/v1/templates
List the templates in your account, newest first.
Response
200 application/json
[
{
"id": "tpl_8f2c…",
"name": "Monthly invoice",
"created_at": "2026-07-02T09:14:31Z",
"updated_at": "2026-08-11T16:02:08Z"
}
]
Example
curl https://sheetrender.com/api/v1/templates \
-H "Authorization: Bearer $SHEETRENDER_API_KEY"
POST/api/v1/designs
Design a new template from your data plus an example document or a written brief.
- •This is the same design step the app runs: Claude reads your columns and the example, writes an HTML template, and maps each placeholder to a column. It takes 30 to 120 seconds, so the call returns 202 straight away and you poll GET /designs/{id}.
- •Give the data one way: dataset_id (an existing dataset), rows (a JSON array), or a multipart data file (.csv, .xlsx, .xlsm, or tab-separated .tsv or .txt). A dataset file or rows creates a new project.
- •Give the design source one way: an example file (.pdf, .png, .jpg, .webp, or .docx, multipart field example) on its own, or a brief (up to 2,000 characters) with an optional style_id from the style gallery.
- •Send JSON when there is no file. Send multipart/form-data when there is; rows then goes in as a JSON string.
- •One design at a time per project. Sending the same request again while it runs returns the same design_id without charging again; a different design for that project answers 409 until the first one finishes.
- •Counts against your plan's template and AI limits, the same as designing in the app.
Request body
{
"dataset_id": "ds_1a90…",
"brief": "One-page invoice. Company name top left, line items in a table, total bottom right.",
"name": "Invoice"
}
Response
202 application/json
{ "design_id": "tpl_9d21…", "status": "running" }
Example
curl -X POST https://sheetrender.com/api/v1/designs \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
-F "data=@clients.xlsx" \
-F "example=@last-months-invoice.pdf" \
-F "name=Invoice"
GET/api/v1/designs/{id}
Poll a design until it has a template.
- •status is running, succeeded, or failed. Poll every few seconds.
- •On succeeded the design_id is also the template_id: pass it to POST /templates/{id}/render or POST /jobs. mapping lists which column each placeholder reads.
- •On failed, error says why in one line. A design that was running when the server restarted reports failed; start a new one.
Response
200 application/json
{
"design_id": "tpl_9d21…",
"status": "succeeded",
"template_id": "tpl_9d21…",
"dataset_id": "ds_1a90…",
"name": "Invoice",
"mapping": [{ "variable": "client_name", "column": "Client" }],
"preview_url": "/api/v1/templates/tpl_9d21…/preview.pdf"
}
Example
curl https://sheetrender.com/api/v1/designs/tpl_9d21 \
-H "Authorization: Bearer $SHEETRENDER_API_KEY"
GET/api/v1/templates/{id}/preview.pdf
Render the first row of a template as a preview PDF.
- •Free and not counted as a document. Carries the preview watermark. Use it to check a design before queueing a job.
- •A template with no finished version yet answers 404.
Response
200 application/pdf
<PDF bytes>
Example
curl https://sheetrender.com/api/v1/templates/tpl_9d21/preview.pdf \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
--output preview.pdf
POST/api/v1/templates/{id}/render
Render one PDF from a saved template and a row of data.
- •Uses the template’s current version and its saved page settings. Pass page_settings to override them for this call only.
- •Billed as one document, same as /renders. The filename comes from the template name.
- •A template with no current version answers 400.
Request body
{
"data": { "invoice_no": "1043", "client": "Acme Ltd", "total": "1,280.00" },
"page_settings": { "page_size": "letter" }
}
Response
200 application/pdf
Content-Disposition: inline; filename="Monthly invoice.pdf"
<PDF bytes>
Example
curl -X POST https://sheetrender.com/api/v1/templates/tpl_8f2c/render \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"data":{"invoice_no":"1043","client":"Acme Ltd"}}' \
--output invoice.pdf
POST/api/v1/templates/{id}/datasets
Upload a spreadsheet (.csv, .xlsx, .xlsm, or tab-separated .tsv or .txt) as a dataset the given template can run a batch against.
- •The body is multipart/form-data with one part named file. The dataset is stored on the template’s project, so it is immediately usable with POST /jobs — and with any other template in that project.
- •columns[].key is the sanitized column name that POST /jobs group_by and template placeholders use. original is the spreadsheet header as uploaded; inferred_type is "string", "number", or "date". A date column is one whose values read as dates, including real date cells in .xlsx; the date filter formats them.
- •Creating a dataset is free. Only rendering against it counts toward your monthly document allowance. Files over 20 MB answer 413; more than 500,000 cells, or a file that is not a readable spreadsheet, answers 400. An unknown template answers 404.
Request body
multipart/form-data
file: customers.csv
Response
201 application/json
{
"id": "ds_1a90…",
"filename": "customers.csv",
"sheet_name": "csv",
"columns": [
{ "key": "client", "original": "Client", "inferred_type": "string" },
{ "key": "invoice_no", "original": "Invoice No", "inferred_type": "string" },
{ "key": "total", "original": "Total", "inferred_type": "number" }
],
"row_count": 240,
"created_at": "2026-08-19T11:18:02Z"
}
Example
curl -X POST https://sheetrender.com/api/v1/templates/tpl_8f2c/datasets \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
-F "file=@customers.csv"
POST/api/v1/templates/{id}/datasets/rows
Create the same kind of dataset from JSON rows, when you have no file to post.
- •rows is a JSON array of objects. The spreadsheet header is the union of those objects’ keys, in first-seen order; a missing key on a later row becomes a blank cell. name is an optional label stored as the filename.
- •Creating a dataset is free. Only rendering against it counts toward your monthly document allowance.
- •At most 50,000 rows, and at most 500,000 cells including the header row. Over either limit, or an empty rows list, answers 400.
Request body
{
"rows": [
{ "name": "Ada", "total": 42 },
{ "name": "Ben", "total": 17 }
],
"name": "August customers"
}
Response
201 application/json
{
"id": "ds_7e22…",
"filename": "August customers.xlsx",
"sheet_name": "Data",
"columns": [
{ "key": "name", "original": "name", "inferred_type": "string" },
{ "key": "total", "original": "total", "inferred_type": "number" }
],
"row_count": 2,
"created_at": "2026-08-19T11:18:02Z"
}
Example
curl -X POST https://sheetrender.com/api/v1/templates/tpl_8f2c/datasets/rows \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"rows":[{"name":"Ada","total":42},{"name":"Ben","total":17}],"name":"August customers"}'
GET/api/v1/templates/{id}/datasets
List the datasets this template can run a batch against, newest first.
- •Scoped to the template’s project, so a dataset uploaded against any template there appears here. An unknown template answers 404.
Response
200 application/json
[
{
"id": "ds_1a90…",
"filename": "customers.csv",
"sheet_name": "csv",
"columns": [
{ "key": "client", "original": "Client", "inferred_type": "string" },
{ "key": "invoice_no", "original": "Invoice No", "inferred_type": "string" },
{ "key": "total", "original": "Total", "inferred_type": "number" }
],
"row_count": 240,
"created_at": "2026-08-19T11:18:02Z"
}
]
Example
curl https://sheetrender.com/api/v1/templates/tpl_8f2c/datasets \
-H "Authorization: Bearer $SHEETRENDER_API_KEY"
POST/api/v1/jobs
Queue a batch: every row of a dataset rendered with a template.
- •The template and the dataset must belong to the same project. Create the dataset with POST /templates/{id}/datasets or POST /templates/{id}/datasets/rows, or pick one from GET /templates/{id}/datasets.
- •filename_template and group_by are project settings, not per-job arguments: passing them durably reconfigures the project, exactly as editing them in the app would. Later jobs — including scheduled ones — use the new values.
- •group_by must name a column key from the dataset (columns[].key), otherwise 400.
- •Billed per rendered document as the job runs.
Request body
{
"template_id": "tpl_8f2c…",
"dataset_id": "ds_1a90…",
"filename_template": "{{ client }}-{{ invoice_no }}.pdf",
"group_by": "client"
}
Response
201 application/json
{ "job_id": "job_44b1…" }
Example
curl -X POST https://sheetrender.com/api/v1/jobs \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"template_id":"tpl_8f2c","dataset_id":"ds_1a90"}'
GET/api/v1/jobs/{id}
Poll a batch job and collect the document ids it produced.
- •status moves through queued and running to succeeded, failed, or cancelled.
- •documents is empty until the job finishes, then lists one entry per rendered PDF in row order.
Response
200 application/json
{
"id": "job_44b1…",
"status": "succeeded",
"rows_total": 240,
"rows_done": 240,
"rows_failed": 0,
"created_at": "2026-08-19T11:20:04Z",
"finished_at": "2026-08-19T11:22:47Z",
"documents": [{ "id": "doc_71c3…", "filename": "Acme-1043.pdf" }],
"merged_available": true,
"zip_available": true
}
Example
curl https://sheetrender.com/api/v1/jobs/job_44b1 \
-H "Authorization: Bearer $SHEETRENDER_API_KEY"
GET/api/v1/documents/{id}
Download one rendered PDF from a batch.
- •A document id from another account answers 404, never 403.
Response
200 application/pdf
Content-Disposition: attachment; filename="Acme-1043.pdf"
<PDF bytes>
Example
curl https://sheetrender.com/api/v1/documents/doc_71c3 \
-H "Authorization: Bearer $SHEETRENDER_API_KEY" \
--output Acme-1043.pdf