---
title: "Identify a card from a photo"
method: POST
path: "/v1/vision/identify"
tags: ["vision"]
---

# Identify a card from a photo

`POST /v1/vision/identify`

## Request

- Base URL: `https://api.pokemontcgapi.com`
- URL: `https://api.pokemontcgapi.com/v1/vision/identify`
- Auth: API key in header `X-Api-Key`

Send a photograph and get ranked candidate cards. Verified Free accounts get 5 trial recognitions at 25 credits each; ongoing access starts on Growth.

## Query parameters

- `include` string

## Request body

- object
  - `image` string, required: The photograph. Three ways to send it: a multipart field named `image`, the raw bytes as the whole body with an `image/*` content type, or base64 (a `data:` URL is accepted) in a JSON field of this name. JPEG, PNG, WebP and GIF; 10 MB and 96 px per side are the bounds.
  - `top_k` integer: How many candidates to return, 1 to 10. In query string when the body is the image itself.
  - `set` string: Restrict to one set code. This is the parameter that resolves a reprint: from artwork alone the original and its reprint are the same card, and if you know which pack it came out of, say so.
  - `region` string: Restrict to a print region — `WEST`, `JP` or `CN`. Same purpose: a Japanese card and its Western twin share the illustration.
  - `include` string: Comma-separated index and/or prices. In multipart or JSON alongside image; query is also accepted for multipart and raw image bodies. index adds index_eur and last_price_at; prices adds those fields and plan-filtered prices[]. Price restrictions are listed in top-level withheld and X-Plan-Withheld. Unknown values return 400 INVALID_PARAMETER without charge.

## Response `200`

Success

- object
  - `data` object, required
    - `decision` 'match' | 'ambiguous' | 'no_match', required
    - `decision_reason` 'clear' | 'reprint' | 'close_call' | 'none' | 'ocr_number' | 'ocr_set_number', required
    - `id` string, nullable, required: Present only for a match.
    - `candidates` object[], required
      - `id` string, required
      - `name` string, required
      - `number` string, required
      - `set` object, required
        - `code` string, required
        - `name` string, required
        - `print_region` 'WEST' | 'JP' | 'CN', required
      - `rarity` string, nullable, required
      - `image_url` string, nullable, required
      - `distance` integer, required
      - `confidence` number, required
      - `index_eur` number, nullable: Present with include=index or include=prices.
      - `last_price_at` string, date-time, nullable: Present with include=index or include=prices.
      - `prices` object[]: Present with include=prices; filtered by plan.
        - `source` string: `TCGPLAYER`, `CARDMARKET`, `CARDTRADER`, `EBAY`, `PRICECHARTING`, `COMMUNITY`, `PTCG_INDEX` — the seven on the allow-list today, listed live by `GET /v1/prices/sources`. The source names where the price formed and who delivered it; `provenance` on the same row is the printable form. What each covers and how delayed it is: [price methodology](/price-methodology).
        - `variant` string: `LOW`, `MARKET`, `TREND`, `AVG_1D`, `AVG_7D`, `AVG_30D`, `MEDIAN_GRADED`, `INDEX`.
        - `basis` string: `SOLD` (completed sale), `ASKING` (listing), `GUIDE` (published guide value), `DERIVED` (computed by us). Read it before comparing two rows: a Cardmarket or eBay figure is what someone is asking, a PriceCharting figure is built on sales history. Averaging the two mixes offers with transactions.
        - `amount` number: Decimal, in the currency named by the next field. Not converted — the row is served in the currency it was observed in.
        - `currency` string: `EUR`, `USD`, `GBP`, `JPY`. `PTCG_INDEX` rows are EUR; the marketplace sources are mostly USD, so a single card returns rows in more than one currency.
        - `locale` string, nullable: Language of the printing the price refers to, when the source distinguishes them.
        - `condition` string, nullable: `MINT`, `NEAR_MINT`, `EXCELLENT`, `GOOD`, `LIGHT_PLAYED`, `PLAYED`, `POOR`. Null when the source does not grade condition, which is most of them.
        - `printing` string, nullable: `NORMAL`, `HOLOFOIL`, `REVERSE_HOLO`, `FIRST_EDITION`, `FIRST_EDITION_HOLOFOIL`. Null when the source quotes one number for all printings.
        - `grading` object, nullable: `{ company, score }` for graded slabs — `PSA`, `BGS`, `CGC`, `SGC`, `ACE`, `TAG`. Set on every `MEDIAN_GRADED` row; null only on ungraded ones. `company` is itself null when the source publishes a grade band valid for any grader (PriceCharting’s “grade 9”), so a graded row is recognised by `grading !== null`, never by the company.
        - `as_of` string, date: `YYYY-MM-DD` of the observation, not of the request.
        - `sample_n` integer, nullable: Observations behind the number. Null when the source does not disclose it — which is itself worth surfacing to your users.
        - `provenance` string: Human-readable attribution string. Display it wherever you display the price.
  - `meta` object, required
    - `count` integer, required
    - `cards_indexed` integer, required
    - `index_built_at` string, date-time, required
    - `index_loaded_at` string, date-time, required
    - `signature_version` integer, required
    - `regions_detected` integer, required
    - `hypotheses_tried` integer, required
    - `elapsed_ms` integer, required
    - `ocr` object: Present when the collector-number tie-break was considered.
      - `applied` boolean
      - `eligible` integer
      - `level` 1 | 2
      - `number` integer
      - `ms` integer: Time spent on the OCR step.
      - `crop_ms` integer: Time spent preparing the image strip.
      - `reason` 'no_text' | 'no_fraction' | 'no_match' | 'multiple' | 'timeout' | 'disabled' | 'error'
      - `set_code` string: Printed set code used with the collector number.
      - `set_reason` 'no_token' | 'no_match' | 'multiple' | 'timeout' | 'error'
  - `withheld` string[]: Top-level plan restrictions with include=prices, as on the card batch.

## Other responses

- `400`: INVALID_PARAMETER, SET_NOT_FOUND
- `401`: MISSING_API_KEY
- `403`: PLAN_REQUIRED, TRIAL_EXPIRED, CARD_ALLOWANCE_EXCEEDED
- `413`: PAYLOAD_TOO_LARGE
- `415`: UNSUPPORTED_MEDIA_TYPE
- `429`: QUOTA_EXCEEDED
- `503`: FEATURE_NOT_CONFIGURED

---

[API](https://skmtc.dev/pokemontcgapi/apis/pokemon-tcg-api.md) · [All operations](https://skmtc.dev/pokemontcgapi/apis/pokemon-tcg-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pokemontcgapi/apis/pokemon-tcg-api/revisions/9e4ee9a375da?raw)
