Skip to content
TCGFlea Sell

Store API

Connect your store's website to TCGFlea. Your site sends its singles stock and prices; TCGFlea lists them, and calls your site the moment a card sells here, so you never sell the same card twice.

Last updated November 8, 2026

Getting started

  • The API is for approved TCGFlea sellers. Create a key in Seller Dashboard → Store API.
  • Base URL: https://tcgflea.vercel.app/api/v1. Send and receive JSON.
  • Authenticate every request with Authorization: Bearer tf_live_…. Keep the key on your server only.
  • Up to 120 requests a minute per key. Over that you get 429 with a Retry-After header.
  • Prices are in Philippine pesos. Times are ISO 8601 in UTC.

Check your key works:

curl https://tcgflea.vercel.app/api/v1/me -H "Authorization: Bearer tf_live_YOUR_KEY"

Errors always look like this, with an HTTP status of 400–503:

{ "error": { "code": "unauthorized", "message": "This API key doesn't exist or was revoked." } }

Find TCGFlea card ids

Each listing is one exact printing. You can send TCGFlea's card_id, or let TCGFlea match the card from its game, set code and collector number (the same matching as Bulk Upload). To look ids up:

GET https://tcgflea.vercel.app/api/v1/cards?game=mtg&set_code=DSK&number=123
GET https://tcgflea.vercel.app/api/v1/cards?game=pokemon&name=pikachu&limit=50
GET https://tcgflea.vercel.app/api/v1/cards?id=mtg:...

Each card comes with id, name, set, set_code, number, the finishes it's printed in, its market_price in pesos and an image.

Send your stock

PUT https://tcgflea.vercel.app/api/v1/stock adds or updates up to 500 products per request (POST works too). Each product is identified by your own SKU.

PUT https://tcgflea.vercel.app/api/v1/stock
Authorization: Bearer tf_live_YOUR_KEY
Content-Type: application/json

{
  "items": [
    { "sku": "DSK-123-NF-NM", "game": "mtg", "set_code": "DSK", "number": "123",
      "finish": "normal", "condition": "NM", "language": "EN", "quantity": 4, "price": 350 },
    { "sku": "PKM-SV10-201-H", "card_id": "pokemon:...", "finish": "holofoil", "quantity": 1 },
    { "sku": "DSK-123-NF-NM", "quantity": 3 }
  ]
}
FieldTypeWhat it is
skutext, requiredYour product code, up to 100 characters. One SKU = one TCGFlea listing.
quantityinteger, requiredHow many you can sell right now (0 = sold out). Send what your own site shows as available.
pricenumberYour price in pesos. Leave it out on a new product to use the TCGFlea market price; leave it out on an update to keep the current price.
price_mode"auto" | "manual"auto follows the market price daily; manual needs price.
activebooleanfalse pauses the listing without removing it.
card_idtextTCGFlea's card id. Or send the next fields instead:
gametextmtg, pokemon, riftbound, onepiece, yugioh, fab, lorcana, grandarchive, unionarena, palworld
set_code, numbertextThe set code and collector number printed on the card.
nametextThe card name: enough on its own only when the card has a single printing.
finishtextDefault normal. e.g. foil, holofoil, reverse-holofoil, etched.
conditiontextDefault NM. One of NM, LP, MP, HP, DMG.
languagetextDefault EN. A 2–3 letter code like JP.

Once a SKU is on TCGFlea, updates only need sku and quantity (and price if it changed), like the third item above. The reply has one result per item, in order:

{
  "summary": { "created": 1, "updated": 1, "linked": 0, "unchanged": 0, "error": 1 },
  "results": [
    { "sku": "DSK-123-NF-NM", "result": "created", "listing_id": "…", "error": null },
    { "sku": "PKM-SV10-201-H", "result": "error", "listing_id": null, "error": "No card with id pokemon:..." }
  ]
}
  • created: a new listing. updated / unchanged: an existing SKU. linked: you already had this card listed by hand or by Bulk Upload; your website now manages it.
  • Each item succeeds or fails on its own; fix the errors and send just those again.
  • Cards held for a buyer who is paying right now stay held, whatever quantity you send, so TCGFlea never sells more than you have.

Read or remove listings

GET    https://tcgflea.vercel.app/api/v1/stock?limit=500&page=1          all your listings, newest change first
GET    https://tcgflea.vercel.app/api/v1/stock?api_only=true             only the ones your website manages
GET    https://tcgflea.vercel.app/api/v1/stock?updated_since=2026-11-08T00:00:00Z
GET    https://tcgflea.vercel.app/api/v1/stock/{sku}
DELETE https://tcgflea.vercel.app/api/v1/stock/{sku}                       take it off TCGFlea

Each listing shows available (on sale now), held (reserved for a buyer who is paying), price, active, the card and its url on TCGFlea. Sending quantity: 0 keeps the SKU linked for later; DELETE removes it.

Orders

When a buyer pays, TCGFlea holds the money until they confirm delivery, and your part of the order appears here. Unpaid checkouts never do.

GET  https://tcgflea.vercel.app/api/v1/orders?status=paid               paid, waiting for you to ship
GET  https://tcgflea.vercel.app/api/v1/orders?updated_since=2026-11-08T00:00:00Z
GET  https://tcgflea.vercel.app/api/v1/orders/{id}
POST https://tcgflea.vercel.app/api/v1/orders/{id}/ship   { "carrier": "J&T Express", "tracking_no": "JT0001234567" }

Statuses: paid, shipped, completed, disputed, refunded. Each order has the order_code, ship_to (name, mobile and address), totals (cards, shipping and your payout) and items with your sku, quantity and unit_price.

Webhooks

Add your URL in Store API and pick the events. TCGFlea sends a POST as soon as your part of an order changes:

  • order.paid: a buyer paid. Lower your stock by each item's quantity.
  • order.shipped, order.completed, order.disputed: the order moved on.
  • order.refunded: TCGFlea refunded the buyer. If the cards come back to you, add them to your stock again.
  • ping: sent by the “Send test” button.
POST https://yourstore.ph/webhooks/tcgflea
TCGFlea-Event: order.paid
TCGFlea-Delivery: 7b0c…            (same id on every retry of this call)
TCGFlea-Signature: t=1762560000,v1=5f2a…

{ "id": "7b0c…", "type": "order.paid", "created_at": "2026-11-08T04:00:00Z", "attempt": 1,
  "data": { "order": { "id": "…", "order_code": "TF-2C0699F0", "status": "paid",
            "items": [ { "sku": "DSK-123-NF-NM", "quantity": 1, "unit_price": 350, … } ], … } } }

Check the signature: compute HMAC-SHA256 of {t}.{raw body} with your signing secret and compare it with v1. Reject calls older than 5 minutes. In Node.js:

import crypto from "node:crypto";

function fromTCGFlea(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}
  • Answer with any 2xx status within 8 seconds, then do the work. Redirects aren't followed.
  • Failed calls are retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h. You can see every call in Store API.
  • A call can arrive more than once: skip a TCGFlea-Delivery id you've already handled.

Keeping stock right

The simplest setup that never oversells:

  • Whenever your own stock changes (a sale on your site, a restock), send the new quantity for those SKUs.
  • On order.paid, lower your stock first, then send the new quantities if your site does that automatically.
  • Once a day, send your whole inventory (500 at a time) to fix anything that drifted, and use GET /orders?status=paid to catch any webhook you missed.

Listings your website manages show “Synced from your website” in your dashboard (and managed_by: "api" in the API). If you edit one on TCGFlea, your website's next update wins.

Questions? Contact us.