Fetching LeetCode Problems Without Skipping or Repeating Pages
LeetCode's problem list uses offset-based pagination, which can skip or repeat items when the list changes. Track a client-side cursor instead of trusting page numbers.
13 Sept 2026, 11:30 UTC

The problem: page two looks like page one
You write a script to export every LeetCode problem you have solved. The platform lists problems through a paginated endpoint, so you request page one, then page two, then page three. Halfway through, you notice the same problem slug appearing on two different pages, while another problem never shows up at all.
This happens because LeetCode's problem listing is typically offset-based: the client sends a page number and a page size, and the server returns that slice of the current sorted list. If the underlying sort order changes—new submissions, daily challenges, or algorithmic re-ranking—the offset for page two can point to a different set of problems than it did a minute earlier.
Use a client-side cursor, not just a page number
A page number tells the server where to start counting, but it does not identify a specific problem. A client-side cursor does: it remembers the last problem slug or ID you successfully processed. On the next request, you start after that cursor instead of blindly incrementing the page counter.
In LeetCode's GraphQL problemset endpoint, a typical request looks like this:
query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) {
problemsetQuestionList(
categorySlug: $categorySlug
limit: $limit
skip: $skip
filters: $filters
) {
total
questions {
titleSlug
title
difficulty
status
}
hasMore
}
}
Run this in an authenticated browser session or a script that sends your LeetCode session cookie. Replace $skip with the number of items already processed, not the page number multiplied by page size, if you want to resume from a known position.
Worked example: resume after the last seen slug
Suppose your script processes 50 problems per page. After page one, the last slug is two-sum. Before requesting the next batch, store that slug. On the next run, skip past every problem up to and including two-sum, then continue. If the server returns hasMore: false, you have reached the end of the current filtered set.
A minimal loop in pseudocode:
lastSlug = null
skip = 0
limit = 50
loop:
response = fetchProblems(skip: skip, limit: limit)
for problem in response.questions:
if lastSlug and problem.titleSlug == lastSlug:
continue
process(problem)
lastSlug = problem.titleSlug
if not response.hasMore:
break
skip += limit
Run this script from your local machine with Node or Python. You must include a valid LEETCODE_SESSION cookie or CSRF token; otherwise the endpoint returns 401. The main risk is rate limiting: LeetCode may respond with 429 if you poll too quickly, so add a short delay between requests and back off on errors.
Limitation: offset pagination is still a hint, not a contract
Even with a client cursor, offset-based pagination can miss newly inserted problems or show duplicates if a problem is removed or re-ranked between requests. The safest check is to deduplicate by titleSlug after every batch and compare the total count against your accumulated list. If the count drifts, restart from the last known slug rather than the last page number.
Verify the behavior yourself: open Chrome DevTools, go to the Network tab, filter for GraphQL, and load the LeetCode problem page. Compare the skip and limit values across two consecutive page loads. If the response order changes between sessions, treat page numbers as unstable.
Closing
LeetCode's offset-based listing works for casual browsing, but it is not a stable API for bulk exports. Track a client-side cursor based on problem slugs, deduplicate aggressively, and verify with DevTools before you trust a pagination loop in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.