Developer reference

One API. A complete data foundation.

Query verified marketplace sales alongside a structured card catalog for identity, discovery, pricing, and market intelligence. The same account powers REST and AI-native MCP 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-06-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-06-14",
      "sold_at":               "2026-06-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-06-01",
    "coverage_date_to":    "2026-06-17",
    "platforms_covered":   ["eBay", "Goldin", "Lelands", "SCP Auctions", "Hakes", "REA", "TCGplayer"],
    "generated_at":        "2026-06-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 newly indexed card sales without polling. Sales are delivered as signed batches of up to 1,000 records. Delivery is at-least-once: the cursor advances only after every active endpoint accepts a batch, and a retry may deliver the same sale again. Deduplicate on sale id.

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 contains a batch with the core sale fields from GET /v1/market/sales plus delivery fields such as pg_id.

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",
      "pg_id":        123456789,
      "title":        "2011 Topps Update Mike Trout RC BGS 9.5",
      "price":        4200.00,
      "listing_type": "auction",
      "sold_at":      "2026-06-20T06:00:24Z",
      ...
    }
  ]
}

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 latencyPolled every 30 seconds after indexing; backlog and endpoint retries can add delay
Batch sizeUp to 1,000 records per delivery
Retry policy3 attempts with backoff (0s / 5s / 30s). Cursor holds on all-fail.
Delivery contractAt-least-once. Deduplicate on sale id.
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.6M+ cards across 332,000+ sets and 15 sports, with checklists, rookie/auto/relic flags, and print runs. Relationship and checklist coverage varies by release. 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, URID, CFID, UPID

Catalog uses stable typed identifiers in one format, like UC-1KJZD-TZG7C-6: UC- for cards (ucid), US- for individual sets and checklists (usid), UR- for releases (urid), CF- for corresponding card families (cfid), and UP- for subjects (upid). Where an existing UF-family anchor maps one-to-one to a release, it remains a permanent lookup alias for that release's UR- ID. 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; issued IDs remain stable. Key your integration on issued IDs.

  • • Use each ID with its documented route or filter: /catalog/{ucid}, set_id=US-..., or player_id=UP-....
  • • 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

Releases, checklists, and card families

A Release (URID) identifies one Catalog release anchor. A checklist (USID), also called a Catalog Set, is one named card list within that release. Each exact catalog card or version has its own UCID; a UCID does not identify an individual physical copy. An unlinked checklist remains available by USID and is absent from Release membership results.

Conceptual example · one Catalog release · URID

Card family
Base · USID
Silver · USID
Gold · USID
CFID 1 · #27
UCID A
UCID B
UCID C
CFID 2 · #28
UCID D
UCID E
UCID F

Whole table = Release · Column = checklist · Row = card family · Cell = exact catalog card. Labels are illustrative and appear only when reviewed.

A CFID groups corresponding exact catalog cards within the same Release/design boundary. For exact-correspondence coverage inferred from checklists, complete normalized card-number and subject rosters must match. Reviewed families are accepted separately. Each card keeps its own UCID. The relationship subtype is separate: an exact correspondence does not by itself mean parallel. Parallel, image variation, promo, and proof labels appear only where reviewed; unclassified members return version_subtype=unknown and subtype_classification_state=unknown. This leaves the correspondence usable without assigning an unproved version label.

Find a card's corresponding versions

Start with GET /catalog/{ucid}. The card detail includes release_id, card_family_id, and card_family_href. Follow the family link to retrieve the member UCIDs and each member's version subtype. Each UCID still identifies a separate exact card.

Family coverage is partial. A null family ID and link mean that the card has no published family assignment; they do not establish that no other versions exist. An unresolved release is also null. These Catalog relationships do not by themselves identify the exact version offered in a sale or establish a Sales-to-UCID match.

Example — 2025 Bowman #17

The 2025 Bowman Shohei Ohtani #17 card has its own UCID. Its published CFID connects 23 corresponding cards across checklists in the same release and design component. Each member keeps a separate UCID and USID. The correspondence is established; the version subtype of these members remains unknown unless separately reviewed.

bash
# Look up the exact card, then follow its card_family_href
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/catalog/UC-J8FJK-CEYCS-P"

# This family's 23 corresponding card versions
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/catalog/card-families/CF-GXXDT-9630C-7"

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), with release and CFID links when assigned
/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/familiesLegacy direct-parent family index — UFID, name, year, and direct member count
/catalog/releasesBrowse releases by name, sport, or year
/catalog/releases/{id}Resolve a release URID or its mapped durable UFID alias
/catalog/releases/{id}/checklistsList that release's main, insert, and version checklists
/catalog/card-familiesBrowse CFIDs within one release; requires release_id
/catalog/card-families/{id}One CFID with its member UCIDs and known version subtypes
/catalog/subjectsBroad Catalog subject search — athletes, teams, characters, and other depicted subjects
/catalog/playersLegacy alias of /catalog/subjects — retained for compatibility
/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 UFIDoptionalLegacy filter for the currently linked root checklist and its direct children. Use release_id to browse the release's assigned checklists.
release_idURID or UFIDoptionalReturn checklists in the specified release anchor; a mapped UFID alias resolves to the same release.
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.
release_idURID or UFIDoptionalFilter to cards in a release, using its URID or mapped legacy UFID alias.
card_family_idCFIDoptionalFilter to the exact cards in one published card family.
player_idUPIDoptionalDiscover Catalog cards associated with a subject UPID. These source-backed subject links have partial coverage; they are not reviewed exact-athlete or Sales attributions.
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 }
}

Existing set responses retain parent product context through parent_set_name and parent_set_usid. A parent link alone does not establish whether the checklist is an insert, parallel, or another version. Serial-numbered cards expose print_run when known.

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
EnterpriseCustom — defined in Order Form

Higher catalog allowances and bulk licensing are scoped under an Enterprise agreement — reach out to discuss.

Players

Players API

Search a reviewed cohort of 2,317 athlete identities, open a profile by stable UPID, browse Catalog cards linked to that subject, and read timestamped bookmaker prices for reviewed player-award futures. Coverage is intentionally partial. A missing Players result does not mean the athlete or their cards are missing from the broader Catalog.

For broad subject discovery, use /catalog/subjects. It includes athletes, teams, characters, and other depicted subjects; /catalog/players remains an equivalent legacy alias. A Catalog subject is not automatically a reviewed athlete profile.

Endpoints

Base URL: https://www.thecardapi.com/api/v1/players — use the same x-api-key header and Catalog entitlement.

GET /players?search=Prefix-search reviewed athlete names
GET /players/{upid}Reviewed athlete profile with Catalog and futures section links
GET /players/{upid}/cardsPaginated Catalog cards linked to the subject UPID
GET /players/{upid}/futuresReviewed player-award futures prices from a local snapshot

Search parameters — GET /players

searchstringrequiredCase-insensitive start-of-name search (2–100 characters), or an exact UPID.
sportstringoptionalOptional reviewed primary-sport filter.
pageintegeroptionalPage number. Default: 1.
limitintegeroptionalResults per page. Default: 20; maximum: 50.

Results are ordered by canonical name, then UPID. Responses include deterministic pagination,schema_version, an opaque data_revision, and ISO data_as_of. Coverage is partial: a missing result does not establish that the athlete is absent from Catalog.

Profile and discovery fields

upidstringoptionalStable player identifier.
canonical_namestringoptionalReviewed athlete display name.
primary_sportstringoptionalReviewed primary sport.
primary_leaguestringoptionalReviewed league, normalized separately from primary sport.
schema_version / data_revision / data_as_ofstring / string / ISO date or datetimeoptionalPublic schema version, opaque dataset revision, and data freshness.
catalog.card_countintegeroptionalCards linked to this Catalog subject.
catalog.first_card_year / last_card_yearinteger|nulloptionalObserved linked-card year range; this is not the athlete's career range.
sections.cards / sections.futuresobjectoptionalStatus and link for each separately permissioned section.

The profile links to available sections. It does not claim a current team, upcoming game, career status, confirmed honor, or player statistics.

Card links and allowance

/players/{upid}/cards uses the same query, ordering, pagination, and accounting as /catalog?player_id=. Card-link coverage is partial. These links support Catalog discovery; they are not independently reviewed exact-athlete card attribution and do not assert a match to any Sales API record.

Players requests use the existing Catalog daily record pool and return its standard quota headers. Search and card-list requests charge records returned, with a minimum of one record per request; a profile request charges one. Unknown or unreviewed UPIDs return 404. A registry or Catalog identity mismatch fails closed with 503.

Build a player card browser

Search for a reviewed athlete, use the returned UPID to open the small discovery profile, then follow its cards section to browse linked Catalog cards. The profile's card count and card-year range describe Catalog links, not playing-career statistics.

bash
# Search by name; exact UPID lookup also works in search=
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/players?search=Shohei"

# Open the reviewed profile and its section links
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/players/UP-CRNYZ-EV13Q-8"

# Browse the first five linked Catalog cards
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/players/UP-CRNYZ-EV13Q-8/cards?limit=5"

Selected fields from the held Shohei Ohtani profile, using a Builder or higher key:

JSON
{
  "upid": "UP-CRNYZ-EV13Q-8",
  "canonical_name": "Shohei Ohtani",
  "primary_sport": "baseball",
  "primary_league": "MLB",
  "catalog": {"card_count": 18822},
  "sections": {
    "cards": {"status": "partial", "href": "/api/v1/players/UP-CRNYZ-EV13Q-8/cards"},
    "futures": {"status": "available", "href": "/api/v1/players/UP-CRNYZ-EV13Q-8/futures"}
  },
  "schema_version": "2026-10-09",
  "data_revision": "players-2026-10-08.1",
  "data_as_of": "2026-10-08"
}

Player-award futures — Builder, Pro, and Enterprise

GET /players/{upid}/futures returns reviewed player-award market prices from local snapshots. Current coverage is 781 players and 2,253 player-award quote links across MLB, NFL, NBA, and NHL; a player can have no matched quotes. Coverage is partial. Starter keys are denied, including those with Catalog access. Eligible requests use the normal Catalog daily allowance: each response charges its returned rows, with a minimum of one record, and returns X-RateLimit-Limit and, for capped plans, X-RateLimit-Remaining from the Catalog pool. Customer requests read our saved snapshot; they do not contact the quote source live.

Player futures market data powered by PropLine; prices originate from the named bookmakers. The response identifies PropLine once as thedata_provider, while each quote identifies its bookmaker.

data_providerstringoptionalMarket-data provider for this futures response: PropLine.
marketobjectoptionalNormalized key, readable name, scope=player_award, and reviewed league.
bookmaker / outcome_namestring / stringoptionalBookmaker and priced player outcome.
price_american / price_decimalinteger|null / number|nulloptionalBookmaker market price in American and decimal formats; null when unpriced.
source_updated_at / observed_atdatetime|null / datetimeoptionalSource quote time and our capture time, so clients can assess age.
statestringoptionalquoted, stale, unpriced, settled, or unknown_freshness when source time is unavailable.
seven_day_baselineobject|nulloptionalActual same-book quote near seven days earlier; null until comparable history exists.
raw_implied_probability_change_ppnumber|nulloptionalChange in raw implied probability, in percentage points; bookmaker margin is not removed.

We plan one four-sport snapshot per day at launch. Seven-day movement is unavailable at launch and appears only after actual captures provide a comparable same-book baseline. These are market prices, not confirmed honors, predictions, fair probabilities, or card valuations. Player statistics, upcoming games, current team, team futures, and game lines are outside this release.

Compare books on one award

Fetch a reviewed player's futures and compare rows with the same market key and league. The captured Connor McDavid Hart Trophy rows below illustrate why each book's source time matters: one line is stale while another is freshly quoted. This is a selected-fields excerpt from the held snapshot, not a current price or a complete API response.

bash
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/players/UP-KYTXM-Z8Z5H-P/futures"
JSON
{
  "upid": "UP-KYTXM-Z8Z5H-P",
  "data_provider": "PropLine",
  "quotes": [
  {
    "market": {"key": "hart_trophy_winner", "name": "2026-27 Hart Trophy Winner", "scope": "player_award", "league": "NHL"},
    "bookmaker": "fanduel", "outcome_name": "Connor McDavid",
    "price_american": 250, "price_decimal": 3.5,
    "source_updated_at": "2026-10-01T22:02:41Z",
    "observed_at": "2026-10-08T23:53:37Z", "state": "stale",
    "seven_day_baseline": null, "raw_implied_probability_change_pp": null
  },
  {
    "market": {"key": "hart_trophy_winner", "name": "NHL Awards 2026/27 - Hart Trophy Winner", "scope": "player_award", "league": "NHL"},
    "bookmaker": "draftkings", "outcome_name": "Connor McDavid",
    "price_american": 300, "price_decimal": 4.0,
    "source_updated_at": "2026-10-08T23:52:36Z",
    "observed_at": "2026-10-08T23:53:37Z", "state": "quoted",
    "seven_day_baseline": null, "raw_implied_probability_change_pp": null
  }
  ]
}

Watch real seven-day movement

A watchlist can alert only when raw_implied_probability_change_pp is non-null and crosses a threshold you choose, comparing the same bookmaker and outcome with an actual earlier snapshot. At launch this field and seven_day_baseline are null; do not treat missing history as zero movement. A player with no accepted quote returns an emptyquotes: [] with partial coverage; that does not prove no market exists. A stale or unpriced row keeps its explicit state.

bash
# Read the saved award prices once a real seven-day baseline exists
curl -H "x-api-key: $KEY" \
  "https://www.thecardapi.com/api/v1/players/UP-KYTXM-Z8Z5H-P/futures"

A player without an accepted award link has an empty quote list:

JSON
{
  "quotes": [],
  "coverage": {"status": "partial", "scope": "reviewed_player_awards"}
}

Rate

Rate limits

Sales requests are limited by rows returned per UTC day and how far back you can query. Catalog and Players share a separate daily Catalog record allowance.

Lookback controls how far back your key may query. Daily allowance controls how many rows may be returned. Neither is a completeness guarantee. Platform availability and reporting time vary; see Status for current freshness.

PlanDaily sales-row allowanceLookback
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

Unlimited Lookback ($99/mo) — removes the 90-day rolling window. Query all history currently available in the product.

Unlimited Lookback requires 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-06-16",
      "sold_at":      "2026-06-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-07-03", "to": "2026-07-16",
    "sales": 42, "avg_price": 1380
  },
  "prior_window": {
    "from": "2026-06-19", "to": "2026-07-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-06-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 rows returned per UTC day with 3-day lookback. No credit card required.