Most application data lives as JSON. Your database returns it, your APIs serve it, and your frontend consumes it. But when that data needs to become a report — something a manager prints, a client reviews, or an auditor files — JSON alone is not enough. You need formatted tables, calculated totals, and a layout that looks professional on paper.
This guide covers the full workflow for converting JSON data into polished reports. We will compare PDF and HTML as output formats, walk through the ReportForge API pipeline, and build working examples using real sales data. By the end, you will have a pattern you can drop into any application that needs to produce reports from structured data.
The JSON-to-Report Problem
JSON is great for machines. It is terrible for humans. Consider a quarterly sales dataset with 50 line items. As raw JSON, that is a wall of curly braces and quoted strings. What your finance team actually needs is a table with aligned columns, currency formatting, subtotals by category, and a grand total at the bottom. Bridging the gap between raw data and a presentable document is where report generation APIs come in.
The traditional approach involves writing custom rendering code: building an HTML template with a templating engine, populating it with data, styling it with CSS, and then printing it to PDF. That works, but it means maintaining template code, CSS, and a headless browser setup. For most teams, that is undifferentiated effort that a dedicated API handles better.
PDF vs HTML: Which Output Format?
Before writing code, decide what your end users actually need. Both formats have clear strengths:
| Criteria | HTML | |
|---|---|---|
| Viewing | Opens in any browser, responsive | Fixed layout, same on every device |
| Printing | Good with print CSS | Pixel-perfect, no surprises |
| File size | Smaller (text only) | Larger (embedded fonts, layout) |
| Searchability | Full text search in browser | Searchable but less flexible |
| Embedding | Inline in emails, dashboards | Attachment-based |
| Generation speed | Instant (API returns HTML) | Requires headless browser step |
The ReportForge API returns HTML by design. HTML reports are faster to generate, easier to embed in emails and dashboards, and already include print-optimized CSS for when users need a physical copy. If you need a PDF file specifically, you add a single Puppeteer step to render the HTML to PDF. This two-step approach gives you both formats from one API call.
The ReportForge Workflow: JSON to Template to Report
The conversion follows three steps. First, you serialize your JSON data as a CSV string (the API's input format). Second, you choose one of the four templates. Third, you POST the data and receive a finished HTML report. Here is the flow:
- Prepare your data — Start with a JSON array of objects. Each object becomes a row; each key becomes a column header.
- Convert to CSV — Serialize the array to a CSV string. This is a straightforward transformation that any language handles in a few lines.
- Call the API — POST the CSV string, template name, and title to
/api/csv-to-report. The API returns complete HTML with inlined styles.
Practical Example: Sales Data Report
Suppose your application has an endpoint that returns quarterly sales data as JSON. Here is what the data looks like and how to turn it into a formatted report.
const salesData = [ { item: "Enterprise License", amount: 24500.00, quantity: 7, region: "North America" }, { item: "Professional License", amount: 12800.00, quantity: 32, region: "Europe" }, { item: "Starter Plan", amount: 4500.00, quantity: 90, region: "Asia Pacific" }, { item: "Add-on: Analytics", amount: 6200.00, quantity: 45, region: "North America" }, { item: "Add-on: Support", amount: 3100.00, quantity: 28, region: "Europe" }, { item: "Training Package", amount: 8900.00, quantity: 12, region: "Global" }, ];
Step 1: Convert JSON to CSV
function jsonToCsv(data) { const headers = Object.keys(data[0]); const rows = data.map(row => headers.map(h => { const val = String(row[h]); // Wrap in quotes if the value contains commas return val.includes(',') ? `"${val}"` : val; }).join(',') ); return [headers.join(','), ...rows].join('\n'); } const csv = jsonToCsv(salesData); // item,amount,quantity,region // Enterprise License,24500,7,North America // ...
Step 2: Generate the HTML Report
async function generateReport(csv, template, title) { const response = await fetch( 'https://reportforge-api.vercel.app/api/csv-to-report', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ csv, template, title }), } ); if (!response.ok) { const err = await response.json(); throw new Error(`ReportForge error: ${err.error}`); } return response.json(); } const { html, meta } = await generateReport( csv, 'sales-summary', 'Q1 2026 Sales Report' ); console.log(`Report ready: ${meta.rowCount} rows, template=${meta.template}`);
Step 3: Convert to PDF (Optional)
If your stakeholders need a PDF file, add a Puppeteer rendering step. The HTML from ReportForge already includes print-optimized CSS, so the PDF output matches the browser view closely.
import puppeteer from 'puppeteer'; import { writeFileSync } from 'fs'; async function htmlToPdf(html, outputPath) { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setContent(html, { waitUntil: 'networkidle0' }); const pdf = await page.pdf({ format: 'A4', printBackground: true, margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' }, }); await browser.close(); writeFileSync(outputPath, pdf); console.log(`PDF saved to ${outputPath}`); } // Generate HTML, then convert to PDF const { html } = await generateReport(csv, 'sales-summary', 'Q1 Sales'); await htmlToPdf(html, './q1-sales-report.pdf');
Performance note: Puppeteer adds 2-5 seconds per PDF conversion because it launches a headless browser. For high-volume PDF generation, consider keeping a browser instance alive across requests instead of launching a new one each time.
Python Example with Sales Data
The same workflow applies in Python. If your data lives in a Pandas DataFrame (common in data engineering and analytics), the conversion to CSV is a single method call.
import requests import pandas as pd import json # Start with JSON data (e.g., from an API response) sales_json = [ {"item": "Enterprise License", "amount": 24500, "quantity": 7}, {"item": "Professional License", "amount": 12800, "quantity": 32}, {"item": "Starter Plan", "amount": 4500, "quantity": 90}, ] # Convert JSON to DataFrame, then to CSV string df = pd.DataFrame(sales_json) csv_string = df.to_csv(index=False) # Generate the report response = requests.post( "https://reportforge-api.vercel.app/api/csv-to-report", json={ "csv": csv_string, "template": "sales-summary", "title": "Q1 2026 Sales Report", }, timeout=10, ) response.raise_for_status() result = response.json() print(f"Report: {result['meta']['rowCount']} rows") # Save the HTML report with open("sales-report.html", "w") as f: f.write(result["html"])
Choosing the Right Template
The template you select determines how ReportForge interprets your columns and what summary statistics it calculates. Each template expects specific column names:
- sales-summary — Expects
itemandamount. Calculates grand total, average, and count. Best for revenue and sales data. - expense-report — Expects
descriptionandamount. Groups by category with subtotals. Best for cost tracking and budget reports. - inventory-status — Expects
itemandquantity. Highlights low-stock items. Best for warehouse and stock management. - invoice — Expects
descriptionandamount. Adds subtotal, tax calculation, and payment terms. Best for client-facing billing documents.
All templates accept additional columns beyond the required ones. Extra columns appear as additional data fields in the generated table, which means you do not need to strip your data down before sending it.
Error Handling for Production
When integrating report generation into a production application, handle the common failure cases explicitly. The API returns descriptive error messages for each scenario:
- 400 Bad Request — Missing required fields (
csv,template) or missing required columns for the chosen template. - 413 Payload Too Large — CSV input exceeds the size limit for your plan (100KB on free, 2MB on Starter, 10MB on Business).
- 429 Too Many Requests — Daily report limit exceeded. Free tier allows 5 per day.
- 500 Internal Server Error — Unexpected failure. Retry once, then report the issue.
Summary
Converting JSON data to polished reports follows a clean three-step pattern: serialize to CSV, pick a template, and call the API. The ReportForge API handles layout, styling, and summary calculations so you can focus on your application's data layer. HTML output is immediate and embeddable; PDF output is one Puppeteer step away.
Start with the live playground to see how your data looks in each template, then integrate the API call into your application. The free tier gives you 5 reports per day to build and test your pipeline before upgrading.
Try the JSON-to-Report Pipeline
Paste your data, choose a template, and see the result instantly. No sign-up required.
Try It Live →