Developer API
Read Cartheca's published decks
A read-only HTTP API over the decks Cartheca's builders have published. It exists for sites and tools that aggregate deck data — what commanders people build, which cards they reach for, how a staple's play rate moves over a season. No account, no key, no application to fill in.
Before anything else
The decks this API serves were written by people, and the numbers you build from them describe those people's play. Two requests, neither of them enforced by a rate limiter:
- Credit Cartheca, and link back. Every deck carries a
urlpointing at the deck itself. A builder whose list feeds someone's statistics should be one click from the page they made. - Ingest, don't mirror. Counting cards across decks is what this is for. Republishing decklists wholesale as your own catalogue is not.
Card data — oracle text, prices, images — is Scryfall's to serve, and this API does not duplicate it. Every card here carries its Scryfall card id and oracle id so you can join against your own copy. Cartheca is unofficial Fan Content and is not approved or endorsed by Wizards of the Coast.
Endpoints
Everything lives under /api/v1, and GET /api/v1 returns a machine-readable index of what follows, including the current limits — worth reading first rather than hard-coding what is on this page.
GET /api/v1/decks
Published decks, paginated.
page- 1-based. Defaults to 1.
pageSize- Defaults to 50, maximum 200.
updatedSince- ISO 8601. Returns only decks edited at or after this moment — how to stay current without re-crawling.
format- Restrict to one format, e.g.
commander.
GET /api/v1/decks?format=commander&pageSize=2
{
"data": [
{
"id": "8f2a1c34-…",
"url": "https://cartheca.app/builder.html?deck=8f2a1c34-…",
"name": "Frodo, Sauron's Bane",
"format": "commander",
"colorIdentity": ["G", "W"],
"commanders": [
{ "scryfallId": "d0b5f…", "oracleId": "1a2b3…", "name": "Frodo, Adventurous Hobbit" }
],
"counts": { "mainboard": 100, "sideboard": 0, "considering": 4 },
"tags": ["budget", "lifegain"],
"builder": { "id": "u_7c2…", "name": "Brett", "url": "https://cartheca.app/profile.html?builder=u_7c2…" },
"viewCount": 214,
"createdAt": "2026-07-02T18:44:10.221Z",
"updatedAt": "2026-08-30T09:12:55.004Z"
}
],
"meta": {
"page": 1, "pageSize": 2, "total": 1483, "totalPages": 742,
"hasMore": true,
"order": "createdAt then id, ascending",
"filters": { "format": "commander", "updatedSince": null }
}
}
GET /api/v1/decks/{id}
One deck, with its decklist. Same fields as the summary, plus the primer and the cards.
GET /api/v1/decks/8f2a1c34-…
{
"data": {
"id": "8f2a1c34-…",
"name": "Frodo, Sauron's Bane",
"primer": "# The plan\n\nChip in early, …",
"cards": [
{
"scryfallId": "91fdb56b-…",
"oracleId": "6ad8011d-…",
"name": "Sol Ring",
"quantity": 1,
"board": "mainboard",
"printing": { "scryfallId": "91fdb56b-…", "set": "MSC", "collectorNumber": "211", "finish": "nonfoil" }
}
]
}
}
printing is the exact printing the builder chose, and is null when they did not choose one — that means no preference, not that the card has no printings. board is mainboard, sideboard or considering; a Commander deck's commanders appear both in commanders and in the mainboard, which is where they are in the deck.
Crawling it properly
The order is stable, which is the whole reason a full crawl works. Decks come back oldest-created first, tie-broken by id — both fixed for the life of a deck. Ordering by most-recently-edited, which is how the Community page ranks, would move a deck between pages while you were paging through it, and you would silently miss some and see others twice.
Then poll updatedSince. Take the highest updatedAt you saw, and pass it back next time. That is far cheaper for both of us than walking every page again.
Decks do disappear. A builder can unpublish or delete one, a moderator can withhold one, and an account can be locked — after any of those it stops appearing in the list and returns 404 by id. There is no tombstone feed: if a deck you hold is gone from a full crawl, treat it as withdrawn.
Limits and errors
120 requests a minute per address. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining; exceeding it returns 429 with Retry-After in seconds. Responses are cacheable for a minute. If you need more than this allows, use the contact form — it is a conversation, not a paywall.
Errors carry a readable message: { "error": "pageSize cannot be more than 200." }. A bad parameter is a 400 rather than a quietly reinterpreted default, so a typo does not silently give you the wrong data.
What it does not serve
- Private and unlisted decks. An unlisted deck is reachable by its link, not published; it is not in the list and not fetchable by id here.
- Decks that break their format's rules. Cartheca does not hold up an illegal deck as a public example, so a half-built list is not in here either. Card packages are included: they have no legality to pass, by design.
- Anything that writes. This API is
GETonly. Editing a deck needs the account that owns it — see Connect AI for how an assistant does that, with the owner approving every change. - Collections. What someone owns is theirs, and is never public.
Versioning
The version is in the path. Fields will be added to v1 responses without notice, so parse leniently and ignore what you do not recognise. Removing or renaming a field, or changing what one means, needs a new version alongside this one.