Your team exported a CSV from the CRM. Now someone needs to turn it into a formatted report for the weekly meeting. If this sounds familiar, you already know the pain: opening Excel, applying styles, fussing with column widths, saving as PDF — and then doing it all again next week.

There is a better way. The ReportForge API accepts raw CSV data and returns a complete, professionally styled HTML report. One POST request. No spreadsheet software. No manual formatting. This guide shows you exactly how to do it, with working code in JavaScript and Python.

The Core API Endpoint

Everything goes through a single endpoint: POST /api/csv-to-report. You send a JSON body containing your CSV string, the template name, and an optional report title. The API returns a complete HTML document ready to open in a browser and print to PDF.

Request Shape
{
  "csv": "item,amount,quantity\nWidgets,1250.00,50\n...",
  "template": "sales-summary",
  "title": "Q1 Sales Report"
}
Response Shape
{
  "html": "<!DOCTYPE html>...",
  "meta": {
    "rowCount": 5,
    "template": "sales-summary",
    "title": "Q1 Sales Report",
    "generatedAt": "2026-02-28T10:30:00Z"
  }
}

The html field is a self-contained document with all styles inlined. Open it in any browser to view the report, or use a headless browser like Puppeteer to render it as a PDF.

The Four Available Templates

ReportForge ships with four templates, each designed for a specific reporting use case. Choosing the right template determines how your CSV columns are interpreted and what summary statistics are calculated.

Tip: All templates accept extra columns beyond the required ones. The report will include them as additional data columns in the table.

JavaScript Example

Here is a complete Node.js function that reads a CSV file and generates an HTML report. It uses the native fetch API available in Node 18+ and the fs module to read the file.

JavaScript — generate-report.js
import { readFileSync, writeFileSync } from 'fs';

async function generateReport(csvPath, template, title) {
  // Read CSV from disk (or substitute with data from your database)
  const csv = readFileSync(csvPath, 'utf-8');

  const response = await fetch('https://reportforge-api.vercel.app/api/csv-to-report', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      // Add your API key header once you upgrade to a paid plan:
      // 'X-API-Key': process.env.REPORTFORGE_API_KEY,
    },
    body: JSON.stringify({ csv, template, title }),
  });

  if (!response.ok) {
    const err = await response.json();
    throw new Error(`API error ${response.status}: ${err.error}`);
  }

  const { html, meta } = await response.json();
  console.log(`Generated ${template} report: ${meta.rowCount} rows`);
  return html;
}

// Usage
const html = await generateReport(
  './sales-q1.csv',
  'sales-summary',
  'Q1 2026 Sales Report'
);

// Save to disk — open in a browser or send to a headless printer
writeFileSync('./report.html', html);
console.log('Report saved to report.html');

Rendering to PDF with Puppeteer

If you need a PDF instead of HTML, add a Puppeteer step after generating the report. This works with any of the four templates since the output HTML includes print-optimized CSS.

JavaScript — html-to-pdf.js
import puppeteer from 'puppeteer';
import { writeFileSync } from 'fs';

async function htmlToPdf(html, outputPath) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // Load the HTML directly into the page — no file server needed
  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);
}

// Chain with the generate function above
const html = await generateReport('./sales-q1.csv', 'sales-summary', 'Q1 Sales');
await htmlToPdf(html, './report.pdf');
console.log('PDF saved to report.pdf');

Python Example

The Python version uses the requests library, which is available in any standard Python environment. This pattern is common in data pipelines built with Pandas, where you already have the data in memory and just need to format it for presentation.

Python — generate_report.py
import requests
import pandas as pd
import io

def generate_report(df: pd.DataFrame, template: str, title: str) -> str:
    """
    Convert a Pandas DataFrame to a formatted HTML report.
    Returns the HTML string.
    """
    # Serialize DataFrame to CSV string
    csv_buffer = io.StringIO()
    df.to_csv(csv_buffer, index=False)
    csv_string = csv_buffer.getvalue()

    response = requests.post(
        "https://reportforge-api.vercel.app/api/csv-to-report",
        json={
            "csv": csv_string,
            "template": template,
            "title": title,
        },
        # headers={"X-API-Key": os.environ["REPORTFORGE_API_KEY"]},  # paid tier
        timeout=10,
    )
    response.raise_for_status()

    data = response.json()
    print(f"Generated report: {data['meta']['rowCount']} rows, template={template}")
    return data["html"]


# Example: load sales data from a CSV file
df = pd.read_csv("sales_q1.csv")

# Optional: filter or transform before reporting
df = df[df["amount"] > 0]

html = generate_report(df, "sales-summary", "Q1 2026 Sales Report")

# Save to disk
with open("report.html", "w") as f:
    f.write(html)

print("Report saved to report.html")

Handling Errors Gracefully

The API returns structured error responses when something goes wrong. The two most common issues are missing required columns and oversized input (the free tier enforces a 100KB limit on CSV input).

Error Response
{
  "error": "Missing required column: amount",
  "template": "sales-summary",
  "required": ["item", "amount"]
}

If you are working with CSV exported from Excel or Google Sheets, watch out for these common column-naming issues:

Building an Automated Reporting Pipeline

The real power of a CSV-to-HTML report API is in automation. Here is a pattern for scheduling weekly reports using a cron job or a CI/CD pipeline step:

JavaScript — scheduled-report.js
import { fetchSalesData } from './db.js';
import { sendEmail } from './mailer.js';

// Run this with: node scheduled-report.js (or via cron)
async function weeklyReport() {
  // 1. Pull this week's data from your database
  const rows = await fetchSalesData({ period: 'this-week' });

  // 2. Convert to CSV string
  const headers = ['item', 'amount', 'quantity', 'category'];
  const csv = [
    headers.join(','),
    ...rows.map(r => headers.map(h => r[h]).join(',')),
  ].join('\n');

  // 3. Generate the HTML report
  const res = await fetch('https://reportforge-api.vercel.app/api/csv-to-report', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ csv, template: 'sales-summary', title: 'Weekly Sales' }),
  });
  const { html } = await res.json();

  // 4. Email the report to the team
  await sendEmail({
    to: 'team@company.com',
    subject: 'Weekly Sales Report',
    html,
  });

  console.log('Weekly report sent.');
}

weeklyReport();

Free Tier Limits and Upgrading

The free tier allows 5 reports per day with a 100KB input limit — more than enough for development and testing. When you are ready for production, the Starter plan at $9/month provides 100 reports per day with a 2MB input limit. The Business plan at $29/month removes the daily limit and supports 10MB inputs.

No API key is required for the free tier. For paid plans, pass your key in the X-API-Key request header.

Summary

Converting CSV data to professional HTML reports does not need to involve spreadsheet software or hours of manual formatting. The ReportForge /api/csv-to-report endpoint handles layout, styling, and summary statistics in a single API call. Your data comes in as a CSV string, a polished report comes back as HTML.

The workflow is the same whether you are working in JavaScript, Python, or any other language that can make HTTP requests. Export your data, POST it to the API, and get a print-ready report in return.

Start Generating Reports for Free

5 reports/day, no API key required. Try all four templates right now.

Try It Live →