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
429with aRetry-Afterheader. - 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 }
]
}| Field | Type | What it is |
|---|---|---|
| sku | text, required | Your product code, up to 100 characters. One SKU = one TCGFlea listing. |
| quantity | integer, required | How many you can sell right now (0 = sold out). Send what your own site shows as available. |
| price | number | Your 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. |
| active | boolean | false pauses the listing without removing it. |
| card_id | text | TCGFlea's card id. Or send the next fields instead: |
| game | text | mtg, pokemon, riftbound, onepiece, yugioh, fab, lorcana, grandarchive, unionarena, palworld |
| set_code, number | text | The set code and collector number printed on the card. |
| name | text | The card name: enough on its own only when the card has a single printing. |
| finish | text | Default normal. e.g. foil, holofoil, reverse-holofoil, etched. |
| condition | text | Default NM. One of NM, LP, MP, HP, DMG. |
| language | text | Default 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 TCGFleaEach 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-Deliveryid 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
quantityfor 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=paidto 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.
