Build a Rainbow
Walk a base set's parallels end to end: find the set by partial name, enumerate its child sets, and pull each child's checklist.
What you'll learn​
- Why a "rainbow" is a walk over child sets, not an expansion of a card
- How to find a base set when you only know part of its name
- How to list a set's parallels, inserts, autograph subsets and variations
- How to read the checklist response, which puts cards in
includedrather than underdata - How to page a large checklist when the response carries no total
Before you start, read Querying Conventions — the parameter shapes used below are not uniform across resources, and the page explains why.
The modelling concept​
This is the single most important thing to get right, and it is the one that most often sends new integrations down the wrong path.
A parallel, insert, autograph subset or variation is its own set. It is a row in sets with parent_id pointing at the base set, carrying is_parallel, is_insert, is_autograph or is_variation to say what kind of release it is.
A card's children is something else entirely. It holds variations of that one specific card — not the parallel versions of the whole set.
So building a rainbow means walking sideways across sibling child sets and collecting the same card number out of each one. It does not mean fetching a card and expanding its relationships. If you find yourself calling /v1/cards/{id}?include=children and getting far fewer rows than the rainbow should have, this is why.
2024 Prizm Baseball (root set — parent_id is null)
├── Silver Prizm (is_parallel)
├── Gold Prizm /10 (is_parallel)
├── Orange Wave (is_parallel)
├── Rookie Revolution (is_insert)
└── Signatures (is_autograph)
Card #1's rainbow is card #1 from each of those child sets.
Step 1: Find the base set​
Set-name filtering is exact by default, so search with the like: prefix on the value:
curl "https://api.tradingcardapi.com/v1/sets?name=like:Prizm&per_page=25" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/vnd.api+json"
Two things to know about this call:
- Without a
nameorparent_id,/v1/setsreturns root sets only. Searching by name is what gets you past the top level of the tree. ?name=Prizmwith no prefix is an exact match and returns nothing. There are no*wildcards.
Pick the base set out of the results and keep its id.
Step 2: List the child sets​
Pass the base set's id as parent_id:
curl "https://api.tradingcardapi.com/v1/sets?parent_id=BASE_SET_UUID&per_page=100" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/vnd.api+json"
Each child comes back with the flags that classify it:
{
"data": [
{
"type": "sets",
"id": "11111111-1111-1111-1111-111111111111",
"attributes": {
"name": "Silver Prizm",
"card_count": 300,
"is_parallel": true,
"is_insert": false,
"is_autograph": false,
"is_variation": false,
"serial": null
}
}
]
}
There is no filter parameter for these flags — /v1/sets accepts filter[status] and nothing else bracketed, so filter[is_parallel]=true would be silently ignored and you would get an unfiltered page back. Fetch the children and filter them in your own code:
is_parallel— reproduces every card in the base set. These are the rainbow.is_variation— reproduces only some base cards.is_insert— its own themed checklist with its own numbering.is_autograph— a signature release, which may also be a parallel.
Child sets can themselves have children, so if you want the whole tree rather than one level, recurse on parent_id until a level returns nothing.
Step 3: Fetch each child's checklist​
For each child set you care about:
curl "https://api.tradingcardapi.com/v1/sets/CHILD_SET_UUID/checklist?per_page=100&page=1" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/vnd.api+json"
Only three query parameters exist here: format, page and per_page.
Read the checklist response​
The checklist response is shaped differently from the list endpoints, and this trips people up.
data is the set, not the cards. It is a single set resource — the checklist you asked for — with relationships.checklist listing the card resource identifiers.
The cards are in included. They are full card resources, matched to the identifiers in data.relationships.checklist by id.
{
"data": {
"type": "sets",
"id": "11111111-1111-1111-1111-111111111111",
"attributes": { "name": "Silver Prizm", "card_count": 300 },
"relationships": {
"checklist": {
"data": [
{ "type": "cards", "id": "aaaaaaaa-0000-0000-0000-000000000001" },
{ "type": "cards", "id": "aaaaaaaa-0000-0000-0000-000000000002" }
]
}
}
},
"included": [
{
"type": "cards",
"id": "aaaaaaaa-0000-0000-0000-000000000001",
"attributes": { "name": "Silver Prizm #1", "number": "1" }
},
{
"type": "cards",
"id": "aaaaaaaa-0000-0000-0000-000000000002",
"attributes": { "name": "Silver Prizm #2", "number": "2" }
}
]
}
Three consequences:
- No
include=parameter is needed. The cards are always inincludedon this endpoint.includeis not one of its parameters at all, so passing it does nothing. - Do not look for cards under
data. If you are readingdataas an array of cards, you will read a single set object instead and conclude the checklist is empty. - If you only need the cards, read
includeddirectly. The join throughdata.relationships.checklistmatters when you need the checklist's ordering or want to detect identifiers with no matching resource in the page.
Pagination without a total​
meta comes back null on this endpoint in practice, so there is no last_page or total to drive a loop with. Use the set's own card_count — which you already have, both from the Step 2 listing and from data.attributes.card_count in the response — and page until you have collected that many cards, or until a page comes back with an empty included.
Large sets​
For a large checklist where you only need identifiers and numbers, ask for the compact form:
curl "https://api.tradingcardapi.com/v1/sets/CHILD_SET_UUID/checklist?format=compact&per_page=100" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/vnd.api+json"
format=compact returns only id, number and name per card, cutting the response size by roughly 75%. That is usually all a rainbow needs, since you are joining on card number across sets.
/v2 checklist exists/v2/sets/{set}/checklist returns the same data with the shape inverted — cards as the primary data array, with the set available in included via include=set. It takes the same format, page and per_page parameters. /v2 is not a general migration target and this is currently its only endpoint, so build against /v1 unless the flatter shape is worth the exception.
Step 4: Assemble the rainbow​
You now have, for every child set, a list of cards. Group them by card number to produce the rainbow for each base card:
// childChecklists: [{ set, cards }, ...] from Step 3
const rainbows = new Map();
for (const { set, cards } of childChecklists) {
for (const card of cards) {
const number = card.attributes.number;
if (!rainbows.has(number)) {
rainbows.set(number, []);
}
rainbows.get(number).push({
setName: set.attributes.name,
isParallel: set.attributes.is_parallel,
serial: set.attributes.serial,
cardId: card.id,
});
}
}
// rainbows.get('1') — every parallel of card #1
Join on number rather than on card name: parallel sets reuse the base set's card numbers, which is exactly what makes the rainbow well-defined, while names vary with the parallel's treatment.
If you only need one player's cards​
You do not need the rainbow walk for this. Look the player up, then filter cards by their id:
curl "https://api.tradingcardapi.com/v1/players?full_name=like:Griffey" \
-H "Authorization: Bearer YOUR_TOKEN"
curl "https://api.tradingcardapi.com/v1/cards?filter[player_id]=PLAYER_UUID&per_page=25" \
-H "Authorization: Bearer YOUR_TOKEN"
Note the shape change: /v1/players filters are bare, /v1/cards filters are bracketed, and the two endpoints spell page size differently (limit versus per_page).
per_page small herePlayer-card lookups are the slowest read on the API today, and latency scales with page size. Start small and increase only if the response time suits your use case.
Common mistakes​
| Symptom | Cause |
|---|---|
| Set search returns 0 rows | Exact match — use name=like:Prizm, not name=Prizm or name=*Prizm* |
| Catalog looks tiny and vintage-only | You are reading the default /v1/sets listing, which is root sets only |
| Rainbow has only a couple of entries | You expanded a card's children instead of walking child sets |
| Checklist looks empty | You read data expecting cards; the cards are in included |
| A filter appears to do nothing | The parameter name is not valid for that resource, and unknown parameters are silently ignored |
Next steps​
- Querying Conventions — the full parameter reference and the silently-ignored-parameter hazard
- Set Types — what each set-type flag means and how parallels differ from variations
- API Reference — generated endpoint documentation
- Data Coverage — what is and is not in the catalog