Endpoint reference

POST /api/inspect

List the AcroForm fields in a PDF you upload — names, types, options, current values — plus a paste-ready fillTemplate for /api/fill-form. Returns JSON. Prefer a browser? The free inspector at /tools/inspect runs the same engine.

This is the first call in the natural workflow: inspect to learn the field names, fill-form to set them, merge to combine the results. You can't fill fields whose names you don't know.

Request

Method: POST. URL: https://pdfops.dev/api/inspect. Body: multipart/form-data.

FieldTypeRequiredDescription
pdfFile (PDF binary)YesThe PDF to inspect. Maximum size: ~10 MB.
HeaderRequiredDescription
X-API-KeyNoOptional. Same auth semantics as /api/fill-form and /api/merge: no header → anonymous, 100 requests/IP/month; valid key → your tier's quota; invalid or revoked key → 401 invalid_api_key.

Successful (2xx) calls count 1 request against your quota — the same meter as the other PDF endpoints. Check consumption with GET /api/usage.

Response

On success: 200 OK, Content-Type: application/json:

{
  "count": 4,
  "fields": [
    { "name": "customer_name", "type": "text", "value": "", "readOnly": false },
    { "name": "agreed_to_terms", "type": "checkbox", "checked": false, "readOnly": false },
    { "name": "plan", "type": "radio", "value": "Starter", "options": ["Starter", "Pro"], "readOnly": false },
    { "name": "approval", "type": "unsupported", "raw": "PDFSignature", "readOnly": false }
  ],
  "fillTemplate": {
    "customer_name": "",
    "agreed_to_terms": "true",
    "plan": "Starter"
  }
}
FieldTypeMeaning
countnumberNumber of AcroForm fields found.
fieldsarrayOne object per field — schema below.
fillTemplateobjectA paste-ready JSON object for /api/fill-form's fields parameter. Supported fields only; checkbox placeholders are the string "true", choice placeholders are the first option. Overwrite the placeholder values and send it.

A PDF with no AcroForm returns 200 with {"count": 0, "fields": [], "fillTemplate": {}} — deliberately not an error, because "does this PDF have fields?" is exactly the question you're asking.

On any failure: 4xx, Content-Type: application/json, body is { "error": "<code>", "details": "<explanation>" }.

Field objects

FieldTypeMeaning
namestringThe AcroForm field name — exactly what /api/fill-form expects as a fields key.
typestringOne of text, checkbox, dropdown, radio, optionlist, unsupported.
valuestringPresent on text and choice fields: the current saved value.
checkedbooleanPresent on checkbox fields: whether the box is currently checked.
optionsstring[]Present on choice fields (dropdown, radio, optionlist): the allowed values.
readOnlybooleanWhether the field is flagged read-only in the PDF.
rawstringPresent on unsupported fields: the underlying AcroForm class, e.g. PDFButton or PDFSignature.

How type maps to AcroForm classes and which extra property each carries:

typeAcroForm classCarries
textPDFTextFieldvalue
checkboxPDFCheckBoxchecked
dropdownPDFDropdownoptions + value
radioPDFRadioGroupoptions + value
optionlistPDFOptionListoptions + value
unsupportedPDFButton, PDFSignatureraw

Error codes

Error codeStatusTrigger
invalid_multipart400Request body could not be parsed as multipart/form-data.
missing_pdf400No pdf field present or it wasn't a File.
invalid_pdf400The uploaded PDF couldn't be parsed (corrupt, encrypted, not a PDF).
invalid_api_key401An X-API-Key header was sent but the key is unknown or revoked. No fallback to the anonymous IP cap — get a fresh key at /pricing.
rate_limited (anonymous)429No X-API-Key header and the per-IP limit (100/month) is exhausted. Retry-After header gives seconds until next calendar month.
rate_limited (keyed)429Your key's tier quota (Free 250 / Indie 4,000 / Pro 25,000 per month) is exhausted. Retry-After header gives seconds until next calendar month.

Note there's no no_form error here: a form-less PDF is a valid answer (count: 0), not a failure.

Example — curl

Don't have a fillable PDF? Grab the sample invoice-template.pdf with customer_name + total fields.

curl -X POST https://pdfops.dev/api/inspect \
  -F "pdf=@invoice-template.pdf" \
  -H "X-API-Key: pdfops_live_..."  # optional — raises your quota to your tier

Example — JavaScript: inspect, then fill

fillTemplate is shaped so it pipes straight into /api/fill-form — overwrite the placeholders and send:

import { readFile, writeFile } from 'node:fs/promises';

const pdf = await readFile('invoice-template.pdf');
const blob = new Blob([pdf], { type: 'application/pdf' });

// 1. Inspect — learn the field names.
const fd = new FormData();
fd.append('pdf', blob, 'invoice-template.pdf');
const { fillTemplate } = await (
  await fetch('https://pdfops.dev/api/inspect', { method: 'POST', body: fd })
).json();

// 2. Fill — overwrite the placeholders, pipe the template into fill-form.
const fields = { ...fillTemplate, customer_name: 'Acme Co', total: '$1,250.00' };
const fillFd = new FormData();
fillFd.append('pdf', blob, 'invoice-template.pdf');
fillFd.append('fields', JSON.stringify(fields));
const res = await fetch('https://pdfops.dev/api/fill-form', { method: 'POST', body: fillFd });

await writeFile('filled.pdf', Buffer.from(await res.arrayBuffer()));

FAQ

Why did I get count: 0 on a PDF that looks like a form?

The PDF has no AcroForm layer. Three common cases: a flat PDF (the "form" is just text drawn on the page — no interactive fields), an XFA form (a different, XML-based form technology that AcroForm tooling doesn't read), or a scanned document (an image of a form — reading it would need OCR, which PDFops doesn't do). count: 0 is a 200, not an error, because "does this PDF have fields?" is exactly the question this endpoint answers. To make the PDF fillable, add AcroForm fields in Acrobat (Tools → Prepare Form), LibreOffice Draw, or pdftk.

What does type: "unsupported" mean?

The field exists in the AcroForm but isn't a type /api/fill-form can set — buttons and signatures. The raw property carries the underlying class (e.g. PDFButton, PDFSignature). Inspect reports them so you see the complete form; they're excluded from fillTemplate, and sending them to /api/fill-form would return 400 unsupported_field_type.

Does /api/inspect consume my quota?

Yes — a successful (2xx) inspect counts as 1 request against the same monthly quota as /api/fill-form and /api/merge. Failed requests (4xx/5xx) don't consume budget. A typical inspect-then-fill round trip costs 2 requests.

What's the difference between /api/inspect and the free tool at /tools/inspect?

Same engine, different surface. /tools/inspect is the browser version — drop a PDF on the page and read the field list, no code, no key. /api/inspect is the same inspection over HTTP for your scripts, CI, and edge functions, and it returns the machine-readable fillTemplate you can pipe into /api/fill-form.

Are value and checked the current saved values in the PDF?

Yes. value (text and choice fields) and checked (checkboxes) reflect what's currently saved in the uploaded PDF. That makes /api/inspect double as a reader: inspect a filled PDF to extract what's in it — for example, a form a customer filled in and sent back.