API voor verkopers
Verkoop je LEGO via meerdere kanalen? Met de BrickVillage-API synchroniseer je advertenties, voorraad en bestellingen automatisch met je eigen webshop, zodat je nooit iets dubbel verkoopt.
- • API-toegang is op aanvraag voor zakelijke verkopers. Mail naar help@brickvillage.eu.
- • Kopers betalen bovenop je prijs kopersbescherming; jij ontvangt de volledige prijs.
- • De technische referentie hieronder is in het Engels.
Authentication
API access is granted on request (help@brickvillage.eu). Once enabled, create keys under My account → API. A key looks like bv_live_… and is shown only once. Send it as a bearer token over HTTPS:
curl https://brickvillage.eu/api/v1/listings \
-H "Authorization: Bearer bv_live_xxxxxxxx"All requests and responses are JSON (UTF-8). Amounts are integers in euro cents.
Rate limits & errors
120 requests per minute per key. Exceeding it returns 429 with a Retry-After header (seconds).
Errors always use this shape:
{ "error": { "code": "validation_failed", "message": "One or more fields are invalid",
"fields": { "price_cents": "Number must be greater than or equal to 50" } } }| 400 | bad_request | Malformed JSON or query parameter |
| 401 | unauthorized | Missing, invalid or revoked key |
| 403 | forbidden | API access disabled or account closed |
| 404 | not_found | Resource doesn't exist or isn't yours |
| 409 | conflict | Duplicate external_ref, image limit reached, sold item |
| 422 | validation_failed | Field validation failed (see fields) |
| 429 | rate_limited | Too many requests |
Listings
Use your own SKU as external_ref and upsert by reference — the recommended path for sync systems.
PUT/listings/by-ref/{external_ref}
curl -X PUT https://brickvillage.eu/api/v1/listings/by-ref/SKU-10297 \
-H "Authorization: Bearer $BV_KEY" -H "Content-Type: application/json" \
-d '{ "title": "LEGO Icons 10297 Boutique Hotel", "price_cents": 18999,
"quantity": 2, "condition": "new", "category": "setjes",
"set_number": "10297", "images": ["https://cdn.example.com/10297.jpg"] }'{ "id": 1234, "slug": "lego-icons-10297-boutique-hotel",
"url": "https://brickvillage.eu/product/1234/lego-icons-10297-boutique-hotel",
"status": "active", "quantity": 2, "price_cents": 18999,
"images": ["https://…/product-images/…jpg"], "external_ref": "SKU-10297",
"source": "api", "created_at": "…", "updated_at": "…" }Fields: title (3–120), description (≤4000), price_cents (50–10,000,000), quantity (0–999), condition (new, good, used_visible, incomplete, parts), category (populair, onderdelen, setjes, minifigures, fantasie, films), set_number, piece_count, year, theme, age_recommendation, lego_category, weight_grams, color, external_ref, images (≤12 https URLs). title, price_cents and category are required on create.
GET/listings?status=active|sold|offline&external_ref=&limit=50&cursor=
next_cursor back as cursor.POST/listings
GET · PATCH · DELETE/listings/{id}
GET · PATCH · DELETE/listings/by-ref/{external_ref}
curl -X PATCH https://brickvillage.eu/api/v1/listings/by-ref/SKU-10297 \
-H "Authorization: Bearer $BV_KEY" -H "Content-Type: application/json" \
-d '{ "price_cents": 17499, "quantity": 1 }'API listings don't expire and don't trigger listing e-mails.
Stock logic
quantity: 0takes the listing offline (statusoffline, not sold).quantity > 0on an offline API listing brings it back online.- When a buyer pays, stock is deducted atomically. If your stock update lowered the quantity during checkout, the payment is refunded automatically — nothing is ever sold twice.
- When the last item sells the listing becomes
soldand you receivelisting.sold_out.
Images
JPEG, PNG or WebP, max 10 MB, max 12 per listing. URLs must be public https://; internal/private addresses are rejected.
POST/listings/{id}/images
file, or JSON { "url": "https://…" }.curl -X POST https://brickvillage.eu/api/v1/listings/1234/images -H "Authorization: Bearer $BV_KEY" -F "file=@photo.jpg"DELETE/listings/{id}/images/{index}
PUT/listings/{id}/images/order
{ "order": [2, 0, 1] } — a permutation of current indexes.Orders
GET/orders?since=2026-09-01T00:00:00Z&status=paid|shipped|delivered|refunded&limit=&cursor=
GET/orders/{id}
{ "id": "0b6c…", "status": "paid", "currency": "EUR",
"amounts": { "subtotal_cents": 18999, "shipping_cents": 695,
"buyer_protection_cents": 1075, "total_cents": 20769 },
"items": [ { "listing_id": 1234, "external_ref": "SKU-10297",
"title": "LEGO Icons 10297", "qty": 1, "price_cents": 18999 } ],
"shipping": { "method": "postnl:standard", "carrier": "postnl", "kind": "home",
"address": { … }, "service_point": null,
"tracking_number": null, "tracking_url": null },
"paid_at": "2026-09-28T10:00:00Z" }Buyer protection is paid by the buyer on top of your price.
Webhooks
Set a webhook URL and secret under My account → API. Events: order.paid, order.refunded, listing.stock_changed, listing.sold_out (and test).
POST https://your-shop.example/webhooks/brickvillage
X-BrickVillage-Signature: t=1790000000,v1=5f2c…
{ "id": "evt uuid", "type": "listing.stock_changed", "created_at": "…",
"data": { "id": 1234, "external_ref": "SKU-10297", "quantity": 0 } }Respond with any 2xx within 10 seconds. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h. Use the event id to de-duplicate.
Verify the signature: HMAC-SHA256 of t + "." + rawBody with your secret.
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const sig = parts.v1 ?? "";
return fresh && sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}OpenAPI
Machine-readable spec (OpenAPI 3.1): /api/v1/openapi.json
