Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Valo.Data Sealed API
summary: 'Trading card catalogue and market prices: Cardmarket, US market, eBay sales, graded prices, populations.'
description: 'Catalogue and price data for trading card games (Pokémon, One Piece, Yu-Gi-Oh!, Magic, Lorcana and more):
Cardmarket and US market prices, eBay sold prices, graded prices, PSA/CCC populations and price history. ## Français
**Authentification.** Chaque requête porte une clé API : `Authorization: Bearer vd_live_…`. Les clés se créent dans
le portail développeur et ne sont affichées qu''une fois. `vd_test_` et
`vd_live_` sont une étiquette choisie à la création pour séparer préproduction et production : mêmes données, mêmes
crédits, mêmes quotas. Une clé peut porter une date d''expiration (`401 api_key_expired`), une liste d''adresses IP ou
de blocs CIDR autorisés (`403 ip_not_allowed`) et un plafond mensuel de crédits (`429 key_credit_cap_reached`). Le Sandbox
sert les mêmes prix que les forfaits payants, sans les cotes gradées ni les populations. Les
identifiants et les liens Cardmarket et marché US (`cardmarket_product_id`, `tcgplayer_product_id`, `cardmarket_url`,
`tcgplayer_url`) sont inclus à partir de Growth : ils valent `null` en Sandbox et en Starter. **Crédits.** Chaque forfait inclut des crédits mensuels (barème ci-dessous). Les réponses en erreur ne sont jamais
facturées. Une fois les crédits épuisés, l''API répond `429 quota_exceeded` jusqu''au renouvellement (`resets_at`). Le Sandbox
lit au plus 250 cartes et produits scellés distincts par mois, toutes routes confondues ; une fiche déjà lue
reste disponible, une nouvelle au-delà répond `429 catalog_limit_reached`. Il envoie au plus 100 requêtes facturées
par jour (UTC) : au-delà, `429 daily_limit_reached` jusqu''à minuit UTC (`resets_at`). **Débit.** Les requêtes sont limitées par organisation et par minute selon le forfait : `429 rate_limited`, à rejouer après
`Retry-After` secondes. **Prix.** Chaque source garde sa devise native, sans conversion. `amount` vaut `null` (jamais `0`) dès que `status`
n''est pas `available`. `observed_at` est la date d''observation par la source ; `computed_at` celle du dernier calcul. Les ventes eBay sont collectées au mieux (`collection: best_effort`). **Analyses.** Les routes `/data/v1/analysis/*` répondent prêt à afficher : identification d''une carte à partir d''un texte libre, prix recommandé, fiabilité et variations sur 7, 30 et 90 jours, plus fortes hausses et baisses, comparaison, synthèse d''une édition. `recommended` est le prix qu''affiche l''app ValoTCG, toujours en EUR : le marché US y est converti au dernier taux BCE (`fx_rate`), alors que `prices[]` garde la devise native. **Pagination.** Les listes renvoient `next_cursor` ; passez-le en `cursor` pour la page suivante (`page_size` ≤ 100). **Attribution.** Affichez « Données : ValoTCG » à proximité des données affichées. Voir les
conditions de l''API. ## English
**Authentication.** Every request carries an API key: `Authorization: Bearer vd_live_…`. Keys are created in the
developer portal and shown once. `vd_test_` and `vd_live_` are a label
chosen at creation to separate staging from production: same data, same credits, same quotas. A key may carry an
expiry date (`401 api_key_expired`), a list of allowed IP addresses or CIDR blocks (`403 ip_not_allowed`) and a
monthly credit cap (`429 key_credit_cap_reached`). The Sandbox serves the same prices as paid plans, without graded prices
and populations. Cardmarket and US market ids and links
(`cardmarket_product_id`, `tcgplayer_product_id`, `cardmarket_url`, `tcgplayer_url`) are included from Growth up:
they are `null` on Sandbox and Starter. **Credits.** Each plan includes monthly credits (scale below). Error responses are never billed. Once credits are
exhausted, the API answers `429 quota_exceeded` until renewal (`resets_at`). The Sandbox reads at most 250
distinct cards and sealed products per month across all routes; an item already read stays available, a new one
beyond the cap answers `429 catalog_limit_reached`.'
termsOfService: https://valotcg.com/developers/terms
contact:
name: ValoTCG
url: https://valotcg.com/developers
email: contact@valotcg.com
version: 1.0.0
servers:
- url: https://api.valotcg.com
description: Production
- url: https://api.preprod.valotcg.com
description: Preproduction
tags:
- name: Sealed
description: Sealed products (boosters, displays, ETB…), prices and history.
paths:
/data/v1/sealed:
get:
tags:
- Sealed
summary: Search sealed products (1 credit)
operationId: search_sealed_data_v1_sealed_get
security:
- ApiKey: []
parameters:
- name: game
in: query
required: true
schema:
type: string
description: Game slug from GET /data/v1/games.
title: Game
description: Game slug from GET /data/v1/games.
- name: q
in: query
required: false
schema:
anyOf:
- type: string
minLength: 2
maxLength: 80
- type: 'null'
description: Product name.
title: Q
description: Product name.
- name: edition_id
in: query
required: false
schema:
anyOf:
- type: integer
maximum: 2147483647
minimum: 1
- type: 'null'
title: Edition Id
- name: product_type
in: query
required: false
schema:
anyOf:
- type: string
maxLength: 50
- type: 'null'
description: e.g. booster, display, etb.
title: Product Type
description: e.g. booster, display, etb.
- name: language
in: query
required: false
schema:
anyOf:
- enum:
- fr
- en
- ja
- zh
- de
- es
- it
- pt
type: string
- type: 'null'
title: Language
- name: cursor
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Cursor
- name: page_size
in: query
required: false
schema:
type: integer
maximum: 100
minimum: 1
default: 50
title: Page Size
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/Page_SealedSummary_'
'401':
description: '`invalid_api_key`, `api_key_expired`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: '`insufficient_scope`, `subscription_inactive`, `plan_feature_unavailable`, `ip_not_allowed`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: '`game_not_found`, `card_not_found`, `sealed_not_found`, `edition_not_found`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: '`invalid_request`, `invalid_cursor`, `range_exceeds_plan`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: '`rate_limited` (see `Retry-After`), `quota_exceeded`, `key_credit_cap_reached` (key credit cap), `catalog_limit_reached` or `daily_limit_reached` (Sandbox)'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'503':
description: '`quota_unavailable`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/data/v1/sealed/{sealed_id}:
get:
tags:
- Sealed
summary: Sealed product sheet (1 credit)
operationId: read_sealed_data_v1_sealed__sealed_id__get
security:
- ApiKey: []
parameters:
- name: sealed_id
in: path
required: true
schema:
type: integer
maximum: 2147483647
minimum: 1
title: Sealed Id
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SealedDetail'
'401':
description: '`invalid_api_key`, `api_key_expired`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: '`insufficient_scope`, `subscription_inactive`, `plan_feature_unavailable`, `ip_not_allowed`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: '`game_not_found`, `card_not_found`, `sealed_not_found`, `edition_not_found`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: '`invalid_request`, `invalid_cursor`, `range_exceeds_plan`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: '`rate_limited` (see `Retry-After`), `quota_exceeded`, `key_credit_cap_reached` (key credit cap), `catalog_limit_reached` or `daily_limit_reached` (Sandbox)'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'503':
description: '`quota_unavailable`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/data/v1/sealed/{sealed_id}/prices:
get:
tags:
- Sealed
summary: Current prices (1 credit)
operationId: read_sealed_prices_data_v1_sealed__sealed_id__prices_get
security:
- ApiKey: []
parameters:
- name: sealed_id
in: path
required: true
schema:
type: integer
maximum: 2147483647
minimum: 1
title: Sealed Id
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SealedPrices'
'401':
description: '`invalid_api_key`, `api_key_expired`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: '`insufficient_scope`, `subscription_inactive`, `plan_feature_unavailable`, `ip_not_allowed`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: '`game_not_found`, `card_not_found`, `sealed_not_found`, `edition_not_found`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: '`invalid_request`, `invalid_cursor`, `range_exceeds_plan`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: '`rate_limited` (see `Retry-After`), `quota_exceeded`, `key_credit_cap_reached` (key credit cap), `catalog_limit_reached` or `daily_limit_reached` (Sandbox)'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'503':
description: '`quota_unavailable`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/data/v1/sealed/{sealed_id}/price-history:
get:
tags:
- Sealed
summary: Daily price history of one source (3 credits)
operationId: read_sealed_price_history_data_v1_sealed__sealed_id__price_history_get
security:
- ApiKey: []
parameters:
- name: sealed_id
in: path
required: true
schema:
type: integer
maximum: 2147483647
minimum: 1
title: Sealed Id
- name: source
in: query
required: true
schema:
enum:
- cardmarket
- us_market
- ebay
type: string
title: Source
- name: from
in: query
required: false
schema:
anyOf:
- type: string
format: date
- type: 'null'
description: Defaults to the oldest day allowed.
title: From
description: Defaults to the oldest day allowed.
- name: to
in: query
required: false
schema:
anyOf:
- type: string
format: date
- type: 'null'
description: Defaults to today.
title: To
description: Defaults to today.
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SealedPriceHistory'
'401':
description: '`invalid_api_key`, `api_key_expired`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: '`insufficient_scope`, `subscription_inactive`, `plan_feature_unavailable`, `ip_not_allowed`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: '`game_not_found`, `card_not_found`, `sealed_not_found`, `edition_not_found`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: '`invalid_request`, `invalid_cursor`, `range_exceeds_plan`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: '`rate_limited` (see `Retry-After`), `quota_exceeded`, `key_credit_cap_reached` (key credit cap), `catalog_limit_reached` or `daily_limit_reached` (Sandbox)'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'503':
description: '`quota_unavailable`'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
EditionRef:
properties:
id:
type: integer
title: Id
description: Edition id, usable as `edition_id` filter.
name:
type: string
title: Name
code:
anyOf:
- type: string
- type: 'null'
title: Code
description: Publisher set code, e.g. `SV06`, `OP-01`.
release_date:
anyOf:
- type: string
format: date
- type: 'null'
title: Release Date
type: object
required:
- id
- name
title: EditionRef
SealedPrices:
properties:
sealed_id:
type: integer
title: Sealed Id
prices:
items:
$ref: '#/components/schemas/PriceQuote'
type: array
title: Prices
type: object
required:
- sealed_id
- prices
title: SealedPrices
SealedSummary:
properties:
sealed_id:
type: integer
title: Sealed Id
description: Stable Valo.Data sealed product id (`sealed:{id}` in batches).
game:
type: string
title: Game
name:
type: string
title: Name
product_type:
anyOf:
- type: string
- type: 'null'
title: Product Type
description: e.g. booster, display, etb, starter_deck.
language:
type: string
enum:
- fr
- en
- ja
- zh
- de
- es
- it
- pt
title: Language
edition:
anyOf:
- $ref: '#/components/schemas/EditionRef'
- type: 'null'
release_date:
anyOf:
- type: string
format: date
- type: 'null'
title: Release Date
msrp:
anyOf:
- $ref: '#/components/schemas/RetailPrice'
- type: 'null'
description: Launch retail price (MSRP) on the product's market; `null` when unknown.
image_url:
anyOf:
- type: string
- type: 'null'
title: Image Url
slug:
type: string
title: Slug
description: Slug of the product's page on valotcg.com.
cardmarket_product_id:
anyOf:
- type: integer
- type: 'null'
title: Cardmarket Product Id
description: Cardmarket idProduct; `null` on Sandbox and Starter.
tcgplayer_product_id:
anyOf:
- type: integer
- type: 'null'
title: US market product id
description: US market product id; `null` on Sandbox and Starter.
type: object
required:
- sealed_id
- game
- name
- language
- slug
title: SealedSummary
PriceQuote:
properties:
source:
type: string
enum:
- cardmarket
- us_market
- ebay
title: Source
metric:
type: string
enum:
- trend
- low
- market
- sold_median_raw
title: Metric
description: '`trend` and `low` (Cardmarket), `market` (US market, USD), `sold_median_raw` (eBay sold, ungraded).'
amount:
anyOf:
- type: number
- type: 'null'
title: Amount
description: Price in `currency`; `null` whenever `status` is not `available`.
currency:
type: string
enum:
- EUR
- USD
title: Currency
description: Native currency of the source; never converted.
variant:
type: string
enum:
- default
- foil
title: Variant
card_language:
type: string
enum:
- fr
- en
- ja
- zh
- de
- es
- it
- pt
title: Card Language
market_language:
type: string
enum:
- fr
- en
- global
title: Market Language
description: 'Language of the listings behind the price: `fr` for French Cardmarket listings, `en` for the US market, `global` for language-agnostic markets.'
observed_at:
anyOf:
- type: string
format: date
- type: 'null'
title: Observed At
description: Date the source observed this price (not the recompute date).
computed_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Computed At
description: When Valo.Data last recomputed the snapshot.
status:
type: string
enum:
- available
- stale
- insufficient_sample
- missing
title: Status
description: '`stale`: Cardmarket/US market observation older than 48 h (35 days for French Cardmarket listings, scraped weekly at best). `insufficient_sample`: fewer than 3 eBay sales in the window. `missing`: no observation.'
reason:
anyOf:
- type: string
- type: 'null'
title: Reason
description: Machine readable cause when not `available`.
window_days:
anyOf:
- type: integer
- type: 'null'
title: Window Days
description: 'eBay only: sales window in days.'
sales_count:
anyOf:
- type: integer
- type: 'null'
title: Sales Count
description: 'eBay only: sales in the window.'
collection:
anyOf:
- type: string
const: best_effort
- type: 'null'
title: Collection
description: 'eBay only: sales are collected on a best effort basis.'
last_collected_at:
anyOf:
- type: string
format: date
- type: 'null'
title: Last Collected At
description: 'eBay only: latest sale day collected.'
condition:
anyOf:
- type: string
enum:
- NM
- EX
- GD
- LP
- PL
- PO
- type: 'null'
title: Condition
description: 'French Cardmarket listings only: condition of the offer behind `amount`.'
conditions:
anyOf:
- items:
$ref: '#/components/schemas/ConditionPrice'
type: array
- type: 'null'
title: Conditions
description: 'French Cardmarket listings only: cheapest offer per condition in the same scrape, best condition first; `null` whenever `amount` is `null`.'
type: object
required:
- source
- metric
- amount
- currency
- variant
- card_language
- market_language
- observed_at
- computed_at
- status
title: PriceQuote
ErrorDetail:
properties:
code:
type: string
title: Code
description: Stable snake_case error code, e.g. `card_not_found`, `quota_exceeded`.
message:
type: string
title: Message
description: Human readable explanation.
request_id:
type: string
title: Request Id
description: Identifier of the request, also returned in the `X-Request-Id` header.
docs_url:
type: string
title: Docs Url
description: Documentation of this error code.
additionalProperties: true
type: object
required:
- code
- message
- request_id
- docs_url
title: ErrorDetail
ConditionPrice:
properties:
condition:
type: string
enum:
- NM
- EX
- GD
- LP
- PL
- PO
title: Condition
amount:
type: number
title: Amount
description: Cheapest French-language offer in this condition, in EUR.
type: object
required:
- condition
- amount
title: ConditionPrice
ErrorResponse:
properties:
error:
$ref: '#/components/schemas/ErrorDetail'
type: object
required:
- error
title: ErrorResponse
RetailPrice:
properties:
amount:
type: number
title: Amount
description: Launch retail price, in `currency`.
currency:
type: string
enum:
- EUR
- USD
- JPY
title: Currency
description: Currency of the product's own market (EUR for French, USD for English, JPY for Japanese), never converted.
source:
type: string
enum:
- estimated
- verified
title: Source
description: '`estimated` from the product type, name and release period; `verified` checked product by product.'
type: object
required:
- amount
- currency
- source
title: RetailPrice
Page_SealedSummary_:
properties:
items:
items:
$ref: '#/components/schemas/SealedSummary'
type: array
title: Items
next_cursor:
anyOf:
- type: string
- type: 'null'
title: Next Cursor
description: Opaque cursor for the next page; `null` on the last page.
type: object
required:
- items
title: Page[SealedSummary]
SealedDetail:
properties:
sealed_id:
type: integer
title: Sealed Id
description: Stable Valo.Data sealed product id (`sealed:{id}` in batches).
game:
type: string
title: Game
name:
type: string
title: Name
product_type:
anyOf:
- type: string
- type: 'null'
title: Product Type
description: e.g. booster, display, etb, starter_deck.
language:
type: string
enum:
- fr
- en
- ja
- zh
- de
- es
- it
- pt
title: Language
edition:
anyOf:
- $ref: '#/components/schemas/EditionRef'
- type: 'null'
release_date:
anyOf:
- type: string
format: date
- type: 'null'
title: Release Date
msrp:
anyOf:
- $ref: '#/components/schemas/RetailPrice'
- type: 'null'
description: Launch retail price (MSRP) on the product's market; `null` when unknown.
image_url:
anyOf:
- type: string
- type: 'null'
title: Image Url
slug:
type: string
title: Slug
description: Slug of the product's page on valotcg.com.
cardmarket_product_id:
anyOf:
- type: integer
- type: 'null'
title: Cardmarket Product Id
description: Cardmarket idProduct; `null` on Sandbox and Starter.
tcgplayer_product_id:
anyOf:
- type: integer
- type: 'null'
title: US market product id
description: US market product id; `null` on Sandbox and Starter.
boosters:
anyOf:
- type: integer
- type: 'null'
title: Boosters
description: Boosters contained, when known.
cardmarket_url:
anyOf:
- type: string
- type: 'null'
title: Cardmarket Url
description: Cardmarket product page; `null` on Sandbox and Starter.
tcgplayer_url:
anyOf:
- type: string
- type: 'null'
title: US market URL
description: US market product page; `null` on Sandbox and Starter.
type: object
required:
- sealed_id
- game
- name
- language
- slug
title: SealedDetail
HistoryPoint:
properties:
date:
type: string
format: date
title: Date
amount:
type: number
title: Amount
currency:
type: string
enum:
- EUR
- USD
title: Currency
metric:
type: string
enum:
- trend
- low
- market
- sold_median_raw
title: Metric
sales_count:
anyOf:
- type: integer
- type: 'null'
title: Sales Count
description: 'eBay only: sales that day.'
condition:
anyOf:
- type: string
enum:
- NM
- EX
- GD
- LP
- PL
- PO
- type: 'null'
title: Condition
description: 'French Cardmarket listings only: condition of the offer kept that day (best available, NM first, unless the `condition` filter is set).'
type: object
required:
- date
- amount
- currency
- metric
title: HistoryPoint
SealedPriceHistory:
properties:
source:
type: string
enum:
- cardmarket
- us_market
- ebay
title: Source
variant:
type: string
enum:
- default
- foil
title: Variant
market_language:
type: string
enum:
- fr
- en
- global
title: Market Language
description: 'Language of the listings behind the curve, as on current prices: `fr` for French Cardmarket listings (cheapest offer, collected weekly at best), `en` for the US market, `global` otherwise.'
from:
type: string
format: date
title: From
to:
type: string
format: date
title: To
points:
items:
$ref: '#/components/schemas/HistoryPoint'
type: array
title: Points
description: One point per observed day; gaps are not interpolated.
sealed_id:
type: integer
title: Sealed Id
type: object
required:
- source
- variant
- market_language
- from
- to
- points
- sealed_id
title: SealedPriceHistory
securitySchemes:
ApiKey:
type: http
description: '`Authorization: Bearer vd_live_…` (or `vd_test_…`). Keys are shown once at creation.'
scheme: bearer