Tarot API
The full 78-card Rider-Waite-Smith encyclopedia as JSON, server-side spread draws, and optional AI interpretation — all behind one Bearer token.
Authentication
Every account has an API token — it's on your account page. Send it as a Bearer header on every request.
Create a free account to get your tokenThe deck
Free with any account. Card copy is returned in your requested language — pass ?lang= with any of the 100 supported ISO codes.
curl -s "https://tarot.free/api/v1/cards/?arcana=major" \
-H "Authorization: Bearer YOUR_API_TOKEN"
curl -s "https://tarot.free/api/v1/cards/the-tower/?lang=es" \
-H "Authorization: Bearer YOUR_API_TOKEN"
curl -s https://tarot.free/api/v1/spreads/ \
-H "Authorization: Bearer YOUR_API_TOKEN"
{
"ok": true, "lang": "en", "count": 22,
"cards": [
{"slug": "the-fool", "name": "The Fool", "number": 0,
"arcana": "major", "suit": "", "yes_no": "yes",
"keywords_upright": ["beginnings", "innocence", "leap of faith"],
"keywords_reversed": ["recklessness", "hesitation"],
"image": "/static/cards/the-fool.webp",
"url": "https://tarot.free/cards/the-fool/"}
]
}
Draw a reading
The draw happens server-side — cards are sampled without replacement with an independent 50% chance of reversal, so nobody can stack the deck. Set interpret to true to get the AI reading in the same response.
POST https://tarot.free/api/v1/reading/
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
{
"spread": "three-card", // GET /api/v1/spreads/ for the list
"question": "What should I focus on this month?",
"interpret": true, // false = just the draw (free)
"tier": "deep", // basic (free) | deep (2) | master (4)
"lang": "en"
}
{
"ok": true,
"token": "9f3c…",
"url": "https://tarot.free/reading/9f3c…/",
"spread": "three-card",
"cards": [
{"position": 1, "reversed": false, "card": {"slug": "the-star", …}},
{"position": 2, "reversed": true, "card": {"slug": "five-of-cups", …}},
{"position": 3, "reversed": false, "card": {"slug": "the-sun", …}}
],
"interpretation": "…",
"tier": "deep",
"credits": 8
}
Python
import requests
H = {"Authorization": "Bearer YOUR_API_TOKEN"}
r = requests.post("https://tarot.free/api/v1/reading/", headers=H,
json={"spread": "three-card",
"question": "What should I focus on this month?",
"interpret": True, "tier": "deep"}).json()
for slot in r["cards"]:
card = slot["card"]
print(slot["position"], card["name"],
"(reversed)" if slot["reversed"] else "")
print(r["interpretation"])
Node.js
const r = await fetch("https://tarot.free/api/v1/reading/", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({spread: "celtic-cross", interpret: false}),
});
const {cards, token} = await r.json();
Re-fetch a reading
curl -s https://tarot.free/api/v1/reading/9f3c…/ \
-H "Authorization: Bearer YOUR_API_TOKEN"
Errors
Errors come back with ok:false and a code: unauthorized (401), bad_json / bad_spread (400), insufficient_credits (402), not_found (404), rate_limited (429, with retry_after), engine_busy (503). When an interpretation is refused the draw is still saved — top up and re-fetch it by token.
API pricing
The API bills exactly like the site — there is no separate developer plan and no per-request fee.
- Cards, spreads and un-interpreted draws: free with any account, rate-limited
- basic reader: free · deep reader: 2 credits · master reader: 4 credits
- Credits come from the same packs as the site: $3 / $7 / $13, and never expire