Skip to content
binder
Cart 0
Developers

Sync listings and stock, fetch orders, mark them shipped and get webhooks: one REST API for every game on Binder.

Getting started

The Seller API is a JSON REST API at https://www.binder.market/api/v1. It works the same for every game on Binder (Pokémon, One Piece and the ones that follow): products are identified by their catalogue productId, and you can keep your own sku on every listing. Money is always an integer in euro cents (1999 = €19.99), timestamps are ISO 8601 in UTC, and ids are strings.

Create a key under Account → API, then try it out. The snippets below expect it in $KH_KEY.

export KH_KEY="kh_live_ab12cd34_…"
curl https://www.binder.market/api/v1/listings?limit=1 -H "Authorization: Bearer $KH_KEY"

Authentication

Every request carries the key as a Bearer token: Authorization: Bearer kh_live_…. A key belongs to one store and can only see and change that store’s data. Keys look like kh_live_<8 characters>_<secret>; the first part is shown in your account so you can tell keys apart. We store only a SHA-256 fingerprint, so a lost key can’t be shown again: revoke it and create a new one. Never put a key in a browser or app that customers can download.

Scopes

Each key has a list of scopes. A request without the scope it needs fails with 403.

listings:readGET /listings, GET /listings/{id}
listings:writePOST, PATCH, DELETE /listings, PATCH, DELETE /listings/{id}, PUT /stock
orders:readGET /orders, GET /orders/{id}
orders:writePOST /orders/{id}/ship
webhooks:writeGET, POST /webhooks, DELETE /webhooks/{id}, POST /webhooks/{id}/test
any keyGET /products

Errors

Errors always have the same shape. code is stable and meant for your program; message is for people; field is set when one input is at fault. Bulk calls are all-or-nothing: if one item is invalid nothing is written, and the message starts with its index, e.g. items[3]: ….

HTTP/1.1 422 Unprocessable Entity
{
  "error": {
    "code": "validation_failed",
    "message": "items[3]: priceCents must be an integer between 1 and 100000000",
    "field": "priceCents"
  }
}
400invalid_requestMalformed JSON, a bad query parameter or cursor, too many items.
401unauthorizedMissing, malformed or revoked API key.
403insufficient_scopeThe key lacks the scope this endpoint needs.
404not_foundNo such listing, order or endpoint in your store (bulk calls: one unknown id or sku).
409idempotency_conflictIdempotency-Key reused with a different request, or the first one is still running.
422validation_failedA value is invalid; `field` names it and the message names the item index.
422invalid_stateThe order is not in a state that allows this (e.g. shipping an unpaid order).
429rate_limitedToo many requests; wait for Retry-After seconds.
503database_not_configuredThe marketplace backend is not available yet.

Idempotency

Send an Idempotency-Key header (any unique string up to 255 characters, e.g. a UUID) with POST, PUT, PATCH and DELETE requests. If the connection drops, retry with the same key and the same body: you get the original response again, with the header Idempotent-Replayed: true, and nothing is created twice. The same key with a different body returns 409 idempotency_conflict. Keys are remembered for 24 hours. Server errors (5xx) are not stored, so a retry runs the request again.

curl -X POST https://www.binder.market/api/v1/listings -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c1d3e-6a54-4d7e-9a43-1f0f6c3c2b10" \
  -d '{"items":[{"productId":"sv151-199","condition":"NM","language":"EN","priceCents":11900,"quantity":2}]}'

Pagination

Lists return { "data": [...], "nextCursor": "…" }. Pass limit (default 100, max 500) and, for the next page, cursor=<nextCursor> unchanged. When nextCursor is null you have everything. Cursors are keyset-based, so pages stay stable while rows are added: nothing is skipped or repeated. Listings come oldest first, orders newest first.

curl "https://www.binder.market/api/v1/listings?limit=500&cursor=WyIyMDI2LTEwLTE5IDA4OjEyOjAzLjUxMjM0KzAwIiwiTF9YYjJmIl0" -H "Authorization: Bearer $KH_KEY"

Rate limits

Each key may make 120 requests per minute (short bursts are fine). Responses carry X-RateLimit-Limit and X-RateLimit-Remaining; over the limit you get 429 with Retry-After. Use the bulk endpoints: one call can create, change or restock up to 500 listings.

Products

GET/productsany key

Look up catalogue products to map your own articles to a productId. q matches name (any language we know), set and number; game (pokemon, onepiece, …) and set (e.g. OP01) narrow it down. Give q or set. limit up to 100 (default 20).

Request
curl "https://www.binder.market/api/v1/products?q=charizard%20199&game=pokemon" -H "Authorization: Bearer $KH_KEY"
Response
{
  "data": [
    {
      "id": "sv151-199",
      "game": "pokemon",
      "set": "SV151",
      "kind": "single",
      "sealedKind": null,
      "name": "Charizard ex",
      "names": { "de": "Glurak-ex" },
      "number": "199/165",
      "rarity": "Special Illustration Rare",
      "imageUrl": "/cards/sv151-199.webp",
      "referencePriceCents": 11900
    }
  ]
}

Listings

A listing is one offer: a product in one condition and language at one price, with a quantity. condition is one of MT NM EX GD LP PL PO (leave it out for sealed product and graded cards); language one of EN DE JP FR IT ES PT KO ZH; graded is { "company": "PSA", "grade": 10 } (PSA, CGC, BGS, SGC, TAG, ACE) and then needs the slab's certNumber. A sku must be unique among your active listings. A listing with quantity 0 stays active and comes back when you restock.

GET/listingslistings:read

Filters: productId, sku, status (active, paused, deleted; deleted ones are left out unless asked for), updatedSince (ISO time; includes deletions, for incremental sync).

Request
curl "https://www.binder.market/api/v1/listings?status=active&updatedSince=2026-10-19T00:00:00Z" -H "Authorization: Bearer $KH_KEY"
Response
{ "data": [{
    "id": "L_Xb2fQ9mR1kTz0aVc",
    "productId": "sv151-199",
    "condition": "NM",
    "language": "EN",
    "priceCents": 11900,
    "quantity": 2,
    "reverseHolo": false,
    "firstEdition": false,
    "signed": false,
    "altered": false,
    "graded": null,
    "sku": "CHAR-151-NM-EN",
    "note": null,
    "status": "active",
    "source": "api",
    "version": 1,
    "createdAt": "2026-10-19T08:12:03.512Z",
    "updatedAt": "2026-10-19T08:12:03.512Z"
  }], "nextCursor": null }

POST/listingslistings:write

Creates up to 500 listings in one call, all or nothing.

Request
curl -X POST https://www.binder.market/api/v1/listings -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "items": [
      { "productId": "sv151-199", "condition": "NM", "language": "EN",
        "priceCents": 11900, "quantity": 2, "sku": "CHAR-151-NM-EN" },
      { "productId": "op01-120", "condition": "NM", "language": "JP",
        "priceCents": 4500, "quantity": 1, "sku": "OP01-120-JP" }
    ]
  }'
Response
HTTP/1.1 201 Created
{ "data": [{
    "id": "L_Xb2fQ9mR1kTz0aVc",
    "productId": "sv151-199",
    "condition": "NM",
    "language": "EN",
    "priceCents": 11900,
    "quantity": 2,
    "reverseHolo": false,
    "firstEdition": false,
    "signed": false,
    "altered": false,
    "graded": null,
    "sku": "CHAR-151-NM-EN",
    "note": null,
    "status": "active",
    "source": "api",
    "version": 1,
    "createdAt": "2026-10-19T08:12:03.512Z",
    "updatedAt": "2026-10-19T08:12:03.512Z"
  }, …] }

PATCH/listingslistings:write

Changes up to 500 listings, each found by id or else by sku. Fields: priceCents, quantity, status (active or paused), note, and sku (only when the item is identified by id). Every change bumps version.

Request
curl -X PATCH https://www.binder.market/api/v1/listings -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "items": [
        { "sku": "CHAR-151-NM-EN", "priceCents": 11500 },
        { "id": "L_Xb2fQ9mR1kTz0aVc", "status": "paused" }
      ] }'
Response
{ "data": [ { "id": "L_…", "sku": "CHAR-151-NM-EN", "priceCents": 11500, "version": 2, … }, … ] }

DELETE/listingslistings:write

Removes listings by ids or skus (up to 500). Past orders keep their copy of the listing.

Request
curl -X DELETE https://www.binder.market/api/v1/listings -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "skus": ["CHAR-151-NM-EN", "OP01-120-JP"] }'
Response
{ "deleted": 2, "ids": ["L_Xb2fQ9mR1kTz0aVc", "L_8fK2…"] }

GET · PATCH · DELETE/listings/{id}listings:read / listings:write

Read, change or remove one listing. PATCH takes the same fields as the bulk call.

Request
curl -X PATCH https://www.binder.market/api/v1/listings/L_Xb2fQ9mR1kTz0aVc -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "priceCents": 10900, "quantity": 3 }'
Response
{
  "id": "L_Xb2fQ9mR1kTz0aVc",
  "productId": "sv151-199",
  "condition": "NM",
  "language": "EN",
  "priceCents": 10900,
  "quantity": 3,
  "reverseHolo": false,
  "firstEdition": false,
  "signed": false,
  "altered": false,
  "graded": null,
  "sku": "CHAR-151-NM-EN",
  "note": null,
  "status": "active",
  "source": "api",
  "version": 2,
  "createdAt": "2026-10-19T08:12:03.512Z",
  "updatedAt": "2026-10-19T08:12:03.512Z"
}

Stock

PUT/stocklistings:write

Sets absolute quantities for up to 500 listings (by id or sku), all or nothing. This is the call for syncing stock from your own system, e.g. after a sale in your shop. Items sitting in a buyer’s cart are reserved separately and never oversold.

Request
curl -X PUT https://www.binder.market/api/v1/stock -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: stock-sync-2026-10-19T10:00" \
  -d '{ "items": [
        { "sku": "CHAR-151-NM-EN", "quantity": 0 },
        { "sku": "OP01-120-JP", "quantity": 4 }
      ] }'
Response
{
  "data": [
    { "id": "L_Xb2fQ9mR1kTz0aVc", "sku": "CHAR-151-NM-EN", "quantity": 0, "version": 3, "updatedAt": "…" },
    { "id": "L_8fK2…", "sku": "OP01-120-JP", "quantity": 4, "version": 2, "updatedAt": "…" }
  ]
}

Orders

One order is one parcel from your store. Buyers pay Binder; we hold the money until the buyer confirms receipt (or automatically after the confirmation period), then pay you out. payoutStatus is none, held, on_hold (a problem was reported), scheduled or paid_out. Status flow: pending_payment → paid → shipped → delivered → confirmed → paid_out (or cancelled / disputed).

GET/ordersorders:read

Newest first. Filters: status (comma-separated, e.g. paid,shipped), updatedSince. Items carry your sku and a snapshot of the listing at the time of sale.

Request
curl "https://www.binder.market/api/v1/orders?status=paid" -H "Authorization: Bearer $KH_KEY"
Response
{ "data": [{
    "id": "O_q8Zr2LkM0pXs",
    "number": "K-100123",
    "checkoutId": "CO_7hT1xQ",
    "storeId": "S_4kd9Lm",
    "status": "paid",
    "currency": "EUR",
    "subtotalCents": 11900,
    "shippingCents": 495,
    "totalCents": 12395,
    "shippingMethod": "tracked",
    "shipTo": { "name": "Mia Weber", "street": "Hauptstr. 5", "postcode": "10115", "city": "Berlin", "country": "DE" },
    "carrier": null,
    "trackingNumber": null,
    "items": [
      { "listingId": "L_Xb2fQ9mR1kTz0aVc", "productId": "sv151-199", "sku": "CHAR-151-NM-EN",
        "qty": 1, "unitPriceCents": 11900, "condition": "NM", "language": "EN", "reverseHolo": false,
        "graded": null, "cert": null }
    ],
    "payoutStatus": "held",
    "paidAt": "2026-10-19T09:01:44.000Z",
    "shippedAt": null,
    "deliveredAt": null,
    "confirmedAt": null,
    "paidOutAt": null,
    "cancelledAt": null,
    "createdAt": "2026-10-19T09:00:12.000Z",
    "updatedAt": "2026-10-19T09:01:44.000Z"
  }], "nextCursor": null }

GET/orders/{id}orders:read

One order with its items.

Request
curl https://www.binder.market/api/v1/orders/O_q8Zr2LkM0pXs -H "Authorization: Bearer $KH_KEY"
Response
{
  "id": "O_q8Zr2LkM0pXs",
  "number": "K-100123",
  "checkoutId": "CO_7hT1xQ",
  "storeId": "S_4kd9Lm",
  "status": "paid",
  "currency": "EUR",
  "subtotalCents": 11900,
  "shippingCents": 495,
  "totalCents": 12395,
  "shippingMethod": "tracked",
  "shipTo": { "name": "Mia Weber", "street": "Hauptstr. 5", "postcode": "10115", "city": "Berlin", "country": "DE" },
  "carrier": null,
  "trackingNumber": null,
  "items": [
    { "listingId": "L_Xb2fQ9mR1kTz0aVc", "productId": "sv151-199", "sku": "CHAR-151-NM-EN",
      "qty": 1, "unitPriceCents": 11900, "condition": "NM", "language": "EN", "reverseHolo": false,
      "graded": null, "cert": null }
  ],
  "payoutStatus": "held",
  "paidAt": "2026-10-19T09:01:44.000Z",
  "shippedAt": null,
  "deliveredAt": null,
  "confirmedAt": null,
  "paidOutAt": null,
  "cancelledAt": null,
  "createdAt": "2026-10-19T09:00:12.000Z",
  "updatedAt": "2026-10-19T09:01:44.000Z"
}

POST/orders/{id}/shiporders:write

Marks a paid order as shipped and sends the tracking number to the buyer. Any other status returns 422 invalid_state. Emits order.shipped.

Request
curl -X POST https://www.binder.market/api/v1/orders/O_q8Zr2LkM0pXs/ship -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "carrier": "DHL", "trackingNumber": "00340434161094042557" }'
Response
{ "id": "O_q8Zr2LkM0pXs", "status": "shipped", "carrier": "DHL",
  "trackingNumber": "00340434161094042557", "shippedAt": "2026-10-19T14:20:00.000Z", … }

Webhooks

Add endpoints under Account → API or with the API below. We send each event as a POST with a JSON body { "id", "type", "createdAt", "data" } and these headers: Binder-Event-Id, Binder-Event-Type and Binder-Signature: t=<unix time>,v1=<hex>. Answer with any 2xx within 10 seconds. Otherwise we retry after 1 min, 5 min, 30 min, 2 h and 12 h, then give up. Deliveries can arrive twice or out of order: use the event id to skip duplicates. URLs must use https.

order.createdA buyer checked out an order with your items (payment pending).
order.paidPayment succeeded. Ship now; the money is held until the buyer confirms.
order.shippedThe order was marked shipped (by you, in the app or via the API).
order.deliveredThe carrier reported delivery.
order.confirmedThe buyer confirmed receipt (or it was confirmed automatically). Payout is scheduled.
order.cancelledThe order was cancelled; reserved stock went back to the listings.
listing.sold_outA sale brought a listing's quantity to 0.
payout.sentWe sent a payout to your bank account.

Verify every delivery: compute HMAC-SHA256 with your endpoint’s whsec_… secret over <t>.<raw body> and compare it to v1; reject timestamps older than five minutes. Use the raw body exactly as received, before any JSON parsing.

Node.js
import crypto from "node:crypto";
import express from "express";

const app = express();
const secret = process.env.BINDER_WEBHOOK_SECRET; // whsec_…

function verify(header, rawBody, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(parts.v1 ?? "", "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/binder/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  if (!verify(req.get("Binder-Signature") ?? "", raw)) return res.sendStatus(400);
  const event = JSON.parse(raw);
  if (event.type === "order.paid") {
    // pick and pack event.data.order …
  }
  res.sendStatus(200);
});

GET · POST/webhookswebhooks:write

List your endpoints or add one (up to 10). The signing secret is returned once, on creation.

Request
curl -X POST https://www.binder.market/api/v1/webhooks -H "Authorization: Bearer $KH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://shop.example.com/binder/webhook",
        "events": ["order.paid", "order.cancelled"] }'
Response
HTTP/1.1 201 Created
{
  "id": "we_3nV0aK1qPz7m",
  "url": "https://shop.example.com/binder/webhook",
  "events": ["order.paid", "order.cancelled"],
  "active": true,
  "createdAt": "2026-10-19T08:00:00.000Z",
  "secret": "whsec_0Jk2…"
}

DELETE/webhooks/{id}webhooks:write

Removes an endpoint; pending deliveries to it are dropped.

Request
curl -X DELETE https://www.binder.market/api/v1/webhooks/we_3nV0aK1qPz7m -H "Authorization: Bearer $KH_KEY"
Response
{ "deleted": true, "id": "we_3nV0aK1qPz7m" }

POST/webhooks/{id}/testwebhooks:write

Sends a signed webhook.test event right away and reports what your server answered. Nothing is retried.

Request
curl -X POST https://www.binder.market/api/v1/webhooks/we_3nV0aK1qPz7m/test -H "Authorization: Bearer $KH_KEY"
Response
{ "delivered": true, "statusCode": 200, "error": null }