Download a PDF invoice from FastAPI, Flask, or Django

A PDF download endpoint has three jobs: load the record the caller may access, render the document, and return the bytes with the right HTTP headers. Fullbleed's FastAPI, Flask, and Django starter makes that path runnable with a fictional invoice and a local download page.

The updated sample uses the engine's styling features: large display type, a structured item table, fine rules, and a green total panel with orange accents. All three apps share the same editable HTML/CSS and generate the same one-page Northstar Studio invoice for Maple & Finch, totaling USD 1,870.00.

Download the Python starter ZIP · Open the actual PDF · Read the complete framework guide

The ZIP contains the three small apps, shared renderer, HTML/CSS templates, local download page, fonts and licenses, and output checker. Its source record identifies the exact files and download hashes. Fullbleed is MIT licensed, including commercial use.

Run one framework locally

Use Python 3.10–3.14. Extract the ZIP, open its fullbleed-python-invoice directory, and create an environment with python -m venv .venv. Activate it with source .venv/bin/activate on macOS/Linux or .venv\Scripts\Activate.ps1 in Windows PowerShell.

Choose one app. The requirement files install the web framework separately from Fullbleed:

  • FastAPI: install with python -m pip install fullbleed==2.5.6 -r requirements-fastapi.txt. Start with python -m uvicorn fastapi_app:app --host 127.0.0.1 --port 8000.

  • Flask: install with python -m pip install fullbleed==2.5.6 -r requirements-flask.txt. Start with python -m flask --app flask_app run --host 127.0.0.1 --port 8000.

  • Django: install with python -m pip install fullbleed==2.5.6 -r requirements-django.txt. Start with python django_app.py runserver 127.0.0.1:8000 --noreload.

Open http://127.0.0.1:8000/ and select Download invoice. The browser downloads invoice.pdf. The PDF endpoint is /invoices/INV-1042.pdf; an unknown ID returns HTTP 404. Stop the server before trying another framework on the same port.

Make the template your own

Edit templates/invoice.html for structure and templates/invoice.css for typography, columns, spacing, and color. The renderer reads both files on each request, so your next download uses the edits without restarting the server.

The HTML uses Python string.Template placeholders such as $customer, $number, $rows, and $total. Write a literal dollar sign as $$. Customer and item text is escaped before substitution; placeholder-like customer text remains literal.

Inter ships with Fullbleed. The starter also includes DM Serif Display and Bebas Neue, their licenses, and pinned source hashes. The included design is a fixed one-page A4 sample for three items. Adapt its geometry and pagination before using longer records, then inspect the final PDF.

Keep document code separate from HTTP code

The shared invoice.py has a fictional record lookup and a render_invoice(data) function. It calculates amounts with Decimal, escapes text, registers the fonts, and creates a new PdfEngine for each render.

The framework adapter passes the resulting bytes to Response in FastAPI or Flask, or HttpResponse in Django. There is no shared intermediate output file. The framework guide includes all three routes and the view and URL pattern to adapt for an existing Django project.

Make the response describe the download

The examples send Content-Type: application/pdf, Content-Disposition: attachment; filename="invoice.pdf", and Cache-Control: private, no-store. These identify the PDF body, supply the download filename, and tell caches not to store the response. PDF bytes do not need JSON or base64 encoding.

FastAPI uses a small PDFResponse class to advertise application/pdf in OpenAPI. Its route is a normal def: rendering is synchronous, and FastAPI runs synchronous route functions in its thread pool. See the FastAPI concurrency documentation.

Check the document as well as the status code

From the extracted directory, install python -m pip install -r requirements-check.txt, then run python check_examples.py --out output/check.

The checker exercises all three framework clients and starts their actual local servers. It checks PDF text with an independent reader, totals, page count, embedded fonts, download headers, 404/405 responses, and matching simultaneous downloads. It retains the PDFs, preview, server logs, and verification.json, then stops the servers.

The local page shows a saved preview of the sample; its download link renders the current template. After changing the design, refresh that preview with python check_examples.py --out output/check --update-preview and review it. Source CI checks the examples with a built wheel on Linux and Windows. The documentation pipeline also extracts and replays the downloadable ZIP with the published 2.5.6 wheel.

These are synthetic integration and output checks. They do not establish production capacity or PDF standards conformance.

Connect your application data

Replace load_invoice() with your application's authorized lookup, keeping access checks before rendering. The sample expects string dates, integer quantities, decimal unit-price strings, and optional item details. Tax, discounts, invoice numbering, payment collection, and authentication belong in your application.

The launch commands run development servers on your computer. Use your framework's deployment setup for a public app; for large jobs, render through a job queue and serve the completed file. Start with the framework guide, or try a PDF without setting up a server in the browser playground.

Updated October 4, 2026. This walkthrough was prepared with AI assistance. Its sample outputs and HTTP behavior were checked locally and in CI.

Previous
Previous

Rebuild your PDF when you save CSS

Next
Next

Fullbleed 2.4: six PDFs you can inspect and reproduce