v1

MBNF Stock API

Live, read-only access to the sneaker stock of MBNF SRL for our B2B partners. Plug it into your shop, ERP or spreadsheet and always see what we can ship, size by size.

BASE URLhttps://mbnf.supply/api/v1
FORMATJSON over HTTPS, read-only
UPDATEDEvery few minutes
Contents
  1. Introduction
  2. Quick start
  3. Sandbox
  4. Try it
  5. Authentication
  6. Endpoints
  7. Response fields
  8. Keeping your stock in sync
  9. Rate limits
  10. Errors
  11. Security
  12. Versioning
  13. Support

Quick start#

  1. Get your API key

    MBNF sends it to you personally. It looks like mbnf_AbCd1234_… — the first 8 characters after mbnf_ are the key ID (safe to quote to us), the rest is secret.

  2. Check that it works

    Call /v1/me: it returns your account name, your limits and when the stock was last updated.

    curl https://mbnf.supply/api/v1/me \
      -H "Authorization: Bearer $MBNF_API_KEY"
  3. Read the stock

    Call /v1/stock and walk through the pages. That's it — see Keeping your stock in sync for a complete, production-ready loop.

    curl "https://mbnf.supply/api/v1/stock?per_page=200&page=1" \
      -H "Authorization: Bearer $MBNF_API_KEY"

Sandbox#

Want to build before you get your key? Use the public sandbox key: it works on every endpoint and always answers with the same sample products — never our real stock. Responses carry "sandbox": true in meta and the header X-MBNF-Sandbox: true.

Sandbox key — public, sample data only
mbnf_Sandbox1_public-test-key-sample-data-only

It is limited to 30 requests per minute and 1,000 per day per IP address. When you switch to your own key nothing else changes: same URLs, same fields — only real data.

Prefer clicking to reading? The interactive API reference lists every endpoint with its parameters and responses; the sandbox key is already filled in — open an endpoint, press Test Request and see the answer.

Try it#

Paste your key — or press Use the sandbox key — and send a real request: you see exactly what your system will receive. The key stays in this page: it is not saved anywhere and disappears when you close the tab. Requests count against your limits like any other.

Authentication#

Send the key in the Authorization header of every request:

HTTP
Authorization: Bearer mbnf_AbCd1234_your-secret-part

X-API-Key: <key> works too. Keys in the URL (?api_key=, ?token= …) are rejected with 400, because URLs end up in logs and browser history.

A missing, wrong, revoked or expired key always gets the same 401 invalid_api_key — we never say which.

Endpoints#

All endpoints are GET (and HEAD), return JSON and live under https://mbnf.supply/api/v1.

GET

/v1/stock

The products we can offer you right now, paginated and ordered by brand and name. Only products with at least one size available are listed.

Query parameterDescription
pageinteger · default 1Page number, starting from 1.
per_pageinteger · 1–200 · default 100Products per page.
brandstringExact brand, case-insensitive — e.g. Nike, Asics.
skustringExact SKU, case-insensitive.
qstring · max 100Free text searched in SKU and name.
200 OK
{
  "data": [
    {
      "sku": "DD1391-100",
      "name": "Nike Dunk Low Retro White Black",
      "brand": "Nike",
      "image_url": "https://image.goat.com/…/DD1391-100.png",
      "price": 145,
      "currency": "EUR",
      "price_vat_included": false,
      "total_quantity": 7,
      "sizes": [
        { "size": "9",  "size_system": "US", "size_eu": "42.5", "quantity": 3 },
        { "size": "10", "size_system": "US", "size_eu": "44",   "quantity": 4 }
      ]
    }
  ],
  "meta": {
    "page": 1, "per_page": 100, "total": 48, "total_pages": 1,
    "stock_updated_at": "2026-10-14T09:12:00+00:00",
    "stale": false
  }
}
GET

/v1/stock/{sku}

One product, same shape as an item of data above, wrapped as {"data": {…}, "meta": {…}}. The SKU is case-insensitive. 404 not_found means it is not available for you right now.

GET

/v1/me

Your key and account. The cheapest way to test a key or check the freshness of the data.

200 OK
{
  "client": "Your Company B.V.",
  "key_id": "AbCd1234",
  "key_expires": null,
  "prices_included": true,
  "limits": { "requests_per_minute": 60, "requests_per_day": 10000 },
  "usage_today": { "requests": 42, "remaining": 9958 },
  "products": 48,
  "stock_updated_at": "2026-10-14T09:12:00+00:00",
  "stale": false
}
GET

/v1/openapi.json

The machine-readable OpenAPI 3.1 specification (no key needed). Import it in Postman, Insomnia or your code generator — or browse it as an interactive reference.

Response fields#

Product

FieldDescription
skustringManufacturer style code (e.g. DD1391-100). Unique in the feed: use it as your key.
namestringProduct name.
brandstring | nullBrand.
image_urlstring | nullHTTPS product image, when we have one.
priceinteger | nullPrice per pair, whole units of currency. Only present if prices are enabled for your account. null means we have no price for that product yet: ask us.
currencystringAlways EUR (present with price).
price_vat_includedbooleanWhether price includes Italian VAT (22%).
total_quantityintegerSum of sizes[].quantity.
sizesarrayAvailable sizes, smallest first. See below.

Size

FieldDescription
sizestring | nullThe size as printed on the box (US for most sneakers). null for one-size items.
size_systemstring | nullUS, US Men, US Women, US Youth, US Kids, EU or Apparel.
size_eustring | nullEU equivalent, when a conversion exists (e.g. 42.5, 36 2/3).
quantityinteger ≥ 1Pairs available in that size. It may be capped for your account (we never show more than we have).

Meta

FieldDescription
page, per_pageintegerEcho of your pagination.
total, total_pagesintegerProducts matching your filters, and pages at this per_page.
stock_updated_atISO 8601, UTCWhen this data left our warehouse system.
stalebooleantrue if that is more than 24 hours ago. The data is still served, but treat it with care.

Keeping your stock in sync#

  • Poll every 5–10 minutes. Our data does not change faster than that.
  • Send the ETag back. If page 1 answers 304 Not Modified, nothing changed anywhere: stop there. A 304 does not count against your daily quota.
  • Read every page, then replace. The feed is the full truth: a SKU or a size that is not in it is sold out — set it to 0 on your side.
  • Check meta.stale. If it is true, pause automatic selling.
  • Quantities are an offer, not a reservation. Every order is confirmed by MBNF.
sync.py — complete loop
import os, time, requests

BASE = "https://mbnf.supply/api/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['MBNF_API_KEY']}"
etag = None

def get(params, extra=None):
    while True:
        r = S.get(f"{BASE}/stock", params=params, headers=extra or {}, timeout=20)
        if r.status_code == 429:                       # too fast: wait as told
            time.sleep(int(r.headers.get("Retry-After", "5")))
            continue
        return r

def fetch_all():
    """{sku: product} for everything on offer, or None if nothing changed."""
    global etag
    first = get({"page": 1, "per_page": 200}, {"If-None-Match": etag} if etag else None)
    if first.status_code == 304:
        return None
    first.raise_for_status()
    body = first.json()
    products = body["data"]
    for page in range(2, body["meta"]["total_pages"] + 1):
        r = get({"page": page, "per_page": 200})
        r.raise_for_status()
        products += r.json()["data"]
    etag = first.headers.get("ETag")               # only after a complete read
    if body["meta"]["stale"]:
        print("warning: MBNF stock older than 24h")
    return {p["sku"]: p for p in products}

while True:
    stock = fetch_all()
    if stock is not None:
        update_my_catalog(stock)    # your code: anything NOT in `stock` is sold out
    time.sleep(300)

Rate limits#

Each key has a per-minute allowance (with a small burst) and a daily quota; /v1/me shows yours. Every authenticated response tells you where you stand:

HeaderMeaning
RateLimit-LimitRequests per minute for your key.
RateLimit-RemainingRequests you can make right now (burst left).
RateLimit-ResetSeconds until your allowance is full again.
X-Daily-Quota-LimitRequests per day (UTC).
X-Daily-Quota-RemainingRequests left today. Resets at 00:00 UTC.
Retry-AfterOn 429 and 503: seconds to wait before retrying.
ETagVersion of this response; send it back as If-None-Match.
X-Request-IdUnique ID of the request — quote it when you contact us.

Ten invalid keys from the same IP address within ten minutes block that address for fifteen minutes (429 too_many_failed_attempts), even for valid keys.

Errors#

Errors are JSON with a stable machine-readable error code:

429 Too Many Requests
{ "error": "rate_limited", "message": "Too many requests per minute. …", "request_id": "9f2c41d07e3a4b18" }
Status · errorWhat to do
400 · invalid_parameterFix the query parameter named in message.
400 · key_in_queryMove the key from the URL to the Authorization header.
401 · invalid_api_keyCheck the key. It may have been revoked or have expired: ask us for a new one.
403 · ip_not_allowedYour key is restricted to some IP addresses: call from one of them, or tell us your new IP.
404 · not_foundSKU not available for you right now, or wrong path.
405 · method_not_allowedOnly GET and HEAD.
429 · rate_limitedToo fast. Wait Retry-After seconds.
429 · daily_quota_exceededQuota used up. It resets at 00:00 UTC; use ETags to save requests.
429 · too_many_failed_attemptsToo many invalid keys from your IP. Wait Retry-After seconds and fix the key.
429 · server_busyRetry in a second.
503 · stock_unavailableData not loaded yet. Retry in a few minutes.
503 · service_suspendedThe API is temporarily switched off by MBNF. Retry later.
500 · internal_errorOur fault. Retry later; if it persists, send us the request_id.

Security#

  • Server-side only. Never put the key in a website, a mobile app or anything that runs on your customers' devices: anyone could read it there. The API intentionally does not support browser CORS requests.
  • Store it like a password — in an environment variable or a secret manager, not in your source code.
  • Rotation without downtime. You can have two active keys: ask for a new one, deploy it, then tell us to revoke the old one.
  • IP allow-list. On request we lock your key to your servers' IP addresses.
  • Leaked? Tell us: revocation is immediate.

Versioning#

/v1 is stable. We may add fields and endpoints at any time, so ignore fields you don't know. We will never rename or remove a field, or change its meaning, inside v1: that would be a new version, announced in advance.

DateChange
2026-10-14v1 launched: /v1/stock, /v1/stock/{sku}, /v1/me, sandbox key and interactive reference.

Support#

Reply to the message you received your key with, quoting your key ID (the 8 characters after mbnf_) and the request_id of the failing call. Never send us the full key.