Using pdf-inspector in Node.js - @firecrawl/pdf-inspector Guide
API ReferenceNode.js
The Node binding is @firecrawl/pdf-inspector, a napi-rs native implementation. Sync APIs parse on the calling thread; async APIs run on the libuv thread pool without blocking the event loop — bulk parsing won't stall your server.
Install
npm install @firecrawl/pdf-inspector
# or
bun add @firecrawl/pdf-inspectorPrebuilt binaries cover Linux x64/ARM64 (glibc & musl), macOS ARM64, and Windows x64 — npm installs only the one matching your platform; no Rust toolchain required. TypeScript types included.
Core APIs
import { classifyPdf, processPdf, extractPagesMarkdown } from '@firecrawl/pdf-inspector'
// 1. Lightweight classification (~10–50ms)
const c = classifyPdf(pdf)
// { pdfType: 'TextBased', pageCount: 42,
// pagesNeedingOcr: [5, 12], confidence: 0.875 }
// 2. All-in-one: detect + extract + Markdown (sync)
const result = processPdf(pdf)
console.log(result.markdown)
// 3. Per-page Markdown (with needsOcr layout metadata)
const { pages } = extractPagesMarkdown(pdf)
for (const p of pages) {
console.log(p.page, p.needsOcr)
}Also available: detectPdf (classification only), extractText (plain text), extractTextInRegions (bounding boxes with per-region needsOcr flags).
Sync or async?
import { classifyPdfAsync, extractPagesMarkdownAsync } from '@firecrawl/pdf-inspector'
// One-off script: sync is fine
const classification = classifyPdf(pdf)
// Server / large files: async versions run on the libuv pool
const classification = await classifyPdfAsync(pdf)
if (classification.pdfType === 'TextBased') {
const { pages } = await extractPagesMarkdownAsync(pdf)
}The async variants copy the input buffer before returning, so it's safe to reuse or mutate immediately after the call.
Selective OCR
import { OcrMode, processPdfWithOcr } from '@firecrawl/pdf-inspector'
const result = await processPdfWithOcr(pdf, {
mode: OcrMode.Auto, // only pages rejected by native extraction get OCR
pageNumbers: [1, 3], // 1-indexed
})
for (const page of result.pages) {
console.log(page.pageNumber, page.provenance.source) // native | ocr | fused
}
console.log(result.pagesRoutedToOcr)OCR requires PDFium and ONNX Runtime shared libraries (PDFIUM_LIB_PATH, ORT_DYLIB_PATH); the pinned model set downloads on the first routed page with checksum verification. For offline deployments pass offline: true plus a warm cache or modelDirectory. The default build embeds none of these components — they never load unless you opt into OCR.
Common scenarios
Classify-first bulk ingestion
import { readdir, readFile, writeFile } from 'node:fs/promises'
import { classifyPdfAsync, extractPagesMarkdownAsync } from '@firecrawl/pdf-inspector'
const dir = './pdfs'
for (const name of await readdir(dir)) {
if (/\.pdf$/i.test(name)) {
const buf = await readFile(`${dir}/${name}`)
const c = await classifyPdfAsync(buf)
if (c.pdfType !== 'TextBased') {
console.warn(`Skipping ${name}: ${c.pdfType}, needs OCR on ${c.pagesNeedingOcr}`)
continue
}
const { pages } = await extractPagesMarkdownAsync(buf)
await writeFile(`${dir}/${name}.md`, pages.map(p => p.markdown).join('\n\n'))
}
}Parse uploads directly
// Once you have an uploaded File object, parse its bytes
const bytes = new Uint8Array(await file.arrayBuffer())
const result = processPdf(bytes)
// Return to the frontend or feed straight into your LLM pipelineExpose to AI agents
Wrap processPdf as a tool function so LLM agents can "read" uploaded PDFs:
const parsePdf = async (path) => (await import('@firecrawl/pdf-inspector')).processPdf(await readFile(path))
// register as a tool → agents call it directlyTips
- Use
*Asyncvariants on servers; sync is fine for one-off scripts - Don't call
processPdfwhen you only need the verdict —classifyPdfis an order of magnitude faster - The official napi/README is the source of truth for signatures