Python Examples

Example code for using PDF API with Python.

Python Examples

Use PDF API with Python using the requests library.


Installation

pip install requests

Basic Example

import requests
import os

def generate_pdf(markdown, theme='default', options=None):
    """Generate a PDF from markdown content."""
    response = requests.post(
        'https://api.pdfapi.dev/v1/pdf/markdown',
        headers={
            'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
            'Content-Type': 'application/json',
        },
        json={
            'markdown': markdown,
            'theme': theme,
            'options': options or {}
        }
    )

    if response.status_code != 200:
        error = response.json()
        raise Exception(error['error']['message'])

    return response.content

# Usage
pdf = generate_pdf('# Hello World\n\nThis is my first PDF!')

with open('hello.pdf', 'wb') as f:
    f.write(pdf)

print('PDF saved to hello.pdf')

With Options

import requests
import os

markdown = """# Monthly Report

## Executive Summary

- Revenue: $45,678
- Users: 1,234
- Growth: 15%

## Details

| Metric | Q1 | Q2 | Q3 |
|--------|----|----|----|
| Users | 1000 | 1100 | 1234 |
| Revenue | $30K | $38K | $46K |

## Conclusion

Strong growth trajectory continues.
"""

response = requests.post(
    'https://api.pdfapi.dev/v1/pdf/markdown',
    headers={
        'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
        'Content-Type': 'application/json',
    },
    json={
        'markdown': markdown,
        'theme': 'corporate',
        'options': {
            'format': 'A4',
            'margin': {
                'top': '25mm',
                'bottom': '25mm',
                'left': '20mm',
                'right': '20mm'
            },
            'header': '<div style="text-align: center; font-size: 10px;">Monthly Report - Q3 2025</div>',
            'footer': '<div style="text-align: center; font-size: 10px;">Page <span class="pageNumber"></span></div>'
        }
    }
)

with open('report.pdf', 'wb') as f:
    f.write(response.content)

HTML to PDF

import requests
import os

html = """
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; padding: 40px; }
    h1 { color: #2563eb; }
    table { width: 100%; border-collapse: collapse; margin: 20px 0; }
    th, td { border: 1px solid #e5e7eb; padding: 12px; text-align: left; }
    th { background: #f9fafb; }
  </style>
</head>
<body>
  <h1>Invoice #1234</h1>
  <table>
    <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
    <tr><td>Widget Pro</td><td>2</td><td>$59.98</td></tr>
    <tr><td>Gadget Plus</td><td>1</td><td>$49.99</td></tr>
  </table>
  <p><strong>Total: $109.97</strong></p>
</body>
</html>
"""

response = requests.post(
    'https://api.pdfapi.dev/v1/pdf/html',
    headers={
        'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
        'Content-Type': 'application/json',
    },
    json={
        'html': html,
        'options': {
            'format': 'A4',
            'print_background': True
        }
    }
)

with open('invoice.pdf', 'wb') as f:
    f.write(response.content)

URL to PDF

import requests
import os

response = requests.post(
    'https://api.pdfapi.dev/v1/pdf/url',
    headers={
        'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
        'Content-Type': 'application/json',
    },
    json={
        'url': 'https://example.com',
        'options': {
            'format': 'A4',
            'wait_for': 2000,
            'full_page': True,
            'viewport': {
                'width': 1920,
                'height': 1080
            }
        }
    }
)

with open('webpage.pdf', 'wb') as f:
    f.write(response.content)

Template to PDF

import requests
import os
from datetime import datetime

response = requests.post(
    'https://api.pdfapi.dev/v1/pdf/template',
    headers={
        'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
        'Content-Type': 'application/json',
    },
    json={
        'template': 'invoice',
        'data': {
            'invoice_number': f'INV-{datetime.now().strftime("%Y%m%d")}-001',
            'date': datetime.now().strftime('%Y-%m-%d'),
            'due_date': '2025-02-18',
            'customer': {
                'name': 'Acme Corporation',
                'email': 'billing@acme.com',
                'address': '123 Business St, Suite 100\nNew York, NY 10001'
            },
            'items': [
                {'description': 'Widget Pro', 'quantity': 2, 'unit_price': 29.99},
                {'description': 'Gadget Plus', 'quantity': 1, 'unit_price': 49.99},
                {'description': 'Service Fee', 'quantity': 1, 'unit_price': 9.99}
            ],
            'subtotal': 119.96,
            'tax': 10.80,
            'total': 130.76
        }
    }
)

with open('invoice.pdf', 'wb') as f:
    f.write(response.content)

PDFClient Class

import requests
import os
from typing import Optional, Dict, Any, List

class PDFClient:
    """Python client for PDF API."""

    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.environ.get('PDFAPI_KEY')
        self.base_url = 'https://api.pdfapi.dev/v1'

        if not self.api_key:
            raise ValueError('API key is required')

    def _request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
        """Make an API request."""
        headers = {
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json',
        }

        response = requests.request(
            method,
            f'{self.base_url}{endpoint}',
            headers=headers,
            **kwargs
        )

        if response.status_code == 429:
            raise RateLimitError(response.json()['error']['message'])
        elif response.status_code >= 400:
            raise APIError(response.json()['error']['message'])

        return response

    def markdown_to_pdf(
        self,
        markdown: str,
        theme: str = 'default',
        options: Optional[Dict[str, Any]] = None
    ) -> bytes:
        """Convert markdown to PDF."""
        response = self._request(
            'POST',
            '/pdf/markdown',
            json={'markdown': markdown, 'theme': theme, 'options': options or {}}
        )
        return response.content

    def html_to_pdf(
        self,
        html: str,
        options: Optional[Dict[str, Any]] = None
    ) -> bytes:
        """Convert HTML to PDF."""
        response = self._request(
            'POST',
            '/pdf/html',
            json={'html': html, 'options': options or {}}
        )
        return response.content

    def url_to_pdf(
        self,
        url: str,
        options: Optional[Dict[str, Any]] = None
    ) -> bytes:
        """Convert URL to PDF."""
        response = self._request(
            'POST',
            '/pdf/url',
            json={'url': url, 'options': options or {}}
        )
        return response.content

    def get_usage(self) -> Dict[str, Any]:
        """Get current usage statistics."""
        response = self._request('GET', '/usage')
        return response.json()['data']

    def list_themes(self) -> List[Dict[str, Any]]:
        """List available themes."""
        response = self._request('GET', '/themes')
        return response.json()['data']


class APIError(Exception):
    """API error exception."""
    pass


class RateLimitError(APIError):
    """Rate limit exceeded exception."""
    pass


# Usage
client = PDFClient()

# Generate PDF
pdf = client.markdown_to_pdf('# Hello World', theme='corporate')
with open('hello.pdf', 'wb') as f:
    f.write(pdf)

# Check usage
usage = client.get_usage()
print(f"Used {usage['pdfs_generated']} of {usage['pdfs_limit']} PDFs")

Django Integration

# views.py
from django.http import HttpResponse
from django.views import View
import requests
import os

class GeneratePDFView(View):
    def post(self, request):
        markdown = request.POST.get('markdown', '')
        theme = request.POST.get('theme', 'default')

        response = requests.post(
            'https://api.pdfapi.dev/v1/pdf/markdown',
            headers={
                'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
                'Content-Type': 'application/json',
            },
            json={'markdown': markdown, 'theme': theme}
        )

        if response.status_code != 200:
            return HttpResponse(status=response.status_code)

        http_response = HttpResponse(
            response.content,
            content_type='application/pdf'
        )
        http_response['Content-Disposition'] = 'attachment; filename="document.pdf"'
        return http_response

Flask Integration

from flask import Flask, request, send_file
import requests
import os
from io import BytesIO

app = Flask(__name__)

@app.route('/generate-pdf', methods=['POST'])
def generate_pdf():
    data = request.json

    response = requests.post(
        'https://api.pdfapi.dev/v1/pdf/markdown',
        headers={
            'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
            'Content-Type': 'application/json',
        },
        json={
            'markdown': data.get('markdown', ''),
            'theme': data.get('theme', 'default')
        }
    )

    if response.status_code != 200:
        return response.json(), response.status_code

    return send_file(
        BytesIO(response.content),
        mimetype='application/pdf',
        as_attachment=True,
        download_name='document.pdf'
    )

if __name__ == '__main__':
    app.run(debug=True)

Batch Processing

import requests
import os
import time
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed

def generate_single_pdf(markdown_file: Path, output_dir: Path) -> dict:
    """Generate a PDF from a single markdown file."""
    try:
        markdown = markdown_file.read_text()

        response = requests.post(
            'https://api.pdfapi.dev/v1/pdf/markdown',
            headers={
                'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
                'Content-Type': 'application/json',
            },
            json={'markdown': markdown, 'theme': 'default'}
        )

        if response.status_code == 429:
            # Rate limited, wait and retry
            time.sleep(60)
            return generate_single_pdf(markdown_file, output_dir)

        if response.status_code != 200:
            return {'file': str(markdown_file), 'success': False, 'error': response.json()['error']['message']}

        output_file = output_dir / f'{markdown_file.stem}.pdf'
        output_file.write_bytes(response.content)

        return {'file': str(markdown_file), 'success': True, 'output': str(output_file)}

    except Exception as e:
        return {'file': str(markdown_file), 'success': False, 'error': str(e)}


def batch_convert(input_dir: str, output_dir: str, max_workers: int = 3):
    """Convert all markdown files in a directory to PDFs."""
    input_path = Path(input_dir)
    output_path = Path(output_dir)
    output_path.mkdir(exist_ok=True)

    markdown_files = list(input_path.glob('*.md'))
    results = []

    # Use thread pool with limited workers to respect rate limits
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(generate_single_pdf, f, output_path): f
            for f in markdown_files
        }

        for future in as_completed(futures):
            result = future.result()
            results.append(result)

            if result['success']:
                print(f"✓ {result['file']} -> {result['output']}")
            else:
                print(f"✗ {result['file']}: {result['error']}")

            # Small delay between completions
            time.sleep(0.5)

    return results


# Usage
results = batch_convert('documents/', 'output/')
successful = sum(1 for r in results if r['success'])
print(f"\nCompleted: {successful}/{len(results)} files converted")