The Elasticsearch REST API
Every operation in Elasticsearch — indexing a document, running a search, checking cluster health, creating an index — is exposed as an HTTP endpoint that accepts and returns JSON. There is no proprietary binary protocol required for basic usage; you can drive the entire system with curl, Kibana's Dev Tools console, or any HTTP client library. The URL pattern typically follows /{index}/_{action}/{id} for document-level operations, such as PUT /products/_doc/1 to index a document with a specific ID, or POST /products/_doc to let Elasticsearch auto-generate one.
Cricket analogy: Interacting with Elasticsearch purely over HTTP is like a scorer submitting every update through a single standardized online scoring app rather than needing separate specialized radios for different stadiums.
CRUD Operations
Basic document operations map cleanly to HTTP verbs: PUT /index/_doc/{id} creates or fully replaces a document at a specific ID; GET /index/_doc/{id} retrieves it; POST /index/_update/{id} with a partial doc performs a partial update without resending the whole document; and DELETE /index/_doc/{id} removes it. Under the hood, Elasticsearch documents are never modified in place — an update actually marks the old Lucene segment entry as deleted and writes a brand-new document, with old, deleted entries reclaimed later during Lucene's background segment merge process.
Cricket analogy: Updating a scorecard entry isn't really an in-place edit — it's like crossing out an old tally line and writing a fresh corrected one below it, with the old line only fully erased during a later scorebook cleanup.
# Index a document with an explicit ID
curl -X PUT "http://localhost:9200/products/_doc/1" \
-H 'Content-Type: application/json' -d '{
"name": "Wireless Mouse",
"price": 24.99,
"in_stock": true
}'
# Retrieve it
curl -X GET "http://localhost:9200/products/_doc/1"
# Partially update just the price
curl -X POST "http://localhost:9200/products/_update/1" \
-H 'Content-Type: application/json' -d '{
"doc": { "price": 19.99 }
}'
# Delete it
curl -X DELETE "http://localhost:9200/products/_doc/1"Every write in Elasticsearch is versioned internally. The response to an index/update/delete request includes a _seq_no and _primary_term, which can be used with if_seq_no/if_primary_term parameters to implement optimistic concurrency control, preventing lost updates when two clients modify the same document concurrently.
The Search API and the Bulk API
Searching uses GET or POST /index/_search with a JSON request body written in Query DSL, which combines a query clause (what to match, and how to score it, e.g. match, term, bool) with optional aggregations, sorting, and pagination via from/size or the more scalable search_after cursor for deep pagination. For high-throughput writes, the _bulk API lets you batch many index, update, and delete operations into a single HTTP request using a newline-delimited JSON format, dramatically reducing per-request network overhead compared to issuing thousands of individual calls.
Cricket analogy: Using the bulk API to index thousands of historical match records in one request is like a statistician submitting an entire season's scorecards in one batch upload instead of mailing in each match's card separately.
GET /products/_search
{
"query": {
"bool": {
"must": [
{ "match": { "name": "mouse" } }
],
"filter": [
{ "term": { "in_stock": true } }
]
}
},
"sort": [{ "price": "asc" }],
"size": 10
}Deep pagination with from/size becomes expensive and is capped at 10,000 results by default (index.max_result_window), because Elasticsearch must fetch and sort from+size documents on every shard before merging results. For paging through large result sets, use search_after with a stable sort (typically including _shard_doc or a tiebreaker field) instead.
- Every Elasticsearch operation is a JSON-over-HTTP REST endpoint, usable via curl, Kibana Dev Tools, or any HTTP client.
- PUT/_doc creates or replaces a document; POST/_update performs a partial update; DELETE/_doc removes it.
- Documents are never modified in place internally; updates write new Lucene entries and mark old ones deleted.
- _seq_no and _primary_term enable optimistic concurrency control to prevent lost updates.
- The _search endpoint uses Query DSL, combining query clauses, aggregations, sorting, and pagination.
- The _bulk API batches many operations into one HTTP request for much higher write throughput.
- Deep pagination with from/size is capped at 10,000 by default; use search_after for large result sets.
Practice what you learned
1. What format does the Elasticsearch REST API use for both requests and responses?
2. Which endpoint performs a partial update on an existing document without resending the entire document?
3. What actually happens internally when you 'update' a document in Elasticsearch?
4. What is the purpose of the _bulk API?
5. What is the default cap on results retrievable via from/size pagination, and what is the recommended alternative for deep pagination?
Was this page helpful?
You May Also Like
Indices, Documents, and Mappings
How Elasticsearch organizes data into indices and documents, and how mappings define the field types that control indexing and search behavior.
What Is Elasticsearch?
An introduction to Elasticsearch as a distributed search and analytics engine, why it exists, and the core concepts that make full-text search fast at scale.
Installing and Running Elasticsearch
How to install, configure, and start a local Elasticsearch node or cluster, including key settings and common startup issues.