Overview

The Luminous External V1 API uses two different pagination styles depending on the endpoint you are calling:

  1. Offset (page-number) pagination — used by standard list endpoints such as sales orders, products, purchase orders, companies, etc.
  2. Cursor (keyset) pagination — used by dedicated bulk-export endpoints designed for large, consistent snapshots.

Understanding which style applies to the endpoint you are using will let you reliably walk through every record in a large data set.

Path prefix varies by endpoint. Most list endpoints (sales orders, products) live under /external/v1/. Newer endpoints, including the bulk exports, invoices and inbound shipments, live under /external/api/v1/. Check each endpoint's reference page for its exact path.


1. Offset Pagination (standard list endpoints)

Most GET list endpoints (for example, /external/v1/sales-orders, /external/v1/products, /external/v1/purchase-orders) use standard page-number pagination.

Request parameters

ParameterDefaultMaximumDescription
page1—The page number to retrieve. Starts at 1.
per_page10100How many records to return per page.

Example request (page 2, 50 records per page):

GET /external/v1/sales-orders?page=2&per_page=50

Response structure

Every offset-paginated response wraps the records in a data array and includes two helper objects:

{
  "data": [ ...records... ],
  "links": {
    "first": "https://yourstore.com/external/v1/sales-orders?page=1",
    "last":  "https://yourstore.com/external/v1/sales-orders?page=10",
    "prev":  "https://yourstore.com/external/v1/sales-orders?page=1",
    "next":  "https://yourstore.com/external/v1/sales-orders?page=3"
  },
  "meta": {
    "current_page": 2,
    "from": 51,
    "last_page": 10,
    "per_page": 50,
    "to": 100,
    "total": 500
  }
}

Key fields to use when paging:

  • meta.current_page — the page you just received.
  • meta.last_page — the final page; stop when current_page equals this value.
  • links.next — the pre-built URL for the next page (can be used directly).
  • meta.total — total number of matching records across all pages.

Tips for offset pagination

  • Combine with filters to narrow the result set before paging (for example, ?created_at[gte]=2024-01-01&per_page=100).
  • The default sort order is by updated_at descending. If records are being updated while you page, consider filtering by a stable date range so pages remain consistent.
  • Request up to 100 records per page to minimize the number of round trips.

2. Cursor Pagination (bulk export endpoints)

Bulk export endpoints are designed for complete, consistent snapshots of large catalogs or stock positions. They use an opaque cursor token instead of page numbers, which guarantees a stable walk even while new data is being written.

The two bulk export endpoints that use cursor pagination are:

EndpointPurpose
GET /external/api/v1/products/exportFull product catalog export
GET /external/api/v1/inventory/stocks/exportFull current stock snapshot

Products export — request parameters

ParameterRequiredDescription
sinceYes, on the first request onlyISO date/datetime. Sets the snapshot cutoff. Do not send this on follow-up pages.
cursorYes, on every request after the firstOpaque token from the previous response's next_cursor.
limitNoRecords per page. Default: 100. Maximum: 1000.

First page:

GET /external/api/v1/products/export?since=2024-01-01&limit=500

Subsequent pages (use the next_cursor from the previous response):

GET /external/api/v1/products/export?cursor=<opaque_token>&limit=500

Important: Do not send both since and cursor in the same request. The first response fixes a cutoff timestamp so the entire walk is a consistent snapshot. Sending both since and cursor returns a 422 error. The stocks export accepts only cursor and limit; any other parameter returns 422.

Stock export — request parameters

ParameterRequiredDescription
cursorNo, omit on the first requestOpaque token from the previous response's next_cursor.
limitNoRecords per page. Default: 100. Maximum: 1000.

First page:

GET /external/api/v1/inventory/stocks/export?limit=1000

Subsequent pages:

GET /external/api/v1/inventory/stocks/export?cursor=<opaque_token>&limit=1000

Cursor export response structure

{
  "data": [ ...records... ],
  "next_cursor": "<opaque_token>",
  "has_more": true,
  "meta": {
    "rows": 1000
  }
}

Key fields:

  • has_more — true means more pages exist; false means you have received all records. Stop when this is false.
  • next_cursor — pass this as the cursor parameter on your next request.
  • meta.rows — how many records are in this page's data array.

Choosing the right approach

ScenarioRecommended approach
Fetching filtered sales orders, POs, companies, etc.Offset pagination (page / per_page)
Syncing your entire product catalogProducts export cursor pagination (since then cursor)
Syncing current stock levels for all SKUsStock export cursor pagination (cursor)
Ad-hoc queries with filtersOffset pagination with filter parameters

Quick checklist

  • Set per_page=100 (the maximum) on offset endpoints to minimize round trips.
  • Stop looping when meta.current_page equals meta.last_page (offset) or has_more is false (cursor).
  • On the products export, only send since on the very first request; switch to cursor for all subsequent pages.
  • Treat next_cursor as an opaque string — do not parse or construct it manually.