---
title: "How to route documents automatically with the Nutrient DWS Classify API"
canonical_url: "https://www.nutrient.io/blog/route-documents-automatically-classify-api/"
md_url: "https://www.nutrient.io/blog/route-documents-automatically-classify-api.md"
last_updated: "2026-08-25T16:27:50.837Z"
description: "Build a document router with the Nutrient Data Extraction Classify endpoint, with examples for insurance, legal intake, and HR onboarding."
---

**TL;DR**

This builds a document router on top of the Nutrient DWS Data Extraction API’s Classify endpoint:

- **No training data, no templates.** Classify scores a document against labels supplied in the request. Swapping to a new document type means writing a new label list, not retraining a model.

- **One router, any domain.** `route_document()` takes its label-to-destination map as a parameter, so the same function routes insurance claims, legal filings, or HR paperwork — only the labels and routes change.

- **Independent confidence scores.** Each label’s score stands on its own instead of summing to 1 across all labels, so a router can catch a document that plausibly matches two categories instead of silently picking one.

**Call to Action**

Try Nutrient Data Extraction API

[Learn More](https://www.nutrient.io/api/data-extraction-api/)

A shared inbox, an upload folder, or an intake API receives files that all look the same on disk — PDFs and images — but need to go to different places: a claims adjuster, a paralegal, an HR system. Manual sorting is what actually causes the backlog in most document-heavy processes, before a single field ever gets extracted.

The [Nutrient DWS Data Extraction API](https://www.nutrient.io/api/data-extraction-api/) recently added a Classify endpoint for exactly this: sorting a document into a caller-defined set of categories, and then routing on the result. It’s a new addition alongside the existing [Parse](https://www.nutrient.io/guides/dws-data-extraction/parsing.md) and [Extract](https://www.nutrient.io/guides/dws-data-extraction/extract.md) endpoints.

This is a different product from [Nutrient AI Document Processing](https://www.nutrient.io/guides/ai-document-processing/classify-documents.md)’s classification feature, which uses template-based `DocumentTemplate` objects in the.NET and Java SDKs. The Data Extraction Classify endpoint is a hosted REST API scored against labels supplied per request, with no templates to maintain.

## How classification works

Classify runs zero-shot: It scores a document against whatever labels arrive in the request, not labels learned from a fixed training set. There’s no model to train and no dataset to maintain — labels can change per request.

A document is scored two ways — once from its extracted text, and once from its page images — and the two scores combine into a ranked list of predictions. Dedicated scoring models do the work, not a generative large language model (LLM) or vision language model (VLM). `textWeight` and `imageWeight` control how much each branch contributes; setting either to `0` skips that branch (and its associated text extraction or page rendering) entirely.

## Prerequisites

- A Nutrient DWS account and Data Extraction API key from the [dashboard](https://dashboard.nutrient.io/sign_up/?product=data-extraction). The key starts with `pdf_live_`.

- Python 3.10 or later, plus the `requests` and `python-dotenv` packages (`pip install requests python-dotenv`).

- The key stored in a `.env` file as `NUTRIENT_API_KEY=your_data_extraction_api_key_here`. `classify.py` in the next section loads it with `python-dotenv`; the other files below just import from `classify.py`, so the key only needs to be read once.

## 1. Define candidate labels

A label list is the only thing that changes between domains, so it’s worth getting the descriptions right. The classifier scores against the description as much as the label name, so vague descriptions produce vague boundaries between categories.

```python

# labels.py

INTAKE_LABELS = [
    {"label": "invoice", "description": "A commercial invoice or bill."},
    {"label": "contract", "description": "A legal agreement between parties."},
    {"label": "resume", "description": "A job applicant's résumé or CV."},
    {"label": "correspondence", "description": "A letter or email printed to PDF."},
]

```

## 2. Call the Classify endpoint

`POST https://api.nutrient.io/extraction/classify` takes the same bearer-token authentication as Parse and Extract, along with the document as either a multipart `file` upload or a `url` in a JSON body. The `labels` array goes in an `instructions` object alongside two optional knobs — `topK` caps how many ranked predictions come back, and `textWeight`/`imageWeight` (0–1) tune how much each scoring branch counts:

```python

# classify.py

import json
import os

import requests
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.environ["NUTRIENT_API_KEY"]
ENDPOINT = "https://api.nutrient.io/extraction/classify"

def classify_document(file_path: str, labels: list[dict]) -> dict:
    """Classify a document against a set of candidate labels."""
    instructions = {"labels": labels}
    with open(file_path, "rb") as f:
        response = requests.post(
            ENDPOINT,
            headers={"Authorization": f"Bearer {API_KEY}"},
            files={"file": f},
            data={"instructions": json.dumps(instructions)},
        )
    return response.json()

```

A successful call returns HTTP 200 with the top label, its score, and the full ranked list:

```json

{
  "status": 200,
  "requestId": "req_cl_001",
  "output": {
    "classification": {
      "label": "invoice",
      "score": 0.92,
      "predictions": [
        { "label": "invoice", "score": 0.92 },
        { "label": "contract", "score": 0.14 },
        { "label": "resume", "score": 0.03 }
      ]
    }
  },
  "metrics": { "pagesProcessed": 1 },
  "usage": {
    "data_extraction_credits": { "cost": 1, "remainingCredits": 4999 }
  }
}

```

Each `score` is an independent confidence for that label, not a slice of a probability distribution — the numbers don’t have to sum to 1. That’s deliberate: A cover letter attached to a contract can legitimately score high on both `correspondence` and `contract`, and reading scores independently is what lets a router notice that instead of being forced into a single answer. A rejected or failed call still returns a JSON body — with a matching non-200 `status` and an `errorMessage` — which is why the code below checks `status` in the body rather than reaching for `raise_for_status()`.

Classify accepts `application/pdf`, `image/png`, `image/jpeg`, and `image/tiff`. Check the [file types](https://www.nutrient.io/guides/dws-data-extraction/file-types.md) guide before building against this list, since supported formats can change as the endpoint matures.

To classify a document already sitting at a public URL, send JSON instead of a file upload:

```python

def classify_from_url(url: str, labels: list[dict]) -> dict:
    """Classify a document at a public URL against a set of candidate labels."""
    response = requests.post(
        ENDPOINT,
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={"url": url, "labels": labels},
    )
    return response.json()

```

## 3. Route on the result

A router only needs the top label and, ideally, a sense of how close the runner-up came. `route_document()` takes its label-to-destination map as a plain argument rather than a module-level constant — that’s what lets the same function serve every domain further down this post:

```python

# router.py

INTAKE_ROUTES = {
    "invoice": "queue:accounts-payable",
    "contract": "queue:legal-review",
    "resume": "queue:recruiting",
    "correspondence": "queue:general-intake",
}

def route_document(
    classification: dict, routes: dict[str, str], ambiguity_gap: float = 0.15
) -> dict:
    """Map a classify response to a destination, flagging close calls for review."""
    top = classification["label"]
    predictions = classification["predictions"]
    destination = routes.get(top, "queue:manual-review")

    runner_up = predictions[1] if len(predictions) > 1 else None
    needs_review = (
        runner_up is not None
        and (classification["score"] - runner_up["score"]) < ambiguity_gap
    )

    return {
        "destination": "queue:manual-review" if needs_review else destination,
        "predicted_label": top,
        "confidence": classification["score"],
        "needs_review": needs_review,
    }

```

## 4. Put it together

The following `main.py` ties the classifier and the router together: It classifies a file passed on the command line, and then routes and prints the result.

```python

# main.py

import sys

from classify import classify_document
from labels import INTAKE_LABELS
from router import INTAKE_ROUTES, route_document

if __name__ == "__main__":
    file_path = sys.argv[1]
    result = classify_document(file_path, INTAKE_LABELS)

    if result.get("status")!= 200:
        raise RuntimeError(f"Classify failed: {result.get('errorMessage')}")

    decision = route_document(result["output"]["classification"], INTAKE_ROUTES)
    print(decision)

```

## Reusing the router across three domains

Nothing above is specific to a generic intake queue. Swapping the labels and the routes dictionary retargets the whole pipeline at a different document mix.

### Insurance claim intake

A claims queue mixes forms, medical documentation, and correspondence for every claim filed. Sorting before assignment means adjusters only see documents relevant to their part of the claim:

```python

CLAIM_LABELS = [
    {"label": "claim_form", "description": "A completed first-notice-of-loss or claim form."},
    {"label": "medical_report", "description": "A medical record, diagnosis, or treatment summary."},
    {"label": "police_report", "description": "An official police or incident report."},
    {"label": "repair_estimate", "description": "A repair or replacement cost estimate."},
    {"label": "correspondence", "description": "A letter or email related to the claim."},
]

CLAIM_ROUTES = {
    "claim_form": "queue:claims-intake",
    "medical_report": "queue:medical-review",
    "police_report": "queue:fraud-review",
    "repair_estimate": "queue:estimate-approval",
    "correspondence": "queue:claims-general",
}

result = classify_document(file_path, CLAIM_LABELS)
decision = route_document(result["output"]["classification"], CLAIM_ROUTES)

```

### Legal intake

A law firm’s intake mixes contracts, filings, and discovery material from multiple matters. Routing by document type gets each file to the right practice group before anyone reads it:

```python

LEGAL_LABELS = [
    {"label": "contract", "description": "A legal agreement between parties."},
    {"label": "pleading", "description": "A court filing such as a complaint or motion."},
    {"label": "discovery_request", "description": "A discovery request or response."},
    {"label": "invoice", "description": "A commercial invoice or bill."},
    {"label": "correspondence", "description": "A letter or email between parties."},
]

LEGAL_ROUTES = {
    "contract": "queue:contract-review",
    "pleading": "queue:litigation",
    "discovery_request": "queue:discovery",
    "invoice": "queue:billing",
    "correspondence": "queue:matter-general",
}

result = classify_document(file_path, LEGAL_LABELS)
decision = route_document(result["output"]["classification"], LEGAL_ROUTES)

```

### HR onboarding

A new-hire packet typically arrives as one batch of scanned pages covering identity, tax, and background-check documents. Classifying at the page or document level splits the batch so each piece lands in the right system:

```python

ONBOARDING_LABELS = [
    {"label": "offer_letter", "description": "A signed employment offer letter."},
    {"label": "i9_form", "description": "A completed Form I-9, Employment Eligibility Verification."},
    {"label": "w4_form", "description": "A completed Form W-4, Employee's Withholding Certificate."},
    {"label": "background_check", "description": "A background check or reference check report."},
    {"label": "id_document", "description": "A government-issued photo ID or passport."},
]

ONBOARDING_ROUTES = {
    "offer_letter": "system:hris-records",
    "i9_form": "system:compliance-vault",
    "w4_form": "system:payroll",
    "background_check": "system:compliance-vault",
    "id_document": "system:compliance-vault",
}

result = classify_document(file_path, ONBOARDING_LABELS)
decision = route_document(result["output"]["classification"], ONBOARDING_ROUTES)

```

Background checks, W-4s, and government IDs are personal and sensitive data. Review the [privacy](https://www.nutrient.io/guides/dws-data-extraction/privacy.md) and [security](https://www.nutrient.io/guides/dws-data-extraction/security.md) guides for how the Data Extraction API handles document content and retention before pointing this pipeline at real employee or claimant documents.

## Why the router doesn’t need per-domain code

The three examples above share every line of `classify.py` and `router.py`. What changes is data, not logic: a label list the classifier scores against, and a routes dictionary the application already needs to define somewhere. That split is what makes a fourth domain — an expense-report queue, a support-ticket triage inbox — a configuration change instead of a new integration.

The tradeoff is that label quality does all the work. A vague description (“a form”) gives the classifier less to work with than a specific one (“a completed first-notice-of-loss or claim form”), and a boundary that’s ambiguous in the description will show up as ambiguous scores at runtime — which is exactly what `ambiguity_gap` in `route_document()` is there to catch.

## FAQ

#### How much does Classify cost?

Classify costs a flat 1 credit per page, independent of any parse mode — a 10-page document costs 10 credits. See the [pricing](https://www.nutrient.io/guides/dws-data-extraction/pricing.md) guide for current plan thresholds.

#### What happens on rate limits or server errors?

Handle `429` responses with backoff, and treat `500` as retryable. Refer to the [error handling](https://www.nutrient.io/guides/dws-data-extraction/errors.md) guide for the full status code reference.

#### How should a router handle a low-confidence or ambiguous result?

Route anything below a confidence threshold, or with a close runner-up, to a manual-review queue instead of auto-routing it. `route_document()`’s `ambiguity_gap` parameter covers the runner-up case; pair it with a minimum absolute `score` threshold on the top label for documents that don’t clearly match anything.

## Where to go next

- [Data Extraction API Studio](https://dashboard.nutrient.io/data-extraction-api/studio/classify/) — Try the endpoint interactively before wiring it into a pipeline.

- [Data Extraction API overview](https://www.nutrient.io/guides/dws-data-extraction/api-overview.md) — Authentication and the full endpoint list.

- [Extract endpoint](https://www.nutrient.io/guides/dws-data-extraction/extract.md) — Pull structured fields once a document has been routed to the right category.

**Call to Action**

Get started with Nutrient Data Extraction API

[Learn More](https://www.nutrient.io/guides/dws-data-extraction/getting-started.md)
---

## Related pages

- [The business case for accessibility: Five ways it drives enterprise value](/blog/5-ways-accessibility-drives-enterprise-value.md)
- [Accessibility Untangled Why It Matters Guide](/blog/accessibility-untangled-why-it-matters-guide.md)
- [Advanced Techniques For React Native Ui Components](/blog/advanced-techniques-for-react-native-ui-components.md)
- [`vector_store` holds your indexed documents (see the multimodal RAG post](/blog/agentic-rag.md)
- [Ai Document Automation Extraction To Action](/blog/ai-document-automation-extraction-to-action.md)
- [Ai Legal Assistant Document Authoring](/blog/ai-legal-assistant-document-authoring.md)
- [Amazon Textract Alternatives](/blog/amazon-textract-alternatives.md)
- [Start (clears any prior buffer), navigate the document, then stop into a file.](/blog/android-faster-pdf-rendering.md)
- [Android Pdf Out Of Memory Handling](/blog/android-pdf-out-of-memory-handling.md)
- [Angular File Viewer Pdf Image Office Files](/blog/angular-file-viewer-pdf-image-office-files.md)
- [Auto Tagging And Document Accessibility In Dotnet Sdk](/blog/auto-tagging-and-document-accessibility-in-dotnet-sdk.md)
- [Simple PII redaction.](/blog/automated-pii-removal.md)
- [Best Document Ai Platforms](/blog/best-document-ai-platforms.md)
- [Best Document Viewers](/blog/best-document-viewers.md)
- [Build Vs Buy Document Extraction](/blog/build-vs-buy-document-extraction.md)
- [The CEO’s AI playbook: Why decision architecture beats model selection](/blog/ceo-ai-playbook-decision-architecture.md)
- [1. Extract and chunk the PDF.](/blog/chat-with-pdf.md)
- [Complete Guide To Pdfjs](/blog/complete-guide-to-pdfjs.md)
- [Construction Document Data Extraction](/blog/construction-document-data-extraction.md)
- [Convert One Drive Files To Pdf In Sharepoint](/blog/convert-one-drive-files-to-pdf-in-sharepoint.md)
- [Create And Edit Pdfs In Flutter](/blog/create-and-edit-pdfs-in-flutter.md)
- [Create Pdfs With React](/blog/create-pdfs-with-react.md)
- [Creating A Document Scanner With Ocr In Python](/blog/creating-a-document-scanner-with-ocr-in-python.md)
- [Creating And Filling Pdf Forms Programmatically In Javascript](/blog/creating-and-filling-pdf-forms-programmatically-in-javascript.md)
- [The CTO’s AI playbook: Why accountability architecture beats orchestration](/blog/cto-ai-playbook-accountability-architecture.md)
- [Digital Signatures](/blog/digital-signatures.md)
- [Digital Workflow Automation](/blog/digital-workflow-automation.md)
- [Document Ai Vs Ocr](/blog/document-ai-vs-ocr.md)
- [Document Extraction Confidence Scores](/blog/document-extraction-confidence-scores.md)
- [Document Viewer](/blog/document-viewer.md)
- [Document Watermarking](/blog/document-watermarking.md)
- [Emerging threats: Your logging system may be an agentic threat vector](/blog/emerging-threats-your-logging-system.md)
- [Extract Patient Data On Premises](/blog/extract-patient-data-on-premises.md)
- [app.py](/blog/extract-text-from-pdf-using-python.md)
- [Fillable Pdf](/blog/fillable-pdf.md)
- [How To Add Digital Signature To Pdf Using React](/blog/how-to-add-digital-signature-to-pdf-using-react.md)
- [How To Build A Dotnet Maui Pdf Viewer](/blog/how-to-build-a-dotnet-maui-pdf-viewer.md)
- [How To Build A Flutter Pdf Viewer](/blog/how-to-build-a-flutter-pdf-viewer.md)
- [or](/blog/how-to-build-a-javascript-pdf-viewer-with-pdfjs.md)
- [How To Build A Javascript Pdf Viewer](/blog/how-to-build-a-javascript-pdf-viewer.md)
- [or](/blog/how-to-build-a-nextjs-pdf-viewer.md)
- [How To Build A Powerpoint Viewer Using Javascript](/blog/how-to-build-a-powerpoint-viewer-using-javascript.md)
- [Using Yarn](/blog/how-to-build-a-react-excel-viewer.md)
- [How To Build A React Native Pdf Viewer](/blog/how-to-build-a-react-native-pdf-viewer.md)
- [How To Build A React Powerpoint Viewer](/blog/how-to-build-a-react-powerpoint-viewer.md)
- [How To Build A Reactjs File Viewer](/blog/how-to-build-a-reactjs-file-viewer.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer-with-react-pdf.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer.md)
- [How To Build A Reactjs Viewer With Pdfjs](/blog/how-to-build-a-reactjs-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer With Pdfjs](/blog/how-to-build-a-vuejs-pdf-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer](/blog/how-to-build-a-vuejs-pdf-viewer.md)
- [How To Build An Android Pdf Viewer](/blog/how-to-build-an-android-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Ng2 Pdf Viewer](/blog/how-to-build-an-angular-pdf-viewer-with-ng2-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Pdfjs](/blog/how-to-build-an-angular-pdf-viewer-with-pdfjs.md)
- [How To Convert Docx To Pdf Using Javascript](/blog/how-to-convert-docx-to-pdf-using-javascript.md)
- [How To Convert Docx To Pdf Using Python](/blog/how-to-convert-docx-to-pdf-using-python.md)
- [How To Convert Html To Pdf Using Html2pdf](/blog/how-to-convert-html-to-pdf-using-html2pdf.md)
- [or](/blog/how-to-convert-html-to-pdf-using-react.md)
- [How To Convert Html To Pdf Using Wkhtmltopdf And Csharp](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-csharp.md)
- [or](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-python.md)
- [How To Convert Word To Pdf In Nodejs](/blog/how-to-convert-word-to-pdf-in-nodejs.md)
- [or](/blog/how-to-create-a-react-js-signature-pad.md)
- [How To Create Pdfs With React To Pdf](/blog/how-to-create-pdfs-with-react-to-pdf.md)
- [How To Edit Pdfs Using Ios Pdf Library](/blog/how-to-edit-pdfs-using-ios-pdf-library.md)
- [How To Embed A Pdf Viewer In Your Website](/blog/how-to-embed-a-pdf-viewer-in-your-website.md)
- [How To Extract Tables From Pdf And Images](/blog/how-to-extract-tables-from-pdf-and-images.md)
- [How To Generate Pdf From Html With Nodejs](/blog/how-to-generate-pdf-from-html-with-nodejs.md)
- [base_url tells WeasyPrint where to resolve relative asset paths](/blog/how-to-generate-pdf-reports-from-html-in-python.md)
- [How To Merge Pdfs Using Javascript](/blog/how-to-merge-pdfs-using-javascript.md)
- [How To Ocr Pdfs In Linux](/blog/how-to-ocr-pdfs-in-linux.md)
- [How To Print Pdf In Csharp](/blog/how-to-print-pdf-in-csharp.md)
- [Open an image.](/blog/how-to-use-tesseract-ocr-in-python.md)
- [From an HTML string.](/blog/html-in-pdf-format.md)
- [Html To Pdf In Javascript](/blog/html-to-pdf-in-javascript.md)
- [Javascript Pdf Editors](/blog/javascript-pdf-editors.md)
- [Javascript Pdf Libraries](/blog/javascript-pdf-libraries.md)
- [Langextract Vs Llamaindex Extraction Comparison](/blog/langextract-vs-llamaindex-extraction-comparison.md)
- [Linearized Pdf](/blog/linearized-pdf.md)
- [or](/blog/merge-pdfs.md)
- [Swift Package Manager](/blog/mobile-pdf-sdk.md)
- [`elements` come from your document parser — each has a type and content.](/blog/multimodal-rag.md)
- [Nutrient Flutter 6 Bindings Api](/blog/nutrient-flutter-6-bindings-api.md)
- [Nutrient Vs Conga Composer](/blog/nutrient-vs-conga-composer.md)
- [Online Document Viewer](/blog/online-document-viewer.md)
- [Open Pdf In Your Web App](/blog/open-pdf-in-your-web-app.md)
- [Building WCAG 2.2, Section 508, and PDF/UA-compliant PDFs with an SDK](/blog/pdf-accessibility.md)
- [Extract data from PDF files: A developer guide to structured data from PDFs and scans](/blog/pdf-data-extraction-developer-guide.md)
- [Pdf Extraction Benchmark Opendataloader Bench](/blog/pdf-extraction-benchmark-opendataloader-bench.md)
- [Pdf Extraction Document Case Studies](/blog/pdf-extraction-document-case-studies.md)
- [Pdf Page Labels](/blog/pdf-page-labels.md)
- [Pdf Sdk Compliance Security Checklist](/blog/pdf-sdk-compliance-security-checklist.md)
- [Pdf Sdk Performance Benchmark](/blog/pdf-sdk-performance-benchmark.md)
- [Pdf Ua Compliance Guide](/blog/pdf-ua-compliance-guide.md)
- [Pdfjs Accessibility Structtree Printing](/blog/pdfjs-accessibility-structtree-printing.md)
- [Pdfjs Advanced Loading Streaming Workers](/blog/pdfjs-advanced-loading-streaming-workers.md)
- [Pdfjs Annotation Editor Layer](/blog/pdfjs-annotation-editor-layer.md)
- [Pdfjs Area Annotations Canvas Capture](/blog/pdfjs-area-annotations-canvas-capture.md)
- [Pdfjs Coordinate Systems Pdf To Screen](/blog/pdfjs-coordinate-systems-pdf-to-screen.md)
- [Pdfjs Document Outline Bookmarks Metadata](/blog/pdfjs-document-outline-bookmarks-metadata.md)
- [Pdfjs Eventbus Guide](/blog/pdfjs-eventbus-guide.md)
- [macOS](/blog/pdfjs-file-format-conversion-to-pdf.md)
- [macOS](/blog/pdfjs-generating-pdf-thumbnails-pdf2pic.md)
- [Pdfjs Limitations Commercial Upgrade](/blog/pdfjs-limitations-commercial-upgrade.md)
- [Pdfjs Native Annotation Layer Forms](/blog/pdfjs-native-annotation-layer-forms.md)
- [Pdfjs Navigation Zoom Rotation](/blog/pdfjs-navigation-zoom-rotation.md)
- [Pdfjs Pdf Page Manipulation Pdf Lib](/blog/pdfjs-pdf-page-manipulation-pdf-lib.md)
- [Pdfjs React Viewer Setup](/blog/pdfjs-react-viewer-setup.md)
- [Pdfjs Rendering Overlays React Portals](/blog/pdfjs-rendering-overlays-react-portals.md)
- [Pdfjs Server Side Text Extraction](/blog/pdfjs-server-side-text-extraction.md)
- [Pdfjs Sticky Note Annotations](/blog/pdfjs-sticky-note-annotations.md)
- [Pdfjs Text Highlight Annotations](/blog/pdfjs-text-highlight-annotations.md)
- [Pdfjs Text Search Pdffindcontroller](/blog/pdfjs-text-search-pdffindcontroller.md)
- [Pdfjs Thumbnail Sidebar](/blog/pdfjs-thumbnail-sidebar.md)
- [Process Flows](/blog/process-flows.md)
- [React Native Pdf Annotation](/blog/react-native-pdf-annotation.md)
- [Using Yarn](/blog/react-pdf-editor.md)
- [React Pdf Loading States Errors Passwords](/blog/react-pdf-loading-states-errors-passwords.md)
- [React Pdf Setup Basic Rendering](/blog/react-pdf-setup-basic-rendering.md)
- [React Pdf Text Layer Custom Renderer](/blog/react-pdf-text-layer-custom-renderer.md)
- [or](/blog/sample-blog-updated.md)
- [Sdk Product Updates Q2 2026](/blog/sdk-product-updates-q2-2026.md)
- [Add DWS MCP Server to your Claude Code project.](/blog/teaching-llms-to-read-pdfs.md)
- [Open an image file.](/blog/tesseract-python-guide.md)
- [Define the HTML part of the document.](/blog/top-10-ways-to-generate-pdfs-in-python.md)
- [Top 5 Javascript Pdf Viewers](/blog/top-5-javascript-pdf-viewers.md)
- [or](/blog/top-js-pdf-libraries.md)
- [Convert an HTML file to PDF.](/blog/top-ten-ways-to-convert-html-to-pdf.md)
- [Vector Pdf](/blog/vector-pdf.md)
- [Wcag2 Accessibility Requirements Documents](/blog/wcag2-accessibility-requirements-documents.md)
- [Web Sdk Is Now Headless](/blog/web-sdk-is-now-headless.md)
- [What Are Annotations](/blog/what-are-annotations.md)
- [What Is A Vpat](/blog/what-is-a-vpat.md)
- [What Is Document Processing](/blog/what-is-document-processing.md)
- [What Is Intelligent Document Processing](/blog/what-is-intelligent-document-processing.md)
- [What Is Pdf Ua](/blog/what-is-pdf-ua.md)
- [Why Pdfium Is A Trusted Platform For Pdf Rendering](/blog/why-pdfium-is-a-trusted-platform-for-pdf-rendering.md)
- [Why Your Ai Agent Hallucinates Pdf Table Data](/blog/why-your-ai-agent-hallucinates-pdf-table-data.md)

