Documentation
API Documentation
Convert HTML to PDF through our RESTful API
Overview
The pdfy API allows you to convert HTML content into high-quality PDF documents. All API endpoints are RESTful and return JSON responses.
Base URL: https://pdfy.app/api/v1
Authentication
All API requests require authentication using a Bearer token in the Authorization header.
Authorization: Bearer YOUR_API_TOKEN
PHP SDK
We provide an official PHP SDK that makes it easy to integrate PDF generation into your PHP applications. The SDK handles authentication, error handling, and provides a clean, object-oriented interface.
Official PHP SDK
Complete documentation, installation instructions, and examples available on GitHub.
Quick Start
composer require pdfy/php-sdk
Rate Limiting
To ensure fair usage and maintain service quality, the pdfy API implements rate limiting on all endpoints. Rate limits are applied per API token and reset automatically.
Current Rate Limits
POST /api/v1/pdfs
GET endpoints
Rate Limit Headers
Every API response includes headers that help you track your rate limit status:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1640995200
Retry-After: 45
Rate Limit Exceeded
When you exceed the rate limit, the API returns a 429 Too Many Requests status:
{
"error": true,
"message": "Too many requests. Please wait before trying again.",
"error_code": "RATE_LIMIT_EXCEEDED"
}
Best Practices
Handling Rate Limits in Code
async function generatePdfWithRetry(htmlContent, filename, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('/api/v1/pdfs', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({ html: htmlContent, filename })
});
// Check rate limit headers
const remaining = response.headers.get('X-RateLimit-Remaining');
const resetTime = response.headers.get('X-RateLimit-Reset');
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
// Wait before retrying
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (response.ok) {
console.log(`Rate limit remaining: ${remaining}`);
return await response.json();
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} catch (error) {
if (attempt === maxRetries) throw error;
// Exponential backoff for other errors
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
Generate PDF
Create a new PDF from HTML content. This endpoint returns immediately with a job ID for async processing.
/api/v1/pdfs
Request Body
{
"html": "<h1>Hello World</h1><p>This is a test PDF.</p>",
"filename": "my-document.pdf",
"options": {
"format": "A4",
"orientation": "portrait",
"margin_top": 1.0,
"margin_right": 1.0,
"margin_bottom": 1.0,
"margin_left": 1.0,
"margin_unit": "cm"
}
}
Response
{
"success": true,
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "PDF generation started"
}
}
Options Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
format |
string | A4 | Page format (A4, A3, A5, Letter, Legal, Tabloid) |
orientation |
string | portrait | Page orientation (portrait, landscape) |
margin_top |
number | 0 | Top margin value (0-100) |
margin_right |
number | 0 | Right margin value (0-100) |
margin_bottom |
number | 0 | Bottom margin value (0-100) |
margin_left |
number | 0 | Left margin value (0-100) |
margin_unit |
string | cm | Unit for all margins (cm, mm, in, px) |
print_background |
boolean | true | Include background graphics |
Check Status
Check the status of a PDF generation job.
/api/v1/pdfs/{job_id}
Response
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"filename": "my-document.pdf",
"status": "completed",
"file_size": 245760,
"file_size_human": "240 KB",
"created_at": "2025-08-14T10:30:00Z",
"completed_at": "2025-08-14T10:30:15Z",
"download_url": "/api/v1/pdfs/550e8400-e29b-41d4-a716-446655440000/download"
}
}
Download PDF
Download the generated PDF file. Only available when status is "completed".
/api/v1/pdfs/{job_id}/download
Returns the PDF file as a binary download with appropriate headers.
List PDFs
Get a list of your PDF generation jobs with optional filtering.
/api/v1/pdfs
Query Parameters
status- Filter by status (pending, processing, completed, failed)limit- Number of results (max 100, default 20)
Response
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"filename": "my-document.pdf",
"status": "completed",
"file_size": 245760,
"file_size_human": "240 KB",
"created_at": "2025-08-14T10:30:00Z",
"completed_at": "2025-08-14T10:30:15Z",
"download_url": "/api/v1/pdfs/550e8400-e29b-41d4-a716-446655440000/download"
}
],
"meta": {
"total": 1,
"user_id": 123
}
}
Error Handling
The API uses standard HTTP status codes and returns detailed error information.
Error Response Format
{
"error": true,
"message": "Validation failed",
"error_code": "VALIDATION_ERROR",
"errors": {
"html": ["The html field is required."]
}
}
Common Error Codes
VALIDATION_ERROR - Invalid request dataRATE_LIMIT_EXCEEDED - Too many requestsDAILY_LIMIT_EXCEEDED - Daily PDF limit reachedJOB_NOT_FOUND - PDF job not foundPDF_NOT_READY - PDF not ready for downloadCode Examples
cURL
# Generate PDF
curl -X POST https://pdfy.app/api/v1/pdfs \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Hello World</h1>",
"filename": "hello.pdf"
}'
# Check status
curl -X GET https://pdfy.app/api/v1/pdfs/JOB_ID \
-H "Authorization: Bearer YOUR_API_TOKEN"
# Download PDF (use -L: the API redirects to a short-lived signed URL)
curl -L -X GET https://pdfy.app/api/v1/pdfs/JOB_ID/download \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-o hello.pdf
JavaScript (fetch)
const API_TOKEN = 'YOUR_API_TOKEN';
const BASE_URL = 'https://pdfy.app/api/v1';
async function generatePDF(html, filename) {
const response = await fetch(`${BASE_URL}/pdfs`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
html: html,
filename: filename
})
});
const result = await response.json();
return result.data.job_id;
}
async function checkStatus(jobId) {
const response = await fetch(`${BASE_URL}/pdfs/${jobId}`, {
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
}
});
return await response.json();
}
async function downloadPDF(jobId) {
const response = await fetch(`${BASE_URL}/pdfs/${jobId}/download`, {
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
}
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.pdf';
a.click();
}
PHP
<?php
$apiToken = 'YOUR_API_TOKEN';
$baseUrl = 'https://pdfy.app/api/v1';
function generatePDF($html, $filename) {
global $apiToken, $baseUrl;
$data = [
'html' => $html,
'filename' => $filename
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$baseUrl/pdfs");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiToken,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
return $result['data']['job_id'];
}
function checkStatus($jobId) {
global $apiToken, $baseUrl;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$baseUrl/pdfs/$jobId");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiToken
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}