Automate PDF reports from your data
PDF report automation: query your data, build HTML with SVG or Chart.js charts, add page numbers, render to PDF on a schedule and email it.
Weekly KPI summaries, monthly client reports and end-of-day sales digests are usually the same layout with new numbers. Instead of copying charts into a document every week, build the report as HTML from your data. A scheduled job then turns it into a PDF.
Quick answer. Build the report as HTML, then POST it to https://html-pdf.com/v1/pdf from a scheduled job and save or email the result.
curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" \
-d '{"html":"<h1>Weekly sales report</h1><p>Revenue: $4,210</p>"}' -o weekly-report.pdf
The pipeline
These are the five steps every automated report goes through.
- Query your database, warehouse or analytics API.
- Build HTML: tables for numbers, SVG or Chart.js for charts.
- Render with
POST https://html-pdf.com/v1/pdf. - Deliver: email the PDF, upload it to storage, or post the link to a chat channel.
- Schedule the whole thing with cron, GitHub Actions or a workflow tool such as n8n.
The free report template gives you a starting layout with a title block, KPI cards and a table.
Build the HTML from query results (Python)
This script turns a SQL query into a report with an SVG bar chart, a table and page numbers.
import os, html, sqlite3, requests
db = sqlite3.connect("shop.db")
rows = db.execute("""
SELECT date(created_at) AS day, COUNT(*) AS orders, SUM(total) AS revenue
FROM orders WHERE created_at >= date('now', '-7 day')
GROUP BY day ORDER BY day
""").fetchall()
max_rev = max((r[2] for r in rows), default=0) or 1
bars = "".join(
f'<rect x="{i*60+10}" y="{150 - r[2]/max_rev*140:.0f}" width="40" height="{r[2]/max_rev*140:.0f}" fill="#2563eb"/>'
f'<text x="{i*60+30}" y="168" font-size="10" text-anchor="middle">{html.escape(r[0][5:])}</text>'
for i, r in enumerate(rows))
table = "".join(f"<tr><td>{html.escape(d)}</td><td>{o}</td><td>${rev:,.2f}</td></tr>" for d, o, rev in rows)
page = f"""<!doctype html><html><head><meta charset="utf-8"><style>
body {{ font-family: system-ui, sans-serif; color: #111; }}
table {{ width: 100%; border-collapse: collapse; }} td, th {{ padding: 6px; border-bottom: 1px solid #eee; text-align: left; }}
thead {{ display: table-header-group; }} tr, figure {{ break-inside: avoid; }}
</style></head><body>
<h1>Weekly sales report</h1>
<figure><svg width="440" height="175">{bars}</svg></figure>
<table><thead><tr><th>Day</th><th>Orders</th><th>Revenue</th></tr></thead><tbody>{table}</tbody></table>
</body></html>"""
r = requests.post("https://html-pdf.com/v1/pdf",
headers={"Authorization": f"Bearer {os.environ['HTML_PDF_KEY']}"},
json={
"html": page,
"format": "A4",
"margin": {"top": "15mm", "right": "12mm", "bottom": "18mm", "left": "12mm"},
"footer_html": '<div style="font-size:9px;width:100%;text-align:center;color:#888">'
'Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
"max_pages": 20,
}, timeout=60)
r.raise_for_status()
open("weekly-report.pdf", "wb").write(r.content)
The doubled braces in the CSS are Python f-string escaping. Server-side SVG is the most predictable option for charts. There is no JavaScript to wait for, and the output is sharp at any zoom.
Charts with Chart.js
This section shows how to use Chart.js if your team already relies on it. Load it from a CDN, turn animations off, and tell the renderer when the chart is ready with wait_for_expression.
<canvas id="c" width="700" height="300"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart(document.getElementById("c"), {
type: "line",
data: { labels: ["Mon","Tue","Wed","Thu","Fri"], datasets: [{ label: "Revenue", data: [420, 510, 380, 640, 700] }] },
options: { animation: false, responsive: false }
});
window.chartReady = true;
</script>
Send it with "wait_for_expression": "window.chartReady === true". More detail in Chart.js to PDF.
Page numbers and page breaks
This section keeps multi-page reports readable. footer_html uses Chrome's placeholders (pageNumber, totalPages) and needs a bottom margin to have room.
CSS @page margin boxes such as @bottom-center { content: counter(page) } also work in Chromium. Keep tables readable with a repeating thead and break-inside: avoid on rows and charts.
See headers, footers and page numbers and page breaks.
Schedule it
These examples run the report script automatically every Monday at 07:00.
cron on any server:
0 7 * * 1 cd /opt/reports && HTML_PDF_KEY=... python3 weekly_report.py
GitHub Actions:
name: weekly-report
on:
schedule:
- cron: "0 7 * * 1"
jobs:
report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install requests
- run: python weekly_report.py
env:
HTML_PDF_KEY: ${{ secrets.HTML_PDF_KEY }}
- uses: actions/upload-artifact@v4
with: { name: weekly-report, path: weekly-report.pdf }
Scheduled workflows run in UTC.
n8n uses a Schedule Trigger, a database node, a Code node that builds the HTML, then an HTTP Request node calling the API. See the n8n guide.
Email the PDF
This section covers getting the report to people. Attach the file with your mail provider's SDK or SMTP.
If you'd rather send a link, request "response": "url". The API returns a download URL that stays valid for 24 hours, which suits a "this week's report" email. For anything that must live longer, upload the binary to your own storage.
Automate reports with html-pdf.com
This is what you get with the hosted API. html-pdf.com is a Chromium-based HTML and URL to PDF API with page-based pricing. You get 100 free credits, then from $0.005 per page, and purchased credits never expire.
A 4-page weekly report costs 4 credits. Requests can be up to 20 MB and render for up to 30 seconds, and your HTML is not stored.
With the MCP server, an AI agent can produce the report straight from data you paste or query.
"Take this CSV of last month's sales by region, build a two-page PDF report with a bar chart per region, a summary table and page numbers in the footer, and give me the link."
FAQ
How do I automate PDF reports from a database?
Query your data, build an HTML page with tables and SVG or Chart.js charts, and POST it to https://html-pdf.com/v1/pdf. Run the script on a schedule with cron, GitHub Actions or n8n.
How do I include Chart.js charts in a PDF report?
Turn animations off and set a flag such as window.chartReady = true once the chart is drawn. Then send "wait_for_expression": "window.chartReady === true" so the renderer waits for it.
How much does an automated PDF report cost?
1 credit = 1 PDF page, so a 4-page weekly report costs 4 credits. You get 100 free credits once, then packs from $10 for 1,000 pages to $250 for 50,000 pages.
Can I email a link instead of attaching the PDF?
Yes. Request "response": "url" and the API returns a download link that lasts 24 hours. For longer storage, upload the PDF to your own storage.
