Pagination
Handle cursor-based pagination in Stripe API responses
Many Stripe API endpoints return lists of objects. Stripe uses cursor-based pagination. async-stripe provides ergonomic abstractions to handle cursor management automatically, exposing the results as an asynchronous stream.
Stream Trait
Learn about the Stream trait from the futures crate
TryStreamExt Trait
Extension trait for Stream with convenient combinators
Request-Based Pagination (Recommended)
The most common way to paginate is directly from the API request builder. All List* and Search* request structs (e.g., ListCustomer, ListInvoice) generate a .paginate() method.
This method returns a ListPaginator which implements a stream, lazily fetching pages as you iterate.
let paginator = ListCustomer::new().paginate();
let mut stream = paginator.stream(&client);
// take a value out from the stream
if let Some(val) = stream.try_next().await? {
println!("GOT = {val:?}");
}
// alternatively, you can use stream combinators
let _ = stream.try_collect::<Vec<_>>().await?;The PaginationExt Trait
Sometimes you already have a List<T> object returned from another API call (e.g., CheckoutSession.line_items or Customer.sources). To paginate continuation from this existing list, you must use the PaginationExt trait.
This trait is often "hidden" from IDE autocompletion until imported.
use futures_util::TryStreamExt;
use stripe::{Client, PaginationExt, StripeError};
use stripe_checkout::{CheckoutSessionId, checkout_session::RetrieveCheckoutSession};
pub async fn print_all_line_items(client: &Client, session_id: &str) -> Result<(), StripeError> {
// Retrieve the session, expanding line_items
let session =
RetrieveCheckoutSession::new(CheckoutSessionId::from_str(session_id).expect("infallible"))
.expand(["line_items".to_string()])
.send(client)
.await?;
if let Some(line_items_list) = session.line_items {
// line_items_list is a List<LineItem>.
// To fetch subsequent pages, convert it into a paginator:
let mut stream = line_items_list
.into_paginator() // Available because of PaginationExt
.stream(client);
while let Some(item) = stream.try_next().await? {
println!("Line item: {:?}", item.description);
}
}
Ok(())
}How it works
Calling .into_paginator() on a List<T> extracts the url and the last object's ID (cursor) from the list to configure the paginator automatically.
Manual Pagination
If you prefer not to use streams, you can handle cursors manually using the starting_after parameter available on all List request structs.
pub async fn manual_pagination_example(client: &Client) -> Result<(), stripe::StripeError> {
let mut params = ListCustomer::new().limit(10);
loop {
let page = params.send(client).await?;
if page.data.is_empty() {
break;
}
for customer in &page.data {
println!("{}", customer.id);
}
if !page.has_more {
break;
}
// Set cursor for next loop
if let Some(last) = page.data.last() {
params = params.starting_after(last.id.as_str());
}
}
Ok(())
}