SWU API

Star Wars Unlimited Data API

Overview

The SWU API provides programmatic access to Star Wars Unlimited card data, tournament results, decklists, archetypes, taxonomy, and sync feeds. All responses are JSON unless an endpoint explicitly returns an image.

Public endpoints (no authentication required when API auth is enabled): Health, Docs, QR decode, Cards & Sets, Export, Archetypes, Base Groups, Metas, Merges, Deletions, Findings, and Deck Image.

Private endpoints (API key required when API auth is enabled): Tournaments, Players, Accounts, Venues, Team Prefixes, Matches, Standings, Decklists, Archetype Groups, Subarchetypes, and SWUDB L1 feeds. Pass your key as a Bearer token: Authorization: Bearer <key>

Base URL

https://api.swuapi.com

Incremental Sync

Primary L2 list endpoints support a keep-my-mirror-in-sync workflow with ?since=, ?after=, and ?limit=. Smaller reference feeds and append-only event streams have endpoint-specific pagination noted below.

Parameters

ParameterTypeDescription
sincestring (ISO 8601)Return only rows whose updated_at >= since. Every L2 table stamps updated_at on every upsert, so this is a reliable high-water mark.
afterstring (uuid)Forward cursor. Pass the next_cursor returned by the previous response. Pagination is ordered by uuid ASC (UUID v7 is time-ordered).
limitintegerPage size. Default 200; max 1000 (500 on heavier endpoints).

Contract

  • UUID v7 primary keys. Every L2 row has a time-ordered uuid. Use it everywhere — for lookups, joins, and pagination. Do not paginate by source IDs (melee_id, strapi_id, etc.).
  • ?since= forces cursor (uuid-ASC) ordering on cursor-paginated L2 lists. When ?since= is present, those responses are ordered by uuid ASC and the emitted next_cursor is valid for the next page. Without ?since= or ?after=, list endpoints may return in a display order (e.g. tournament date DESC) for human browsing.
  • Terminal condition. On cursor-paginated endpoints, when the returned row count is less than limit, you have reached the end of the window. Advance since to the max updated_at you observed and poll again later.
  • Merges. When swuapi consolidates duplicates (archetypes, players, accounts, tournaments, venues, etc.) the old row vanishes from list endpoints. Poll GET /merges?since= to discover re-points and update your local foreign keys. Covered entity_type values: tournament, player, account, decklist, match, standing, venue, archetype. card merges never emit — cards.uuid is write-once.

Cursor-Paginated L2 Lists

/cards, /tournaments, /players, /accounts, /matches, /decklists, /standings, /venues, /archetypes, /subarchetypes, and /findings.

Endpoint-Specific Sync

/sets, /archetype-groups, and /base-groups support ?since= without cursor pagination. /merges and /deletions are append-only event streams filtered by ?since= and optional ?entity_type=. /swudb/decklists uses ?since= plus offset pagination because it exposes L1 source data, not L2 UUID rows.

Example: mirror every match

let since = lastSyncWatermark // ISO string from previous run
let after = null
let maxSeen = since
while (true) {
  const url = `${base}/matches?since=${since}${after ? `&after=${after}` : ''}&limit=500`
  const { matches, pagination } = await (await fetch(url, { headers })).json()
  for (const m of matches) {
    upsert(m)
    if (m.updated_at > maxSeen) maxSeen = m.updated_at
  }
  if (matches.length < 500) break
  after = pagination.next_cursor
}
saveWatermark(maxSeen)
GET /health 🔓 Public

Health check endpoint. Returns server status and last scrape info.

curl https://api.swuapi.com/health
const response = await fetch('https://api.swuapi.com/health');
const data = await response.json();
console.log(data.status); // "healthy"
{
  "status": "healthy",
  "cardCount": 5560,
  "tournamentCount": 130,
  "playerCount": 2400,
  "accountCount": 2700,
  "matchCount": 18000,
  "decklistCount": 5200,
  "lastScrape": {
    "completedAt": "2024-01-15T06:00:00.000Z",
    "status": "success",
    "durationMs": 42000,
    "stats": { /* scraper stats */ }
  },
  "lastTournamentScrape": {
    "completedAt": "2026-03-20T07:00:00.000Z",
    "status": "success",
    "durationMs": 180000,
    "stats": { /* scraper stats */ }
  },
  "liveScraper": {
    "alive": true,
    "lastRun": "2026-03-20T12:10:00.000Z",
    "expiresAt": "2026-03-20T12:15:00.000Z"
  },
  "uptime": 3600
}
GET /sets 🔓 Public

List all card sets/expansions.

Query Parameters

ParameterTypeDefaultDescription
sincestring-ISO 8601 timestamp. Returns only sets with updated_at >= since. This endpoint is not cursor-paginated.
curl https://api.swuapi.com/sets
const response = await fetch('https://api.swuapi.com/sets');
const { sets } = await response.json();

sets.forEach(set => {
  console.log(`${set.code}: ${set.name}`);
});
{
  "sets": [
    {
      "code": "SOR",
      "name": "Spark of Rebellion",
      "release_date": "2024-03-08",
      "total_cards": 252
    },
    {
      "code": "SHD",
      "name": "Shadows of the Galaxy",
      "release_date": "2024-07-12",
      "total_cards": 262
    }
  ]
}
GET /sets/:code 🔓 Public

Get a single set by its code.

Path Parameters

Parameter Type Description
code string Set code (e.g., SOR, SHD, TWI)
curl https://api.swuapi.com/sets/SOR
const setCode = 'SOR';
const response = await fetch(`https://api.swuapi.com/sets/${setCode}`);
const set = await response.json();

console.log(set.name); // "Spark of Rebellion"
{
  "code": "SOR",
  "name": "Spark of Rebellion",
  "release_date": "2024-03-08",
  "total_cards": 252
}
GET /cards 🔓 Public

List cards with pagination and optional filtering.

Query Parameters

Parameter Type Default Description
limit integer 100 Number of cards to return (max 500)
offset integer 0 Number of cards to skip
after string - UUID cursor for forward pagination. Pass next_cursor from the previous response. Alias: ?cursor=.
set string - Filter by set code (e.g., SOR, SHD)
type string - Filter by type (Leader, Unit, Event, Upgrade, Base)
rarity string - Filter by rarity (Common, Uncommon, Rare, Legendary, Special)
name string - Search by card name or subtitle (case-insensitive partial match)
since string - ISO 8601 timestamp. Returns only cards with updated_at >= since; pairs with after for cursor sync.
# Get first 100 cards
curl https://api.swuapi.com/cards

# Get leaders from Spark of Rebellion
curl "https://api.swuapi.com/cards?set=SOR&type=Leader"

# Search by name
curl "https://api.swuapi.com/cards?name=Bossk"

# Paginate through results
curl "https://api.swuapi.com/cards?limit=50&offset=100"
// Get all leaders from SOR
const params = new URLSearchParams({
  set: 'SOR',
  type: 'Leader',
  limit: 50
});

const response = await fetch(
  `https://api.swuapi.com/cards?${params}`
);
const { cards, pagination } = await response.json();

console.log(`Found ${pagination.total} cards`);
cards.forEach(card => console.log(card.name));
{
  "cards": [
    {
      "id": "SOR_005",
      "name": "Luke Skywalker",
      "subtitle": "Faithful Friend",
      "type": "Leader",
      "rarity": "Special",
      // ... see Card Object for all fields
    }
  ],
  "pagination": {
    "limit": 100,
    "offset": 0,
    "total": 5560
  }
}
GET /cards/:id 🔓 Public

Get a single card by any identifier: UUID, external_id (integer), collector_number (SOR_005), or card name slug (e.g., "entrenched").

Variant note: collector_number is shared between variants (e.g. SOR_005 is both Luke Standard and Luke Foil). Lookups by collector_number or name slug always return the Standard variant by default. Use ?variant= to request a different variant. UUID and external_id are unique per variant and ignore this parameter.

Path Parameters

Parameter Type Description
id string | int | uuid UUID, external_id (integer), collector_number (e.g., SOR_005), or card name slug

Query Parameters

Parameter Type Default Description
variant string Standard Filter by variant_type when looking up by collector_number or name slug. Common values: Standard, Hyperspace, Showcase. Use all to return an array of every variant sharing the same collector_number. Ignored for UUID and external_id lookups.
# By UUID (unique, stable)
curl https://api.swuapi.com/cards/59e4854c-bd67-47f2-98c9-815d31736928

# By external_id (unique per variant)
curl https://api.swuapi.com/cards/5

# By collector_number — returns Standard variant by default
curl https://api.swuapi.com/cards/SOR_005

# By collector_number — request Hyperspace variant explicitly
curl https://api.swuapi.com/cards/SOR_005?variant=Hyperspace

# By collector_number — return all variants as an array
curl https://api.swuapi.com/cards/SOR_005?variant=all

# By slug
curl https://api.swuapi.com/cards/entrenched
const response = await fetch(
  'https://api.swuapi.com/cards/59e4854c-bd67-47f2-98c9-815d31736928'
);
const card = await response.json();

console.log(card.uuid);              // "59e4854c-..."
console.log(card.collector_number);   // "SOR_005"
console.log(card.external_id);        // 5
{
  "uuid": "59e4854c-bd67-47f2-98c9-815d31736928",
  "external_id": 5,
  "collector_number": "SOR_005",
  "external_uid": "2579145458",
  "name": "Luke Skywalker",
  "subtitle": "Faithful Friend",
  "setCode": "SOR",
  "cardNumber": "5",
  "type": "Leader",
  "type2": "Leader Unit",
  "rarity": "Special",
  "cost": 6,
  "power": 4,
  "hp": 7,
  "arena": "Ground",
  "aspects": ["Vigilance", "Heroism"],
  "traits": ["Force", "Rebel"],
  "keywords": [],
  "variantType": "Standard",
  "text": "Action [1 resource, exhaust]: Give a Shield token...",
  "deployBox": "On Attack: You may give another unit a Shield token.",
  "epicAction": "Epic Action: If you control 6 or more resources...",
  "isUnique": true,
  "isLeader": true,
  "frontImageUrl": "https://cdn.starwarsunlimited.com/...",
  "backImageUrl": "https://cdn.starwarsunlimited.com/...",
  "artist": "Borja Pindado",
  "variants": [981, 9745],
  "variantOf": null,
  "reprints": [100],
  "reprintOf": null
}
GET /export/all 🔓 Public

Export all cards and sets as a single JSON payload. Useful for bulk imports or syncing.

# Download full export
curl https://api.swuapi.com/export/all -o cards.json

# Pipe to jq for processing
curl -s https://api.swuapi.com/export/all | jq '.meta'
const response = await fetch(
  'https://api.swuapi.com/export/all'
);
const { cards, sets, meta } = await response.json();

console.log(`Total cards: ${meta.totalCards}`);
console.log(`Total sets: ${meta.totalSets}`);
console.log(`Last scraped: ${meta.lastScrapedAt}`);

// Process all cards
const leaders = cards.filter(c => c.type === 'Leader');
console.log(`Found ${leaders.length} leaders`);
{
  "cards": [
    { /* card objects */ }
  ],
  "sets": [
    { /* set objects */ }
  ],
  "meta": {
    "totalCards": 5560,
    "totalSets": 19,
    "lastScrapedAt": "2024-01-15T06:00:00.000Z",
    "exportedAt": "2024-01-15T12:00:00.000Z"
  }
}
GET /export/:entity 🔓 Public

Export one L2 entity table. Use this for bulk snapshots when you want one entity type instead of the card-only /export/all payload.

Path Parameters

ParameterTypeDescription
entitystringOne of cards, sets, tournaments, venues, players, accounts, matches, decklists, standings, archetypes.

Query Parameters

ParameterTypeDefaultDescription
cursorstring-UUID cursor. When provided, rows after this UUID are returned in UUID order.
limitintegerall rowsOptional page size. Max 5000. If cursor is provided without limit, defaults to 1000.
curl "https://api.swuapi.com/export/tournaments?limit=1000"
{
  "data": [ { /* rows */ } ],
  "total": 130,
  "has_more": false,
  "next_cursor": null
}
POST /qr/decode 🔓 Public

Decode a deck QR image and fetch deck JSON from a supported source. Supported QR URLs are SWUDB, SWUBase, and SWU Forge deck links.

Request Body

Send multipart/form-data with a single image file field named image. Maximum image size is 10 MB.

curl -X POST https://api.swuapi.com/qr/decode \
  -F "image=@deck-qr.png"

Returns 400 for invalid uploads, 422 when no QR code or recognized deck URL is found, and JSON deck data on success.

GET /tournaments 🔒 Private

List SWU tournaments. Includes competitive events (PQ, SQ, RQ, GC, LCQ), Store Showdowns (SS), community events (COM), and casual side events (CAS).

Query Parameters

Parameter Type Default Description
limit integer 50 Number of tournaments to return (max 200)
offset integer 0 Number of tournaments to skip
after string - UUID cursor for forward pagination. Pass next_cursor from the previous response. Alias: ?cursor=.
tier string - Filter by tier: PQ, SQ, RQ, GC, LCQ, SS, COM, CAS
format string - Filter by format: Premier, Limited
official boolean - Filter by official status (true = FFG-sanctioned, false = community)
country string - Filter by venue country code or country name when available
status string - Filter by tournament status: upcoming, live, or completed
include_upcoming boolean true Set to false to hide upcoming tournaments in browse results
since string - ISO 8601 timestamp. Returns only tournaments with updated_at >= since; pairs with after for cursor sync.
missing string - Comma-separated ingest-health tokens: decklists, top_cut, standings. Returns completed events with matches that are missing those artifacts.
date_after string - Date cutoff (YYYY-MM-DD) for tournament date. Useful with missing= checks.
# List all tournaments
curl https://api.swuapi.com/tournaments

# Filter by tier and format
curl "https://api.swuapi.com/tournaments?tier=PQ&format=Premier"
const response = await fetch(
  'https://api.swuapi.com/tournaments?tier=SQ'
);
const { tournaments, pagination } = await response.json();

console.log(`Found ${pagination.total} Sector Qualifiers`);
{
  "tournaments": [
    {
      "melee_id": 67890,
      "name": "Star Wars: Unlimited Planetary Qualifier Austin",
      "display_name": "Planetary Qualifier (SEC) — Austin, TX, US",
      "date": "2026-02-15T00:00:00.000Z",
      "tier": "PQ",
      "tier_level": "competitive",
      "format": "Premier",
      "official": true,
      "organizer": "Emerald Tavern Games",
      "player_count": 64,
      "has_day_two": true,
      "day_one_swiss_rounds": 8,
      "day_two_swiss_rounds": 4,
      "round_structure": {
        "has_day_two": true,
        "total_swiss_rounds": 12,
        "day_two_start_round": 9,
        "days": [
          { "day": 1, "start_round": 1, "end_round": 8, "swiss_round_count": 8 },
          { "day": 2, "start_round": 9, "end_round": 12, "swiss_round_count": 4 }
        ]
      },
      "melee_url": "https://melee.gg/Tournament/View/67890",
      "event_type": "main",
      "winner_player_uuid": "019d240f-409b-7786-9dbe-74e91e7f1a77"
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 130
  }
}
GET /tournaments/:id 🔒 Private

Get a single tournament by UUID or melee.gg tournament ID.

Path Parameters

Parameter Type Description
id string | integer Tournament UUID or melee.gg tournament ID
curl https://api.swuapi.com/tournaments/67890
{
  "melee_id": 67890,
  "name": "Star Wars: Unlimited Planetary Qualifier Austin",
  "date": "2026-02-15T00:00:00.000Z",
  "tier": "PQ",
  "format": "Premier",
  "organizer": "Emerald Tavern Games",
  "player_count": 64,
  "has_day_two": true,
  "day_one_swiss_rounds": 8,
  "day_two_swiss_rounds": 4,
  "round_structure": {
    "has_day_two": true,
    "total_swiss_rounds": 12,
    "day_two_start_round": 9,
    "days": [
      { "day": 1, "start_round": 1, "end_round": 8, "swiss_round_count": 8 },
      { "day": 2, "start_round": 9, "end_round": 12, "swiss_round_count": 4 }
    ]
  },
  "melee_url": "https://melee.gg/Tournament/View/67890",
  "event_type": "main"
}
GET /tournaments/live 🔒 Private

List live and upcoming tournaments with current-round metadata. Multi-day melee events include inferred Day 1 / Day 2 Swiss structure and the current round’s tournament day.

curl https://api.swuapi.com/tournaments/live
{
  "tournaments": [
    {
      "melee_id": 414243,
      "name": "Sector Qualifier - Atlanta - Saturday - 10:00 am",
      "status": "live",
      "has_day_two": true,
      "day_one_swiss_rounds": 8,
      "day_two_swiss_rounds": 4,
      "round_structure": {
        "day_two_start_round": 9,
        "days": [
          { "day": 1, "start_round": 1, "end_round": 8, "swiss_round_count": 8 },
          { "day": 2, "start_round": 9, "end_round": 12, "swiss_round_count": 4 }
        ]
      },
      "current_round": 8,
      "current_round_name": "Round 8",
      "current_round_day": 1,
      "round_count": 15
    }
  ]
}
GET /tournaments/:id/rounds 🔒 Private

Return all rounds for a tournament. Each round includes round_stage (swiss or top_cut) and tournament_day inferred from melee’s public event schedule when available.

curl https://api.swuapi.com/tournaments/414243/rounds
{
  "round_structure": {
    "has_day_two": true,
    "total_swiss_rounds": 12,
    "day_two_start_round": 9,
    "days": [
      { "day": 1, "start_round": 1, "end_round": 8, "swiss_round_count": 8 },
      { "day": 2, "start_round": 9, "end_round": 12, "swiss_round_count": 4 }
    ]
  },
  "rounds": [
    {
      "round_number": 8,
      "round_name": "Round 8",
      "is_top_cut": false,
      "round_stage": "swiss",
      "tournament_day": 1,
      "pairing_count": 64
    },
    {
      "round_number": 9,
      "round_name": "Round 9",
      "is_top_cut": false,
      "round_stage": "swiss",
      "tournament_day": 2,
      "pairing_count": 0
    },
    {
      "round_number": 100,
      "round_name": "Quarterfinals",
      "is_top_cut": true,
      "round_stage": "top_cut",
      "tournament_day": 2
    }
  ]
}
GET /tournaments/:id/standings 🔒 Private

Return standings for one tournament. Without round, returns the latest available round and falls back to final standings for historical events. With round=N, returns that per-round live standing when the live scraper observed it.

Path Parameters

ParameterTypeDescription
idstring | integerTournament UUID or melee.gg tournament ID.

Query Parameters

ParameterTypeDefaultDescription
roundintegerlatestSpecific round number to fetch.
curl "https://api.swuapi.com/tournaments/414243/standings?round=8"
GET /tournaments/:id/matches 🔒 Private

Get all match results for a tournament by UUID or melee.gg ID. Add ?round=N to fetch one round. Includes player names via join and a derived draw_type field (intentional, played, or null).

curl https://api.swuapi.com/tournaments/67890/matches
{
  "tournament": { "melee_id": 67890, "name": "PQ Austin", ... },
  "matches": [
    {
      "uuid": "019d9557-...",
      "round_number": 1,
      "is_top_cut": false,
      "player1_account_uuid": "019d9557-a1...",
      "player1_uuid": "019d240f-...",
      "player1_name": "Alice",
      "player2_account_uuid": "019d9557-b2...",
      "player2_uuid": "019d2410-...",
      "player2_name": "Bob",
      "player1_wins": 2,
      "player2_wins": 1,
      "draws": 0,
      "draw_type": null,
      "winner_uuid": "019d240f-...",
      "is_bye": false,
      "is_forfeit": false
    }
  ]
}
GET /tournaments/:id/decklists 🔒 Private

Get all submitted decklists for a tournament. Responses include archetype-derived leader/base fields so consumers can render leader + base without a second archetype lookup.

curl https://api.swuapi.com/tournaments/67890/decklists
{
  "tournament": { "melee_id": 67890, "name": "PQ Austin", ... },
  "decklists": [
    {
      "uuid": "019d9557-...",
      "melee_id": "d1e2f3a4-...",
      "player_name": "Alice",
      "archetype_uuid": "019d323a-...",
      "archetype_name": "Darth Vader, Dark Lord of the Sith - Yellow 30",
      "leader_name": "Darth Vader",
      "leader_subtitle": "Dark Lord of the Sith",
      "leader_set_code": "SOR",
      "base_name": "Yellow 30",
      "leader_collector_number": "SOR_010",
      "canonical_base_collector_number": "SOR_179",
      "decklist": [
        { "id": "SOR_010", "count": 1 },
        { "id": "SOR_193", "count": 3 }
      ]
    }
  ]
}
GET /players 🔒 Private

List canonical players, deduplicated across multiple tournament accounts. Sorted by match count descending.

Query Parameters

Parameter Type Default Description
limit integer 50 Number of players to return (max 200)
offset integer 0 Number of players to skip
after string - UUID cursor for forward pagination. Pass next_cursor from the previous response. Alias: ?cursor=.
name string - Search by player name (case-insensitive partial match)
since string - ISO 8601 timestamp. Returns only players with updated_at >= since; pairs with after for cursor sync.
# Search for a player
curl "https://api.swuapi.com/players?name=skywalker"
{
  "players": [
    {
      "uuid": "019d240f-...",
      "name": "42Mops",
      "account_count": "2",
      "tournament_count": "3",
      "match_count": "25",
      "melee_handles": ["42Mops"]
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 1
  }
}
GET /players/:id 🔒 Private

Get a single player by canonical player UUID. Also accepts a melee.gg account identifier as fallback.

Path Parameters

Parameter Type Description
id string Player UUID or melee.gg account identifier
curl https://api.swuapi.com/players/019d240f-409b-7786-9dbe-74e91e7f1a77
{
  "uuid": "019d240f-409b-7786-9dbe-74e91e7f1a77",
  "name": "Alice",
  "account_count": "2",
  "melee_handles": ["Alice"],
  "accounts": [
    { "melee_id": "abc-123", "name": "Alice" },
    { "melee_id": "def-456", "name": "Alice" }
  ]
}
GET /players/:id/matches 🔒 Private

Get all matches for a player across all tournaments and accounts. Includes tournament name, tier, and canonical player IDs.

curl https://api.swuapi.com/players/019d240f-409b-7786-9dbe-74e91e7f1a77/matches
{
  "player": { "uuid": "019d240f-...", "name": "Alice" },
  "matches": [
    {
      "uuid": "019d9557-...",
      "tournament_name": "PQ Austin",
      "tier": "PQ",
      "format": "Premier",
      "round_number": 3,
      "player1_uuid": "019d240f-...",
      "player1_name": "Alice",
      "player2_uuid": "019d2410-...",
      "player2_name": "Bob",
      "player1_wins": 2,
      "player2_wins": 0,
      "draw_type": null
    }
  ]
}
GET /players/:id/decklists 🔒 Private

Get all decklists submitted by a player across tournaments and accounts. Responses include archetype-derived leader/base fields.

curl https://api.swuapi.com/players/019d240f-409b-7786-9dbe-74e91e7f1a77/decklists
{
  "player": { "uuid": "019d240f-...", "name": "Alice" },
  "decklists": [
    {
      "uuid": "019d9557-...",
      "melee_id": "d1e2f3a4-...",
      "tournament_name": "PQ Austin",
      "tier": "PQ",
      "archetype_uuid": "019d323a-...",
      "archetype_name": "Darth Vader, Dark Lord of the Sith - Yellow 30",
      "leader_name": "Darth Vader",
      "base_name": "Yellow 30",
      "decklist": [ ... ]
    }
  ]
}
GET /matches 🔒 Private

List matches across all tournaments with pagination and optional filters. Each match includes a derived draw_type field so intentional draws (0-0-3) can be separated from played draws.

Query Parameters

Parameter Type Default Description
limit integer 50 Number of matches to return (max 200)
offset integer 0 Number of matches to skip
after string - UUID cursor for forward pagination. Pass next_cursor from the previous response. Alias: ?cursor=.
tournament integer - Filter by tournament melee_id
account string - Filter by account ID
since string - ISO 8601 timestamp. Returns only matches with updated_at >= since; pairs with after for cursor sync.
# Get all matches for a tournament
curl "https://api.swuapi.com/matches?tournament=67890"

# Get all matches for an account
curl "https://api.swuapi.com/matches?account=abc-123-def"
{
  "matches": [
    {
      "uuid": "019d9557-...",
      "tournament_uuid": "019d83a7-...",
      "tournament_name": "PQ Austin",
      "round_number": 1,
      "player1_account_uuid": "019d9557-a1...",
      "player1_uuid": "019d240f-...",
      "player1_name": "Alice",
      "player2_account_uuid": "019d9557-b2...",
      "player2_uuid": "019d2410-...",
      "player2_name": "Bob",
      "player1_wins": 2,
      "player2_wins": 1,
      "draws": 0,
      "draw_type": null,
      "winner_uuid": "019d240f-..."
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 24929
  }
}
GET /matches/:id 🔒 Private

Get a single match by UUID.

Path Parameters

Parameter Type Description
id string Match UUID
curl https://api.swuapi.com/matches/019d9557-...
{
  "uuid": "019d9557-...",
  "tournament_uuid": "019d83a7-...",
  "tournament_name": "PQ Austin",
  "round_number": 3,
  "is_top_cut": false,
  "player1_account_uuid": "019d9557-a1...",
  "player1_uuid": "019d240f-...",
  "player1_name": "Alice",
  "player2_account_uuid": "019d9557-b2...",
  "player2_uuid": "019d2410-...",
  "player2_name": "Bob",
  "player1_wins": 2,
  "player2_wins": 0,
  "draws": 0,
  "draw_type": null,
  "winner_uuid": "019d240f-...",
  "is_bye": false,
  "is_forfeit": false
}
GET /decklists 🔒 Private

List decklists across all tournaments with pagination and filters. Each row includes archetype-derived leader/base fields so downstream consumers do not need to join archetypes separately.

Query Parameters

Parameter Type Default Description
limit integer 50 Number of decklists to return (max 200)
offset integer 0 Number of decklists to skip
after string - UUID cursor for forward pagination. Pass next_cursor from the previous response. Alias: ?cursor=.
tournament_uuid string - Filter by tournament UUID. Preferred over tournament for new integrations — matches /standings's param naming.
tournament integer - Filter by tournament melee_id (legacy)
account string - Filter by account ID
archetype string - Search by archetype name (case-insensitive partial match)
hasCards boolean - Filter to only decklists with card data (true)
since string - ISO 8601 timestamp. Returns only decklists with updated_at >= since; pairs with after for cursor sync.
# Find all Darth Vader decklists
curl "https://api.swuapi.com/decklists?archetype=Vader"

# Get decklists for a specific tournament (preferred)
curl "https://api.swuapi.com/decklists?tournament_uuid=019d83a7-1155-74fd-90fe-7fe2c55135f4"

# Or by melee_id (legacy)
curl "https://api.swuapi.com/decklists?tournament=67890"
{
  "decklists": [
    {
      "uuid": "019d9557-...",
      "melee_id": "d1e2f3a4-...",
      "archetype_uuid": "019d323a-...",
      "archetype_name": "Darth Vader, Dark Lord of the Sith - Yellow 30",
      "leader_name": "Darth Vader",
      "leader_subtitle": "Dark Lord of the Sith",
      "leader_set_code": "SOR",
      "base_name": "Yellow 30",
      "leader_collector_number": "SOR_010",
      "canonical_base_collector_number": "SOR_179",
      "player_name": "Alice",
      "tournament_name": "PQ Austin",
      "tier": "PQ",
      "decklist": [
        { "id": "SOR_010", "count": 1 }
      ]
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 5216
  }
}
GET /decklists/:meleeId 🔒 Private

Get a single decklist by its ID. Includes the full decklist plus archetype-derived leader/base fields.

Path Parameters

Parameter Type Description
meleeId string Decklist ID (UUID)
curl https://api.swuapi.com/decklists/d1e2f3a4-5678-90ab-cdef
{
  "uuid": "019d9557-...",
  "melee_id": "d1e2f3a4-...",
  "account_uuid": "019d9557-c54d-...",
  "player_name": "Alice",
  "tournament_uuid": "019d83a7-1155-...",
  "archetype_uuid": "019d323a-...",
  "archetype_name": "Darth Vader, Dark Lord of the Sith - Yellow 30",
  "leader_name": "Darth Vader",
  "leader_subtitle": "Dark Lord of the Sith",
  "leader_set_code": "SOR",
  "base_name": "Yellow 30",
  "leader_collector_number": "SOR_010",
  "canonical_base_collector_number": "SOR_179",
  "decklist": [
    { "id": "SOR_010", "count": 1 },
    { "id": "SOR_193", "count": 3 }
  ],
  "tournament_name": "PQ Austin",
  "tier": "PQ",
  "format": "Premier"
}
GET /archetypes 🔓 Public

List archetypes (leader + normalized base combinations) with pagination and search. Common bases are normalized to color+HP (e.g., "Yellow30"), rare bases keep their name (e.g., "Tarkintown").

Parameter Type Default Description
format string - Required. Filter by format: Premier or Limited.
limit integer 20 Number of archetypes to return (max 200)
after string - UUID cursor for forward pagination. Pass next_cursor from the previous response. Alias: ?cursor=.
name string - Search name or nickname (case-insensitive)
since string - ISO 8601 timestamp. Returns only archetypes with updated_at >= since; pairs with after for cursor sync.
curl "https://api.swuapi.com/archetypes?format=Premier"

curl "https://api.swuapi.com/archetypes?format=Premier&name=Han+Solo"
// Paginate through every Premier archetype using cursor
let cursor = null
do {
  const url = `https://api.swuapi.com/archetypes?format=Premier${cursor ? `&cursor=${cursor}` : ''}`
  const { archetypes, pagination } = await (await fetch(url)).json()
  archetypes.forEach(a => console.log(a.name))
  cursor = pagination.next_cursor
} while (cursor)
{
  "archetypes": [
    {
      "uuid": "019d3239-22ef-76ec-b412-ac0b8def717a",
      "name": "Han Solo, Worth the Risk - Energy Conversion Lab",
      "nickname": "Han Solo (SHD) - Energy Conversion Lab",
      "format": "Premier",
      "leader_aspects": ["Cunning", "Heroism"],
      "base_aspects": ["Vigilance"],
      "leader_uuid": "019d3177-...",
      "leader_name": "Han Solo",
      "leader_subtitle": "Worth the Risk",
      "leader_collector_number": "SHD_015",
      "leader_image_url": "https://cdn.starwarsunlimited.com/...",
      "base_group_uuid": "019d363c-...",
      "base_group_name": "Energy Conversion Lab",
      "canonical_base_uuid": "019d3178-...",
      "canonical_base_collector_number": "SHD_101",
      "canonical_base_image_url": "https://cdn.starwarsunlimited.com/...",
      "decklist_count": 437
    }
  ],
  "pagination": { "next_cursor": "019d3239-22ef-76ec-b412-ac0b8def717a" }
}
GET /archetypes/:id 🔓 Public

Get a single archetype by UUID. Includes leader/base details, decklist count, archetype groups, and subarchetypes.

Path Parameters

Parameter Type Description
id string Archetype UUID
curl https://api.swuapi.com/archetypes/019d3239-22ef-76ec-b412-ac0b8def717a
const response = await fetch(
  'https://api.swuapi.com/archetypes/019d3239-22ef-76ec-b412-ac0b8def717a'
)
const archetype = await response.json()
{
  "uuid": "019d3239-22ef-76ec-b412-ac0b8def717a",
  "name": "Han Solo, Worth the Risk - Energy Conversion Lab",
  "nickname": "Han Solo (SHD) - Energy Conversion Lab",
  "format": "Premier",
  "leader": {
    "uuid": "019d3177-...",
    "name": "Han Solo",
    "subtitle": "Worth the Risk",
    "collector_number": "SHD_015",
    "image_url": "https://cdn.starwarsunlimited.com/...",
    "aspects": ["Cunning", "Heroism"]
  },
  "base_group": {
    "uuid": "019d363c-...",
    "canonical_name": "Energy Conversion Lab",
    "cards": [ { "uuid": "019d3178-...", "collector_number": "SHD_101" } ]
  },
  "canonical_base_card": {
    "uuid": "019d3178-...",
    "collector_number": "SHD_101",
    "image_url": "https://cdn.starwarsunlimited.com/..."
  },
  "leader_aspects": ["Cunning", "Heroism"],
  "base_aspects": ["Vigilance"],
  "decklist_count": 437,
  "archetype_groups": [ { "uuid": "019d4a12-...", "name": "Han Builds" } ],
  "subarchetypes": []
}
GET /archetypes/:id/groups 🔓 Public

Reverse taxonomy lookup: returns every archetype_group that contains this archetype. An archetype can belong to zero or more groups. Returns 404 if the archetype UUID doesn't exist.

Path Parameters

ParameterTypeDescription
idstringArchetype UUID.
curl https://api.swuapi.com/archetypes/019d3239-22ef-76ec-b412-ac0b8def717a/groups
{
  "archetype_groups": [
    {
      "uuid": "019d4a12-...",
      "name": "Yellow Aggro",
      "description": "Yellow-based aggressive leaders running fast ground units.",
      "created_at": "2026-01-15T00:00:00Z",
      "updated_at": "2026-03-02T14:20:00Z"
    }
  ]
}
GET /archetypes/resolve 🔓 Public

Stateless compute: returns the canonical identity (name, nickname, base_group_uuid, aspects) for a given (leader, base, format) tuple. Does NOT create a row. Returns uuid: null if no archetype exists yet for the tuple. Use POST if you want find-or-create.

Query Parameters

ParameterTypeRequiredDescription
leader_card_uuidstringYesUUID of the leader card.
base_card_uuidstringYesUUID of the base card.
formatstringYesPremier or Limited.
curl "https://api.swuapi.com/archetypes/resolve?leader_card_uuid=019d3177-...&base_card_uuid=019d3177-...&format=Premier"
{
  "uuid": "019d3239-22ef-76ec-b412-ac0b8def717a",
  "name": "Han Solo, Audacious Smuggler - Yellow 30",
  "nickname": "Han Solo (SOR) - Yellow 30",
  "format": "Premier",
  "leader_card_uuid": "019d3177-...",
  "base_group_uuid": "019d363c-...",
  "canonical_base_card_uuid": "019d3178-...",
  "leader_aspects": ["Cunning", "Heroism"],
  "base_aspects": ["Cunning"]
}
POST /archetypes/resolve 🔓 Public

Find-or-create an archetype. Accepts either UUID-based or name-based bodies; UUIDs take precedence if both are present. Returns the archetype's UUID plus canonical identity fields, creating a new row if the (leader, base, format) tuple didn't exist. Downstream importers such as Wayfinder should prefer the UUID shape when they have card UUIDs, including Karabast preview-set archetypes that appear before tournament results exist.

Name-based input rejection. If leader_name/leader_subtitle doesn't match an is_leader=true card, or base_name doesn't match either a base_group's canonical_name or an is_base=true card, the request returns 400 (not a silent create). This is a deliberate guardrail against upstream artifacts polluting the catalog.

Request Body (UUID shape)

FieldTypeRequiredDescription
leader_card_uuidstringYesUUID of the leader card.
base_card_uuidstringYesUUID of the base card.
formatstringYesPremier or Limited.

Request Body (name shape)

FieldTypeRequiredDescription
leader_namestringYesLeader card name (e.g., "Han Solo").
leader_subtitlestringYesLeader subtitle (e.g., "Audacious Smuggler"). Required to disambiguate multiple printings of the same leader name.
base_namestringYesBase group canonical name (e.g., "Yellow 30", "Force Blue") or specific rare base card name (e.g., "Aldhani Garrison").
formatstringYesPremier or Limited.

Error Codes

CodeStatusMeaning
missing_param400Required field not provided.
invalid_format400format must be Premier or Limited.
leader_name_not_found400(name-shape) leader_name does not match any is_leader=true card. Don't retry — fix the input.
base_name_not_found400(name-shape) base_name does not match any base_group or is_base=true card.
leader_not_found404(UUID-shape) the given leader_card_uuid does not exist.
base_not_found404(UUID-shape) the given base_card_uuid does not exist.
not_a_leader400Card exists but is not flagged is_leader=true.
not_a_base400Card exists but is not flagged is_base=true.
# UUID-based (Wayfinder/Karabast ingest)
curl -X POST https://api.swuapi.com/archetypes/resolve \
  -H "Content-Type: application/json" \
  -d '{
    "leader_card_uuid": "019d3177-...",
    "base_card_uuid": "019d3178-...",
    "format": "Premier"
  }'

# Name-based fallback
curl -X POST https://api.swuapi.com/archetypes/resolve \
  -H "Content-Type: application/json" \
  -d '{
    "leader_name": "Han Solo",
    "leader_subtitle": "Audacious Smuggler",
    "base_name": "Yellow 30",
    "format": "Premier"
  }'
{
  "uuid": "019d3239-22ef-76ec-b412-ac0b8def717a",
  "name": "Han Solo, Audacious Smuggler - Yellow 30",
  "nickname": "Han Solo (SOR) - Yellow 30",
  "format": "Premier",
  "leader_card_uuid": "019d3177-...",
  "base_group_uuid": "019d363c-...",
  "canonical_base_card_uuid": "019d3178-...",
  "leader_aspects": ["Cunning", "Heroism"],
  "base_aspects": ["Cunning"]
}
POST /archetypes/reconcile 🔓 Public

Batch-reconcile archetype UUIDs against the current server state. For each input UUID, returns whether it's still active, has been merged into another archetype (with to_uuid pointing at the terminal of the merge chain), or is missing entirely. Designed for downstream caches to prune stale entries after migrations or upstream deletions.

Request Body

FieldTypeDescription
uuidsstring[]Array of archetype UUIDs (max 1000 per request).
curl -X POST https://api.swuapi.com/archetypes/reconcile \
  -H "Content-Type: application/json" \
  -d '{"uuids": ["019d3239-22ef-76ec-b412-ac0b8def717a", "019d3db5-e3d3-79e4-b339-941b02248012", "00000000-0000-7000-8000-000000000000"]}'
{
  "results": [
    { "uuid": "019d3239-22ef-76ec-b412-ac0b8def717a", "status": "active" },
    { "uuid": "019d3db5-e3d3-79e4-b339-941b02248012", "status": "merged",
      "to_uuid": "019d3239-22ef-76ec-b412-ac0b8def717a" },
    { "uuid": "00000000-0000-7000-8000-000000000000", "status": "missing" }
  ]
}
GET /archetype-groups 🔒 Private

Curated meta-strategy clusters that roll multiple archetypes up under a shared label (e.g. "Blue Green Villain" covers Qira/ECL, Krennic/ECL, and Iden/ECL). Designed for higher-level meta analysis such as "Red Aggro is up 5% this week". Groups are small (typically < 100 rows) and hand-maintained.

Query Parameters

ParameterTypeDescription
sincestringISO-8601 timestamp. Returns only groups with updated_at >= since for incremental sync. Membership changes bump the parent group's updated_at.
curl https://api.swuapi.com/archetype-groups

# Incremental sync: only rows changed since yesterday
curl "https://api.swuapi.com/archetype-groups?since=2026-04-18T00:00:00Z"
{
  "archetype_groups": [
    {
      "uuid": "019d4a12-...",
      "name": "Blue Green Villain",
      "description": "Villain leaders running Green 30 + ECL bases.",
      "member_count": 3,
      "created_at": "2026-01-15T00:00:00Z",
      "updated_at": "2026-03-02T14:20:00Z"
    }
  ]
}
GET /archetype-groups/:id 🔒 Private

Single archetype_group with its member archetypes inline. Returns 404 if the UUID doesn't exist. Use /archetype-groups/:id/members for a richer shape that also includes subarchetypes.

curl https://api.swuapi.com/archetype-groups/019d4a12-...
{
  "uuid": "019d4a12-...",
  "name": "Blue Green Villain",
  "description": "Villain leaders running Green 30 + ECL bases.",
  "created_at": "2026-01-15T00:00:00Z",
  "updated_at": "2026-03-02T14:20:00Z",
  "archetypes": [
    { "uuid": "019d3239-...", "name": "Qira - Energy Conversion Lab", "nickname": "Qira ECL", "format": "Premier" }
  ]
}
GET /archetype-groups/:id/members 🔒 Private

All members of the group, split into archetypes and subarchetypes. A group can contain either or both — subarchetypes are build variants within a single archetype (e.g. "Lando Heroic" vs "Lando Yellow") that deserve their own meta slot.

Each member row includes weight and confidence as explicit nulls. Scoring columns don't exist in the schema yet; the fields are present today so the response shape stays stable when scoring lands.

curl https://api.swuapi.com/archetype-groups/019d4a12-.../members
{
  "archetypes": [
    {
      "uuid": "019d3239-...",
      "name": "Qira - Energy Conversion Lab",
      "nickname": "Qira ECL",
      "format": "Premier",
      "weight": null,
      "confidence": null
    }
  ],
  "subarchetypes": [
    {
      "uuid": "019d5b33-...",
      "archetype_uuid": "019d3240-...",
      "name": "Lando Heroic",
      "description": "Yellow/Blue Heroic aspect build.",
      "weight": null,
      "confidence": null
    }
  ]
}
GET /base-groups 🔓 Public

Canonical base groupings used for normalization. Common bases collapse to color + HP (e.g. "Yellow 30" covers every yellow 30-HP base printing); rare bases keep their card name (e.g. "Tarkintown"). This is what drives archetype naming — two decklists with different "Yellow 30" printings resolve to the same archetype.

Query Parameters

ParameterTypeDescription
sincestringISO-8601 timestamp. Returns only base groups with updated_at >= since.
curl https://api.swuapi.com/base-groups
{
  "base_groups": [
    {
      "uuid": "019d363c-...",
      "canonical_name": "Yellow 30",
      "color": "Yellow",
      "hp": 30,
      "rarity_class": "common",
      "created_at": "2025-11-01T00:00:00Z",
      "updated_at": "2025-11-01T00:00:00Z"
    },
    {
      "uuid": "019d363d-...",
      "canonical_name": "Tarkintown",
      "color": null,
      "hp": null,
      "rarity_class": "named",
      "created_at": "2025-11-01T00:00:00Z",
      "updated_at": "2025-11-01T00:00:00Z"
    }
  ]
}
GET /base-groups/:id 🔓 Public

Single base group with its member base cards (all printings that normalize to this group). Returns 404 if the UUID doesn't exist.

curl https://api.swuapi.com/base-groups/019d363c-...
{
  "uuid": "019d363c-...",
  "canonical_name": "Yellow 30",
  "color": "Yellow",
  "hp": 30,
  "rarity_class": "common",
  "created_at": "2025-11-01T00:00:00Z",
  "updated_at": "2025-11-01T00:00:00Z",
  "cards": [
    { "uuid": "019d3178-...", "collector_number": "SOR_101", "set_code": "SOR" }
  ]
}
GET /base-groups/:id/archetypes 🔓 Public

List archetypes using one base group in a specific format, ordered by decklist count descending.

Path Parameters

ParameterTypeDescription
idstringBase group UUID.

Query Parameters

ParameterTypeRequiredDescription
formatstringYesPremier or Limited.
curl "https://api.swuapi.com/base-groups/019d363c-.../archetypes?format=Premier"
GET /subarchetypes 🔒 Private

Build variants within a single archetype, driven by scoring rules against the decklist. Each subarchetype has 0+ rules of type aspect, trait, or card; classification counts matches and picks the highest-scoring subarchetype.

Query Parameters

ParameterTypeDefaultDescription
sincestring-ISO-8601. Filter by updated_at >= since.
archetype_uuidstring-Only subarchetypes belonging to this parent archetype.
limitinteger200Page size (max 1000).
afterstring-UUID cursor. Pass pagination.next_cursor from the previous response.
curl https://api.swuapi.com/subarchetypes

# Only subarchetypes for one archetype
curl "https://api.swuapi.com/subarchetypes?archetype_uuid=019d3240-..."

# Incremental sync
curl "https://api.swuapi.com/subarchetypes?since=2026-04-18T00:00:00Z"
{
  "subarchetypes": [
    {
      "uuid": "019d5b33-...",
      "archetype_uuid": "019d3240-...",
      "name": "Lando Heroic",
      "description": "Yellow/Blue Heroic aspect build.",
      "created_at": "2026-02-01T00:00:00Z",
      "updated_at": "2026-02-01T00:00:00Z",
      "scoring_rules": [
        { "uuid": "019d5b40-...", "rule_type": "aspect", "values": ["Heroism"] },
        { "uuid": "019d5b41-...", "rule_type": "card", "values": ["SOR_001", "SOR_002"] }
      ]
    }
  ],
  "pagination": { "next_cursor": null }
}
GET /subarchetypes/:id 🔒 Private

Single subarchetype with its scoring rules. Returns 404 if the UUID doesn't exist.

curl https://api.swuapi.com/subarchetypes/019d5b33-...
{
  "uuid": "019d5b33-...",
  "archetype_uuid": "019d3240-...",
  "name": "Lando Heroic",
  "description": "Yellow/Blue Heroic aspect build.",
  "created_at": "2026-02-01T00:00:00Z",
  "updated_at": "2026-02-01T00:00:00Z",
  "scoring_rules": [
    { "uuid": "019d5b40-...", "rule_type": "aspect", "values": ["Heroism"] }
  ]
}
POST /deck-image 🔓 Public

Generate a deck image (PNG) from a JSON card list or text decklist. Returns the image directly. Cards are resolved against the card database for art. Supports custom branding and card variant art.

Request Body

Send either JSON (Content-Type: application/json) or plain text decklist (Content-Type: text/plain).

JSON Fields

FieldTypeRequiredDescription
cardsarray*Array of card objects (see below)
textstring*Text decklist (e.g., "1 Han Solo (SOR) 017"). Use instead of cards
titlestringNoTitle text (default: leader name or "Deck")
subtitlestringNoSubtitle text (e.g., player name, event)
datestringNoDate string to display
brandingobjectNo{ url: "yoursite.com" } — override default swuapi branding

* Provide either cards or text, not both.

Card Object

FieldTypeDescription
namestringCard name (e.g., "Han Solo, Audacious Smuggler")
typestring"Leader", "Base", "Ground Unit", "Space Unit", "Event", "Upgrade"
countintegerNumber of copies (default 1)
variantstringCard variant for art (e.g., "Hyperspace", "Showcase"). Falls back to Standard

Text Decklist Format

Standard decklist format: COUNT NAME (SET) NUMBER, one card per line.

# JSON format
curl -X POST https://api.swuapi.com/deck-image \
  -H "Content-Type: application/json" \
  -d '{
    "cards": [
      {"name": "Han Solo, Audacious Smuggler", "type": "Leader", "variant": "Hyperspace"},
      {"name": "Mos Eisley", "type": "Base"},
      {"name": "Millennium Falcon, Piece of Junk", "type": "Space Unit", "count": 3},
      {"name": "Cunning", "type": "Event", "count": 3}
    ],
    "title": "Han Solo Aggro",
    "subtitle": "by terronk"
  }' --output deck.png

# Text decklist format
curl -X POST https://api.swuapi.com/deck-image \
  -H "Content-Type: application/json" \
  -d '{
    "text": "1 Han Solo (SOR) 017\n1 Mos Eisley (JTL) 030\n3 Cunning (SOR) 203\n3 Millennium Falcon (SOR) 155",
    "title": "Han Solo Aggro"
  }' --output deck.png

# Plain text body
curl -X POST https://api.swuapi.com/deck-image \
  -H "Content-Type: text/plain" \
  -d '1 Han Solo (SOR) 017
1 Mos Eisley (JTL) 030
3 Cunning (SOR) 203' --output deck.png
const response = await fetch('https://api.swuapi.com/deck-image', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    cards: [...],
    title: 'Han Solo Aggro',
    branding: { url: 'mysite.com' }  // optional
  })
})
const blob = await response.blob()
// Display or download the PNG image
GET /deck-image/decklist/:meleeId 🔓 Public

Generate a deck image from a saved decklist. Returns PNG directly. Includes player name and tournament as subtitle. Embed in <img> tags or share directly.

Path Parameters

ParameterTypeDescription
meleeIdstringDecklist ID (UUID)

Query Parameters

ParameterTypeDescription
brandUrlstringOverride branding URL
brandNamestringOverride branding name
# Download deck image for a specific decklist
curl https://api.swuapi.com/deck-image/decklist/2261b229-5a8a-4ced-8b75-b3e200bca054 --output deck.png

# With custom branding
curl "https://api.swuapi.com/deck-image/decklist/2261b229?brandUrl=mysite.com" --output deck.png
<!-- Embed directly in HTML -->
<img src="https://api.swuapi.com/deck-image/decklist/2261b229-5a8a-4ced-8b75-b3e200bca054" />
GET /metas 🔓 Public

List all meta eras. A meta era is a competitive period defined by available card pool and ban list. Set releases and mid-set bans create distinct sub-eras.

ParameterTypeDefaultDescription
format string all Filter by format: premiere or limited
curl https://api.swuapi.com/metas

curl "https://api.swuapi.com/metas?format=premiere"
{
  "metas": [
    {
      "id": "JTL-pre-ban",
      "name": "Jump to Lightspeed (pre-ban)",
      "set": "JTL",
      "setName": "Jump to Lightspeed",
      "format": "premiere",
      "start": "2025-03-14",
      "end": "2025-04-11",
      "isCurrent": false,
      "bans": []
    },
    {
      "id": "JTL-post-ban",
      "name": "Jump to Lightspeed (post-ban)",
      "set": "JTL",
      "setName": "Jump to Lightspeed",
      "format": "premiere",
      "start": "2025-04-11",
      "end": "2025-07-11",
      "isCurrent": false,
      "bans": [
        { "card": "Jango Fett, Concealing the Conspiracy", "cardId": "TWI_016", "action": "suspended", "effectiveDate": "2025-04-11", "format": "premiere" }
      ]
    }
  ]
}
GET /metas/current 🔓 Public

Get the current active meta era.

ParameterTypeDefaultDescription
format string premiere Format: premiere or limited
curl https://api.swuapi.com/metas/current
GET /metas/:id 🔓 Public

Get a specific meta era by ID (e.g., JTL-post-ban, SOR, TWI-limited).

curl https://api.swuapi.com/metas/JTL-post-ban
GET /metas/dropdown 🔓 Public

Get metas formatted for UI dropdowns. By default, when a set has sub-eras (pre-ban/post-ban), only the sub-eras are returned (not the full set entry). Sorted newest first.

ParameterTypeDefaultDescription
format string premiere Format: premiere or limited
includeFullSet boolean false Include full-set entries alongside sub-eras
curl https://api.swuapi.com/metas/dropdown
{
  "options": [
    { "id": "LAW", "name": "A Lawless Time", "isCurrent": true },
    { "id": "SEC", "name": "Secrets of Power", "isCurrent": false },
    { "id": "LOF-post-ban", "name": "Legends of the Force (post-ban)", "isCurrent": false },
    { "id": "LOF-pre-ban", "name": "Legends of the Force (pre-ban)", "isCurrent": false }
  ]
}
GET /metas/bans 🔓 Public

List all ban/suspension events.

curl https://api.swuapi.com/metas/bans
GET /metas/sets 🔓 Public

List all set release dates.

curl https://api.swuapi.com/metas/sets
GET /accounts 🔒 Private

List melee.gg accounts for incremental sync. An account is a single login; a canonical player can own multiple accounts. Use ?since= to pull only rows whose updated_at advanced since your last poll.

Query Parameters

ParameterTypeDefaultDescription
sincestring-ISO 8601 timestamp. Returns only accounts with updated_at >= since.
player_uuidstring-Filter to accounts belonging to one canonical player.
team_tagstring-Filter to accounts whose handle matches an approved team prefix.
afterstring-Cursor — pair with next_cursor from the previous page.
limitinteger200Page size. Max 1000.
curl "https://api.swuapi.com/accounts?since=2026-03-01T00:00:00Z&limit=500"
GET /accounts/:uuid 🔒 Private

Get one melee.gg account by UUID.

curl https://api.swuapi.com/accounts/019d9557-c54d-...
GET /venues 🔒 Private

List tournament venues for incremental sync. Each row carries city/state/country plus lat/lng when known.

Query Parameters

ParameterTypeDefaultDescription
sincestring-ISO 8601 timestamp. Returns only venues with updated_at >= since.
afterstring-Cursor — pair with next_cursor from the previous page.
limitinteger200Page size. Max 1000.
curl "https://api.swuapi.com/venues?since=2026-03-01T00:00:00Z"
GET /venues/:uuid 🔒 Private

Get one canonical venue by UUID.

curl https://api.swuapi.com/venues/019d81dd-...
GET /standings 🔒 Private

Flat list of final standings across tournaments, for incremental sync. Distinct from GET /tournaments/:uuid/standings?round=N, which is the live per-round view for a single event. The no_decklist_submitted field lives here: true means the source confirmed the player did not submit a decklist, not that swuapi failed to scrape one.

Query Parameters

ParameterTypeDefaultDescription
sincestring-ISO 8601 timestamp. Returns only standings with updated_at >= since.
tournament_uuidstring-Limit to one tournament.
player_uuidstring-Limit to one canonical player.
afterstring-Cursor — pair with next_cursor from the previous page.
limitinteger200Page size. Max 1000.
curl "https://api.swuapi.com/standings?since=2026-03-01T00:00:00Z&limit=500"
{
  "standings": [
    {
      "uuid": "019d9557-...",
      "tournament_uuid": "019d83a7-...",
      "player_uuid": "019d240f-...",
      "rank": 1,
      "final_placement": 1,
      "points": 24,
      "no_decklist_submitted": false,
      "decklist_uuid": "019d9557-...",
      "updated_at": "2026-03-01T00:00:00.000Z"
    }
  ],
  "pagination": { "next_cursor": null }
}

See Incremental Sync for the shared since/after/limit contract used across list endpoints.

GET /standings/:uuid 🔒 Private

Get one final-standing row by UUID.

curl https://api.swuapi.com/standings/019d9557-...
GET /team-prefixes 🔒 Private

List approved team prefixes used to tag melee.gg account handles.

Query Parameters

ParameterTypeDefaultDescription
allbooleanfalseWhen true, include inactive prefixes.
curl https://api.swuapi.com/team-prefixes
GET /swudb/decklists 🔒 Private

Paginated SWUDB L1 decklist feed for downstream consumers. This is source-layer data and uses offset pagination, not L2 UUID cursor pagination.

Query Parameters

ParameterTypeDefaultDescription
sincestring-ISO 8601 timestamp. When present, returns rows whose last_fetched_at is newer than this timestamp, ordered by last_fetched_at ASC.
limitinteger200Page size. Max 500.
offsetinteger0Offset for pagination.
curl "https://api.swuapi.com/swudb/decklists?since=2026-03-01T00:00:00Z&limit=200"
GET /merges 🔓 Public

Get the merge log for sync consumers. When entities are merged (duplicate archetypes, players, accounts, tournaments, venues, etc.), this endpoint reports what was merged and when. Consumers should poll this periodically and re-point their local references. Covered entity_type values: tournament, player, account, decklist, match, standing, venue, archetype.

Query Parameters

Parameter Type Default Description
since string (required) ISO 8601 timestamp — only return merges after this time
entity_type string - Filter to one entity type (e.g. player, tournament, venue).

Contract: card merges never emit. cards.uuid is write-once; upstream card removal is a delete, not a merge. Consumers should not expect entity_type = "card".

curl "https://api.swuapi.com/merges?since=2026-03-01T00:00:00Z"
{
  "merges": [
    {
      "entity_type": "archetype",
      "from_uuid": "019d3239-...",
      "to_uuid": "019d3240-...",
      "merged_at": "2026-03-20T15:30:00.000Z"
    },
    {
      "entity_type": "player",
      "from_uuid": "019d240f-...",
      "to_uuid": "019d2410-...",
      "merged_at": "2026-03-20T16:00:00.000Z"
    }
  ]
}
GET /deletions 🔓 Public

Deletion tombstone stream for sync consumers. Poll this alongside /merges so downstream mirrors can remove entities that swuapi intentionally deleted, such as rejected phantom tournaments.

Query Parameters

ParameterTypeDefaultDescription
sincestring2020-01-01T00:00:00ZOnly return deletions after this timestamp.
entity_typestring-Filter to one entity type.
curl "https://api.swuapi.com/deletions?since=2026-03-01T00:00:00Z"
{
  "deletions": [
    {
      "entity_type": "tournament",
      "swuapi_uuid": "019d83a7-...",
      "melee_id": 418419,
      "reason": "rejected_phantom_tournament",
      "deleted_at": "2026-03-20T16:00:00.000Z"
    }
  ]
}
GET /findings 🔓 Public

Data-quality findings stream for consumers. Open findings describe known upstream or swuapi data issues such as phantom events, stale live tournaments, unresolved archetypes, or missing tournament artifacts.

Query Parameters

ParameterTypeDefaultDescription
statusstringopenopen, accepted, resolved, or all.
kindstring-Filter to one finding kind.
severitystring-Filter by severity.
sourcestring-Filter by source system.
sincestring-ISO 8601 timestamp. Enables UUID cursor pagination for incremental sync.
beforestring-Optional stale cutoff.
afterstring-UUID cursor. Returned as next_cursor only when since or after is present.
limitinteger100Page size. Max 500.
curl "https://api.swuapi.com/findings?status=open&since=2026-03-01T00:00:00Z"
{
  "findings": [
    {
      "uuid": "019d9a44-...",
      "kind": "phantom_melee_event",
      "severity": "high",
      "status": "open",
      "dedupe_key": "melee:418419",
      "last_seen_at": "2026-03-20T16:00:00.000Z"
    }
  ],
  "next_cursor": null
}

Card Object

Complete reference for all card fields.

Card Identifiers

Each card has several identifiers:

Field Type Description Unique?
uuidUUIDStable unique identifier (ours)Yes
external_idintegerSource API integer IDYes
collector_numberstringPrinted card code (SET_NUMBER format)No — variants share the same number
external_uidstringSource API string UIDYes

Examples:

Card uuid external_id collector_number variant_type
Standard Luke59e4...5SOR_005Standard
Foil Lukea3b2...9745SOR_005Standard Foil
Hyperspace Lukef1c7...981SOR_278Hyperspace

Use uuid as the primary identifier. collector_number is not unique — foil and non-foil share the same printed number. Hyperspace cards have their own printed numbers.

Fields

Field Type Description
uuidUUIDStable unique identifier
external_idintSource API integer ID (unique per variant)
collector_numberstringPrinted card code (SET_NUMBER format, e.g., SOR_005)
external_uidstringSource API string UID
namestringCard name
subtitlestringCard subtitle
setCodestringSet code (e.g., SOR)
cardNumberstringNumber within set
serialCodestringFull serial code
typestringLeader, Unit, Event, Upgrade, Base
type2stringSecondary type (e.g., Leader Unit)
raritystringCommon, Uncommon, Rare, Legendary, Special
costintResource cost
powerintAttack power
hpintHealth points
upgradePowerintPower modifier (upgrades)
upgradeHpintHP modifier (upgrades)
arenastringGround or Space
aspectsarrayAspect names
aspectDuplicatesarrayDuplicate aspects
traitsarrayTrait names (Rebel, Jedi, etc.)
keywordsarrayKeywords (Ambush, Sentinel, etc.)
textstringCard ability text
deployBoxstringLeader deploy ability
epicActionstringEpic action text
rulesstringAdditional rules
variantTypestringStandard, Hyperspace, Standard Foil, Hyperspace Foil, Showcase, Prerelease Promo, etc.
isUniqueboolWhether card is unique
isLeaderboolIs a leader card
isBaseboolIs a base card
frontImageUrlstringFront card image URL
backImageUrlstringBack card image URL
thumbnailUrlstringThumbnail image URL
artFrontHorizontalboolFront art orientation
artBackHorizontalboolBack art orientation
artiststringCard artist name
variantsarraystrapiIds of variant cards
variantOfintstrapiId of original (if variant)
reprintsarraystrapiIds of reprint cards
reprintOfintstrapiId of original (if reprint)

Set Codes

Known set codes and their full names.

Code Name
SORSpark of Rebellion
SHDShadows of the Galaxy
TWITwilight of the Republic
JTLJump to Lightspeed
LOFLegends of the Force
SECSecrets of Power
LAWA Lawless Time

Meta Eras

Competitive periods defined by card pool and ban list. Set releases create new eras; mid-set bans split an era into pre-ban and post-ban sub-eras.

ID Name Start End
SORSpark of Rebellion2024-03-082024-07-12
SHDShadows of the Galaxy2024-07-122024-11-09
TWITwilight of the Republic2024-11-092025-03-14
JTL-pre-banJump to Lightspeed (pre-ban)2025-03-142025-04-11
JTL-post-banJump to Lightspeed (post-ban)2025-04-112025-07-11
LOF-pre-banLegends of the Force (pre-ban)2025-07-112025-09-22
LOF-post-banLegends of the Force (post-ban)2025-09-222025-11-07
SECSecrets of Power2025-11-072026-03-13
LAWA Lawless Time2026-03-13(current)

Ban Events

Card Action Date
Jango Fett, Concealing the ConspiracySuspended2025-04-11
Triple Dark RaidSuspended2025-04-11
DJ, Blatant ThiefSuspended2025-04-11
Force ThrowSuspended2025-09-22

Standings

Standings placement fields are sourced from melee.gg's last standings tab for each tournament — usually labeled "Finals" (sometimes a round number for swiss-only events). rank = 1 is the tournament champion when the stored snapshot includes bracket placement. Match and game record fields are recomputed from canonical L2 matches because melee.gg's standings tabs report per-round records, not full-event W-L-D.

Historical tournaments backfilled before April 2026 were persisted with swiss-only standings (rank-1 = top swiss seed, not champion). Those are being re-scraped to the last-tab format; until that completes you may see a mix of the two. The tournaments.winner_player_uuid field is maintained from standings.rank = 1, so it reflects whichever snapshot is currently stored.

final_placement is the canonical tournament finish, derived from the bracket on every pipeline run:

  • 1 — bracket champion (winner of the finals match).
  • 2 — runner-up.
  • 3, 4 — semifinal losers, tiebroken by swiss rank ASC.
  • 5–8 — quarterfinal losers, tiebroken by swiss rank ASC.
  • 9–16, etc. — earlier elimination rounds, same tiebreak.
  • top_cut_size + 1 … — swiss-only players, ordered by swiss rank ASC.
  • NULL — bracket incomplete (any top-cut match without a reported winner).

For pure-swiss tournaments (no top-cut matches), final_placement = rank. Invariant per tournament: MAX(final_placement) <= COUNT(standings). Prefer final_placement over rank for placement-aware queries; rank reflects melee's raw published order and is kept as-is for backwards compatibility.

  • GET /standings?since= — flat incremental-sync feed of final standings (one row per player per tournament).
  • GET /tournaments/:id/standings?round=N — live per-round view while an event is running (only exists for tournaments observed by the live scraper).
  • tournaments.winner_player_uuid — denormalized champion, cheap to query without joining standings.

Tournament Tiers

Competitive event tiers in Star Wars: Unlimited organized play.

Code Name Description
PQPlanetary QualifierLocal-level competitive events (official)
SQSector QualifierRegional-level competitive events (official)
RQRegional QualifierMajor regional events (official)
GCGalactic ChampionshipTop-tier championship events (official)
LCQLast Chance QualifierLast-chance entry events into championships (official)
SSStore ShowdownCasual store-level events (official, Asmodee-branded)
INVInvitationalInvitation-only competitive events (unofficial). Retired — now stored as COM.
COMCommunityCommunity-run competitive events (unofficial)
CASCasual Side EventSealed/Draft/Carbonite/On-Demand/Spotlight side events held alongside qualifier tournaments. Not official competition.

Aspect Colors

SWU has four gameplay aspects, each with a canonical color. Heroism and Villainy are alignment aspects that modify brightness.

Aspect Color Name Hex Sample
Aggression Red #A83632
Command Green #338D5A
Cunning Yellow #C5A14F
Vigilance Blue #4B86AA

Alignment Modifiers

Alignment Effect Use
Heroism Lighter, slightly desaturated Hero leaders (Han Solo, Sabine, Luke, etc.)
Villainy Darker, more saturated Villain leaders (Vader, Palpatine, Boba Fett, etc.)

Base Naming Convention

Base HP Rarity Naming Example
30CommonColor 30Red 30, Yellow 30
28CommonForce ColorForce Blue, Force Red
27CommonSplash ColorSplash Green, Splash Yellow
variesRare/SpecialCard nameTarkintown, Data Vault, ECL

Archetype Nicknames

Each archetype has a short nickname: FirstName (SET) BaseNick. Titles/ranks are stripped to get the character's name. (SET) is only included when multiple distinct leaders share the same first name.

Full Name Nickname Rule
Han Solo, Audacious Smuggler - Red 30Han Red 30Simple first name + base color + HP
Darth Vader, Dark Lord of the Sith - Force BlueVader (SOR) Force BlueTitle stripped, (SET) for disambiguation
Grand Admiral Thrawn, Patient and Insightful - Data VaultThrawn (SOR) DVMulti-word title stripped, base abbreviated
Grand Moff Tarkin, Oversector Governor - TarkintownTarkin TTSW title stripped, base abbreviated
Moff Gideon, Formidable Commander - Red 30Gideon Red 30SW title stripped
Grand Inquisitor, Hunting the Jedi - TarkintownGI TTHardcoded nickname override
The Mandalorian, Sworn To The Creed - Red 30Mando Red 30Special case
Boba Fett, Any Methods Necessary - Energy Conversion LabBoba (JTL) ECLMultiple Bobas need (SET)

Base Abbreviations in Nicknames

BaseNickname
TarkintownTT
Energy Conversion LabECL
Data VaultDV
Lake CountryLake
Red 30 / Blue 30 / etc.Red 30 / Blue 30 / etc. (HP preserved)
Force Blue / Splash Green / etc.Unchanged
Other rare basesFull name