API pagination and filtering
Read time: 5 minutes. Who it's for: Developers listing quotes or events.
What does a list response look like?
Every list endpoint returns the same envelope:
{
"object": "list",
"data": [ … ],
"has_more": true,
"next_cursor": "eyJ1IjoiMjAyNi0wOS0yOVQxNDoxMjowMC4wMDBaIiwiaSI6ImNsejhr…"
}
data- up tolimititems (default 20, maximum 50).has_more-truewhen there's another page.next_cursor- what to pass to get the next page;nullon the last page.
Paging through quotes - GET /v1/quotes
Pass next_cursor back as the cursor query parameter. The cursor is opaque: pass it back
unchanged, don't build or decode it, and keep the same filters and sort you used for the first
page. A cursor that can't be read returns 400 invalid_request on the cursor field.
curl -G https://api.quotway.com/v1/quotes \
-H "Authorization: Bearer $QUOTWAY_API_KEY" \
--data-urlencode "limit=50" \
--data-urlencode "cursor=eyJ1IjoiMjAyNi0wOS0yOVQxNDoxMjowMC4wMDBaIiwiaSI6ImNsejhr…"
Sort
sort |
Order |
|---|---|
-updated_at (default) |
Most recently updated first |
updated_at |
Least recently updated first - use this for sync |
Ties on updated_at are broken by quote id, so paging never skips or repeats a quote that
doesn't change while you page.
Filters
| Parameter | Example | Notes |
|---|---|---|
status |
proposal_sent,countered |
Comma-separated status values. |
mode |
b2b |
wholesale or b2b. |
updated_at[gte] |
2026-09-01T00:00:00Z |
ISO-8601. |
updated_at[lte] |
2026-09-30T23:59:59Z |
ISO-8601. |
created_at[gte] |
2026-09-01T00:00:00Z |
ISO-8601. |
assigned_staff_id |
clx… |
The assigned_staff.id of a staff member. |
po_number |
PO-88213 |
Exact match. |
customer_email |
buyer@example.com |
Case-insensitive exact match. Requires read_customer_data. |
Unknown query parameters are ignored. Invalid values return 400 invalid_request with one
errors entry per bad parameter. Brackets in parameter names must be URL-encoded (%5B, %5D) -
curl -G --data-urlencode does it for you.
Deleted quotes are never returned.
Paging through events - GET /v1/events and GET /v1/quotes/{id}/events
Events are always oldest first, and paginate by event id: pass next_cursor (the last event's
id) as starting_after.
curl -G https://api.quotway.com/v1/events \
-H "Authorization: Bearer $QUOTWAY_API_KEY" \
--data-urlencode "type=quote.accepted,quote.partially_accepted" \
--data-urlencode "starting_after=clz9m1a2b0004qw7hx8y9z0ab"
Event filters: type (comma-separated event types), created[gte], created[lte], limit.
Events are kept for 30 days; a starting_after id older than that returns 400 invalid_request.
See Events.
Recipe: incremental quote sync
To keep an external system in step with QuotWay:
- Store a watermark - the
updated_atof the last quote you processed (start with a date far enough back to cover what you need). - Request
GET /v1/quotes?sort=updated_at&updated_at[gte]=<watermark>&limit=50. - Upsert each quote by
id, then follownext_cursoruntilhas_moreisfalse. - Save the largest
updated_atyou saw as the new watermark. - Repeat on a schedule - or, better, when a webhook arrives.
Because the filter is gte, the quote at the watermark is fetched again next run. That's
deliberate: upserting by id makes it harmless, and it means a quote updated in the same
millisecond is never missed.
Webhook endpoint lists
GET /v1/webhooks returns every endpoint in one page (a store can have at most 20), so has_more
is always false and next_cursor is always null.
Related
Still need a hand? The team is happy to help.