Payments are experiencing issues due to temporary restrictions in Russia. If your payment does not go through, please submit a support request.Our support team is available 24/7 — we are always here to help with hosting and server issues.We are now accepting requests for dedicated server rental and colocation services in our data center.Reminder: we recommend enabling backups for additional data protection.A new VPS/VDS lineup with NVMe storage and improved performance is now available.Maintenance work on some servers has been completed. All services are operating normally.
Article5 min readViews0

API Pagination: Why Full Catalog Exports Can Skip Products

Deleting a record between pages shifts the result set. We examine offset, cursor, consistent snapshot, and export resumption after a failure using a small example.

Comments 0

Denis turns a page with a yellow bookmark: the continuation boundary in page-by-page reading
In this article

The export read the first 100 items, then the next 100. There are no API errors, yet 1 item was skipped in the result. A record was removed between requests from the start of the list: everything after it shifted. The "skip first 100" transition now refers to a different boundary.

For exporting a changing catalog, you must determine two things in advance: how to continue reading and what state counts as a complete result. A stable cursor helps with the first. For the second, a consistent snapshot or separate change verification may be required. The mere presence of a "next page" button does not solve both tasks.

Skipping occurs even with correct sorting

Take a sample list of identifiers 10, 20, 30, 40, 50, 60 with a page size of three. The first response contains 10, 20, 30. Then record 20 is deleted. The list becomes 10, 30, 40, 50, 60. A request with an offset of three skips the first three current records and returns 50, 60. Identifier 40 did not appear in any response.

If a record is inserted at the beginning before the second page, the opposite effect may occur: an already read product may end up shifted. Removing duplicates on the receiver side will eliminate the extra copy but will not restore the skipped record. Therefore, the number of error-free responses is insufficient for acceptance testing of the export.

Even with an unchanged dataset, a specific order is required. Sorting solely by modification time does not establish a unique sequence if multiple products share the same timestamp. An additional stable attribute is needed to resolve ties. PostgreSQL documentation explicitly warns that limiting a query without a unique order may return unpredictable portions of the result. This is a property of the query, not a guarantee of a specific API.

A cursor locks the continuation boundary

In a simple educational variant, data is ordered by an increasing immutable identifier. After the first page, the last read key 30 is remembered, and subsequent records are requested after that key. Removing 20 no longer shifts this boundary: the next set will be 40, 50, 60. This model resolves the demonstrated shifting problem.

After deleting ID 20, an offset of three returns 50 and 60, skipping 40; reading after key 30 returns 40, 50, and 60
A sample list with immutable keys. A cursor eliminates the display shift, but alone it does not fix the catalog snapshot.

A real API may return an opaque continuation token. It should be returned to the service in the designated field, preserving filters and traversal direction. Do not attempt to extract a page number from the token or increment it manually. The format, lifetime, and behavior after data changes are defined by the service.

Different interfaces also differ in their end-of-stream indicators. This may be the absence of a next token or a separate flag. A record count lower than the requested limit does not always signify the end. Google's API design guidelines allow for incomplete, including empty, pages with continuation. The stop condition is derived from the specific method's contract.

A cursor does not freeze the catalog

Now, after reading the key 30, a new product with a smaller key may appear, or an already read card may change. A standard pass "only after the last key" will not detect this change. If sorting is based on a mutable field, a record may cross the boundary in either direction. Therefore, the promise that "cursor-based pagination excludes any skips" is too broad.

For the task of "full catalog at a single point in time," a mechanism that truly captures state is required: for example, a server-prepared export or a supported snapshot with retention rules. Passing the same date in a filter is insufficient if records still change between requests and the service does not promise consistency.

For the task of bringing the recipient to the current state, an alternative model is acceptable: an initial load followed by applying changes from a consistent point. Here, decisions are made separately regarding how deletions are handled, how long history is retained, and what to do when the retention period expires. A full snapshot and a stream of changes require different readiness criteria.

After a break, resume the confirmed segment

The position must be saved along with an understanding of which records the recipient has already reliably processed. If the next cursor is saved before the current page's records, a failure will leave unprocessed items behind the checkpoint. If the cursor is saved later, a page may be repeated; the handler must recognize the already accepted version of the object.

A single token in the log does not describe the entire selection. Resumption requires consistent filters, sorting, format version, and access context. If the token expires, one cannot guess a new one and continue from an approximate location. The contract must provide for a full snapshot or another verifiable recovery method.

First read the page, then reliably save objects, and finally record the continuation
An early checkpoint leaves a risk of skipping items. A late one allows page repetition, which the handler must account for.

In a test copy, it is useful to process a small known set and, between pages, delete an early record, add a new one, change a sort field, and interrupt page saving. Identifiers and versions are compared, not just the final count: one omission and one repetition can yield a beautiful but incorrect total.

This article examines a sample list implementation, not a specific 1C-Bitrix module. When requesting an exchange, ask the developer for a precise answer: what state is exported, what criterion determines the continuation of the traversal, and how completeness is confirmed after a failure. These three conditions allow you to validate the result. The last page received alone does not provide such confirmation.

Discussion 0

Share your experience and ask questions. Comments without links appear after editorial review.

No comments yet. Start the discussion.