> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.milliseconds.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.milliseconds.ai/_mcp/server.

# Classify

`POST /v1/decision-machine-1/classify` puts one text into one label. You send the text and between
2 and 64 labels. The API returns the winning label name, its probability, a confidence number, and
the full score distribution. The model reads the label text itself. A label named `billing` with
the description `payments, invoices, charges, refunds` therefore scores better than a bare name.
Typical latency is about 0.4 seconds end to end. Every request needs an API key. See [Authentication](/authentication).

## When to use it

* You route text into one of several buckets: intents, topics, queues, tiers.
* You need the runner-up scores, not only the winner.
* You want a stable number to threshold on, not a generated word.
* Use [Yes / no](/capabilities/yes-no) instead when the two "labels" are one claim and its
  negation.
* Use [Rate](/capabilities/rate) instead when the labels are ordered levels, such as calm to angry.

## Three example decisions

1. A support message arrives. Classify it into `billing`, `shipping`, or `account`, then put it in
   that queue.
2. A user sentence arrives at an agent. Classify it over tool descriptions and call the winning tool.
3. A document paragraph arrives. Classify it into a taxonomy node, then walk one level deeper.

## Request

| Field    | Type      | Required                           | Meaning                          | Limits                                                  |
| -------- | --------- | ---------------------------------- | -------------------------------- | ------------------------------------------------------- |
| `text`   | string    | one of `text` / `texts`            | The text to classify             | 1 to 20,000 characters                                  |
| `texts`  | string\[] | one of `text` / `texts`            | A batch of texts                 | 1 to 32 items, each 1 to 20,000 characters              |
| `labels` | string\[] | yes (this form or the object form) | Label names                      | 2 to 64 items, each at least 1 character                |
| `labels` | object    | yes (this form or the array form)  | Map of label name to description | Keys at least 1 character; no count limit in the schema |

The API sends `"name: description"` to the model when a description exists. It sends the bare name
otherwise. The response always returns the label **name**, never the description.

#### curl

```bash
curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/classify \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer test_sk-...' \
  -d '{
    "text": "I was charged twice for my subscription this month and support has not replied.",
    "labels": {
      "billing": "payments, invoices, charges, refunds",
      "shipping": "delivery, tracking, returns of physical goods",
      "account": "login, password, profile settings"
    }
  }'
```

#### Python

```python
import requests

response = requests.post(
    "https://api.milliseconds.ai/v1/decision-machine-1/classify",
    headers={"authorization": "Bearer test_sk-..."},
    json={
        "text": "I was charged twice for my subscription this month and support has not replied.",
        "labels": {
            "billing": "payments, invoices, charges, refunds",
            "shipping": "delivery, tracking, returns of physical goods",
            "account": "login, password, profile settings",
        },
    },
)
result = response.json()
print(result["label"], result["probability"], result["confidence"])
```

#### TypeScript

```typescript
const response = await fetch(
  "https://api.milliseconds.ai/v1/decision-machine-1/classify",
  {
    method: "POST",
    headers: { "content-type": "application/json", authorization: "Bearer test_sk-..." },
    body: JSON.stringify({
      text: "I was charged twice for my subscription this month and support has not replied.",
      labels: {
        billing: "payments, invoices, charges, refunds",
        shipping: "delivery, tracking, returns of physical goods",
        account: "login, password, profile settings",
      },
    }),
  },
);

const result = await response.json();
console.log(result.label, result.probability, result.confidence);
```

## Response

```json
{
  "label": "billing",
  "probability": 0.995,
  "confidence": 0.97,
  "scores": {
    "billing": 0.995,
    "shipping": 0,
    "account": 0.005
  }
}
```

* `label` — the name of the winning label.
* `probability` — the winner's normalized share, between 0 and 1, rounded to 3 decimals.
* `confidence` — `1 − normalized entropy` over `scores`. 1 means one clear winner. 0 means a flat
  distribution.
* `scores` — one normalized probability per label name. The values sum to 1, before 3-decimal
  rounding.

Two response headers report the input size. `x-input-chars` is the number of input characters.
`x-input-tokens` is the input tokens billed for this call. See [Pricing](/reference/pricing).

## Reading the numbers

The response above says three things. The winner is `billing` with a 0.995 share of the
distribution. `confidence` 0.97 says the distribution has one sharp peak, so the runner-up is far
behind. `scores` shows the same fact from the other side: `shipping` scored 0 and `account` scored
0.005.

Now read an unclear case. The text `"Hi, I have a question about my order."` over the same labels
returns:

```json
{
  "label": "billing",
  "probability": 0.52,
  "confidence": 0.067,
  "scores": {
    "billing": 0.52,
    "shipping": 0.244,
    "account": 0.237
  }
}
```

The winner still looks acceptable at 0.52, but `confidence` 0.067 reports a near-flat
distribution. Two labels sit within 0.01 of each other. Route this message to a person, not to a
queue.

Use both numbers in your rule. Act when `probability` and `confidence` are both high. Ask the user
when `probability` is high and `confidence` is low. Escalate every other case. Pick the exact
cut-offs per action from your own data, as
[Thresholds and confidence routing](/concepts/thresholds) describes.

> **Tip**
>
> Label descriptions move the numbers. The same text over the bare names `billing`, `shipping` and
> `account` returned a tie: `probability` 0.5 for `billing`, `confidence` 0.369, with `account` also
> at 0.5. The described labels returned 0.995 and 0.97.

## Batching

Send `texts` instead of `text` to classify up to 32 texts in one call. The response is always
`{"results": [...]}`, one entry per text, in input order.

```bash
curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/classify \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer test_sk-...' \
  -d '{
    "texts": [
      "I was charged twice for my subscription this month.",
      "Where is my package? Tracking has not updated in six days.",
      "I cannot log in, the password reset email never arrives."
    ],
    "labels": {
      "billing": "payments, invoices, charges, refunds",
      "shipping": "delivery, tracking, returns of physical goods",
      "account": "login, password, profile settings"
    }
  }'
```

```json
{
  "results": [
    {"label":"billing","probability":0.999,"confidence":0.993,"scores":{"billing":0.999,"shipping":0,"account":0.001}},
    {"label":"shipping","probability":0.997,"confidence":0.98,"scores":{"billing":0.001,"shipping":0.997,"account":0.002}},
    {"label":"account","probability":1,"confidence":0.996,"scores":{"billing":0,"shipping":0,"account":1}}
  ]
}
```

One label set applies to the whole batch. Each text costs one inference call, so a batch of 32
costs 32 calls. See [Batching](/capabilities/batching) for how batches spread across inference slots.

## Limits and gotchas

* `text` holds up to 20,000 characters. `texts` holds up to 32 items.
* The array form of `labels` takes 2 to 64 items. One label returns
  `{"error":{"code":"invalid_request","message":"labels: Too small: expected array to have >=2 items"}}`.
* `text` and `texts` are mutually exclusive. Sending both fails with
  `body: provide text, texts or image`. Sending **neither** fails with the same message, which is
  misleading in that case.
* Label names must describe the case. Names such as `type_a` and `other` carry no meaning for the
  model. Write a description for every label.
* The API chunks texts over 2,000 characters. A label's score is its **maximum over the chunks**,
  so a topic that appears only in the last paragraph still scores high. Cost stays linear in
  characters. [Long text and chunking](/concepts/long-text) has the detail.
* The object form of `labels` has no count limit. One label always returns `probability` 1 and
  `confidence` 1, whatever the text says. Send two or more labels.
* `scores` keys are the label names you sent, including names with spaces or punctuation.

> **Warning**
>
> `probability` is a normalized share of the label set you sent, not a calibrated truth value. Adding
> or removing a label changes every number. Re-tune your thresholds after you change the label set.

## Images

Send `image` instead of `text` to run this capability over a photograph or a scanned page. `image` is
a `data:image/jpeg;base64,` URL or bare base64 of a JPEG, PNG or WebP, at most 5 MB decoded. `detail`
picks the resolution: `low` is 512 px, `medium` is 768 px and is the default, `high` is 1024 px.

```bash
curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/classify \
  -H "content-type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
    "detail": "low",
    "labels": {
      "invoice": "A bill that asks for payment",
      "receipt": "Proof that a payment happened",
      "contract": "An agreement between two parties"
    }
  }'
```

The response shape does not change: `label`, `probability`, `confidence` and `scores`.

`image` works with `text`, never with `texts`. The image bills a fixed number of tokens:
1,000 for `low`, 2,000 for `medium`, 4,000 for `high`. The rest of the body bills as today. See [Images](/concepts/images).

## Next

* [Writing good statements and labels](/concepts/writing-good-questions) — how to word a label so the model reads it correctly.
* [Thresholds and confidence routing](/concepts/thresholds) — turn `probability` and `confidence` into an action.
* [Intent routing](/patterns/intent-routing) — classify as a router in front of tools or agents.