Build an Automated Invoice Generator using HTML & Python
If you are building an e-commerce platform, a SaaS app, or just managing your own freelance work, you eventually hit the "Invoice Problem." You have data in a database, but your customers need a clean, professional PDF invoice.
Trying to build PDFs from scratch using low-level Python libraries like ReportLab is painful. It involves calculating x/y coordinates for every line of text.
A much smarter approach is to design your invoice using standard HTML/CSS, and then use an API to convert that HTML into a PDF.
In this tutorial, we will build a Python script that takes raw invoice data, merges it into an HTML template, and generates a professional PDF using the free aPDF.io API.
The Workflow
- Data Source: A simple Python dictionary (mimicking a database).
- Template Engine: We use Jinja2 to inject data into HTML.
- PDF Engine: We send the rendered HTML to aPDF.io.
- Result: We get a download link for the PDF.
Step 1: The Setup
- Go to aPDF.io.
- Sign up (it's free).
- Copy your API Token from the dashboard.
Next, install the requests and jinja2 library if you haven't already:
pip install requests jinja2
Step 2: Create the HTML Invoice Template
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: 'Helvetica', sans-serif; color: #333; }
.box { max-width: 800px; margin: auto; padding: 30px; border: 1px solid #eee; }
.header { display: flex; justify-content: space-between; margin-bottom: 20px; }
.title { font-size: 24px; font-weight: bold; color: #2c3e50; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
.total { text-align: right; margin-top: 20px; font-size: 18px; font-weight: bold; }
</style>
</head>
<body>
<div class="box">
<div class="header">
<div>
<div class="title">INVOICE</div>
<p>Invoice #: {{ invoice_id }}<br>Date: {{ date }}</p>
</div>
<div style="text-align: right;">
<strong>{{ company_name }}</strong><br>
{{ company_email }}
</div>
</div>
<p><strong>Bill To:</strong><br>
{{ client_name }}<br>
{{ client_address }}</p>
<table>
<thead>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.qty }}</td>
<td>USD {{ item.price }}</td>
<td>USD {{ item.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="total">
Grand Total: USD {{ grand_total }}
</div>
</div>
</body>
</html>
Step 3: The Python Generator Script
import requests
from jinja2 import Template
# 1. Your API Token
API_TOKEN = "YOUR_API_TOKEN_HERE"
API_URL = "https://apdf.io/api/pdf/file/create"
# 2. Mock Data (In a real app, this comes from your DB)
invoice_data = {
"invoice_id": "INV-2025-001",
"date": "November 21, 2025",
"company_name": "Tech Solutions Inc.",
"company_email": "billing@techsolutions.com",
"client_name": "John Doe Enterprises",
"client_address": "123 Market St, San Francisco, CA",
"items": [
{"name": "Web Development Service", "qty": 1, "price": 500.00, "total": 500.00},
{"name": "Server Maintenance", "qty": 2, "price": 100.00, "total": 200.00},
{"name": "SSL Certificate", "qty": 1, "price": 50.00, "total": 50.00},
],
"grand_total": 750.00
}
# 3. Load and Render the HTML Template
with open("invoice_template.html", "r") as file:
template_content = file.read()
jinja_template = Template(template_content)
rendered_html = jinja_template.render(invoice_data)
# 4. Send to API
print("Generating PDF...")
try:
response = requests.post(
API_URL,
headers={
'Authorization': f'Bearer {API_TOKEN}'
},
data={
'html': rendered_html,
# Optional Layout Parameters
'format': 'a4',
'margin_top': '20',
'margin_bottom': '20',
'margin_left': '20',
'margin_right': '20'
}
)
# 5. Handle the Response
if response.status_code == 200:
result = response.json()
print("Success!")
print(f"Download your invoice here: {result['file']}")
# Optional: Print file expiration or size
print(f"Pages: {result['pages']}")
print(f"Size: {result['size']} bytes")
else:
print(f"Error {response.status_code}: {response.text}")
except Exception as e:
print(f"An error occurred: {e}")
Run the script
python generate_invoice.py
Output
Generating PDF...
Success!
Download your invoice here: https://apdf-files.s3.eu-central-1.amazonaws.com/6f1674e2703953aa.pdf
Pages: 1
Size: 12405 bytes
Next Steps
- Secure the Invoice: Use the Password Protection endpoint to lock the PDF with the client's ID.
- Store Efficiently: If your invoices have high-resolution logos, use the Compress endpoint to shrink the file size before archiving.