:::note
**TL;DR**
- A tarot deck is a structured dataset of 78 cards: 22 Major Arcana and 56 Minor Arcana across 4 suits, each with upright and reversed interpretations.
- Spreads are schemas that define named positions with interpretive contexts, transforming the same card into different meanings depending on placement.
- A reading is a composite object combining a spread definition, drawn cards placed in positions, reversal states, and generated interpretations.
- Build a complete tarot reading feature with the [Roxy Tarot API](/products/tarot-api) in under 30 minutes.
:::

**About the author:** Valentina Alcantara is a tarot reader and crystal healing practitioner with 15 years of experience across Mexico, Argentina, and Spain. She founded TarotVivo, an online school through which she has certified over 3,000 tarot readers across Latin America. Her interpretations draw on Rider-Waite symbolism, Mesoamerican cosmology, and the healing properties of crystals native to the Americas.

If you are building a tarot application, you need a solid **tarot data model** before writing a single line of UI code. Most tarot apps treat cards as flat lists and readings as random outputs. The result is brittle code that cannot support spreads, reversals, or position-dependent interpretation. This guide covers how to model the tarot deck as a typed data structure, define spreads as reusable schemas, compose readings as first-class objects, and design database tables that support all three. Every example maps directly to real API responses you can query today.

## The tarot data model starts with 78 typed cards

A standard Rider-Waite tarot deck contains exactly 78 cards divided into two groups. The **Major Arcana** consists of 22 numbered trump cards (0 through 21), running from The Fool to The World. These represent major life themes, spiritual lessons, and karmic turning points. The **Minor Arcana** consists of 56 cards across four suits: Cups (emotions and relationships), Wands (creativity and passion), Swords (intellect and conflict), and Pentacles (material wealth and finances). Each suit contains 14 cards: Ace through 10, then Page, Knight, Queen, and King.

Every card in your data model needs these fields at minimum: a unique identifier, a display name, an arcana type (major or minor), an optional suit (only for minor arcana), a number, and an image URL. For the list endpoint, this lightweight schema keeps payloads small when browsing the full deck. Here is what a card looks like from the Roxy Tarot API:

```bash
curl -s "https://roxyapi.com/api/v2/tarot/cards?arcana=major&limit=3" \
  -H "X-API-Key: YOUR_API_KEY"
```

```json
{
  "total": 22,
  "limit": 3,
  "offset": 0,
  "cards": [
    {
      "id": "fool",
      "name": "The Fool",
      "arcana": "major",
      "suit": null,
      "number": 0,
      "imageUrl": "https://roxyapi.com/img/tarot/major/fool.jpg"
    }
  ]
}
```

Ready to build this? [Roxy Tarot API](/products/tarot-api) gives you the complete 78-card Rider-Waite deck with full upright and reversed interpretations, spreads, and daily readings. [See pricing](/pricing).

## How reversed cards double the interpretation space

The detail endpoint for a single card reveals the full depth of your data model. Each card carries two complete sets of meanings: **upright** and **reversed**. When a card is drawn upside down during a reading, its energy shifts. The Fool upright means new beginnings and spontaneity. The Fool reversed means recklessness and poor judgment. This reversal mechanic means your system must handle 78 cards multiplied by 2 orientations, giving you 156 distinct interpretive states.

Each orientation contains keywords for quick reference, a full narrative description, and domain-specific guidance across love, career, finances, health, and spirituality. Fetch the complete card detail like this:

```bash
curl -s "https://roxyapi.com/api/v2/tarot/cards/fool" \
  -H "X-API-Key: YOUR_API_KEY"
```

The response includes top-level `keywords` with both `upright` and `reversed` arrays, plus full `upright` and `reversed` objects containing `keywords`, `description`, `love`, `career`, `finances`, `health`, and `spirituality` fields. Storing these as nested objects rather than flat columns makes your schema extensible. When you add a new interpretation domain later, you add a field to the meaning object instead of altering your table structure.

## Spreads as reusable schemas with named positions

A spread is not just a number of cards. It is a **schema** that defines positions, each with a name and an interpretive context. The three-card spread has three positions: Past, Present, and Future. The Celtic Cross has ten positions: Present Situation, Challenge, Distant Past, Recent Past, Best Outcome, Near Future, Your Approach, External Influences, Hopes and Fears, and Final Outcome. Each position modifies how the drawn card is interpreted. The Tower in the "Past" position means a disruption that already happened. The Tower in the "Near Future" position means a disruption that is approaching.

This position-dependent interpretation is the key insight for your data model. A spread definition is an array of position objects, each with a `name` and an `interpretation` field that describes what that slot reveals. The API handles card drawing and position-specific interpretation for you:

```bash
curl -s -X POST "https://roxyapi.com/api/v2/tarot/spreads/three-card" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question": "What do I need to know about my career?"}'
```

The response returns a `positions` array where each element contains a `position` number, a `name`, a position-specific `interpretation`, and a `card` object with `id`, `name`, `arcana`, `reversed` boolean, `keywords`, `meaning`, and `imageUrl`.

## The reading as a composite object

A reading brings together the spread schema and the drawn cards into a single composite structure. In data modeling terms, a reading is a join between a spread definition and a set of card draws placed into named positions. The response shape from the Celtic Cross endpoint illustrates this:

```bash
curl -s -X POST "https://roxyapi.com/api/v2/tarot/spreads/celtic-cross" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question": "What should I know about this situation?", "seed": "demo-2026"}'
```

The response includes `spread` (the spread name), `question`, `seed`, a `positions` array of 10 elements, and a `summary` that weaves all cards into a cohesive narrative. The `seed` parameter is critical for developers. Passing the same seed produces the same card draws in the same positions every time. This makes readings **reproducible**, which matters for testing, debugging, sharing saved readings, and ensuring consistent results when a user revisits the same reading. Omit the seed for truly random draws.

## Building custom spreads with the schema builder

The custom spread endpoint lets you define your own position schemas at request time. You send an array of position definitions and the API draws one card per position, returning the same structured response. This is where the tarot data model becomes fully programmable:

```bash
curl -s -X POST "https://roxyapi.com/api/v2/tarot/spreads/custom" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "spreadName": "Chakra Reading",
    "positions": [
      {"name": "Root", "interpretation": "Your foundation and security"},
      {"name": "Sacral", "interpretation": "Your creativity and emotions"},
      {"name": "Solar Plexus", "interpretation": "Your confidence and willpower"},
      {"name": "Heart", "interpretation": "Your capacity for love and compassion"},
      {"name": "Throat", "interpretation": "Your communication and truth"},
      {"name": "Third Eye", "interpretation": "Your intuition and insight"},
      {"name": "Crown", "interpretation": "Your spiritual connection"}
    ],
    "question": "What does each energy center need right now?",
    "seed": "chakra-demo"
  }'
```

You can define 1 to 10 positions. Each position requires a `name` and an `interpretation`. The `spreadName` field labels the response. This endpoint powers therapists building signature spreads, coaches creating workshop tools, and app developers offering customizable reading experiences. The position schema you send becomes the scaffold for the reading response. No two custom spreads need to look alike.

## Database schema considerations for tarot apps

When persisting tarot data in your own database, three tables cover the full domain. The **cards** table holds static reference data: the 78 cards with their IDs, names, arcana types, suits, numbers, and image URLs. This table is seeded once and rarely updated. You can enrich it with keyword and meaning columns, or fetch interpretations from the API on demand and cache them.

The **spreads** table stores spread definitions with a name, description, and a JSON column for position metadata (an array of `{name, interpretation}` objects). Predefined spreads like three-card and Celtic Cross are seeded rows. Custom spreads created by users become new rows, linking to the user who created them.

The **readings** table records completed readings. Each row references a spread ID, stores the drawn cards and their positions as a JSON column (mirroring the API `positions` array structure), captures the seed if one was used, the question text, the summary narrative, and a timestamp. This table grows with every reading your users perform. Index on user ID and timestamp for efficient history queries. The JSON positions column preserves the complete card-in-position mapping without needing a separate junction table.

## Frequently Asked Questions

**Q: How many cards are in a standard tarot deck?**
A: A standard Rider-Waite tarot deck contains 78 cards. The Major Arcana has 22 cards numbered 0 through 21, representing major life themes. The Minor Arcana has 56 cards across four suits (Cups, Wands, Swords, Pentacles) with 14 cards each.

**Q: What is a tarot spread and how does it affect interpretation?**
A: A tarot spread is a predefined layout of positions, each with a name and interpretive context. The same card means different things in different positions. The Tower in a "Past" position means disruption already experienced, while in a "Future" position it signals approaching change.

**Q: How do you make tarot readings reproducible in software?**
A: Pass a seed value when requesting a reading. The same seed combined with the same spread produces identical card draws and positions every time. This enables testing, debugging, sharing saved readings, and consistent user experiences across sessions.

**Q: Can I create custom tarot spreads through an API?**
A: Yes. The Roxy Tarot API custom spread endpoint accepts 1 to 10 position definitions, each with a name and interpretation context. The API draws cards for your defined positions and returns structured results matching your schema.

**Q: What is the difference between upright and reversed tarot cards in data terms?**
A: Each card has two complete interpretation sets. Upright represents the standard energy of the card. Reversed represents blocked, inverted, or internalized energy. Your data model must track a boolean `reversed` field per drawn card and serve the correct meaning set based on orientation.

## Start modeling tarot data with a production API

Designing a tarot data model means thinking in three layers: cards as reference data, spreads as position schemas, and readings as composite objects that join the two. The reversal mechanic doubles your interpretation space. Seeded randomness makes readings testable and shareable. Position-dependent meaning transforms static card data into contextual guidance.

The [Roxy Tarot API](/products/tarot-api) gives you all 78 cards, multiple predefined spreads, a custom spread builder, and structured JSON responses that map directly to the **tarot data model** described in this guide. Browse the full [API reference](/api-reference#tag/tarot) or check [pricing](/pricing) to start building.
