Learn the slugs before you filter
Guessing a slug is risky: on search an unknown value makes the request fail rather than being skipped, and a style missing from the category is refused. Send the scope you intend to search, for example `body.category` set to `furniture/seating/lounge-chairs`, to `post-1stdibs-filters` first. It returns `filters` for that scope, and a filter's `name` matches the search input it feeds where one exists, so you can copy a `value` straight into `body.style` or `body.period` on `post-1stdibs-search` with the same category.
Walking down the category tree
Start broad with `body.category` set to a top level such as `furniture`. `post-1stdibs-filters` returns the child categories of the requested level in `categories`, with counts, so you can append one child at a time until the path reaches the depth you need; paths take one to four levels. Keep using `body.category` once you have a path: `body.vertical` is ignored whenever a category is also present.
From a result page to full records
Run `post-1stdibs-search` with your filters and a `body.max_results` value, then collect `item_id` from the returned rows. `post-1stdibs-detail` takes as many as 10 of those ids at once in `body.item`, separated by commas, so a page of 50 rows needs 5 detail calls. Compare `requested` with `found` and read `missing` for ids that were not found. Check `price_mismatch` before using an item's price: when it is set, the listed price and the schema.org offer disagree.
Studying a single dealer
Search rows carry a `seller` block, but `order_count` and `is_distinguished` are null there. To read those figures, pass the dealer's storefront slug, for example `morentz`, as `body.seller` to `post-1stdibs-seller`; it returns them in its `seller` block along with `ships_from_country`. `newest` in `body.sort` puts the most recently listed first, and the response includes `has_more`, which tells you whether to raise `body.page`.
Pitfalls specific to this data
A search or filters body without `body.query` or `body.category` passes input validation but does not meet the requirement for one of them; always send one. Paging stops at offset 6000, so for large categories split the work with `body.price_min` and `body.price_max` bands or extra slugs instead of raising `body.page`; both price bounds are included, so bands that share an endpoint overlap and should not both be counted if you plan to combine their rows, and each band needs a returned `total_available` small enough to fit inside the window. A row whose dealer withholds the figure has an empty `price` and a price status of upon request, so do not treat an empty price as zero in averages. `body.origin` filters on where a piece was made and `body.location` on where the dealer ships from. Send only the field names listed on the reference page: `colour`, `limit` and `q` are not accepted.
[
{
"body": {
"category": "furniture/seating/lounge-chairs"
}
},
{
"body": {
"category": "furniture/seating/lounge-chairs",
"style": "mid-century-modern",
"period": "20th-century",
"sort": "price-low",
"max_results": 50
}
},
{
"body": {
"item": "f_52078502,a_18430382"
}
}
]Server-side request template
This server-side template shows authentication and request identity using the first operation of this API. Choose the operation for your use case and check its current availability and price on the API page. After a timeout, resend the unchanged request with the same Idempotency-Key.
The gateway request is always POST.
Use a JSON object with the documented path, query, headers and body locations; omit unused locations. input.headers is schema-validated data, not browser request headers. Do not put credentials here. Keep body arrays, scalar values and null as documented; omission is different from null.
# Server-side only. Set ATLAS_API_KEY in your secret manager.
# Set ATLAS_API_ORIGIN to your configured deployment origin.
# Every gateway operation is called with POST.
# Only operations shown as enabled on the API page can be called.
# Generate once per intent; retain the same value on status checks.
REQUEST_ID="$(uuidgen)"
curl --request POST 'https://apigate.vercel.app/api/v1/firstdibs/post-1stdibs-detail' \
--header "Content-Type: application/json" \
--header "x-api-key: $ATLAS_API_KEY" \
--header "Idempotency-Key: $REQUEST_ID" \
--data '{"body":{"item":"f_52078502"}}'Integration checklist
- The API key is stored in a server-side secret manager and never sent to a browser.
- Each request carries its own Idempotency-Key, kept until the request has a final answer.
- The integration reads ok and error.code and handles each error code as the error table describes.
- An uncertain POST is inspected using its original identity, never blindly repeated with a new one.
- The operation’s current availability and price have been checked on its API page.