Overview
The Auth0 Go SDK provides built-in pagination support for Management API endpoints that return lists of resources. The SDK uses a Page type with automatic iterator support to simplify working with paginated data.
Page Type
The Page type represents a single page of results:
type Page [ Cursor comparable , T any , R any ] struct {
Results [] T
Response R
RawResponse PageResponse [ Cursor , T , R ]
StatusCode int
Header http . Header
NextPageFunc func ( context . Context ) ( * Page [ Cursor , T , R ], error )
}
Type Parameters
Cursor: The type used for pagination (e.g., int for page numbers, string for checkpoint cursors)
T: The type of individual items in the page (e.g., Client, User)
R: The response type returned by the paginated endpoint
The SDK supports two pagination approaches:
Page-based pagination - Uses page numbers and per-page counts (offset-based)
Checkpoint pagination - Uses cursors to mark positions (cursor-based)
Most Management API endpoints use page-based pagination with page and per_page parameters.
Using GetNextPage
Manually fetch pages one at a time:
import (
" errors "
" github.com/auth0/go-auth0/v2/management "
" github.com/auth0/go-auth0/v2/management/core "
)
ctx := context . Background ()
page := 0
for {
listRequest := & management . ListClientsRequestParameters {
Page : management . Int ( page ),
PerPage : management . Int ( 25 ),
}
clientsPage , err := mgmt . Clients . List ( ctx , listRequest )
if err != nil {
return err
}
// Process current page results
for _ , client := range clientsPage . Results {
fmt . Printf ( "Client: %s ( %s ) \n " ,
client . GetName (),
client . GetClientID (),
)
}
// Try to get next page
nextPage , err := clientsPage . GetNextPage ( ctx )
if err != nil {
if errors . Is ( err , core . ErrNoPages ) {
// No more pages, we're done
break
}
return err
}
// Continue with next page
clientsPage = nextPage
page ++
}
Using Page Iterator
The recommended approach using automatic pagination:
listRequest := & management . ListClientsRequestParameters {
PerPage : management . Int ( 50 ),
}
clientsPage , err := mgmt . Clients . List ( ctx , listRequest )
if err != nil {
return err
}
iterator := clientsPage . Iterator ()
for iterator . Next ( ctx ) {
client := iterator . Current ()
fmt . Printf ( "Client: %s ( %s ) \n " ,
client . GetName (),
client . GetClientID (),
)
}
if iterator . Err () != nil {
return iterator . Err ()
}
The iterator automatically fetches the next page when needed, making it easier to process all results without manual page tracking.
Filtering and Sorting
Combine pagination with filtering and sorting:
listRequest := & management . ListClientsRequestParameters {
AppType : management . String ( "spa" ),
Fields : management . String ( "client_id,name,app_type" ),
IncludeFields : management . Bool ( true ),
PerPage : management . Int ( 25 ),
}
clientsPage , err := mgmt . Clients . List ( ctx , listRequest )
if err != nil {
return err
}
iterator := clientsPage . Iterator ()
for iterator . Next ( ctx ) {
client := iterator . Current ()
// Only SPA clients will be returned
fmt . Printf ( "SPA Client: %s \n " , client . GetName ())
}
Some endpoints like Logs and Organizations use checkpoint (cursor-based) pagination, where you use the ID of the last item as a checkpoint for the next request.
Using Iterator (Recommended)
logListRequest := & management . ListLogsRequestParameters {
Take : management . Int ( 100 ),
}
logsPage , err := mgmt . Logs . List ( ctx , logListRequest )
if err != nil {
return err
}
iterator := logsPage . Iterator ()
for iterator . Next ( ctx ) {
log := iterator . Current ()
fmt . Printf ( "Log: %s - %s \n " ,
log . GetID (),
log . GetType (),
)
}
if iterator . Err () != nil {
return iterator . Err ()
}
For advanced use cases where you need fine-grained control:
var fromLogID * string
for {
logListRequest := & management . ListLogsRequestParameters {
Take : management . Int ( 100 ),
}
// Set checkpoint from previous iteration
if fromLogID != nil {
logListRequest . From = fromLogID
}
logsPage , err := mgmt . Logs . List ( ctx , logListRequest )
if err != nil {
return err
}
// Process logs
for _ , log := range logsPage . Results {
fmt . Printf ( "Log: %s - %s \n " ,
log . GetID (),
log . GetType (),
)
}
// Check if we have more logs
if len ( logsPage . Results ) == 0 {
break // No more logs
}
// Use the ID of the last log as the checkpoint for the next request
lastLog := logsPage . Results [ len ( logsPage . Results ) - 1 ]
fromLogID = lastLog . ID
}
Error Handling
The pagination system uses the ErrNoPages sentinel error to signal when no more pages are available:
var ErrNoPages = errors . New ( "no pages remain" )
Checking for End of Pages
import " errors "
nextPage , err := currentPage . GetNextPage ( ctx )
if err != nil {
if errors . Is ( err , core . ErrNoPages ) {
// This is expected - no more pages
fmt . Println ( "Finished processing all pages" )
return nil
}
// This is an actual error
return fmt . Errorf ( "error fetching next page: %w " , err )
}
Iterator Error Handling
The iterator automatically handles ErrNoPages:
iterator := page . Iterator ()
for iterator . Next ( ctx ) {
// Process item
item := iterator . Current ()
}
// Check for errors (ErrNoPages is filtered out)
if iterator . Err () != nil {
return fmt . Errorf ( "iterator error: %w " , iterator . Err ())
}
Common Patterns
Collecting All Results
func getAllClients ( ctx context . Context , mgmt * client . Management ) ([] * management . Client , error ) {
var allClients [] * management . Client
listRequest := & management . ListClientsRequestParameters {
PerPage : management . Int ( 100 ),
}
clientsPage , err := mgmt . Clients . List ( ctx , listRequest )
if err != nil {
return nil , err
}
iterator := clientsPage . Iterator ()
for iterator . Next ( ctx ) {
allClients = append ( allClients , iterator . Current ())
}
if iterator . Err () != nil {
return nil , iterator . Err ()
}
return allClients , nil
}
Processing with Early Exit
func findUserByEmail ( ctx context . Context , mgmt * client . Management , email string ) ( * management . User , error ) {
listRequest := & management . ListUsersRequestParameters {
Search : management . String ( fmt . Sprintf ( "email: \" %s \" " , email )),
PerPage : management . Int ( 50 ),
}
usersPage , err := mgmt . Users . List ( ctx , listRequest )
if err != nil {
return nil , err
}
iterator := usersPage . Iterator ()
for iterator . Next ( ctx ) {
user := iterator . Current ()
if user . GetEmail () == email {
return user , nil // Found it!
}
}
if iterator . Err () != nil {
return nil , iterator . Err ()
}
return nil , fmt . Errorf ( "user not found" )
}
Batch Processing
func batchProcessUsers ( ctx context . Context , mgmt * client . Management ) error {
listRequest := & management . ListUsersRequestParameters {
PerPage : management . Int ( 50 ),
}
usersPage , err := mgmt . Users . List ( ctx , listRequest )
if err != nil {
return err
}
pageNum := 0
for {
pageNum ++
fmt . Printf ( "Processing page %d ( %d users) \n " , pageNum , len ( usersPage . Results ))
// Process all users in current page
for _ , user := range usersPage . Results {
if err := processUser ( ctx , user ); err != nil {
return fmt . Errorf ( "error processing user %s : %w " , user . GetUserID (), err )
}
}
// Get next page
nextPage , err := usersPage . GetNextPage ( ctx )
if err != nil {
if errors . Is ( err , core . ErrNoPages ) {
break
}
return err
}
usersPage = nextPage
}
fmt . Printf ( "Processed %d pages \n " , pageNum )
return nil
}
Best Practices
Use iterators for most cases
The Iterator() pattern is cleaner and handles pagination automatically. Use GetNextPage() only when you need fine-grained control over page fetching.
Set appropriate page sizes
Balance between fewer API calls (larger pages) and memory usage (smaller pages). Typical values are 25-100 items per page.
Handle ErrNoPages explicitly
When using GetNextPage(), always check for ErrNoPages using errors.Is() to distinguish between “no more pages” and actual errors.
Use context for cancellation
Pass a context to pagination methods to enable cancellation of long-running operations: ctx , cancel := context . WithTimeout ( context . Background (), 30 * time . Second )
defer cancel ()
iterator := page . Iterator ()
for iterator . Next ( ctx ) {
// Will respect timeout
}
When processing large result sets, be mindful of Auth0’s rate limits. Consider adding delays between pages if processing many resources.
Next Steps
Management API Learn more about the Management API client
Error Handling Learn how to handle pagination errors