REST Endpoint Map

This is a reference map from the REST endpoints you actually type into curl to the Java handler class that serves them and the chapter that explains the machinery behind the path. Use it the way you'd use a phone book: you know the endpoint, you want the class, you want where to read next.

Every row is method + path → handler class → chapter. The handler is almost always a BaseRestHandler subclass whose routes() declares the (method, path) pairs — see The REST Layer for how RestController turns a path into a handler. The handler parses the request and calls NodeClient.execute(SomeAction.INSTANCE, ...); the real work is in the matching Transport*Action (action-framework.md).

Warning: Class names, package locations, and even which endpoints exist evolve by version. Treat the class column as "the name to grep for," not a fixed line number. Plugin endpoints (_plugins/_*) live in their own repos (k-NN, neural-search, ml-commons, security), not in OpenSearch/server. Always confirm against your checkout:

# core handler for a path: grep the literal path segment in rest/action
grep -rn '"_cluster/health"\|_cluster/health' \
  server/src/main/java/org/opensearch/rest/action/

# or find every BaseRestHandler and read its routes()
grep -rln "extends BaseRestHandler" server/src/main/java/org/opensearch/rest/action/
grep -rn "new Route(" server/src/main/java/org/opensearch/rest/action/cat/RestShardsAction.java

How to read this map

ColumnMeaning
Method + pathWhat you send. {index} etc. are templated path segments bound from the URL.
Handler classThe RestHandler (usually BaseRestHandler) whose routes() matches. Grep the simple name; package may move.
Action / where it goesThe ActionType + Transport*Action the handler dispatches to, or the subsystem that does the work.
ChapterWhere the path behind the endpoint is explained in this book.

Core handlers live under server/src/main/java/org/opensearch/rest/action/{admin/cluster,admin/indices,cat,document,search}. Cat handlers extend AbstractCatAction (which extends BaseRestHandler).


Cluster APIs

State, health, settings, and the allocation/reroute control plane. These ride the cluster-state and cluster coordination machinery.

Method + pathHandler classAction / where it goesChapter
GET /_cluster/healthRestClusterHealthActionClusterHealthAction → TransportClusterHealthActioncluster-and-node-model.md
GET /_cluster/stateRestClusterStateActionClusterStateAction → TransportClusterStateAction (reads the published ClusterState)cluster-state.md, cluster-state-publishing.md
GET/PUT /_cluster/settingsRestClusterUpdateSettingsAction / RestClusterGetSettingsActionClusterUpdateSettingsAction → TransportClusterUpdateSettingsActioncluster-state.md
GET /_cluster/allocation/explainRestClusterAllocationExplainActionClusterAllocationExplainAction → TransportClusterAllocationExplainAction (runs AllocationDeciders)shard-allocation.md
POST /_cluster/rerouteRestClusterRerouteActionClusterRerouteAction → TransportClusterRerouteAction (drives AllocationService)shard-allocation.md
GET /_cluster/pending_tasksRestPendingClusterTasksActionPendingClusterTasksAction (cluster-manager queue depth)cluster-state-publishing.md

Note: _cluster/allocation/explain is your first stop when a shard is UNASSIGNED or won't move — it returns the exact AllocationDecider that said "no." _cluster/reroute with ?explain lets you dry-run a manual move. Both are explained from the inside in shard-allocation.md and the allocation/rebalancing lab.

grep -rln "extends BaseRestHandler" server/src/main/java/org/opensearch/rest/action/admin/cluster/
grep -rn "new Route(" .../admin/cluster/RestClusterHealthAction.java

The cluster-manager (formerly "master") node owns the ClusterState; every write here is a cluster-state update task it serializes.


Cat APIs

_cat/* are the human-readable, column-oriented diagnostics. They render plain text tables (add ?v for headers, ?h=... to pick columns, &s=... to sort). All extend AbstractCatAction. They are thin views over the same stats/state the JSON APIs expose.

Method + pathHandler classReadsChapter
GET /_cat/shardsRestShardsActionshard routing + per-shard stats (IndicesStats + ClusterState routing table)index-shard-lifecycle.md, shard-allocation.md
GET /_cat/nodesRestNodesActionNodesInfo + NodesStats + cluster statecluster-and-node-model.md
GET /_cat/segmentsRestSegmentsActionIndicesSegments (per-segment: docs, size, codec)segments-and-codecs.md, refresh-flush-merge.md
GET /_cat/thread_poolRestThreadPoolActionNodesStats thread-pool section (queue/active/rejected)threadpools-concurrency.md
GET /_cat/cluster_managerRestClusterManagerActionwho the elected cluster-manager is (alias: _cat/master, deprecated)discovery-coordination.md
GET /_cat/recoveryRestCatRecoveryActionRecoveryState per shard (peer/store/snapshot recovery progress)recovery.md
GET /_cat/segment_replicationRestCatSegmentReplicationActionsegment-replication lag/bytes-behind per shardreplication.md, remote-store-and-durability.md
GET /_cat/allocationRestAllocationActiondisk + shard counts per nodeshard-allocation.md
GET /_cat/indicesRestIndicesActionper-index health/docs/sizeindex-shard-lifecycle.md
GET /_cat/pending_tasksRestCatPendingClusterTasksActioncluster-manager task queuecluster-state-publishing.md

Note: _cat/master was renamed _cat/cluster_manager in the terminology rename; the old path is kept as a deprecated alias. _cat/segment_replication only returns rows when segment replication is the replication strategy.

# Every cat handler and its columns:
grep -rln "extends AbstractCatAction" server/src/main/java/org/opensearch/rest/action/cat/
grep -rn "getTableWithHeader\|new Route(" .../cat/RestShardsAction.java

Nodes APIs

Per-node introspection — the heavy machinery behind debugging & profiling.

Method + pathHandler classActionChapter
GET /_nodes/statsRestNodesStatsActionNodesStatsAction → TransportNodesStatsAction (per-node: jvm, os, thread_pool, indices, breakers, ...)circuit-breakers-memory.md, threadpools-concurrency.md
GET /_nodes/{nodeId}/hot_threadsRestNodesHotThreadsActionNodesHotThreadsAction → TransportNodesHotThreadsAction (samples stacks via HotThreads)debugging-profiling/lab-01-hot-threads-profile-api.md
GET /_nodes/pluginsRestNodesInfoAction (?filter_path=**.plugins)NodesInfoAction → TransportNodesInfoAction (installed plugins/modules)plugin-architecture.md
GET /_nodes / /_nodes/{nodeId}RestNodesInfoActionNodesInfoAction (settings, jvm, os, transport, http)cluster-and-node-model.md
GET /_nodes/usageRestNodesUsageActionNodesUsageAction (REST action call counts)rest-layer.md
POST /_nodes/reload_secure_settingsRestReloadSecureSettingsActionNodesReloadSecureSettingsAction (re-read keystore)plugin-architecture.md

Note: _nodes/hot_threads is the first tool when the cluster is hot or stuck: it samples each node's thread stacks, ranks by CPU/wait/block, and shows the hottest frames. It is implemented by the HotThreads class (grep -rn "class HotThreads"), not a profiler. For deeper work attach JFR / async-profiler — see the debugging masterclass.


Document and search APIs

The data plane: index, get, update, delete, bulk, and every flavor of search. These dispatch to the actions in action-framework.md and run the path in search-execution.md.

Method + pathHandler classActionChapter
PUT/POST /{index}/_doc/{id}RestIndexActionIndexAction → TransportIndexAction (via TransportBulkAction)index-shard-lifecycle.md, translog.md
GET /{index}/_doc/{id}RestGetActionGetAction → TransportGetAction (realtime get from translog)translog.md
DELETE /{index}/_doc/{id}RestDeleteActionDeleteAction → TransportDeleteActionrefresh-flush-merge.md
POST /_bulk, /{index}/_bulkRestBulkActionBulkAction → TransportBulkAction (groups per-shard)action-framework.md
POST /{index}/_update/{id}RestUpdateActionUpdateAction → TransportUpdateAction (get + script/merge + index)index-shard-lifecycle.md
GET/POST /{index}/_searchRestSearchActionSearchAction → TransportSearchAction (query+fetch over shards)search-execution.md, query-dsl-querybuilders.md
GET/POST /_msearchRestMultiSearchActionMultiSearchAction → TransportMultiSearchAction (NDJSON of searches)search-execution.md
GET/POST /{index}/_countRestCountActionSearchAction with size:0 (count-only)search-execution.md
GET /{index}/_explain/{id}RestExplainActionExplainAction → TransportExplainAction (per-doc score breakdown)query-engine/lab-02-bm25-and-scoring.md
GET/POST /{index}/_validate/queryRestValidateQueryActionValidateQueryAction → TransportValidateQueryAction (parse/rewrite without running)query-dsl-querybuilders.md
POST /{index}/_search?profile=trueRestSearchAction (profile flag)SearchAction with ProfileResult per shard/collectorquery-engine/index.md, debugging-profiling/lab-01-hot-threads-profile-api.md
POST /{index}/_pit / DELETE /_pitRestCreatePitAction / RestDeletePitActionCreatePitAction / DeletePitAction (Point-in-Time reader)search-execution.md
POST /_search/scroll / DELETE /_search/scrollRestSearchScrollAction / RestClearScrollActionSearchScrollAction / ClearScrollActionsearch-execution.md
GET/POST /{index}/_termvectors/{id}RestTermVectorsActionTermVectorsAction → TransportTermVectorsActioninverted-index-and-postings.md

Note: _count and _search?size=0 both render through the search action; _count is just RestCountAction packaging a count-only request. ?profile=true adds a ProfileResult to the SearchResponse with rewrite/build_scorer/score/next_doc/advance timings per query and per aggregation — read it next to the Profile API discussion in the query-engine masterclass.

grep -rn "new Route(\|profile\|pointInTime" \
  server/src/main/java/org/opensearch/rest/action/search/RestSearchAction.java

Point-in-Time vs scroll

EndpointHolds openPaginates byUse
_search/scrolla scroll context (segment snapshot) per requestscroll_idlegacy deep export; one consumer
_pit + search_aftera PIT reader id (snapshot, shareable)search_after + pit.idpreferred deep pagination

Both pin Lucene readers (keeping segments from being merged away), so close them. See search-execution.md.


Index admin APIs

Create/configure indices and drive the refresh/flush/merge and resize machinery. Handlers live under rest/action/admin/indices.

Method + pathHandler classActionChapter
PUT /{index}RestCreateIndexActionCreateIndexAction → TransportCreateIndexAction (cluster-state update)index-shard-lifecycle.md
DELETE /{index}RestDeleteIndexActionDeleteIndexAction → TransportDeleteIndexActionindex-shard-lifecycle.md
GET/PUT /{index}/_mappingRestGetMappingAction / RestPutMappingActionGetMappingsAction / PutMappingActionmapping-and-analysis.md
GET/PUT /{index}/_settingsRestGetSettingsAction / RestUpdateSettingsActionGetSettingsAction / UpdateSettingsActioncluster-state.md
POST /{index}/_refreshRestRefreshActionRefreshAction → TransportRefreshAction (open new reader)refresh-flush-merge.md
POST /{index}/_flushRestFlushActionFlushAction → TransportFlushAction (Lucene commit + trim translog)refresh-flush-merge.md, translog.md
POST /{index}/_forcemergeRestForceMergeActionForceMergeAction → TransportForceMergeAction (max_num_segments)indexwriter-and-merges.md
POST /{index}/_split/{target}RestResizeHandler.RestSplitIndexActionResizeAction (split: more shards, hard-linked segments)sharding-and-scaling.md
POST /{index}/_shrink/{target}RestResizeHandler.RestShrinkIndexActionResizeAction (shrink: fewer shards)sharding-and-scaling.md
POST /{index}/_clone/{target}RestResizeHandler.RestCloneIndexActionResizeAction (clone: same shard count, hard-linked)sharding-and-scaling.md
GET /{index}/_recoveryRestRecoveryActionRecoveryAction → TransportRecoveryActionrecovery.md
GET /{index}/_statsRestIndicesStatsActionIndicesStatsAction → TransportIndicesStatsActionindex-shard-lifecycle.md
GET /{index}/_segmentsRestIndicesSegmentsActionIndicesSegmentsAction (per-segment detail)segments-and-codecs.md
POST /{index}/_open / _closeRestOpenIndexAction / RestCloseIndexActionOpenIndexAction / CloseIndexActionindex-shard-lifecycle.md

Note: _split, _shrink, and _clone are inner classes of RestResizeHandler dispatching the one ResizeAction with a ResizeType. They hard-link (not copy) segment files where possible — which is why the source index must be read-only/blocked first (Lucene file formats).

grep -rn "RestSplitIndexAction\|RestShrinkIndexAction\|RestCloneIndexAction\|ResizeType" \
  server/src/main/java/org/opensearch/rest/action/admin/indices/RestResizeHandler.java

Aggregations

Aggregations have no dedicated endpoint — they ride inside the _search body under "aggs" and are served by RestSearchAction. The one exception worth mapping is composite, the only paginating aggregation: it returns an after_key you feed back in "after" to get the next page.

Method + pathHandler / mechanismWhere it goesChapter
POST /{index}/_search with "aggs"RestSearchAction (body parses AggregatorFactories)SearchAction → AggregationPhaseaggregations.md, aggregations/index.md
composite agg after_key pagingsame; CompositeAggregationBuilder reads "after"CompositeAggregator (paginating)aggregations/lab-03-composite-pipeline-memory.md
?profile=true with aggsRestSearchActionper-aggregation ProfileResultaggregations/index.md
// Composite paging: take after_key from response N, send it as "after" in N+1.
POST /sales/_search
{ "size": 0,
  "aggs": { "by": { "composite": {
    "size": 1000,
    "sources": [ { "sku": { "terms": { "field": "sku" } } } ],
    "after": { "sku": "Z-998" }
  } } } }

MultiBucketConsumer enforces search.max_buckets; aggs are slice-safe under concurrent segment search via their CollectorManager. Eligible aggregations can short-circuit through the star-tree index.


Tasks API

The task framework tracks every running action; cancellation flows through it.

Method + pathHandler classActionChapter
GET /_tasksRestListTasksActionListTasksAction → TransportListTasksActiondebugging-profiling/index.md
GET /_tasks/{taskId}RestGetTaskActionGetTaskAction → TransportGetTaskActiondebugging-profiling/index.md
POST /_tasks/{taskId}/_cancel, /_tasks/_cancelRestCancelTasksActionCancelTasksAction → TransportCancelTasksAction (TaskManager)debugging-profiling/index.md

Note: Long search/reindex jobs register a cancellable Task with the TaskManager. _tasks/<id>/_cancel flips a flag the action polls; well-behaved actions check it and abort. Find a runaway with GET /_tasks?detailed&actions=*search*.


Snapshot and repository APIs

Backup/restore over a pluggable repository (fs, s3, ...).

Method + pathHandler classActionChapter
PUT/GET /_snapshot/{repo}RestPutRepositoryAction / RestGetRepositoriesActionPutRepositoryAction / GetRepositoriesActionsnapshots-repositories.md
PUT /_snapshot/{repo}/{snapshot}RestCreateSnapshotActionCreateSnapshotAction → TransportCreateSnapshotActionsnapshots-repositories.md
GET /_snapshot/{repo}/{snapshot}RestGetSnapshotsActionGetSnapshotsActionsnapshots-repositories.md
POST /_snapshot/{repo}/{snapshot}/_restoreRestRestoreSnapshotActionRestoreSnapshotAction → TransportRestoreSnapshotActionsnapshots-repositories.md, recovery.md
GET /_snapshot/{repo}/{snapshot}/_statusRestSnapshotsStatusActionSnapshotsStatusActionsnapshots-repositories.md
DELETE /_snapshot/{repo}/{snapshot}RestDeleteSnapshotActionDeleteSnapshotActionsnapshots-repositories.md

Snapshots are incremental at the segment file level — unchanged .cfs/.si/etc. are referenced, not re-uploaded (Lucene file formats), and the same Repository abstraction underpins remote-backed storage.


k-NN APIs (_plugins/_knn/*)

Provided by the k-NN plugin (org.opensearch.knn, separate repo). The vector search itself rides inside _search via the knn query clause; the dedicated endpoints are for warmup, stats, and model training.

Method + pathHandler class (in k-NN repo)PurposeChapter
GET /_plugins/_knn/warmup/{index}RestKNNWarmupHandlerload native HNSW/faiss graphs into off-heap memory before queryingknn/warmup.md, knn/native-jni-and-memory.md
GET /_plugins/_knn/statsRestKNNStatsHandlerper-node k-NN stats: cache hits, graph memory, miss countsknn/architecture.md, knn/warmup.md
POST /_plugins/_knn/models/{id}/_trainRestTrainModelHandlertrain an IVF/PQ model (faiss) before indexing quantized vectorsknn/quantization-and-disk-ann.md, vector-internals/lab-02-faiss-index-types-jni.md
GET/DELETE /_plugins/_knn/models/{id}RestGetModelHandler / RestDeleteModelHandlerfetch/delete a trained model from the model system indexknn/quantization-and-disk-ann.md
_search with "knn" query clauseparsed by KNNQueryBuilder (not a REST handler)approximate / exact vector searchknn/query-path.md, hnsw-vector-search.md
// The knn query lives inside _search, not its own endpoint:
POST /my-vectors/_search
{ "size": 10,
  "query": { "knn": { "my_vector": { "vector": [0.1, 0.2, ...], "k": 10 } } } }
# In a k-NN checkout, find the handlers (names vary by version):
grep -rln "extends BaseRestHandler" src/main/java/org/opensearch/knn/plugin/rest/
grep -rn "_plugins/_knn\|warmup\|/stats\|_train" src/main/java/org/opensearch/knn/plugin/rest/

Note: "warmup" exists because the HNSW/faiss graphs live in native, off-heap memory and are loaded lazily on first query — see native-jni-and-memory.md. The graph construction itself is in vector-internals/lab-01-hnsw-graph-construction.md.


Neural search and ML APIs

Provided by neural-search and ml-commons (separate repos). Models are registered/deployed via ml-commons; ingest and search pipelines call them; the neural/hybrid/neural_sparse query clauses ride inside _search.

Method + pathHandler / mechanismPurposeChapter
POST /_plugins/_ml/models/_registerRestMLRegisterModelAction (ml-commons)register a model (embedding / LLM / sparse)vectorization-embeddings/index.md
POST /_plugins/_ml/models/{id}/_deploy / _undeployRestMLDeployModelAction / RestMLUndeployModelActionload/unload a model onto ML nodesvectorization-embeddings/index.md
POST /_plugins/_ml/models/{id}/_predictRestMLPredictionActionrun inference directlyvectorization-embeddings/index.md
text_embedding ingest processorTextEmbeddingProcessor (neural-search) via _ingest/pipelineembed text → vector at index timevectorization-embeddings/index.md
_search with "neural" clauseNeuralQueryBuilderembed query text → ANN vector searchknn/query-path.md, vectorization-embeddings/index.md
_search with "hybrid" clauseHybridQueryBuilder + normalization search pipelinecombine BM25 + vector, normalize/combine scoresquery-engine/index.md
_search with "neural_sparse" clauseNeuralSparseQueryBuilderlearned sparse (term-expansion) retrievalvectorization-embeddings/index.md
PUT /_search/pipeline/{id}RestPutSearchPipelineAction (core)register request/response processors (e.g. score normalization for hybrid)search-execution.md
GET/DELETE /_search/pipeline/{id}RestGetSearchPipelineAction / RestDeleteSearchPipelineActionmanage search pipelinessearch-execution.md

Note: Search pipelines (_search/pipeline) are a core feature (SearchRequestProcessor / SearchResponseProcessor), but hybrid query scoring depends on a normalization-combination response processor shipped by neural-search. The pipeline runs around the search; the query clause runs inside it. Endpoint paths and class names move quickly here — confirm on docs.opensearch.org and in the plugin repo.

# In neural-search / ml-commons checkouts:
grep -rln "extends BaseRestHandler" src/main/java/org/opensearch/ml/rest/        # ml-commons
grep -rn "NeuralQueryBuilder\|HybridQueryBuilder\|NeuralSparseQueryBuilder" \
  src/main/java/org/opensearch/neuralsearch/query/                              # neural-search

Security APIs (_plugins/_security/api/*)

Provided by the security plugin (org.opensearch.security, separate repo). Configuration lives in a system index (.opendistro_security / newer .opensearch_security) and is loaded from YAML with securityadmin.sh; the REST admin surface mirrors those YAML files.

Method + pathBacking configPurposeChapter
GET/PUT/PATCH /_plugins/_security/api/internalusers/{name}internal_users.ymlmanage internal (basic-auth) userssecurity/lab-01-authn-tls.md
.../api/roles/{name}roles.ymlcluster/index permissions + DLS/FLS/masking per rolesecurity/lab-02-authz-dls-fls.md
.../api/rolesmapping/{name}roles_mapping.ymlmap users / backend-roles → rolessecurity/lab-02-authz-dls-fls.md
.../api/actiongroups/{name}action_groups.ymlnamed bundles of actionssecurity/index.md
.../api/tenants/{name}tenants.ymlDashboards multitenancysecurity/index.md
GET /_plugins/_security/authinfo(runtime)who am I: the authenticated user + roles in the ThreadContextsecurity/lab-01-authn-tls.md
GET /_plugins/_security/health(runtime)security plugin healthsecurity/index.md
.../api/ssl/certs, .../api/securityconfigTLS / config.ymlcert info, the authc/authz chain configsecurity/lab-01-authn-tls.md

Requests are intercepted on the REST side by SecurityRestFilter and on the transport side by SecurityFilter (an ActionFilter); PrivilegesEvaluator decides per action. The free, built-in security plugin is one of the headline OpenSearch-vs-Elasticsearch differences.

# In a security checkout:
grep -rln "extends BaseRestHandler\|AbstractApiAction" src/main/java/org/opensearch/security/dlic/rest/api/
grep -rn "class SecurityRestFilter\|class SecurityFilter\b\|class PrivilegesEvaluator" \
  src/main/java/org/opensearch/security/

Finding the handler for any endpoint

When a row here is stale or missing, derive it yourself — three reliable moves:

# 1. Grep the literal path segment under rest/action (core):
grep -rn "_forcemerge\|forcemerge" server/src/main/java/org/opensearch/rest/action/

# 2. List every handler, then read its routes():
grep -rln "extends BaseRestHandler\|extends AbstractCatAction" \
  server/src/main/java/org/opensearch/rest/action/
grep -n "new Route(" server/src/main/java/org/opensearch/rest/action/admin/indices/RestForceMergeAction.java

# 3. From a running cluster, see which action a request hit:
GET /_nodes/usage              # per-REST-action call counts (RestNodesUsageAction)
GET /_tasks?detailed&actions=* # live actions, names like indices:data/read/search

The action name (indices:data/read/search, cluster:monitor/health, ...) in _tasks is the bridge from REST to the ActionType registered in ActionModule — follow it into action-framework.md.