POST /quickjob
Generate PDFs instantly (< 30 seconds) - Perfect for simple documents (up to 25 pages maximum).
When to Use
✅ Use QuickJob when:
- Document is simple and small (up to 25 pages maximum)
- Need PDF immediately
- Synchronous workflow
- Converting images to PDF (up to 25 images maximum)
- Converting a publicly accessible URL to PDF
❌ Use LongJob instead when:
- Document is large (more than 25 pages)
- Can use async processing
- Want webhook notifications
QuickJob supports HTML, Markdown, URL, and image-to-PDF conversion. Pass a public HTTPS URL and the API will fetch and convert it automatically. Images process fast (~0.5-2s per image).
Quick Example
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"input_type": "html",
"html": "<h1>Invoice</h1><p>Amount: $100</p>"
}' \
--output invoice.pdf
# Or convert a URL directly
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"input_type": "url",
"url": "https://example.com/"
}' \
--output invoice.pdf
Authentication
Required. Send either an API key or a dashboard (Cognito) ID token:
X-API-Key: your_api_key_here
Authorization: Bearer <cognito_id_token>
Request
Endpoint
POST https://api.podpdf.com/quickjob
Request Body
- HTML Input
- URL Input
- Markdown Input
- Images (Multipart)
{
"input_type": "html",
"html": "<!DOCTYPE html><html><head><title>Invoice</title></head><body><h1>Invoice</h1><p>Thank you!</p></body></html>",
"options": {
"format": "A4",
"margin": {
"top": "20mm",
"right": "20mm",
"bottom": "20mm",
"left": "20mm"
},
"printBackground": true
}
}
{
"input_type": "url",
"url": "https://example.com/",
"options": {
"format": "A4",
"margin": {
"top": "20mm",
"right": "20mm",
"bottom": "20mm",
"left": "20mm"
},
"printBackground": true
}
}
The API fetches the URL server-side and converts it based on the remote Content-Type. Supported content types: text/html, text/markdown, image/png, image/jpeg.
URL requirements:
- Must use
https://(HTTP is rejected) - Must be publicly accessible (private/internal IPs are rejected)
- Remote server must respond within 10 seconds
- Response body must not exceed 5 MB
{
"input_type": "markdown",
"markdown": "# Invoice\n\n**Customer:** John Doe\n**Amount:** $100\n\nThank you!",
"options": {
"format": "A4",
"margin": {
"top": "20mm",
"right": "20mm",
"bottom": "20mm",
"left": "20mm"
},
"printBackground": true
}
}
Content-Type: multipart/form-data
# cURL example - multiple images
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: your_api_key_here" \
-F "input_type=image" \
-F "images=@photo1.png" \
-F "images=@photo2.jpg" \
-F 'options={"format":"A4","fit":"contain"}'
// JavaScript/Browser example
const formData = new FormData();
formData.append('input_type', 'image');
formData.append('images', file1); // File from input[type=file]
formData.append('images', file2);
formData.append('options', JSON.stringify({
format: 'A4',
margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' },
fit: 'contain'
}));
const response = await fetch('https://api.podpdf.com/quickjob', {
method: 'POST',
headers: { 'X-API-Key': apiKey },
body: formData
});
Each image becomes one page in the PDF.
Request Fields
For HTML/Markdown/URL (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
input_type | string | ✅ | "html", "markdown", "url", or "image". Must be enabled for your plan (see enabled_conversion_types in plan details). |
html | string | ✅* | HTML content (*required if input_type is "html") |
markdown | string | ✅* | Markdown content (*required if input_type is "markdown") |
url | string | ✅* | Publicly accessible HTTPS URL (*required if input_type is "url"). HTTP URLs and private/internal IPs are rejected. See URL fetching below. |
options | object | ❌ | PDF generation options |
options.format | string | ❌ | Paper format: "A4", "Letter", etc. (default: "A4") |
options.margin | object | ❌ | Margins for the PDF |
options.margin.top | string | ❌ | Top margin (e.g., "20mm") |
options.margin.right | string | ❌ | Right margin |
options.margin.bottom | string | ❌ | Bottom margin |
options.margin.left | string | ❌ | Left margin |
options.printBackground | boolean | ❌ | Print background graphics (default: true) |
options.scale | number | ❌ | Scale of rendering (default: 1.0) |
options.landscape | boolean | ❌ | Landscape orientation (default: false) |
options.preferCSSPageSize | boolean | ❌ | Use page size from CSS instead of format (default: false) |
options.width, options.height | string | ❌ | Custom paper size, e.g. "100mm" and "150mm". Send "format": null with them: format defaults to "A4" and wins over width/height |
options.pageRanges | string | ❌ | Pages to keep, e.g. "1-3, 5" (default: all) |
options.displayHeaderFooter | boolean | ❌ | Print headerTemplate and footerTemplate on every page (default: false) |
options.headerTemplate, options.footerTemplate | string | ❌ | HTML for the header and footer. Elements with the classes pageNumber, totalPages, date, title and url are filled in. Give them an explicit font-size, and leave room with margin.top / margin.bottom |
store | boolean | ❌ | When true, upload the PDF to S3 and return a JSON response with a signed download URL instead of binary. URL expires after 1 hour. (default: false) |
For Images (Multipart form-data):
| Field | Type | Required | Description |
|---|---|---|---|
input_type | string | ✅ | Must be "image" |
images | file(s) | ✅ | One or more PNG or JPEG image files (repeat field for multiple) |
options | string | ❌ | JSON string with options |
options.format | string | ❌ | Page size (default: "A4") |
options.margin | object | ❌ | Margins (default: 10mm all sides) |
options.fit | string | ❌ | How to fit image: "contain" (default), "cover", "fill", "none" |
options.landscape | boolean | ❌ | Landscape orientation (default: false) |
store | string | ❌ | Send "true" to store the PDF and get a JSON response with a download URL, exactly as in the JSON API |
Image Limits:
- Maximum 5MB per image
- Maximum request size of about 4.5 MB of image data in total: the API accepts request bodies up to 6 MB, and uploads are base64-encoded on the way in. Larger requests are rejected with
413before they are processed - Maximum 10000×10000 pixels per image
- Each image = 1 page in the PDF
- Maximum 25 images per request (each image is a page, so the page limit applies)
Images that fail validation are skipped rather than failing the whole request: if at least one image is usable you still get a PDF, and the X-PDF-Pages header tells you how many were included.
URL fetching
With input_type: "url", PodPDF fetches the URL server-side and converts whatever comes back — it does not open the page in a browser at your address, so relative assets on the remote page are not resolved. Inline or absolute-URL assets work; relative <img src="logo.png"> will not.
| Rule | Value |
|---|---|
| Scheme | HTTPS only — HTTP is rejected |
| Blocked targets | localhost, private and link-local IPs, .local / .internal / .localhost hosts |
| Redirects | Up to 5, re-checked for SSRF at each hop |
| Timeout | 10 seconds |
| Max download | 5 MB |
The response's Content-Type decides how the content is treated:
| Content-Type | Treated as |
|---|---|
text/html, application/xhtml+xml | HTML |
text/markdown, text/x-markdown | Markdown |
image/png, image/jpeg, image/jpg | Image |
If the type is generic (for example text/plain), the file extension of the final URL is used instead: .html/.htm, .md/.markdown, .png, .jpg/.jpeg. When neither identifies a supported type, the request fails with 422 URL_CONTENT_TYPE_NOT_SUPPORTED.
Page size, headers and footers
A 100 × 150 mm label with a page-numbered footer:
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"input_type": "html",
"html": "<h1>Shipping label</h1><p>Order 1042</p>",
"options": {
"format": null,
"width": "100mm",
"height": "150mm",
"margin": { "top": "10mm", "bottom": "15mm", "left": "8mm", "right": "8mm" },
"displayHeaderFooter": true,
"headerTemplate": "<span></span>",
"footerTemplate": "<div style=\"font-size:8px;width:100%;text-align:center\">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>"
}
}' \
--output label.pdf
displayHeaderFooter prints a default header (date and title) unless you pass headerTemplate; send an empty element, as above, to print only a footer.
Response
Success — Binary (default)
Without store, you receive the PDF file directly as binary data.
Headers:
Content-Type: application/pdf
Content-Disposition: inline; filename="document.pdf"
X-PDF-Pages: 5
X-PDF-Truncated: false
X-Job-Id: 9f0a4b78-2c0c-4d14-9b8b-123456789abc
The X-PDF-Pages header tells you how many pages were generated.
Success — Stored (store: true)
When you pass "store": true, the PDF is uploaded to S3 and you receive a JSON response instead of binary. Use this when you need to share or download the PDF later rather than streaming it directly.
{
"job_id": "9f0a4b78-2c0c-4d14-9b8b-123456789abc",
"pages": 5,
"truncated": false,
"download_url": "https://podpdf-prod-pdfs.s3.eu-central-1.amazonaws.com/9f0a4b78...pdf?X-Amz-Signature=...",
"download_url_expires_at": "2025-12-21T11:30:00.000Z"
}
The signed download_url expires after 1 hour. After that the file is inaccessible and deleted after 30 days. Download or share it before it expires.
If the S3 upload fails, download_url and download_url_expires_at will be null.
Timeout (408 Request Timeout)
If generation takes longer than 30 seconds:
{
"error": {
"code": "QUICKJOB_TIMEOUT",
"message": "Job processing exceeded 30-second timeout. Please use /longjob endpoint for larger documents.",
"details": {
"job_id": "9f0a4b78-2c0c-4d14-9b8b-123456789abc",
"timeout_seconds": 30,
"suggestion": "use_longjob_endpoint"
}
}
}
If you hit the timeout, your document is too large for QuickJob.
Use the LongJob endpoint instead →
Error Responses
| Status | Code | Meaning | Solution |
|---|---|---|---|
| 400 | MISSING_INPUT_TYPE | No input_type in the request | Send input_type |
| 400 | INVALID_INPUT_TYPE | Invalid input_type | Use "html", "markdown", "url", or "image" |
| 400 | MISSING_CONTENT_FIELD | The field matching input_type is absent | Provide content |
| 400 | EMPTY_CONTENT_FIELD | The content field is empty | Provide content |
| 400 | CONFLICTING_FIELDS | Both html and markdown were sent | Send only the field matching input_type |
| 400 | INPUT_SIZE_EXCEEDED | Content is over 5 MB | Split the document |
| 400 | MISSING_URL | url field absent or empty when input_type is "url" | Include the url field |
| 400 | INVALID_URL | Not a valid URL, not HTTPS, or resolves to a private IP | Use a public HTTPS URL |
| 400 | PAGE_LIMIT_EXCEEDED | PDF exceeds max pages | Reduce content or use LongJob |
| 400 | INVALID_IMAGE_FORMAT | Image is not PNG or JPEG | Use PNG or JPEG only |
| 400 | INVALID_IMAGE_DATA | Image is corrupted or invalid | Check image file |
| 400 | IMAGE_TOO_LARGE | Image exceeds 5MB or 10000×10000px | Resize or compress image |
| 400 | MISSING_IMAGES | No image files provided | Include at least one image |
| 400 | INVALID_MULTIPART | Malformed multipart request | Check Content-Type and form data |
| 400 | INVALID_OPTIONS_JSON | options is not valid JSON | Fix the JSON string |
| 401 | UNAUTHORIZED | Invalid or missing credentials | Check your API key or token |
| 402 | UPGRADE_REQUIRED | The account has no paid plan | Buy credits or subscribe to activate the account |
| 403 | ACCOUNT_NOT_FOUND | Account doesn't exist | Create an account |
| 403 | CONVERSION_TYPE_NOT_ENABLED | Conversion type not enabled for plan | Check plan's enabled_conversion_types |
| 403 | RATE_LIMIT_EXCEEDED | Your plan's per-minute limit was hit | Wait details.retry_after seconds (only plans that set a limit) |
| 403 | INSUFFICIENT_CREDITS | Not enough allowance or credits | Buy credits or upgrade your plan in the dashboard |
| 408 | QUICKJOB_TIMEOUT | Took too long | Use /longjob instead |
| 422 | URL_FETCH_FAILED | DNS failure or connection refused when fetching URL | Check the URL is reachable |
| 422 | URL_FETCH_TIMEOUT | Remote server did not respond within 10 seconds | Ensure the URL responds quickly |
| 422 | URL_FETCH_HTTP_ERROR | Remote server returned a non-2xx status | Check the URL returns 200 |
| 422 | URL_CONTENT_TYPE_NOT_SUPPORTED | Remote Content-Type is not HTML, Markdown, or PNG/JPEG | Use a supported content type |
| 429 | TOO_MANY_REQUESTS | Platform throttling from API Gateway (not your plan's limit) | Back off and retry with jitter |
| 500 | PDF_GENERATION_FAILED | The renderer failed on this document | Check the HTML/CSS renders in a browser, then retry |
| 500 | INTERNAL_SERVER_ERROR | Server error | Try again later |
Complete Examples
Store PDF to S3 (cURL)
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: $PODPDF_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input_type": "html",
"html": "<h1>Invoice</h1><p>Amount: $100</p>",
"store": true
}'
# Returns JSON with download_url (expires in 1 hour)
Store PDF to S3 (JavaScript)
const response = await fetch('https://api.podpdf.com/quickjob', {
method: 'POST',
headers: {
'X-API-Key': process.env.PODPDF_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
input_type: 'html',
html: '<h1>Invoice</h1>',
store: true
})
});
const { job_id, download_url, download_url_expires_at } = await response.json();
console.log('Download:', download_url); // valid for 1 hour
Store PDF to S3 (Python)
import requests, os
response = requests.post(
'https://api.podpdf.com/quickjob',
headers={
'X-API-Key': os.getenv('PODPDF_API_KEY'),
'Content-Type': 'application/json'
},
json={
'input_type': 'html',
'html': '<h1>Invoice</h1>',
'store': True
}
)
data = response.json()
print('Download URL:', data['download_url']) # expires in 1 hour
URL to PDF (cURL)
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: $PODPDF_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input_type": "url",
"url": "https://example.com/",
"options": { "format": "A4", "printBackground": true }
}' \
--output report.pdf
URL to PDF (JavaScript)
import fs from 'node:fs';
const response = await fetch('https://api.podpdf.com/quickjob', {
method: 'POST',
headers: {
'X-API-Key': process.env.PODPDF_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
input_type: 'url',
url: 'https://example.com/',
options: { format: 'A4', printBackground: true }
})
});
if (response.ok) {
const buffer = await response.arrayBuffer();
fs.writeFileSync('report.pdf', Buffer.from(buffer));
}
URL to PDF (Python)
import requests, os
response = requests.post(
'https://api.podpdf.com/quickjob',
headers={
'X-API-Key': os.getenv('PODPDF_API_KEY'),
'Content-Type': 'application/json'
},
json={
'input_type': 'url',
'url': 'https://example.com/',
'options': {'format': 'A4', 'printBackground': True}
}
)
if response.status_code == 200:
with open('report.pdf', 'wb') as f:
f.write(response.content)
Images to PDF (cURL)
# Single image
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: $PODPDF_API_KEY" \
-F "input_type=image" \
-F "images=@photo.jpg" \
-F 'options={"format":"A4","fit":"contain"}' \
--output photo.pdf
# Multiple images (photo album)
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: $PODPDF_API_KEY" \
-F "input_type=image" \
-F "images=@photo1.jpg" \
-F "images=@photo2.jpg" \
-F "images=@photo3.jpg" \
-F 'options={"format":"A4","margin":{"top":"5mm","right":"5mm","bottom":"5mm","left":"5mm"}}' \
--output album.pdf
Images to PDF (JavaScript)
const FormData = require('form-data');
const fs = require('fs');
const fetch = require('node-fetch');
async function imagesToPDF() {
const formData = new FormData();
formData.append('input_type', 'image');
formData.append('images', fs.createReadStream('photo1.jpg'));
formData.append('images', fs.createReadStream('photo2.jpg'));
formData.append('options', JSON.stringify({
format: 'A4',
fit: 'contain',
margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' }
}));
const response = await fetch('https://api.podpdf.com/quickjob', {
method: 'POST',
headers: {
'X-API-Key': process.env.PODPDF_API_KEY,
},
body: formData
});
if (response.ok) {
const buffer = await response.buffer();
fs.writeFileSync('output.pdf', buffer);
console.log('PDF created successfully!');
}
}
imagesToPDF();
Images to PDF (Python)
import requests
import os
def images_to_pdf():
api_key = os.getenv('PODPDF_API_KEY')
files = [
('images', ('photo1.jpg', open('photo1.jpg', 'rb'), 'image/jpeg')),
('images', ('photo2.jpg', open('photo2.jpg', 'rb'), 'image/jpeg')),
]
data = {
'input_type': 'image',
'options': '{"format":"A4","fit":"contain"}'
}
response = requests.post(
'https://api.podpdf.com/quickjob',
headers={'X-API-Key': api_key},
files=files,
data=data
)
if response.status_code == 200:
with open('output.pdf', 'wb') as f:
f.write(response.content)
print('PDF created successfully!')
images_to_pdf()
JavaScript (Node.js)
const fetch = require('node-fetch');
const fs = require('fs');
async function generatePDF() {
const response = await fetch('https://api.podpdf.com/quickjob', {
method: 'POST',
headers: {
'X-API-Key': process.env.PODPDF_API_KEY, // Use environment variable
'Content-Type': 'application/json'
},
body: JSON.stringify({
input_type: 'html',
html: '<h1>Hello World</h1><p>My first PDF!</p>'
})
});
if (response.ok) {
const buffer = await response.buffer();
fs.writeFileSync('output.pdf', buffer);
console.log('PDF created successfully!');
} else {
console.error('Error:', await response.json());
}
}
generatePDF();
Python
import requests
import os
def generate_pdf():
api_key = os.getenv('PODPDF_API_KEY') # Use environment variable
response = requests.post(
'https://api.podpdf.com/quickjob',
headers={
'X-API-Key': api_key,
'Content-Type': 'application/json'
},
json={
'input_type': 'html',
'html': '<h1>Hello World</h1><p>My first PDF!</p>'
}
)
if response.status_code == 200:
with open('output.pdf', 'wb') as f:
f.write(response.content)
print('PDF created successfully!')
else:
print('Error:', response.json())
generate_pdf()
cURL
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"input_type": "html",
"html": "<h1>Hello World</h1><p>My first PDF!</p>"
}' \
--output output.pdf
PHP
<?php
$apiKey = getenv('PODPDF_API_KEY');
$data = array(
'input_type' => 'html',
'html' => '<h1>Hello World</h1><p>My first PDF!</p>'
);
$ch = curl_init('https://api.podpdf.com/quickjob');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'X-API-Key: ' . $apiKey,
'Content-Type: application/json'
));
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200) {
file_put_contents('output.pdf', $response);
echo "PDF created successfully!";
} else {
echo "Error: " . $response;
}
?>
Tips & Best Practices
✅ DO:
- Use
store: truewhen you need to share the PDF or download it later rather than streaming it - Download the stored PDF before the 1-hour expiry — after that it's gone
- Keep HTML under 5MB for best performance
- Include all CSS inline in your HTML
- Test with small documents first
- Use quickjob for simple documents (up to 25 pages or 25 images maximum)
- Handle timeout errors gracefully
- Store API keys in environment variables
- URL: Use
https://URLs only — public, fast-responding pages - URL: Handle
422URL fetch errors (the remote server may be down or slow) - Images: Use PNG or JPEG format, keep under 5MB each
- Images: Each image becomes one page (great for photo albums)
❌ DON'T:
- Don't rely on a stored
download_urlafter 1 hour — it will be expired and the file deleted after 30 days - Don't use external CSS/JS links (they won't load)
- Don't generate large reports with quickjob
- Don't forget to check the X-PDF-Pages header
- Don't ignore timeout responses
- Don't hardcode API keys in your code
- URL: Don't use
http://URLs or private/internal addresses — they are rejected - URL: Don't point to URLs that return PDF or other unsupported content types
- Images: Don't use unsupported formats (GIF, WebP, etc.)
- Images: Don't exceed 10000×10000 pixel dimensions
Image Fit Options
When converting images to PDF, you can control how images fit on the page:
contain(default) - Fit entire image on page, maintain aspect ratiocover- Fill entire page, may crop imagefill- Stretch to fill (may distort)none- Use natural image size
Example:
curl -X POST https://api.podpdf.com/quickjob \
-H "X-API-Key: $PODPDF_API_KEY" \
-F "input_type=image" \
-F "images=@photo.jpg" \
-F 'options={"fit":"cover"}' \
--output photo.pdf
Limits
| Limit | Value |
|---|---|
| Max pages | 25 per PDF |
| Max images | 25 per request (each image is a page) |
| Max input size | 5 MB |
| Timeout | 30 seconds |
| Rate limit | None per account on the standard paid plan; API Gateway still throttles (429) |
| Billing | $0.01 per successful PDF, deducted from your subscription allowance first, then your credits (Plans & Billing) |
See Limits for every limit across the API.
Next Steps
- Try Async Generation →
- Check Job Status →
- View your account in the dashboard
- See More Examples →