NeoSatoshi Card Recognition
Identify trading and football cards in a photo, search and correct them, with Cardmarket prices.
Install
Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).
neosatoshi-card-mcpclaude mcp add neosatoshi-card-recognition -e 'NEOSATOSHI_API_KEY=<your neosatoshi_api_key>' -e 'NEOSATOSHI_ALLOWED_DIRS=<NEOSATOSHI_ALLOWED_DIRS>' -- npx -y neosatoshi-card-mcp@0.6.0
{
"mcpServers": {
"neosatoshi-card-recognition": {
"command": "npx",
"args": [
"-y",
"neosatoshi-card-mcp@0.6.0"
],
"env": {
"NEOSATOSHI_API_KEY": "<your neosatoshi_api_key>",
"NEOSATOSHI_ALLOWED_DIRS": "<NEOSATOSHI_ALLOWED_DIRS>"
}
}
}
}
Environment variables
NEOSATOSHI_API_KEYsecret Your NeoSatoshi API key. Optional: without it the public demo key is used (10 detections per day per network).NEOSATOSHI_ALLOWED_DIRSOptional list of directories the server may read images from, separated by ':' (';' on Windows). Unset means any readable image file.
Documentation
From the project's README on GitHub, refreshed weekly. Links open on GitHub.
Contents
- NeoSatoshi Card Recognition API
- Quickstart (30 seconds)
- Streaming
- Supported games and languages
- Request
- Response
- Errors
- Limits and pricing
- Card search and corrections
- When to use this instead of a general vision LLM
- MCP server (Claude, Cursor, Codex and other AI assistants)
- Repository layout
- FAQ
- License and terms
NeoSatoshi Card Recognition API
Card recognition API for Pokémon, One Piece, Disney Lorcana, Yu-Gi-Oh!, Magic: The Gathering, Star Wars: Unlimited and football (soccer) cards: send a photo, get JSON with set, number, rarity, Cardmarket ID and daily prices (football: player, release and parallel).
This repository contains runnable examples (curl, Python, Node.js), an MCP server for AI assistants, a Postman collection and a copy of the OpenAPI spec. The API is run by NeoSatoshi (NeoGen Solutions GmbH, Bern, Switzerland).
- Endpoint:
POST https://europe-west1-neosatoshi.cloudfunctions.net/detectApi - Auth:
Authorization: Bearer <API_KEY> - Public demo key (no signup, 10 detections per day per network):
nsk_live_9c253fbed995042a98280ad13e5c47d3c4a59c9b4f0f7274 - Docs: https://neosatoshi.com/developers/detection-api
- Card search and corrections:
POST .../cardsApi/search,POST .../cardsApi/corrections,GET .../cardsApi/cards/{tcg}/{card_id}(see Card search and corrections) - OpenAPI: openapi.json (also at https://neosatoshi.com/openapi.json)
Quickstart (30 seconds)
From a clone of this repository (or use any photo of a card). Works on macOS and Linux as is:
{ printf '{"tcg":"pokemon","image":"'; base64 < examples/images/example_api_single.jpg | tr -d '\n'; printf '"}'; } > body.json
curl -sS --max-time 120 -X POST https://europe-west1-neosatoshi.cloudfunctions.net/detectApi \
-H "Authorization: Bearer nsk_live_9c253fbed995042a98280ad13e5c47d3c4a59c9b4f0f7274" \
-H "Content-Type: application/json" \
--data-binary @body.json
One card usually takes about 4 seconds, about 10 to 12 seconds on the first request after a quiet period; a binder page with 9 cards about 17 seconds. Keep the client timeout at 120 seconds or more.
Ready-made scripts that print a per-card summary:
./examples/curl/detect.sh examples/images/example_api_single.jpg
python3 examples/python/detect.py examples/images/example_api_lorcana_de.jpg lorcana
node examples/node/detect.mjs examples/images/example_api_multi.jpg pokemon --json
All scripts read NEOSATOSHI_API_KEY and fall back to the demo key.
Streaming
Add "stream": true to get Server-Sent Events: each card as soon as it is recognised, then the same result as a normal call, at the same cost. On a Pokémon binder page with 9 cards the first card came after about 6 seconds and all nine after about 17. Football (one card per photo) gets progress events, not a faster result.
./examples/curl/stream.sh https://neosatoshi.com/static/images/example_api_multi.jpg
python3 examples/python/stream.py https://neosatoshi.com/static/images/example_api_multi.jpg
node examples/node/stream.mjs https://neosatoshi.com/static/images/example_api_lorcana.jpg lorcana
Events: accepted, progress, card (index, card), then exactly one of done (same body as the JSON response) or error (status, error, message). A : keep-alive comment comes every 15 seconds. Errors before the detection starts stay plain JSON with their status code. A stream cannot be resumed: a reconnect is a new, charged detection. Details: documentation.
Supported games and languages
| Game | tcg value | Card languages | Cards per photo |
|---|---|---|---|
| Pokémon TCG | pokemon (default) | English, German, French, Italian, Spanish, Japanese, Korean, Simplified Chinese | Several (binder pages work, best up to about 20) |
| One Piece Card Game | onepiece | English, Japanese | Several (binder pages work) |
| Disney Lorcana | lorcana | English, German, French, Italian | Several (binder pages work) |
| Yu-Gi-Oh! TCG (not OCG) | yugioh | English, German, French, Italian, Spanish, Portuguese | Several (binder pages work) |
| Magic: The Gathering | magic | English, German, French, Italian, Spanish | Several (binder pages work) |
| Star Wars: Unlimited | star_wars_unlimited | English, German, French, Italian, Spanish | Several (binder pages work) |
| Football (soccer) cards: Topps, Panini and other makers | football | (no card language) | One, the card front, at most 4 MB |
Request
POST with a JSON body:
{ "image": "<base64 JPEG or PNG, max 12 MB>", "tcg": "pokemon" }
| Field | Required | Description |
|---|---|---|
image | one of image or image_url | Base64 encoded JPEG or PNG, at most 12 MB (football: 4 MB). |
image_url | one of image or image_url | Public https:// URL of a JPEG or PNG, at most 12 MB, fetched by the API. Alternative to image. |
tcg | no | pokemon (default), onepiece, lorcana, yugioh, magic, star_wars_unlimited or football. |
stream | no | true for Server-Sent Events, see Streaming. The header Accept: text/event-stream does the same. |
Response
Recorded response for examples/images/example_api_single.jpg (Pokémon, Cinderace ex):
{
"request_id": "req_adec9d16-14dc-40a8-9d8c-2df9de2ad3a5",
"count": 1,
"cards": [
{
"name": "Cinderace ex",
"name_ja": null,
"name_ko": null,
"name_zh": null,
"language": "en",
"set": { "id": "svp", "ptcgo_code": "PR-SV", "name": "Scarlet & Violet Black Star Promos", "number": "163" },
"rarity": "Promo",
"cardmarket_id": 796939,
"reverse_holo": false,
"first_edition": false,
"special_variant": null,
"prices": { "cardmarket": { "trend": 3.59, "low": 1, "avg7": 3.71, "avg30": 4.1, "currency": "EUR" } },
"images": {
"small": "https://d1m1gwcombja0u.cloudfront.net/public_images/cards_en/svp/svp-163_small.webp",
"large": "https://d1m1gwcombja0u.cloudfront.net/public_images/cards_en/svp/svp-163.png"
},
"confidence": 0.85,
"image_quality": { "blurred": false, "unusual_dimensions": false },
"alternatives": [
{ "name": "Cinderace ex", "set": "Stellar Crown", "number": "28", "score": 1, "possible_variants": null }
]
}
],
"demo": { "limit_per_day": 10, "remaining_today": 9 }
}
(Shortened; the full recording is in examples/responses/pokemon_single_cinderace.json.) For the 9 card binder page in example_api_multi.jpg the API returns every card in cards, one entry each.
More recordings:
- lorcana_en_genie.json: Disney Lorcana, English, photo example_api_lorcana.jpg
- lorcana_de_blume.json: Disney Lorcana, German (foil detected), photo example_api_lorcana_de.jpg
- football_beste_autograph.json: football, a numbered autograph (Gold Wave Refractor 37/50), shortened to two of its four alternatives
- catalog-shape/: One Piece, Lorcana, Yu-Gi-Oh! and Magic responses recorded from clean catalog images, included only to show the response shape per game (the catalog images themselves are not in this repo)
Fields shared by every game
| Field | Description |
|---|---|
request_id | ID of the request, quote it in support questions. |
count | Number of detected cards. |
cards[] | One object per detected card (see below). |
credits.remaining | Remaining card scans of your key's plan (own keys only). |
demo | Public demo key only, instead of credits: limit_per_day and remaining_today for your network. |
name | Card name as printed. |
language | Card language: en, de, fr, it, es, pt, ja, ko, zh. |
tcg | onepiece, lorcana, yugioh, magic, star_wars_unlimited or football. Absent for Pokémon. |
set | id, ptcgo_code (Pokémon, One Piece, Lorcana) or code (Yu-Gi-Oh!, Magic), name, number. |
rarity | Rarity as named by the game. |
cardmarket_id | Cardmarket product ID, or null. |
prices.cardmarket | trend, low, avg7, avg30 in EUR, updated daily. |
prices.tcgplayer | TCGplayer prices in USD, where available. |
images | small and large reference image URLs. |
confidence | 0 to 1. |
image_quality | blurred, unusual_dimensions (booleans about the photo). |
alternatives[] | Other likely matches for a manual pick. The shape differs per game. |
A missing price is null, never 0.
Game-specific fields
| Game | Fields |
|---|---|
| Pokémon | name_ja, name_ko, name_zh, reverse_holo, first_edition, special_variant |
| One Piece | card_id (for example OP01-003) |
| Disney Lorcana | version, full_name, name_en, prices.cardmarket_foil |
| Yu-Gi-Oh! TCG | passcode, set.code, edition, price_versions, *_unconfirmed flags (for example print_unconfirmed: the exact print could not be confirmed, check alternatives) |
| Magic: The Gathering | printing (set.code, set.number, print_unconfirmed), prices.cardmarket_foil, TCGplayer foil price (prices.tcgplayer.foil) |
| Football | card_id (fb-...), season, subset, teams, rookie, card_type, sure, score, parallel (pick only when the recognizer is sure, candidates with print_run, serial_number). prices and images are null: no football prices in the API yet, and no reference images. |
Graded slabs: a photo of a PSA, BGS or CGC slab (football also SGC) fills graded with the grader, the grade and the cert number as read from the label; null for a raw card. The grade is never estimated.
The full field reference is in the documentation.
Errors
Errors return JSON with error (a code) and usually message.
| HTTP | error | Meaning |
|---|---|---|
| 400 | missing_image, bad_image, image_too_large, unsupported_image, unsupported_tcg | Fix the request. Only JPEG and PNG up to 12 MB (football 4 MB). |
| 400 | bad_image_url, image_url_fetch_failed | Only with image_url: the URL is invalid or could not be fetched. |
| 401 | missing_api_key, invalid_api_key | Check the Authorization header. |
| 402 | out_of_credits | The plan has no card scans left. |
| 405 | method_not_allowed | Only POST. |
| 422 | no_card_detected, unreadable_image (football) | No card recognised. Not charged. |
| 429 | rate_limited, demo_limit_reached, football_busy | Slow down, or the demo key's 10 per day per network are used up, or the key already has 5 football photos in progress (Retry-After). |
| 502 | (backend step failed) | Retry. |
| 503 | football_daily_limit, football_unavailable, football_busy | Football only: the daily football capacity is used up (resets_at), or the recognizer is unavailable or busy. Not charged; the other games still work. |
| 504 | detection_timeout | Often a cold start. Retry. |
Limits and pricing
- Images: JPEG or PNG, at most 12 MB (football 4 MB). All six trading card games: several cards per photo (binder pages work; Pokémon best up to about 20), one game per photo. Football: one card per photo.
- One card usually takes about 4 seconds, about 10 to 12 seconds on the first request after a quiet period; a football card about 6 to 25 seconds. Use a client timeout of at least 120 seconds.
- Demo key: 10 detections per day per network, no signup. Good for trying the API, not for production.
- Own keys: create them in the dashboard at https://neosatoshi.com/dashboard/settings/settings#api-keys . Any paid plan works; the smallest, Start-Up, costs 19 per month and includes 1,000 card scans. One credit per detected card; images without a card are not charged.
- The API identifies cards. It does not grade card condition.
Card search and corrections
Base URL: https://europe-west1-neosatoshi.cloudfunctions.net/cardsApi, same Bearer key. All six games and football.
- Correct a detection (free): every detected card has
card_index, the response hascorrectionwith therequest_id. Within 24 hours,POST /searchwithrequest_id,card_indexandq(a card name, orset+number) returns up to 5 full card objects;POST /correctionswithrequest_id,card_indexand the chosencard_idreturns the corrected card. 5 searches and 3 corrections per detected card. - Card search (paid):
POST /searchwithtcgandq(orset+number), optionallanguageandlimit(up to 10).GET /cards/{tcg}/{card_id}returns one card, for example to refresh a price. 0.5 credits per call that returns a card, from the same balance as detection; no result is free. AnIdempotency-Keyheader makes a repeat free. Daily limit per plan; the demo key allows 10 searches and 3 lookups per day per network.
curl -X POST https://europe-west1-neosatoshi.cloudfunctions.net/cardsApi/search \
-H "Authorization: Bearer nsk_live_9c253fbed995042a98280ad13e5c47d3c4a59c9b4f0f7274" \
-H "Content-Type: application/json" \
-d '{"tcg": "pokemon", "q": "charizard 4/102", "limit": 3}'
Football: {"tcg": "football", "q": "kane chrome 2023"}, text only (the player, optionally with set or season words), no language, no prices yet. Each result carries ref; GET /cards/football/{card_id}?ref=... (same key, within 24 hours) returns the card with all parallels of its subset and their print runs. Without a valid ref the lookup is 400 and free. Football detections cannot be corrected yet.
Details: https://neosatoshi.com/developers/detection-api#card-search
When to use this instead of a general vision LLM
A general vision model can often read a card name from a photo. Use this API when you need:
- Structured catalog IDs: set ID, set code and number in a fixed JSON shape, matched against a card database, plus alternatives when the print is ambiguous.
- Cardmarket product IDs and daily prices:
cardmarket_idand Cardmarket trend, low, 7 day and 30 day averages (TCGplayer where available). General vision LLMs do not return these. - Predictable per-card cost: one credit per detected card, nothing for images without a card.
If you only need a rough description of a photo, a general vision LLM may be enough.
MCP server (Claude, Cursor, Codex and other AI assistants)
mcp-server/ (npm: neosatoshi-card-mcp) is a stdio MCP server with six tools: recognize_cards (local file, https URL or base64; cards reported one by one, catalog images on request), recognize_folder (a folder of photos to a CSV or JSON file), search_cards, correct_card, get_card and list_supported_games, for all six games and football. It is listed in the official MCP registry as io.github.Cookiebreed/card-recognition-mcp. Quick setup for Claude Code:
claude mcp add neosatoshi-cards -e NEOSATOSHI_API_KEY=your_key -- npx -y neosatoshi-card-mcp
Without NEOSATOSHI_API_KEY it uses the demo key. See mcp-server/README.md for Claude Desktop, Cursor and Codex, and for the file access restriction NEOSATOSHI_ALLOWED_DIRS.
Repository layout
| Path | Contents |
|---|---|
| examples/curl/detect.sh | Bash and curl, summary via jq if installed |
| examples/python/detect.py | Python with requests |
| examples/python/stream.py, examples/node/stream.mjs, examples/curl/stream.sh | Streaming: each card as soon as it is recognised |
| examples/python/batch_to_csv.py | Process a folder, write a CSV (name, set, number, rarity, language, cardmarket_id, Cardmarket trend) |
| examples/node/detect.mjs | Node.js 18+, built-in fetch, no dependencies |
| examples/images/ | Test photos (testing the API only) |
| examples/responses/ | Recorded API responses |
| mcp-server/ | MCP server (TypeScript) |
| postman/ | Postman collection v2.1 |
| openapi.json | OpenAPI 3.1 spec |
| llms.txt | Plain-text summary for LLMs |
FAQ
Is there a free API to identify Pokémon cards from a photo?
Yes, for testing: the public demo key nsk_live_9c253fbed995042a98280ad13e5c47d3c4a59c9b4f0f7274 allows 10 detections per day per network without signup. For more, create your own key on any paid plan.
Can it recognise several cards in one photo? Yes, in all six trading card games: binder pages work (Pokémon best up to about 20 cards per photo; an 8 card Lorcana page took about 8 seconds). One game per photo. Football cards: one card per photo.
Does it return prices?
Yes. Each card comes with its Cardmarket product ID and daily Cardmarket prices in EUR (trend, low, 7 and 30 day average), plus TCGplayer prices in USD where available. A missing price is null. Football cards have no prices in the API yet.
Does it recognise Japanese, Korean or Chinese cards? Pokémon: Japanese, Korean and Simplified Chinese, besides English, German, French, Italian and Spanish. One Piece: English and Japanese. See the table above for the other games.
Does it grade card condition? No. It identifies which card it is (set, number, rarity, language, variant fields). It does not judge condition.
Does it recognise football (soccer) cards?
Yes, with "tcg": "football": Topps, Panini and other makers, one card front per photo (at most 4 MB). It returns the player, the release (set and number), season, subset, teams, rookie flag and the parallel (three candidates with print runs, a pick only when it is sure). No prices yet. search_cards and the card search find football cards by player name.
Does it support Yu-Gi-Oh! OCG (Asian) cards? No, only the TCG in English, German, French, Italian, Spanish and Portuguese.
How long does a request take? One card usually takes about 4 seconds, about 10 to 12 seconds on the first request after a quiet period, and a binder page with 9 cards about 17 seconds. Use a 120 second client timeout. 502 and 504 can be retried.
Can I get the cards one by one?
Yes. With "stream": true each card arrives as a Server-Sent Event as soon as it is recognised. It helps with binder pages; see Streaming.
What does it cost? One credit per detected card, from the plan's monthly card scans. Images without a card are not charged. The smallest paid plan, Start-Up, costs 19 per month and includes 1,000 card scans.
Can I send an image URL instead of base64?
Yes, send "image_url": "https://..." instead of "image". The URL must be public https and point to a JPEG or PNG of at most 12 MB.
Can AI agents call it directly? Yes. Agents may call the endpoint with the demo key, use the OpenAPI spec, or use the MCP server.
Where do I get support?
api@neosatoshi.com . Include the request_id from the response.
License and terms
The code in this repository is MIT licensed (see LICENSE). The API itself is a commercial service governed by https://neosatoshi.com/termsofuse . The example images in examples/images/ may only be used for testing the API.
Frequently asked questions
What is NeoSatoshi Card Recognition?
Identify trading and football cards in a photo, search and correct them, with Cardmarket prices.
How do I install NeoSatoshi Card Recognition?
It runs on your machine. In Claude Code: claude mcp add neosatoshi-card-recognition -e 'NEOSATOSHI_API_KEY=<your neosatoshi_api_key>' -e 'NEOSATOSHI_ALLOWED_DIRS=<NEOSATOSHI_ALLOWED_DIRS>' -- npx -y neosatoshi-card-mcp@0.6.0. For other clients, paste the JSON configuration from this page into their MCP settings.
Does NeoSatoshi Card Recognition need an API key?
Yes. It expects NEOSATOSHI_API_KEY, which you set in its configuration.
Is NeoSatoshi Card Recognition open source?
Its code is public on GitHub, but the repository does not declare a standard license.
Who maintains NeoSatoshi Card Recognition?
It is published by Cookiebreed. AI.tn lists it but does not maintain or endorse it.
From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.