Generating PDFs from web pages or HTML templates is a common task — invoices, reports, receipts, documentation snapshots. This tutorial shows you how to do it cleanly in Python using the URLSnap API, no wkhtmltopdf binary, no WeasyPrint C dependencies, no Selenium setup.
pip install requests
That's it. No system-level dependencies needed.
import requests
resp = requests.post(
'https://urlsnap.dev/api/register',
json={'email': 'you@example.com'}
)
print(resp.json())
# {'key': 'us_abc123...', 'message': 'Free tier: 20 requests/day...'}
import requests
API_KEY = 'us_your_key_here'
def url_to_pdf(url: str, output_path: str, **kwargs):
"""
Download a URL as a PDF file.
kwargs: format (A4/Letter/Legal), landscape (bool), margin (css string)
"""
params = {'url': url, **kwargs}
response = requests.get(
'https://urlsnap.dev/api/pdf',
params=params,
headers={'x-api-key': API_KEY},
)
response.raise_for_status()
with open(output_path, 'wb') as f:
f.write(response.content)
print(f'Saved PDF to {output_path}')
# Basic usage
url_to_pdf('https://example.com', 'page.pdf')
# Letter-size landscape
url_to_pdf(
'https://en.wikipedia.org/wiki/Python_(programming_language)',
'python-wiki.pdf',
format='Letter',
landscape='true',
margin='15px',
)
Got a Jinja2 template or a dynamic HTML string? Post it directly:
import requests
from jinja2 import Template
API_KEY = 'us_your_key_here'
def html_to_pdf(html: str, output_path: str):
response = requests.post(
'https://urlsnap.dev/api/pdf',
json={'html': html, 'format': 'A4'},
headers={'x-api-key': API_KEY},
)
response.raise_for_status()
with open(output_path, 'wb') as f:
f.write(response.content)
# Example: render an invoice template
INVOICE_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: sans-serif; padding: 40px; color: #111; }
h1 { color: #6366f1; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 10px; border-bottom: 1px solid #eee; text-align: left; }
.total { font-weight: bold; font-size: 1.1em; }
</style>
</head>
<body>
<h1>Invoice #{{ invoice_id }}</h1>
<p>Bill to: {{ customer }}</p>
<table>
<tr><th>Item</th><th>Amount</th></tr>
{% for item in items %}
<tr><td>{{ item.name }}</td><td>${{ item.amount }}</td></tr>
{% endfor %}
<tr class="total"><td>Total</td><td>${{ total }}</td></tr>
</table>
</body>
</html>
"""
invoice_html = Template(INVOICE_TEMPLATE).render(
invoice_id='INV-2026-042',
customer='Acme Corp',
items=[
{'name': 'API Starter Plan', 'amount': '9.00'},
{'name': 'Setup fee', 'amount': '0.00'},
],
total='9.00',
)
html_to_pdf(invoice_html, 'invoice.pdf')
For high-throughput use in async Python applications (FastAPI, etc.):
import httpx
import asyncio
API_KEY = 'us_your_key_here'
async def url_to_pdf_async(url: str) -> bytes:
async with httpx.AsyncClient() as client:
resp = await client.get(
'https://urlsnap.dev/api/pdf',
params={'url': url, 'format': 'A4'},
headers={'x-api-key': API_KEY},
timeout=60.0,
)
resp.raise_for_status()
return resp.content
async def main():
urls = [
'https://example.com',
'https://httpbin.org/html',
]
pdfs = await asyncio.gather(*[url_to_pdf_async(u) for u in urls])
for i, pdf in enumerate(pdfs):
with open(f'output_{i}.pdf', 'wb') as f:
f.write(pdf)
print(f'Saved {len(pdf)//1024}KB PDF')
asyncio.run(main())
import requests
API_KEY = 'us_your_key_here'
info = requests.get(
'https://urlsnap.dev/api/me',
headers={'x-api-key': API_KEY}
).json()
print(f"Plan: {info['plan']}")
print(f"Used today: {info['requests_today']}/{info['daily_limit']}")
print(f"Total all time: {info['requests_total']}")
import requests
def safe_url_to_pdf(url: str, output_path: str, api_key: str):
try:
resp = requests.get(
'https://urlsnap.dev/api/pdf',
params={'url': url},
headers={'x-api-key': api_key},
timeout=60,
)
if resp.status_code == 429:
print("Daily limit reached. Upgrade at urlsnap.dev/pricing")
return False
if resp.status_code == 401:
print("Invalid API key.")
return False
resp.raise_for_status()
with open(output_path, 'wb') as f:
f.write(resp.content)
return True
except requests.exceptions.Timeout:
print(f"Timeout generating PDF for {url}")
return False
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return False
Free tier: 20 PDFs/day. No credit card, no install, no config.
Get your free API key →