Automate Report Generation: From Data to PDF in Minutes

C
Christian Mesa
Feb 26, 2026
5 min read

Why Automate Report Generation?

Every business generates reports: sales summaries, analytics dashboards, client deliverables, compliance documents. Doing this manually means hours of copy-pasting data into templates, adjusting formatting, and exporting PDFs.

With the ToolCenter HTML-to-PDF API, you can turn any HTML template into a polished PDF in a single API call. Feed it data, get a report.

The Architecture

The workflow is straightforward:

  1. Fetch your data — Database query, API call, or spreadsheet
  2. Render HTML template — Inject data into an HTML template
  3. Convert to PDF — Send the HTML to ToolCenter
  4. Deliver — Email, upload to S3, or serve to users

Step 1: Design Your Report Template

Create a professional HTML report template:

<!DOCTYPE html>
<html>
<head>
  <style>
    @page { size: A4; margin: 40px; }
    body { font-family: 'Helvetica Neue', sans-serif; color: #333; line-height: 1.6; }
    .header { display: flex; justify-content: space-between; align-items: center;
      border-bottom: 3px solid #667eea; padding-bottom: 20px; margin-bottom: 30px; }
    .logo { font-size: 24px; font-weight: bold; color: #667eea; }
    .meta { text-align: right; font-size: 14px; color: #666; }
    h1 { font-size: 28px; color: #1a1a1a; margin-bottom: 10px; }
    .summary-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin: 30px 0; }
    .card { background: #f8f9fa; border-radius: 8px; padding: 20px; text-align: center; }
    .card-value { font-size: 32px; font-weight: bold; color: #667eea; }
    .card-label { font-size: 14px; color: #666; margin-top: 5px; }
    table { width: 100%; border-collapse: collapse; margin: 20px 0; }
    th { background: #667eea; color: white; padding: 12px; text-align: left; }
    td { padding: 10px 12px; border-bottom: 1px solid #eee; }
    tr:nth-child(even) { background: #f8f9fa; }
    .footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd;
      font-size: 12px; color: #999; text-align: center; }
  </style>
</head>
<body>
  <div class="header">
    <div class="logo">Acme Analytics</div>
    <div class="meta">
      <div>Monthly Sales Report</div>
      <div>{{reportDate}}</div>
    </div>
  </div>

  <h1>{{reportTitle}}</h1>

  <div class="summary-cards">
    <div class="card">
      <div class="card-value">{{totalRevenue}}</div>
      <div class="card-label">Total Revenue</div>
    </div>
    <div class="card">
      <div class="card-value">{{totalOrders}}</div>
      <div class="card-label">Orders</div>
    </div>
    <div class="card">
      <div class="card-value">{{avgOrderValue}}</div>
      <div class="card-label">Avg Order Value</div>
    </div>
  </div>

  <h2>Top Products</h2>
  <table>
    <tr><th>Product</th><th>Units Sold</th><th>Revenue</th></tr>
    {{#products}}
    <tr><td>{{name}}</td><td>{{units}}</td><td>{{revenue}}</td></tr>
    {{/products}}
  </table>

  <div class="footer">
    Generated automatically on {{generatedAt}} | Confidential
  </div>
</body>
</html>

Step 2: Populate the Template with Data

Use a template engine to inject your data:

const Handlebars = require('handlebars');
const fs = require('fs');

function renderReport(data) {
  const templateSrc = fs.readFileSync('./templates/report.html', 'utf-8');
  const template = Handlebars.compile(templateSrc);

  return template({
    reportTitle: `Sales Report — ${data.month} ${data.year}`,
    reportDate: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),
    totalRevenue: `$${data.totalRevenue.toLocaleString()}`,
    totalOrders: data.totalOrders.toLocaleString(),
    avgOrderValue: `$${(data.totalRevenue / data.totalOrders).toFixed(2)}`,
    products: data.topProducts,
    generatedAt: new Date().toISOString(),
  });
}

Step 3: Convert to PDF with ToolCenter

const axios = require('axios');

async function generatePDF(html) {
  const response = await axios.post(
    'https://api.toolcenter.dev/v1/pdf',
    {
      html: html,
      format: 'A4',
      margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
      printBackground: true,
      preferCSSPageSize: true,
    },
    {
      headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
      responseType: 'arraybuffer',
    }
  );

  return Buffer.from(response.data);
}

Step 4: Put It All Together

async function createMonthlyReport(month, year) {
  // 1. Fetch data from your database
  const data = await fetchSalesData(month, year);

  // 2. Render HTML
  const html = renderReport(data);

  // 3. Convert to PDF
  const pdf = await generatePDF(html);

  // 4. Save or send
  const filename = `sales-report-${year}-${month}.pdf`;
  fs.writeFileSync(`./reports/${filename}`, pdf);

  // Optional: send via email
  await sendEmail({
    to: '[email protected]',
    subject: `Sales Report — ${month}/${year}`,
    attachments: [{ filename, content: pdf }],
  });

  return filename;
}

Python Implementation

import requests
from jinja2 import Template
from datetime import datetime

def generate_report(data):
    # Load and render template
    with open('templates/report.html') as f:
        template = Template(f.read())

    html = template.render(
        report_title=f"Sales Report — {data['month']}",
        report_date=datetime.now().strftime('%B %d, %Y'),
        total_revenue=f"${data['total_revenue']:,.2f}",
        total_orders=f"{data['total_orders']:,}",
        products=data['top_products'],
    )

    # Convert to PDF
    response = requests.post(
        'https://api.toolcenter.dev/v1/pdf',
        json={
            'html': html,
            'format': 'A4',
            'margin': {'top': '20mm', 'bottom': '20mm', 'left': '15mm', 'right': '15mm'},
            'printBackground': True,
        },
        headers={'Authorization': 'Bearer YOUR_API_KEY'},
    )

    return response.content

Scheduling Reports

With Cron (Node.js)

const cron = require('node-cron');

// Generate monthly report on the 1st at 9 AM
cron.schedule('0 9 1 * *', async () => {
  const now = new Date();
  const lastMonth = now.getMonth(); // 0-indexed, so this is last month
  const year = now.getFullYear();
  await createMonthlyReport(lastMonth, year);
  console.log('Monthly report generated and sent!');
});

With AWS Lambda

exports.handler = async (event) => {
  const { month, year } = event;
  const filename = await createMonthlyReport(month, year);
  return { statusCode: 200, body: `Report generated: ${filename}` };
};

Advanced Features

Adding Charts

Include chart images in your report using a charting library:

const { ChartJSNodeCanvas } = require('chartjs-node-canvas');

async function renderChart(data) {
  const canvas = new ChartJSNodeCanvas({ width: 600, height: 300 });
  const image = await canvas.renderToDataURL({
    type: 'bar',
    data: {
      labels: data.map(d => d.month),
      datasets: [{ label: 'Revenue', data: data.map(d => d.revenue) }],
    },
  });
  return image; // data:image/png;base64,...
}

Embed it in your HTML template:

<img src="{{chartImage}}" style="width:100%;max-width:600px;" />

Multi-Page Reports

For long reports, use CSS page breaks:

.page-break { page-break-after: always; }

Headers and Footers

const response = await axios.post('https://api.toolcenter.dev/v1/pdf', {
  html: html,
  format: 'A4',
  headerTemplate: '<div style="font-size:10px;text-align:center;width:100%;">Acme Corp — Confidential</div>',
  footerTemplate: '<div style="font-size:10px;text-align:center;width:100%;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
  displayHeaderFooter: true,
});

Conclusion

Automated report generation eliminates hours of manual work. Design your templates once in HTML/CSS, populate them with live data, and let the ToolCenter PDF API handle the conversion. Whether it's daily dashboards, weekly summaries, or monthly analytics, the pipeline stays the same: data in, PDF out.

Share this article

CM

Christian Mesa

Founder & Developer at ToolCenter

Full-stack developer from the Canary Islands, Spain. Building developer tools and APIs that simplify web development. Passionate about clean code, performance, and making complex things simple.

Try ToolCenter APIs Free

100 API calls/month free. No credit card required.

Related Posts