REST API
Every Elasticsearch operation — indexing, search, cluster admin — is an HTTP request to a JSON REST API on port 9200, which is the single contract all clients and tools speak.
Why it matters
There is no binary protocol you must use and no driver lock-in: anything that can do HTTP can drive the cluster, which is why clients in every language, Kibana Dev Tools, and curl all interoperate. Understanding the raw API is essential for debugging, since client libraries are thin wrappers and their errors are just relayed HTTP responses.
How it works
Requests follow METHOD /<index>/<endpoint> with a JSON body; the verb carries intent and the path carries the target.
| Method | Typical use | Example |
|---|---|---|
GET | read / search | GET /logs/_search |
PUT | create with a known id | PUT /logs/_doc/42 |
POST | create with generated id / actions | POST /logs/_doc |
DELETE | remove | DELETE /logs/_doc/42 |
HEAD | existence check (no body) | HEAD /logs |
_catAPIs — human-readable plain-text tables (_cat/indices?v) for operators; everything else returns JSON.- Common query params —
?prettyindents output,?format=yaml,?filter_path=hits.hits._sourcetrims the response,?error_trace=trueexpands stack traces. - Status codes are real —
201created,200updated,404missing,409version conflict,429rejected (queue full),503cluster unavailable. - Bulk uses NDJSON — the bulk-api is not normal JSON: it is newline-delimited with
Content-Type: application/x-ndjsonand a trailing newline.
Example
PUT /products/_doc/1 -> 201 Created {"result":"created","_version":1}
{ "name": "Widget", "price": 9.99 }
GET /products/_search?filter_path=hits.total,hits.hits._source
{ "query": { "match": { "name": "widget" } } }
-> 200 { "hits": { "total": {"value":1}, "hits": [ {"_source": {...}} ] } }
filter_path cut the response to just totals and source — over millions of hits that saves real bandwidth and parse time.
Pitfalls
GETwith a body — search usesGETwith a JSON body; some proxies and old HTTP libraries strip it, so clients fall back toPOST /_search(semantically identical).- 9200 vs 9300 — 9200 is the REST/HTTP port; 9300 is the internal node-to-node transport. Never point clients or load balancers at 9300.
- Trailing newline on bulk — omitting the final
\nmakes the bulk-api silently drop the last action. - No body-size guard by default — a giant request hits
http.max_content_length(default 100mb) and returns413; chunk large bulk loads instead.