Messy text is where useful data goes to hide.
A customer mentions a company name in the middle of a support rant. A sales call transcript includes three people, two dates, one competitor, and a budget number nobody added to the CRM. A legal note has organizations, addresses, case names, dates, and contract terms scattered across ten paragraphs. A resume contains skills, employers, universities, locations, job titles, and dates, all formatted like the writer had a personal war with consistency.
That is where Named Entity Recognition helps.
Named Entity Recognition, or NER, turns text like this:
Maria Chen from BrightCart emailed Acme Logistics on July 14 about the delayed Chicago shipment.
into structured data like this:
{
"people": ["Maria Chen"],
"organizations": ["BrightCart", "Acme Logistics"],
"dates": ["July 14"],
"locations": ["Chicago"]
}
Much nicer.
This guide shows how to do Named Entity Recognition with JavaScript using LLMAPI, so messy text can become cleaner structured data. We’ll also look at top tools for NER, how they work, when to use LLMAPI instead of a classic NER API, and the best practices that keep your extraction pipeline from turning into a beautiful JSON hallucination machine.
What is Named Entity Recognition?
Named Entity Recognition is an NLP task that finds named things in text and classifies them into categories.
Common entity types include:
| Entity type | Example |
|---|---|
| Person | Maria Chen |
| Organization | Acme Logistics |
| Location | Chicago |
| Date | July 14, 2026 |
| Money | $12,500 |
| Product | Salesforce |
| Event | Black Friday |
| Job title | Head of Operations |
| Skill | Python |
| [email protected] | |
| Phone number | +1 312 555 0182 |
| URL | https://example.com |
Classic NER tools usually focus on common categories like people, organizations, locations, dates, and money. LLM-based extraction can go further and pull custom entities like:
customer pain point
contract obligation
software feature
compliance risk
recipe ingredient
sales objection
job requirement
That flexibility is the fun part.
It is also the part where validation becomes very important.
Why NER matters in real apps
NER helps your app turn unstructured text into something searchable, sortable, filterable, and automatable.
For example, NER can help with:
- CRM enrichment.
- Support ticket routing.
- Contract analysis.
- Resume parsing.
- Invoice and receipt processing.
- News monitoring.
- Medical text extraction.
- Legal document review.
- Customer feedback analysis.
- Knowledge graph building.
- Search indexing.
- Compliance workflows.
Without NER, your app sees this:
“John from Northwind asked if the Zurich contract renewal could move to Sept 3.”
With NER, your app sees this:
{
"person": "John",
"organization": "Northwind",
"location": "Zurich",
"date": "Sept 3",
"event_or_action": "contract renewal"
}
That structured layer makes the text usable.
Why we can write this guide
We’ve spent around 6 years working with AI APIs, NLP workflows, document parsing, structured extraction, embeddings, LLM-based automation, and JavaScript backend integrations. We also checked current docs for LLMAPI, Google Cloud Natural Language, Amazon Comprehend, Azure AI Language, spaCy, and recent NER research while preparing this guide.
The research direction is pretty clear: NER is no longer only a classic “tag words in a sentence” task. Modern systems mix rule-based extraction, statistical models, transformers, domain-specific models, and LLMs depending on the data and accuracy needs. A 2025 review of NER in Artificial Intelligence Review covers the field from learning methods to modeling paradigms and tasks. A 2026 paper on NERLLM argues for hybrid NER setups that combine local NER models with LLM reasoning, which is very close to how practical production systems are moving.
Translation for builders: use the tool that matches the job. Classic NER is fast and predictable. LLM-based NER is flexible and better for custom schemas. A strong product may use both.
What does “NER with JavaScript and LLMAPI” mean?
In this article, JavaScript is the backend language and LLMAPI is the model gateway.
The workflow looks like this:
text
→ JavaScript backend
→ LLMAPI extraction prompt
→ structured JSON
→ validation
→ app/database/workflow
Example:
“Please follow up with Anna Petrova at Stripe about the $8,000 invoice due next Friday.”
Output:
{
"people": [
{
"name": "Anna Petrova",
"role": null
}
],
"organizations": [
{
"name": "Stripe"
}
],
"money": [
{
"amount": 8000,
"currency": "USD",
"raw_text": "$8,000"
}
],
"dates": [
{
"raw_text": "next Friday",
"normalized": null
}
],
"actions": [
{
"type": "follow_up",
"description": "Follow up about the invoice"
}
]
}
The important part is that LLMAPI can extract entities based on your schema, not only a fixed set of built-in labels.
That is useful when your app has its own entity types.
Classic NER vs LLM-based NER
Both approaches are useful.
| Approach | Best for | Watch out for |
|---|---|---|
| Classic NER model | Fast extraction of common entities | Limited custom categories |
| Rule-based extraction | Emails, phone numbers, IDs, dates, SKUs | Brittle with messy text |
| Cloud NER API | Stable entity extraction at scale | Provider-specific categories |
| LLM-based NER | Custom entities and flexible schemas | Needs validation and guardrails |
| Hybrid NER | Production workflows with quality control | More setup and evaluation |
A practical setup often looks like this:
regex/rules for obvious things
+ NER API for common entities
+ LLMAPI for custom entities and reasoning
+ validation before saving
That mix gives you better control than asking one model to do everything.
Top tools for NER workflows
Here are the tools worth knowing.
| Tool | Best for |
|---|---|
| LLMAPI | Custom entity extraction, schema-based JSON, summaries, routing |
| Google Cloud Natural Language | Cloud entity analysis and salience |
| Amazon Comprehend | AWS-native entity detection and custom entities |
| Azure AI Language | Microsoft/Azure NER and custom NER workflows |
| spaCy | Local open-source NER and custom pipelines |
| compromise | Lightweight JavaScript NLP |
| winkNLP | Fast JavaScript NLP pipelines |
| Transformers.js | Running transformer models in JavaScript |
| Regex/rules | Emails, phone numbers, URLs, IDs, SKUs |
| Vector search + LLMAPI | Entity linking and knowledge-base enrichment |
Now let’s look at how these fit.
1. LLMAPI for custom structured extraction
LLMAPI is the best fit when your entity schema is custom, flexible, or business-specific.
Classic NER can find:
person, organization, location, date
LLMAPI can extract:
customer complaint, requested action, renewal risk, competitor mention, product feature, pricing objection
The LLMAPI quick-start docs show an OpenAI-compatible chat completions pattern using the /v1/chat/completions style endpoint. That means you can use OpenAI-compatible JavaScript clients and keep your backend integration fairly simple.
Good fits for LLMAPI NER:
- Support ticket extraction.
- CRM notes.
- Sales calls.
- Legal clauses.
- Resume fields.
- Product feedback.
- Research notes.
- Healthcare-adjacent admin text with proper compliance review.
- Procurement documents.
- Internal knowledge base cleanup.
Example output for a support ticket:
{
"customer": {
"name": "Jordan Lee",
"company": "Northstar Labs"
},
"issue": {
"type": "billing",
"summary": "Customer says they were charged twice"
},
"dates": [
{
"raw_text": "last Thursday",
"normalized": null
}
],
"money": [
{
"raw_text": "$299",
"amount": 299,
"currency": "USD"
}
],
"urgency": "high"
}
That is more useful than a plain list of entities.
2. Google Cloud Natural Language
Google Cloud Natural Language entity analysis can inspect text for known entities and return information like entity types, salience, mentions, and metadata. Google’s Natural Language API basics explain that the API can perform entity analysis and other text analysis methods, and the analyzeEntities reference documents the REST method.
Good fits:
- Google Cloud apps.
- News/content analysis.
- Search indexing.
- Entity salience scoring.
- Document tagging.
- Apps that already use Google Cloud Storage, BigQuery, or Cloud Functions.
How it works:
text
→ Google Cloud Natural Language
→ entities + type + salience + mentions
→ app stores tags/metadata
This is especially useful when you want entity salience, meaning which entities are more central to the text.
3. Amazon Comprehend
Amazon Comprehend is the AWS-native NLP service for entity detection, sentiment, key phrases, language detection, and custom classification/extraction. The Amazon Comprehend developer guide describes the service as using machine learning to find insights and relationships in text, and its entity detection operations can identify entities like people, places, organizations, quantities, dates, and more.
Good fits:
- AWS apps.
- S3 document pipelines.
- Lambda-based text processing.
- Support ticket analysis.
- Entity detection at scale.
- Custom entity recognition.
- Data lake enrichment.
- Enterprise text analytics.
How it works:
text / documents
→ Amazon Comprehend
→ detected entities
→ S3 / database / search index
Amazon Comprehend is convenient if your documents already live in S3 and your processing pipeline already uses AWS.
4. Azure AI Language
Azure AI Language named entity recognition can identify entities in text and categorize them. Azure also supports custom named entity recognition, which lets teams build domain-specific extraction models.
Good fits:
- Microsoft/Azure apps.
- Enterprise document workflows.
- Custom NER projects.
- Customer support analysis.
- Legal or internal text extraction with governance.
- Teams using Azure AI Foundry or Azure Functions.
How it works:
text
→ Azure AI Language NER
→ entities and categories
→ optional custom NER
→ app workflow
Azure is especially worth considering if your company already has Azure governance, identity, compliance, and security controls in place.
5. spaCy
spaCy is one of the most popular open-source NLP libraries for production-style pipelines. Its named entity recognition docs describe NER as assigning labels to contiguous spans of tokens, and its EntityRecognizer API documents the trainable ner pipeline component.
spaCy is Python-first, but it still fits JavaScript apps well when you expose it as a small internal service.
Workflow:
JavaScript backend
→ internal spaCy service
→ entities
→ JavaScript app continues workflow
Good fits:
- Local/private NER.
- Custom trained models.
- Offline processing.
- Internal document pipelines.
- Teams comfortable running Python microservices.
- Cases where data should not leave your infrastructure.
This is a nice hybrid option: JavaScript runs the app, spaCy handles local NER, LLMAPI handles custom reasoning or cleanup.
6. compromise
compromise is a lightweight JavaScript NLP library.
It can help with simpler entity-like extraction in Node.js or the browser, such as people, places, organizations, dates, and other grammatical patterns depending on the plugin/setup.
Good fits:
- Lightweight JavaScript apps.
- Quick prototypes.
- Browser-side text hints.
- Simple name/date extraction.
- Cases where you do not need heavy NLP infrastructure.
Example direction:
text
→ compromise
→ quick local entities
→ LLMAPI for cleanup/custom fields
Use compromise when you need something simple and fast inside JavaScript.
7. winkNLP
winkNLP is another JavaScript NLP library focused on fast text processing.
It can support tokenization, entity extraction patterns, and other NLP tasks depending on models and setup.
Good fits:
- JavaScript-only NLP.
- Fast local processing.
- Preprocessing before LLMAPI.
- Text cleaning and tokenization.
- Lightweight extraction.
A practical setup:
winkNLP preprocesses text
→ rules extract obvious entities
→ LLMAPI extracts custom schema
→ app validates result
This is useful when you want to reduce the amount of text sent to an LLM or clean it first.
8. Transformers.js
Transformers.js lets developers run transformer models in JavaScript. For NER, that can mean running token classification models in Node.js or even in the browser, depending on the model and environment.
Good fits:
- JavaScript ML experiments.
- Local model inference.
- Browser-based demos.
- Privacy-sensitive workflows where external API calls are limited.
- Lightweight custom model deployment.
Watch out for:
- Model size.
- Latency.
- Browser performance.
- Tokenization behavior.
- Accuracy on your domain.
- Maintenance.
This is a strong option if you want local inference in JavaScript, but cloud APIs are usually simpler for early production builds.
9. Regex and rules
Do not underestimate boring rules.
Regex is still great for entities that follow predictable patterns:
| Entity | Example |
|---|---|
[email protected] | |
| Phone | +1 312 555 0144 |
| URL | https://example.com |
| ZIP code | 60632 |
| Invoice ID | INV-1042 |
| Order ID | ORD-8831 |
| SKU | SKU-AB-2026 |
| Currency | $1,250.00 |
Example:
const emailRegex = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
function extractEmails(text) {
return [...text.matchAll(emailRegex)].map((match) => ({
value: match[0],
start: match.index,
end: match.index + match[0].length
}));
}
Use rules for obvious things.
Use LLMAPI for messy context.
10. Vector search plus LLMAPI for entity linking
NER finds entities.
Entity linking connects those entities to known records.
Example:
Apple
Could mean:
- Apple Inc.
- apple fruit.
- Apple Records.
- A project name inside your company.
Entity linking resolves that ambiguity.
A practical workflow:
extract entity
→ search knowledge base
→ retrieve candidate records
→ LLMAPI chooses or marks uncertain
→ save linked entity ID
Example:
{
"entity": "Apple",
"type": "organization",
"linked_record": {
"id": "org_apple_inc",
"name": "Apple Inc."
},
"confidence": 0.87,
"review_required": false
}
This is useful for CRMs, knowledge graphs, customer support, news analysis, and legal research.
A simple JavaScript + LLMAPI NER workflow
Here is the clean version.
POST /extract-entities
→ validate text
→ build extraction schema
→ call LLMAPI
→ parse JSON
→ validate schema
→ normalize entities
→ return result
Minimal JavaScript example:
import OpenAI from "openai";
import { z } from "zod";
const client = new OpenAI({
apiKey: process.env.LLMAPI_API_KEY,
baseURL: "https://api.llmapi.ai/v1"
});
const EntityExtractionSchema = z.object({
people: z.array(z.string()).default([]),
organizations: z.array(z.string()).default([]),
locations: z.array(z.string()).default([]),
dates: z.array(z.string()).default([]),
money: z.array(z.string()).default([]),
products: z.array(z.string()).default([])
});
async function extractEntities(text) {
const response = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: `
Extract named entities from the text.
Return only valid JSON.
Use empty arrays when no entities are found.
Do not invent entities that are not present in the text.
`
},
{
role: "user",
content: text
}
],
temperature: 0
});
const raw = response.choices[0].message.content;
const parsed = JSON.parse(raw);
return EntityExtractionSchema.parse(parsed);
}
That is enough for a simple prototype.
For production, you need better schemas, source spans, validation, and error handling.
Better output: include spans and evidence
A plain list is useful, but source spans are better.
Instead of:
{
"organizations": ["Acme Logistics"]
}
return:
{
"organizations": [
{
"value": "Acme Logistics",
"start": 38,
"end": 52,
"evidence": "Acme Logistics",
"confidence": "high"
}
]
}
Why spans matter:
- The UI can highlight entities.
- Reviewers can verify extraction.
- You can avoid invented entities.
- You can debug model behavior.
- You can map entities back to source text.
- You can build audit trails.
A stronger schema:
const ExtractedEntitySchema = z.object({
value: z.string(),
type: z.enum([
"person",
"organization",
"location",
"date",
"money",
"product",
"email",
"phone",
"url",
"custom"
]),
start: z.number().nullable(),
end: z.number().nullable(),
evidence: z.string(),
confidence: z.enum(["low", "medium", "high"]),
normalized_value: z.string().nullable(),
review_required: z.boolean()
});
const NERResponseSchema = z.object({
entities: z.array(ExtractedEntitySchema),
warnings: z.array(z.string()).default([])
});
That gives your app much better output.
Example response for messy text
Input:
Hey, can you ask Daniel Rivera from Northwind to send the revised $14,500 proposal by Sept 12? Also loop in Maya from legal.
Output:
{
"entities": [
{
"value": "Daniel Rivera",
"type": "person",
"start": 17,
"end": 31,
"evidence": "Daniel Rivera",
"confidence": "high",
"normalized_value": "Daniel Rivera",
"review_required": false
},
{
"value": "Northwind",
"type": "organization",
"start": 37,
"end": 46,
"evidence": "Northwind",
"confidence": "high",
"normalized_value": "Northwind",
"review_required": false
},
{
"value": "$14,500",
"type": "money",
"start": 67,
"end": 74,
"evidence": "$14,500",
"confidence": "high",
"normalized_value": "14500 USD",
"review_required": false
},
{
"value": "Sept 12",
"type": "date",
"start": 87,
"end": 94,
"evidence": "Sept 12",
"confidence": "medium",
"normalized_value": null,
"review_required": true
},
{
"value": "Maya",
"type": "person",
"start": 109,
"end": 113,
"evidence": "Maya",
"confidence": "medium",
"normalized_value": "Maya",
"review_required": true
}
],
"warnings": [
"The date 'Sept 12' has no year.",
"The person 'Maya' has no last name."
]
}
Notice the review flags.
That is what makes the output practical.
Best practices for LLM-based NER
LLM-based NER is powerful, but you need guardrails.
Use a strict schema
Do not ask:
Find important entities.
Ask:
Extract entities using this JSON schema.
Use only the allowed entity types.
Use null when a field is missing.
Require evidence
Ask for the exact source phrase for each entity.
If the model cannot point to evidence, your backend should flag the entity.
Use empty arrays
Tell the model to return empty arrays when nothing is found.
That is cleaner than random prose like:
No entities found.
Validate with code
Use Zod, JSON Schema, or another validator.
The model generates. Your backend checks.
Keep temperature low
NER is an extraction task, so use low temperature.
temperature: 0
You want consistency, not creativity.
Add confidence and review flags
Let the system say:
{
"confidence": "medium",
"review_required": true
}
This helps with ambiguous names, missing dates, unclear organizations, and domain-specific terms.
Avoid over-extraction
Tell the model what to ignore.
Example:
Do not extract generic nouns unless they are part of a named entity.
Otherwise “customer,” “invoice,” and “proposal” may start showing up as fake entities.
Common NER problems
NER gets messy fast.
| Problem | Example | Fix |
|---|---|---|
| Ambiguous entity | Apple | Use entity linking |
| Partial name | Maya | Flag for review |
| Missing year | Sept 12 | Normalize only if reference date is known |
| Generic noun extracted | proposal | Restrict entity types |
| Duplicate entities | IBM / International Business Machines | Normalize aliases |
| Nested entities | University of Chicago Medical Center | Decide span rules |
| Wrong type | Jordan tagged as location | Use validation/review |
| Hallucinated entity | Model invents company | Require evidence span |
| Domain-specific entity | “React” skill vs reaction | Use custom schema/context |
| Multilingual text | Mixed English/Spanish names | Detect language and test |
NER is not only extraction. It is cleanup.
Entity normalization
Raw entities are often not enough.
You may need normalized values.
Examples:
| Raw entity | Normalized value |
|---|---|
| Sept 12 | 2026-09-12 |
| $1.2k | 1200 USD |
| NYC | New York City |
| IBM | International Business Machines |
| [email protected] | [email protected] |
| next Friday | 2026-08-28, if reference date is known |
Do not normalize dates like “next Friday” without a reference date.
Your app should pass one:
{
"reference_date": "2026-08-21",
"timezone": "America/Chicago"
}
Then LLMAPI can return:
{
"raw_text": "next Friday",
"normalized_value": "2026-08-28",
"timezone": "America/Chicago"
}
This prevents date chaos.
Tiny detail. Big deal.
Entity deduplication
Text often repeats the same entity.
Example:
Acme said Acme Logistics will send the Acme invoice tomorrow.
You may extract Acme three times.
That can be useful if you need spans. But for CRM enrichment, you may want unique entities.
Return both:
{
"mentions": [
{
"value": "Acme",
"start": 0,
"end": 4
},
{
"value": "Acme Logistics",
"start": 10,
"end": 24
}
],
"unique_entities": [
{
"canonical_name": "Acme Logistics",
"aliases": ["Acme"]
}
]
}
Mentions help with highlighting.
Unique entities help with databases.
Entity linking
Entity linking connects extracted text to known records.
Example:
{
"value": "Northwind",
"type": "organization",
"linked_id": "org_9821",
"canonical_name": "Northwind Traders",
"confidence": 0.91
}
Useful linking targets:
- CRM contacts.
- Companies.
- Products.
- SKUs.
- Locations.
- Knowledge base pages.
- Legal matters.
- Patient/customer records.
- Vendor records.
- Internal project IDs.
Workflow:
extract entity
→ search database
→ rank candidates
→ link if confidence is high
→ review if uncertain
This is where NER becomes actual automation.
NER for support tickets
Input:
Lisa from BrightCart says checkout failed twice yesterday and wants a refund for order BC-8821.
Useful extraction:
{
"customer_name": "Lisa",
"company": "BrightCart",
"issue_type": "checkout_failure",
"date": "yesterday",
"requested_action": "refund",
"order_id": "BC-8821"
}
Best practices:
- Extract order IDs with rules.
- Extract customer/company with NER.
- Extract issue type with LLMAPI.
- Route high-urgency or refund cases to review.
- Link company to CRM if possible.
Workflow:
ticket text
→ entities
→ issue type
→ urgency
→ route queue
NER for sales notes
Input:
Call with Priya at DataNest. They’re comparing us with Segment and want pricing before Q4 planning.
Useful extraction:
{
"contact": "Priya",
"account": "DataNest",
"competitor": "Segment",
"requested_info": "pricing",
"timeline": "before Q4 planning"
}
This can update CRM fields automatically:
- Contact mentioned.
- Account mentioned.
- Competitor detected.
- Buying timeline found.
- Follow-up needed.
LLMAPI is helpful here because “pricing before Q4 planning” is not a standard classic NER category. It is business context.
NER for legal documents
Legal NER may need entities like:
- Parties.
- Effective dates.
- Governing law.
- Jurisdiction.
- Payment obligations.
- Termination dates.
- Notice addresses.
- Contract values.
- Clause references.
- Defined terms.
Classic NER can find people, organizations, dates, and locations.
LLMAPI can extract legal-specific fields if you provide a strict schema.
Example:
{
"parties": [],
"effective_date": null,
"governing_law": null,
"payment_obligations": [],
"termination_rights": [],
"notice_address": null,
"missing_fields": []
}
Best practice: legal extraction should always include source quotes and review flags. Do not let a model silently summarize obligations without evidence.
NER for resumes
Resume parsing uses NER-style extraction all over the place.
Useful entities:
- Candidate name.
- Email.
- Phone.
- Location.
- Companies.
- Job titles.
- Skills.
- Degrees.
- Universities.
- Dates.
- Certifications.
- Tools and technologies.
Example schema:
{
"candidate": {
"name": null,
"email": null,
"phone": null,
"location": null
},
"experience": [],
"education": [],
"skills": [],
"certifications": []
}
For resumes, combine:
regex for email/phone
+ classic NER for names/orgs/locations
+ LLMAPI for structured resume sections
+ validation for dates and duplicates
That layered approach works better than relying on one extraction pass.
JavaScript endpoint shape
Here is a practical Express-style endpoint without turning the article into a code wall.
app.post("/extract-entities", async (req, res) => {
const { text, schemaName, referenceDate } = req.body;
if (!text || text.length < 5) {
return res.status(400).json({
error: "Text is required."
});
}
const extraction = await extractEntitiesWithLLMAPI({
text,
schemaName,
referenceDate
});
const validation = validateExtraction(extraction);
if (!validation.valid) {
return res.status(422).json({
error: "Entity extraction failed validation.",
details: validation.errors
});
}
return res.json({
status: "success",
entities: extraction.entities,
warnings: extraction.warnings || []
});
});
The important pieces are:
- Validate input.
- Use the right schema.
- Extract with LLMAPI.
- Validate output.
- Return clean JSON.
That is the article in one endpoint.
Prompt pattern for LLMAPI NER
Use a prompt like this:
You extract named entities from text.
Rules:
- Return only valid JSON.
- Use only the entity types in the schema.
- Do not invent entities.
- Each entity must include an exact evidence phrase from the source text.
- Use null when a normalized value is unknown.
- Flag ambiguous entities with review_required: true.
- Do not extract generic nouns unless they are part of a named entity.
Then pass the schema:
{
"entities": [
{
"value": "string",
"type": "person | organization | location | date | money | product | email | phone | url | custom",
"evidence": "string",
"start": "number or null",
"end": "number or null",
"normalized_value": "string or null",
"confidence": "low | medium | high",
"review_required": "boolean"
}
],
"warnings": ["string"]
}
This gives the model less room to wander.
Validation checklist
After extraction, validate:
- Is the output valid JSON?
- Does it match the schema?
- Are all entity types allowed?
- Does every entity have evidence?
- Does the evidence appear in the source text?
- Are dates normalized only when enough context exists?
- Are money fields parsed correctly?
- Are emails and phone numbers valid?
- Are duplicates handled?
- Are uncertain entities flagged?
- Are sensitive fields protected?
- Are high-risk domains routed to review?
That checklist matters more than making the prompt longer.
Security and privacy best practices
NER often extracts personal data.
Names, emails, phone numbers, addresses, IDs, and workplace details may be sensitive.
Best practices:
- Keep API keys on the backend.
- Avoid logging raw sensitive text.
- Redact PII in logs.
- Store only needed entities.
- Encrypt sensitive extracted data.
- Add retention rules.
- Limit access to extraction results.
- Follow privacy requirements for your region/use case.
- Add review for medical, legal, HR, finance, and identity workflows.
- Let users delete or correct stored data when applicable.
A safe logging record:
{
"request_id": "ner_123",
"schema": "support_ticket_v2",
"text_length": 842,
"entity_count": 12,
"review_required": true,
"latency_ms": 920
}
Avoid logging:
{
"raw_text": "Hi my name is..."
}
NER creates structured data. That makes it easier to use and easier to misuse.
Handle it carefully.
Evaluation: how to know if your NER works
Do not judge NER by a few sample sentences.
Build a small test set from real examples.
Track:
| Metric | What it tells you |
|---|---|
| Precision | How many extracted entities are correct |
| Recall | How many real entities were found |
| F1 score | Balance of precision and recall |
| Type accuracy | Whether entities get the right category |
| Span accuracy | Whether boundaries are correct |
| Normalization accuracy | Whether dates/money/entities are cleaned correctly |
| False positives | Extra entities that should not be there |
| False negatives | Missed entities |
| Review rate | How often humans need to check |
| Downstream usefulness | Whether the output helps the workflow |
For LLM-based extraction, also track:
- JSON validity.
- Schema validity.
- Evidence validity.
- Hallucinated entity rate.
- Cost per extraction.
- Latency.
- Retry/fallback rate.
A pretty response is useless if it invents entities.
Common mistakes
| Mistake | Better approach |
|---|---|
| Asking for “important entities” | Define exact entity types |
| No schema validation | Validate with Zod or JSON Schema |
| No source evidence | Require exact evidence text |
| No review flags | Mark ambiguous entities |
| Treating all NER as the same | Use domain-specific schemas |
| Extracting generic nouns | Restrict to named or useful fields |
| No entity linking | Connect entities to known records |
| No deduplication | Keep mentions and unique entities separately |
| Logging raw PII | Redact logs |
| Using only LLMs for emails/phones | Use regex/rules for obvious patterns |
| Ignoring date context | Pass reference date and timezone |
| Saving uncertain data as truth | Add confidence and review states |
The biggest mistake is treating extraction as a final answer.
Extraction is a first pass. Your backend still needs validation, normalization, and review logic.
Where LLMAPI fits best
LLMAPI fits best when your NER workflow needs one of these:
- Custom entity types.
- Structured JSON.
- Domain-specific extraction.
- Human-readable review notes.
- Routing decisions.
- Entity cleanup.
- Entity normalization.
- Summaries from extracted entities.
- Multi-step extraction workflows.
- Fallback between models.
Example workflow:
support ticket
→ rules extract email/order ID
→ LLMAPI extracts issue, company, date, requested action
→ backend validates schema
→ CRM/contact linking
→ route ticket
Another workflow:
contract text
→ LLMAPI extracts parties/dates/obligations
→ source evidence check
→ legal review queue
LLMAPI gives you flexibility. Your backend gives you discipline.
That combination is the whole point.
A simple production workflow
Here is the version we would actually ship:
text enters app
→ validate length and language
→ extract obvious patterns with rules
→ run classic NER or cloud NER for common entities
→ use LLMAPI for custom schema extraction
→ validate JSON
→ check source evidence
→ normalize dates/money/entities
→ deduplicate and link records
→ flag uncertain fields
→ save or route to review
For small apps, start simpler:
text
→ LLMAPI schema extraction
→ Zod validation
→ return entities
Then add rules, linking, and review as your use case grows.
The practical takeaway
You can do Named Entity Recognition with JavaScript and LLMAPI by building a backend that sends text to LLMAPI with a strict schema, receives structured JSON, validates the output, and returns clean entities to your app.
Use LLMAPI when you need custom entity types, business-specific extraction, review notes, routing, or structured JSON. Use Google Cloud Natural Language, Amazon Comprehend, or Azure AI Language when you want cloud NER services. Use spaCy when you want local/custom open-source NER. Use JavaScript tools like compromise, winkNLP, regex, or Transformers.js when you want lighter local extraction.
A strong workflow looks like this:
messy text
→ JavaScript backend
→ NER tools + LLMAPI
→ structured entities
→ validation
→ database/workflow
That is how messy text becomes cleaner structured data without pretending the first JSON response is automatically perfect.