Skip to main content

Overview

The Moflay API uses cursor-based pagination for all list endpoints. This approach provides several advantages over traditional offset-based pagination:
  • Consistent results - No duplicate or missing items when data changes during pagination
  • Performance - Efficient for large datasets with proper database indexing
  • Reliability - Works well even when items are added or removed between requests

How Cursor Pagination Works

Cursor pagination uses a “cursor” (a pointer to a specific position in the dataset) rather than an offset. The cursor is a base64-encoded value that points to the last item from the previous page, based on the createdAt field for consistent ordering.

Basic Flow

  1. First Request - Make a request without a cursor to get the first page
  2. Subsequent Requests - Use the cursor from the previous response to get the next page
  3. Last Page - When hasNextPage is false, you’ve reached the end

Pagination Parameters

All list endpoints support these query parameters:
ParameterTypeRequiredDefaultDescription
cursorstringNo-Base64-encoded cursor for pagination, representing the last item from the previous page
limitintegerNo10Number of items to return (1-100)

Example Request

curl -H "Authorization: Bearer mf_test_1234567890" \
  "https://api.moflay.com/v1/customers?limit=5"

Example Response

{
  "data": [
    {
      "id": "cus_ABC123",
      "name": "John Doe",
      "email": "john@example.com",
      "phoneNumber": "254712345678",
      "createdAt": "2024-12-01T10:00:00.000Z"
    },
    {
      "id": "cus_DEF456", 
      "name": "Jane Smith",
      "email": "jane@example.com",
      "phoneNumber": "254723456789",
      "createdAt": "2024-12-01T11:00:00.000Z"
    }
  ],
  "meta": {
    "cursor": "eyJpZCI6ImN1c19ERUY0NTYifQ==",
    "hasNextPage": true,
    "hasPreviousPage": false
  }
}

Getting the Next Page

To get the next page, use the cursor value from the previous response:
curl -H "Authorization: Bearer mf_test_1234567890" \
  "https://api.moflay.com/v1/customers?cursor=eyJpZCI6ImN1c19ERUY0NTYifQ==&limit=5"

End of Results

When you’ve reached the last page, the response will have:
{
  "data": [...],
  "meta": {
    "cursor": null,
    "hasNextPage": false,
    "hasPreviousPage": true
  }
}

Sorting and Pagination

All list endpoints support sorting, which affects the pagination order:
  • Default sorting - Most endpoints default to createdAt in descending order (newest first)
  • Custom sorting - Use sortBy and sortOrder parameters to customize
  • Consistent ordering - The same sort parameters must be used across all pages
  • Cursor basis - The cursor is always based on the createdAt field for reliable pagination

Example with Sorting

# Sort customers by name in ascending order
curl -H "Authorization: Bearer mf_test_1234567890" \
  "https://api.moflay.com/v1/customers?sortBy=name&sortOrder=asc&limit=10"

Filtering and Pagination

Filters can be combined with pagination. The same filters must be applied to all pages:
# Filter and paginate transactions
curl -H "Authorization: Bearer mf_test_1234567890" \
  "https://api.moflay.com/v1/transactions?statuses[]=completed&statuses[]=pending&limit=20"

Best Practices

1. Use Appropriate Limit Values

  • Small datasets - Use smaller limits (5-20) for better performance
  • Large datasets - Use larger limits (50-100) to reduce API calls
  • Real-time updates - Use smaller limits for more frequent updates

2. Handle Rate Limits

  • Implement exponential backoff when you receive 429 responses
  • Consider the rate limits when choosing your pagination strategy

3. Error Handling

  • Always check for hasNextPage before making the next request
  • Handle cases where the cursor might be invalid or expired
  • Implement retry logic for transient failures

4. Caching Considerations

  • Cursors are not stable across API versions
  • Don’t store cursors for long periods
  • Refresh cursors if you encounter errors

Common Patterns

Iterating Through All Results

async function getAllCustomers(apiKey) {
  let allCustomers = [];
  let cursor = null;
  let hasMore = true;

  while (hasMore) {
    const params = new URLSearchParams({ limit: '50' });
    if (cursor) params.append('cursor', cursor);

    const response = await fetch(`https://api.moflay.com/v1/customers?${params}`, {
      headers: { Authorization: `Bearer ${apiKey}` }
    });

    const data = await response.json();
    allCustomers.push(...data.data);
    
    cursor = data.meta.cursor;
    hasMore = data.meta.hasNextPage;
  }

  return allCustomers;
}

Processing in Batches

async function processCustomersInBatches(apiKey, processor) {
  let cursor = null;
  let hasMore = true;

  while (hasMore) {
    const params = new URLSearchParams({ limit: '20' });
    if (cursor) params.append('cursor', cursor);

    const response = await fetch(`https://api.moflay.com/v1/customers?${params}`, {
      headers: { Authorization: `Bearer ${apiKey}` }
    });

    const data = await response.json();
    
    // Process the current batch
    await processor(data.data);
    
    cursor = data.meta.cursor;
    hasMore = data.meta.hasNextPage;
  }
}

Troubleshooting

Invalid Cursor Error

If you receive an error about an invalid cursor:
  1. Check cursor format - Ensure the cursor is properly URL-encoded
  2. Verify cursor source - Make sure you’re using the cursor from the correct endpoint
  3. Handle expired cursors - Start pagination from the beginning if cursors are too old

Empty Results

If you get empty results but hasNextPage is true:
  1. Check filters - Verify your filter parameters are correct
  2. Verify permissions - Ensure your API key has access to the data
  3. Check date ranges - Verify your date filters are appropriate

Performance Issues

If pagination is slow:
  1. Reduce limit - Try smaller page sizes
  2. Add indexes - Ensure your database has proper indexes for sorting
  3. Check filters - Complex filters can impact performance
I