🛡 10-Day Money-Back Guarantee.See Fixed-Price Offers
1,000,000+ Patches DeliveredMockup in 12-24 HoursLow 5-Piece Minimum10-Day Money-Back Guarantee

Partner API · v1 · read-only

Partner API reference

Three endpoints: what we make, what it costs you, and your whole price grid as a CSV. Prices come from the same engine as pandapatches.com — there is no second price list to drift out of date.

Read this before you quote

Fixed-size, fixed-quantity packs on pandapatches.com/offers are a separate product and are not available through this API. They may be cheaper than a custom-spec quote at the same size and quantity, and partners are welcome to buy packs at the public price. API pricing is the custom calculator price less your partner discount.

You will not have to go looking for this: when a pack matches your exact spec, the price response carries a public_pack object with its price and a link. If the pack is the better buy, we would rather tell you than have a client show you.

Authentication

Send your key in the X-API-Key header. Authorization: Bearer … works too.

curl https://www.pandapatches.com/api/v1/catalogue \
  -H "X-API-Key: $PANDA_API_KEY"

We store only a SHA-256 hash of your key, so we cannot read it back to you. Your key is shown once when it is issued. If you lose it or it leaks, tell us and we will revoke it and issue another — a revoked key stops working immediately. Keys carry scopes; a key without the scope an endpoint needs gets 403, not 401.

Every response carries an X-Request-Id header. Quote it if you need to ask us about a response and we can find the exact request.

How your price is worked out

retail        = the live calculator price for type / size / qty
discount_pct  = your partner discount
partner_price = round(retail x (1 - discount_pct / 100), 2)

Every pricing response carries all three figures so you can see your margin without keeping a copy of our retail prices. retail is the order total, not the per-piece price; the CSV price sheet carries per-piece columns as well.

Size is a band, not an area. We price on ceil((width_in + height_in) / 2), so a 2"Ă—6" and a 4"Ă—4" cost the same, and a 3.5"Ă—3.5" bills at the 4-inch band. This is the most common surprise in our pricing, so it is worth checking against your own assumptions before you build against it.

What every price carries

FieldWhat it means
valid_untilPrices hold for 30 days. An order placed against this quote_id inside that window is honoured at the quoted price even if our rates move.
quote_idQuote this when you place the order. We look the figures up on our side rather than taking the order's word for what we said.
rush_feeRush is 25% of YOUR discounted subtotal with a $50 minimum — the discount applies first. partner_price_with_rush is the two added together.
price_basis"quoted" from a factory-costed row. "estimate" where the figure is derived from the sizes either side of it rather than costed directly — see below.
public_packThe equivalent public pack, when one matches your spec exactly. Null when none does.
size_band_inThe band we actually priced: ceil((width_in + height_in) / 2).
currencyAlways USD. An account configured in any other currency is refused rather than quoted in USD figures wearing the wrong label.

Rows marked “estimate”

A few sizes carry "price_basis": "estimate" — woven at the 8-inch band, leather at 7 and 8 inches, printed at 13 and 14 inches. Those figures are derived from the sizes either side of them rather than costed directly by the factory. They are good enough to quote against at small quantities and we are not going to pretend otherwise at large ones.

An order above 250 pieces at one of those sizes is confirmed by email before we accept it. We are getting real factory numbers for them; when those land the label comes off and the confirmation step goes with it.

GET /api/v1/catalogue

Types you can order, the minimum, and the size above which we quote by hand instead of pricing automatically. Scope: catalogue.

Type ids: embroidered, woven, pvc, chenille, leather, printed, silicone, sequin, chenille-tpu, chenille-glitter, 3d-embroidered-transfer. Minimum 5 pieces on every type.

GET /api/v1/price

One price. Scope: price. Pass size=3x3, or width_in and height_in separately.

curl "https://www.pandapatches.com/api/v1/price?type=embroidered&size=3x3&qty=250" \
  -H "X-API-Key: $PANDA_API_KEY"

{
  "type": "embroidered",
  "width_in": 3, "height_in": 3, "qty": 250,
  "size_band_in": 3,
  "currency": "USD",
  "retail": 489.50,
  "discount_pct": 10.00,
  "partner_price": 440.55,
  "price_basis": "quoted",
  "rush_fee": 110.14,
  "partner_price_with_rush": 550.69,
  "public_pack": null,
  "discount_source": "tier",
  "quote_id": "b7f2…",
  "valid_until": "2026-10-15T00:00:00.000Z"
}

POST /api/v1/price

Up to 100 lines at once. A bad line does not fail the batch — each result carries its own ok, so 40 SKUs with one mistake gives you 39 prices and one clear failure.

curl -X POST https://www.pandapatches.com/api/v1/price \
  -H "X-API-Key: $PANDA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"items":[
        {"type":"embroidered","size":"3x3","qty":250},
        {"type":"pvc","size":"2x2","qty":500}
      ]}'

GET /api/v1/price-sheet

Your whole grid as CSV — every type, eight sizes, nine quantities, with per-piece columns. Add ?type=pvc for one type, or ?format=json to get the CSV inside a JSON envelope. Scope: price.

Combinations we will not auto-price — below the 5-piece minimum, or above a type’s auto-pricing size ceiling — are omitted, not shown as zero. Generate a fresh sheet rather than keeping one: it is priced live, so a downloaded copy goes stale the moment a rate moves.

Rate limits

Per key, per minute. If you hold both a sandbox and a live key, they have separate budgets — a test loop cannot exhaust your production allowance.

EndpointLimitWhy
/v1/price60 / minBatch up to 100 items in one POST rather than looping — it costs one request, not a hundred.
/v1/catalogue60 / minIt changes rarely. Cache it.
/v1/price-sheet10 / minEach request prices every cell in your grid live — around 800 calculations. It is the expensive one, deliberately.

Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. A 429 carries Retry-After in seconds — wait that long rather than retrying immediately. Repeated failed authentication is limited separately and more tightly, by IP.

Errors

CodeMeaning
401Key missing, unrecognised, or revoked.
403Key is valid but does not carry the scope this endpoint needs.
422Unknown type, a size or quantity we will not auto-price, or a malformed request. The message says which.
429Rate limited. Back off and retry.
500Our fault. Quote the X-Request-Id.

A 422 on a size is not a refusal to make it — patches above a type’s auto-pricing ceiling are still produced, they just need a hand quote, because above that size cost follows design complexity rather than area. Ask us and we will price it.

Not in this version

Week one is read-only. Order submission, mockup approve / request-changes, and order status are next, and are not available yet. Build against pricing now; we will publish the write endpoints here when they are live rather than before.

Questions, a new key, or a revocation: lance@pandapatches.com.