Static TCGPlayer Expansion Mapping
| Date | 2026-04-12 |
| Status | Accepted |
| Decision Makers | @GaultierRomon |
Context and Problem Statement
The Grand Archive API has 54 sets, TCGPlayer has 44 groups. Set names and abbreviations differ between the two platforms in several ways:
- Re:Collections: GA API uses
Re:Collection Aurelian Regent, TCGPlayer usesMordred Re: Collection, Aurelian Regent(character name prefix) - First Editions: GA API has separate sets (e.g.,
HVN 1st), TCGPlayer merges them into the base set - Promos: GA API splits by year (P22-P26), TCGPlayer has one “Promotional Cards” group
- Event Packs: GA API consolidates old event packs into one
EVPset, TCGPlayer splits per-set - Abbreviations: Some sets have different codes (
DTRvsDTR1E,AMBvsAMB1E)
We need a reliable way to map GA set codes to TCGPlayer group IDs for product-level matching.
Decision Drivers
- Expansion mapping errors cause wrong prices on all products in that expansion
- Grand Archive has only 54 sets — small enough for a complete manual mapping
- The mapping must be verifiable (every entry can be checked against both APIs)
- New sets should fall back gracefully (fuzzy matching) rather than silently fail
Considered Options
- Static deterministic map: Manually map every GA set code → TCGPlayer groupId
- Fuzzy-only matching: Use Levenshtein name similarity for all sets
- Abbreviation matching: Match by set code/abbreviation automatically
Decision Outcome
Chosen option: Static deterministic map with a fuzzy fallback for unmapped sets. All 55 GA set codes are mapped in GA_CODE_TO_TCGPLAYER_GROUP with verified groupIds.
Mapping Confidence (Tested Against Real Data)
| Tier | Sets | Tested Match Rate | Risk |
|---|---|---|---|
| EXACT (same code + name) | 26 | 100% | None |
| VERIFIED (different code, verified name) | 11 | 100% | None |
| INFERRED (1st ed → base set) | 7 | ~95% | Low |
| RISKY (event packs, promos) | 6 | 93-97% | Medium |
| NONE (no TCGPlayer) | 5 | 0% | N/A |
Key Mapping Patterns
One-to-one (most sets):
"HVN": [24070] // Abyssal Heaven → Abyssal HeavenOne-to-many (GA API consolidates, TCGPlayer splits):
"EVP": [23125, 23436, 23784, 24252] // Event Packs → 4 TCGPlayer groupsMany-to-one (GA API splits, TCGPlayer consolidates):
"P22": [23133] // All yearly promo sets → single "Promotional Cards" group
"P23": [23133]
"P24": [23133]Redirect (1st edition → base set):
"HVN 1st": [24070] // Same group as base Abyssal HeavenAbsent (no TCGPlayer equivalent):
"FTCA": [] // Empty array = known absent, don't searchProduct-Level Matching
Within mapped groups, products match by:
- Print number — zero-padded 3 digits on both sides (e.g.,
"001") - Card name — exact match after normalization (strip parenthesized suffixes, lowercase)
- Finish —
"Normal"→ Standard,"Foil"→ Foil via TCGPlayersubTypeName
Tested at 100% match rate on standard sets, 97% on promos (after parenthesis stripping).
Consequences
Positive
- 100% verifiable — every mapping was checked against both GA API and TCGPlayer data
- No false positives — deterministic lookup prevents wrong-expansion matches
- Explicit absent sets —
[]clearly marks sets without TCGPlayer data - Fuzzy fallback — new future sets not in the map get fuzzy matching automatically
Negative
- Manual maintenance — new sets require adding a mapping entry
- 55 entries to maintain — though most won’t change once verified
- Promo collisions — different promo cards can share print numbers within TCGPlayer’s single promo group (1 known case: Apotheosis Rite vs Sacramental Rite at #000)
Impacted Files
| File | Role |
|---|---|
grand-archive-tcgplayer.utils.ts | Static GA_CODE_TO_TCGPLAYER_GROUP map |
grand-archive-tcgplayer.service.ts | Matching service with deterministic + fuzzy tiers |
Last updated on