API Reference
Convert Markdown or HTML to a production-ready PDF with a single HTTP request.
Base URL
https://www.docrenders.com
All endpoints accept and return JSON unless otherwise noted. PDF responses are application/pdf.
Authentication
Every render endpoint requires an API key passed as a Bearer token in the Authorization header.
Authorization: Bearer dcr_live_YOUR_API_KEY
Keys are prefixed dcr_live_. Generate keys from your dashboard.
Errors
All errors return a consistent JSON shape:
{
"error": {
"code": "quota_exceeded",
"message": "monthly render limit of 100 reached; upgrade your plan to continue"
}
}
| Code | HTTP | Meaning |
|---|---|---|
unauthorized | 401 | Missing or invalid API key |
quota_exceeded | 429 | Monthly render limit reached |
rate_limited | 429 | Too many requests per minute |
invalid_request | 400 | Malformed JSON, missing fields, or unknown parameters |
missing_fields | 400 | Required template data fields not provided |
render_timeout | 504 | Render exceeded the 26s time limit |
render_failed | 500 | Internal rendering error |
internal_error | 500 | Unexpected server error |
Rate Limits
| Plan | Requests / minute | Renders / month |
|---|---|---|
| Free | 10 | 100 |
| Starter | 60 | 5,000 |
| Pro | 300 | 25,000 |
Exceeding the per-minute limit returns 429 rate_limited. Exceeding the monthly quota returns 429 quota_exceeded.
SDKs
Official client libraries are available for Go, JavaScript/TypeScript, and Python. All SDKs are open source at github.com/JWhist/docrenders-sdks.
Install
go get github.com/JWhist/docrenders-sdks/go
Render from markdown
import docrenders "github.com/JWhist/docrenders-sdks/go"
client := docrenders.NewClient("dcr_live_YOUR_API_KEY")
pdf, err := client.Render(ctx, docrenders.RenderRequest{
Markdown: "# Hello\n\nGenerated by DocRenders.",
Options: docrenders.RenderOptions{Format: "A4"},
})
Render from template + data
pdf, err := client.Render(ctx, docrenders.RenderRequest{
Template: "invoice",
Data: map[string]any{
"name": "Acme Corp",
"date": "2026-06-03",
"total": 1500.00,
"items": []map[string]any{
{"description": "Design", "qty": 1, "unit_price": 1500, "amount": 1500},
},
},
})
Render to signed URL
result, err := client.RenderSignedURL(ctx, docrenders.RenderRequest{
Markdown: "# Report",
})
fmt.Println(result.URL) // expires in 15 minutes
Check usage
usage, err := client.Usage(ctx)
fmt.Printf("%d / %d renders used\n", usage.RendersUsed, usage.RendersLimit)
Install
npm install docrenders-sdk
Render from markdown
import DocRendersClient from "docrenders-sdk";
const client = new DocRendersClient("dcr_live_YOUR_API_KEY");
const pdf = await client.render({
markdown: "# Hello\n\nGenerated by DocRenders.",
options: { format: "A4" },
}); // Uint8Array
Render from template + data
const pdf = await client.render({
template: "invoice",
data: {
name: "Acme Corp",
date: "2026-06-03",
total: 1500,
items: [{ description: "Design", qty: 1, unit_price: 1500, amount: 1500 }],
},
});
Render to signed URL
const { url, expires_at } = await client.renderSignedURL({
markdown: "# Report",
});
console.log(url); // expires in 15 minutes
Error handling
import { DocRendersError } from "docrenders-sdk";
try {
const pdf = await client.render({ markdown: "# Hello" });
} catch (err) {
if (err instanceof DocRendersError) {
console.error(err.code, err.message);
}
}
Check usage
const usage = await client.usage();
console.log(`${usage.renders_used} / ${usage.renders_limit} renders used`);
Install
pip install docrenders-sdk
Render from markdown
from docrenders import DocRendersClient, RenderRequest, RenderOptions
client = DocRendersClient("dcr_live_YOUR_API_KEY")
pdf = client.render(RenderRequest(
markdown="# Hello\n\nGenerated by DocRenders.",
options=RenderOptions(format="A4"),
))
Render from template + data
pdf = client.render(RenderRequest(
template="invoice",
data={
"name": "Acme Corp",
"date": "2026-06-03",
"total": 1500.00,
"items": [{"description": "Design", "qty": 1, "unit_price": 1500, "amount": 1500}],
},
))
Render to signed URL
result = client.render_signed_url(RenderRequest(markdown="# Report")) print(result.url) # expires in 15 minutes
Error handling
from docrenders import DocRendersError
try:
pdf = client.render(RenderRequest(markdown="# Hello"))
except DocRendersError as e:
print(e.code, str(e))
Check usage
usage = client.usage()
print(f"{usage.renders_used} / {usage.renders_limit} renders used")
POST /render
Synchronously converts Markdown or HTML to a PDF and returns the raw binary.
Request body
{
"markdown": "# Hello\n\nGenerated by DocRenders.",
"template": "invoice",
"data": {
"name": "Acme Corp",
"date": "2026-06-03",
"total": 1500.00,
"items": [{ "description": "Design", "qty": 1, "unit_price": 1500, "amount": 1500 }]
},
"options": {
"format": "A4",
"margin_top": "1in",
"margin_right": "1in",
"margin_bottom": "1in",
"margin_left": "1in",
"landscape": false
},
"output": "binary"
}
| Field | Type | Default | Notes | |
|---|---|---|---|---|
markdown | required* | string | — | Markdown content. GFM supported. |
html | required* | string | — | Raw HTML. Alternative to markdown. |
template | optional | string | — | Built-in or custom template name/ID. See Templates. |
data | optional | object | — | Variables injected into the template. When provided with template, the template supplies the content — no markdown needed. See Templates. |
options.format | optional | string | "A4" | "A4", "Letter", or "Legal" |
options.margin_top | optional | string | "1in" | Any CSS length: "1in", "20mm", "72px" |
options.margin_right | optional | string | "1in" | |
options.margin_bottom | optional | string | "1in" | |
options.margin_left | optional | string | "1in" | |
options.landscape | optional | bool | false | Rotate page to landscape orientation |
output | optional | string | "binary" | "binary" returns raw PDF bytes; "url" returns a signed download URL |
filename | optional | string | "render.pdf" | Name of the stored file. Also used as the Content-Disposition filename on binary responses. |
*One of markdown, html, or template + data is required.
Every render is stored server-side regardless of the output value. The output parameter only controls what is returned in the response — the PDF is always retained and accessible via its signed URL.
Response — output=binary (default)
HTTP 200 Content-Type: application/pdf Content-Disposition: attachment; filename="render.pdf" <binary PDF bytes>
Response — output=url
{
"url": "https://storage.docrenders.dev/pdfs/...",
"expires_at": "2026-05-30T15:00:00Z",
"render_time_ms": 1240
}
The signed URL expires after 15 minutes and is scoped to your account — it cannot be used to access another user's files. Use this mode when you want to redirect a user directly to the PDF rather than proxy the bytes through your own server.
Example — binary
curl -X POST https://www.docrenders.com/render \
-H "Authorization: Bearer dcr_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"markdown":"# Hello\n\nGenerated by DocRenders."}' \
--output output.pdf
Example — signed URL
curl -X POST https://www.docrenders.com/render \
-H "Authorization: Bearer dcr_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"markdown":"# Hello\n\nGenerated by DocRenders.","output":"url"}'
POST /render/file
Same as /render but accepts a file upload via multipart/form-data. Supports .md, .markdown, and .html files.
Form fields
| Field | Notes | |
|---|---|---|
file | required | .md, .markdown, or .html — max 10 MB |
format | optional | "A4" (default), "Letter", "Legal" |
margin_top | optional | CSS length, default "1in" |
margin_right | optional | CSS length, default "1in" |
margin_bottom | optional | CSS length, default "1in" |
margin_left | optional | CSS length, default "1in" |
landscape | optional | "true" to enable landscape |
Response
HTTP 200 Content-Type: application/pdf Content-Disposition: attachment; filename="render.pdf" <binary PDF bytes>
Example
curl -X POST https://www.docrenders.com/render/file \ -H "Authorization: Bearer dcr_live_YOUR_API_KEY" \ -F "file=@invoice.md" \ -F "format=A4" \ --output output.pdf
