π‘ We check deckofcardsapi.com (and 75+ other public dev APIs) with a plain GET every 30 minutes β see the live status page. As of publishing it's up and answering normally. This guide is about its design traps, not availability.
The Deck of Cards API is one of the friendliest teaching APIs on the internet: new/shuffle a deck, draw cards with hosted PNG/SVG images, move them into named piles β the classic backend for bootcamp blackjack, React memory games, and "build a card game" exercises. Keyless, CORS-open, been around for years. If you're doing the exercise as written, use it and enjoy it.
Three traps show up when the exercise becomes a project (all checked live on September 18, 2026):
deck_id saved in localStorage, a database, a test fixture, or the tutorial you're following eventually answers 404 {"success": false, "error": "Deck ID does not exist."}. Every "resume your game" feature built on a stored deck_id is on a two-week timer.success: false β and a partial cards array anyway. We drew 60 from a deck holding 49: the response was status 200 with "success": false, an error string, and all remaining cards in cards. Code that checks res.ok misses the failure; code that checks data.cards.length misses it too. You must check the success flag specifically β the one thing tutorials skip.The fix that keeps your rendering code: host the same 52 cards β same field names (code, value, suit, image, the nested images.png/svg object, verbatim) β on a free mock API you control. Decks never expire, hands are pinnable, draws can be random or deterministic, and one param reproduces their exact success/cards/remaining envelope. No signup, no key.
# 1. draw all 52 cards from their API once, wrap as db.json
curl "https://deckofcardsapi.com/api/deck/new/draw/?count=52" \
| python3 -c "import sys,json; cards=json.load(sys.stdin)['cards']; print(json.dumps({'cards':[dict(id=i+1,**c) for i,c in enumerate(cards)]}))" \
> db.json
# 2. import β the response includes your project id + admin key
curl -X POST "https://mockbird.mockbird.workers.dev/api/projects/import?name=cards" \
-H 'content-type: application/json' --data-binary @db.json
That hosted all 52 cards for us verbatim: code "AS", value "ACE", suit "SPADES", the image URL and the nested images {png, svg} object untouched, still pointing at their hosted card art (hotlinking it for a dev project is how every tutorial uses it anyway). Your card component renders it unchanged.
| Deck of Cards API | Your Mockbird project |
|---|---|
/deck/<id>/draw/?count=5 (random) | /cards?sortBy=random&limit=5 β 3 consecutive draws gave us 3 different first cards |
| draw a specific card β not possible | /cards?code=AS β the ace of spades. Deal any exact hand a test needs |
remaining in the response | X-Total-Count header on every list (or $total in the envelope, Β§4) |
| draw removes the card from the deck | DELETE /cards/:id after drawing β count went 52 β 51 for us, and the drawn card's GET is a real 404 (Β§3) |
new/shuffle a fresh deck | restore the fulldeck snapshot β back to 52 instantly (Β§3) |
piles (/pile/<name>/add) | a field, not a sub-API: PATCH {"pile":"player1"}, then ?pile=player1 lists the hand |
| deck expires after two weeks idle | never expires |
| fixed 52 (+2 jokers) cards | POST your own: wilds, tarot, Uno, a 5th suit β it persists |
# save the fresh deck once (admin key from your import response)
curl -X POST "https://mockbird.mockbird.workers.dev/api/projects/PROJECT_ID/snapshots" \
-H 'x-admin-key: KEY' -H 'content-type: application/json' -d '{"name":"fulldeck"}'
# DRAW = pick random, then delete it from the deck
curl "https://mockbird.mockbird.workers.dev/m/PROJECT_ID/cards?sortBy=random&limit=1"
curl -X DELETE "https://mockbird.mockbird.workers.dev/m/PROJECT_ID/cards/28"
# β X-Total-Count on the next list is 51; GET /cards/28 is now a real 404
# DEAL to a hand = tag the card with a pile field
curl -X PATCH "https://mockbird.mockbird.workers.dev/m/PROJECT_ID/cards/5" \
-H 'content-type: application/json' -d '{"pile":"player1"}'
curl "https://mockbird.mockbird.workers.dev/m/PROJECT_ID/cards?pile=player1" # player1's hand
# RESHUFFLE = restore the snapshot β 52 cards again
curl -X POST "https://mockbird.mockbird.workers.dev/api/projects/PROJECT_ID/snapshots/fulldeck/restore" \
-H 'x-admin-key: KEY'
Every step above ran against a live project before publishing, in that order, with those results. And because the deck is just records, your tests can skip randomness entirely: restore fulldeck in beforeEach, deal exact cards by ?code=, assert the blackjack you constructed. Deterministic hands are the feature a real deck can never give you β more on that pattern in deterministic test data.
Tutorial code reads data.cards and data.remaining. Reproduce it:
curl "https://mockbird.mockbird.workers.dev/m/PROJECT_ID/cards?mock_envelope=%7B%22success%22%3A%20true%2C%20%22deck_id%22%3A%20%22mockdeck%22%2C%20%22cards%22%3A%20%22%24data%22%2C%20%22remaining%22%3A%20%22%24total%22%7D&sortBy=random&limit=2"
# β {"success":true,"deck_id":"mockdeck","cards":[β¦2 cardsβ¦],"remaining":52}
A miss keeps cards an array ([], remaining: 0) β and asking for more cards than exist (?limit=60 on 52 records) simply returns all 52 with HTTP 200, no success:false half-failure to special-case. Set the envelope once as the project default (PUT /api/projects/:id/settings {"envelope":β¦}) and every GET answers in their shape. Details in docs β envelope.
# two 502s, then success β exercise your retry logic
curl "https://mockbird.mockbird.workers.dev/m/PROJECT_ID/cards?mock_seq=502,502,200&mock_seq_key=deckdrill"
# 2-second deal for your card-flip loading state
curl "https://mockbird.mockbird.workers.dev/m/PROJECT_ID/cards?mock_delay=2000"
More recipes in testing loading & error states.
Credit where due. The card art is theirs and it's great β hosted PNG and SVG for every card, plus backs and jokers. Server-side deck state is genuinely convenient when you want shared randomness with zero logic on your side: one URL shuffles, another draws, piles move cards between hands, and multiple players hitting the same deck_id see one consistent deck. Our version makes you do the draw-then-DELETE step, and simultaneous draws from two clients could race (two players could both "draw" the same card before either deletes it β fine for single-player exercises and tests, not a casino). The honest split: quick shared-deck demos with real shuffle mechanics β deckofcardsapi.com. Card data that never expires, exact hands for tests, your own cards, their envelope shape, and failure drills β your import.
The two-curl block in section 1, or zero setup at all:
curl https://mockbird.mockbird.workers.dev/m/demo/products?limit=3
Facts checked live on September 18, 2026: deckofcardsapi.com's homepage states decks idle for two weeks are thrown away; /api/deck/NOTADECK/draw/ answers 404 {"success": false, "error": "Deck ID does not exist."}; drawing 60 from a deck holding 49 answered HTTP 200 with "success": false, "remaining": 0, an error string, and all 49 remaining cards in cards; /deck/new/draw/?count=52 returns all 52 cards with nested images.png/svg. Every Mockbird command on this page was run against a live project before publishing (random draws distinct 3-of-3, delete β X-Total-Count: 51 β restore β 52, pile filter, envelope hit and miss, mock_seq 502/502/200), and the scratch project was deleted after.
Full API reference in the docs. More guides: Rick and Morty API alternative Β· PokΓ©API alternative Β· Deterministic test data Β· Mock API for React Β· Host a JSON file as an API. Create your API β