The index.max_result_window setting caps the maximum from + size value Elasticsearch will accept for a search request against an index. Its default is 10000. Requests that ask for results beyond this point return Result window is too large, from + size must be less than or equal to: [10000]. The limit exists because deep from/size pagination requires every shard to fully sort the entire result set up to from + size, which is O(from + size) memory per shard. Raising the cap addresses the symptom; the right fix for deep pagination is almost always search_after (real-time) or scroll/point-in-time (snapshot), both of which keep cost flat regardless of depth.
Definition
index.max_result_window is a dynamic per-index integer setting. It bounds the from + size sum that Elasticsearch will execute. It does not affect aggregations, the size parameter alone when from is 0 (the first page is fine), or the search_after / scroll / point-in-time APIs. The limit is enforced per-index, so cross-index queries take the most permissive value of the indices they touch.
Default and Range
| Property | Value |
|---|---|
| Default | 10000 |
| Type | Positive integer, dynamic index setting |
| Scope | Per index |
| Applies to | from + size pagination on _search |
| Does not apply to | search_after, scroll, point-in-time (PIT), aggregations, _count |
The hard memory cost is per-shard, not per-request. With 5 primary shards, a request for from: 0, size: 10000 causes each shard to sort and ship 10000 hits to the coordinating node, which then sorts the 50000 hits and returns the top 10000. The coordinating-node buffer is the dominant cost on wide queries.
How to Change It
Per index:
PUT /my-index/_settings
{
"index.max_result_window": 20000
}
Per wildcard:
PUT /logs-*/_settings
{
"index.max_result_window": 20000
}
Via an index template so new indices inherit it:
PUT /_index_template/wide-pagination-template
{
"index_patterns": ["analytics-*"],
"template": {
"settings": { "index.max_result_window": 50000 }
}
}
Inspect:
GET /my-index/_settings?filter_path=*.settings.index.max_result_window
Reset to default by setting to null.
Why You Probably Should Not Raise It
The 10000 cap is not arbitrary. Every shard must keep a priority queue of size from + size to support the request. Doubling the limit doubles the per-shard heap demand and the network payload. At from + size = 100000 across 10 shards, the coordinating node ranks 1 million hits to return 10000. This is fine occasionally; it is fatal as a hot path.
The intended alternatives:
| Use case | Right API |
|---|---|
| Real-time user-facing pagination beyond page 1000 | search_after with a sort tie-breaker (usually _id) |
| Bulk export of a large result set, point-in-time consistent | Point-in-Time (PIT) + search_after (7.10+) |
| Bulk export, legacy | scroll (kept for compatibility; PIT is preferred on modern versions) |
| Aggregations over the full corpus | _search with aggs; no pagination needed |
search_after keeps cost flat per request regardless of depth. PIT pins a snapshot of the index so the export is consistent across pages.
Example: search_after
# First page
GET /my-index/_search
{
"size": 1000,
"sort": [
{ "created_at": "asc" },
{ "_id": "asc" }
]
}
# Next page, using the last hit's sort values from the previous response
GET /my-index/_search
{
"size": 1000,
"search_after": [ "2026-05-13T10:15:30Z", "doc-12345" ],
"sort": [
{ "created_at": "asc" },
{ "_id": "asc" }
]
}
Always include a unique tie-breaker (typically _id) in the sort. Without it, results across pages can repeat or drop documents.
Operational Impact
A raised index.max_result_window does not make pagination cheaper; it just removes the safety net. Symptoms of abuse include:
- Bursty heap usage on data nodes during deep-paged queries.
- Coordinating-node OOMs on wide cross-index searches.
- Tail latency spikes in the p95/p99 even when median latency looks fine.
- Search threadpool queue exhaustion under concurrent deep-pagination load.
If you have an application that genuinely needs to scroll through a large result set, switch to search_after or PIT. If you have a UI that allows users to paginate past page 100, consider whether infinite scroll plus search_after would serve the user better than letting them request page 5000.
Common Mistakes
- Raising the cap to "make the error go away". Now the cluster crashes instead of returning an error.
- Using
from/sizefor analytics exports. Use point-in-time +search_after, or aggregations. - Calling deep
from/sizefrom a web UI. A user-facing back button that goes to page 4523 of search results is almost never the right product behaviour. - Not including a tie-breaker in
search_aftersorts. Without_id(or another unique field), pagination can skip or repeat documents. - Forgetting the limit is per-index. Cross-index searches take the most permissive value; raising one index's limit can let queries that touch multiple indices return more than expected.
Prevent Deep-Pagination Misuse with Pulse
Pulse is an AI DBA for Elasticsearch and OpenSearch that tracks index.max_result_window (default 10000) across every index, flagging:
- Drift between intended values and per-index overrides applied via
PUT /<index>/_settings - Settings that are unsafe for your workload (e.g. window raised to 100,000 on a high-cardinality index where one deep-paginated request can OOM the coordinating node, or raised on one index in a wildcard pattern where cross-index search inherits the most permissive value)
- The downstream operational impact: coordinating-node heap during deep-paged queries, search threadpool queue saturation, p95/p99 latency on queries that paginate beyond page 100
When a client is paginating past page 1000 with from/size instead of search_after, Pulse names the offending query and the index whose max_result_window was raised - before a deep page request OOMs the coordinator.
Frequently Asked Questions
Q: What is the default value of index.max_result_window in Elasticsearch?
A: The default is 10000. This is the maximum value of from + size Elasticsearch will accept on a search request against the index. Requests beyond this cap return a Result window is too large error.
Q: How do I paginate beyond 10000 results in Elasticsearch?
A: Use search_after for real-time deep pagination, or Point-in-Time (PIT) plus search_after for consistent bulk exports. Both keep cost flat regardless of depth. Raising index.max_result_window works but does not scale.
Q: Does index.max_result_window affect aggregations?
A: No. The setting only caps from + size on hits-style searches. Aggregations are not affected, and _count queries are not affected.
Q: Can I change index.max_result_window on a running index?
A: Yes. It is a dynamic per-index setting. Use PUT /<index>/_settings and the new value takes effect on the next search. No close or reindex is required.
Q: What is the difference between search_after and scroll?
A: scroll opens a persistent snapshot that you iterate through. It keeps server-side state and is best for one-off exports. search_after is stateless and uses sort values from the previous response to fetch the next page; combined with Point-in-Time it provides the consistency of scroll without the server-side state. Modern Elasticsearch documentation recommends search_after + PIT over scroll for most use cases.
Q: What happens if I set index.max_result_window very high?
A: Per-shard memory pressure scales linearly with from + size, and coordinating-node memory scales with shards x (from + size). Very high values can OOM data nodes or coordinating nodes during heavy load. Use search_after instead.
Q: What's the best tool to prevent deep-pagination misuse in Elasticsearch?
A: Pulse is built for this. It is an AI DBA for Elasticsearch and OpenSearch that continuously tracks index.max_result_window across indices, flags clients paginating past the cap, and correlates deep-paged queries with coordinating-node heap pressure - recommending a switch to search_after or PIT before the next OOM.