Menu
pdfhtml-to-pdfnodejspythonapiautomation

HTML to PDF API: Convert URLs and HTML in 2026

SnapSharp Team·April 14, 2026·7 min read

Generating PDFs from HTML is one of those tasks that looks simple until you actually have to do it in production. You spin up Puppeteer, deal with font rendering inconsistencies, fight with page margins, handle crashes from headless Chromium running out of memory under load, and end up maintaining a fragile internal service that nobody wants to touch.

There is a better approach: call an API, get a PDF back. This article covers how to do that with the SnapSharp /v1/pdf endpoint — using Node.js, Python, and PHP — and explains when an API approach beats running your own headless browser.


Why "just use Puppeteer" breaks down

For a prototype, spinning up Puppeteer locally works fine. In production, the cracks appear quickly:

  • Memory spikes. Chromium consumes 200–400 MB per process. Under concurrent load, your server runs out of memory.
  • Cold starts. Launching a new browser instance per request adds 1–3 seconds. Browser pooling helps but adds implementation complexity.
  • Font and CSS rendering. Headless Chromium on Linux needs specific font packages installed, otherwise your PDFs render with fallback fonts.
  • Crashes. Chromium crashes on malformed HTML, large pages, or when it hits memory limits. You need a supervisor, restart logic, and retry queues.
  • Scaling. A PDF generation spike means your app server slows down because Chromium is eating its CPU and memory.

The SnapSharp PDF API offloads all of this. Your app makes one HTTP request and receives a binary PDF response. No browser to manage.


The /v1/pdf endpoint

The endpoint accepts either a url or raw html as input, renders it using headless Chromium on SnapSharp's infrastructure, and returns the PDF.

Available parameters:

ParameterTypeDescription
urlstringURL to render as PDF
htmlstringRaw HTML to render
formatstringPage format: "A4", "A3", "Letter" (default: "A4")
marginnumberMargin in pixels on all sides (default: 40)
landscapebooleanLandscape orientation (default: false)
print_backgroundbooleanInclude CSS background colors and images (default: true)

One of url or html is required. The endpoint requires the Growth plan or higher.


Use cases

Before the code, here are the common reasons developers reach for HTML-to-PDF generation:

Invoices and receipts. You already have invoice data rendered in HTML for the email. Generating a PDF copy for download or attachment is a one-liner. HTML gives you full control over layout, fonts, and branding with CSS.

Reports and data exports. Dashboards rendered as HTML tables with charts can be exported to PDF for clients or compliance archives. This is far simpler than building a dedicated report renderer.

Contracts and legal documents. Legal teams need signed documents in PDF. Generate from an HTML template with dynamic field substitution, then store or email the result.

Email-to-PDF archiving. Render an HTML email template to PDF for record-keeping — same source, two output formats.

Print-ready marketing materials. One-pagers, proposals, spec sheets — design in HTML/CSS, export as PDF for distribution.


Node.js: using the @snapsharp/sdk

Install the SDK:

npm install @snapsharp/sdk

Generate a PDF from a URL:

import { SnapSharp } from '@snapsharp/sdk';
import { writeFileSync } from 'fs';

const snap = new SnapSharp('sk_live_YOUR_API_KEY');

const pdf = await snap.pdf({
  url: 'https://example.com/invoice/1234',
  format: 'A4',
  margin: 40,
  print_background: true,
});

writeFileSync('invoice.pdf', Buffer.from(pdf));
console.log('PDF saved to invoice.pdf');

Generate a PDF from raw HTML (useful when you control the template entirely):

import { SnapSharp } from '@snapsharp/sdk';
import { writeFileSync } from 'fs';

const snap = new SnapSharp('sk_live_YOUR_API_KEY');

const html = `
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <style>
    body { font-family: Inter, sans-serif; padding: 48px; color: #111; }
    .invoice-header { display: flex; justify-content: space-between; }
    .total { font-size: 24px; font-weight: 700; margin-top: 32px; }
  </style>
</head>
<body>
  <div class="invoice-header">
    <h1>Invoice #1234</h1>
    <span>2026-04-14</span>
  </div>
  <table>
    <tr><td>API access — Growth plan</td><td>$49.00</td></tr>
  </table>
  <div class="total">Total: $49.00</div>
</body>
</html>
`;

const pdf = await snap.pdf({
  html,
  format: 'A4',
  margin: 48,
  print_background: true,
});

writeFileSync('invoice.pdf', Buffer.from(pdf));

For landscape reports:

const pdf = await snap.pdf({
  url: 'https://app.example.com/reports/monthly',
  format: 'A3',
  landscape: true,
  margin: 32,
  print_background: true,
});

Python: using the snapsharp SDK

Install the SDK:

pip install snapsharp

Generate a PDF:

from snapsharp import SnapSharp

snap = SnapSharp("sk_live_YOUR_API_KEY")

pdf_bytes = snap.pdf(
    url="https://example.com/invoice/1234",
    format="A4",
    margin=40,
    print_background=True,
)

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

print("PDF saved to invoice.pdf")

Render from HTML in Python — useful when building invoice generation as part of a Django or FastAPI service:

from snapsharp import SnapSharp

snap = SnapSharp("sk_live_YOUR_API_KEY")

html = """
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <style>
    body { font-family: sans-serif; padding: 48px; }
    table { width: 100%; border-collapse: collapse; }
    td { padding: 8px; border-bottom: 1px solid #eee; }
  </style>
</head>
<body>
  <h1>Invoice #5678</h1>
  <table>
    <tr><td>SnapSharp Growth plan</td><td>$49.00</td></tr>
  </table>
  <p><strong>Total: $49.00</strong></p>
</body>
</html>
"""

pdf_bytes = snap.pdf(
    html=html,
    format="A4",
    margin=48,
    print_background=True,
)

with open("invoice_5678.pdf", "wb") as f:
    f.write(pdf_bytes)

PHP: using curl

No SDK needed — a direct curl request works cleanly:

<?php

$apiKey  = 'sk_live_YOUR_API_KEY';
$payload = json_encode([
    'url'              => 'https://example.com/invoice/1234',
    'format'           => 'A4',
    'margin'           => 40,
    'print_background' => true,
]);

$ch = curl_init('https://api.snapsharp.dev/v1/pdf');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode === 200) {
    file_put_contents('invoice.pdf', $response);
    echo "PDF saved.\n";
} else {
    $error = json_decode($response, true);
    echo "Error: " . $error['message'] . "\n";
}

To render from an HTML string in PHP:

<?php

$apiKey = 'sk_live_YOUR_API_KEY';
$html   = '<html><body><h1>Invoice #9012</h1><p>Total: $49.00</p></body></html>';

$payload = json_encode([
    'html'             => $html,
    'format'           => 'Letter',
    'margin'           => 40,
    'print_background' => true,
]);

$ch = curl_init('https://api.snapsharp.dev/v1/pdf');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
]);

$pdf = curl_exec($ch);
curl_close($ch);

file_put_contents('invoice_9012.pdf', $pdf);

Raw HTTP request (curl)

For testing or shell scripts:

# From URL
curl -X POST https://api.snapsharp.dev/v1/pdf \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/invoice/1234",
    "format": "A4",
    "margin": 40,
    "print_background": true
  }' \
  --output invoice.pdf

# From HTML string
curl -X POST https://api.snapsharp.dev/v1/pdf \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<html><body><h1>Hello PDF</h1></body></html>",
    "format": "A4"
  }' \
  --output hello.pdf

API approach vs DIY headless browser

ConcernPuppeteer/Playwright DIYSnapSharp API
Setup timeHours (install, pool, error handling)Minutes (install SDK, one call)
Memory overhead200–400 MB per Chromium instanceNone on your server
ConcurrencyComplex pooling logic requiredHandled by API infrastructure
Crash recoveryYou write retry logicHandled automatically
Font renderingLinux font packages, CI configConsistent across all renders
ScalingChromium spikes impact app serverIsolated from your application
MaintenanceBrowser version updates, patchesZero maintenance

The DIY approach makes sense when you need deep control over Chromium internals, have extremely high volume that makes API costs prohibitive, or have strict data residency requirements that prevent sending HTML to an external service. For most applications — invoicing, reporting, document generation — the API approach ships faster and costs less in engineering time.


Error handling

The API returns standard JSON error responses when something goes wrong:

{
  "error": "plan_required",
  "message": "The /v1/pdf endpoint requires the Growth plan or higher.",
  "status": 403,
  "request_id": "req_01j..."
}

Common errors:

  • 401 unauthorized — invalid or missing API key
  • 403 plan_required — your plan does not include PDF generation (requires Growth)
  • 400 validation_error — neither url nor html provided, or both provided
  • 429 rate_limit_exceeded — too many requests per minute for your plan

Next steps

Full parameter reference is in the PDF endpoint docs.

The /v1/pdf endpoint is available on the Growth plan. If you are on Free or Starter and need PDF generation, upgrade your plan or start a trial.

If you are generating PDFs at scale — bulk invoice generation, sitemap-wide document exports — the batch and async endpoints let you queue multiple jobs and poll for completion rather than holding open HTTP connections.

HTML to PDF API: Convert URLs and HTML in 2026 — SnapSharp Blog