Entities

Find every span of every type you name, with character offsets you can slice.

POST /v1/decision-machine-1/entities takes your text and a list of types. It returns every span that matches a type, sorted by start offset. Each entity carries its type, the matched text, a probability, and character offsets. You name the types yourself. There is no fixed taxonomy.

When to use it

  • You need all occurrences of something, not one best answer.
  • You need offsets to redact, highlight, or link spans in the original text.
  • You need a custom type set: case_number, drug_name, ticker, policy_id.
  • Use Extract instead when you want one typed record with named fields. Use Answer instead when you want one span per question.

Three example decisions

  • Find every person, organization, and location in a news paragraph.
  • Find every email address, phone number, and account number in a support message, then redact the offsets.
  • Find every drug name and dosage in a clinical note, then count them.

Request

FieldTypeRequiredMeaningLimits
textstringone of text / textsThe text to scan1 to 20,000 characters
textsstring[]one of text / textsA batch of texts1 to 32 items, each 1 to 20,000 characters
typesstring[]yes (this form or the object form)Type names. Each name is its own description.1 to 64 items, each at least 1 character
typesobjectyes (this form or the array form)Map of type name to descriptionNo count cap

Send text or texts, never both. The object form gives the model a description per type. Describe any type whose name alone is ambiguous.

curl -s -X POST https://api.milliseconds.ai/v1/decision-machine-1/entities \
-H 'content-type: application/json' \
-d '{
"text": "Tim Cook announced the iPhone 17 in Cupertino on 9 September.",
"types": ["person", "product", "location"]
}'
No API key is required during the launch period.

Response

{
"entities": [
{"type": "person", "text": "Tim Cook", "probability": 0.999, "start": 0, "end": 8},
{"type": "product", "text": "iPhone 17", "probability": 0.993, "start": 23, "end": 32},
{"type": "location", "text": "Cupertino", "probability": 0.999, "start": 36, "end": 45}
]
}
  • entities — every matching span, sorted by start ascending.
  • entities[].type — the type name you requested, copied back.
  • entities[].text — the matched span.
  • entities[].probability — the span confidence, 0 to 1, rounded to 3 decimals.
  • entities[].start and entities[].end — character offsets into your text. They are never null on this capability.

The slice text[start:end] equals entities[].text. Use the offsets, not a string search: the same string can occur many times.

An empty result is a normal result. A text with no match for any requested type returns an empty array:

{"entities": []}

Reading the numbers

This request maps each type to a description:

{
"text": "Contact Maria Alvarez at maria.alvarez@northwind.example or +1 415 555 0132. Her account number is 4471-8890.",
"types": {
"email": "an email address",
"phone": "a telephone number",
"account_number": "a customer account or card number",
"person": "the full name of a person"
}
}

It returns:

{
"entities": [
{"type": "person", "text": "Maria Alvarez", "probability": 0.999, "start": 8, "end": 21},
{"type": "email", "text": "maria.alvarez@northwind.example", "probability": 0.994, "start": 25, "end": 56},
{"type": "phone", "text": "+1 415 555 0132", "probability": 0.998, "start": 60, "end": 75},
{"type": "account_number", "text": "4471-8890", "probability": 0.991, "start": 99, "end": 108}
]
}

Read account_number at 0.991. The probability is the raw span confidence from the extractor. It is not normalized across types, so the numbers do not sum to 1. Each entity stands alone.

Pick one threshold per action:

ActionThresholdWhy
Redact before storageKeep everythingA missed span leaks data. A wrong redaction costs little.
Highlight in a UI0.5 and aboveThe reader corrects the rest.
Write to a database field0.9 and aboveRoute the rest to a person.

Raise the bar with the blast radius. Thresholds and confidence routing covers the bands.

This capability returns no confidence field. confidence exists on Classify and Rate, where the scores compete over one label set.

Batching

Send texts instead of text. The response is {"results": [...]}, one entry per text, in input order.

curl -s -X POST https://api.milliseconds.ai/v1/decision-machine-1/entities \
-H 'content-type: application/json' \
-d '{
"texts": [
"Acme Corp signed with Globex in Berlin.",
"Jane Foster joined Initech in Austin."
],
"types": ["organization", "location"]
}'
{
"results": [
{
"entities": [
{"type": "organization", "text": "Acme Corp", "probability": 0.984, "start": 0, "end": 9},
{"type": "organization", "text": "Globex", "probability": 0.979, "start": 22, "end": 28},
{"type": "location", "text": "Berlin", "probability": 0.999, "start": 32, "end": 38}
]
},
{
"entities": [
{"type": "organization", "text": "Initech", "probability": 0.993, "start": 19, "end": 26},
{"type": "location", "text": "Austin", "probability": 0.999, "start": 30, "end": 36}
]
}
]
}

The second text names Jane Foster. You did not request a person type, so no person span comes back. The model returns only the types you name.

Offsets are per text. Index result n against texts[n]. Each text is a separate inference call, so a batch of 32 fans out to 32 calls. Statements and questions follow different rules, covered in Batching.

Limits and gotchas

LimitValue
text length20,000 characters
texts items32
types array items1 to 64
types object keysNo count cap
Typical latency0.50 s for 3 types on a short text
  • An empty types array fails: {"error":{"code":"invalid_request","message":"types: Too small: expected array to have >=1 items"}}.
  • Sending both text and texts fails with body: provide text or texts, not both. Sending neither returns the same message.
  • The common mistake: vague type names. number matches dates, quantities and ids. Name the type for the case, or move to the object form and describe it. Writing good statements and labels has the rules.
  • Duplicate mentions each return their own entity. Deduplicate on text in your own code when you want a set.
  • Over 2,000 characters the service scans overlapping windows and remaps the offsets, so the offsets stay correct against your original string. Cost is linear in characters. Long text and chunking covers the windows.
  • The extractor is multilingual. Type descriptions in English work on non-English text. Languages carries the measurements.
  • A 502 runner_error or a 529 overloaded means retry with backoff. Errors lists every code.

Next

  • PII detection — described PII types and offset redaction, end to end.
  • Choosing a capability — entities against extract, and the other near misses.
  • Extract — one typed record instead of a span list.