Generate PDFs in Python with Pyppeteer: Full Guide

This tutorial walks through generating a certificate PDF using Pyppeteer and Mako templates in Python. We'll set up the project, design an HTML certificate template, populate it with data, and convert it to PDF using Pyppeteer's headless browser.
What is Pyppeteer?
Pyppeteer is a Python port of Puppeteer that provides a high-level API to control Chromium in headless mode. It can render dynamic HTML, run JavaScript, take screenshots, and convert HTML to PDF. If you're already familiar with Puppeteer in Node.js, Pyppeteer works the same way but with a Pythonic API.
Differences Between Puppeteer and Pyppeteer

While this guide focuses on using Pyppeteer to generate certificates in Python, it's helpful to understand how it differs from its JavaScript counterpart, Puppeteer. For the Node.js version, see our Puppeteer PDF generation guide. Below are some key distinctions.
Language and API Design
- Puppeteer is built for Node.js and uses JavaScript’s asynchronous model with Promises.
- Pyppeteer adapts this functionality for Python, offering a more Pythonic API that accepts both dictionaries and keyword arguments, making it more intuitive for Python developers.
Options Passing
- Puppeteer: Uses JavaScript objects (e.g.,
launch({ headless: true })). - Pyppeteer: Supports both dictionaries and keyword arguments (e.g.,
launch(headless=True)).
Element Selection
- Puppeteer: Employs concise methods like
$(),$$(), and$x(). - Pyppeteer: Uses more descriptive methods (
querySelector(),querySelectorAll(), andxpath()) along with shorthands (J(),JJ(), andJx()).
JavaScript Evaluation
- Both libraries use
evaluate(), but Pyppeteer may needforce_expr=Trueto correctly interpret expressions.
In practice, if you know Puppeteer, picking up Pyppeteer is straightforward.
Step-by-Step Guide: Creating a Certificate PDF with Pyppeteer
We'll use Mako for templating and Pyppeteer for PDF rendering.
Step 1: Set Up Your Environment
Prerequisites: Make sure you have Python and pip installed before starting.
| Requirement | Recommendation and Download Links |
|---|---|
| Python | Python 3.6 or higher installed. If not, download from Python.org. |
| Package Manager | Use pip (included with Python) or pipenv for dependency management. Learn more in the pip documentation. |
| IDE | Use PyCharm – my personal recommendation – or VS Code. |
- Then create a new project directory and navigate into it:
mkdir certificate-generation
cd certificate-generation
- Install the necessary packages.
Use pip to install Pyppeteer (for generating PDFs) and Mako (for templating):
pip install pyppeteer mako
Step 2: Set Up Your Project Directory Structure
Organize your project files like this:
Recommended layout:
certificate-generation/
├── data/ # Directory for your JSON data files
│ └── certificate_data.json # File containing certificate details
├── templates/ # Directory for your HTML templates
│ └── certificate_template.html # Mako template for the certificate design
└── generate_certificate.py # Python script for generating the PDF
Step 3: Create Your Certificate Template with Mako
- Design your certificate using HTML and CSS, and use Mako templating syntax to dynamically insert data.
- Save the template as
certificate_template.htmlin yourtemplatesdirectory. - This template should include placeholders that will be dynamically populated with your certificate data.
View Certificate Template (HTML)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Certificate</title>
<style>
body, html {
margin: 0;
padding: 0;
}
body {
font-family: Georgia, serif;
color: #000;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 100%;
aspect-ratio: 1050 / 742;
border: 20px solid #c9aa81;
padding: 30px;
box-sizing: border-box;
display: flex;
flex-direction: column;
background-image: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
}
.header {
color: #b59467;
margin: 50px 0 30px;
text-align: center;
}
.title {
font-size: 78px;
letter-spacing: 4px;
margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.subtitle {
font-size: 30px;
letter-spacing: 2px;
margin-top: 5px;
}
.person-description {
text-align: center;
font-size: 24px;
margin: 10px 0;
}
.person {
font-size: 62px;
font-style: italic;
margin: 20px auto;
text-align: center;
color: #333;
}
.course {
text-align: center;
margin: 20px 0;
}
.course-description {
font-size: 24px;
margin-bottom: 20px;
}
.course-name {
font-size: 30px;
color: #333;
}
.footer-details {
display: flex;
justify-content: space-between;
margin-top: auto;
padding-top: 15px;
}
.footer-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
padding: 10px;
}
.footer-item .label {
font-size: 18px;
text-align: center;
margin-top: 5px;
}
.value {
font-size: 24px;
font-family: 'Satisfy', cursive;
font-style: italic;
}
.divider {
width: 60%;
border: 0;
border-top: 1px solid #7c5f38;
margin: 10px auto;
}
@media (max-width: 600px) {
.title {
font-size: 36px;
}
.person {
font-size: 24px;
}
.person-description,
.course-description {
font-size: 18px;
}
.course-name {
font-size: 28px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1 class="title">Certificate</h1>
<h2 class="subtitle">of Completion</h2>
</div>
<div class="person-description">This certificate is proudly presented to</div>
<div class="person">${recipient_name}</div>
<div class="course">
<div class="course-description">
For successfully completing the course:
</div>
<div class="course-name">${course_name}</div>
</div>
<div class="footer-details">
<div class="footer-item">
<div class="value">${date}</div>
<hr class="divider">
<div class="label">Date</div>
</div>
<div class="footer-item">
<div class="value">${instructor}</div>
<hr class="divider">
<div class="label">Instructor</div>
</div>
</div>
</div>
</body>
</html>
Step 4: Prepare Your Certificate Data
Next, create a JSON file named certificate_data.json in your data directory with the dynamic values for your certificate.
View Certificate Data (JSON)
{
"recipient_name": "Jane Doe",
"course_name": "Digital Marketing Strategies",
"date": "2025-03-09",
"instructor": "John Smith"
}
Step 5: Write the PDF Generation Script
Now, create a Python script called generate_certificate.py in the root of your project. This script will load the certificate data, render the HTML using the Mako template, and generate a PDF using Pyppeteer.
View Complete Script
import os
import json
import asyncio
from mako.template import Template
import datetime
import random
# Set the desired Chromium revision for pyppeteer
PYPPETEER_CHROMIUM_REVISION = '1263111'
os.environ['PYPPETEER_CHROMIUM_REVISION'] = PYPPETEER_CHROMIUM_REVISION
from pyppeteer import launch
async def generate_pdf(html_content, output_path):
# Launch a headless Chromium browser
browser = await launch()
page = await browser.newPage()
# Set the HTML content for the page and wait for all resources to load
await page.setContent(html_content)
# Generate the PDF in A4 landscape mode with background graphics printed
await page.pdf({
'path': output_path,
'format': 'A4',
'printBackground': True,
'landscape': True
})
# Close the browser
await browser.close()
def render_template(template_path, data):
# Load the Mako template from file
with open(template_path, 'r', encoding='utf-8') as file:
template_content = file.read()
template = Template(template_content)
# Render the template with provided data
return template.render(**data)
def load_data(json_path):
# Load data from a JSON file
with open(json_path, 'r', encoding='utf-8') as file:
data = json.load(file)
return data
async def main():
# Define file paths
json_path = os.path.join('data', 'certificate_data.json')
template_path = os.path.join('templates', 'certificate_template.html')
# Create a unique output file name using a timestamp and a random number
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
output_pdf = f'certificate_{timestamp}.pdf'
# Load certificate data and render the HTML template
data = load_data(json_path)
rendered_html = render_template(template_path, data)
# Generate the PDF
await generate_pdf(rendered_html, output_pdf)
print(f'PDF generated: {output_pdf}')
if __name__ == '__main__':
asyncio.run(main())
Step 6: Run the Script
With everything in place, navigate to your certificate-generation directory and run your script from the terminal:
python generate_certificate.py
Step 7: Verify Your Certificate PDF
After running the script, open the generated certificate_<timestamp>.pdf file and check that the layout and data look correct.
Certificate PDF Preview:

Bonus: Generating a Web Page PDF
In addition to creating PDFs from HTML, you can also generate a PDF directly from an existing web page. This is useful if you need to archive a webpage or create a snapshot of a live site.
Below is a quick example of how to do it using Pyppeteer.
View Code for Generating PDF from URL
import asyncio
import os
# Set the desired Chromium revision for pyppeteer
PYPPETEER_CHROMIUM_REVISION = '1263111'
os.environ['PYPPETEER_CHROMIUM_REVISION'] = PYPPETEER_CHROMIUM_REVISION
from pyppeteer import launch
async def generate_pdf_from_webpage(target_url, output_file):
# Initialize a headless Chromium browser instance
browser = await launch()
page = await browser.newPage()
# Navigate to the target URL and wait until network activity calms down
await page.goto(target_url, {'waitUntil': 'networkidle2'})
# Create a PDF file in A4 format, ensuring background graphics are included
await page.pdf({
'path': output_file,
'format': 'A4',
'printBackground': True
})
# Close the browser to free up resources
await browser.close()
# Example usage: Generate a PDF snapshot of a live webpage
asyncio.run(generate_pdf_from_webpage('https://example.com', 'webpage.pdf'))
Simplified Approach: Using PDF Templates
Pyppeteer gives you full control, but you also need to manage Chromium, handle browser lifecycles, and deal with memory on your server. If you'd rather skip that, PDFBolt Templates let you design the layout once and generate PDFs via API with just JSON data – no browser dependencies on your end.
Certificate Generation via Templates
Below is the process for generating our certificate through PDFBolt's template system.
Step 1: Create Your Template
- Sign up or log into your account.
- Navigate to Templates → Create Template.
- Choose Create from Scratch or select a certificate from the gallery and customize it to your needs.
- Browse professionally designed templates in the Template Gallery.
- Follow the complete Template Management Guide for detailed creation and management instructions.
- Design your certificate using HTML/CSS with Handlebars syntax:
Certificate Template with Handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Certificate</title>
<style>
body, html {
margin: 0;
padding: 0;
}
body {
font-family: Georgia, serif;
color: #000;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 100%;
aspect-ratio: 1050 / 742;
border: 20px solid #c9aa81;
padding: 30px;
box-sizing: border-box;
display: flex;
flex-direction: column;
background-image: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
}
.header {
color: #b59467;
margin: 50px 0 30px;
text-align: center;
}
.title {
font-size: 78px;
letter-spacing: 4px;
margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.subtitle {
font-size: 30px;
letter-spacing: 2px;
margin-top: 5px;
}
.person-description {
text-align: center;
font-size: 24px;
margin: 10px 0;
}
.person {
font-size: 62px;
font-style: italic;
margin: 20px auto;
text-align: center;
color: #333;
}
.course {
text-align: center;
margin: 20px 0;
}
.course-description {
font-size: 24px;
margin-bottom: 20px;
}
.course-name {
font-size: 30px;
color: #333;
}
.footer-details {
display: flex;
justify-content: space-between;
margin-top: auto;
padding-top: 15px;
}
.footer-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
padding: 10px;
}
.footer-item .label {
font-size: 18px;
text-align: center;
margin-top: 5px;
}
.value {
font-size: 24px;
font-family: 'Satisfy', cursive;
font-style: italic;
}
.divider {
width: 60%;
border: 0;
border-top: 1px solid #7c5f38;
margin: 10px auto;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1 class="title">Certificate</h1>
<h2 class="subtitle">of Completion</h2>
</div>
<div class="person-description">This certificate is proudly presented to</div>
<div class="person">{{recipient_name}}</div>
<div class="course">
<div class="course-description">
For successfully completing the course:
</div>
<div class="course-name">{{course_name}}</div>
</div>
<div class="footer-details">
<div class="footer-item">
<div class="value">{{date}}</div>
<hr class="divider">
<div class="label">Date</div>
</div>
<div class="footer-item">
<div class="value">{{instructor}}</div>
<hr class="divider">
<div class="label">Instructor</div>
</div>
</div>
</div>
</body>
</html>
Step 2: Test and Publish
- Add sample data in the DATA tab to preview your template:
{
"recipient_name": "Jane Doe",
"course_name": "Digital Marketing Strategies",
"date": "2025-03-09",
"instructor": "John Smith"
}
- Use Real PDF Preview to generate an actual PDF and verify the output.
- Click Publish to make your template available via API.
- Copy your template ID for integration or access ready-to-use code snippets for multiple programming languages through the Get API Code button.
Step 3: Generate Certificates from Python
Here's the equivalent Python code using PDFBolt's API – notice how much simpler it becomes:
import requests
import json
url = "https://api.pdfbolt.com/v1/direct"
headers = {
"API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"Content-Type": "application/json"
}
data_json = '''{
"templateId": "your-certificate-template-id",
"templateData": {
"recipient_name":"Jane Doe",
"course_name":"Digital Marketing Strategies",
"date":"2025-03-09",
"instructor":"John Smith"
}
}'''
data = json.loads(data_json)
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
with open('pdfbolt_certificate_example.pdf', 'wb') as f:
f.write(response.content)
print("PDF generated successfully")
except requests.exceptions.HTTPError as e:
print(f"HTTP {response.status_code}")
print(f"Error Message: {response.text}")
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
You can configure PDF options like format, landscape, printBackground, and other parameters in two ways:
- In the Template Designer: Set options in the OPTIONS tab when creating your template.
- In API Requests: Include parameters directly in your API request payload:
{
"templateId": "your-certificate-template-id",
"templateData": { ... },
"format": "A4",
"landscape": true,
"printBackground": true
}
That's it. To generate a certificate for a different recipient, just change the JSON data – the template stays the same.
Beyond templates, PDFBolt also supports:
- URL to PDF – convert any web page directly to PDF.
- HTML to PDF – send raw HTML and get a PDF back.
See the Python quick start guide for code examples.
Conclusion
Pyppeteer with Mako templates gives you full control over PDF generation in Python – you own the rendering pipeline and can customize everything. The tradeoff is managing Chromium and its memory footprint on your server.
PDFBolt Templates remove that overhead: design your layout in the browser, send JSON data via API, get a PDF back. Less code, no browser dependencies.
Pick Pyppeteer when you need fine-grained control over the rendering. Pick templates when you'd rather focus on your application logic.
Looking for a different library? See our guides on WeasyPrint and PyPDF2, PDFKit, and xhtml2pdf, or browse the full Python HTML to PDF libraries comparison.
