from=10000&size=10 makes every shard produce 10,010 hits and the coordinating node sort 50,050 of them to return ten. The cost grows with the offset, which is why there is a hard limit at 10,000.
# refused past index.max_result_window
GET /products/_search { "from": 10000, "size": 10 }
# for a user scrolling: search_after, using the last sort value
GET /products/_search
{ "size": 10, "sort": [ { "price": "asc" }, { "_id": "asc" } ],
"search_after": [ 4900, "FR-100" ] }
# for an export: the scroll API, which holds a snapshot
search_after needs a tie-breaker in the sort or the cursor is ambiguous, which is why _id is there. Neither approach gives a jump-to-page-500 control, and that is the actual finding: nobody pages to 500, and building it costs more than it is worth. Raising max_result_window is available and is a way to make the cluster fall over politely.