Scope and the key separation

This design applies to Stripe v1 list pagination. A cursor tells the client where to ask for another page; it is not, by itself, evidence that target writes and checkpoint storage completed together.

For the usual v1 list direction, returned objects are ordered newest first. Responses expose their items through data and indicate whether another request is needed with has_more.

The proposed importer therefore separates four concerns: obtain a page, apply repeatable target writes, durably record progress, and resolve an interruption whose outcome cannot be established. That separation is an application architecture, not a Stripe transaction guarantee.

Use a replay-safe checkpoint policy

For a forward traversal, retain the final object identifier from a fully handled page and use it as starting_after on the subsequent request. Stripe documents that starting_after and ending_before are alternative cursor parameters, so do not send both in one list request.

Choose a target-write rule that can safely receive the same source object again, such as an upsert keyed by the source object ID or a durable deduplication record. This is a recommendation: the supplied pagination material does not define target idempotency, atomic commits, or recovery behavior.

Advance the durable cursor only after the application has decided that every object in its page has a known successful disposition. If a process stops after writes but before the checkpoint commit, replaying the page is intentional; the repeatable-write rule is what makes that recovery tolerable.

Page-processing state transition table

The table describes one conservative application policy. “Durable” refers to the importer's own datastore, not to a persistence property supplied by the pagination API.

Illustrative state machine for one fetched list page
StateEntry conditionActionDurable record before exitNext state
ReadyA prior checkpoint is available, or this is the initial request.Request a page using the selected traversal cursor.The prior checkpoint remains unchanged.Fetched
FetchedA response has arrived.Capture page identity, object IDs, and the candidate cursor represented by its final object.Optional attempt record only; it is not progress.Writing
WritingThe page has objects awaiting application.Apply each repeatable write and retain per-object success evidence according to the application's rule.Per-object outcomes, when the target supports them.Checkpointing or Uncertain
CheckpointingAll page objects have known successful dispositions.Atomically commit the candidate cursor with any required target evidence, if the datastore can provide that transaction.New checkpoint after commit.Ready or Complete
UncertainA request, write, or checkpoint result is unknown.Do not advance progress merely because an attempt was made; inspect durable evidence and replay safely when needed.The last known checkpoint and any recorded outcomes.Writing, Checkpointing, or Ready
CompleteThe processed response says has_more is false and its handling is committed.Record completion under the application's run policy.Final checkpoint and completion marker.Complete

A checkpoint denotes application progress only after its own commit succeeds. The pagination documentation supplies page navigation signals, but it does not specify this write-and-checkpoint protocol.

Crash matrix for an interrupted page

Use the matrix to turn a vague “retry the job” response into a defined recovery decision. Each row is a hypothetical interruption, not a report of an executed failure.

Illustrative recovery matrix
Interruption pointWhat may be durableCheckpoint positionRecovery actionWhy the action is conservative
Before any target writeAt most an attempt record.Prior page.Fetch or replay the page from the prior checkpoint.No object has a known completed disposition.
During target writesA subset of object outcomes may exist.Prior page.Reapply the page with ID-based deduplication or inspect object outcome records first.The page is not eligible for cursor advancement.
After all writes, before checkpoint commitAll writes may exist.Prior page.Replay the page or prove every outcome before committing progress.Replay covers an unrecorded progress transition.
While checkpoint commit is uncertainWrites and perhaps the new checkpoint.Either prior or candidate position is possible.Read the durable checkpoint; if it is absent, replay safely, and if it is present, continue after it.The importer does not infer commit status from a lost response.
After checkpoint commitWrites and new cursor.Candidate page cursor.Resume with the stored cursor and request the following page.Progress is explicitly recorded before moving on.

This policy has an unavoidable prerequisite: the target must define how duplicate object delivery is recognized. Without that rule, a page replay can create a duplicate even though the source cursor is handled correctly.

Traversal choices and ordering traps

Stripe's library helpers can continue list retrieval until has_more becomes false. They reduce request-loop code, but they do not create a durable boundary between the importer's writes and its checkpoint.

Manual traversal is usually easier to align with page-level recovery because the importer controls when the next cursor request is issued. Stripe's documented manual pattern derives the next forward cursor from the last object in the received page when more results remain.

Do not assume every traversal has the same processing order. Auto-pagination used with ending_before is documented to deliver chronological results, unlike the normal newest-first list direction; encode the chosen direction in checkpoint and reconciliation rules.

Keep list, search, and v2 contracts separate

Search pagination has a different continuation mechanism: the first search call has no page cursor, and a later call submits the preceding response's next_page value as page. A list-import checkpoint must not be reused as though it were a search token.

The supplied documentation also distinguishes v2 pagination from the v1 interface. Verify the specific resource contract before applying this state machine outside v1 list endpoints.

Changing-source limits and operational questions

A list cursor identifies a location in the returned ordering; the supplied material does not establish a frozen source snapshot for a long-running import. Treat inserts, updates, ordering stability, and reconciliation coverage as unresolved design questions until separately verified.

Consider a bounded overlap or a later reconciliation scan when missed changes would be costly. That is a proposed mitigation, not a documented guarantee, and its useful window depends on the target resource and the application's change model.

  • Can the target store per-object outcomes and the page cursor in one atomic transaction?
  • Which source object identifier is the idempotency key, and how long must deduplication evidence remain available?
  • What retry budget and alert should apply when a checkpoint commit result cannot be determined?
  • Does the chosen endpoint use v1 list cursors, search tokens, or a separate v2 pagination contract?

Stripe v1 list requests use a default page size of 10, while limit accepts values from 1 through 100. Select a page size based on target transaction duration, retry cost, and rate constraints rather than treating the maximum as an import-safety setting.

Limitations

This article does not verify a particular datastore's transaction model, an endpoint's source-change consistency, or an application's idempotency implementation. It also does not establish behavior for Stripe v2 resources or substitute search tokens for v1 list cursors.