# Pagination Learn how to paginate through list results using cursor-based pagination. ## Overview All filter endpoints in the MVMNT API use cursor-based pagination to efficiently retrieve large result sets. Pagination is consistent across all resource types (orders, vendors, carriers, customers, etc.). ## How It Works ### Request Format Filter endpoints use POST requests with pagination parameters in the request body: ```bash POST /v1/vendors/filter Content-Type: application/json { "filter": { ... }, "pageSize": 50, "cursor": null } ``` ### Response Format All paginated responses return a consistent structure: ```json { "data": [ { "id": "...", "name": "..." }, { "id": "...", "name": "..." } ], "pageInfo": { "pageSize": 50, "hasNextPage": true, "hasPreviousPage": false, "endCursor": "eyJpZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCJ9" } } ``` ## Pagination Parameters ### pageSize **Type:** Integer **Default:** 50 **Range:** 1 - 250 **Description:** Number of results to return per page ```json { "pageSize": 100 } ``` ### cursor **Type:** String (nullable) **Default:** null **Description:** Opaque cursor token from previous response's `pageInfo.endCursor` ```json { "cursor": "eyJpZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCJ9" } ``` **First page:** Omit `cursor` or set to `null` **Subsequent pages:** Use `endCursor` from previous response ## PageInfo Object The `pageInfo` object in every response provides pagination metadata: | Field | Type | Description | | --- | --- | --- | | `pageSize` | integer | Number of items in current page | | `hasNextPage` | boolean | Whether more results are available | | `hasPreviousPage` | boolean | Whether previous page exists | | `endCursor` | string | Cursor for next page (null if no next page) | ## Basic Example ### Fetch First Page ```bash curl -X POST https://api.mvmnt.io/v1/vendors/filter \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "filter": { "status": { "equalTo": "ACTIVE" } }, "pageSize": 50 }' ``` **Response:** ```json { "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "friendlyId": "V100001", "name": "ABC Warehouse", "status": "ACTIVE" }, { "id": "660e8400-e29b-41d4-a716-446655440001", "friendlyId": "V100002", "name": "XYZ Storage", "status": "ACTIVE" } // ... 48 more results ], "pageInfo": { "pageSize": 50, "hasNextPage": true, "hasPreviousPage": false, "endCursor": "eyJpZCI6IjY2MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMSJ9" } } ``` ### Fetch Next Page Use `endCursor` from the previous response: ```bash curl -X POST https://api.mvmnt.io/v1/vendors/filter \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "filter": { "status": { "equalTo": "ACTIVE" } }, "pageSize": 50, "cursor": "eyJpZCI6IjY2MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMSJ9" }' ``` ### Last Page When `hasNextPage` is `false`, you've reached the end: ```json { "data": [ // ... remaining results ], "pageInfo": { "pageSize": 50, "hasNextPage": false, "hasPreviousPage": true, "endCursor": null } } ``` ## Best Practices ### ✅ Do - **Use maximum page size for batch processing**: Set `pageSize: 250` when fetching all results - **Store cursor tokens temporarily**: Only valid for the current query, don't persist long-term - **Handle hasNextPage correctly**: Always check `hasNextPage` instead of checking if data is empty - **Preserve filter criteria**: Use identical filter on all pages of a query - **Implement retry logic**: Network failures can occur during pagination ### ❌ Don't - **Don't use offset-based pagination**: MVMNT uses cursor-based pagination, not `page` numbers - **Don't modify filters mid-pagination**: Using different filters with the same cursor produces undefined results - **Don't persist cursors**: Cursors may expire or become invalid over time - **Don't assume page size matches result count**: Last page may have fewer results - **Don't paginate unnecessarily**: If you need all results, fetch them in one efficient loop ## Troubleshooting ### Empty Results on First Page **Problem:** `data` array is empty but no error **Possible Causes:** 1. Filter criteria matches no records 2. All matching records are deleted (see [Soft Deletes](/getting-started/soft-deletes)) **Solutions:** 1. Verify filter criteria 2. Check if records exist using less restrictive filters 3. Include deleted records if needed (see [Filtering](/getting-started/filtering)) ### Invalid Cursor Error **Problem:** API returns error: "Invalid cursor" **Possible Causes:** 1. Cursor from a different query (different filter/sort) 2. Cursor expired (very old cursor) 3. Malformed cursor string **Solutions:** 1. Always use cursor from the same filter query 2. Don't persist cursors - start fresh queries with `cursor: null` 3. Ensure cursor is passed as-is without modification ### Duplicate Results **Problem:** Same record appears in multiple pages **Possible Causes:** 1. Records were created/modified during pagination 2. Using cursors from different queries **Solutions:** 1. Accept eventual consistency for real-time data 2. Deduplicate results by ID on client side 3. For consistent snapshots, consider using timestamps in filters ### Page Size Ignored **Problem:** Receiving different number of results than requested **Possible Causes:** 1. Last page has fewer results 2. `pageSize` exceeds maximum (250) 3. Some records filtered out after query **Solutions:** 1. Check `pageInfo.hasNextPage` instead of counting results 2. Ensure `pageSize` is between 1-250 3. This behavior is normal and expected ## Next Steps - [Filtering](/getting-started/filtering) - Learn how to filter results before pagination - [Soft Deletes](/getting-started/soft-deletes) - Understand how deleted records affect pagination - [API Reference](/apis/openapi) - See pagination on specific endpoints