API Reference
Send us a PDF, we send back a summary. One endpoint, a multipart file upload, a JSON response — built to drop into a backend job or a document pipeline in a few minutes.
Overview
The PDF Summarizer API takes a PDF file as input — not a block of text you have to paste or extract yourself — and returns a clean, structured summary. Under the hood it runs the file through a multi-method text-extraction pipeline (including OCR for scanned pages) before handing the extracted content to an LLM.
Base URL
Key capabilities
- Real PDF input — upload the file directly; extraction, cleanup, and language detection happen server-side
- Scanned document support — pages with no embedded text fall back to OCR automatically
- Output control — short, medium, long, or extensive; paragraph, bullets, or mixed
- Writing styles — professional, casual, academic, technical, simple
- Keyword extraction — optionally pull 5–7 key terms out of the document
- Streaming — real-time token-by-token summary output via Server-Sent Events, once extraction completes
Authentication
Every request needs a valid API key in the Authorization header, using the Bearer scheme.
Authorization: Bearer YOUR_API_KEY
API key format
Keys look like sk_live_xxxxxxxxxxxxxxxxxxxxxxxx in production (or sk_test_... in a test environment). Treat your key like a password — anyone holding it can spend your monthly quota.
Quick Start
POST a PDF to /api/summarize as multipart/form-data — the file goes in a field named file, everything else is an optional form field alongside it.
# -F sends the file and form fields as multipart/form-data curl -X POST https://pdf-summarize.com/api/summarize \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "[email protected]" \ -F "length=medium"
const summarizePdf = async (file) => { const form = new FormData(); form.append('file', file); form.append('length', 'medium'); const res = await fetch('https://pdf-summarize.com/api/summarize', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, body: form // no Content-Type header — the browser sets the multipart boundary }); const data = await res.json(); console.log(data.summary); }; // <input type="file" id="pdf" accept="application/pdf"> summarizePdf(document.getElementById('pdf').files[0]);
import requests with open("report.pdf", "rb") as f: response = requests.post( "https://pdf-summarize.com/api/summarize", headers={"Authorization": "Bearer YOUR_API_KEY"}, files={"file": f}, data={"length": "medium"} ) data = response.json() print(data["summary"])
<?php $ch = curl_init('https://pdf-summarize.com/api/summarize'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_API_KEY'], CURLOPT_POSTFIELDS => [ 'file' => new CURLFile('report.pdf', 'application/pdf'), 'length' => 'medium' ] ]); $data = json_decode(curl_exec($ch), true); echo $data['summary'];
Example response
{
"success": true,
"summary": "A concise summary of the document.",
"keywords": null,
"page_count": 12,
"original_length": 18420,
"summary_length": 612,
"processing_time": 4.31,
"requests_remaining": 48
}
File Requirements & OCR
Extraction runs through a cascade of methods so a single bad PDF doesn't fail the whole request: an embedded-text parser is tried first, then a couple of lower-level fallbacks, and if the document looks like it's mostly scanned images, OCR kicks in automatically. You never choose a method — the API picks whatever gets the most usable text out of the file you sent.
| Limit | Default | Notes |
|---|---|---|
| Max file size | 20 MB | Larger files return 413. |
| Max pages | 300 | Longer documents return 400 before any extraction is attempted. |
| File type | application/pdf only | Checked by both MIME type and file extension. |
422 rather than a low-quality summary.Endpoint
The API exposes a single endpoint for uploading and summarizing a PDF.
Accepts a PDF file plus optional form fields, returns a structured JSON summary.
Request headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer YOUR_API_KEY |
Required |
| Content-Type | multipart/form-data |
Set automatically by your HTTP client when you send a file |
Request Parameters
Send as multipart/form-data. Only file is required — everything else has a sensible default.
| Field | Type | Default | Description |
|---|---|---|---|
| file required |
file | — | The PDF to summarize. Max 20MB, max 300 pages. |
| length optional |
string | medium |
Output length.short — 2–3 sentencesmedium — 1 paragraphlong — 2–3 paragraphsextensive — 4–5 paragraphs
|
| style optional |
string | professional |
Writing style.professional casual academic technical simple
|
| format optional |
string | paragraph |
Output format.paragraph — flowing prosebullets — bullet point listmixed — short paragraph + key bullets
|
| outputLang optional |
string | auto |
Output language. auto matches the document's language, or pass any ISO 639-1 code:en fr es de it pt ar zh ja ru …
|
| includeKeywords optional |
boolean | false |
Send as "true" to extract 5–7 key terms into the keywords field. |
| max_length optional |
integer | — | Hard cap on the summary length in characters. Overrides length when set. |
Response Format
All responses are JSON. A successful request returns HTTP 200.
Response fields
| Field | Type | Description |
|---|---|---|
| success | boolean | Always true on success. |
| summary | string | The generated summary text. |
| keywords | string | null | null unless includeKeywords was true. |
| page_count | integer | null | Page count of the uploaded PDF, when detectable. |
| original_length | integer | Character count of the text extracted from the PDF. |
| summary_length | integer | Character count of the summary. |
| processing_time | float | Total time in seconds, extraction included. |
| requests_remaining | integer | Remaining requests in the current billing period. -1 on unlimited plans. |
Streaming (SSE)
Append ?stream=1 to get the summary as Server-Sent Events instead of a single JSON blob. Extraction still happens up front (it isn't itself streamed) — once the document's text is ready, summary tokens stream back as they're generated.
Event format
The stream emits data: lines, each carrying a JSON payload with a type field:
| type | Fields | Description |
|---|---|---|
summary | content | Incremental text token. Append these to build the full summary. |
done | summary | Stream complete. summary holds the full text. |
error | error | Something failed. The stream closes right after. |
The stream always ends with a bare data: [DONE] line — use it to know the connection closed, independent of whether the last real event was done or error.
JavaScript streaming example
const streamSummary = async (file, onChunk, onDone) => { const form = new FormData(); form.append('file', file); const res = await fetch('https://pdf-summarize.com/api/summarize?stream=1', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, body: form }); const reader = res.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const lines = decoder.decode(value).split('\n'); for (const line of lines) { if (!line.startsWith('data: ')) continue; if (line === 'data: [DONE]') continue; const event = JSON.parse(line.slice(6)); if (event.type === 'summary') onChunk(event.content); if (event.type === 'done') onDone(event.summary); } } }; // Usage streamSummary( document.getElementById('pdf').files[0], chunk => process.stdout.write(chunk), full => console.log('\n\nDone. Full summary:', full) );
Errors
Error responses include an error field describing what went wrong. HTTP status codes follow standard conventions.
{
"error": "Invalid API key"
}
| Status | Error | Description |
|---|---|---|
| 400 | Invalid request | Missing file, wrong MIME type, malformed upload, or the PDF exceeds the page limit. |
| 401 | Unauthorized | Missing or invalid API key. Check the Authorization header. |
| 403 | Account suspended | Contact support. |
| 405 | Method not allowed | Only POST requests are accepted. |
| 413 | File too large | Exceeds the 20MB size limit. |
| 422 | Unprocessable PDF | Extraction failed or found under 50 characters of usable text (scanned with no OCR-readable content, encrypted, or empty). |
| 429 | Rate limit exceeded | Either the 60-requests/minute burst limit or your monthly quota. Check Retry-After on the burst case. |
| 500 | Server error | All summarization providers were unavailable. Retry with backoff; if it persists, contact support. |
cURL Examples
Complete examples using curl — available on Linux, macOS, and Windows.
Standard request
curl -X POST https://pdf-summarize.com/api/summarize \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "[email protected]" \ -F "length=short" \ -F "style=professional" \ -F "format=paragraph" \ -F "outputLang=en" \ -F "includeKeywords=true"
With streaming
curl -X POST "https://pdf-summarize.com/api/summarize?stream=1" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "[email protected]" \ --no-buffer # Output (streamed): # data: {"type":"summary","content":"The report"} # data: {"type":"summary","content":" outlines"} # ... # data: {"type":"done","summary":"Full summary text."} # data: [DONE]
JavaScript Examples
Works in browsers and Node.js (v18+, for FormData/fetch). For older Node, install form-data and node-fetch.
class PdfSummarizerClient { constructor(apiKey) { this.apiKey = apiKey; this.baseURL = 'https://pdf-summarize.com/api'; } async summarize(file, options = {}) { const form = new FormData(); form.append('file', file); for (const [k, v] of Object.entries(options)) form.append(k, v); const res = await fetch(`${this.baseURL}/summarize`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}` }, body: form }); if (!res.ok) { const err = await res.json(); throw new Error(err.error); } return res.json(); } } // Usage const client = new PdfSummarizerClient('YOUR_API_KEY'); const result = await client.summarize( document.getElementById('pdf').files[0], { length: 'medium', style: 'professional', includeKeywords: 'true' } ); console.log('Summary:', result.summary); console.log('Pages:', result.page_count); console.log('Compression:', Math.round((1 - result.summary_length / result.original_length) * 100) + '%');
// npm install axios form-data const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const client = axios.create({ baseURL: 'https://pdf-summarize.com/api', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); async function summarize(path, options = {}) { const form = new FormData(); form.append('file', fs.createReadStream(path)); for (const [k, v] of Object.entries(options)) form.append(k, v); const { data } = await client.post('/summarize', form, { headers: form.getHeaders() }); return data; } summarize('report.pdf', { length: 'short' }) .then(r => console.log(r.summary)) .catch(e => console.error(e.response?.data?.error));
Python Examples
Requires Python 3.7+ and the requests library (pip install requests).
import requests class PdfSummarizerClient: BASE_URL = "https://pdf-summarize.com/api" def __init__(self, api_key: str): self.session = requests.Session() self.session.headers.update({"Authorization": f"Bearer {api_key}"}) def summarize(self, path: str, **fields) -> dict: with open(path, "rb") as f: response = self.session.post( f"{self.BASE_URL}/summarize", files={"file": f}, data=fields ) response.raise_for_status() return response.json() # Usage client = PdfSummarizerClient("YOUR_API_KEY") result = client.summarize( "report.pdf", length="medium", style="academic", outputLang="en", includeKeywords="true" ) print(f"Summary: {result['summary']}") print(f"Pages: {result['page_count']}") print(f"Reduced by {100 - round(result['summary_length'] / result['original_length'] * 100)}%")
# pip install httpx import asyncio import httpx async def summarize(api_key: str, path: str, **fields) -> dict: async with httpx.AsyncClient() as client: with open(path, "rb") as f: response = await client.post( "https://pdf-summarize.com/api/summarize", headers={"Authorization": f"Bearer {api_key}"}, files={"file": f}, data=fields ) response.raise_for_status() return response.json() # Usage result = asyncio.run(summarize( "YOUR_API_KEY", "report.pdf", length="short" )) print(result["summary"])
PHP Examples
Works with PHP 7.4+. Uses cURL (enabled by default in most PHP installations).
<?php class PdfSummarizerClient { private string $apiKey; private string $baseUrl = 'https://pdf-summarize.com/api'; public function __construct(string $apiKey) { $this->apiKey = $apiKey; } public function summarize(string $pdfPath, array $fields = []): array { $ch = curl_init($this->baseUrl . '/summarize'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $this->apiKey], CURLOPT_POSTFIELDS => array_merge( ['file' => new CURLFile($pdfPath, 'application/pdf')], $fields ), CURLOPT_TIMEOUT => 120 ]); $body = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { $err = json_decode($body, true); throw new \RuntimeException($err['error'] ?? 'API error'); } return json_decode($body, true); } } // Usage $client = new PdfSummarizerClient('YOUR_API_KEY'); $result = $client->summarize('report.pdf', [ 'length' => 'medium', 'includeKeywords' => 'true' ]); echo $result['summary'] . PHP_EOL; echo $result['page_count'] . ' pages' . PHP_EOL;
<?php // composer require guzzlehttp/guzzle use GuzzleHttp\Client; $client = new Client([ 'base_uri' => 'https://pdf-summarize.com/api/', 'headers' => ['Authorization' => 'Bearer YOUR_API_KEY'] ]); $response = $client->post('summarize', [ 'multipart' => [ ['name' => 'file', 'contents' => fopen('report.pdf', 'r')], ['name' => 'length', 'contents' => 'medium'], ['name' => 'includeKeywords', 'contents' => 'true'] ] ]); $data = json_decode($response->getBody(), true); echo $data['summary'];
Plans & Limits
Request quotas are enforced per calendar month and reset on the 1st. A separate burst limit of 60 requests/minute applies to every plan.
| Plan | PDF summaries / month | API access |
|---|---|---|
| Free | 50 | Full API access |
| Professional — $9/mo | 1,000 | Full API access + priority processing |
| Business — $19/mo | Unlimited | Full API access + dedicated support + SLA 99.9% |
Past your monthly quota, the API returns HTTP 429. Every successful response includes requests_remaining so you can track usage without a separate call.
Changelog
- Multipart PDF upload endpoint (
/api/summarize) - Multi-method text extraction with automatic OCR fallback for scanned pages
- Real-time SSE streaming, once extraction completes
- 5 writing styles, 4 output formats, auto-detected output language
- API key authentication, free tier with no card required
Have a feature request or found a bug? Contact us — we read every message.