A read-only JSON feed of the VESTRA wholesale catalogue, for partners who want the range inside their own shop or ERP rather than in a spreadsheet. Brands, articles, size runs, colours, minimum order quantities, tiered wholesale prices in EUR, photograph URLs and a link back to each product page.
Two things this feed does not carry. Both are stated here rather than discovered later, because a missing field found after you have written an importer costs more than one found before.
No live stock. Per-unit inventory is not tracked, so every product returns
"stock": {"tracked": false, "quantity": null}. Do not resell against a
quantity from this feed — confirm availability before you promise a delivery date.
No EAN / GTIN. Barcode data is not held for this catalogue, so there is no barcode field at all. If your marketplace requires an EAN to list, this feed alone will not satisfy it.
Keys are issued per partner to verified trade accounts, because the feed carries
wholesale pricing. Write to support@vestrasales.com
from the address on your account. A key looks like vsk_…. We store only a
one-way hash of it, so it appears in full exactly once — at the moment it is issued.
Nobody here can read it back to you afterwards; if it goes missing we revoke it and
issue another.
Send it on every request:
curl -H "Authorization: Bearer vsk_your_key_here" \
"https://vestrasales.com/api/catalog?a=whoami"
A missing or revoked key returns 401 with
{"ok": false, "error": "unauthorized"}. Revocation takes effect
immediately.
The catalogue, paginated. Parameters, all optional:
| Parameter | Meaning |
|---|---|
| page | 1-based page number. Default 1. |
| per | Items per page, 1–200. Default 100. |
| brand | Exact brand name, case-insensitive. See a=brands. |
| cat | Exact category name, case-insensitive. |
| since | Any date your language can format, e.g. 2026-08-01. Returns articles added on or after it — use it for incremental syncs instead of pulling the whole catalogue each night. |
The response carries next_page so you do not have to compute it; it is
null on the last page. Walking until next_page is null is
the whole of correct pagination here.
{
"ok": true,
"generated_at": "2026-08-27T12:40:11+00:00",
"page": 1, "per_page": 100, "pages": 4, "total": 344,
"next_page": 2,
"items": [ { … } ]
}
One article, same shape as an item above, under item.
Unknown id returns 404.
Every brand in the catalogue with a product count — useful for building your own
filter, and for checking a brand name before passing it to brand=.
Confirms the key works and reports which partner it belongs to, the field list, and the same “not available” notes as above. Start here when wiring up.
| Field | Type | Notes |
|---|---|---|
| id | string | Stable VESTRA reference. Use it as your foreign key. |
| sku | string | Supplier article number. May be empty on older listings. |
| brand | string | |
| name | string | |
| category | string | |
| description | string | Plain text. |
| unit | string | Usually pc; some lines are sold in packs or boxes. |
| moq | integer | Minimum order quantity in unit. |
| sizes | string | The size run as written on the listing, e.g. S–XXL or 44–54. Free text, not a list — it also carries pack rules where a line is sold in size runs. |
| colours | string[] | May be empty. |
| currency | string | Always EUR. See below. |
| pricing | string | fixed, sale, or on_request. On on_request lines the tiers are indicative and the real number comes from an offer. |
| price_tiers | object[] | [{"min_qty":20,"price":34.00}, …], ascending. The price that applies is the one for the highest min_qty your quantity reaches. |
| price_from | number|null | Convenience: the first tier's price. |
| list_price | number|null | Pre-discount price where a line is on sale. |
| rrp | number|null | Brand recommended retail, where known. |
| origin | string | Provenance as declared by the seller, e.g. EEA stock · proof on request. |
| seller | string | via VESTRA where the seller has chosen not to be named publicly. |
| images | string[] | Absolute URLs. First image is the primary. |
| url | string | The product page — trade prices there still require a signed-in trade account. |
| added_at | string | ISO 8601, or empty on catalogue lines that predate the field. |
| stock | object | {"tracked": false, "quantity": null, "note": "…"}. See the note at the top. |
Prices are always EUR here, with no conversion parameter. The website converts the shelf price for visitors outside the euro area as a reading convenience, but orders are contracted and invoiced in EUR — so a feed that quoted anything else would hand you a number your invoice will not match. Convert at your own rate, on your own schedule, in your own shop.
There is no hard rate limit, and no charge for access. Responses carry
Cache-Control: private, max-age=60: the catalogue does not change by the
second, and a nightly full pull plus since= during the day is both
gentler and more accurate than polling.
The catalogue API above is read-only. Placing orders one piece at a time is a separate
endpoint, /api/dropship, with its own key. It is open to verified trade
partners buying for their own customers: you complete checkout and enter your
customer's delivery address, and no contract of sale arises between VESTRA and that
end customer. The commercial side of it — pricing rule, exclusions, availability,
duties and photograph rights — is set out on the
dropshipping page.
Every article available for single-piece purchase, with its price. Ralph Lauren, Lacoste and boxershorts are excluded from dropshipping — treat this feed as the authority rather than filtering the catalogue yourself.
Price, the three shipping zones, and stock_tracked — which is
false for catalogue articles, for the same reason the catalogue feed
reports no stock. Also returns colours and sizes: the values
the listing knows, for you to offer your customer. Either may be empty, which means
the listing does not state them — not that none exist. shipping is keyed by
zone and carries label, fee and
transit_working_days for each.
{ "id": "dsq-101211", "colour": "Black", "size": "M", "qty": 1,
"country": "JP",
"reference": "your-order-id",
"customer_email": "…", "customer_name": "…" }
Returns { ok, ref, checkout_url }. Open checkout_url to pay.
Your own reference is echoed back and shown on the order.
| Field | Notes |
|---|---|
| zone / country | zone is EU or a destination ISO-2 code from the shipping table; or pass country as an ISO-2 code and we map it. Anything else falls back to EU. |
| colour, size | What your customer ordered. Send one of the values a=stock lists for the article; where it lists none, send free text. |
The dropship price is the wholesale price of the smallest quantity tier plus 20%. Shipping is charged once per order by zone:
| Zone | Rate | Delivers to | Transit |
|---|---|---|---|
| EU | €16.00 | the 27 EU member states | 5–7 working days |
| GB | €30.00 | United Kingdom | 5–10 working days |
| US | €30.00 | United States | 5–10 working days |
| JP | €30.00 | Japan | 7–14 working days |
| SG | €30.00 | Singapore | 7–14 working days |
| AE | €30.00 | United Arab Emirates | 5–8 working days |
| SA | €30.00 | Saudi Arabia | 5–8 working days |
| QA | €30.00 | Qatar | 5–8 working days |
| AU | €35.00 | Australia | 7–14 working days |
| CA | €35.00 | Canada | 5–10 working days |
| KR | €35.00 | South Korea | 7–14 working days |
Transit is working days from dispatch, not calendar days, and does not include the time customs holds a consignment at destination.
Outside the EU the zone code is the destination's ISO-2 country code, so
passing country is enough and zone is the same string.
A country not in this table falls back to EU, whose session will then refuse
that address rather than ship it at the wrong rate.
The zone is fixed before the payment session opens, and the session then accepts only that zone's countries — so the rate charged and the address entered cannot diverge.
Duties and import taxes are not included in the price or the shipping rate. They are payable on delivery and are yours to settle or to pass to your customer. Goods of EU preferential origin may attract zero customs duty entering Japan under the EU–Japan Economic Partnership Agreement when a statement on origin accompanies the consignment — that is the tariff only, and does not cover consumption tax or the carrier's clearance fee.
Availability is confirmed after the order, because per-unit stock is not tracked. If an article cannot be supplied, the order is refunded in full.
CSV and XLSX exports of the same catalogue are available to signed-in trade accounts from the price list — the XLSX carries the photographs embedded next to each row. There is no XML feed and no FTP drop.