Querying Conventions
Which query parameters each resource accepts, why some are bare and some are bracketed, how to search on a partial name, and the one failure mode that costs new integrators the most time.
Why this page exists​
The per-endpoint tables in the API reference list the parameters each endpoint accepts, but they cannot tell you the things that only become obvious once you have called two endpoints and noticed they disagree. The parameter names are not uniform across resources, partial-name search needs a prefix nobody guesses, and an unrecognised parameter is ignored rather than rejected — so a typo returns a plausible-looking answer instead of an error.
This page states those conventions in one place.
Parameters by resource​
There is no single filtering grammar across the API. Some filters are bare parameters, some are bracketed under filter[...], and pagination is not spelled the same way everywhere. Copy the row you need rather than generalising from another resource.
/v1/players​
| Parameter | Shape | Notes |
|---|---|---|
first_name | bare | Filter by first name |
last_name | bare | Filter by last name |
full_name | bare | Searches across both first and last name |
limit | bare | Page size, max 100, default 25 |
include | bare | Comma-separated relationships |
Players have no filter[...] parameters at all, and they paginate on limit — not per_page.
/v1/sets​
| Parameter | Shape | Notes |
|---|---|---|
name | bare | Filter by set name |
genre | bare | A genre UUID, not a genre name |
year | bare | A year UUID, not a literal year like 1989 |
parent_id | bare | Returns the child sets of the given parent |
filter[status] | bracketed | published or draft |
per_page | bare | Page size, max 100, default 25 |
limit | bare | Deprecated alias for per_page — prefer per_page |
order_by | bare | One of name, created_at, updated_at, card_count |
sort | bare | asc or desc |
include | bare | Comma-separated relationships |
Sets are the only resource that mixes both shapes: everything is bare except filter[status].
year and genre take UUIDs?year=1989 does not filter to 1989. The year parameter expects the UUID of a year record, so a literal year is silently ignored and you get an unfiltered page back. Resolve the UUID from /v1/years (and /v1/genres) first, then pass it.
/v1/cards​
| Parameter | Shape | Notes |
|---|---|---|
filter[set_id] | bracketed | Set UUID |
filter[player_id] | bracketed | Player UUID — every card the player appears on, directly or via a player-team |
page | bare | Page number |
per_page | bare | Page size, max 100, default 25 |
include | bare | One or more of set, oncard, attributes, children |
Cards are the mirror image of players: both filters are bracketed, and pagination is per_page / page — not limit, and not JSON:API's page[limit] / page[number].
Cards accept no order_by or sort. Ordering is a /v1/sets feature only.
/v1/sets/{set}/checklist​
| Parameter | Shape | Notes |
|---|---|---|
format | bare | full (default) or compact |
page | bare | Page number, starts at 1 |
per_page | bare | Page size, max 100. Omit it and the whole checklist is returned |
The checklist response has an unusual shape — cards arrive in the included array rather than nested under data. That is covered in Build a rainbow.
Quick reference​
| Resource | Filters | Page size | Page number |
|---|---|---|---|
/v1/players | bare | limit | — |
/v1/sets | bare, plus filter[status] | per_page | — |
/v1/cards | bracketed | per_page | page |
/v1/sets/{set}/checklist | — | per_page | page |
Partial matching with the like: prefix​
String filters are exact and case-insensitive by default. This is the single most common reason a search that should obviously match returns nothing:
# Exact match — returns 0 rows, because no set is named exactly "Prizm"
curl "https://api.tradingcardapi.com/v1/sets?name=Prizm" \
-H "Authorization: Bearer YOUR_TOKEN"
For a partial match, prefix the value with like::
# Partial match — returns every set whose name contains "Prizm"
curl "https://api.tradingcardapi.com/v1/sets?name=like:Prizm" \
-H "Authorization: Bearer YOUR_TOKEN"
The prefix goes on the value, not the parameter name, and applies to string filters generally — including the player name filters:
curl "https://api.tradingcardapi.com/v1/players?last_name=like:Griff" \
-H "Authorization: Bearer YOUR_TOKEN"
Note the difference from a wildcard syntax: there are no * characters. ?name=*Prizm* is not a partial-match expression — it is an exact search for a name that literally contains asterisks, and it returns nothing.
like: is documented and confirmed on set names. It is not declared per-parameter in the OpenAPI spec, so on a string filter you have not tried before, check it against a value you know exists: if the like: form returns fewer rows than an exact match, the prefix is being read as part of the literal value rather than as an operator, and you should fall back to exact matching for that filter.
like: on set namesA like: search against /v1/sets?name= can currently return a 500 for some values. The convention above is correct and is what the API is intended to do; the error is a defect on the API side and is tracked there. If you hit it, fall back to an exact name= lookup or narrow the term until the fix ships.
The default /v1/sets listing shows root sets only​
With neither name nor parent_id supplied, /v1/sets returns only sets whose parent_id is null — the root sets. Every parallel, insert, autograph subset and variation is a child of some root set, so none of them appear in the default listing.
This matters more than it sounds. The default listing is a few hundred rows, heavily weighted toward older base sets, and reading it as "the catalog" understates both the depth and the modern coverage of the data by a wide margin. What you are seeing is the top level of a tree, not the tree.
To walk down a level, pass the parent's id:
# 1. Find the base set
curl "https://api.tradingcardapi.com/v1/sets?name=like:Prizm&per_page=25" \
-H "Authorization: Bearer YOUR_TOKEN"
# 2. List its children — parallels, inserts, autograph subsets, variations
curl "https://api.tradingcardapi.com/v1/sets?parent_id=BASE_SET_UUID&per_page=100" \
-H "Authorization: Bearer YOUR_TOKEN"
Each child carries is_parallel, is_insert, is_autograph and is_variation so you can tell what kind of release it is. There is no filter parameter for those flags — read them off the child rows you get back. See Set Types for what each flag means, and Build a rainbow for the full walk.
Finding a player's cards​
Two steps: a bare-filter lookup against /v1/players, then a bracketed-filter call against /v1/cards:
# 1. Look the player up by name
curl "https://api.tradingcardapi.com/v1/players?full_name=like:Griffey" \
-H "Authorization: Bearer YOUR_TOKEN"
# 2. Fetch their cards
curl "https://api.tradingcardapi.com/v1/cards?filter[player_id]=PLAYER_UUID&per_page=25" \
-H "Authorization: Bearer YOUR_TOKEN"
filter[player_id] returns every card the player appears on, both directly and through a player-team relationship.
per_page small on player lookupsPlayer-card lookups are the slowest read on the API today, and the cost scales with page size. Start with a small per_page and increase it only if the latency is acceptable for your use case.
Unknown parameters are ignored, not rejected​
This is the one to internalise. If you send a parameter an endpoint does not recognise, the API does not return a 400 — it drops the parameter and answers the request as though you had not sent it.
# docs-lint: intentional-invalid-parameter
# filter[year] is not a parameter on /v1/cards.
# This does not error. It returns the first page of ALL cards.
curl "https://api.tradingcardapi.com/v1/cards?filter[year]=1989" \
-H "Authorization: Bearer YOUR_TOKEN"
You get 200 OK, a well-formed JSON:API body and a page of real cards — which is exactly what a correctly-filtered response looks like. Nothing in the response says the filter was discarded.
Practical defences:
- Check the shape of the result, not just the status code. If a filter that should narrow the set returns a suspiciously round number of rows, or
meta.totalmatches the unfiltered total, the filter did not apply. - Sanity-check a new filter against a value you know is rare. If the count does not change, the parameter name is wrong.
- Copy parameter names from this page or the API reference rather than inferring them from another resource — the naming genuinely is not uniform, and a name that works on
/v1/cardsmay be silently inert on/v1/players.
A related consequence: because unrecognised parameters cost nothing, a stale integration written against a guessed parameter name can run for months returning unfiltered data without ever raising an error.
Next steps​
- Build a rainbow — walk a base set's children and join a checklist end to end
- Set Types — what
is_parallel,is_insert,is_variationandis_autographmean - API Reference — the generated per-endpoint parameter tables
- Making Your First Request — response structure and error handling