If you've ever needed to email a quarterly report from a cron job, attach an invoice to a Zapier workflow, or ship a weekly digest from an n8n trigger, you've hit the same wall I've been hitting for years: generating a PDF programmatically is weirdly hard.
The options today are all flavors of the same compromise.
What exists in 2026
PDFMonkey - Starter €5/mo for 300 documents, Pro €15/mo for 3,000. You design your template in their visual editor, then POST variables to fill it in. Solid editor, reasonable pricing. The friction is that every new document shape means a trip back to the visual tool.
APITemplate.io - PDF Basic $19/mo (annual) for 3,000 PDFs. Same core idea as PDFMonkey: design a template in a web editor, then POST JSON data to render it.
DocRaptor - from $15/mo, powered by Prince. HTML-in, PDF-out - so no template editor, but you own the problem of producing print-ready HTML and CSS (page breaks, headers, footers, paged media rules). Powerful if you already have a styled HTML pipeline; more work if you don't.
Rolling your own - Puppeteer or Chromium in a Lambda, @react-pdf/renderer, or wkhtmltopdf in a Docker image. Works until you need it to not-work: font loading, CJK support, memory limits, cold starts, timeouts, and the joy of debugging headless Chrome in CI.
The pattern: most hosted PDF APIs either hand you a template editor (design once, fill slots later) or hand you an HTML-to-PDF pipe (bring your own rendered HTML). Both are fine when the layout is known ahead of time. Both are friction when:
- The shape of your data is determined at runtime (e.g., an LLM decides what to render)
- You want multiple outputs from the same pipeline - a PDF, a web view, and a raw data table
- You need a dashboard with metrics + charts + tables composed together, not a static invoice
The one-API-call approach
A few months ago I started building genui.sh for the workflows I couldn't make the existing tools fit. The pitch is simple: POST a JSON payload describing what you want, get back a signed URL.
Here's the smallest version - a markdown document rendered to a hosted PDF:
curl -X POST https://www.genui.sh/api/artifacts/share \
-H "Authorization: Bearer $GENUI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "pdf",
"title": "Q4 Report",
"content": {
"text": "# Q4 2025 Performance\n\nRevenue grew **23%** year-over-year.\n\n## Highlights\n- Launched mobile app\n- Expanded to 3 new markets",
"pageSize": "A4"
},
"expiresIn": "7d"
}'
Response:
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://genui.sh/a/550e8400-e29b-41d4-a716-446655440000?token=eyJhbGc...",
"expiresAt": "2026-04-19T14:30:45.123Z"
}
}
See it live: Q4 report PDF →
That's the whole workflow. No template to design first, no visual editor to maintain, no data-shape gymnastics. The content field is just markdown - the same kind you'd write in a README. The expiresIn field is optional: on Pro it defaults to 30 days, so pass "never" explicitly if you want a permanent link; on Free and Starter it's clamped to the plan max (7 and 30 days respectively). And Authorization accepts either Bearer <key> or the raw key, whichever your HTTP client prefers.
If you want a table instead of prose:
-d '{
"template": "table",
"title": "Recent orders",
"content": {
"columns": [
{"header": "Order ID", "accessorKey": "id"},
{"header": "Customer", "accessorKey": "customer"},
{"header": "Amount", "accessorKey": "amount"}
],
"rows": [
{"id": "ORD-001", "customer": "Acme Corp", "amount": "$1,250"},
{"id": "ORD-002", "customer": "Globex", "amount": "$890"},
{"id": "ORD-003", "customer": "Initech", "amount": "$3,420"},
{"id": "ORD-004", "customer": "Umbrella", "amount": "$560"},
{"id": "ORD-005", "customer": "Stark Industries", "amount": "$7,800"}
],
"config": {
"enableSearch": true,
"enablePagination": true,
"enableExport": true
}
}
}'
See it live: Orders table →
Or a bar chart:
-d '{
"template": "chart",
"title": "Monthly revenue",
"content": {
"type": "bar",
"data": [
{"name": "Jan", "value": 84000},
{"name": "Feb", "value": 92000},
{"name": "Mar", "value": 108000},
{"name": "Apr", "value": 121000},
{"name": "May", "value": 134000},
{"name": "Jun", "value": 142000}
],
"config": {
"showGrid": true,
"showLegend": true,
"colors": ["#3b82f6"]
}
}
}'
See it live: Monthly revenue chart →
Same URL, same auth header, same response shape. Five output types (markdown, table, chart, pdf, and a composable dashboard format called @std/dynamic) all sharing one endpoint. The chart template supports four sub-types - line, bar, area, pie - so swapping from a bar to a line chart is a one-word change. That's the one-call, multi-format part: the same POST body shape gives you a PDF, a hosted web view, or a live dashboard depending on template.
When things go wrong, errors come back in a consistent shape too:
// 403 - hit your monthly artifact quota
{
"error": "Monthly artifact limit reached (50). Upgrade your plan for more.",
"code": "FORBIDDEN"
}
// 429 - rate limited (100 req/min per key)
{
"error": "Too many requests",
"code": "RATE_LIMITED"
}
Composing a dashboard
The interesting template is @std/dynamic. Instead of a single content blob, you POST a tree of components - Metric, Chart, Table, Card, Stack - and the renderer composes them into a hosted page:
-d '{
"template": "@std/dynamic",
"title": "Weekly snapshot",
"content": {
"component": "Stack",
"props": { "gap": "md" },
"children": [
{
"component": "Metric",
"props": { "label": "Revenue", "value": "$284,000", "delta": "+12%" }
},
{
"component": "Chart",
"props": {
"type": "line",
"data": [
{"day": "Mon", "visits": 1200},
{"day": "Tue", "visits": 1450},
{"day": "Wed", "visits": 1380}
],
"config": {
"categoryKey": "day",
"series": [{"key": "visits", "label": "Visits"}]
}
}
}
]
}
}'
See it live: Weekly snapshot dashboard →
That's the actual payoff: the same tree can be served as a web view today and re-POSTed as "template": "pdf" tomorrow to get a printable version, without re-designing anything. If an LLM is deciding the shape of the output at runtime, this is the format you want it writing.
What you don't have to think about
- Hosting the output. The returned URL is a genui.sh page that renders the artifact. No S3 bucket, no CDN config, no expired-link headache.
- Signing. The URL comes pre-signed with a token you can set to expire in 1h, 30d, or never.
- Fonts, print styles, headers, footers. The renderer handles A4/Letter layout, page breaks, and PDF metadata.
- View counts, if you care. Each hosted link tracks opens; you can see them in the dashboard.
-
Multiple formats from the same data. If you want a PDF and a web dashboard of the same report, you just POST twice with different
templatevalues. No duplicated template design.
Works cleanly as an HTTP Request node in n8n, a Webhooks step in Zapier, or an HTTP module in Make — the single POST plus signed-URL response maps to what those tools already expect, so you don't need a custom function to glue anything together.
Pricing
| Tier | Price | Artifacts / month | Max payload | Link expiry |
|---|---|---|---|---|
| Free | $0 | 50 | 1MB | 7 days |
| Starter | $7/mo | 500 | 5MB | 30 days |
| Pro | $19/mo | 3,000 | 10MB | No expiry |
To be upfront: on raw PDF-per-dollar, you can match or beat these numbers on the big template-editor APIs - PDFMonkey's Pro is €15/mo for 3,000 documents, APITemplate.io's PDF Basic is $19/mo (annual) for the same. genui.sh isn't trying to be the cheapest PDF factory; it's trying to be the one call you make when you want a PDF and a web view and a dashboard from the same JSON, without designing a template first.
The Free tier needs no credit card. Starter and Pro are managed through Stripe's hosted customer portal, so you can change plan, update payment, or cancel at any time without talking to anyone.
When this is not the right tool
Being honest: if your use case is a heavily-branded invoice PDF with an exact legal layout your accountant approves annually, you probably want PDFMonkey's template editor. genui.sh is optimized for the case where the data is dynamic and the layout doesn't need to match a pixel-perfect brand document.
If you need PDF/A compliance, digital signatures, or long-term archival metadata, none of the hosted APIs (including this one) will fit - you want a library like iText or Prince with full control.
Everything else - reports, dashboards, invoices that don't need legal compliance, data exports, AI-generated summaries, automation workflow outputs - one POST is probably enough.
Try it
Grab a free key at genui.sh - 50 artifacts/month, no credit card. Ping me at support@genui.sh with feedback, or open an issue if the docs are unclear.
If you want to see what the composable dashboard format looks like, there's a live example on the landing page rendering a real dashboard from a single POST body.




Top comments (0)