Pagination & filtering
List endpoints return one page at a time. You choose the page with offset and limit, and you have reached the end when a page comes back with fewer rows than you asked for. Lists do not tell you how many rows there are in total.
Paging#
limit is how many rows you want, offset how many to skip. The second page of 100 is offset=100&limit=100.
The response is the page under data and nothing else: no total, no page count, no link to the next page. Keep asking with a larger offset until a page is shorter than limit.
Each list has its own default page size, so always send limit. Page in a fixed order: when records change while you page, a row can move from one page to the next.
const limit = 100; const orders = []; for (let offset = 0; ; offset += limit) { const url = `https://api.morffeus.com/api/v2/orders?orderBy=id&offset=${offset}&limit=${limit}`; const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.MORFFEUS_TOKEN}` } }); const { data } = await res.json(); orders.push(...data); if (data.length < limit) break; }
The lists#
| Endpoint | Paging |
|---|---|
| GET /products | offset and limit, default 10. Also pageNo and perPage: pageNo=3&perPage=20 is offset=40&limit=20. The products are in data.products; see Your first request. |
| GET /orders | offset and limit, default 30, at most 10,000 per page. |
| GET /customers | offset and limit, default 30. |
| GET /warehouses | offset and limit, default 30. |
| GET /stocks | offset and limit, default 30. |
| GET /stock_movements | Not paged: you get every movement between dateFrom and dateUntil. Keep the range short. |
Sorting#
| Endpoint | How to sort |
|---|---|
| GET /orders | orderBy with one field and an optional asc or desc, for example orderBy=order_date desc. Fields: id, order_number, order_date, invoice_number, sales, net_sales, discount, vat, paid, debt, customer_name. A field alone sorts ascending. Anything else falls back to the default, newest first, without an error. Rows with the same value are ordered by id, newest first. |
| GET /products | orderBy with name, price (the lowest price of the product), inserted_at or sales_quantity, followed by desc in lower case to reverse it: orderBy=price desc. Separate several with a comma and no space. Default: by name; with a search, best matches first. |
| GET /customers | By first name. |
| GET /warehouses | By name. |
| GET /stocks | By product. |
| GET /stock_movements | In the order they were booked. |
Filtering#
Filters are query parameters in camelCase. Ids are our ids; the codes are yours.
| Endpoint | Filters |
|---|---|
| GET /products | search (product names), searchAll (names, product and variant codes and barcodes), code, barcode, slug, id, priceFrom, priceTo, onlyOnStock. One of code, barcode, slug or id on its own returns a single product. |
| GET /orders | search (order and invoice number, customer name, email and phone), customerId, appLocationId, dateTimeFrom, dateTimeTo. |
| GET /stocks | warehouseId, productId, productVariantId. See Stock. |
| GET /stock_movements | dateFrom and dateUntil, both required; warehouseId, productId. |
Dates#
Send dates as ISO 8601, a date alone or a date and a time: 2026-09-01 or 2026-09-01T14:30:00. Both ends are included. A date alone means midnight at the start of that day, so dateUntil=2026-09-30 ends as the 30th begins; send 2026-09-30T23:59:59 to include the whole day.
- Orders filter on the order date with
dateTimeFromanddateTimeTo. Both are optional. - Stock movements filter on the movement date with
dateFromanddateUntil. Both are required; without them the call answers400.
Related#
- Your first request: a list call from an empty terminal.
- Stock: the stock list and its filters in detail.
- Errors: what a list answers when a parameter is wrong.