Unstructured text carries structured information: who is mentioned, what organization, what date, what the passage is actually about. DetectEntities and DetectKeyPhrases pull that structure out without a custom model or any training step.
This page covers both operations, how their outputs relate, and the offset-based design that lets you map results back onto the original text - for highlighting, indexing, or feeding into a downstream pipeline.
# --- Python (boto3) ---import boto3comprehend = boto3.client("comprehend")entities = comprehend.detect_entities( Text="Acme Corp announced a partnership with Nova Labs in Austin.", LanguageCode="en",)["Entities"]phrases = comprehend.detect_key_phrases( Text="Acme Corp announced a partnership with Nova Labs in Austin.", LanguageCode="en",)["KeyPhrases"]
// --- TypeScript (AWS SDK v3) ---import { ComprehendClient, DetectEntitiesCommand, DetectKeyPhrasesCommand } from "@aws-sdk/client-comprehend";const comprehend = new ComprehendClient({});const text = "Acme Corp announced a partnership with Nova Labs in Austin.";const { Entities } = await comprehend.send(new DetectEntitiesCommand({ Text: text, LanguageCode: "en" }));const { KeyPhrases } = await comprehend.send(new DetectKeyPhrasesCommand({ Text: text, LanguageCode: "en" }));
When to reach for this:
Tagging support tickets or articles with who/what/where without hand-written rules.
Building a lightweight search index from free text without a full NLP pipeline.
Highlighting mentioned entities or key terms in a UI over the original document.
Extracting structured fields from semi-structured text (emails, reports, transcripts).
DetectEntities scans text for spans matching built-in types: PERSON, LOCATION, ORGANIZATION, COMMERCIAL_ITEM, EVENT, DATE, QUANTITY, TITLE, and a catch-all OTHER. Each match is an object with Type, Text, Score, BeginOffset, and EndOffset. The offsets are character indices into the exact string you sent, not into any tokenized or normalized version of it - a common cause of off-by-one bugs when a document is preprocessed differently before display than before detection.
DetectKeyPhrases is untyped by design - it returns KeyPhrases, each with Text, Score, BeginOffset, EndOffset, but no category. A key phrase is a noun-phrase fragment ("the quarterly earnings report") rather than a single named entity. It often overlaps with entity spans (a company name may show up as both an ORGANIZATION entity and a key phrase) but the two calls are computed independently and neither result set is derived from the other.
A common pattern is to use entities for typed tagging (link a PERSON match to a CRM record, an ORGANIZATION match to a company database) and key phrases for a broader "what is this about" summary or search index. Because both use the same offset contract, you can merge them into one ordered list, as in the working example, without reconciling two different coordinate systems.
Each call operates on a single text blob with a per-call size limit (check the current service quota for your input format). For documents longer than the limit, split by paragraph or sentence boundary rather than an arbitrary character count, and keep a running offset so results from each chunk map back to positions in the original, unsplit document.
Treating offsets as token indices - BeginOffset/EndOffset are character positions in the exact string sent, not word or token counts. Fix: slice the original string with text[begin:end] (Python) or text.slice(begin, end) (TypeScript) to recover the match.
Re-detecting after reformatting text - if you strip whitespace, normalize unicode, or translate the text before rendering, old offsets no longer line up. Fix: run Detect on the exact string you will render, or keep a mapping between the original and transformed text.
Assuming key phrases are typed like entities - KeyPhrases has no Type field; do not expect a category. Fix: use DetectEntities when you need typed categories, DetectKeyPhrases for general topical signal.
Sending a whole large document in one call - long input either gets rejected or truncated at the service limit. Fix: chunk by paragraph and track cumulative offsets per chunk.
Dropping low-confidence matches by accident - some pipelines filter on Score but forget both entities and phrases carry it. Fix: apply a consistent minimum-score threshold across both result sets if you filter at all.
Expecting duplicate mentions to be deduplicated - the same organization name mentioned three times returns three separate entities. Fix: deduplicate downstream by normalized Text if you need unique mentions.
What entity types does DetectEntities recognize out of the box?
Built-in types include PERSON, LOCATION, ORGANIZATION, COMMERCIAL_ITEM, EVENT, DATE, QUANTITY, TITLE, and OTHER. For types outside this list, train a Comprehend Custom entity recognizer.
Do key phrases have a category like entities do?
No. DetectKeyPhrases returns text, score, and offsets only - it identifies meaningful noun phrases without assigning a type.
How do I get the exact matched text back from an offset?
Slice the original string with BeginOffset and EndOffset - text[begin:end] in Python or text.slice(begin, end) in TypeScript - using the exact string you sent to Comprehend.
Can the same word show up as both an entity and a key phrase?
Yes. The two calls are independent, so overlap is normal - an organization name can appear as an ORGANIZATION entity and also within a key phrase span.
What should I do with a document longer than the size limit?
Split it by paragraph or sentence boundary, call Detect on each chunk, and keep a running offset so chunk-relative positions map back to the original document.
Is there a bulk version of these calls?
Yes - StartEntitiesDetectionJob and StartKeyPhrasesDetectionJob run the same models asynchronously over an S3 prefix of documents instead of one string at a time.
Should I filter results by Score?
It depends on your tolerance for false positives. Both entities and key phrases carry a Score; a common starting threshold is to drop matches below roughly 0.5-0.7 confidence, tuned to your data.