Metadata-Version: 2.4
Name: cardshowdata
Version: 4.0.1
Summary: Official Python SDK for the CardShowData TCG pricing API
Project-URL: Homepage, https://cardshowdata.com
Project-URL: Documentation, https://cardshowdata.com/docs
Author-email: CardShowData <support@cardshowdata.com>
License-Expression: MIT
Keywords: api,magic,pokemon,pricing,sdk,tcg,yugioh
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.22; extra == 'dev'
Description-Content-Type: text/markdown

# CardShowData Python SDK

Official Python client for the [CardShowData](https://cardshowdata.com) TCG pricing API.

## Install

```bash
pip install cardshowdata
```

## Quick Start

```python
from cardshowdata import CardShowData

client = CardShowData(api_key="csd_live_...")

# Get a card by pretty ID
card = client.cards.get("pk_bs_004_holofoil")
print(card.name)  # "Charizard"
print(card.image)  # "https://images.cardshowdata.com/..."

# Search cards
page = client.cards.search(tcg="pk", q="name:charizard")
for card in page.data:
    print(f"{card.name} — {card.expansion.name}")

# v4 pricing — variants[].raw[] keyed by (condition, currency)
price = client.pricing.get("aaaa-bbbb-cccc-dddd")
for variant in price.variants:
    for row in variant.raw:                       # row = RawPriceRow
        print(row.condition, row.currency,        # "NM" "USD"
              row.reference_prices.market_cents)  # 12500
        # Per-vertical recommended sell prices
        if row.recommended_sell.online:
            print("online:", row.recommended_sell.online.price_cents)
        if row.recommended_sell.show:
            print("show:", row.recommended_sell.show.price_cents)
        if row.recommended_sell.lcs:
            print("lcs:", row.recommended_sell.lcs.price_cents)

# Per-grader graded data (population, listings, recent sales)
for grader_slug, block in price.variants[0].graded.items():
    if block.population:
        print(grader_slug, "total pop:", block.population.total)

# Bulk pricing
prices = client.pricing.bulk(["uuid1", "uuid2", "uuid3"])

# Browse the catalog
tcgs = client.catalog.list_tcgs()
sets = client.catalog.list_sets("pk")
```

## Async

```python
from cardshowdata import AsyncCardShowData

async with AsyncCardShowData(api_key="csd_live_...") as client:
    card = await client.cards.get("pk_bs_004_holofoil")
    async for product in client.catalog.list_products_all("pk"):
        print(product.product_name)
```

## Auto-Pagination

Endpoints with pagination have `_all()` methods that automatically fetch every page:

```python
# Offset-paginated (cards)
for card in client.cards.search_all(tcg="pk", q="name:pikachu"):
    print(card.name)

# Cursor-paginated (catalog products, pricing changes)
for product in client.catalog.list_products_all("pk"):
    print(product.pretty_id)

for change in client.pricing.changes_all(since="2026-04-01T00:00:00Z"):
    print(f"{change.pretty_id}: {change.pct_change}%")
```

## Resources

| Resource | Methods |
|----------|---------|
| `client.cards` | `get()`, `search()`, `search_all()`, `list_by_expansion()`, `get_ebay_sales()` |
| `client.pricing` | `get()`, `bulk()`, `history()`, `changes()`, `changes_all()` |
| `client.catalog` | `list_tcgs()`, `list_sets()`, `get_taxonomy()`, `list_products()`, `list_products_all()`, `export_csv()`, `resolve()`, `search()` |
| `client.grading` | `get()`, `history()`, `changes()`, `changes_all()` |
| `client.lifecycle` | `get()` |
| `client.usage` | `get()` |
| `client.sales` | `submit()`, `submit_batch()`, `submit_csv()` |
| `client.feedback` | `submit()` |
| `client.onboarding` | `quickstart()` |

## Error Handling

```python
from cardshowdata import NotFoundError, RateLimitError

try:
    card = client.cards.get("nonexistent")
except NotFoundError:
    print("Card not found")
except RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after}s")
```

Errors: `AuthenticationError` (401), `PermissionError` (403), `NotFoundError` (404), `RateLimitError` (429), `ApiError` (5xx).

## Configuration

```python
client = CardShowData(
    api_key="csd_live_...",                        # or set CSD_API_KEY env var
    base_url="https://api.cardshowdata.com",       # default
    timeout=30,                                     # seconds
    max_retries=3,                                  # auto-retry on 429/5xx
)
```

Rate limit info is available after any request:

```python
print(client.rate_limit.remaining)  # requests left this minute
```

## Requirements

- Python 3.10+
- httpx 0.27+
