Using Cursor‑Based Pagination in the Facebook Graph API to Retrieve Large Result Sets Reliably
Learn how to page through Facebook Graph API edges with cursors, follow the paging.next link, and avoid common pitfalls that cause incomplete or duplicated data.
20 Feb 2026, 20:57 UTC

Use cursor‑based pagination
The Facebook Graph API supports three ways to page through edges: offset‑based, time‑based (since/until), and cursor‑based (before/after). Cursor‑based pagination is the recommended default for most edges because the opaque cursor values remain valid even if items are added or removed while you iterate.
Each response includes a paging object that contains cursors.after, cursors.before, and often a fully formed next URL. To get the next page you either follow that URL verbatim or pass the after cursor as a query parameter on the same endpoint.
Worked example
Suppose you want to fetch the feed of a Facebook Page. Replace {page-id} with the numeric ID of the page and {access-token} with a valid page access token that has the pages_read_engagement permission.
GET https://graph.facebook.com/v19.0/{page-id}/feed?limit=25&access_token={access-token}
The API returns a JSON body similar to:
{
"data": [
{ "id": "12345_67890", "message": "First post" },
… up to 25 items …
],
"paging": {
"cursors": {
"after": "Q2c4…",
"before": "Abc1…"
},
"next": "https://graph.facebook.com/v19.0/{page-id}/feed?limit=25&access_token={access-token}&after=Q2c4…"
}
}
The after cursor is an opaque string; you must not try to decode or reconstruct it. To fetch the next page, either:
- Send a GET request to the URL in
paging.nextexactly as it appears, or - Repeat the original request and add
&after=Q2c4…(keeping the samelimitandaccess_token).
Continue this process until the response no longer contains a paging.next field. The absence of that link is the reliable signal that you have reached the end of the collection.
Limits and common mistakes
- Page size is not guaranteed. The
limitparameter requests a maximum number of items; the API may return fewer (even zero) while still providing anextlink. Do not stop paging just because a page returned fewer items than requested. - Cursors are edge‑ and version‑specific. A cursor obtained from
/feedcannot be reused on/photosor on a different API version. Always treat cursors as opaque tokens tied to the exact edge and version you used. - Do not construct cursors manually. Attempting to guess or modify the cursor value will result in errors or silent data loss.
- Rate limits and token expiration. Long pagination sequences can hit app‑ or user‑level rate limits, producing HTTP 4xx responses with error codes like
4(application limit) or17(user limit). Implement exponential backoff and refresh the access token if you receive an OAuth error (190or102). - Empty data does not mean end. An empty
dataarray can appear mid‑iteration (e.g., when a filter removes all items on a page). Only the missingpaging.nextindicates termination.
Practical verification
To confirm your implementation works:
- Create a test app in the Meta for Developers dashboard, generate a page access token, and request the first page of
/me/feedor a page’s/feededge with a modest limit (e.g., 5). - Inspect the response for
paging.cursors.afterandpaging.next. - Follow the
nextlink and verify that theaftercursor in the new response differs from the previous one. - Repeat until
paging.nextis absent; count the total number of items retrieved and compare with a known total (if available) to ensure no duplication or omission. - Optionally, repeat the same request with
limit=100to observe that the API may still return fewer than 100 items while providing anextlink.
These steps let you validate that your cursor‑handling logic respects the API’s semantics before deploying it in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.