curl --request GET \
--url https://{host}/v1/watches \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/v1/watches', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{host}/v1/watches",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}import requests
url = "https://{host}/v1/watches"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": [
{
"type": "watches",
"id": "91827",
"attributes": {
"reference": "116610LN",
"name": "Rolex Submariner Date",
"description": "Full set, bought new in 2019 and worn sparingly.",
"translatedDescription": "Full set, bought new in 2019 and worn sparingly.",
"price": 1295000,
"shippingCosts": 0,
"priceOnRequest": false,
"applicationFee": null,
"box": true,
"warranty": true,
"papers": true,
"status": "available",
"reviewNote": null,
"reserved": false,
"feedManaged": false,
"minimumProposal": 1165500,
"agreedPrice": null,
"favorited": false,
"viewerOrder": null,
"webUrl": "https://wristler.eu/watches/91827-rolex-submariner-date-116610ln",
"purchasable": true,
"availabilityOnRequest": false,
"referencedWatchUuid": "3f7c1e02-8a11-4a7b-9f0e-2c6d5b4a1e93",
"specifications": {
"CONDITION": "VERY_GOOD",
"CASE_MATERIAL": "STEEL"
},
"specificationLabels": {
"CONDITION": {
"label": "Condition",
"value": "Very good"
},
"CASE_MATERIAL": {
"label": "Case material",
"value": "Steel"
}
},
"createdAt": "2026-08-04T08:21:00.000000Z",
"updatedAt": "2026-09-19T12:02:11.000000Z"
}
}
],
"meta": {
"total": 184,
"pages": 8,
"facets": {
"brand": {
"name": "Brands",
"group": "Brand",
"options": [
{
"value": "Rolex",
"label": "Rolex",
"count": 184,
"active": true
},
{
"value": "Omega",
"label": "Omega",
"count": 96,
"active": false
}
]
},
"price": {
"name": "Price",
"range": {
"min": 150000,
"max": 2500000
}
}
},
"page": {
"currentPage": 1,
"from": 1,
"lastPage": 8,
"perPage": 25,
"to": 25,
"total": 184
}
},
"links": {
"first": "https://api.wristler.eu/v1/watches?page%5Bnumber%5D=1",
"next": "https://api.wristler.eu/v1/watches?page%5Bnumber%5D=2"
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}Search catalogue
Scope: catalogue:read
What is for sale, from every seller. A seller’s own stock, including what is
not for sale, is at /v1/sellers/{sellerId}/watches.
filter[q] searches rather than filters. The words are read first — a
brand, a model, a case size, a price ceiling, a sort — and the index ranks what
is left, exactly as on the website. The answer then carries meta.facets, each
option counted against the result set, and every facet name can be sent back as
a filter of its own. Searching pages by page[number] and page[size]:
relevance order has no stable cursor.
A facet alone searches too. filter[brand][]=Rolex with no words is the
same question as searching for one, and it comes back with the same counts —
which is what a filter panel is drawn from.
meta.total is a count rather than an estimate: the index is asked for a
numbered page, which makes it count the whole matching set. meta.pages comes
with it, and meta.capped: true appears in the one case the count stops at the
index’s own ceiling. meta.facets comes with the first page only: it describes
the whole result set, so it is the same on every page. If the index is
unreachable the answer is 503 search_unavailable, never an empty page.
Without filter[q], a facet filter or filter[seller] this is the plain list:
everything for sale, cursor-paged with page[after] and page[limit], with no
facets.
curl --request GET \
--url https://{host}/v1/watches \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/v1/watches', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{host}/v1/watches",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}import requests
url = "https://{host}/v1/watches"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": [
{
"type": "watches",
"id": "91827",
"attributes": {
"reference": "116610LN",
"name": "Rolex Submariner Date",
"description": "Full set, bought new in 2019 and worn sparingly.",
"translatedDescription": "Full set, bought new in 2019 and worn sparingly.",
"price": 1295000,
"shippingCosts": 0,
"priceOnRequest": false,
"applicationFee": null,
"box": true,
"warranty": true,
"papers": true,
"status": "available",
"reviewNote": null,
"reserved": false,
"feedManaged": false,
"minimumProposal": 1165500,
"agreedPrice": null,
"favorited": false,
"viewerOrder": null,
"webUrl": "https://wristler.eu/watches/91827-rolex-submariner-date-116610ln",
"purchasable": true,
"availabilityOnRequest": false,
"referencedWatchUuid": "3f7c1e02-8a11-4a7b-9f0e-2c6d5b4a1e93",
"specifications": {
"CONDITION": "VERY_GOOD",
"CASE_MATERIAL": "STEEL"
},
"specificationLabels": {
"CONDITION": {
"label": "Condition",
"value": "Very good"
},
"CASE_MATERIAL": {
"label": "Case material",
"value": "Steel"
}
},
"createdAt": "2026-08-04T08:21:00.000000Z",
"updatedAt": "2026-09-19T12:02:11.000000Z"
}
}
],
"meta": {
"total": 184,
"pages": 8,
"facets": {
"brand": {
"name": "Brands",
"group": "Brand",
"options": [
{
"value": "Rolex",
"label": "Rolex",
"count": 184,
"active": true
},
{
"value": "Omega",
"label": "Omega",
"count": 96,
"active": false
}
]
},
"price": {
"name": "Price",
"range": {
"min": 150000,
"max": 2500000
}
}
},
"page": {
"currentPage": 1,
"from": 1,
"lastPage": 8,
"perPage": 25,
"to": 25,
"total": 184
}
},
"links": {
"first": "https://api.wristler.eu/v1/watches?page%5Bnumber%5D=1",
"next": "https://api.wristler.eu/v1/watches?page%5Bnumber%5D=2"
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}Authorizations
Headers
The language to answer in: specificationLabels, translatedDescription, facet
and label names, the content pages, and every other translated word in the
answer. Read on every request, whatever the endpoint. One we do not have falls
back to English rather than failing.
en, nl, de, es, fr, it, pt, fi, pl Query Parameters
Related resources to return alongside. Combine them with commas —
include=images,watchModel. Anything else is refused with 400 rather than
ignored.
favorite is the viewer's own favorite of this listing, or null. Ask for
it when you draw a heart that has to be able to turn itself off: removing a
favorite needs its id, and without this a client has to fetch somebody's whole
list to find one. favorited is the cheaper read when the heart is all you are
drawing.
seller, watchModel, images, cover, favorite Leave it off for relevance, which is also where the words themselves are heard:
"goedkoopste rolex" is a price sort with a brand filter. A sort you send wins
over one the words implied.
year is a specification rather than a column, so only the index can sort by
it: it works on a search (filter[q], or a facet filter) and nowhere else. On
the plain catalogue list and on a seller's own inventory it is refused with
400 search_only_sort rather than ordering "1998" against "2010" as text. A
search sorts by the first field only. updatedAt sorts the plain list; a search
does not know it and keeps its own order.
price, -price, year, -year, createdAt, -createdAt, updatedAt, -updatedAt Send it empty to draw a filter panel. filter[q]= with nothing after it
is a search with no words: the whole catalogue, ranked, with
meta.facets and their counts. Leaving the parameter off entirely, with no
facet filter or filter[seller] either, gets the plain cursor-paged list
instead, which carries no facets.
Words somebody typed. Parsed for a brand, a model, a size, a price ceiling and a sort before the rest is searched.
A facet from a previous answer, sent back. Every key under meta.facets
works the same way — filter[attributes.dial][],
filter[attributes.case_material][], and so on — and
filter[price][min]/[max] take cents. A facet filter on its own makes the
request a search, with meta.facets, as filter[q] does.
Response
A page of the catalogue.