---
title: "Classify endpoint"
canonical_url: "https://www.nutrient.io/guides/dws-data-extraction/classify/"
md_url: "https://www.nutrient.io/guides/dws-data-extraction/classify.md"
last_updated: "2026-09-07T00:00:00.000Z"
description: "Sort documents into categories you define with the /extraction/classify endpoint. Zero-shot scoring against per-request labels, no training data or templates."
---

# Classify endpoint

The classify endpoint scores a document against a list of labels you supply and returns a ranked list of predictions:

```

POST https://api.nutrient.io/extraction/classify

```

Use the classify endpoint to decide where a document goes before you extract anything from it. A claim form, a medical report, and a cover letter are all “a PDF” until something reads them. Classify makes that call so an intake queue can route each file to the right destination. To pull fields out of a document once it’s routed, refer to the [extract endpoint](https://www.nutrient.io/guides/dws-data-extraction/extract.md) guide.

## How classification works

Classification is zero-shot. The API scores the document against the labels in your request, not labels learned from a training set. There’s no model to train and no template to maintain. Switching to a new set of document types means sending a new label list.

Each document is scored two ways: once from its extracted text, and once from its page images. The two scores are merged into a single ranked list. Dedicated scoring models do the work, not a generative large language model (LLM) or vision language model (VLM).

## Request formats

Every classify request needs at least two labels. Each label has a `label` name and an optional `description`. The description matters: The classifier scores against it as much as against the name, so a specific description gives a sharper boundary than a vague one.

### Multipart form upload

Send the document as the `file` form field, and send the labels inside a JSON-serialized `instructions` field:

### curl

```shell

curl -X POST https://api.nutrient.io/extraction/classify \
  -H "Authorization: Bearer your_api_key_goes_here" \
  -F "file=@document.pdf" \
  -F 'instructions={"labels":[{"label":"invoice","description":"A commercial invoice or bill."},{"label":"contract","description":"A legal agreement between parties."},{"label":"resume","description":"A résumé or CV from a job applicant."}]}'

```

### Python

```python

import json

import requests

labels = [
    {"label": "invoice", "description": "A commercial invoice or bill."},
    {"label": "contract", "description": "A legal agreement between parties."},
    {"label": "resume", "description": "A résumé or CV from a job applicant."},
]

with open("document.pdf", "rb") as file:
    response = requests.post(
        "https://api.nutrient.io/extraction/classify",
        headers={"Authorization": "Bearer your_api_key_goes_here"},
        files={"file": file},
        data={"instructions": json.dumps({"labels": labels})},
    )

result = response.json()
if result["status"]!= 200:
    raise RuntimeError(result["errorMessage"])

print(result["output"]["classification"])

```

### JavaScript

```javascript

import fs from "node:fs";

const labels = [
  { label: "invoice", description: "A commercial invoice or bill." },
  { label: "contract", description: "A legal agreement between parties." },
  { label: "resume", description: "A résumé or CV from a job applicant." },
];

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("document.pdf")]), "document.pdf");
form.append("instructions", JSON.stringify({ labels }));

const response = await fetch("https://api.nutrient.io/extraction/classify", {
  method: "POST",
  headers: { Authorization: "Bearer your_api_key_goes_here" },
  body: form,
});

const result = await response.json();
if (result.status!== 200) {
  throw new Error(result.errorMessage);
}

console.log(result.output.classification);

```

### JSON URL input

To classify a document that’s already reachable at a URL, send a JSON body with `url` and `labels` as top-level properties:

```shell

curl -X POST https://api.nutrient.io/extraction/classify \
  -H "Authorization: Bearer your_api_key_goes_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://storage.example.com/contract.pdf",
    "labels": [
      { "label": "invoice", "description": "A commercial invoice or bill." },
      { "label": "contract", "description": "A legal agreement between parties." }
    ]
  }'

```

## Request options

All options go alongside `labels`, inside the multipart `instructions` JSON or the top level of a JSON body.

| Option        | Type    | Description                                                                                                                                                                                   |
| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `labels`      | array   | Required. At least two candidate labels. Each item has a `label` string and an optional `description`.                                                                                        |
| `topK`        | integer | Maximum number of ranked predictions to return. `0` or omitted returns all of them.                                                                                                           |
| `textWeight`  | number  | Set to `0` to skip the text branch and its text extraction. Any other value between `0` and `1` is accepted but doesn’t change the result; the engine balances the two branches per document. |
| `imageWeight` | number  | Set to `0` to skip the image branch and its page rendering. Any other value between `0` and `1` is accepted but doesn’t change the result; the engine balances the two branches per document. |

Leave at least one branch enabled. With both weights set to `0`, no branch produces a score.

## Response

A successful request 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": {
    "processingTimeMs": 1840,
    "pagesProcessed": 1
  },
  "usage": {
    "data_extraction_credits": {
      "cost": 1,
      "remainingCredits": 4999
    }
  }
}

```

| Field                               | Description                                                                               |
| ----------------------------------- | ----------------------------------------------------------------------------------------- |
| `output.classification.label`       | The top-ranked label.                                                                     |
| `output.classification.score`       | The top label’s score. Present when the engine returns one.                               |
| `output.classification.predictions` | Every candidate label with its score, highest first. `topK` caps the length of this list. |
| `metrics.pagesProcessed`            | The number of pages scored. This is the number of credits the request cost.               |

### Reading the scores

Each score is an independent confidence for that label, in the range `0` to `1`. The scores aren’t a probability distribution and don’t sum to 1. A cover letter attached to a contract can legitimately score high on both `correspondence` and `contract`.

That independence is what lets a router flag an ambiguous document instead of forcing it into one bucket. Compare the top score with the runner-up, and send close calls to manual review:

```python

def route(classification, routes, ambiguity_gap=0.15):
    predictions = classification["predictions"]
    top = predictions[0]
    runner_up = predictions[1] if len(predictions) > 1 else None

    if runner_up and top["score"] - runner_up["score"] < ambiguity_gap:
        return "queue:manual-review"

    return routes.get(top["label"], "queue:manual-review")

```

The threshold is yours to choose. Because the label list and the routes map are both parameters, the same function serves insurance intake, legal filings, or HR onboarding paperwork. For a complete worked example, refer to the [document routing walkthrough](https://www.nutrient.io/blog/route-documents-automatically-classify-api.md).

## Stored classifiers

You can save a label list as a classifier in the [Data Extraction Studio](https://dashboard.nutrient.io/data-extraction-api/studio/classify/) and run it by reference. Send `processor` with the classifier’s ID instead of `labels`, and optionally pin a published `version`. Add `storeRun: true` to keep the run in your processing history. Refer to the [API reference](https://www.nutrient.io/api/reference/data-extraction/public/) for the full request schema.

## Pricing

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

## Supported file types

The [API reference](https://www.nutrient.io/api/reference/data-extraction/public/) lists four input types for classify: `application/pdf`, `image/png`, `image/jpeg`, and `image/tiff`. Office documents, which the parse and extract endpoints accept, aren’t listed for classify. Refer to the [supported file types](https://www.nutrient.io/guides/dws-data-extraction/file-types.md) guide for the extensions and MIME types behind each format.

## Errors

A request with fewer than two labels returns HTTP 400 with `errorDetails.failingPaths` pointing at `$.labels`. Failed requests always return a JSON body with a non-200 `status` and an `errorMessage`. Refer to the [error handling](https://www.nutrient.io/guides/dws-data-extraction/errors.md) guide for the full status code reference.

## Try it

Open the [Classify workbench in Studio](https://dashboard.nutrient.io/data-extraction-api/studio/classify/) to run a document against your own labels before wiring the endpoint into a pipeline.