HobbyCardIndex Card Price API

Card prices at every grade, card ID from a photo, cert lookups and pop reports, for your own business or under a commercial license for your app.

16M+ cards across sports and trading card games. New here? Start with what the API covers, then the reference below.

Which plan do I need?

Your own business

Pricing your own inventory, your own buying and selling, internal tools and reports only your team uses.

Enterprise plan. View pricing

Your users see our data

Apps, websites, widgets and paid products that show our prices, valuations or sales to your customers.

Commercial plan, starting with a free setup month. Sign in to see commercial plans

Getting Started

Authentication

All API endpoints require an API key for authentication. Keys come with the Enterprise plan or a Commercial plan (see which plan do I need).

Authentication Methods:

  • Include the X-API-Key header
  • Use Authorization: Bearer header

X-API-Key Header

Recommended for backend services.

curl 'https://hobbycardindex.com/api/v1/prices/search?q=Mike%20Trout' \ -H 'X-API-Key: hci_live_a1b2c3d4e5f6...'

Bearer Token

Works with Authorization header.

curl 'https://hobbycardindex.com/api/v1/prices/search' \ -H 'Authorization: Bearer hci_live_a1b2c3d4e5f6...'

Get Your API Key: Sign in to your Enterprise or Commercial account and go to Settings, then API Access, then Generate API Key. Save it right away, because it is only shown once. Each account has one active key, and generating a new key replaces the old one.

Rate Limits

Enterprise Tier: 1 request per second per key, up to 100,000 requests a day (the daily count resets at UTC midnight)
Commercial plans: 10, 20 or 50 lookups a second depending on the plan, with a monthly allowance of requests, scans and sale lookups. The free setup month allows 10 lookups a second and 1,000 requests a day. Your plan's numbers are under Settings, then API Access.

Rate Limit Headers:
  • X-RateLimit-Remaining: requests left in the current one-second window
  • X-RateLimit-Reset: when that window resets. The standard RateLimit-* headers are sent as well.

Pricing Data Format

All prices are returned in USD dollars with 2 decimal places. Example: 12.50

Every successful response is wrapped as {"success": true, "data": {...}}. The examples below show what sits inside data.

Supported card grades:

  • loose: raw, ungraded card
  • psa10, psa9, psa8: PSA 10, 9 and 8
  • bgs10: BGS 9.5, or BGS 10 when there is no 9.5 price. cgc10, sgc10: CGC 10 and SGC 10

API Endpoints

GET /api/v1/prices/search

Search card prices by name. A plain q search returns the 10 best matches with all grade prices. Base cards come first, then parallels (a parallel's name shows it in brackets, for example Mike Trout [Gold] #US175), and the most-traded cards first within each. Add any filter below to narrow the search or page through it; filtered searches return 25 a page by default, with hasMore telling you when there's another page. To list every card in one set, pass category and set (a set id from GET /api/v1/sets) with no q. Pass a result's id to the card and sales endpoints below. Every plan.

Query Parameters

Parameter Type Required Description
q string Yes, unless set or added_since is given Search query (2 to 200 characters). Example: "Mike Trout 2011"
category string No A category from /market/categories, for example pokemon or baseball
set string No A set id from /sets, for example pokemon-scarlet-violet-151. Needs category. Results come in card-number order.
language string No The language printed on the card: english, japanese, chinese, korean, german, french, spanish, italian, portuguese, dutch, thai, indonesian, russian or polish
year integer No Card year, for example 2023
added_since date No Only cards added to our catalog since then (UTC): a date like 2026-10-01 or a time like 2026-10-01T12:00:00Z. Results come in the order they were added, and each result's addedAt says when. For a long list, setting added_since to the last addedAt you got is faster than a large offset.
min_price / max_price number No Raw (ungraded) price range in US dollars
sort string No price_asc, price_desc, volume_desc, name_asc, added_asc or added_desc (raw price, trading volume, name, or when the card was added)
limit / offset integer No Page size 1 to 100 (default 25) and how many to skip. A q search pages through its best 2,000 matches; a set, or added_since without q, pages through every card in it.
Example: every English card in one Pokemon set, 100 at a time
curl 'https://hobbycardindex.com/api/v1/prices/search?category=pokemon&set=pokemon-scarlet-violet-151&limit=100&offset=0' -H 'X-API-Key: hci_live_...'
Example: English Pokemon cards added since October 1
curl 'https://hobbycardindex.com/api/v1/prices/search?category=pokemon&language=english&added_since=2026-10-01&limit=100&offset=0' -H 'X-API-Key: hci_live_...'

A search that matches too many cards to sort or filter by price returns 400 SEARCH_TOO_BROAD; add a category, set or year (or a later added_since).

Example Request (cURL)
curl 'https://hobbycardindex.com/api/v1/prices/search?q=Mike%20Trout%202011' \ -H 'X-API-Key: hci_live_...'
Example Request (JavaScript)
fetch('https://hobbycardindex.com/api/v1/prices/search?q=Mike%20Trout%202011', { headers: { 'X-API-Key': 'hci_live_...' } }) .then(r => r.json()) .then(d => console.log(d));
Example Request (Python)
import requests response = requests.get( 'https://hobbycardindex.com/api/v1/prices/search?q=Mike%20Trout', headers={'X-API-Key': 'hci_live_...'} ) print(response.json())
Example Response
{ "results": [ { "id": 12345, "externalId": "67890", "name": "2011 Topps Mike Trout RC #175", "year": 2011, "setName": "Topps", "category": "Baseball", "playerName": "Mike Trout", "prices": { "loose": 4500.00, "psa10": 15000.00, "psa9": 8500.00, "psa8": 4200.00, "bgs10": 12000.00, "cgc10": null, "sgc10": null } } ], "count": 1 }
GET /api/v1/prices/:cardId

Get detailed price information for a specific card across all grades and grading companies.

Path Parameters

Parameter Type Description
cardId integer or string The card's id from search or scan results. To look a card up by its externalId instead, put ext- in front of it, for example ext-67890.
Example Request (cURL)
curl 'https://hobbycardindex.com/api/v1/prices/12345' \ -H 'X-API-Key: hci_live_...'
Example Response
{ "card": { "id": 12345, "externalId": "67890", "name": "2011 Topps Mike Trout RC #175", "year": 2011, "setName": "Topps", "category": "Baseball", "playerName": "Mike Trout", "imageUrl": "https://..." }, "prices": { "loose": 4500.00, "psa10": 15000.00, "psa9": 8500.00, "psa8": 4200.00, "bgs10": 12000.00, "cgc10": null, "sgc10": null, "volume": 34, "updated": "2026-03-20T12:00:00Z" } }
GET /api/v1/prices/:cardId/sales

List verified sold sales for a specific card: individual sale records with price, date, grade and marketplace, plus summary stats for the period. Growth and Platform plans (the free setup month includes 500 lookups to test with); other keys get REQUIRES_COMMERCIAL_LICENSE. Which plan do I need?

Parameters

Parameter Type Description
cardId integer or string The card's id from search or scan results, or ext- plus its externalId.
days integer Lookback window in days, 1–365. Default 90. Each call returns the newest 100 sales in the window, and counts as one sale lookup. Need more sales per call? That's available on a written commercial agreement: email [email protected].
Example Request (cURL)
curl 'https://hobbycardindex.com/api/v1/prices/12345/sales?days=90' \ -H 'X-API-Key: hci_live_...'
Example Response
{ "card": { "id": 12345, "name": "2011 Topps Mike Trout RC #175", "playerName": "Mike Trout" }, "periodDays": 90, "summary": { "count": 14, "average": 2350.75, "low": 1900.00, "high": 2800.00 }, "sales": [ { "soldAt": "2026-06-28T17:04:00Z", "price": 2500.00, "grade": "PSA 10", "gradingCompany": "PSA", "title": "2011 Topps Update Mike Trout RC #US175 PSA 10", "auction": true, "source": "ebay" } ] }
GET /api/v1/certs/:grader/:certNumber

Look up a graded card by its cert number: the grading company's record (grade, autograph grade, player, year, set, card number, population when the grader reports it), the matching card in our catalog, its prices at every grade and the price at that grade. The slab block also links the cert on the grading company's own site (verifyUrl: PSA, CGC and TGA open the cert itself, Beckett and SGC open their lookup page, where the number is entered) and carries the grader's slab photos when the grader has them (frontImageUrl, backImageUrl). Growth and Platform, with a monthly number of cert lookups (Growth 3,000, Platform 50,000, the free setup month 100; the pop + cert add-on raises Growth's number). One lookup per second per key.

Path Parameters

ParameterTypeDescription
graderstringpsa, bgs, cgc or sgc
certNumberstringThe cert number on the slab label, for example 12345678
Example Request (cURL)
curl 'https://hobbycardindex.com/api/v1/certs/psa/12345678' \ -H 'X-API-Key: hci_live_...'
Example Response
{ "cert": { "grader": "PSA", "certNumber": "12345678", "grade": "10", "player": "Mike Trout", "year": "2011", "cardNumber": "US175" }, "card": { "id": 12345, "name": "Mike Trout #US175", "prices": { "loose": 293.00, "psa10": 4500.00 } }, "slab": { "grader": "PSA", "grade": 10, "certVerified": true, "priceAtGrade": 4500.00, "verifyUrl": "https://www.psacard.com/cert/12345678", "frontImageUrl": "https://...", "backImageUrl": "https://..." } }
GET /api/v1/prices/:cardId/history

The card's daily price at every grade over time, for charts. Growth and Platform plans. ?days= from 1 to 3650, default 365. Individual sales are in /sales.

Example Response
{ "card": { "id": 12345, "name": "Mike Trout #US175" }, "days": 365, "history": [ { "date": "2026-10-13", "loose": 293.00, "psa10": 4500.00, "psa9": 1250.00, "volume": 1400 } ] }
GET /api/v1/prices/:cardId/population

Population report for the card: how many copies PSA, BGS, SGC and CGC have graded, grade by grade, with gem counts and gem rate. Platform, or Growth with the pop + cert add-on. population is null when we don't hold a report for that card yet.

Example Response
{ "card": { "id": 12345, "name": "Mike Trout #US175" }, "population": { "totalPopulation": 41000, "totalGems": 9800, "graders": { "PSA": { "totalGraded": 35000, "gems": 8200, "gemRate": 23.4 } } } }
GET /api/v1/sets

Every set in a category, with the set id that search (set=) and the watch list take, the language printed on its cards, its year and how many cards it has. Query: category (required), language, q (part of the set name), limit (up to 5,000, default 1,000), offset. Every plan.

Example Request
curl 'https://hobbycardindex.com/api/v1/sets?category=pokemon&language=english&q=151' -H 'X-API-Key: hci_live_...'
Example Response
{ "category": "pokemon", "language": "english", "total": 1, "sets": [ { "id": "pokemon-scarlet-violet-151", "name": "Pokemon Scarlet & Violet 151", "language": "english", "year": 2023, "cards": 409 } ] }
POST /api/v1/watchlist

Choose the cards you want price updates for. Growth (up to 100,000 cards) and Platform (up to 500,000); the free setup month can watch 1,000. Send {"cardIds": [12345, 67890]} (up to 1,000 ids a call; a search id, or ext- plus an externalId), or a whole set: {"category": "pokemon", "set": "pokemon-scarlet-violet-151", "language": "english"}, or a whole category: {"category": "pokemon", "language": "english"}. A set or category added this way stays followed: cards we add to it later join your list on their own and come in the daily webhook as cards.added. The response says how many were added, how many were already on the list, any ids we couldn't find, and your total.

GET /api/v1/watchlist lists the watched cards (limit up to 5,000, offset) and the sets you follow (following). POST /api/v1/watchlist/remove with {"cardIds": [...]} takes cards off, {"category": "pokemon", "set": "pokemon-scarlet-violet-151", "language": "english"} takes that set's cards off and stops following it (send the same fields you added it with), or {"all": true} clears the list.

GET /api/v1/watchlist/prices downloads the current prices of every watched card in one call (JSON, or ?format=csv). Prices change once a day, so once a day is plenty; one download a minute at most.

PUT /api/v1/webhook

Get a call at your own server once a day with every watched card whose price changed. Send {"url": "https://your-server.example/hci-prices", "hour": 14}: an https address and the hour (UTC, 0 to 23) you want it sent. The first time, the response includes your signing secret; it's shown only then, so save it (send "rotateSecret": true for a new one). Webhook deliveries don't count against your requests.

Once a day at that hour we compare every watched card with the prices we last sent you and POST the changes, up to 1,000 cards a request. Each card lists the grades that changed with the previous and new price (null means no price), plus its current prices. Answer with any 2xx. If your server doesn't, we retry after 5 minutes, 30 minutes, 2, 6 and 12 hours; after 3 deliveries fail every retry the webhook pauses, and setting the URL again turns it back on. GET /api/v1/webhook shows the URL, hour and last delivery; DELETE /api/v1/webhook stops it; POST /api/v1/webhook/test sends a signed ping right away.

Example delivery
POST https://your-server.example/hci-prices HCI-Event-Type: prices.changed HCI-Signature: t=1792065604,v1=5f2c... { "id": "evt_8c1f...", "type": "prices.changed", "created": "2026-10-16T14:07:12Z", "data": { "day": "2026-10-16", "cards": [ { "id": 12345, "name": "Charizard ex #199", "setName": "Pokemon Scarlet & Violet 151", "cardNumber": "199", "changes": [ { "grade": "psa10", "previous": 410.00, "price": 435.00 } ], "prices": { "loose": 92.50, "psa10": 435.00, "psa9": 140.00 }, "updated": "2026-10-16T09:12:44Z" } ] } }
Checking the signature

HCI-Signature is t=<unix time>,v1=<hex>, where the hex is the HMAC-SHA256 of <t>.<raw request body> with your secret. Compute it over the raw body and compare; reject old t values to stop replays. Grades are loose (raw), psa10, psa9, psa8, psa7, bgs10 (BGS 9.5), cgc10 and sgc10.

New cards in the sets you follow

A whole set or category added to the watch list stays followed. Each day, before the price changes, any card we've added to it since the day before goes onto your list and comes in its own cards.added event, up to 1,000 cards a request, with the set it joined (following), its prices if it has any yet, and when it was added. New cards count toward your watch limit; any that don't fit are counted in notAdded. With no webhook set, they still join the list.

HCI-Event-Type: cards.added { "id": "evt_41d0...", "type": "cards.added", "created": "2026-10-20T14:07:03Z", "data": { "day": "2026-10-20", "following": { "category": "pokemon", "set": "pokemon-scarlet-violet-151", "language": "english" }, "cards": [ { "id": 12345, "name": "Charizard ex #199", "setName": "Pokemon Scarlet & Violet 151", "setId": "pokemon-scarlet-violet-151", "cardNumber": "199", "language": "english", "prices": { "loose": 92.50, "psa10": 435.00 }, "addedAt": "2026-10-19T14:02:11Z" } ] } }
GET /api/v1/collection

Retrieve your portfolio collection with current values and performance metrics.

Example Request (cURL)
curl 'https://hobbycardindex.com/api/v1/collection' \ -H 'X-API-Key: hci_live_...'
Example Response
{ "items": [ { "id": 1, "cardId": 12345, "name": "2011 Topps Mike Trout RC #175", "grade": "PSA 9", "quantity": 1, "purchasePrice": 1200.00, "notes": "Great condition", "addedAt": "2025-06-15T10:30:00Z" } ], "total": 1 }
GET /api/v1/market/movers

Get top market movers by category. Shows cards with the highest average sale prices in recent period.

Query Parameters

Parameter Type Default Description
category string baseball Card category: baseball, football, basketball, hockey, pokemon, etc.
period integer 7 Days of history to analyze (1-90)
Example Request (cURL)
curl 'https://hobbycardindex.com/api/v1/market/movers?category=baseball&period=7' \ -H 'X-API-Key: hci_live_...'
Example Response
{ "movers": [ { "id": 54321, "name": "1989 Upper Deck Ken Griffey Jr RC", "playerName": "Ken Griffey Jr", "category": "Baseball", "recentSales": 12, "averagePrice": 2300.00, "period": "7 days" } ], "period": "7 days", "category": "baseball" }
GET /api/v1/market/categories

Get statistics for all card categories. Useful for market analysis and portfolio segmentation.

Example Request (cURL)
curl 'https://hobbycardindex.com/api/v1/market/categories' \ -H 'X-API-Key: hci_live_...'
Example Response
{ "categories": [ { "category": "Baseball", "cardCount": 2100000, "averagePriceLoose": 45.50, "averagePricePsa10": 320.75, "lastSaleAt": "2026-03-20T14:32:00Z" } ], "total": 8 }

Use it from an AI assistant (MCP)

HobbyCardIndex runs an MCP server, so AI assistants that speak the Model Context Protocol, like Claude Code and Cursor, can look up card prices with your API key. It is listed in the official MCP Registry as com.hobbycardindex/card-prices.

Server URL: https://hobbycardindex.com/api/v1/mcp (Streamable HTTP). Send your key as the X-API-Key header, or as Authorization: Bearer. It is the same key as the REST API, with the same plan and the same limits, and every lookup counts toward your usage.

Tools:

  • search_cards: finds a card the way a collector describes it (year, set, player or character, card number). Up to 5 matches, each with its raw and PSA 10 price and a link to the card page.
  • get_card_prices: one card at every grade: raw, PSA 7 to PSA 10, BGS 9.5, CGC 10 and SGC 10.

Claude Code

claude mcp add --transport http hobbycardindex https://hobbycardindex.com/api/v1/mcp --header "X-API-Key: hci_live_..."

Cursor (.cursor/mcp.json)

{ "mcpServers": { "hobbycardindex": { "url": "https://hobbycardindex.com/api/v1/mcp", "headers": { "X-API-Key": "hci_live_..." } } } }

In VS Code the same entry goes in .vscode/mcp.json under "servers", with "type": "http" added.

Without a key, an assistant can connect and see the two tools, but every lookup answers with a note to get a key. To get one, see which plan do I need.

Error Handling

All error responses follow a consistent JSON structure:

{ "error": { "code": "ERROR_CODE", "message": "Human-readable error description" } }

Common Error Codes

Code HTTP Status Description
MISSING_API_KEY 401 API key not provided in X-API-Key header or Authorization header
INVALID_API_KEY 401 API key is invalid or malformed
KEY_REVOKED 401 API key has been revoked
PLAN_REQUIRED 403 The account's Dealer or Enterprise plan has ended; the key works again once the plan is renewed
REQUIRES_COMMERCIAL_LICENSE 403 The endpoint (individual sold-sale records) is only available under a Commercial Data License. See Data use below.
RATE_LIMITED 429 More than 1 request per second on one key. Wait a second and retry.
RATE_LIMIT_EXCEEDED 429 The key reached its daily ceiling of 100,000 requests. Resets at UTC midnight.
VALIDATION_ERROR 400 A required query parameter is missing or invalid, for example q on search
NOT_FOUND 404 No card with that ID
SERVER_ERROR 500 Internal server error. Try again later.

Pricing & Tiers

Enterprise, for your own business

API access for internal use is included with the Enterprise plan, $49.99 a month or $499.99 a year, which also includes 30,000 card scans a month. Each key can make 1 request per second, up to 100,000 requests a day.

Included with Enterprise:

  • One API key per account
  • 1 request per second, up to 100,000 a day
  • Access to all pricing data (16M+ cards)
  • Market movers and category analytics
  • Portfolio API endpoints
  • Priority support
  • Licensed for your own internal use (see Data use below)

Don't have Enterprise? View pricing plans →

Commercial plans, for apps and websites

If your customers or the public will see our data, you need a Commercial plan instead. A new account can start with a free setup month to build and test against real data, and an Enterprise payment already made comes off the first commercial invoice.

Sign in to see commercial plans and start a setup month →

Data use & commercial license

What your Enterprise plan covers

Your API key licenses HobbyCardIndex data for your own business's internal use: pricing your own inventory, your own buying and selling decisions, internal reports, and tools only your team uses. Setting your own price on a card using our data counts as internal use.

What needs a Commercial Data License

  • Showing our prices, sold sales or market data to your customers or the public: a website, app, marketplace, widget, stream overlay, point-of-sale screen or printed price list
  • Reselling, sublicensing or redistributing the data, including API responses and bulk price files
  • Building or training a price guide, database or AI model from the data that you sell or offer to others
  • Including the data in a product or service you charge for
  • Sharing your API key, exports or bulk files with another company
  • Individual sold-sale records from /prices/:cardId/sales: this endpoint is only switched on for commercial plans that include it, one card per call with a monthly cap. No plan includes a bulk download of sold sales.

How to get started

Sign in and open Settings, API Access to see the commercial plans, start a free setup month, or buy Starter or Growth by card. Questions, or a bigger deal in mind? Email [email protected] with the subject "Commercial Data License". Commercial plans ask for one credit to Hobby Card Index with a link, either beside the prices or on your website. Until you're on one, an Enterprise plan covers internal use only.

When your plan ends

Stop using the data and delete stored copies, including cached API responses, sold-sale records and bulk files, within 30 days.

Full terms: Terms of Service, section 8.

Common questions

Is there an API for sports card prices?

Yes. The HobbyCardIndex REST API returns current prices by grade: raw, PSA 10, 9 and 8, BGS 9.5, CGC 10 and SGC 10. It also returns real sold-sale history and market data, for baseball, football, basketball, hockey, soccer, Pokémon, Magic: The Gathering, Yu-Gi-Oh, One Piece and more than 25 categories in all.

How much does the HobbyCardIndex API cost?

For your own business's internal use, API access is included with the Enterprise plan: $49.99 a month or $499.99 a year, which also includes 30,000 card scans a month, at 1 request per second up to 100,000 requests a day. Apps and websites that show our data to their users need a Commercial plan, which starts with a free setup month. Sign in to see commercial plans.

How many cards does the API cover?

The catalog holds more than 16 million card records, with more than 22 million prices across grades and more than 8 million card images.

Can I identify a card from a photo with the API?

Photo identification runs our own scanner, which matches a card photo against more than 8 million card images. It is not in the public API documentation yet. To use it in your product, email [email protected].

How current are the prices?

Prices are based on real sold prices, and each card's price record includes the time it was last updated.

Can I show HobbyCardIndex prices on my own website or app?

Yes, with a Commercial plan. An Enterprise key licenses the data for your own business's internal use, like pricing your own inventory. Showing our prices to your customers or the public, or building them into a product you offer others, needs a Commercial plan, which starts with a free setup month. Sign in to see commercial plans, or read data use.

How do I get an API key?

Subscribe to the Enterprise plan, then generate a key in Settings under API Access. Each account has one active key, and generating a new one replaces the old one.

Does HobbyCardIndex have an MCP server?

Yes. AI assistants that speak the Model Context Protocol, such as Claude Code and Cursor, can connect to https://hobbycardindex.com/api/v1/mcp and look up card prices with a HobbyCardIndex API key, sent as the X-API-Key header. It has two tools, search_cards and get_card_prices, and is listed in the official MCP Registry as com.hobbycardindex/card-prices. How to connect.

Ready to integrate?

Get your API key and start building with HobbyCardIndex pricing data.

Create Free Account