> 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.

# Rate

`POST /v1/decision-machine-1/rate` puts one text on a scale you define. You send the levels in order, from low to high. The model scores every level. The response gives you a weighted position (`score`), the winning level index (`level`), its text (`label`), a `confidence` and the full `scores` array. The levels are descriptions, not numbers. "Major outage" carries meaning; "4" does not.

## When to use it

* You need an ordered judgment: severity, priority, sentiment strength, harm, seniority, risk.
* You want a continuous number, not a bucket. `score` moves between levels.
* You want to combine several judgments. Run one rate call per dimension, then weight the scores with [composite scoring](/patterns/composite-scoring).
* Use [Classify](/capabilities/classify) instead when the options have no order, such as `billing` against `shipping`.
* Use [Yes / no](/capabilities/yes-no) instead when one threshold is the whole decision, such as "this ticket is urgent".

## Three example decisions

* An incident report, on the scale `No impact → Minor inconvenience → Degraded service → Major outage`.
* A product review, on the scale `Very negative → Negative → Mixed → Positive → Very positive`.
* A support message, on the scale `Calm → Annoyed → Frustrated → Threatening to leave`.

Each scale describes what each level looks like. The model reads the level text, so write levels a person could apply without a rubric.

## Request

| Field   | Type      | Required                 | Meaning                         | Limits                                     |
| ------- | --------- | ------------------------ | ------------------------------- | ------------------------------------------ |
| `text`  | string    | One of `text` or `texts` | The text to rate                | 1 to 20,000 characters                     |
| `texts` | string\[] | One of `text` or `texts` | A batch of texts                | 1 to 32 items, each 1 to 20,000 characters |
| `scale` | string\[] | Yes                      | The levels, ordered low to high | 2 to 10 items, each at least 1 character   |

`text` and `texts` are mutually exclusive. Every request needs an API key. See [Authentication](/authentication).

#### curl

```bash
curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/rate \
  -H "content-type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "text": "Our production database is down and every customer request is failing right now.",
    "scale": ["No impact", "Minor inconvenience", "Degraded service", "Major outage"]
  }'
```

#### Python

```python
import requests

response = requests.post(
    "https://api.milliseconds.ai/v1/decision-machine-1/rate",
    headers={"authorization": "Bearer test_sk-..."},
    json={
        "text": "Our production database is down and every customer request is failing right now.",
        "scale": ["No impact", "Minor inconvenience", "Degraded service", "Major outage"],
    },
    timeout=30,
)
result = response.json()
print(result["score"], result["level"], result["confidence"])
```

#### TypeScript

```typescript
const response = await fetch(
  "https://api.milliseconds.ai/v1/decision-machine-1/rate",
  {
    method: "POST",
    headers: { "content-type": "application/json", authorization: "Bearer test_sk-..." },
    body: JSON.stringify({
      text: "Our production database is down and every customer request is failing right now.",
      scale: ["No impact", "Minor inconvenience", "Degraded service", "Major outage"],
    }),
  },
);

const result = await response.json();
console.log(result.score, result.level, result.confidence);
```

## Response

```json
{"score":2.5,"level":2,"label":"Degraded service","confidence":0.5,"scores":[0,0,0.5,0.5]}
```

* `score` — number. The probability-weighted position, `Σ pᵢ · i`. It runs from 0 to `scale.length − 1`.
* `level` — integer. The index of the most likely level, the argmax of `scores`.
* `label` — string. The scale entry at `level`.
* `confidence` — number from 0 to 1. `1 − normalized entropy` over `scores`. A single peak gives 1. A flat spread gives 0.
* `scores` — number\[]. One normalized probability per level, in `scale` order. The array sums to 1.

Every number is rounded to three decimals.

> **Note**
>
> `scores` is an array, not an object. Read the level name from `label`. It is the entry of your `scale` array at index `level`.

## Reading the numbers

The example rates a severe incident on four levels. The response splits the mass evenly: `scores` is `[0, 0, 0.5, 0.5]`. "No impact" and "Minor inconvenience" are ruled out. "Degraded service" and "Major outage" tie.

Read each number:

* `score` 2.5 sits exactly between level 2 and level 3. This is the honest summary of a tie.
* `level` 2 is the argmax. The two scores print as a tie because every number is rounded to three decimals. The argmax reads the unrounded scores, so a printed tie can resolve either way.
* `confidence` 0.5 reports the split. The model picked the top half of the scale and went no further.

Two rules follow from this example.

**Route on `score`, not on `level`.** `score` keeps the ordering information that `level` throws away. A ticket at 2.5 outranks a ticket at 2.0, and both share `level` 2.

**Use `confidence` as a second axis.** High score plus high confidence acts. High score plus low confidence goes to a person. This example, at 0.5, belongs in a review queue. A calmer text on the same scale returned `{"score":0.852,"level":1,"label":"Minor inconvenience","confidence":0.691,"scores":[0.149,0.85,0.001,0]}` — one clear peak, and safe to route without review.

Pick your bands per action, not per system. [Thresholds and confidence routing](/concepts/thresholds) gives the act, confirm and escalate bands.

> **Warning**
>
> `score` is a position on your scale, not a percentage. On a 4-level scale the top is 3.0, not 1.0. Divide by `scale.length − 1` when you need a 0 to 1 number.

## Batching

Send `texts` instead of `text`. One `scale` applies to every text. The response is `{ "results": [...] }`, one entry per text, in input order.

#### curl

```bash
curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/rate \
  -H "content-type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "texts": [
      "Our production database is down and every customer request is failing right now.",
      "Please send me the updated invoice when you get a chance, no rush."
    ],
    "scale": ["No impact", "Minor inconvenience", "Degraded service", "Major outage"]
  }'
```

#### Python

```python
import requests

response = requests.post(
    "https://api.milliseconds.ai/v1/decision-machine-1/rate",
    headers={"authorization": "Bearer test_sk-..."},
    json={
        "texts": [
            "Our production database is down and every customer request is failing right now.",
            "Please send me the updated invoice when you get a chance, no rush.",
        ],
        "scale": ["No impact", "Minor inconvenience", "Degraded service", "Major outage"],
    },
    timeout=60,
)
for item in response.json()["results"]:
    print(item["score"], item["confidence"])
```

#### TypeScript

```typescript
const response = await fetch(
  "https://api.milliseconds.ai/v1/decision-machine-1/rate",
  {
    method: "POST",
    headers: { "content-type": "application/json", authorization: "Bearer test_sk-..." },
    body: JSON.stringify({
      texts: [
        "Our production database is down and every customer request is failing right now.",
        "Please send me the updated invoice when you get a chance, no rush.",
      ],
      scale: ["No impact", "Minor inconvenience", "Degraded service", "Major outage"],
    }),
  },
);

const { results } = await response.json();
results.forEach((item) => console.log(item.score, item.confidence));
```

Real response:

```json
{
  "results": [
    {"score":2.5,"level":2,"label":"Degraded service","confidence":0.5,"scores":[0,0,0.5,0.5]},
    {"score":0.852,"level":1,"label":"Minor inconvenience","confidence":0.691,"scores":[0.149,0.85,0.001,0]}
  ]
}
```

Each text costs one inference call, so a batch of 32 costs 32 calls. [Batching](/capabilities/batching) covers result ordering and the 32 limit.

## Limits and gotchas

| Limit           | Value                      |
| --------------- | -------------------------- |
| `scale` items   | 2 to 10                    |
| `text` length   | 20,000 characters          |
| `texts` items   | 32                         |
| Typical latency | 0.42 s for a 4-level scale |

* **Write levels as descriptions.** `["1","2","3","4","5"]` gives the model nothing to read. Use words that describe the case. [Writing good statements and labels](/concepts/writing-good-questions) covers the level text.
* **Keep the order right.** Index 0 is the low end. `score` and `level` mean nothing if your scale is unsorted.
* **Keep the levels distinct.** Adjacent levels that overlap in meaning produce the tie you saw above, and a low `confidence`.
* **Long text is chunked at 2,000 characters.** A level's score is its maximum over the chunks, so a single severe paragraph can lift the rating of a long document. [Long text and chunking](/concepts/long-text) explains the split.
* **Errors.** A scale of one item returns `400 invalid_request` with the message `scale: Too small: expected array to have >=2 items`. A body with neither `text` nor `texts` returns `body: provide text, texts or image`. That message is misleading in the second case. [Errors](/reference/errors) lists every code.

> **Tip**
>
> One rate call answers one question. Split a broad judgment such as "lead quality" into separate calls for budget, authority and fit, then weight the scores in your own code.

## 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/rate \
  -H "content-type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
    "detail": "low",
    "scale": ["Unreadable", "Poor", "Readable", "Clean"],
    "question": "How legible is this scan?"
  }'
```

The response shape does not change: `score`, `level`, `label`, `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

* [Thresholds and confidence routing](/concepts/thresholds) — turn `score` and `confidence` into act, confirm and escalate bands.
* [Composite scoring](/patterns/composite-scoring) — combine several rate calls into one number you control.
* [Content moderation](/recipes/content-moderation) — rate harm, flag hard cases, route the rest to a person.