API Overview
Use this page as your checklist before calling any endpoint: authenticate requests, respect rate limits, understand credit usage, and set up callbacks/idempotency headers for reliability.
Authentication
- Generate tokens from Dashboard → Keys.
- Send them as bearer tokens:
Authorization: Bearer pl_xxx. - Tokens inherit the permissions of the account that created them.
Example request
curl -X GET https://api.pdfloom.com/v1/convert/url \
-H "Authorization: Bearer YOUR_API_TOKEN"
Never embed API tokens in browser code or public repositories. Rotate them if they leak.
Rate Limits
Authenticated capture requests (those with an auth block) are throttled by default at 30 requests/minute over 60 seconds. Regular requests are not globally throttled unless configured by your deployment.
- Use backoff/retry when you receive HTTP 429.
- Long-running conversions count toward the limit as soon as the request starts.
Rate Limit Headers
All API responses include rate limit information in headers, allowing you to monitor your usage proactively:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1738483200
X-RateLimit-Limit: Maximum requests allowed in the current windowX-RateLimit-Remaining: Number of requests remaining in the current windowX-RateLimit-Reset: Unix timestamp when the rate limit resets
When rate limited (HTTP 429), you'll also receive a Retry-After header indicating how many seconds to wait:
Retry-After: 30
Best Practice: Monitor the X-RateLimit-Remaining header and implement exponential backoff before hitting the rate limit. This prevents service disruption and improves reliability.
Response format
Successful responses follow this schema:
{
"success": true,
"response": "https://pdfloom-processed.s3.amazonaws.com/generated/pdf/<uuid>.pdf?...",
"fileSize": 6139
}
responseis a signed S3 URL that expires in 30 minutes. Download or proxy it immediately.fileSizehelps you predict credit consumption (~1 credit per 5 MB).
Errors return HTTP 4xx/5xx with success: false and a descriptive message:
{
"success": false,
"message": "Insufficient credits.",
"status": 402
}
See Errors for common codes (402, 409, 422).
Credits
- Each request deducts
ceil(fileSize / (5 * 1024 * 1024))credits (~1 credit per 5 MB). - Credit use is logged per request in the dashboard.
- Generator failures do not charge credits; you'll receive
success: false.
You can check your current credit balance at any time using the GET /v1/account/credits endpoint without consuming credits.
Service Resilience
Circuit Breaker
PDF-Loom implements a circuit breaker pattern to protect against cascading failures when the generator service is degraded or experiencing issues.
How it works:
- If 10 generator failures occur within 60 seconds, the circuit "opens"
- While open (30 seconds), all conversion requests immediately return
503 Service Unavailablewithout attempting the conversion - After 30 seconds, the circuit enters a "half-open" state where one test request is allowed
- If the test succeeds, the circuit closes and normal operation resumes
- If the test fails, the circuit remains open for another 30 seconds
503 Response:
{
"success": false,
"error_code": "SERVICE_UNAVAILABLE",
"message": "Generator service is temporarily unavailable. Please try again later.",
"retry_after": 30
}
The circuit breaker prevents overwhelming a degraded service and ensures faster failure responses during outages. When you receive a 503 with SERVICE_UNAVAILABLE, wait for the duration specified in the retry_after field before retrying.
Handling 503 Errors: Implement exponential backoff when receiving 503 responses. The circuit breaker is designed to protect both the service and your application from cascading failures.
Optional fields & callbacks
| Field | Applies to | Description |
|---|---|---|
callback_url | All convert/screenshot endpoints | HTTPS URL notified when processing finishes (respond with 2xx to acknowledge). |
engine_options.timeout | URL & HTML endpoints | Override the default 30s timeout. |
engine_options.waitForNetworkToIdle | URL & HTML endpoints | Wait until network is idle before snapshotting. |
assets | /v1/convert/html, /v1/screenshot/html | Array of { filename, content } attachments referenced by your HTML. |
options.* | All endpoints | See the Convert/Screenshot references for per-feature options (format, margins, clip, etc.). |
auth | URL & HTML convert/screenshot | Use bearer/header/cookie/basic/form/script auth to capture behind login. HTTPS required; see Authenticated capture. Auth calls are rate-limited (default 30/min). |
Callbacks must use HTTPS. Callback retries are not automatic—log the attempt if you need auditing.
Idempotency
Provide an Idempotency-Key header for every request (UUID recommended). We store keys for five minutes:
- Reusing a key while a request is running returns HTTP 409 with
message: "This idempotency key is already being processed." - Reusing a key after completion returns the cached response and avoids double-charging credits.
-H "Idempotency-Key: 6b2f7c5a-1cb2-42b2-a099-42f1e2f729c7"
Webhooks
- Paddle webhooks hit
/paddle/webhookand are signature-verified. - Your own conversion callbacks (
callback_url) receive the original payload plus result metadata. If you configureCALLBACK_SIGNING_SECRET, verify theX-PDFLoom-Signatureheader in your handler.
Need more detail? Jump to Authentication, Errors, or the Convert and Screenshot references.
SDK quickstart
Each SDK example should include:
Authorization: Bearer ...header with your API tokenContent-Type: application/jsonIdempotency-Keyheader- Optional
callback_urlwhere you acknowledge receipt with a 2xx response
await fetch('https://api.pdfloom.com/v1/convert/html', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFLOOM_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
content: '<html>...</html>',
callback_url: 'https://example.com/conversion-webhook',
options: { format: 'A4' },
}),
})
import os, uuid, requests
resp = requests.post(
'https://api.pdfloom.com/v1/convert/url',
headers={
'Authorization': f"Bearer {os.environ['PDFLOOM_TOKEN']}",
'Content-Type': 'application/json',
'Idempotency-Key': str(uuid.uuid4()),
},
json={
'url': 'https://example.com',
'callback_url': 'https://example.com/webhook',
},
)
print(resp.json())
<?php
$client = new GuzzleHttp\Client();
$response = $client->post('https://api.pdfloom.com/v1/convert/url', [
'headers' => [
'Authorization' => 'Bearer '.getenv('PDFLOOM_TOKEN'),
'Content-Type' => 'application/json',
'Idempotency-Key' => uniqid('req_', true),
],
'json' => [
'url' => 'https://example.com',
'callback_url' => 'https://example.com/webhook',
],
]);