📄 Docs ⚙️ Dashboard 💳 Pricing 🔑 Get API Key 🏠 Main Site ↗

Marketplace Listing Automation

Stop hand-pricing every card. Pull live market price + condition multiplier, generate the right marketplace URL, and POST your listings via the seller endpoints.

Pipeline

  1. Recognise card from photo (POST /api/cards/recognize) → card_id + image-quality score.
  2. Pull cheapest live source price (GET /api/cards/{id}/prices).
  3. Apply condition multiplier + your margin → asking_price.
  4. Pull exact marketplace URLs (GET /api/cards/{id}/marketplace-urls) to point your listing at the right product page on Cardmarket/TCGPlayer.
  5. POST to /seller/listings with title / condition / price / photo.

curl — fetch the right Cardmarket URL

curl -H "Authorization: Bearer $CSM_KEY" \
  "https://collectorstashmarket.com/api/cards/3782/marketplace-urls?variant=normal"

Python — recognise → price → list

import os, requests

CSM, KEY = "https://collectorstashmarket.com", os.environ["CSM_KEY"]
HDR = {"Authorization": f"Bearer {KEY}"}
COND_MULT = {"NM": 0.95, "LP": 0.80, "MP": 0.55}

def smart_list(photo_path: str, condition: str, qty: int, margin: float = 1.05):
    # 1. recognise
    with open(photo_path, "rb") as f:
        scan = requests.post(f"{CSM}/api/cards/recognize", headers=HDR, files={"file": f}).json()
    if scan.get("confidence_label") not in ("high", "medium"):
        return print(f"[skip] low-confidence scan: {scan.get('confidence_label')}")
    card_id = scan["candidates"][0]["card_id"]
    quality = (scan.get("capture_quality") or {}).get("overall_score", 0)
    if quality < 0.55:
        return print(f"[skip] photo quality {quality:.2f} too low — re-shoot")

    # 2. price
    rows = requests.get(f"{CSM}/api/cards/{card_id}/prices", headers=HDR).json().get("prices", [])
    eur  = [r["market_price"] for r in rows if r.get("currency") == "EUR" and r.get("market_price")]
    if not eur:
        return print(f"[skip] card {card_id}: no EUR prices yet")
    asking = round(min(eur) * COND_MULT.get(condition, 0.95) * margin, 2)

    # 3. list
    resp = requests.post(f"{CSM}/seller/listings", headers=HDR, json={
        "card_id":   card_id,
        "condition": condition,
        "quantity":  qty,
        "price":     asking,
        "currency":  "EUR",
    })
    resp.raise_for_status()
    print(f"[ok] listed {card_id} → {asking} EUR ({condition} x{qty})")

smart_list("photos/charizard_base.jpg", "NM", qty=1)

TypeScript — slow-mover detector + auto-discount

// Drop the price 5% every 7 days if a listing sits unsold
import { fetch } from "undici";

const KEY = process.env.CSM_KEY!;
const HDR = { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" };

const { items } = await fetch(
  "https://collectorstashmarket.com/seller/listings?status=active&sort=oldest",
  { headers: HDR },
).then((r) => r.json()) as any;

for (const l of items) {
  const ageDays = (Date.now() - Date.parse(l.listed_at)) / 86_400_000;
  const discount = Math.min(0.20, 0.05 * Math.floor(ageDays / 7));
  if (discount === 0) continue;
  const newPrice = +(l.price * (1 - discount)).toFixed(2);
  await fetch(`https://collectorstashmarket.com/seller/listings/${l.id}`, {
    method: "PATCH",
    headers: HDR,
    body: JSON.stringify({ price: newPrice }),
  });
  console.log(`#${l.id} ${l.price} → ${newPrice} (-${discount*100}%)`);
}

PHP — bulk-import from a Cardmarket CSV

<?php
$key = getenv("CSM_KEY");
$rows = array_map("str_getcsv", file("cardmarket_export.csv"));
$header = array_shift($rows);
foreach (array_chunk($rows, 100) as $chunk) {
    $payload = array_map(fn($r) => [
        "card_id"   => (int)$r[0],
        "condition" => $r[1],
        "quantity"  => (int)$r[2],
        "price"     => (float)$r[3],
        "currency"  => "EUR",
    ], $chunk);
    $ch = curl_init("https://collectorstashmarket.com/seller/listings/bulk");
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer $key",
            "Content-Type: application/json",
        ],
        CURLOPT_POST       => 1,
        CURLOPT_POSTFIELDS => json_encode(["listings" => $payload]),
        CURLOPT_RETURNTRANSFER => 1,
    ]);
    echo curl_exec($ch), PHP_EOL;
}

Node — fee-preview before listing

const r = await fetch(
  "https://collectorstashmarket.com/api/listings/fee-preview?price=120&image_protection=true",
).then((x) => x.json());
// → { gross: 120, seller_fee: 6, payout: 114, ... }
console.log(`You'll receive €${r.payout} after fees`);

Related