Skip to main content
Many FlexPrice API endpoints that return lists of resources support pagination to manage large result sets efficiently. This guide explains how to work with paginated responses.

Pagination Method

FlexPrice uses limit-offset pagination for list endpoints. This method provides:
  • Consistent results - Results are stable across pagination requests
  • Random access - Jump to any page in the result set
  • Total count - Know the total number of results available

Pagination Parameters

List endpoints that support pagination accept these query parameters:
limit
integer
default:"20"
Maximum number of items to return per page. Most endpoints default to 20 items.Range: Typically 1-100 (varies by endpoint)
offset
integer
default:"0"
Number of items to skip before starting to return results. Use this to navigate to different pages.Example: offset=40 with limit=20 returns items 41-60

Example Request

Basic Pagination
# Get first page (items 1-20)
curl -X GET "http://localhost:8080/v1/customers?limit=20&offset=0" \
  -H "X-API-Key: your-api-key"

# Get second page (items 21-40)
curl -X GET "http://localhost:8080/v1/customers?limit=20&offset=20" \
  -H "X-API-Key: your-api-key"

# Get third page (items 41-60)
curl -X GET "http://localhost:8080/v1/customers?limit=20&offset=40" \
  -H "X-API-Key: your-api-key"

Pagination Response Format

Paginated responses include both the data items and pagination metadata:
Paginated Response
{
  "items": [
    {
      "id": "cus_abc123",
      "external_customer_id": "cus_123",
      "name": "Acme Corp",
      "email": "billing@acme.com",
      "created_at": "2024-03-15T10:30:00Z"
    },
    {
      "id": "cus_def456",
      "external_customer_id": "cus_456",
      "name": "TechStart Inc",
      "email": "finance@techstart.io",
      "created_at": "2024-03-14T15:20:00Z"
    }
  ],
  "pagination": {
    "total": 145,
    "limit": 20,
    "offset": 0
  }
}
items
array
Array of resource objects for the current page
pagination
object
Pagination metadata
total
integer
Total number of items available across all pages
limit
integer
Number of items per page (as requested)
offset
integer
Current offset in the result set

Calculating Pagination

Total Pages

Calculate the total number of pages:
Calculate Pages
const totalPages = Math.ceil(pagination.total / pagination.limit);

// Example: 145 total items, 20 per page
// totalPages = Math.ceil(145 / 20) = 8 pages

Current Page Number

Determine which page you’re on:
Current Page
const currentPage = Math.floor(pagination.offset / pagination.limit) + 1;

// Example: offset=40, limit=20
// currentPage = Math.floor(40 / 20) + 1 = 3

Next Page Offset

Calculate the offset for the next page:
Next Page
const nextOffset = pagination.offset + pagination.limit;

// Example: offset=40, limit=20
// nextOffset = 40 + 20 = 60

Has More Pages

Check if there are more pages:
Check More Pages
const hasMore = (pagination.offset + pagination.limit) < pagination.total;

// Example: offset=40, limit=20, total=145
// hasMore = (40 + 20) < 145 = true

Endpoints with Pagination

These FlexPrice API endpoints support pagination:

List Endpoints

Customers

GET /v1/customers

Plans

GET /v1/plans

Subscriptions

GET /v1/subscriptions

Invoices

GET /v1/invoices

Events

GET /v1/events

Meters

GET /v1/meters

Features

GET /v1/features

Wallets

GET /v1/wallets

Prices

GET /v1/prices

Credit Grants

GET /v1/creditgrants

Payments

GET /v1/payments

Entitlements

GET /v1/entitlements

Search Endpoints

Search/query endpoints (POST /v1/*/search) also support pagination through the request body:
Search with Pagination
curl -X POST http://localhost:8080/v1/customers/search \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "status": "active"
    },
    "limit": 50,
    "offset": 0
  }'

Implementation Examples

Iterating Through All Pages

from flexprice import Flexprice

client = Flexprice(api_key="your-api-key")

def get_all_customers():
    all_customers = []
    offset = 0
    limit = 100
    
    while True:
        response = client.customers.list(limit=limit, offset=offset)
        all_customers.extend(response.items)
        
        # Check if there are more pages
        if offset + limit >= response.pagination.total:
            break
            
        offset += limit
    
    return all_customers

customers = get_all_customers()
print(f"Retrieved {len(customers)} customers")

Building a Paginated UI

React Component
import { useState, useEffect } from 'react';
import { Flexprice } from '@flexprice/sdk';

const client = new Flexprice({ apiKey: process.env.FLEXPRICE_API_KEY });

function CustomerList() {
  const [customers, setCustomers] = useState([]);
  const [pagination, setPagination] = useState({ total: 0, limit: 20, offset: 0 });
  const [currentPage, setCurrentPage] = useState(1);
  
  const fetchPage = async (page: number) => {
    const offset = (page - 1) * pagination.limit;
    const response = await client.customers.list({ 
      limit: pagination.limit, 
      offset 
    });
    
    setCustomers(response.items);
    setPagination(response.pagination);
    setCurrentPage(page);
  };
  
  useEffect(() => {
    fetchPage(1);
  }, []);
  
  const totalPages = Math.ceil(pagination.total / pagination.limit);
  
  return (
    <div>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
          </tr>
        </thead>
        <tbody>
          {customers.map(customer => (
            <tr key={customer.id}>
              <td>{customer.external_customer_id}</td>
              <td>{customer.name}</td>
              <td>{customer.email}</td>
            </tr>
          ))}
        </tbody>
      </table>
      
      <div className="pagination">
        <button 
          onClick={() => fetchPage(currentPage - 1)}
          disabled={currentPage === 1}
        >
          Previous
        </button>
        
        <span>Page {currentPage} of {totalPages}</span>
        
        <button 
          onClick={() => fetchPage(currentPage + 1)}
          disabled={currentPage === totalPages}
        >
          Next
        </button>
      </div>
    </div>
  );
}

Best Practices

1

Use appropriate page sizes

Balance between reducing API calls and response size. For most use cases, 20-100 items per page is optimal.
# Too small - many API calls
curl "http://localhost:8080/v1/customers?limit=5"

# Good balance
curl "http://localhost:8080/v1/customers?limit=50"

# Might be too large
curl "http://localhost:8080/v1/customers?limit=500"
2

Handle empty results

Check if the items array is empty to handle pages with no results.
if (response.items.length === 0) {
  console.log('No items found');
}
3

Cache total counts

The total count doesn’t change frequently. Cache it to avoid unnecessary requests.
// Fetch first page to get total
const firstPage = await client.customers.list({ limit: 50, offset: 0 });
const totalPages = Math.ceil(firstPage.pagination.total / 50);

// Use cached totalPages for pagination UI
4

Consider cursor pagination for real-time data

For frequently changing data (like event streams), consider using filters or time-based queries instead of offset pagination.
# Query events by time window instead of pagination
curl -X POST http://localhost:8080/v1/events/query \
  -d '{
    "filters": {
      "timestamp_gte": "2024-03-15T00:00:00Z",
      "timestamp_lt": "2024-03-16T00:00:00Z"
    }
  }'
5

Handle pagination edge cases

Account for scenarios like deleted items or filtered views.
// When filtering, total reflects filtered count
const response = await client.customers.list({
  limit: 20,
  offset: 0,
  filters: { status: 'active' }
});

// pagination.total is count of active customers only
console.log(`${response.pagination.total} active customers`);

Pagination Limits

Different endpoints may have different maximum limit values. The API will return a validation error if you exceed the maximum:
{
  "success": false,
  "error": {
    "message": "limit must be between 1 and 100",
    "internal_error": "validation_error"
  }
}
Common limits:
  • Most list endpoints: 100 items
  • Event endpoints: 1000 items (due to high volume)
  • Search endpoints: 500 items

Performance Considerations

Deep pagination can be slow. Fetching results at very high offsets (e.g., offset=10000) takes longer because the database must scan and skip many rows.For better performance with large datasets:
  1. Use filters to narrow results before paginating
  2. Cache results when possible
  3. Consider time-based queries for event data
  4. Export to files for bulk operations (use the Tasks API)

Next Steps

API Overview

Learn about the API structure and resources

Authentication

Set up API authentication