html2pdf-sdk 1.0.0
Official Python SDK for html2pdf service - convert HTML to PDF with ease
License: MIT
Requires Python: >=3.9
Keywords: api-client, async, chromium, html-to-pdf, pdf, pdf-generation, sdk

# html2pdf-sdk

Official Python SDK for the html2pdf service - convert HTML to PDF with ease.

[![PyPI version](https://img.shields.io/pypi/v/html2pdf-sdk.svg)](https://pypi.org/project/html2pdf-sdk/)
[![Python](https://img.shields.io/pypi/pyversions/html2pdf-sdk.svg)](https://pypi.org/project/html2pdf-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- 🚀 **Simple & Intuitive API** - Easy to use, powerful capabilities
- 🐍 **Python 3.9+** - Full type hints and modern Python features
- ⚡ **Async/Await Support** - Both sync and async clients available
- 🔄 **Sync & Async Rendering** - Choose based on your needs
- 📦 **Batch Processing** - Render multiple PDFs efficiently
- 🎨 **Template Support** - Use pre-built templates (invoices, reports, etc.)
- 🔐 **Webhook Integration** - Get notified when jobs complete
- ✅ **Compliance** - PDF/A, PDF/UA, WCAG, PDF/X validation
- 🔄 **Automatic Retries** - Built-in retry logic for resilience
- 📊 **Progress Tracking** - Monitor long-running jobs
- 🎯 **Type Safe** - Comprehensive type hints with Pydantic models

## Installation

```bash
pip install html2pdf-sdk
```

```bash
poetry add html2pdf-sdk
```

```bash
uv add html2pdf-sdk
```

## Quick Start

### Synchronous Usage

```python
from html2pdf import Html2PdfClient
from html2pdf.types import RenderRequest, RenderOptions, Margins

# Initialize the client
client = Html2PdfClient(
    base_url="https://api.rendly.cloud",
    api_key="your-api-key"
)

# Render HTML to PDF (synchronous)
result = client.render.create(RenderRequest(
    html="<h1>Hello World</h1>",
    options=RenderOptions(
        page_size="A4",
        margins=Margins(
            top="1in",
            right="1in",
            bottom="1in",
            left="1in"
        )
    )
))

# Save the PDF
with open("output.pdf", "wb") as f:
    f.write(result.pdf)

print(f"Generated {result.metadata.pages} pages in {result.metadata.duration_ms}ms")
```

### Asynchronous Usage

```python
from html2pdf import AsyncHtml2PdfClient
from html2pdf.types import RenderRequest, RenderOptions
import asyncio

async def main():
    async with AsyncHtml2PdfClient(
        base_url="https://api.rendly.cloud",
        api_key="your-api-key"
    ) as client:
        result = await client.render.acreate(RenderRequest(
            html="<h1>Hello World</h1>",
            options=RenderOptions(page_size="A4")
        ))

        with open("output.pdf", "wb") as f:
            f.write(result.pdf)

        print(f"Generated {result.metadata.pages} pages")

asyncio.run(main())
```

## Authentication

The SDK supports two authentication methods:

### API Key Authentication

```python
client = Html2PdfClient(
    base_url="https://api.rendly.cloud",
    api_key="your-api-key"
)
```

### JWT Token Authentication

```python
client = Html2PdfClient(
    base_url="https://api.rendly.cloud",
    access_token="your-jwt-token"
)
```

## Core Features

### Synchronous Rendering

For quick rendering (max 5MB HTML, 30s timeout):

```python
result = client.render.create(RenderRequest(
    html="<h1>Invoice</h1><p>Total: $100</p>",
    options=RenderOptions(
        page_size="A4",
        print_background=True,
        margins=Margins(top="1in", right="1in", bottom="1in", left="1in")
    )
))

with open("invoice.pdf", "wb") as f:
    f.write(result.pdf)
```

### Asynchronous Rendering

For large documents or complex pages:

```python
# Submit the job
job = client.render.create_async(RenderRequest(
    url="https://example.com/large-report",
    options=RenderOptions(
        page_size="Letter",
        display_header_footer=True,
        header_template='<div style="font-size: 10px;">Report Header</div>',
        footer_template='<div style="font-size: 10px;">Page <span class="pageNumber"></span></div>'
    )
))

print(f"Job created: {job.job_id}")

# Poll for status
import time
while True:
    status = client.render.get_status(job.job_id)
    if status.status == "succeeded":
        pdf = client.render.download(job.job_id)
        with open("output.pdf", "wb") as f:
            f.write(pdf)
        break
    elif status.status == "failed":
        print(f"Render failed: {status.error.message}")
        break
    time.sleep(1)
```

### Render and Wait (Simplified Async)

```python
result = client.render.render_and_wait(
    RenderRequest(
        url="https://example.com",
        options=RenderOptions(page_size="A4")
    ),
    poll_interval=1.0,
    max_wait_time=300.0,
    on_progress=lambda status: print(f"Status: {status.status}")
)

with open("output.pdf", "wb") as f:
    f.write(result.pdf)
```

## Batch Processing

Process multiple PDFs in parallel:

```python
from html2pdf.types import BatchRequest, BatchItem

# Create a batch
batch = client.batch.create(BatchRequest(
    items=[
        BatchItem(
            mode="html",
            html="<h1>Document 1</h1>",
            item_id="doc1",
            options=RenderOptions(page_size="A4")
        ),
        BatchItem(
            mode="url",
            url="https://example.com/page1",
            item_id="doc2",
            options=RenderOptions(page_size="Letter")
        ),
        BatchItem(
            mode="html",
            html="<h1>Document 3</h1>",
            item_id="doc3"
        )
    ]
))

print(f"Batch created: {batch.batch_id}")

# Wait for completion
result = client.batch.wait_for_completion(
    batch.batch_id,
    on_progress=lambda status: print(f"Progress: {status.completed}/{status.total}")
)

# Download all PDFs as a ZIP
if result.status == "completed":
    zip_data = client.batch.download_zip(batch.batch_id)
    with open("batch-results.zip", "wb") as f:
        f.write(zip_data)
```

### Batch from CSV

```python
csv_data = """
mode,html,url,itemId
html,"<h1>Doc 1</h1>",,doc1
url,,https://example.com,doc2
html,"<h1>Doc 3</h1>",,doc3
"""

batch = client.batch.create_from_csv(csv_data)
```

### Bulk Status Check

```python
from html2pdf.types import BulkStatusRequest

statuses = client.batch.bulk_status(BulkStatusRequest(
    job_ids=["job_1", "job_2", "job_3"]
))

for job_id, status in statuses.jobs.items():
    print(f"{job_id}: {status.status}")
```

## Template Rendering

Use pre-built templates for common documents:

```python
from html2pdf.types import TemplateRenderRequest

result = client.templates.render(TemplateRenderRequest(
    template="invoice",
    data={
        "invoiceNumber": "INV-001",
        "date": "2024-01-15",
        "customerName": "John Doe",
        "items": [
            {
                "description": "Consulting Services",
                "quantity": 10,
                "price": 150,
                "total": 1500
            },
            {
                "description": "Software License",
                "quantity": 1,
                "price": 500,
                "total": 500
            }
        ],
        "subtotal": 2000,
        "tax": 200,
        "total": 2200
    },
    options=RenderOptions(
        page_size="A4",
        print_background=True
    )
))

with open("invoice.pdf", "wb") as f:
    f.write(result.pdf)
```

### Validate Template Data

```python
from html2pdf.types import TemplateValidateRequest

validation = client.templates.validate(TemplateValidateRequest(
    template="invoice",
    data={"invoiceNumber": "INV-001"}
))

if not validation.valid:
    print("Validation errors:", validation.errors)
```

### Preview Template

```python
html = client.templates.preview(
    template="certificate",
    data={
        "name": "John Doe",
        "achievement": "Course Completion",
        "date": "2024-01-15"
    }
)

print(html)
```

## Webhooks

Get notified when rendering jobs complete:

```python
from html2pdf.types import WebhookConfig

# Register a webhook
client.webhooks.create(WebhookConfig(
    url="https://myapp.com/webhooks/html2pdf",
    secret="your-secret-key-min-16-chars",
    events=["job.completed", "job.failed", "batch.completed"],
    enabled=True
))

# Get webhook config
webhook = client.webhooks.get()
print(f"Webhook: {webhook.url}")

# Update webhook
client.webhooks.update(WebhookConfig(
    url="https://myapp.com/webhooks/html2pdf",
    secret="your-secret-key",
    events=["job.completed", "batch.completed"],
    enabled=False
))

# Delete webhook
client.webhooks.delete()
```

### Verify Webhook Signatures

In your webhook endpoint (Flask example):

```python
from flask import Flask, request
from html2pdf.resources.webhooks import WebhooksResource

app = Flask(__name__)

@app.route("/webhooks/html2pdf", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("x-webhook-signature")
    body = request.get_data(as_text=True)
    secret = "your-secret-key"

    is_valid = WebhooksResource.verify_signature_local(body, signature, secret)

    if not is_valid:
        return {"error": "Invalid signature"}, 401

    # Process webhook event
    event = request.get_json()
    print(f"Event: {event['type']}, Job: {event['jobId']}")

    return {"status": "ok"}
```

FastAPI example:

```python
from fastapi import FastAPI, Request, HTTPException
from html2pdf.resources.webhooks import WebhooksResource

app = FastAPI()

@app.post("/webhooks/html2pdf")
async def handle_webhook(request: Request):
    signature = request.headers.get("x-webhook-signature")
    body = await request.body()
    secret = "your-secret-key"

    is_valid = WebhooksResource.verify_signature_local(
        body.decode(), signature, secret
    )

    if not is_valid:
        raise HTTPException(status_code=401, detail="Invalid signature")

    event = await request.json()
    print(f"Event: {event['type']}, Job: {event['jobId']}")

    return {"status": "ok"}
```

## Compliance & Validation

Validate PDFs against compliance standards:

```python
from html2pdf.types import ComplianceCertificateRequest

with open("document.pdf", "rb") as f:
    pdf_data = f.read()

# Generate compliance certificate
certificate = client.compliance.generate_certificate(
    ComplianceCertificateRequest(
        pdf=pdf_data,
        standards=["pdfa", "pdfua", "wcag"],
        format="json"
    )
)

print(f"Certificate ID: {certificate.certificate_id}")

for result in certificate.results:
    print(f"{result.standard}: {'PASS' if result.compliant else 'FAIL'}")
    print(f"Score: {result.score}/100")

    if not result.compliant:
        print("Issues:")
        for issue in result.issues:
            print(f"  [{issue.severity}] {issue.message}")
            if issue.recommendation:
                print(f"    → {issue.recommendation}")
```

### Specific Validations

```python
# PDF/A validation
pdfa_result = client.compliance.validate_pdfa(pdf_data)

# PDF/UA (accessibility) validation
pdfua_result = client.compliance.validate_pdfua(pdf_data)

# WCAG compliance
wcag_result = client.compliance.validate_wcag(pdf_data)

# PDF/X (print production)
pdfx_result = client.compliance.validate_pdfx(pdf_data)
```

## Advanced Options

### Page Size Options

```python
# Preset sizes
result = client.render.create(RenderRequest(
    html="<h1>Test</h1>",
    options=RenderOptions(
        page_size="A4"  # 'A4', 'Letter', 'Legal', 'A3', 'A5', 'Tabloid'
    )
))

# Custom dimensions
from html2pdf.types import CustomPageSize

result = client.render.create(RenderRequest(
    html="<h1>Test</h1>",
    options=RenderOptions(
        page_size=CustomPageSize(width="8.5in", height="11in")
    )
))
```

### Headers and Footers

```python
result = client.render.create(RenderRequest(
    html="<h1>Report</h1>",
    options=RenderOptions(
        display_header_footer=True,
        header_template="""
            <div style="font-size: 10px; width: 100%; text-align: center;">
                Company Report
            </div>
        """,
        footer_template="""
            <div style="font-size: 10px; width: 100%; text-align: center;">
                Page <span class="pageNumber"></span> of <span class="totalPages"></span>
            </div>
        """,
        margins=Margins(
            top="1in",
            bottom="1in",
            left="0.5in",
            right="0.5in"
        )
    )
))
```

### PDF/A Compliance with Attachments

```python
from html2pdf.types import PDFAttachment
import base64

with open("invoice.xml", "rb") as f:
    xml_content = f.read()

result = client.render.create(RenderRequest(
    html="<h1>Invoice</h1>",
    options=RenderOptions(
        compliance="pdfa-3",
        attachments=[
            PDFAttachment(
                filename="invoice.xml",
                content=base64.b64encode(xml_content).decode(),
                description="Structured invoice data (ZUGFeRD)",
                mime_type="text/xml",
                relationship="Data"
            )
        ]
    )
))
```

### Digital Signatures

```python
from html2pdf.types import DigitalSignature
import base64

with open("certificate.p12", "rb") as f:
    cert_data = f.read()

result = client.render.create(RenderRequest(
    html="<h1>Signed Document</h1>",
    options=RenderOptions(
        signature=DigitalSignature(
            certificate=base64.b64encode(cert_data).decode(),
            password="cert-password",
            reason="Document approval",
            contact_info="signer@example.com",
            location="New York, USA",
            name="John Doe"
        )
    )
))
```

### Debug Mode

```python
job = client.render.create_async(RenderRequest(
    html="<h1>Debug Test</h1>",
    options=RenderOptions(debug=True)
))

# Wait for completion
import time
while True:
    status = client.render.get_status(job.job_id)
    if status.status in ("succeeded", "failed"):
        break
    time.sleep(1)

# Download debug bundle (includes HTML, console logs, network errors, metadata)
if status.status == "succeeded":
    debug_zip = client.render.download_debug(job.job_id)
    with open("debug.zip", "wb") as f:
        f.write(debug_zip)
```

## Error Handling

The SDK provides typed errors for better error handling:

```python
from html2pdf import (
    Html2PdfError,
    ValidationError,
    AuthenticationError,
    RateLimitError,
    TimeoutError,
    NetworkError
)

try:
    result = client.render.create(RenderRequest(html="<h1>Test</h1>"))
except AuthenticationError as e:
    print(f"Authentication failed: {e.message}")
except RateLimitError as e:
    print(f"Rate limit exceeded: {e.message}")
except ValidationError as e:
    print(f"Validation error: {e.message}")
    print(f"Details: {e.details}")
except TimeoutError as e:
    print(f"Request timeout: {e.message}")
except NetworkError as e:
    print(f"Network error: {e.message}")
except Html2PdfError as e:
    print(f"Error [{e.status_code}]: {e.message}")
except Exception as e:
    print(f"Unknown error: {e}")
```

## Configuration Options

```python
client = Html2PdfClient(
    # Required
    base_url="https://api.rendly.cloud",

    # Authentication (choose one)
    api_key="your-api-key",
    # OR
    # access_token="your-jwt-token",

    # Optional
    timeout=60.0,  # Request timeout in seconds (default: 30.0)
    max_retries=5,  # Max retry attempts (default: 3)
    headers={  # Custom headers for all requests
        "X-Custom-Header": "value"
    }
)
```

## Type Safety

The SDK is fully typed using Pydantic models:

```python
from html2pdf import Html2PdfClient
from html2pdf.types import (
    RenderRequest,
    RenderOptions,
    PageSizeEnum,
    ComplianceStandard,
    Margins
)

# IDE autocomplete and type checking work seamlessly
options = RenderOptions(
    page_size=PageSizeEnum.A4,
    compliance=ComplianceStandard.PDFA_2,
    print_background=True,
    margins=Margins(
        top="1in",
        right="1in",
        bottom="1in",
        left="1in"
    )
)

request = RenderRequest(
    html="<h1>Test</h1>",
    options=options
)
```

## Health Check

Check if the service is available:

```python
is_healthy = client.health_check()
print(f"Service healthy: {is_healthy}")
```

## Metrics

Get Prometheus metrics:

```python
metrics = client.get_metrics()
print(metrics)
```

## Context Managers

Both sync and async clients support context managers:

```python
# Synchronous
with Html2PdfClient(...) as client:
    result = client.render.create(...)

# Asynchronous
async with AsyncHtml2PdfClient(...) as client:
    result = await client.render.acreate(...)
```

## Requirements

- Python 3.9 or higher
- httpx >= 0.27.0
- pydantic >= 2.0.0

## Development

### Setup

```bash
# Clone the repository
git clone https://github.com/nixbit-maker/Rendly.git
cd html2pdf/sdk-python

# Install dependencies
pip install -e ".[dev]"
```

### Running Tests

```bash
pytest
```

### Type Checking

```bash
mypy html2pdf
```

### Linting and Formatting

```bash
ruff check html2pdf
ruff format html2pdf
```

## License

MIT

## Support

- [GitHub Issues](https://github.com/nixbit-maker/Rendly/issues)
- [Documentation](https://docs.html2pdf.example.com)
- Email: support@html2pdf.example.com

## Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](../CONTRIBUTING.md) for details.

## Changelog

See [CHANGELOG.md](./CHANGELOG.md) for release notes.