TL;DR
Elasticsearch APIs enable scalable search and analytics. Index documents as JSON, query using the DSL, and aggregate results for analytics. Authenticate using API keys or basic auth. Use Apidog to validate index mappings, test search queries, and debug aggregations before deploying to production clusters.
Introduction
Elasticsearch is a distributed search and analytics engine for structured text, logs, metrics, and more. It’s commonly used for full-text search in applications, log analysis, and real-time analytics dashboards.
Elasticsearch is part of the ELK stack (Elasticsearch, Logstash, Kibana), but you can use its APIs directly—no Logstash required.
💡 Tip: If you’re building search features or log analysis, Apidog helps you test queries, validate mappings, and debug aggregations. Save and share search templates with your team.
Test Elasticsearch APIs with Apidog — free
By the end of this guide, you’ll be able to:
- Index and manage documents
- Write search queries with Elasticsearch DSL
- Use aggregations for analytics
- Configure mappings and analyzers
- Monitor cluster health
Getting started
Run Elasticsearch locally
# Docker
docker run -p 9200:9200 \
-e "discovery.type=single-node" \
elasticsearch:8.11.0
# Or download from elastic.co
Verify installation
curl -X GET "http://localhost:9200"
Expected response:
{
"name": "elasticsearch-1",
"cluster_name": "elasticsearch",
"cluster_uuid": "abc123",
"version": {
"number": "8.11.0",
"build_flavor": "default"
},
"tagline": "You know, for search"
}
Authentication
Elasticsearch 8.x requires authentication by default:
curl -X GET "http://localhost:9200/_cluster/health" \
-u elastic:your_password
Or use API keys (generate via Kibana or API).
Indices and documents
Create an index
curl -X PUT "http://localhost:9200/products" \
-u elastic:your_password \
-H "Content-Type: application/json" \
-d '{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"name": { "type": "text" },
"price": { "type": "float" },
"category": { "type": "keyword" },
"in_stock": { "type": "boolean" },
"created_at": { "type": "date" }
}
}
}'
Index a document
curl -X POST "http://localhost:9200/products/_doc" \
-u elastic:your_password \
-H "Content-Type: application/json" \
-d '{
"name": "Wireless Headphones",
"price": 79.99,
"category": "electronics",
"in_stock": true,
"created_at": "2026-03-24T10:00:00Z"
}'
Response:
{
"_index": "products",
"_id": "abc123",
"_version": 1,
"result": "created",
"_seq_no": 0,
"_primary_term": 1
}
Get a document
curl -X GET "http://localhost:9200/products/_doc/abc123" \
-u elastic:your_password
Update a document
curl -X PUT "http://localhost:9200/products/_doc/abc123" \
-u elastic:your_password \
-H "Content-Type: application/json" \
-d '{
"name": "Wireless Headphones Pro",
"price": 99.99,
"category": "electronics",
"in_stock": true,
"created_at": "2026-03-24T10:00:00Z"
}'
Delete a document
curl -X DELETE "http://localhost:9200/products/_doc/abc123" \
-u elastic:your_password
Bulk operations
Efficiently index multiple documents:
curl -X POST "http://localhost:9200/products/_bulk" \
-u elastic:your_password \
-H "Content-Type: application/x-ndjson" \
-d '{"index":{"_id":"1"}}
{"name":"Product A","price":10.99,"category":"books","in_stock":true}
{"index":{"_id":"2"}}
{"name":"Product B","price":20.99,"category":"electronics","in_stock":false}
'
Search queries
Basic search
curl -X GET "http://localhost:9200/products/_search" \
-u elastic:your_password \
-H "Content-Type: application/json" \
-d '{
"query": {
"match": {
"name": "headphones"
}
}
}'
Bool queries
Combine multiple conditions:
{
"query": {
"bool": {
"must": [
{ "match": { "name": "headphones" } }
],
"filter": [
{ "term": { "category": "electronics" } },
{ "range": { "price": { "lte": 100 } } },
{ "term": { "in_stock": true } }
]
}
}
}
Full-text search with scoring
{
"query": {
"multi_match": {
"query": "wireless audio headphones",
"fields": ["name^2", "description"],
"type": "best_fields",
"fuzziness": "AUTO"
}
}
}
Fields with ^2 have double weight in scoring.
Phrase search
Find exact phrases:
{
"query": {
"match_phrase": {
"description": "noise canceling"
}
}
}
Wildcard and regex
{
"query": {
"wildcard": {
"name": "*headphone*"
}
}
}
Sorting
{
"query": { "match_all": {} },
"sort": [
{ "price": "asc" },
{ "_score": "desc" }
]
}
Pagination
{
"from": 20,
"size": 10,
"query": { "match_all": {} }
}
Aggregations
Aggregations compute summary statistics on your data.
Average price by category
curl -X GET "http://localhost:9200/products/_search" \
-u elastic:your_password \
-H "Content-Type: application/json" \
-d '{
"size": 0,
"aggs": {
"by_category": {
"terms": { "field": "category" },
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"min_price": { "min": { "field": "price" } },
"max_price": { "max": { "field": "price" } }
}
}
}
}'
Histogram of prices
{
"size": 0,
"aggs": {
"price_histogram": {
"histogram": {
"field": "price",
"interval": 25
}
}
}
}
Date histograms
{
"size": 0,
"aggs": {
"sales_over_time": {
"date_histogram": {
"field": "created_at",
"calendar_interval": "month"
}
}
}
}
Cardinality (unique counts)
{
"size": 0,
"aggs": {
"unique_categories": {
"cardinality": { "field": "category" }
}
}
}
Mappings and analyzers
Field types
| Type | Use for |
|---|---|
text |
Full-text search, analyzed |
keyword |
Exact values, filtering, sorting |
integer, float
|
Numbers |
boolean |
True/false |
date |
Dates and times |
object |
Nested JSON objects |
nested |
Arrays of objects (maintains relationships) |
geo_point |
Lat/lon coordinates |
Custom analyzers
For specialized text processing:
{
"settings": {
"analysis": {
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "autocomplete_filter"]
}
},
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 20
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "autocomplete",
"search_analyzer": "standard"
}
}
}
}
Cluster management
Cluster health
curl -X GET "http://localhost:9200/_cluster/health"
Example response:
{
"cluster_name": "elasticsearch",
"status": "green",
"number_of_nodes": 3,
"active_primary_shards": 25
}
Statuses:
- green: All shards allocated
- yellow: Replicas not allocated (single node)
- red: Primary shards missing
Index statistics
curl -X GET "http://localhost:9200/_cat/indices?v"
Node statistics
curl -X GET "http://localhost:9200/_nodes/stats"
Clear cache
curl -X POST "http://localhost:9200/_cache/clear"
Testing with Apidog
Elasticsearch queries can be complex. Always test thoroughly.
1. Save common queries
Store search templates in Apidog:
{
"query": {
"bool": {
"must": [
{ "match": { "{{search_field}}": "{{search_term}}" } }
],
"filter": [
{ "range": { "{{price_field}}": { "lte": "{{max_price}}" } } }
]
}
}
}
2. Validate responses
pm.test('Search returns results', () => {
const response = pm.response.json()
pm.expect(response.hits.total.value).to.be.above(0)
})
pm.test('Aggregations present', () => {
const response = pm.response.json()
pm.expect(response.aggregations).to.exist
})
3. Environment separation
# Local
ES_HOST: http://localhost:9200
ES_USER: elastic
ES_PASSWORD: your_password
# Production
ES_HOST: https://search.yourcompany.com
ES_API_KEY: prod_api_key
Test Elasticsearch APIs with Apidog — free
Common errors and fixes
403 Forbidden
Cause: Authentication failed or insufficient permissions.
Fix: Verify credentials. Check API key permissions.
404 index_not_found_exception
Cause: Index does not exist.
Fix: Create the index first. Avoid auto-creation for production.
circuit_breaking_exception
Cause: Query uses too much memory.
Fix: Reduce size, simplify queries, or add filters.
search_phase_execution_exception
Cause: Query syntax error.
Fix: Check your JSON syntax and field paths.
Alternatives and comparisons
| Feature | Elasticsearch | OpenSearch | Meilisearch | Typesense |
|---|---|---|---|---|
| Setup | Self-hosted | Self-hosted | Single binary | Single binary |
| Search quality | Excellent | Good | Excellent | Good |
| Learning curve | Steep | Steep | Easy | Easy |
| Scalability | Excellent | Excellent | Good | Good |
| Cloud offering | Elastic Cloud | OpenSearch Serverless | Meilisearch Cloud | Typesense Cloud |
Elasticsearch offers the most features and largest community. Meilisearch and Typesense are easier for basic search use cases.
Real-world use cases
- E-commerce search: Retail sites index large product catalogs. Users search by name, description, category, and price. Autocomplete and filters enhance UX.
- Application logs: DevOps teams ship logs to Elasticsearch (e.g., via Filebeat). Engineers search logs by service, severity, and time range. Dashboards visualize errors and performance.
- Security analytics: Security teams index network logs, search for suspicious IPs, visualize patterns, and set up anomaly alerts using aggregations.
Wrapping up
Key takeaways:
- Index documents as JSON
- Query using Elasticsearch DSL
- Leverage aggregations for analytics
- Configure mappings for optimal search
- Monitor cluster health
Next steps:
- Run Elasticsearch locally
- Create an index with mappings
- Index test documents
- Write search queries
- Try aggregations
Test Elasticsearch APIs with Apidog — free
FAQ
What’s the difference between Elasticsearch and Solr?
Both are Lucene-based search engines. Elasticsearch features better distributed design and APIs. Solr has more enterprise features, but most new projects use Elasticsearch.
How do I handle special characters in search?
Escape special characters: ()[]{}:^"\+-!~*?| with a backslash. Or use simple_query_string for more forgiving syntax.
What’s a shard?
Shards are parts of an index. Each shard is a Lucene index. Primary shards handle writes; replica shards are copies for reads and fault tolerance.
How many shards should I create?
Rule of thumb: 20-50GB per shard. Start with 1 primary shard and add replicas. Only increase primary shards when necessary (cannot decrease).
Can I change mappings after indexing?
Partially—add new fields anytime. To change existing field types, reindex data. Use index templates for consistent mappings.
What’s the _routing parameter?
Routes documents to specific shards based on a field value. Default is _id. Use routing if queries always filter by a specific field (e.g., user_id) for better performance.
How do I handle time-based data?
Use date-based indices: logs-2026.03.24. This approach allows old data to be deleted by dropping indices and improves query performance by targeting fewer indices.

Top comments (0)