NewThe Catalog API is live — 15M+ cards with permanent UCIDs. Included with Pro.Explore →
The Card API

Docs

The market layer for trading cards — transaction-level sales data, cleaned, enriched, and updated daily. Access via REST endpoints and an MCP server with AI analysis tools.

Quickstart

Quickstart

Get your first response in under a minute.

1. Get a free API key

Sign up at thecardapi.com → No credit card required. Your key arrives instantly.

2. Make your first request

Shell
curl "https://thecardapi.com/api/v1/market/sales?q=psa+10+ohtani&limit=5" \
  -H "x-market-api-key: tca_your_key_here"

3. Explore

Use the playground to build and test queries interactively before writing any code. The full parameter reference is in the GET /sales section below.

Authentication

Authentication

All requests require an x-market-api-key header (REST) or an Authorization: Bearer header (MCP). The same key works for both. Get one free — no credit card required.

HTTP
# REST API
GET /api/v1/market/sales
x-market-api-key: tca_your_key_here

# MCP Server
POST /api/mcp
Authorization: Bearer tca_your_key_here

Don't have a key? Get one free →

Base

Base URL

REST API

https://thecardapi.com/api/v1/market

All REST paths below are relative to this base.

MCP Server

https://www.thecardapi.com/api/mcp

JSON-RPC 2.0 over streamable HTTP. See the MCP Server section.

Endpoints

Endpoints

MethodPathDescription
GET/salesSearch and filter sales records
GET/sales/{id}Single sale by ID
GET/dailyAggregate stats for a date
GET/platformsPlatform list with last updated date
GET/coverageLatest data freshness status
GET/sales/export/csvDownload filtered sales as CSV
POST/webhookAdd a webhook endpoint (max 5) — available on all paid plans
GET/webhookList active webhook endpoints
DELETE/webhook/{endpoint_id}Remove a webhook endpoint

MCP tools are invoked via POST /api/mcp — see the MCP Tools section for individual tool references.

GET

GET /sales

Search and filter individual sale records. Results are ordered by date descending by default. Your plan's lookback window is enforced on date_from automatically.

ParameterTypeDescription
qstringoptionalFull-text search on listing title (min 4 chars). Supports boolean syntax: (term1,term2) for OR, -term for NOT, "exact phrase" for phrase match.
platformstringoptionalFilter by platform. Values: ebay · tcgplayer · goldin · lelands · scp · hakes · rea. See GET /platforms for the full list.
listing_typestringoptionalComma-separated: auction, fixed_price, best_offer
date_fromdateoptionalStart date YYYY-MM-DD. Clamped to your plan's lookback window.
date_todateoptionalEnd date YYYY-MM-DD
price_minnumberoptionalMinimum sale price (USD)
price_maxnumberoptionalMaximum sale price (USD)
print_run_minintegeroptionalMinimum print run (e.g. 1 for 1/1 only)
print_run_maxintegeroptionalMaximum print run (e.g. 99 to filter to /99 and lower numbered cards)
shipping_maxnumberoptionalMaximum buyer shipping cost in USD. Use shipping_max=0 for free-shipping listings only. Filters to records where shipping_price ≤ this value (null shipping_price records are excluded).
categorystringoptionalFilter by card category: sports, tcg, non_sport. eBay records indexed from June 6, 2026 onward. Auction house sources (Goldin, REA, etc.) return null until backfilled.
gradedbooleanoptionalgraded=true returns only professionally graded cards (any grading company); graded=false returns only raw/ungraded. Works on every platform. Autograph-authentication-only slabs (PSA/DNA, JSA, BAS) count as ungraded — the card's condition was never graded.
graderstringoptionalFilter by a specific grading company: PSA, BGS, SGC, CGC, etc. (case-insensitive)
gradestringoptionalFilter by grade value: 10, 9.5, 9, etc. Combine with grader (e.g. grader=PSA&grade=10).
conditionstringoptionalFilter by listing condition string, e.g. Pre-Owned (ungraded) or New (Other) (graded slab). eBay records only.
sortstringoptionaldate_desc (default) | date_asc | price_desc | price_asc
pageintegeroptionalPage number (default: 1)
limitintegeroptionalResults per request (default: 25, max: 1,000). Same cap on all plans.
cursorstringoptionalOpaque cursor from a previous response's next_cursor field. Efficiently paginate large result sets without offset drift. When provided, page is ignored. Available on all plans.
indexed_afterdatetimeoptionalISO 8601 UTC. Return only records indexed after this time. Useful for incremental sync.

Cursor pagination

Every response includes a next_cursor field in pagination when more results exist. Pass it as ?cursor= on the next request to fetch the next page — no offset drift, no missed records. Available on all plans.

# Page 1 — no cursor needed
GET /v1/market/sales?q=psa+10+trout&limit=1000
→ pagination.next_cursor: "eyJwZ19pZCI6IDE4N..."

# Page 2 — pass the cursor
GET /v1/market/sales?q=psa+10+trout&limit=1000&cursor=eyJwZ19pZCI6IDE4N...
→ pagination.next_cursor: "eyJwZ19pZCI6IDE2N..."  # or null if last page

CSV export

Download a filtered result set as a CSV file. Same filter params as GET /sales — no cursor or page. Available on Starter and above.

CSV rows count against your shared daily budget — the same pool as API calls. A Builder user who pulls 30K rows via the API has 20K remaining, usable via API or CSV.

curl "https://thecardapi.com/api/v1/market/sales/export/csv?q=psa+10+ohtani&date_from=2026-01-01" \
  -H "x-market-api-key: tca_your_key_here" \
  -o sales.csv

Search syntax

(psa,bgs) — title contains "psa" OR "bgs"
-reprint — title does NOT contain "reprint"
-(lot,album) — title contains neither "lot" nor "album"
"topps chrome" — exact phrase match
ohtani (psa,bgs) -(lot,album) -reprint — combine all forms

Response

Response schema

JSON
{
  "data": [
    {
      "id":                    "137222685761",
      "platform":              "eBay",
      "listing_type":          "auction",
      "title":                 "2021 Bowman Chrome Kyle Harrison Auto PSA 10",
      "sale_date":             "2026-05-14",
      "sold_at":               "2026-05-14T00:00:00Z",
      "price":                 47.50,
      "original_price":        null,
      "currency":              "USD",
      "price_confirmed":       true,
      "bids":                  12,
      "image_url":             "https://i.ebayimg.com/...",
      "thumbnail_url":         "https://i.ebayimg.com/...",
      "listing_url":           "https://www.ebay.com/itm/137222685761",
      "cert":                  "93265128",
      "condition":             null,
      "grade":                 "10",
      "grader":                "PSA",
      "grading_company":       "Professional Sports Authenticator",
      "has_autograph_grade":   false,
      "has_grade_qualifier":   false,
      "label":                 null,
      "grade_qualifier":       null,
      "autograph_grade":       null,
      "player":                "Kyle Harrison",
      "manufacturer":          "Bowman",
      "card_set":              "2021 Bowman Chrome",
      "card_number":           "BCP-42",
      "year":                  2021,
      "season":                "2021",
      "league":                "Major League (MLB)",
      "sport":                 "Baseball",
      "team":                  "San Francisco Giants",
      "features":              ["Auto", "Rookie"],
      "print_run":             null,
      "shipping_price":        4.99,
      "category":              "sports"
    }
  ],
  "pagination": {
    "total":       258817,
    "page":        1,
    "limit":       25,
    "pages":       10353,
    "has_more":    true,
    "next_cursor": "eyJwZ19pZCI6IDE4NzUwMDAwLCAic29ydCI6ICJkYXRlX2Rlc2MifQ=="
  },
  "meta": {
    "coverage_date_from":  "2026-05-01",
    "coverage_date_to":    "2026-05-17",
    "platforms_covered":   ["eBay", "Goldin", "Lelands", "SCP Auctions", "Hakes", "REA", "TCGplayer"],
    "generated_at":        "2026-05-17T10:00:00Z"
  }
}
FieldTypeCoverageDescription
idstring100%Unique sale identifier (platform-prefixed)
platformstring100%Platform name. Values: eBay · Goldin · Lelands · SCP Auctions · Hakes · REA · TCGplayer
listing_typestring100%auction · fixed_price · best_offer
titlestring100%Raw listing title
sale_datestring100%YYYY-MM-DD of the sale
sold_atstring100%ISO 8601 timestamp (UTC). Day-boundary precision: ends in T00:00:00Z unless exact time was captured.
pricenumber100%Final sale price in USD. For eBay: all-in buyer price. For Goldin: hammer price only — buyer also pays ~22% buyer's premium on top. For best_offer: the true negotiated price.
original_pricenumber|nullnewPre-negotiation asking price for best_offer sales (the listed price before the offer was accepted). Compare with price to see the negotiated discount. eBay records from Aug 2026 onward; null elsewhere.
currencystring100%Currency of price and original_price (ISO 4217: USD, GBP, CAD, AUD, EUR, …). Records before Aug 2026 report USD regardless of listing currency until the historical backfill completes.
price_confirmedboolean100%true = confirmed final price. false = fast-settle estimate (auction BIN/BO, updated to true within minutes once confirmed).
bidsinteger|null~60%Number of bids (auction only)
image_urlstring|null~85%Full-size listing image URL
thumbnail_urlstring|null~85%Thumbnail image URL
listing_urlstring|null~99%Direct link to the sold listing on eBay (or other marketplace). Note: eBay may redirect logged-in users to a product catalog page. To view the original sold listing, open the URL in an Incognito/Private browsing window. This redirect does not affect programmatic/API access.
certstring|null~12%Grading certificate number
conditionstring|null~8%Raw condition string from listing
gradestring|null~12%Numeric grade (e.g. "10", "9.5", "Auth")
graderstring|null~12%Grader abbreviation: PSA · BGS · CGC · SGC
grading_companystring|null~12%Full grading company name
has_autograph_gradeboolean|null~12%Whether the slab has a separate auto grade
has_grade_qualifierboolean|null~12%Whether the grade has a qualifier (e.g. OC, MK)
grade_qualifierstring|null~3%Qualifier type (e.g. "OC", "MK", "ST")
labelstring|null~10%Grading label variant (e.g. "Gold Label")
autograph_gradestring|null~4%Autograph sub-grade where graded separately
playerstring|null~0.3%Player name. Populated for catalog-matched records. null if not yet matched.
manufacturerstring|null~0.4%Card manufacturer (e.g. Topps, Panini, Bowman). Populated for catalog-matched records.
card_setstring|null~0.4%Set name (e.g. 2022 Topps Update Series). Populated for catalog-matched records.
card_numberstring|null~0.4%Set card number (e.g. #295 or BCP-42). Distinct from print run (/299).
yearinteger|null~0.3%Card year. Populated for catalog-matched records.
seasonstring|null~0.3%Sports season (e.g. 2024-25). Populated for catalog-matched records.
leaguestring|null~0.3%League name (e.g. Major League (MLB), National Basketball Association (NBA)).
sportstring|null~0.4%Sport (e.g. Baseball, Basketball, Hockey). Populated for catalog-matched records.
teamstring|null~0.3%Team name at time of card printing. Populated for catalog-matched records.
featuresstring[]~0.3%Array of card features extracted from listing (e.g. ["Auto", "Rookie", "Patch"]). Empty array if not populated.
print_runinteger|null~36%Print run (denominator of serial fraction). /99 → 99. /10 → 10. 1/1 → 1. Null for unlisted base cards. Filter with print_run_min / print_run_max.
shipping_pricenumber|null~67%Buyer shipping cost in USD. 0.00 = free shipping. Null when the source doesn't provide it. Filter with shipping_max.
categorystring|null~99%*Card category. Values: sports · tcg · non_sport. eBay records indexed June 6, 2026+. Auction house sources return null until backfilled. Filter with ?category=sports. (*eBay Jun 6, 2026+ only)
listing_type: "best_offer" — the pricefield is the true final negotiated price, not the listing price. Most data providers don't expose this. We do.
Goldin price note — for platform: "goldin" records, price is the hammer price— what the auctioneer called at close. The buyer also pays a buyer's premium (~22% current, ~20% pre-2022) on top. eBay prices are all-in. Factor this in when comparing values across platforms.

Error

Error codes

StatusMeaning
401Invalid or missing API key
403Action requires a higher plan or beta access
404Resource not found
422Validation error — check parameter types and ranges
429Daily sales limit reached. Resets at midnight UTC. Upgrade for more.
500Server error — try again shortly

Webhooks

Webhooks

$9/mo add-on · Available on all paid plans · 7-day free trial. Get started →

Register up to 5 HTTPS endpoints and receive every new card sale pushed to them within seconds of indexing — no polling required. Sales are delivered as signed batches of up to 1,000 records. Your cursor advances on each successful delivery so you never miss a record and never receive the same one twice.

Add an endpoint

HTTP
POST /api/v1/market/webhook
x-market-api-key: tca_your_key_here
Content-Type: application/json

{
  "url":    "https://your-server.com/webhook",
  "secret": "your-hmac-signing-secret",
  "label":  "prod-receiver"
}

Max 5 active endpoints per key. label is optional.

List endpoints

HTTP
GET /api/v1/market/webhook
x-market-api-key: tca_your_key_here
JSON
{
  "endpoints": [
    { "id": 1, "url": "https://your-server.com/webhook", "label": "prod-receiver", "created_at": "2026-06-20T10:00:00Z" },
    { "id": 2, "url": "https://your-server.com/backup",  "label": "backup",         "created_at": "2026-06-20T10:01:00Z" }
  ],
  "count": 2,
  "max":   5
}

Remove an endpoint

HTTP
DELETE /api/v1/market/webhook/{endpoint_id}
x-market-api-key: tca_your_key_here

Delivery payload

Each POST to your endpoint contains a batch of sale records — the same schema as GET /v1/market/sales.

JSON
POST https://your-endpoint.com/webhook
Content-Type: application/json
X-Webhook-Signature: sha256=<hmac-sha256>

{
  "event":     "sales.batch",
  "timestamp": "2026-06-20T06:09:00Z",
  "count":     47,
  "data": [
    {
      "id":           "ebay-387214905012",
      "title":        "2011 Topps Update Mike Trout RC BGS 9.5",
      "price":        4200.00,
      "listing_type": "auction",
      "sold_at":      "2026-06-20T06:00:24Z",
      "print_run":    null,
      ...
    }
  ]
}

Verify the signature

Every delivery includes X-Webhook-Signature: sha256=<hex>. Verify against the raw request body before processing.

Python
import hashlib, hmac

def verify_webhook(body: bytes, secret: str, sig_header: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, sig_header)
DetailValue
Price$9/mo add-on · 7-day free trial · Available on all paid plans
Max endpoints5 active endpoints per API key
Delivery latency~30s from indexing (auction close → indexed → delivered)
Batch sizeUp to 1,000 records per delivery
Retry policy3 attempts with backoff (0s / 5s / 30s). Cursor holds on all-fail.
Poll cadenceEvery 30 seconds
SignatureHMAC-SHA256 — X-Webhook-Signature: sha256=<hex>
Event typesales.batch

Catalog

Catalog API

The Catalog API is the structured complement to the sales feed: 16.1M+ cards across 331,000+ sets and 15 sports, with full checklists, rookie/auto/relic flags, print runs, and parallels linked to their parent products. Sales tell you what's trading — the catalog tells you what exists.

Access

Included with Pro and Enterprise at no extra charge. Available on Builder as a $29/mo add-on. See pricing →

Typed identifiers — UCID, USID, UPID, UFID

Every record carries a permanent typed identifier in one format, like UC-1KJZD-TZG7C-6: UC- for cards (ucid), US- for sets (usid), UP- for players (upid), UF- for set families (ufid). The final character is a check digit, so a typo returns an error instead of the wrong record. Names and years may be corrected over time; these ids never change and are never reused. Key your integration on them.

  • • Accepted anywhere an ID is: /catalog/{ucid}, set_id=US-..., player_id=UP-..., family_of=US-... or UF-...
  • • Case-insensitive, dashes optional — uc1kjzdtzg7c6 works
  • • Responses also include a display slug (2023-topps/27) — for URLs and UI only, never a join key
  • • The integer card id field remains during the transition and is deprecated

Endpoints

Base URL: https://www.thecardapi.com/api/v1/catalog — same x-api-key header as the sales API.

/catalogSearch cards — at least one filter required
/catalog/{id}Single card by UCID (or integer id)
/catalog/categoriesPreferred taxonomy for building catalog navigation and filters
/catalog/setsBrowse and search sets by category, subcategory, name, year, and more
/catalog/sets/{id}Single set by USID with card counts
/catalog/familiesProduct families index — UFID, name, year, member count
/catalog/playersPlayer search — name to UPID with card counts
/catalog/new-releasesSets added to the catalog, newest first — filter by sport or year
/catalog/sportsLegacy source-category list — retained for compatibility

Categories and subcategories

Use /catalog/categoriesto discover the catalog's stable, developer-facing taxonomy. Every set response includes one top-level category and one primary subcategory. The endpoint returns machine IDs, display labels, descriptions, and the valid subcategories beneath each category, so applications can build filter UIs without hardcoding our labels.

categorystringoptionalTop-level ID: sports, trading_card_games, or entertainment.
subcategorystringoptionalStable primary ID such as baseball, pokemon, lorcana, magic_the_gathering, marvel, or star_wars.
sportstringoptionalLegacy source-category field retained for compatibility. New navigation should use category and subcategory.
bash
# Discover every valid category and subcategory
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/catalog/categories"

# Browse Lorcana sets
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/catalog/sets?subcategory=lorcana&limit=100"
JSON
{
  "data": [{
    "usid":        "US-...",
    "set_name":    "Disney Lorcana ...",
    "category":    "trading_card_games",
    "subcategory": "lorcana",
    "sport":       "Gaming"
  }],
  "pagination": { "total": 22, "page": 1, "limit": 100, "pages": 1 }
}

Machine IDs are stable; display labels may improve over time. /catalog/sports and thesport filter continue to work for existing integrations.

Set browse parameters — GET /catalog/sets

categorystringoptionalTop-level category ID from /catalog/categories.
subcategorystringoptionalSubcategory ID from /catalog/categories. A subcategory also implies its parent category.
sportstringoptionalLegacy source-category filter, case-insensitive.
qstringoptionalCase-insensitive set-name search (min 2 chars).
yearintegeroptionalFilter by set year.
family_ofUSID or UFIDoptionalReturn every set in a product family from either a member USID or the family UFID.
main_onlybooleanoptionaltrue returns top-level sets only, excluding inserts, parallels, autos, and other sub-sets.
page / limitintegeroptionalPagination. Default limit: 25; maximum: 100.

Card search parameters — GET /catalog

set_idUSIDoptionalFilter by set — fastest path. Accepts the set's USID.
player_idUPIDoptionalEvery card of a player — find the UPID via /catalog/players.
card_numberstringoptionalExact card number, case-insensitive (leading # ignored). Combine with set_id for a one-record existence check.
set_namestringoptionalCase-insensitive substring match on set name (min 2 chars).
qstringoptionalFull-text search across card subject and set name (min 2 chars).
sportstringoptionalBaseball, Basketball, Football, Hockey, Soccer, Gaming (TCG), Racing, and more.
yearintegeroptionalSet year.
is_rookie / is_autobooleanoptionalCard attribute filters.
page / limitintegeroptionalPagination. Max limit: 100 (Builder), 1,000 (Pro/Enterprise).

Unrecognized parameters return a 400 listing the supported ones — a misspelled filter never silently returns unfiltered results.

Example — one-record existence check

bash
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/catalog?set_id=US-J28FC-5H09C-4&card_number=27"
JSON
{
  "data": [{
    "ucid":            "UC-1KJZD-TZG7C-6",
    "slug":            "2023-topps/27",
    "set_usid":        "US-J28FC-5H09C-4",
    "set_name":        "2023 Topps",
    "parent_set_name": null,
    "card_number":     "27",
    "subject":         "Mike Trout",
    "is_rookie":       false,
    "is_auto":         false,
    "print_run":       null
  }],
  "pagination": { "total": 1, "page": 1, "limit": 100, "pages": 1 }
}

Parallels and inserts are sub-sets that carry their parent product: a "Gold Rainbow Foil" card returns parent_set_name: "2021 Topps Chrome" and parent_set_usid, so version identification never loses the product context. Serial-numbered parallels expose print_run for deterministic disambiguation.

Catalog allowance

Catalog usage has its own daily pool — it never consumes your sales budget, and vice versa. Each record returned counts as 1 (minimum 1 per request); counters reset at 00:00 UTC. Every /catalog response carries X-RateLimit-Limit / X-RateLimit-Remaining headers for this pool.

Builder + Catalog add-on500 records/day
Pro (included)2,000 records/day
Enterprise (included)Unlimited

Need a higher allowance? Email hello@thecardapi.com — per-key allowances and bulk catalog licensing are available.

Rate

Rate limits

The only enforced limits are sales rows returned per day and how far back you can query.

PlanSales/dayLookback
Free5,0003 days
Starter10,00014 days
Builder50,00030 days
Pro200,00090 days
EnterpriseCustomUnlimited

Daily counters reset at 00:00:00 UTC each day. API calls and CSV exports draw from the same shared budget — a Builder user who downloads 30K rows via CSV has 20K remaining for API calls that day.

Max limit is 1,000 rows per request on all plans. View full pricing →

Pro add-ons

Full Daily Feed ($99/mo) — unlimited pulls for sale_date = yesterday. Your normal daily cap still applies to all other date ranges.
Unlimited Lookback ($99/mo) — removes the 90-day lookback window. Query the full historical dataset with no date restriction.

Both add-ons require an active Pro subscription. The Catalog API is included with Pro; on Builder it's a $29/mo add-on. See add-ons →

Every response from /v1/market/sales includes rate limit headers so you can track usage programmatically:

HeaderDescription
X-RateLimit-LimitYour plan's daily sales cap
X-RateLimit-RemainingSales rows remaining today
X-RateLimit-ResetUnix timestamp of next reset (00:00:00 UTC)

MCP

MCP Server

The MCP (Model Context Protocol) server exposes 7 analysis tools over a single streamable HTTP endpoint. Any MCP-compatible client — Claude Desktop, Claude Code, Cursor, Windsurf, ChatGPT Desktop, LangChain — can connect with one URL.

Endpoint

POST https://www.thecardapi.com/api/mcp

Protocol: JSON-RPC 2.0 · Transport: streamable HTTP

Discover available tools

JSON
{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "tools/list"
}

Claude Desktop

JSON
{
  "mcpServers": {
    "thecardapi": {
      "command": "npx",
      "args": ["mcp-remote", "https://www.thecardapi.com/api/mcp",
               "--header", "Authorization: Bearer YOUR_API_KEY"]
    }
  }
}

Claude Code

Shell
claude mcp add thecardapi \
  --transport http \
  --url https://www.thecardapi.com/api/mcp \
  --header "Authorization: Bearer YOUR_API_KEY"

Cursor / Windsurf

JSON
{
  "mcpServers": {
    "thecardapi": {
      "url": "https://www.thecardapi.com/api/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}

See the full MCP integration guide → for all clients.

MCP

MCP Tools

Each tool is called via method: "tools/call" with a name and arguments object.

search_cards

Find recent sold prices for any card. Returns individual sale records sorted by date. Supports filtering by grader, grade, listing type, date range, and price range.

Parameters

querystringoptionalFull-text search — e.g. 'PSA 10 Luka Doncic 2018 Prizm Silver'
platformstringoptionalFilter by platform (e.g. ebay)
graderstringoptionalpsa | cgc | beckett | sgc
gradestringoptional10 | 9.5 | 9 | 8.5 | 8 | 7 | auth | 10pristine
listing_typestringoptionalAuction | FixedPrice | BestOffer
date_fromstringoptionalStart date YYYY-MM-DD
date_tostringoptionalEnd date YYYY-MM-DD
price_minnumberoptionalMinimum sale price
price_maxnumberoptionalMaximum sale price
limitintegeroptionalResults to return (1–20, default: 10)

Example call

JSON
{
  "jsonrpc": "2.0", "id": "1",
  "method": "tools/call",
  "params": {
    "name": "search_cards",
    "arguments": {
      "query":        "PSA 10 Luka Doncic 2018 Prizm Silver",
      "listing_type": "BestOffer",
      "limit":        10
    }
  }
}

Response (abbreviated)

JSON
{
  "total_returned": 10,
  "results": [
    {
      "id":           "ebay-137222685761",
      "title":        "2018 Panini Prizm Silver Luka Doncic PSA 10",
      "platform":     "eBay",
      "listing_type": "BestOffer",
      "price":        4800.00,
      "sale_date":    "2026-05-16",
      "sold_at":      "2026-05-16T14:23:00Z",
      "bids":         null,
      "grader":       "PSA",
      "grade":        "10",
      "slab_serial":  "84566824",
      "listing_url":  "https://www.ebay.com/itm/137222685761"
    }
  ],
  "note": "BestOffer prices reflect the true final negotiated amount, not the list price."
}
grade_impact_analysis

See exactly how PSA/BGS grade affects price for a specific card. Returns a full price ladder sorted grade-high to grade-low, plus the grade where the value jump is sharpest.

Parameters

querystringrequiredCard to analyze — e.g. 'Luka Doncic 2018 Prizm Silver'
graderstringoptionalpsa (default) | cgc | beckett | sgc
lookback_daysintegeroptionalDays of history to include (default: 90)

Example call

JSON
{
  "jsonrpc": "2.0", "id": "1",
  "method": "tools/call",
  "params": {
    "name": "grade_impact_analysis",
    "arguments": {
      "query":         "Luka Doncic 2018 Prizm Silver",
      "grader":        "psa",
      "lookback_days": 90
    }
  }
}

Response (abbreviated)

JSON
{
  "query":        "Luka Doncic 2018 Prizm Silver",
  "grader":       "PSA",
  "lookback_days": 90,
  "total_sales":  49,
  "price_ladder": [
    { "grade": "10", "sales": 6,  "avg_price": 9200, "median_price": 8750, "min_price": 7800, "max_price": 11200 },
    { "grade": "9",  "sales": 28, "avg_price": 3400, "median_price": 3250, "min_price": 2800, "max_price": 4100 },
    { "grade": "8",  "sales": 12, "avg_price": 1950, "median_price": 1875, "min_price": 1600, "max_price": 2300 },
    { "grade": "7",  "sales": 3,  "avg_price": 850,  "median_price": 820,  "min_price": 750,  "max_price": 980  }
  ],
  "best_value_grade": {
    "grade":  "9",
    "reason": "Stepping up to 10 costs 171% more — this is the steepest value jump in the price ladder."
  }
}
grader_comparison

PSA vs CGC vs SGC vs BGS for the same card at the same grade. Find which grader commands the highest market premium.

Parameters

querystringrequiredCard to compare — e.g. 'Wembanyama 2023 Prizm Silver'
gradestringrequiredGrade to compare across graders — e.g. 10 | 9.5 | 9 | 8.5 | 8
lookback_daysintegeroptionalDays of history (default: 90)

Example call

JSON
{
  "jsonrpc": "2.0", "id": "1",
  "method": "tools/call",
  "params": {
    "name": "grader_comparison",
    "arguments": {
      "query": "Wembanyama 2023 Prizm Silver",
      "grade": "9"
    }
  }
}

Response (abbreviated)

JSON
{
  "query":        "Wembanyama 2023 Prizm Silver",
  "grade":        "9",
  "lookback_days": 90,
  "by_grader": [
    { "grader": "PSA", "sales": 18, "avg_price": 1450, "median_price": 1380 },
    { "grader": "BGS", "sales": 7,  "avg_price": 1210, "median_price": 1175 },
    { "grader": "CGC", "sales": 4,  "avg_price": 980,  "median_price": 950  },
    { "grader": "SGC", "sales": 2,  "avg_price": 890,  "median_price": 890  }
  ],
  "takeaway": "PSA commands the highest avg price at $1450",
  "comparisons": [
    { "grader": "BGS", "pct_cheaper_than_top": 17 },
    { "grader": "CGC", "pct_cheaper_than_top": 32 },
    { "grader": "SGC", "pct_cheaper_than_top": 39 }
  ]
}
price_momentum

Is this card trending up or down? Compares a recent time window against the prior window of equal length, with volume data. Upper bound is always yesterday so partial-day data never skews the result.

Parameters

querystringrequiredCard to analyze — e.g. 'Prizm Silver Wembanyama'
lookback_daysintegeroptionalLength of each comparison window in days (default: 7)
graderstringoptionalOptional — filter by grader: psa | cgc | beckett | sgc
gradestringoptionalOptional — filter by grade (e.g. 10, 9.5)

Example call

JSON
{
  "jsonrpc": "2.0", "id": "1",
  "method": "tools/call",
  "params": {
    "name": "price_momentum",
    "arguments": {
      "query":         "Wembanyama Prizm Silver",
      "lookback_days": 14
    }
  }
}

Response (abbreviated)

JSON
{
  "query":       "Wembanyama Prizm Silver",
  "window_days": 14,
  "recent_window": {
    "from": "2026-05-03", "to": "2026-05-16",
    "sales": 42, "avg_price": 1380
  },
  "prior_window": {
    "from": "2026-04-19", "to": "2026-05-02",
    "sales": 35, "avg_price": 1150
  },
  "price_change_pct":  20.0,
  "volume_change_pct": 20.0,
  "momentum": "rising",
  "note": "'rising' means both price and volume increased vs the prior window of the same length."
}
grading_value_calculator

Should you grade this card? Real cost math (PSA/CGC/SGC/BGS fees + shipping, all tiers including bulk) vs actual market comps. Returns expected profit at each grade, break-even analysis, and honest caveats.

Parameters

querystringrequiredCard to evaluate — e.g. '1986 Fleer Jordan 57'
raw_purchase_pricenumberrequiredWhat you paid (or plan to pay) for the raw ungraded card
target_gradestringoptionalGrade you hope to achieve: 10 | 9.5 | 9 | 8.5 (default: 10)
graderstringoptionalpsa (default) | cgc | beckett | sgc
submission_tierstringoptionalbulk | economy | standard (default) | express

Example call

JSON
{
  "jsonrpc": "2.0", "id": "1",
  "method": "tools/call",
  "params": {
    "name": "grading_value_calculator",
    "arguments": {
      "query":              "1986 Fleer Jordan 57",
      "raw_purchase_price": 6500,
      "target_grade":       "10",
      "grader":             "psa",
      "submission_tier":    "standard"
    }
  }
}

Response (abbreviated)

JSON
{
  "query":              "1986 Fleer Jordan 57",
  "raw_purchase_price": 6500,
  "grader":             "PSA",
  "target_grade":       "10",
  "grading_costs": {
    "service_tier":       "standard",
    "per_card_fee":       "$79.99",
    "turnaround":         "25 days",
    "shipping_roundtrip": "$40–$60 (continental US)",
    "total_estimate":     "$119.99–$139.99",
    "total_mid_estimate": 130
  },
  "grade_scenarios": [
    { "grade": "10",  "sales": 8,  "avg_price": 42000, "expected_profit": 35370, "is_target": true },
    { "grade": "9",   "sales": 22, "avg_price": 9800,  "expected_profit": 3170,  "is_target": false },
    { "grade": "8",   "sales": 11, "avg_price": 4200,  "expected_profit": -2330, "is_target": false }
  ],
  "math": {
    "if_card_grades_at_target": "$42000 (avg sale price)",
    "minus_raw_cost":           "-$6500",
    "minus_grading_and_ship":   "-$130 (midpoint estimate)",
    "expected_profit":          "$+35370",
    "break_even_grade":         "8"
  },
  "caveats": [
    "Grading outcome is NOT guaranteed — these numbers assume the card achieves each grade.",
    "Only 8 comps found at 10 — price estimate may be unreliable."
  ]
}

Code

Code examples

Python — REST API

Python
import requests

API_KEY = "tca_your_key_here"
BASE    = "https://thecardapi.com/api/v1/market"
HEADERS = {"x-market-api-key": API_KEY}

# Search recent Ohtani auctions
r = requests.get(f"{BASE}/sales", headers=HEADERS, params={
    "q":            "Shohei Ohtani",
    "listing_type": "auction",
    "price_min":    50,
    "limit":        50,
})
for sale in r.json()["data"]:
    print(sale["sale_date"], sale["price"], sale["title"][:60])

# Incremental sync — only records indexed since your last poll
r = requests.get(f"{BASE}/sales", headers=HEADERS, params={
    "indexed_after": "2026-05-15T10:00:00Z",
    "limit":         100,
})

Python — MCP tools/call

Python
import requests

MCP_URL = "https://www.thecardapi.com/api/mcp"
HEADERS = {"Authorization": "Bearer tca_your_key_here", "Content-Type": "application/json"}

def call_tool(name: str, arguments: dict) -> dict:
    payload = {
        "jsonrpc": "2.0", "id": "1",
        "method":  "tools/call",
        "params":  {"name": name, "arguments": arguments},
    }
    r = requests.post(MCP_URL, headers=HEADERS, json=payload)
    result = r.json()
    if result.get("error"):
        raise RuntimeError(result["error"]["message"])
    import json
    return json.loads(result["result"]["content"][0]["text"])

# Grade ladder for Mantle cards over $1,000
data = call_tool("grade_impact_analysis", {
    "query":         "Mickey Mantle",
    "grader":        "psa",
    "lookback_days": 30,
})
for row in data["price_ladder"]:
    avg = "{:,.0f}".format(row['avg_price'])
    print(f"PSA {row['grade']}: ${avg} avg ({row['sales']} sales)")

cURL

Shell
# REST — search for PSA 10 cards
curl "https://thecardapi.com/api/v1/market/sales?q=psa+10&limit=25" \
  -H "x-market-api-key: tca_your_key_here"

# MCP — call price_momentum tool
curl https://www.thecardapi.com/api/mcp \
  -H "Authorization: Bearer tca_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"price_momentum","arguments":{"query":"Wembanyama Prizm Silver"}}}'

Node.js

JavaScript
const res = await fetch(
  "https://thecardapi.com/api/v1/market/sales?q=topps+chrome&limit=50",
  { headers: { "x-market-api-key": "tca_your_key_here" } }
);
const { data, pagination } = await res.json();
console.log(`${pagination.total} results`);

LangChain (Python)

Python
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from langchain_anthropic import ChatAnthropic

async def main():
    async with MultiServerMCPClient({
        "thecardapi": {
            "url":       "https://www.thecardapi.com/api/mcp",
            "transport": "streamable_http",
            "headers":   {"Authorization": "Bearer tca_your_key_here"},
        }
    }) as client:
        tools  = await client.get_tools()
        model  = ChatAnthropic(model="claude-sonnet-4-6")
        agent  = create_react_agent(model, tools)
        result = await agent.ainvoke({
            "messages": "Is grading my 1986 Fleer Jordan worth it at $6,500?"
        })
        print(result["messages"][-1].content)

asyncio.run(main())

Ready to start building?

Free tier includes 5,000 sales/day with 3-day lookback. No credit card required.