Skip to main content

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.typed marker).
  • Sync CardDex client, plus an AsyncCardDex for async/await code.

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/sdk TypeScript 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.27 can quietly force a resolver fight in an app that pins a different major version, or already standardized on requests.
  • urllib.request handles 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)

Source distribution for carddex 0.2.0
File Size Uploaded
carddex-0.2.0.tar.gz 20.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for carddex 0.2.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page