Build a PDF extraction workflow with a schema, resumable upload, asynchronous job, webhook, and structured row query.
PdfParse Team·
PDF extraction is hard to get right. Correctness, data normalization, automation — miss any of them and the failure shows up downstream, in a back office or a pipeline that trusted the data.
Many extraction platforms answer this with classifiers and keyword rules: some correctness, plus a proprietary learning curve. PdfParse takes a different route. Extracted data lands in SQLite databases, so types and relationships are enforced by the database itself — and anyone with existing SQL knowledge can inspect, filter, and verify the result instead of learning a vendor's rule language.
Still, an extraction API that only returns page text is a text dump with extra steps. A useful one accepts a schema, uploads files safely, runs extraction outside the request cycle, and hands back structured rows.
This guide builds exactly that flow with the PdfParse REST API, extracting invoice fields and repeating line items along the way.
Six operations, start to finish:
Create schema ↓Upload PDF with TUS ↓Create extraction job ↓Poll status or receive webhook ↓Query structured rows ↓Validate the result
Create the key in Project → API Keys and store it in an environment variable. It never goes in browser code and never gets committed to source control.
PdfParse creates stable record identifiers and the parent-child relationship itself — never ask the extraction model to invent primary or foreign keys.
Use an idempotency key whenever you create resources or jobs. The API scopes replay protection to the project, method, and route for 24 hours, so a retried request can't create a duplicate.
The upload endpoint speaks TUS, the resumable upload protocol. With tus-js-client, a dropped connection costs you a retry instead of the whole file — and it beats stuffing a PDF into a JSON body.
npm install tus-js-client
This helper returns the document key the job endpoint needs:
The job is asynchronous, so don't hold the upload request open while extraction runs. For a small integration, poll the job endpoint (the next step). For production, use webhooks (the step after).
A bounded loop that stops when the job completes or fails:
const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));async function waitForJob(jobId) { for (let attempt = 0; attempt < 60; attempt += 1) { const response = await fetch(`${baseUrl}/jobs/${jobId}`, { headers }); if (!response.ok) { throw new Error(await response.text()); } const currentJob = await response.json(); if (currentJob.status === "completed") return currentJob; if (currentJob.status === "failed") { throw new Error(`Extraction job ${jobId} failed.`); } await wait(2000); } throw new Error(`Extraction job ${jobId} did not finish in time.`);}await waitForJob(job.id);
Always set a time limit — forever is a long time to poll. If you hit a 429 or a temporary server error, back off exponentially instead of hammering the endpoint.
Three rules: read the raw body before you parse JSON, reject stale timestamps according to your security policy, and deduplicate events with X-OCR-Webhook-Id.