Named-Entity Recognition API
The NER endpoint finds named entities in a text — people, organizations, locations, dates, times, money amounts, percentages, quantities, products and events — and returns each one with exact character offsets into the text you sent.
Recognition is model-based, but every entity is grounded: each mention the model proposes is located in your text before it is returned, and a mention the text does not actually contain is dropped. The text is never tag-stripped or normalized on this endpoint, so the returned offsets line up with your own copy of the document — the same offsets contract as the PII API. The two endpoints are complements: PII detection finds format- and checksum-validated identifiers (emails, card numbers, IBANs), NER finds names and real-world references.
Sample Code
- cURL
- JavaScript
- Python
curl -X POST https://api.sapling.ai/api/v1/ner \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "text":"Sapling opened a Toronto office on March 3, 2026."}'
import axios from 'axios';
async function run(text) {
try {
const response = await axios.post(
'https://api.sapling.ai/api/v1/ner',
{
key: '<api-key>',
text,
},
);
const {status, data} = response;
console.log({status});
console.log(JSON.stringify(data, null, 4));
} catch (err) {
const msg = err.response?.data?.msg || err.message;
console.log({err: msg});
}
}
run('Sapling opened a Toronto office on March 3, 2026.');
import requests
from pprint import pprint
response = requests.post(
"https://api.sapling.ai/api/v1/ner",
json={
"key": "<api-key>",
"text": "Sapling opened a Toronto office on March 3, 2026.",
}
)
if 200 <= response.status_code < 300:
pprint(response.json())
else:
print('Error: ', response.status_code, response.text)
Sample Response
{
"entities": [
{
"text": "Sapling",
"type": "organization",
"start": 0,
"end": 7
},
{
"text": "Toronto",
"type": "location",
"start": 17,
"end": 24
},
{
"text": "March 3, 2026",
"type": "date",
"start": 35,
"end": 48
}
],
"types": ["date", "location", "organization"]
}
Batch Requests
To analyze many short texts — a queue of support tickets, a set of retrieved documents — in one
request, send texts (a list of 1–10 strings) instead of text. The same types apply to every
item, and the combined length of all items may be up to 10,000 characters (the same cap as a single
text). Exactly one of text and texts must be provided.
curl -X POST https://api.sapling.ai/api/v1/ner \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "texts": ["Sapling is hiring in Toronto.", "Invoices over $500 need approval by Friday."]}'
The batch response is {"results": [...]} with one entry per input, in input order; each entry has
exactly the single-response shape above, and each entry's offsets index that item's own text:
{
"results": [
{
"entities": [
{"text": "Sapling", "type": "organization", "start": 0, "end": 7},
{"text": "Toronto", "type": "location", "start": 21, "end": 28}
],
"types": ["location", "organization"]
},
{
"entities": [
{"text": "$500", "type": "money", "start": 14, "end": 18},
{"text": "Friday", "type": "date", "start": 36, "end": 42}
],
"types": ["date", "money"]
}
]
}
Items are billed individually (a batch of N costs the same as N single requests), and each item is
cached the same way as a single request — batches and single calls share the cache. If any item fails
to process, the whole request returns a 502 and nothing is billed; the items that did succeed are
already cached, so a retry only re-runs the failures.
Request Parameters
POST to https://api.sapling.ai/api/v1/ner
key: String
32-character API key. Can also be supplied via the Authorization header as a bearer token; if both are provided, the key parameter takes precedence.
text: String
The text to analyze, up to 10,000 characters. The text is analyzed exactly as sent — HTML is not
stripped and whitespace is not normalized — so that the returned offsets line up with your original.
Pass the form of the document you want spans into; for the best recognition quality, prefer plain text
over heavy markup. Provide exactly one of text and texts.
texts: List[String]
Batch form (see Batch Requests): 1–10 texts to analyze in one request, each treated
exactly like text above, with a combined length of up to 10,000 characters. An empty item, or an item
containing a NUL character, returns a 400 naming its index. The response becomes
{"results": [...]}, one entry per item in input order.
types: List
Optional list of entity types to recognize. Defaults to all types. Supported values:
| Type | What it matches |
|---|---|
person | Named people, real or fictional, including titles that are part of the name ("Dr. Jane Smith") |
organization | Companies, agencies, institutions, teams and other named groups |
location | Countries, cities, regions, street addresses, landmarks and geographic features |
date | Calendar expressions, absolute or relative ("March 5, 2026", "last Tuesday", "Q3 2025") |
time | Times of day and clock expressions ("3pm", "14:30", "noon") |
money | Monetary amounts including their currency symbol or unit ("$1,299.00", "20 euros") |
percent | Percentages, including the sign or the word ("25%", "twenty percent") |
quantity | Measurements with their unit ("5 km", "two litres", "3.5 GHz") |
product | Named products, models, vehicles and software ("iPhone 15", "Boeing 747") |
event | Named events: conferences, sports events, holidays, wars ("WWDC 2026", "the Olympics") |
Response Parameters
entities: List
The recognized entities, in document order. Each item has:
text: the entity's text, exactly as it appears in the input.type: one of the types listed above.start,end: character offsets into the input text, such thattext[start:end]is the entity.
Every occurrence of a recognized mention is returned as its own span, so an entity that appears three times in the document produces three items.
types: List
The sorted list of distinct entity types found in the text.
Notes
- Offsets count Unicode code points (Python
strindexing), not UTF-16 code units or bytes. In JavaScript, useArray.from(text)if the input may contain characters outside the Basic Multilingual Plane. - Results are cached, so repeating a request (or retrying the failed items of a batch) with the same text and types is fast and is not billed twice.
- A model or upstream failure returns a
502and is not billed — retry rather than changing the request. - For format- and checksum-validated identifiers — emails, phone numbers, card numbers, IBANs — with the same offsets contract, see the PII API.