Skip to navigation

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' \
-H 'authorization: Bearer test_sk-...' \
-d '{
"text": "John Ternus announced the iPhone Duo in Cupertino on 9 September.",
"types": ["person", "product", "location"]
}'
Every request needs an API key. See Authentication.

Response

{
"entities": [
{"type": "person", "text": "John Ternus", "probability": 0.999, "start": 0, "end": 11},
{"type": "product", "text": "iPhone Duo", "probability": 0.973, "start": 26, "end": 36},
{"type": "location", "text": "Cupertino", "probability": 0.999, "start": 40, "end": 49}
]
}
  • 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' \
-H 'authorization: Bearer test_sk-...' \
-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.42 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, texts or image. 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.

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.

curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/entities \
-H "content-type: application/json" \
-H "authorization: Bearer test_sk-..." \
-d '{
"image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"detail": "low",
"types": {
"merchant": "The shop or company name",
"amount": "A money amount"
}
}'

The response keeps the same label-keyed items. An image has no character string to index into, so start, end and probability are null on every item. Code that redacts by offset has nothing to index on an image.

image works with text, never with texts. The image bills a fixed number of tokens: 2,000 for low, 4,000 for medium, 8,000 for high. The rest of the body bills as today. See Images.

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.