Getting started
A JSON API for One Piece TCG card data, plus a deck builder.
Base URL in production is https://tcg.harun.app; the examples below use
http://127.0.0.1:8000 for local development — swap whichever applies.
Every endpoint lives under /api/ and returns JSON.
This page is a hand-written companion to the auto-generated Swagger UI — that one is complete and always in sync with the code, but if you'd rather read prose than an OpenAPI schema tree, you're in the right place.
Authentication
Every request needs one of the following:
| Method | How |
|---|---|
| API key | Authorization: Api-Key <your-key> header — what a client app sends. |
| Session login | Log in at /api-auth/login/ or /admin/ in a browser — lets you
browse /api/ directly with your existing admin account, no key needed. |
curl -H "Authorization: Api-Key <your-key>" \
http://127.0.0.1:8000/api/cards/
Pagination
List endpoints return 25 results per page:
{
"count": 2634,
"next": "http://127.0.0.1:8000/api/cards/?page=2",
"previous": null,
"results": [ ... ]
}
Fetch a later page with ?page=2. Detail endpoints (/api/cards/OP01-001/-style ids) aren't paginated — they return a single object.
Filtering, search & ordering
Three separate query-param mechanisms, not all available on every endpoint:
| Mechanism | Param | Availability |
|---|---|---|
| Field filters | ?field=value |
Only on endpoints that declare them — see each section below. Unrecognized params are silently ignored. |
| Search | ?search=term |
Only Cards supports this today (matches name, card number, effect text, distribution notes). |
| Ordering | ?ordering=field (prefix - for descending) |
Available everywhere. Cards restricts it to card_number, name, cost, power; every other endpoint allows ordering by any of its own response fields. |
Errors & rate limits
| Status | Meaning |
|---|---|
400 | Validation failed — body is a dict of field name → list of messages (or non_field_errors). |
403 | Missing/invalid API key and no session login, or a CSRF failure on a session-authenticated write. |
404 | Not found. |
429 | Rate limit exceeded. |
// 400 example — from POSTing an illegal deck body
{
"cards": ["EB01-022 isn't in leader OP01-002's colors."]
}
Cards
A Card is the rules/stats definition — shared across every printing of it. Full CRUD exists (this is a plain viewset), but in practice you'll mostly read from it; writes are for admin/tooling use.
GET /api/cards/
Lean list serializer — enough to render a grid without pulling every print's full detail.
| Param | Matches |
|---|---|
category | LEADER / CHARACTER / EVENT / STAGE |
card_set | Set code (e.g. OP01) — matches the card's home set, any print's set, or a set it's bundled into. |
color | Color name, e.g. Red (case-insensitive exact match). |
trait | Trait name, e.g. Straw Hat Crew. |
attribute | Attribute name, e.g. Slash. |
rarity | Matches any of the card's prints, e.g. SEC. |
art_style | Matches any of the card's prints, e.g. ALTERNATE_ART. |
block_icon | Exact, e.g. 5. |
has_errata | true / false. |
cost_min / cost_max | Inclusive range on cost. |
search | Free text over name, card number, effect text, distribution notes. |
ordering | card_number, name, cost, power (prefix - for descending). |
curl -H "Authorization: Api-Key <your-key>" \
"http://127.0.0.1:8000/api/cards/?category=LEADER&color=Red&search=Luffy"
GET /api/cards/{card_number}/
Full detail — adds traits, effect_text, trigger_text, the complete errata history, and every prints entry (art variants, rarity, image) for that card.
Prints
A CardPrint is one specific printing of a card — original art, an alternate-art parallel, or a reprint added later by another product. Rarity and image live here, not on the card, because they can differ per printing.
GET /api/prints/
| Param | Matches |
|---|---|
rarity | Exact, e.g. SR. |
card_set | Exact set id this print originally belongs to. |
art_style | Exact, e.g. MANGA. |
confirmed_on_site / art_style_confirmed | true / false. |
ordering | Any field on the print (e.g. variant, rarity). |
Sets
GET /api/sets/ — every product (booster, starter deck, or promo), with code, name, set_type, release_date. No field filters; ordered by release date by default.
Colors / Attributes / Traits
Small reference lookups, mostly useful for populating filter dropdowns or a legend in a client app — no field filters on any of them:
| Endpoint | Fields |
|---|---|
/api/colors/ | name, hex_code |
/api/attributes/ | name, icon |
/api/traits/ | name |
Decks
A deck is one Leader plus a 50-card body. There are no real end-user accounts yet, so a deck saved via an API key stays anonymous (owner is null) — only a deck built through a real logged-in session gets an owner, and from then on only that owner can edit or delete it. Anonymous (unowned) decks stay editable by anyone holding the key.
GET /api/decks/
Filter with ?leader=OP01-002 or ?owner=<user id>. Ordered by most recently updated.
POST /api/decks/
Send the leader and the full card list together — card is a card_number, not a database id:
curl -X POST http://127.0.0.1:8000/api/decks/ \
-H "Authorization: Api-Key <your-key>" \
-H "Content-Type: application/json" \
-d '{
"name": "Law Aggro",
"leader": "OP01-002",
"cards": [
{"card": "OP01-003", "quantity": 4},
{"card": "EB01-012", "quantity": 2}
]
}'
Response:
{
"id": 7,
"name": "Law Aggro",
"leader": "OP01-002",
"cards": [
{"card": "OP01-003", "quantity": 4, "print": 42, "print_detail": { ... }},
{"card": "EB01-012", "quantity": 2, "print": 55, "print_detail": { ... }}
],
"owner": null,
"card_count": 6,
"is_complete": false,
"created_at": "2026-07-25T21:17:20Z",
"updated_at": "2026-07-25T21:17:20Z"
}
is_complete only turns true at exactly 50 non-leader cards — a deck can be saved and resaved mid-build without being rejected. What does get rejected (400) regardless of progress:
| Off-color card | Every card's colors must be a subset of the leader's colors. |
| Copy limit | More copies of a card than its max_copies (4, unless the card overrides it). |
| Duplicate entry | The same card listed twice — combine into one entry with the total quantity. |
| Leader misuse | A Leader card in the 50-card body, or a non-Leader card used as leader. |
| Mismatched print | A print id that belongs to a different card than the entry claims. |
Choosing artwork
Add "print": <id> to any entry to pick a specific art variant (id comes from that card's prints list on the card detail endpoint). Leave it out and the card's standard print is used automatically.
{"card": "OP01-033", "quantity": 2, "print": 186}
PUT /api/decks/{id}/
Same body shape as create. Always send leader and cards together — a partial update touching only one of them is rejected, since the two are validated as a pair.
DELETE /api/decks/{id}/
Only the deck's owner can do this once it has one; an unowned deck can be deleted by anyone holding the key.