Add a PDF download to an existing Flask view, or generate a PDF from a template. The examples use PDFCrowd's HTML to PDF API and work for invoices, reports, receipts, and other documents built from application data.
Set up the client
Install the Python client:
pip install pdfcrowd
You can run the examples below with the included demo credentials, or replace
them with your PDFCrowd username and API key.
Add a PDF download to an existing view
An existing view can display its usual HTML page and return a PDF when the user clicks a download button. Reuse the view's template, data preparation, and access checks.
Choose what to send to PDFCrowd:
- Rendered HTML uses content Flask has already prepared.
- The page URL loads the page through its normal address.
Convert rendered HTML
Here, app is your existing Flask application. The example extends a document view;
get_document_for_current_user(document_id) represents your application's data lookup and
permission checks. Substitute your own logic here. The template at
templates/documents/detail.html receives the result as document.
import pdfcrowd from flask import Response, current_app, render_template, request @app.route("/documents/<int:document_id>", methods=["GET", "POST"]) def document_detail(document_id): document = get_document_for_current_user(document_id) if request.method == "POST" and "download_pdf" in request.form: html = render_template( "documents/detail.html", document=document, pdf_base_url=current_app.config.get("PDF_BASE_URL"), ) try: client = pdfcrowd.HtmlToPdfClient("demo", "demo") # Username, API key client.setContentViewportWidth("balanced") pdf = client.convertString(html) # bytes except pdfcrowd.Error: # Add your application's error handling here. raise response = Response(pdf, mimetype="application/pdf") response.headers.set( "Content-Disposition", "attachment", filename=f"document-{document_id}.pdf", ) return response return render_template("documents/detail.html", document=document)
Add this form to the template, outside any existing form:
<form method="post" class="pdfcrowd-remove"> <button type="submit" name="download_pdf" value="1">Download PDF</button> </form>
Include your application's CSRF field and validation; they are omitted from this fragment. Flask leaves CSRF protection to the application or its form extension.
GET requests display the HTML page. A POST containing download_pdf renders the same
template, converts it, and returns a PDF download. The pdfcrowd-remove class hides the
form in the PDF while leaving it visible on the webpage.
Conversion finishes before the response is created, with the PDF held in memory as bytes.
The except block provides a place for your application's error handling. As written,
raise propagates the original exception. See
SDK error handling for details.
To display the PDF in the browser, change attachment to inline. Set
page size,
margins, or other
PDF options before convertString().
Make CSS and images available
PDFCrowd needs reachable URLs for stylesheets, images, and fonts referenced by the rendered
HTML. Absolute asset URLs, such as https://your-site.example/static/document.css, need no
additional base URL.
For relative paths, set the public site or directory URL in your application configuration:
app.config["PDF_BASE_URL"] = "https://your-site.example/"
Replace https://your-site.example/ with your website's public URL. Then add this to the
template's <head>, before stylesheet links:
{% if pdf_base_url %} <base href="{{ pdf_base_url }}"> {% endif %}
The PDF branch passes this setting as pdf_base_url. The <base> element tells PDFCrowd
where to resolve relative URLs. For example, /static/document.css becomes
https://your-site.example/static/document.css. This applies to assets and hyperlinks;
avoid adding a second <base> if the template already supplies one.
PDFCrowd cannot reach assets on your development machine through localhost or 127.0.0.1.
For local assets, you can instead package the HTML and assets in a ZIP archive.
Keep paths relative to the archive's folders and omit the website <base> for that approach.
Convert the page URL instead
Instead of sending rendered HTML, you can ask PDFCrowd to open the page through its URL.
Keep the lookup, POST check, client setup, error handling, and response code above. Replace
the render_template() call inside the PDF branch and its convertString() call with:
pdf = client.convertUrl(request.url) # bytes
This preserves the current path and query parameters. PDFCrowd makes a separate GET request, so the view returns HTML; only the original POST triggers conversion. The server must be able to handle that GET while the POST waits for the PDF.
The URL must be reachable from PDFCrowd; localhost cannot reach your development machine.
Ensure request.url uses your public address, with the appropriate
trusted-host and
proxy settings.
The separate request does not inherit the user's Flask session. For protected pages, configure cookies or HTTP authentication as appropriate, or use rendered HTML from the original request.
Generate a PDF from a template
You can also generate a PDF directly from a template and application data. Use an existing Jinja template or a separate template designed for the document.
This function renders the supplied template and returns the PDF as bytes. It uses the same asset configuration as the view example:
import pdfcrowd from flask import current_app, render_template def render_pdf(template_name, context): pdf_context = { **context, "pdf_base_url": current_app.config.get("PDF_BASE_URL"), } html = render_template(template_name, **pdf_context) client = pdfcrowd.HtmlToPdfClient("demo", "demo") # Username, API key client.setContentViewportWidth("balanced") return client.convertString(html)
For example, given a document object, call it from a Flask view:
pdf = render_pdf("documents/detail.html", {"document": document}) # bytes
Pass all data the template needs in context. Use the resulting bytes for a download,
storage, or email. Conversion errors propagate to the caller.
Outside a request, call the function inside with app.app_context():, using your existing
Flask app. This supplies the required application context;
use a template that does not depend on request or session data.
For more conversion inputs and PDF settings, see the Python examples and API reference.