OffloadPDF
Convert HTML to PDF documents via a simple, powerful API. Built for developers who need reliable, scalable PDF generation without the hassle of managing browser infrastructure.
Table of Contents
- Quick Start
- Authentication
- API Reference
- Code Examples
- Error Handling
- Subscription Plans & Rate Limits
- Common Use Cases
- Troubleshooting
- Support
Quick Start
Get your first PDF in under 5 minutes:
1. Sign up and subscribe
Visit the dashboard and choose a plan (7-day free trial included).
2. Create an API token
Navigate to the API Tokens page in your dashboard and create a new token. Save it securely – you won’t be able to see it again.
3. Make your first API call
curl -X POST https://your-app.com/api/v1/pdf/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Hello World</h1><p>My first PDF!</p>"
}'4. Success!
You’ll receive a JSON response with your base64-encoded PDF:
{
"pdf_content": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL..."
}Decode the base64 string to get your PDF file.
Authentication
Creating API Tokens
API tokens are created in your dashboard:
- Log into your account
- Navigate to API Tokens
- Click “Create New Token”
- Give your token a descriptive name
- Copy the token immediately – it’s only shown once
Using API Tokens
Include your API token in the Authorization header of every request:
Authorization: Bearer YOUR_API_TOKENSecurity Best Practices
- Never commit tokens to version control – Use environment variables instead
- Rotate tokens periodically – Delete unused tokens
- Use HTTPS – Always make requests over secure connections
- Store securely – Treat tokens like passwords
Subscription Requirement
All API requests require an active subscription. New accounts receive a 7-day free trial to test the service. Requests will fail with a 402 Payment Required error if your subscription is inactive or expired.
Note: API quotas are account-level. Multiple tokens share the same monthly request limit.
API Reference
Endpoint
POST https://your-app.com/api/v1/pdfRequest Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
html | string | Yes | The HTML content to convert to PDF |
Request Headers
| Header | Required | Value | Description |
|---|---|---|---|
Authorization | Yes | Bearer {token} | Your API token |
Content-Type | Yes | application/json | Request format |
Accept | No | application/pdf or application/json | Response format (default: application/json) |
Request Example
{
"html": "<h1>Document Title</h1><p>Your content here</p>"
}Response Formats
JSON Response (default)
Returns a JSON object with the PDF content encoded as base64:
{
"pdf_content": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMi..."
}Decode the pdf_content field to get the binary PDF data.
Binary PDF Response
Set the Accept header to application/pdf to receive the PDF file directly:
Accept: application/pdfThe response will be a binary PDF file with Content-Type: application/pdf.
HTTP Status Codes
| Code | Status | Description |
|---|---|---|
| 200 | Success | PDF generated successfully |
| 401 | Unauthorized | Invalid or missing API token |
| 402 | Payment Required | No active subscription |
| 422 | Validation Error | Invalid request (missing or invalid html) |
| 429 | Too Many Requests | Rate limit exceeded (monthly quota reached) |
| 500 | Server Error | PDF generation failed |
Code Examples
cURL
Basic Request (JSON Response)
curl -X POST https://your-app.com/api/v1/pdf \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Hello World</h1><p>This is a PDF</p>"
}'Download Binary PDF
curl -X POST https://your-app.com/api/v1/pdf \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/pdf" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Invoice</h1><p>Total: $100</p>"
}' \
--output invoice.pdfComplex HTML with Styling
curl -X POST https://your-app.com/api/v1/pdf \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"html": "<!DOCTYPE html><html><head><style>body{font-family:Arial;margin:40px;}h1{color:#333;}</style></head><body><h1>Styled Document</h1><p>This PDF includes custom CSS styling.</p></body></html>"
}'JavaScript / Node.js
Using Fetch API (Browser or Node.js 18+)
const response = await fetch('https://your-app.com/api/v1/pdf', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
html: '<h1>Hello World</h1><p>Generated from JavaScript</p>'
})
});
const data = await response.json();
const pdfContent = data.pdf_content; // Base64-encoded PDF
// Decode base64 to binary
const pdfBinary = atob(pdfContent);Download Binary PDF (Browser)
const response = await fetch('https://your-app.com/api/v1/pdf', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Accept': 'application/pdf',
'Content-Type': 'application/json'
},
body: JSON.stringify({
html: '<h1>Invoice #12345</h1><p>Amount due: $500</p>'
})
});
// Create download link
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'invoice.pdf';
a.click();
window.URL.revokeObjectURL(url);Node.js with Axios
const axios = require('axios');
const fs = require('fs');
async function generatePDF() {
try {
const response = await axios.post(
'https://your-app.com/api/v1/pdf',
{
html: '<h1>Report</h1><p>Generated from Node.js</p>'
},
{
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
}
);
// Decode base64 and save to file
const pdfBuffer = Buffer.from(response.data.pdf_content, 'base64');
fs.writeFileSync('output.pdf', pdfBuffer);
console.log('PDF saved successfully!');
} catch (error) {
console.error('Error:', error.response?.data || error.message);
}
}
generatePDF();Node.js with Axios (Binary Download)
const axios = require('axios');
const fs = require('fs');
async function downloadPDF() {
const response = await axios.post(
'https://your-app.com/api/v1/pdf',
{
html: '<h1>Document</h1><p>Content here</p>'
},
{
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Accept': 'application/pdf',
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
fs.writeFileSync('document.pdf', response.data);
console.log('PDF downloaded successfully!');
}
downloadPDF();PHP
Using Guzzle (Recommended)
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://your-app.com',
'headers' => [
'Authorization' => 'Bearer YOUR_API_TOKEN',
'Content-Type' => 'application/json',
]
]);
$response = $client->post('/api/v1/pdf', [
'json' => [
'html' => '<h1>Hello from PHP</h1><p>This is a test document.</p>'
]
]);
$data = json_decode($response->getBody(), true);
$pdfContent = base64_decode($data['pdf_content']);
file_put_contents('output.pdf', $pdfContent);
echo "PDF generated successfully!";Download Binary PDF with Guzzle
<?php
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://your-app.com',
'headers' => [
'Authorization' => 'Bearer YOUR_API_TOKEN',
]
]);
$response = $client->post('/api/v1/pdf', [
'headers' => [
'Accept' => 'application/pdf',
],
'json' => [
'html' => '<h1>Invoice</h1><p>Order #12345</p>'
]
]);
file_put_contents('invoice.pdf', $response->getBody());
echo "PDF saved!";Using PHP cURL Extension
<?php
$ch = curl_init('https://your-app.com/api/v1/pdf');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer YOUR_API_TOKEN',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'html' => '<h1>Document Title</h1><p>Content here</p>'
]));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$data = json_decode($response, true);
$pdf = base64_decode($data['pdf_content']);
file_put_contents('document.pdf', $pdf);
echo "Success!";
} else {
echo "Error: " . $response;
}Using Laravel HTTP Client
<?php
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
$response = Http::withToken('YOUR_API_TOKEN')
->post('https://your-app.com/api/v1/pdf', [
'html' => '<h1>Laravel Example</h1><p>PDF generation from Laravel</p>'
]);
if ($response->successful()) {
$pdfContent = base64_decode($response->json('pdf_content'));
Storage::put('generated.pdf', $pdfContent);
return response()->download(storage_path('app/generated.pdf'));
}Error Handling
Common Error Responses
401 Unauthorized
Missing or invalid API token.
{
"message": "Unauthenticated."
}Cause: Authorization header is missing or the token is invalid.
Solution: Verify your Authorization header is properly formatted: Authorization: Bearer YOUR_TOKEN
402 Payment Required
No active subscription.
{
"message": "Subscription required."
}Cause: Your trial has expired or you don’t have an active subscription.
Solution: Subscribe to a plan at the billing portal.
422 Validation Error
Invalid or missing request data.
{
"message": "The html field is required.",
"errors": {
"html": [
"The html field is required."
]
}
}Cause: The html parameter is missing or not a string.
Solution: Ensure your request includes the html field with valid HTML content.
Error Handling Examples
JavaScript
try {
const response = await fetch('https://your-app.com/api/v1/pdf/create', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({ html: '<h1>Test</h1>' })
});
if (!response.ok) {
const error = await response.json();
console.error('API Error:', error.message);
return;
}
const data = await response.json();
// Process PDF...
} catch (error) {
console.error('Request failed:', error);
}PHP
<?php
try {
$response = $client->post('/api/v1/pdf/create', [
'json' => ['html' => '<h1>Test</h1>']
]);
$data = json_decode($response->getBody(), true);
// Process PDF...
} catch (\GuzzleHttp\Exception\ClientException $e) {
$statusCode = $e->getResponse()->getStatusCode();
$error = json_decode($e->getResponse()->getBody(), true);
if ($statusCode === 401) {
echo "Authentication failed: Check your API token";
} elseif ($statusCode === 402) {
echo "Subscription required: Please subscribe at /billing";
} elseif ($statusCode === 422) {
echo "Validation error: " . $error['message'];
}
}Subscription Plans & Rate Limits
Plans
All plans include a 7-day free trial.
| Plan | Monthly Requests | Features |
|---|---|---|
| Starter | 1,000 | Unlimited API keys, Unlimited watermarked PDFs (testing), Email support |
| Growth | 5,000 | Unlimited API keys, Unlimited watermarked PDFs (testing), Email support |
| Business | 10,000 | Unlimited API keys, Unlimited watermarked PDFs (testing), Priority email support |
Rate Limits
- Monthly quotas reset on the first day of each billing period
- Account-level limits – All API tokens share the same quota
- Overage handling – Requests over your limit return
429 Too Many Requests
Managing Your Subscription
- View usage and current plan at the billing portal
- Upgrade or downgrade anytime (prorated billing)
- Create unlimited API tokens (they share your account quota)
Common Use Cases
Invoice Generation
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
}
.header {
text-align: center;
margin-bottom: 40px;
}
.invoice-details {
margin: 20px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
th, td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.total {
text-align: right;
font-size: 18px;
font-weight: bold;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="header">
<h1>INVOICE</h1>
<p>Invoice #12345 | Date: January 15, 2025</p>
</div>
<div class="invoice-details">
<p><strong>Bill To:</strong><br>
John Doe<br>
123 Main St<br>
Anytown, USA 12345</p>
</div>
<table>
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Professional Services</td>
<td>10 hours</td>
<td>$150/hr</td>
<td>$1,500.00</td>
</tr>
<tr>
<td>Consulting</td>
<td>5 hours</td>
<td>$200/hr</td>
<td>$1,000.00</td>
</tr>
</tbody>
</table>
<div class="total">
Total: $2,500.00
</div>
</body>
</html>Report with Tables and Charts
For best results:
- Use inline CSS or
<style>tags in the<head>section - Embed images as base64 data URIs or use absolute URLs
- Keep HTML valid – Use proper DOCTYPE and structure
- Test complex layouts using watermarked PDFs (unlimited, free)
Best Practices
- CSS Styling: Use inline styles or
<style>tags. External stylesheets must use absolute URLs. - Images:
- Use base64-encoded data URIs:
<img src="data:image/png;base64,..."> - Or absolute URLs:
<img src="https://example.com/image.png"> - Relative paths won’t work
- Use base64-encoded data URIs:
- Fonts:
- Stick to web-safe fonts (Arial, Times New Roman, etc.)
- Or use
@importwith Google Fonts URLs
- Page Size:
- Current format: Letter (8.5″ × 11″)
- Design your HTML accordingly
Troubleshooting
Common Issues
PDF is blank or incomplete
Possible causes:
- Invalid HTML structure
- External resources failing to load
- JavaScript errors (JavaScript is supported but may not execute as expected)
Solutions:
- Validate your HTML
- Embed images as base64
- Use inline CSS instead of external stylesheets
CSS styles not rendering
Possible causes:
- External stylesheet URLs not accessible
- CSS syntax errors
Solutions:
- Use inline styles:
<p style="color: red;">Text</p> - Or use
<style>tags in the HTML<head> - Ensure external stylesheets use HTTPS URLs
Images not displaying
Possible causes:
- Relative image paths
- External images not loading
- Image URLs using HTTP instead of HTTPS
Solutions:
- Convert images to base64 data URIs
- Use absolute HTTPS URLs for images
- Verify image URLs are publicly accessible
Request timeout
Possible causes:
- HTML is too complex
- Too many external resources
- Large images
Solutions:
- Simplify your HTML structure
- Reduce number of images or compress them
- Embed resources as base64 to reduce external requests
- Note: Maximum timeout is 5 minutes
Technical Limitations
- Timeout: Maximum 5 minutes (300 seconds) per request
- Memory: 2GB allocation for PDF generation
- Storage: 512MB temporary storage
- Page Format: Currently Letter (8.5″ × 11″)
- Processing: Rendered using Chromium via AWS Lambda
Best Practices for Reliable PDFs
- Keep HTML structure simple and valid
- Use inline CSS when possible
- Embed images as base64 or use absolute HTTPS URLs
- Test with watermarked PDFs first (unlimited, doesn’t count toward quota)
- Handle API errors gracefully in your application
- Implement retry logic for network failures
- Monitor your quota usage in the billing portal
Support
Getting Help
- Email Support: Available on all plans (Starter, Growth)
- Priority Email Support: Available on Business plan
- Response Time:
- Standard: Within 24-48 hours
- Priority: Within 12 hours
Useful Links
- Dashboard: https://app.offloadpdf.com/
- API Tokens: https://app.offloadpdf.com/user/api-tokens
- Billing Portal: https://app.offloadpdf.com/billing
Feedback & Feature Requests
We’re constantly improving OffloadPDF. If you have suggestions or feature requests, please reach out.