carddex
Official Python client for the CardDex Pokémon TCG API — cards, sets, print variants, sealed products, and price history from Cardmarket (EUR) and TCGplayer (USD).
- Zero runtime dependencies — standard library only (
urllib). See "Why no httpx?" below. - Python 3.9+, fully typed (
TypedDicts,py.typedmarker). - Sync
CardDexclient, plus anAsyncCardDexforasync/awaitcode.
Install
pip install carddex
Quickstart
from carddex import CardDex
client = CardDex(api_key="pk_live_...") # api_key is optional — see "Anonymous access" below
cards = client.cards.list(name="Charizard", sort="release_date", order="desc")["data"]
card = client.cards.get("sv06-001", include=["prices", "images"])
prices = client.cards.prices("sv06-001")
history = client.cards.price_history("sv06-001", days=180, source=["cardmarket", "tcgplayer"])
Get a free API key
from carddex import CardDex
client = CardDex()
result = client.auth.register("you@example.com", "My App")
api_key = result["api_key"]
# Save api_key now — it is shown only once. If you lose it, sign in at
# https://carddex.dev/account to create a new one or rotate/revoke keys.
Async
import asyncio
from carddex import AsyncCardDex
async def main():
client = AsyncCardDex(api_key="pk_live_...")
card = await client.cards.get("sv06-001")
async for card in client.cards.list_all(set="sv06"):
print(card["name"])
asyncio.run(main())
Authentication and anonymous access
The client sends your key in the X-API-Key header. You can sign in with an email link at carddex.dev/account to see your keys, create up to 2 active keys, and rotate or revoke them.
An api_key is optional. CardDex allows anonymous access at a lower but real rate limit (30 req/min, 5,000 req/day per IP). If you're distributing this client inside an app end users run themselves (a desktop tool, a CLI, a mobile app), the same rule from the web docs applies: don't embed a real API key in code you ship to other people — it becomes everyone's key. Anonymous access, or a backend you control, is the right choice there.
Pagination
Every list endpoint returns {"data": [...], "meta": {"total", "page", "pageSize", "totalPages"}}. Each resource that lists something also exposes a *_all method that returns an iterator (or, on AsyncCardDex, an async iterator), fetching pages lazily as you consume them:
for card in client.cards.list_all(set="sv06"):
print(card["name"])
for product in client.sealed.list_all(set_id="sv06"):
...
for card in client.sets.cards_all("sv06"):
...
You can also use the lower-level paginate() helper directly against any page-shaped fetcher:
from carddex import paginate
for card in paginate(lambda page: client.cards.list(set="sv06", page=page)):
...
Errors
Every non-2xx response raises CardDexError:
from carddex import CardDex, CardDexError
client = CardDex()
try:
client.cards.get("does-not-exist")
except CardDexError as err:
err.status # HTTP status, e.g. 404
err.code # the API's error.code (an int for a real API response; 'NETWORK_ERROR' for a transport failure)
str(err) # the API's error.message
err.retry_after # seconds to wait, parsed from Retry-After on a 429 — None otherwise
err.rate_limit # RateLimitInfo(limit, daily_limit, daily_remaining) parsed from response headers, or None
err.is_rate_limited # True for a 429 (either the per-minute or the daily cap)
Rate limits & retries
Every response carries X-RateLimit-Limit (per minute), X-RateLimit-Daily-Limit and X-RateLimit-Daily-Remaining (per UTC day, approximate). A 429 carries Retry-After in seconds: 60 for the per-minute limit, or the seconds until 00:00 UTC for the daily one.
Automatic retry on 429 is off by default. Turn it on when you want it:
from carddex import CardDex, RetryOptions
client = CardDex(retry=True) # up to 2 retries, never waiting more than 60s
client2 = CardDex(retry=RetryOptions(max_retries=3, max_wait_seconds=30))
A Retry-After longer than max_wait_seconds is never honoured — the call fails immediately instead of sleeping. This is deliberate: the daily-quota 429's Retry-After can be most of a day, and a small SDK default should never block a thread for hours. The per-minute 429's Retry-After: 60 fits comfortably under the default 60s cap, so that case retries; a daily-cap 429 does not, by design.
Why no httpx?
This package has zero runtime dependencies — it uses urllib.request from the standard library rather than httpx or requests. The tradeoffs, and why we picked this side of them:
- It matches the sibling
@carddex/sdkTypeScript package's own zero-dependency design goal — a thin API client shouldn't hand you a dependency tree. - No version pinning or conflicts with whatever HTTP stack your own project already uses. A thin client that adds
httpx>=0.27can quietly force a resolver fight in an app that pins a different major version, or already standardized onrequests. urllib.requesthandles everything this client actually needs: JSON in, JSON out, status codes, headers. This API has no streaming responses, no HTTP/2 requirement, no cookie jars.
The real cost is AsyncCardDex: there's no async HTTP client in the standard library, so it wraps the sync client with asyncio.to_thread instead of speaking a native async transport — see the docstring in carddex/aio.py. Every call is genuinely awaitable and concurrent calls via asyncio.gather work correctly, but a single call doesn't get the latency/throughput benefit of a real async socket. If your workload is dominated by highly concurrent CardDex calls and this matters for you, point httpx.AsyncClient at the same endpoints yourself, or email api@carddex.dev — a carddex[httpx] extra transport is a reasonable future addition if there's demand.
Migrating from pokemontcg.io
The pokemontcg.io v2 compatibility layer is live: an app built on pokemontcg.io v2 can keep its code and stored ids and change its base URL to https://api.carddex.dev/compat/pokemontcg/v2. The guide at carddex.dev/migrate/pokemontcg covers what the layer supports, which official pokemontcg.io SDKs work with it, and the fields that differ. (The official pokemontcg.io Python SDK does not work with the layer yet; the guide explains why and what to use instead.)
This SDK talks to the native /v1 API, which uses CardDex ids (sv02-062) but accepts pokemontcg.io ids as well: client.cards.get("sv2-62") returns the same card. The API also returns a ptcgio_id field on every card and accepts a ptcgio_id filter on the card list; this SDK's types don't include either yet.
API surface
| Resource | Methods |
|---|---|
client.cards |
list, list_all, get, random, prices, price_history |
client.sets |
list, get, cards, cards_all, neighbors, sealed |
client.sealed |
list, list_all, get, prices |
client.prices |
bulk, top, trends, bargains, history |
client.auth |
register, usage |
client |
usage() (shorthand for client.auth.usage()), health() |
AsyncCardDex mirrors the same resources and methods, all awaitable (*_all methods become async iterators).
Development (this monorepo)
Requires Python 3.9+.
py -m pip install -e ".[dev]" # editable install with pytest
py -m pytest
Known spec gap: the OpenAPI spec currently documents paths, parameters and status codes but not response body schemas. The TypedDicts in carddex/types.py are hand-written from the API's own internal row shapes and should be regenerated once the API's OpenAPI spec grows content.schema on its responses.
License
MIT
Metadata
Release files for carddex 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| carddex-0.2.0.tar.gz | 20.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| carddex-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.3 kB
Release files / carddex-0.2.0.tar.gz
| Download URL | carddex-0.2.0.tar.gz |
|---|---|
| Size | 20.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
62a8cdc5bd6bbef8ce13b9dd4e0a4e7f31e2f040571ea3a5bf3c32ff06d84669
|
|
BLAKE2b-256 checksum How to use checksums |
2608725f04f6f954a58d1194c604603316b3356611efad581e27d5807d63d2a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|
Release files / carddex-0.2.0-py3-none-any.whl
| Download URL | carddex-0.2.0-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f9258eefa345dacdc406ebd9fdf13d8cec2e4e2866d3806d0b4278b514beba0
|
|
BLAKE2b-256 checksum How to use checksums |
4af12ac159180fc594c1bf2b7f327737f6b17a360b3f0c74528ac8a8beda3333
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|