API Reference
Complete reference for every PDFPipe endpoint, including request parameters, response schemas, authentication, and error handling.
Base URL
All API requests are made to:
https://api.pdfpipe.dev/v1HTTPS is required for all requests. HTTP connections are rejected.
Authentication
Authenticate by including your API key in the Authorization header:
Authorization: Bearer pk_...API keys are created in the Dashboard. Each key can optionally be restricted to specific IP addresses. Keys start with pk_ and are shown only once at creation. An X-API-Key header is accepted as an alternative to Authorization: Bearer.
Convert a PDF
/v1/convertFetch a PDF from a public URL and return it in your chosen output format. Inline PDFs under 10 MB are processed synchronously (200). Attachment PDFs (headless browser), larger files, requests with async: true, and jobs that exceed your timeout window return a poll URL (202). Use returnMethod: "inline" to embed the converted payload in JSON when the response is synchronous (or on status when using ?returnMethod=inline).
URL_UNAUTHORIZED, URL_FORBIDDEN, or REDIRECT_TO_LOGIN. Token-in-URL links and redirect chains work. Hosts behind bot challenges (Cloudflare, Akamai) often do not.Request body
| Parameter | Type | Description |
|---|---|---|
urlrequired | string | The HTTP or HTTPS URL of the PDF. Max 2048 characters. |
format | string | Output format. One of: json, text, markdown, xml, csv, base64, binary, png, jpg, webp. Default: json. All 10 formats are available on every tier. |
pages | string | Optional page selection: "1", "1-5", "1,3,5", or "last" (combinable, 1-based, max 100 selected pages). Applies to json, text, markdown, xml, and csv. Image formats render the first selected page. Default: all pages. |
type | string | Force the fetch path. "inline" (plain HTTP fetch) or "attachment" (headless Chromium). Omit to auto-detect from the response headers (all tiers). |
async | boolean | Force asynchronous processing. Default: false. |
returnMethod | string | How the result is delivered when complete: "file" (presigned resultUrl) or "inline" (content + contentType in the JSON body, up to ~5.5 MB). Default: file. |
timeout | integer | Optional. Max seconds (1-60) to attempt synchronous processing before the job is queued. Omit for default behaviour. |
webhook | object | Optional. After async completion, POSTs the result to an HTTPS URL. Shape: { url: string, secret?: string }. If secret is omitted, one is generated and returned on the async response. Payloads are signed with HMAC-SHA256 (see Webhooks). |
Example request
{
"url": "https://example.com/report.pdf",
"format": "json",
"pages": "1-5",
"type": "inline",
"async": false,
"returnMethod": "file",
"timeout": 30,
"webhook": { "url": "https://example.com/hooks/pdfpipe" }
}Responses
With returnMethod: "inline", synchronous 200 responses include content (always a string; parse it for the json format), contentType, and for binary formats (png, jpg, webp, binary) contentEncoding: "base64". If inline delivery exceeds the size limit, the API returns a presigned URL instead and sets returnMethodFallback: true with returnMethodFallbackReason.
{
"requestId": "req_a1b2c3...",
"status": "complete",
"format": "json",
"pagesProcessed": 3,
"creditsUsed": 1,
"resultUrl": "https://pdfpipe-results.s3...",
"expiresAt": "2026-08-24T12:00:00.000Z"
}{
"requestId": "req_a1b2c3...",
"status": "complete",
"format": "json",
"pagesProcessed": 3,
"creditsUsed": 1,
"contentType": "application/json",
"content": "{\"pages\":[{\"pageNumber\":1,\"text\":\"...\"}],\"metadata\":{...},\"totalPages\":3,\"extractedAt\":\"...\"}",
"processingDurationMs": 1840
}{
"requestId": "req_a1b2c3...",
"status": "complete",
"format": "png",
"pagesProcessed": 40,
"creditsUsed": 1,
"resultUrl": "https://pdfpipe-results.s3...",
"expiresAt": "2026-08-24T12:00:00.000Z",
"returnMethodFallback": true,
"returnMethodFallbackReason": "Result size (7.2MB) exceeds inline limit (5.5MB)"
}{
"requestId": "req_a1b2c3...",
"status": "queued",
"pollUrl": "/v1/status/req_a1b2c3..."
}{
"requestId": "req_a1b2c3...",
"status": "queued",
"pollUrl": "/v1/status/req_a1b2c3...",
"webhook": {
"url": "https://example.com/hooks/pdfpipe",
"secret": "whsec_generated_or_provided"
},
"message": "Processing did not complete within 30s timeout. Result will be delivered via polling or webhook."
}{
"code": "NOT_A_PDF",
"message": "The URL did not return a PDF file",
"suggestion": "Verify the URL points directly to a PDF file",
"statusCode": 422
}Webhooks
When you include webhook on a convert request that ends up asynchronous, PDFPipe POSTs to your HTTPS URL when processing finishes. Webhooks are available on every tier. The async 202 response includes webhook.url and webhook.secret (generated if you did not supply secret; supplied secrets must be at least 16 characters). Verify deliveries with HMAC-SHA256 over the raw request body using that secret; the request includes X-PDFPipe-Signature: sha256=<hex> (plus X-PDFPipe-Request-Id and X-PDFPipe-Event). The payload carries event (conversion.complete or conversion.failed), requestId, status, format, pagesProcessed, creditsUsed, inline content when the job used returnMethod: "inline" and fits, an error object on failure, and a timestamp. Delivery is retried 3 times (2s, 8s, 32s) on non-2xx responses, with a 10-second timeout per attempt.
Batch Convert
/v1/convert/batchSubmit multiple PDF URLs for conversion in a single request. All jobs are processed asynchronously and each URL counts as one request against your quota. The maximum batch size depends on your tier: 5 (Free), 25 (Starter), 50 (Pro), 100 (Business). Larger batches are rejected with VALIDATION_ERROR.
Request body
| Parameter | Type | Description |
|---|---|---|
urlsrequired | array | Array of URL objects. Each must have a url field. Optional per-item: format, type, pages, and metadata (string key/value pairs stored with the request). |
defaults | object | Defaults applied to every URL that does not set its own: format and type. |
returnMethod | string | Batch-wide: "file" (default) or "inline". Applies to all URLs. |
webhook | object | Batch-wide { url, secret? }. One delivery per URL as each finishes. |
Example
{
"urls": [
{ "url": "https://example.com/invoice-1.pdf" },
{ "url": "https://example.com/invoice-2.pdf", "format": "text" },
{
"url": "https://example.com/invoice-3.pdf",
"type": "attachment",
"pages": "1-3",
"metadata": { "department": "finance" }
}
],
"defaults": { "format": "json" },
"returnMethod": "inline",
"webhook": { "url": "https://example.com/hooks/batch" }
}{
"batchId": "batch_a1b2c3d4e5f6...",
"requests": [
{ "requestId": "req_...", "url": "https://example.com/invoice-1.pdf", "status": "queued" },
{ "requestId": "req_...", "url": "https://example.com/invoice-2.pdf", "status": "queued" },
{ "requestId": "req_...", "url": "https://example.com/invoice-3.pdf", "status": "queued" }
],
"pollUrl": "/v1/batch/batch_a1b2c3d4e5f6..."
}Poll Request Status
/v1/status/:requestIdPoll the status of an asynchronous conversion request. Returns the current status and, when complete, a presigned result URL or inline content depending on query parameters.
Query parameters
| Parameter | Type | Description |
|---|---|---|
returnMethod | string | When complete, return embedded content and contentType (same semantics as convert). Use inline. Default behaviour matches file. |
Response
Status values: pending, processing, complete, failed. Once your plan's retention window has passed, a complete request returns resultUrl: null and expired: true. A failed request includes an error object with code, message, and suggestion.
{
"requestId": "req_a1b2c3...",
"status": "complete",
"format": "json",
"type": "inline",
"detectedType": "inline",
"pagesProcessed": 3,
"creditsUsed": 1,
"resultUrl": "https://pdfpipe-results.s3...",
"expiresAt": "2026-08-24T12:00:00.000Z",
"processingDurationMs": 4230,
"createdAt": "2026-08-23T11:00:00.000Z",
"updatedAt": "2026-08-23T11:00:04.230Z"
}{
"requestId": "req_a1b2c3...",
"status": "complete",
"format": "json",
"type": "inline",
"detectedType": "inline",
"pagesProcessed": 3,
"creditsUsed": 1,
"contentType": "application/json",
"content": "{\"pages\":[{\"pageNumber\":1,\"text\":\"...\"}],...}",
"processingDurationMs": 4230,
"createdAt": "2026-08-23T11:00:00.000Z",
"updatedAt": "2026-08-23T11:00:04.230Z"
}{
"requestId": "req_a1b2c3...",
"status": "failed",
"format": "json",
"type": "attachment",
"error": {
"code": "ATTACHMENT_DOWNLOAD_FAILED",
"message": "The headless browser could not download the PDF from this URL",
"suggestion": "The page may require JavaScript interaction or authentication"
},
"createdAt": "2026-08-23T11:00:00.000Z",
"updatedAt": "2026-08-23T11:00:08.000Z"
}Batch Status
/v1/batch/:batchIdCheck the overall status of a batch job and get individual request results.
{
"batchId": "batch_a1b2c3d4e5f6...",
"total": 3,
"completed": 2,
"failed": 0,
"pending": 1,
"requests": [
{ "requestId": "req_...", "status": "complete", "resultUrl": "https://..." },
{ "requestId": "req_...", "status": "complete", "resultUrl": "https://..." },
{ "requestId": "req_...", "status": "processing" }
]
}Usage
/v1/usageReturns your current month's usage statistics.
{
"tier": "starter",
"month": "2026-08",
"requestsUsed": 47,
"requestsLimit": 500,
"remaining": 453,
"testRequestsUsed": 2,
"testRequestsLimit": 10,
"testRequestsRemaining": 8,
"resetsAt": "2026-09-01T00:00:00.000Z"
}API Keys
/v1/keysList all API keys for your account.
{
"keys": [
{
"keyId": "a1b2c3d4e5f6",
"name": "Production key",
"prefix": "pk_a1b2c",
"allowedIps": ["203.0.113.10"],
"createdAt": "2026-01-15T10:00:00.000Z",
"revokedAt": null
}
]
}/v1/keysCreate a new API key. The full key value is returned only in this response.
{
"name": "Production key",
"allowedIps": ["203.0.113.10"]
}{
"keyId": "a1b2c3d4e5f6",
"name": "Production key",
"prefix": "pk_a1b2c",
"key": "pk_a1b2c3d4...full_key_here"
}/v1/keys/:keyIdUpdate the IP allowlist on an existing API key. Pass an array of IP addresses to restrict the key, or an empty array / omit the field to allow all IPs. Cannot be used on revoked keys.
{
"allowedIps": ["203.0.113.10", "198.51.100.0"]
}{
"keyId": "a1b2c3d4e5f6",
"name": "Production key",
"prefix": "pk_a1b2c",
"allowedIps": ["203.0.113.10", "198.51.100.0"],
"createdAt": "2026-01-15T10:00:00.000Z"
}/v1/keys/:keyIdRevoke an API key. Returns 204 No Content on success.
Error Codes
Errors return a JSON body with a code, a human-readable message, and the statusCode. Errors about the source PDF or URL also carry a suggestion.
{
"code": "VALIDATION_ERROR",
"message": "url: URL must use http or https protocol",
"statusCode": 400
}Request and account errors
| Code | HTTP | Description |
|---|---|---|
UNAUTHORIZED | 401 | Invalid or missing API key. |
VALIDATION_ERROR | 400 | Request body failed validation (bad URL, unknown format, invalid pages string, batch too large). Check the message for specifics. |
NOT_FOUND | 404 | The requested request, batch, or key does not exist. |
FORBIDDEN | 403 | Insufficient permissions for this action. |
ACCOUNT_SUSPENDED | 403 | Account is suspended. Contact support. |
FILE_TOO_LARGE | 413 | PDF exceeds your tier's file size limit. |
RATE_LIMIT_EXCEEDED | 429 | Monthly request limit exceeded (Free, Starter, Pro). Business bills overage instead. |
SSRF_BLOCKED | 403 | URL points to a blocked private or reserved address. |
ATTACHMENT_LIMIT_REACHED | 403 | Monthly attachment PDF cap reached (Free tier: 25/month). |
FORMAT_NOT_ALLOWED | 403 | Output format not available on your tier (all current tiers include all 10 formats). |
PAYLOAD_TOO_LARGE | 413 | Request body larger than 64 KB. |
PROCESSING_ERROR | 500 | Unclassified processing failure. Retry or contact support. |
INTERNAL_ERROR | 500 | Unexpected server error. |
EMAIL_NOT_VERIFIED | 403 | Verify your email address before using the API. |
API_KEY_LIMIT_REACHED | 409 | Maximum number of API keys reached for this user. |
MEMBER_LIMIT_REACHED | 409 | Account team member limit reached. |
CHAT_NOT_AVAILABLE | 403 | Dashboard AI Help is available on Starter, Pro, and Business only. |
CHAT_RATE_LIMIT_EXCEEDED | 429 | Dashboard AI Help daily limit reached (50 questions per user per day). |
Source URL and PDF errors
Returned synchronously with the HTTP status shown, or inside the error object of a failed status/batch entry when the job ran asynchronously. Each includes a suggestion.
| Code | HTTP | Description |
|---|---|---|
NOT_A_PDF | 422 | The URL did not return a PDF file (for example an HTML page). Verify the URL points directly to a PDF. |
REDIRECT_TO_LOGIN | 422 | The URL redirected to a login page instead of serving a PDF. Login-walled PDFs are not supported. |
PDF_PASSWORD_PROTECTED | 422 | The PDF is password-protected and cannot be extracted. |
PDF_CORRUPTED | 422 | The PDF appears to be corrupted or invalid. |
PDF_NO_TEXT_LAYER | 422 | The PDF contains scanned images only. Text extraction is not available for image-only PDFs (no OCR). |
PDF_EMPTY | 422 | The PDF has no pages or content. |
URL_NOT_FOUND | 404 | The source URL returned 404. |
URL_UNAUTHORIZED | 401 | The source URL returned 401. PDFPipe does not send credentials to the source. |
URL_FORBIDDEN | 403 | The source URL returned 403. The host may require authentication or block automated clients. |
URL_UNREACHABLE | 502 | Could not connect to the source host (DNS failure, connection refused, or reset). |
URL_SERVER_ERROR | 502 | The source host returned a 5xx error. |
URL_HTTP_ERROR | 502 | The source host returned another non-success HTTP status. |
SSL_CERTIFICATE_ERROR | 502 | The source host's TLS certificate is invalid or expired. |
URL_TIMEOUT | 504 | The source host took too long to respond. |
PROCESSING_TIMEOUT | 504 | Processing took longer than the maximum allowed time. Try a smaller file or a page range. |
ATTACHMENT_DOWNLOAD_FAILED | 502 | The headless browser could not download the PDF. The page may need JavaScript interaction or a login. |
Rate Limits
Quotas are applied per account on a monthly cycle that resets on the 1st. When you exceed your limit on Free, Starter, or Pro, requests return 429 RATE_LIMIT_EXCEEDED. Business accounts are not capped; requests beyond 20,000 are billed at the overage rate. The Free tier also allows at most 25 attachment (headless browser) PDFs per month. Webhooks, page ranges, batch, and auto-detection are available on all tiers.
Dashboard AI Help is separate from PDF conversion quotas: paid plans (Starter+) include an in-dashboard assistant (Claude) at /dashboard/chat with documentation-grounded answers. Free tier does not include AI Help. Limit: 50 questions per user per calendar day (UTC). See also Getting Started.
| Tier | Requests/mo | Max File | Formats | Batch Size | API Keys | URL Expiry | AI Help |
|---|---|---|---|---|---|---|---|
| Free | 300 | 5 MB | All 10 | 5 | 2 | 1 hour | No |
| Starter | 1,000 | 10 MB | All 10 | 25 | 5 | 24 hours | Yes (50/day) |
| Pro | 5,000 | 25 MB | All 10 | 50 | 10 | 7 days | Yes (50/day) |
| Business | 20,000 | 50 MB | All 10 | 100 | 25 | 30 days | Yes (50/day) |