- 50 documents/month
- 5 requests/minute
- Up to 1 MB input (~300 pages)
- PDF as a hosted link, valid 2 hours
- Page size, orientation, margins
- Syntax highlighted code
Markdown to PDF API
Turn Markdown into a clean, print-ready PDF or PNG. Tables, task lists and syntax highlighted code all render correctly — with thirteen typefaces, a full colour palette, and complete control over page size, orientation and margins.
Built for every Markdown workflow
Export AI answers, turn a README into a handout, build a resume or ship a status report — all from plain Markdown, with output that looks designed rather than generated.
Real Markdown
CommonMark with the GitHub extensions: tables, task lists, strikethrough and autolinks.
Highlighted Code
Fenced code blocks are coloured on the server for more than thirty languages.
Your Colours and Type
Thirteen typefaces, a light and dark theme, and every document colour under your control.
Print-Ready Layout
Code blocks and tables never split across pages, and table headers repeat on every page.
Every Writing System
Devanagari, Chinese, Japanese, Korean, Arabic, Hebrew, Thai, Cyrillic and Greek render with no configuration.
PDF or PNG
Take a paginated PDF, or one tall image of the whole document at up to 300 DPI.
Simple, transparent pricing
Start free and scale as you grow. No hidden fees.
- 1,000 documents/month
- 15 requests/minute
- Up to 2 MB input (~640 pages)
- PNG output, long documents as an image sequence
- Hosted link valid 7 days
- 13 fonts and custom colours
- Headers, footers, page numbers
- 5,000 documents/month
- 30 requests/minute
- Up to 3 MB input (~960 pages)
- PNG output, long documents as an image sequence
- Hosted link valid 7 days
- 13 fonts and custom colours
- Headers, footers, page numbers
- 15,000 documents/month
- 60 requests/minute
- Up to 4 MB input (~1,300 pages)
- PNG output, long documents as an image sequence
- Hosted link valid 7 days
- 13 fonts and custom colours
- Priority processing
- Unlimited documents
- Custom rate limit
- Custom input size
- All features included
- Dedicated support
API reference
Everything you need to integrate the Markdown to PDF API.
Authentication
Every request needs your API key in the x-api-key header. Keys are created in the dashboard and belong to your account rather than to a single API.
x-api-key: your_api_key Keep the key server side. Anyone holding it can spend your quota. Call this API from your backend, never from a browser.
API endpoint
POSThttps://api.corenexis.com/markdown-to-pdf/v1
One synchronous endpoint. Send the Markdown and the finished document comes back in the same response — there is no job to poll.
Input modes
The Markdown can arrive in any of these forms. Use whichever suits your environment.
1. JSON body — recommended
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: your_api_key" \
-H "Content-Type: application/json" \
-d '{"markdown": "# Invoice\n\nThank you for your business.", "page_size": "a4"}' 2. Form field
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: your_api_key" \
-F "markdown=# Hello" \
-F "page_size=letter" 3. File upload
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: your_api_key" \
-F "markdown=@report.md" 4. Raw request body
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: your_api_key" \
-H "Content-Type: text/markdown" \
--data-binary @report.md Parameters
| Parameter | Type | Description |
|---|---|---|
x-api-key Required |
String | Your API key. Send in the request header. |
markdown Required |
String | The Markdown to render. May also be sent as an uploaded file or as the raw request body. |
format |
String | pdf or png. PNG requires a paid plan.Default: pdf |
output |
String | Only url. Every result is delivered as a hosted link, on every plan, so this can be omitted. base64 and binary were withdrawn and now return a clear error.Default: url |
page_size |
String | a3, a4, a5, letter, legal or tabloid.Default: a4 |
orientation |
String | portrait or landscape.Default: portrait |
margin_top |
Number | Top margin in millimetres, 0 to 100. Default: 20 |
margin_right |
Number | Right margin in millimetres, 0 to 100. Default: 15 |
margin_bottom |
Number | Bottom margin in millimetres, 0 to 100. Default: 20 |
margin_left |
Number | Left margin in millimetres, 0 to 100. Default: 15 |
font |
String | Body typeface, from the list below. Paid plans only. Default: roboto |
mono_font |
String | Typeface for code. Paid plans only. Default: jetbrainsmono |
font_size |
Number | Base size in points, 7 to 18. Paid plans only. Default: 11 |
theme |
String | light or dark. Paid plans only.Default: light |
colors |
Object | Colour overrides as hex values. Paid plans only. |
header_text |
String | Running header text. Paid plans only. |
footer_text |
String | Running footer. Use {PAGENO} and {nb} for page numbers. Paid plans only. |
png_mode |
String | stack returns the whole document as an ordered sequence of images, first takes page one, page takes a chosen page.Default: stack |
png_page |
Number | Which page to capture when png_mode is page. Any page the document has.Default: 1 |
png_dpi |
Number | Resolution for PNG output, 72 to 300. Default: 144 |
filename |
String | Name for the produced file, without extension. Default: document |
Everything except the Markdown is optional. Send nothing else and you get a sensible A4 portrait document. Anything you do send is applied.
Fonts, colours & Markdown
Fonts
Pass a key from this list as font for body text, or as mono_font for code. Font selection is a paid feature; the free plan renders in the defaults.
| Group | Available keys |
|---|---|
| Sans | roboto (default), opensans, lato, montserrat, nunito, ibmplexsans |
| Serif | ibmplexserif, liberationserif |
| Monospace | jetbrainsmono (default), ibmplexmono, firacode |
| Devanagari | lohitdevanagari |
Other writing systems work automatically. Devanagari, Chinese, Japanese, Korean, Arabic, Hebrew, Thai, Cyrillic and Greek all render without choosing a font. Whatever the selected typeface cannot draw is filled in from a fallback.
Colours and theme
Send a colors object to restyle the document. Every value must be a hex colour such as #2563eb; anything else is rejected. Available on paid plans.
{
"markdown": "# Report",
"theme": "light",
"colors": {
"text": "#1f2937",
"heading": "#111827",
"link": "#2563eb",
"accent": "#2563eb",
"code_text": "#111827",
"code_bg": "#f3f4f6",
"border": "#e5e7eb",
"quote": "#4b5563",
"background": "#ffffff",
"table_header_bg": "#f9fafb"
}
} Set theme to dark for a dark document, then override individual colours on top if you want.
Supported Markdown
CommonMark plus the GitHub extensions: tables, task lists, strikethrough and autolinks. Fenced code blocks are syntax highlighted on the server for more than thirty languages, so the colours survive into the PDF.
Raw HTML is escaped, not rendered. HTML written inside your Markdown appears as visible text. Use the HTML to PDF API when you need HTML and CSS.
Delivery & output
How the result is delivered
Every result is stored and handed back as a hosted link under data.url, on every plan including the free one. There is nothing to decode and nothing to stream.
| Plan | Link valid for | Why |
|---|---|---|
| Free | 2 hours | Long enough to fetch and use the file in the workflow that produced it. |
| Any paid plan | 7 days | Long enough to email, queue or hand on to another service. |
Fetch the file, do not hotlink it. The link expires. Download it once and store the bytes yourself rather than pointing your users at it.
What changed
Inline delivery was withdrawn. A caller still sending the old value gets an error that says so rather than a silent change of behaviour.
| output | Status | Notes |
|---|---|---|
base64 |
Withdrawn | The file inside the JSON response. It inflated every result by a third and had to be held in memory twice. |
binary |
Withdrawn | The raw file as the response body. It could not carry a document that had become several images. |
url |
The only mode | A hosted link under data.url. This is now the default, so the parameter can be omitted. |
PDF and page images
Set format to png to receive page images instead of a PDF. PNG output is a paid feature.
A document is usually taller than one image can be: the compositor refuses any dimension above 32,000 pixels, which at the default resolution is seventeen A4 pages. So png_mode=stack returns an ordered sequence of images in data.images, each covering a page range, and data.image.truncated tells you whether the sequence covers the whole document.
| png_mode | What you get | When to use it |
|---|---|---|
stack |
The whole document as an ordered sequence of images. | Default. Reading or displaying every page. |
first |
One image of page one. | Thumbnails and previews. |
page |
One image of the page named by png_page. |
Pulling a single page out of a long document. |
How many pages per image. It depends on resolution, because the height limit is fixed: seventeen A4 pages at the default 144 dpi, thirty-five at 72, eight at 300. The response reports it as image.pages_per_image.
Code examples
Python
import requests
resp = requests.post(
"https://api.corenexis.com/markdown-to-pdf/v1",
headers={"x-api-key": "your_api_key"},
json={
"markdown": open("report.md").read(),
"page_size": "a4",
"font": "montserrat",
"colors": {"accent": "#2563eb"},
"footer_text": "Page {PAGENO} of {nb}",
},
timeout=120,
)
data = resp.json()
if not data["success"]:
raise SystemExit(data["message"])
# The result is a hosted link. Fetch it once and keep the file; the link expires.
pdf = requests.get(data["data"]["url"], timeout=120)
with open("report.pdf", "wb") as fh:
fh.write(pdf.content) Node.js
const fs = require("fs");
const res = await fetch("https://api.corenexis.com/markdown-to-pdf/v1", {
method: "POST",
headers: { "x-api-key": "your_api_key", "Content-Type": "application/json" },
body: JSON.stringify({
markdown: fs.readFileSync("report.md", "utf8"),
format: "pdf",
output: "url",
}),
});
const data = await res.json();
if (!data.success) throw new Error(data.message);
console.log(data.data.url, "expires", data.data.expires_at); PHP
<?php
$ch = curl_init('https://api.corenexis.com/markdown-to-pdf/v1');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-api-key: your_api_key',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'markdown' => file_get_contents('report.md'),
'page_size' => 'a4',
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($response['success'])) {
exit($response['message'] ?? 'request failed');
}
// The result is a hosted link. Fetch it once and keep the file; the link expires.
file_put_contents('report.pdf', file_get_contents($response['data']['url'])); Set a User-Agent. Python’s built-in urllib sends a default agent string our edge network refuses. The requests library, Node, Go, Java and cURL all work unchanged.
Response
{
"success": true,
"plan": "pro",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0, "skipped": 0, "bytes": 0, "budget_reached": false
},
"render_time_ms": 324,
"url": "https://cdn.corenexis.com/f/35at4d5YCjQ.pdf",
"filename": "document.pdf",
"size_bytes": 21168,
"expires_at": "2026-08-16T06:10:13+00:00"
},
"usage": { "remaining": 4998, "rate_limit": 30, "monthly_limit": 5000 }
} Always check the success field rather than the status code alone. On failure the body carries a code and a message naming what to change.
Every option, worked
Every option below was sent to the live API and the response shown is what came back, including the request that produced it. Copy a command, put your own key in it, and you get the same shape of answer.
Minimal request
Only the document is required. Everything else has a default: A4 portrait, 11 pt Roboto, light theme.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 631,
"url": "https://cdn.corenexis.com/f/oxeBmDm6eJL.pdf",
"filename": "document.pdf",
"size_bytes": 50395,
"expires_at": "2026-08-09T06:57:05+00:00"
},
"usage": {
"remaining": 4977,
"rate_limit": 120,
"monthly_limit": 5000
}
} Page size
One of a3, a4, a5, letter, legal or tabloid. The response echoes it back, and the produced file really is that size.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"page_size": "a5"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a5",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 194,
"url": "https://cdn.corenexis.com/f/YjzZHeTWCQU.pdf",
"filename": "document.pdf",
"size_bytes": 50393,
"expires_at": "2026-08-09T06:57:06+00:00"
},
"usage": {
"remaining": 4976,
"rate_limit": 120,
"monthly_limit": 5000
}
} Landscape orientation
Swaps the page width and height. Combines with any page size.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"orientation": "landscape"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "landscape",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 164,
"url": "https://cdn.corenexis.com/f/sdCMl1hlrdl.pdf",
"filename": "document.pdf",
"size_bytes": 50394,
"expires_at": "2026-08-09T06:57:06+00:00"
},
"usage": {
"remaining": 4975,
"rate_limit": 120,
"monthly_limit": 5000
}
} All four margins
Millimetres, 0 to 100 each. Set them independently; a value outside the range is refused before the document is rendered.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"margin_top": 30,
"margin_right": 25,
"margin_bottom": 30,
"margin_left": 25
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 139,
"url": "https://cdn.corenexis.com/f/if0faXFgh7A.pdf",
"filename": "document.pdf",
"size_bytes": 50394,
"expires_at": "2026-08-09T06:57:06+00:00"
},
"usage": {
"remaining": 4974,
"rate_limit": 120,
"monthly_limit": 5000
}
} Font size
Points, 7 to 18. Headings scale with it, so the document stays proportioned rather than merely larger.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"font_size": 13
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 128,
"url": "https://cdn.corenexis.com/f/dLV7w0Belpx.pdf",
"filename": "document.pdf",
"size_bytes": 50396,
"expires_at": "2026-08-09T06:57:06+00:00"
},
"usage": {
"remaining": 4973,
"rate_limit": 120,
"monthly_limit": 5000
}
} Body and code font
Any pair from the font tables above. Requesting a font is a paid feature.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"font": "opensans",
"mono_font": "firacode"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "opensans",
"mono_font": "firacode",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 923,
"url": "https://cdn.corenexis.com/f/uEMhTrGlhmI.pdf",
"filename": "document.pdf",
"size_bytes": 63127,
"expires_at": "2026-08-09T06:57:07+00:00"
},
"usage": {
"remaining": 4972,
"rate_limit": 120,
"monthly_limit": 5000
}
} Dark theme
Repaints the page background as well as the text, so the margins are dark too rather than leaving a white border.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"theme": "dark"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "dark",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 136,
"url": "https://cdn.corenexis.com/f/qHKtneUhaVI.pdf",
"filename": "document.pdf",
"size_bytes": 50402,
"expires_at": "2026-08-09T06:57:07+00:00"
},
"usage": {
"remaining": 4971,
"rate_limit": 120,
"monthly_limit": 5000
}
} Custom colour palette
Any subset of the palette keys. Only #rgb and #rrggbb are accepted; an unknown key or a malformed colour is reported rather than ignored.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"colors": {
"accent": "#7c3aed",
"link": "#7c3aed",
"heading": "#111827",
"code_bg": "#f5f3ff"
}
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 169,
"url": "https://cdn.corenexis.com/f/Zpdm4jN9OOU.pdf",
"filename": "document.pdf",
"size_bytes": 50389,
"expires_at": "2026-08-09T06:57:08+00:00"
},
"usage": {
"remaining": 4970,
"rate_limit": 120,
"monthly_limit": 5000
}
} Header and footer with page numbers
{PAGENO} and {nb} are substituted per page. Verified across chunk boundaries on documents of several hundred pages.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "...long document...",
"header_text": "Quarterly Report — Confidential",
"footer_text": "Page {PAGENO} of {nb}"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 37,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 1055,
"url": "https://cdn.corenexis.com/f/RyZN0BtksWp.pdf",
"filename": "document.pdf",
"size_bytes": 86316,
"expires_at": "2026-08-09T06:57:09+00:00"
},
"usage": {
"remaining": 4969,
"rate_limit": 120,
"monthly_limit": 5000
}
} Filename
Sets the stored filename. Letters, digits, dot, dash and underscore, up to 80 characters; the extension is added.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"filename": "invoice-2026-0184"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "pdf",
"mime": "application/pdf",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 308,
"url": "https://cdn.corenexis.com/f/cJYU2qcqOAN.pdf",
"filename": "invoice-2026-0184.pdf",
"size_bytes": 50413,
"expires_at": "2026-08-09T06:57:09+00:00"
},
"usage": {
"remaining": 4968,
"rate_limit": 120,
"monthly_limit": 5000
}
} PNG, whole document
A document too tall for one image comes back as an ordered sequence. Each image holds as many pages as the compositor allows, and truncated tells you whether the set is complete.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "...long document...",
"format": "png"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "png",
"mime": "image/png",
"pages": 37,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 22574,
"url": "https://cdn.corenexis.com/f/ljTe16hzNpA.png",
"filename": "document-p1-17.png",
"size_bytes": 1368378,
"expires_at": "2026-08-09T06:57:32+00:00",
"image": {
"dpi": 144,
"pages_total": 37,
"pages_rendered": 37,
"pages_per_image": 17,
"truncated": false
},
"image_count": 3,
"total_bytes": 2945732,
"images": [
{
"url": "https://cdn.corenexis.com/f/ljTe16hzNpA.png",
"filename": "document-p1-17.png",
"size_bytes": 1368378,
"expires_at": "2026-08-09T06:57:32+00:00",
"page_from": 1,
"page_to": 17,
"width": 1191,
"height": 28628
},
{
"url": "https://cdn.corenexis.com/f/5FtJndj9BX7.png",
"filename": "document-p18-34.png",
"size_bytes": 1387052,
"expires_at": "2026-08-09T06:57:32+00:00",
"page_from": 18,
"page_to": 34,
"width": 1191,
"height": 28628
},
"... 1 more, one per page range, in order"
]
},
"usage": {
"remaining": 4967,
"rate_limit": 120,
"monthly_limit": 5000
}
} PNG, first page only
One image of page one. Useful for a thumbnail.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "...long document...",
"format": "png",
"png_mode": "first"
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "png",
"mime": "image/png",
"pages": 37,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 2665,
"url": "https://cdn.corenexis.com/f/dBsZESv6l3a.png",
"filename": "document.png",
"size_bytes": 148265,
"expires_at": "2026-08-09T06:57:35+00:00",
"image": {
"dpi": 144,
"pages_total": 37,
"pages_rendered": 1,
"pages_per_image": 17,
"truncated": false
},
"image_count": 1,
"total_bytes": 148265,
"images": [
{
"url": "https://cdn.corenexis.com/f/dBsZESv6l3a.png",
"filename": "document.png",
"size_bytes": 148265,
"expires_at": "2026-08-09T06:57:35+00:00",
"page_from": 1,
"page_to": 1,
"width": 1191,
"height": 1684
}
]
},
"usage": {
"remaining": 4966,
"rate_limit": 120,
"monthly_limit": 5000
}
} PNG, one chosen page
Any page the document has. Asking beyond the end reports the real count.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "...long document...",
"format": "png",
"png_mode": "page",
"png_page": 12
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "png",
"mime": "image/png",
"pages": 37,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 1109,
"url": "https://cdn.corenexis.com/f/7qc2gnVdmPw.png",
"filename": "document.png",
"size_bytes": 151355,
"expires_at": "2026-08-09T06:57:36+00:00",
"image": {
"dpi": 144,
"pages_total": 37,
"pages_rendered": 1,
"pages_per_image": 17,
"truncated": false
},
"image_count": 1,
"total_bytes": 151355,
"images": [
{
"url": "https://cdn.corenexis.com/f/7qc2gnVdmPw.png",
"filename": "document.png",
"size_bytes": 151355,
"expires_at": "2026-08-09T06:57:36+00:00",
"page_from": 12,
"page_to": 12,
"width": 1191,
"height": 1684
}
]
},
"usage": {
"remaining": 4965,
"rate_limit": 120,
"monthly_limit": 5000
}
} PNG resolution
72 to 300 dpi. Higher resolution means fewer pages fit in one image, because the compositor’s height limit is fixed.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"format": "png",
"png_dpi": 300
}' {
"success": true,
"plan": "basic",
"data": {
"status": "completed",
"format": "png",
"mime": "image/png",
"pages": 1,
"page_size": "a4",
"orientation": "portrait",
"font": "roboto",
"mono_font": "jetbrainsmono",
"theme": "light",
"source_images": {
"embedded": 0,
"skipped": 0,
"bytes": 0,
"budget_reached": false
},
"render_time_ms": 744,
"url": "https://cdn.corenexis.com/f/tU78MX1UDiV.png",
"filename": "document.png",
"size_bytes": 84565,
"expires_at": "2026-08-09T06:57:37+00:00",
"image": {
"dpi": 300,
"pages_total": 1,
"pages_rendered": 1,
"pages_per_image": 8,
"truncated": false
},
"image_count": 1,
"total_bytes": 84565,
"images": [
{
"url": "https://cdn.corenexis.com/f/tU78MX1UDiV.png",
"filename": "document.png",
"size_bytes": 84565,
"expires_at": "2026-08-09T06:57:37+00:00",
"page_from": 1,
"page_to": 1,
"width": 2480,
"height": 3508
}
]
},
"usage": {
"remaining": 4964,
"rate_limit": 120,
"monthly_limit": 5000
}
} Refused: one unbroken block
Laying out a single run of text costs time in proportion to its length squared, so this one shape is refused by name. The same words split into paragraphs render in under three seconds.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "...40,000 words with no paragraph breaks..."
}' {
"success": false,
"code": "INVALID_INPUT",
"message": "This document contains a single unbroken block of text of 559,999 characters, which is too long to lay out. Break it into paragraphs separated by blank lines. Document length itself is not the problem.",
"longest_text_block": 559999
} Refused: too many table cells
Collapsed borders are resolved across the whole table before any of it is drawn, which is what makes a long table the most memory-hungry shape.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "...a 7,000-row table..."
}' {
"success": false,
"code": "INVALID_INPUT",
"message": "This document contains 28,005 table cells, which is more than can be laid out in one request. Split the tables across several documents and combine the results.",
"table_cells": 28005
} Refused: retired output mode
Inline delivery was withdrawn. Every result is a hosted link.
curl -X POST "https://api.corenexis.com/markdown-to-pdf/v1" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Quarterly Report\n\nRevenue held steady while costs fell.\n\n| Region | Revenue | Change |\n|---|---|---|\n| North | 48,200 | +4% |\n| South | 31,700 | -2% |\n\n- [x] Figures verified\n- [ ] Board review\n\n```python\ndef margin(revenue, cost):\n return (revenue - cost) / revenue\n```\n",
"output": "base64"
}' {
"success": false,
"code": "INVALID_INPUT",
"message": "'output' no longer accepts 'base64'. Every result is delivered as a hosted link, so omit the parameter or send output=url. The response carries the link in data.url."
} Errors
Errors arrive as JSON with a stable code.
| Code | HTTP | Meaning |
|---|---|---|
MISSING_KEY |
401 | The x-api-key header was not sent. |
INVALID_KEY |
401 | The key does not exist or has been revoked. |
KEY_DISABLED |
403 | The key exists but has been switched off. |
ACCOUNT_SUSPENDED |
403 | The account is suspended. |
NO_SUBSCRIPTION |
402 | The account has no subscription to this API. |
SUBSCRIPTION_EXPIRED |
402 | The subscription lapsed and the grace period has passed. |
RATE_LIMIT_EXCEEDED |
429 | Too many requests this minute. Retry shortly. |
QUOTA_EXCEEDED |
429 | The monthly allowance is used up. |
PARAM_LIMIT_EXCEEDED |
400 | A requested feature or value is above what the plan allows. The response names the parameter. |
INVALID_INPUT |
400 | Something in the request is malformed. The message names the field. |
RENDER_TIMEOUT |
408 | The document took too long. Simplify it and retry. |
PROCESSING_FAILED |
422 | The document could not be rendered. |
RENDERER_BUSY |
503 | Every worker is busy. Retry in a moment. |
Quota-safe failures. If rendering fails after your request was accepted, your monthly quota is not charged. Only successful documents count.
Rate limiting. Free 5/min · Starter 15/min · Pro 30/min · Max 60/min.
Limits
| Limit | Value |
|---|---|
| Maximum input size | 1 MB free, up to 4 MB on Max |
| Pages that means in practice | About 300 pages free, about 1,300 on Max, for ordinary prose |
| Page limit | There is no page limit. Documents of a thousand pages and more render normally |
| What is actually refused | A single unbroken block of text, or more table cells than fit in memory. Both are named in the error |
| Maximum delivered file | 15 MB. Text contributes about 1.7 KB per page, so this is only reachable with embedded images |
| Embedded images | 6 MB per image, 40 per document, 12 MB in total |
| Hosted link lifetime | 2 hours on the free plan, 7 days on any paid plan |
Length is not what gets refused. Nothing is rejected for being long. What costs is a single run of text with no break in it, because laying one out takes time in proportion to its length squared. The same words split into paragraphs render in seconds. The error names which property is the problem so you know what to change.
Sample output
The samples/ directory in the project repository holds real output across page sizes, orientations, fonts, themes, palettes and both formats, so you can see what each option produces before calling the API.
Ready to ship better documents?
Create your free account and turn your first Markdown file into a PDF in seconds. No credit card required.