{"openapi":"3.1.0","info":{"title":"PennyPDF API","version":"1.0.0","description":"Public REST API for PennyPDF. Pay pennies per task, never a subscription. Coins never expire.\n\n**Authentication:** Bearer token. Generate keys at https://pennypdf.com/dashboard.\n\n**Pricing:** draws from the same coin balance as the web UI — see https://pennypdf.com/pricing.\n\n**Client-side-only tools (not available via API):** merge, split, extract-pages, remove-pages, rotate, organize, jpg-to-pdf, pdf-to-jpg, pdf-to-text, grayscale, page-numbers, crop, redact, remove-metadata. These run entirely in the browser to preserve user privacy; no server endpoint exists.","contact":{"name":"PennyPDF Support","url":"https://pennypdf.com/support","email":"support@pennypdf.com"},"license":{"name":"Proprietary","url":"https://pennypdf.com/legal/terms"}},"servers":[{"url":"https://api.pennypdf.com","description":"Production"},{"url":"http://localhost:4000","description":"Local dev"}],"tags":[{"name":"compress","description":"Shrink a PDF with low/medium/strong presets."},{"name":"pdf-to-word","description":"Convert PDF to editable .docx preserving layout."},{"name":"word-to-pdf","description":"Convert .docx / .doc to PDF via LibreOffice headless."},{"name":"pdf-to-excel","description":"Extract tables from a PDF into an .xlsx workbook."},{"name":"ocr","description":"Run OCR over a scanned PDF. Base 3 coins for up to 50 pages; +1 per additional 50."},{"name":"html-to-pdf","description":"Render a self-contained HTML document to PDF via Chromium."},{"name":"repair","description":"Attempt to fix corrupted or malformed PDFs."},{"name":"protect","description":"Add a user password to a PDF."},{"name":"unlock","description":"Remove a known password from a PDF."},{"name":"jobs","description":"Poll async job status."}],"paths":{"/v1/compress":{"post":{"tags":["compress"],"summary":"Compress PDF","description":"Shrink a PDF with low/medium/strong presets. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"compress_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{"level":"medium"}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/compress/sync":{"post":{"tags":["compress"],"summary":"Compress PDF (synchronous)","description":"Runs compress pdf inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"compress_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/pdf-to-word":{"post":{"tags":["pdf-to-word"],"summary":"PDF to Word","description":"Convert PDF to editable .docx preserving layout. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"pdf_to_word_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/pdf-to-word/sync":{"post":{"tags":["pdf-to-word"],"summary":"PDF to Word (synchronous)","description":"Runs pdf to word inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"pdf_to_word_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/vnd.openxmlformats-officedocument.wordprocessingml.document":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/word-to-pdf":{"post":{"tags":["word-to-pdf"],"summary":"Word to PDF","description":"Convert .docx / .doc to PDF via LibreOffice headless. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"word_to_pdf_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{"format":"docx"}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/word-to-pdf/sync":{"post":{"tags":["word-to-pdf"],"summary":"Word to PDF (synchronous)","description":"Runs word to pdf inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"word_to_pdf_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/pdf-to-excel":{"post":{"tags":["pdf-to-excel"],"summary":"PDF to Excel","description":"Extract tables from a PDF into an .xlsx workbook. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"pdf_to_excel_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/pdf-to-excel/sync":{"post":{"tags":["pdf-to-excel"],"summary":"PDF to Excel (synchronous)","description":"Runs pdf to excel inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"pdf_to_excel_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ocr":{"post":{"tags":["ocr"],"summary":"OCR (Searchable PDF)","description":"Run OCR over a scanned PDF. Base 3 coins for up to 50 pages; +1 per additional 50. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"ocr_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{"language":"eng","pages":20}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ocr/sync":{"post":{"tags":["ocr"],"summary":"OCR (Searchable PDF) (synchronous)","description":"Runs ocr (searchable pdf) inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"ocr_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/html-to-pdf":{"post":{"tags":["html-to-pdf"],"summary":"HTML to PDF","description":"Render a self-contained HTML document to PDF via Chromium. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"html_to_pdf_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/html-to-pdf/sync":{"post":{"tags":["html-to-pdf"],"summary":"HTML to PDF (synchronous)","description":"Runs html to pdf inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"html_to_pdf_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/repair":{"post":{"tags":["repair"],"summary":"Repair PDF","description":"Attempt to fix corrupted or malformed PDFs. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"repair_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/repair/sync":{"post":{"tags":["repair"],"summary":"Repair PDF (synchronous)","description":"Runs repair pdf inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"repair_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/protect":{"post":{"tags":["protect"],"summary":"Password-protect PDF","description":"Add a user password to a PDF. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"protect_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{"password":"hunter2"}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/protect/sync":{"post":{"tags":["protect"],"summary":"Password-protect PDF (synchronous)","description":"Runs password-protect pdf inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"protect_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/unlock":{"post":{"tags":["unlock"],"summary":"Unlock PDF","description":"Remove a known password from a PDF. Returns a job id — poll GET /v1/jobs/{id} for the result URL.","operationId":"unlock_async","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"},"example":{"input_url":"https://example.com/my.pdf","options":{"password":"hunter2"}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string","description":"JSON-encoded options"}},"required":["file"]}}}},"responses":{"202":{"description":"Job queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobQueued"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/unlock/sync":{"post":{"tags":["unlock"],"summary":"Unlock PDF (synchronous)","description":"Runs unlock pdf inline and streams the result back. Times out after 60s — use the async endpoint for larger files.","operationId":"unlock_sync","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"options":{"type":"string"}},"required":["file"]}}}},"responses":{"200":{"description":"Processed output file.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Job-Id":{"schema":{"type":"string"}},"X-Coins-Spent":{"schema":{"type":"integer"}}}},"400":{"description":"Bad request — malformed body or missing input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient coins. Purchase a pack at https://pennypdf.com/pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCoinsError"}}}},"413":{"description":"Payload too large for this tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error — try again or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Sync job timeout — use async endpoint and poll /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/jobs/{id}":{"get":{"tags":["jobs"],"summary":"Get job status","description":"Poll this endpoint to check progress. When status=done, output_url is included.","operationId":"get_job","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"401":{"description":"Missing, invalid, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found or not owned by this API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"pk_live_*","description":"API key from https://pennypdf.com/dashboard. Format: pk_live_<32 chars>"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"},"hint":{"type":"string"}}},"InsufficientCoinsError":{"type":"object","required":["error","required"],"properties":{"error":{"type":"string","example":"Insufficient coins"},"code":{"type":"string","example":"insufficient"},"required":{"type":"integer","example":2},"tool":{"type":"string"},"top_up_url":{"type":"string","format":"uri"}}},"JobRequest":{"type":"object","properties":{"input_url":{"type":"string","format":"uri","description":"HTTPS URL we fetch the input from."},"input_base64":{"type":"string","description":"Base64-encoded input bytes (for inline uploads)."},"options":{"type":"object","additionalProperties":true}}},"JobQueued":{"type":"object","required":["job_id","status"],"properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued"]},"tool":{"type":"string"},"coin_cost":{"type":"integer"},"poll_url":{"type":"string"},"balance_after":{"type":"integer"}}},"Job":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"tool":{"type":"string"},"status":{"type":"string","enum":["queued","running","done","error","cancelled"]},"coin_cost":{"type":"integer"},"input_bytes":{"type":"integer","nullable":true},"output_bytes":{"type":"integer","nullable":true},"duration_ms":{"type":"integer","nullable":true},"output_url":{"type":"string","format":"uri","nullable":true},"output_url_expires_in":{"type":"integer","description":"Seconds until output_url expires."},"error_message":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"started_at":{"type":"string","format":"date-time","nullable":true},"completed_at":{"type":"string","format":"date-time","nullable":true}}}}},"x-examples":{"curl-compress-async":"# 1) Queue an async compress job:\ncurl -X POST https://api.pennypdf.com/v1/compress \\\n  -H \"Authorization: Bearer $PENNYPDF_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"input_url\":\"https://example.com/big.pdf\",\"options\":{\"level\":\"medium\"}}'\n\n# 2) Poll until done:\ncurl https://api.pennypdf.com/v1/jobs/<JOB_ID> \\\n  -H \"Authorization: Bearer $PENNYPDF_KEY\"\n\n# 3) Follow output_url to download the result.","curl-ocr-sync":"curl -X POST https://api.pennypdf.com/v1/ocr/sync \\\n  -H \"Authorization: Bearer $PENNYPDF_KEY\" \\\n  -F \"file=@scan.pdf\" \\\n  -F 'options={\"language\":\"eng\",\"pages\":10}' \\\n  --output searchable.pdf"}}