Skip to main content

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​

ParameterShapeNotes
first_namebareFilter by first name
last_namebareFilter by last name
full_namebareSearches across both first and last name
limitbarePage size, max 100, default 25
includebareComma-separated relationships

Players have no filter[...] parameters at all, and they paginate on limit — not per_page.

/v1/sets​

ParameterShapeNotes
namebareFilter by set name
genrebareA genre UUID, not a genre name
yearbareA year UUID, not a literal year like 1989
parent_idbareReturns the child sets of the given parent
filter[status]bracketedpublished or draft
per_pagebarePage size, max 100, default 25
limitbareDeprecated alias for per_page — prefer per_page
order_bybareOne of name, created_at, updated_at, card_count
sortbareasc or desc
includebareComma-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​

ParameterShapeNotes
filter[set_id]bracketedSet UUID
filter[player_id]bracketedPlayer UUID — every card the player appears on, directly or via a player-team
pagebarePage number
per_pagebarePage size, max 100, default 25
includebareOne 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​

ParameterShapeNotes
formatbarefull (default) or compact
pagebarePage number, starts at 1
per_pagebarePage 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​

ResourceFiltersPage sizePage number
/v1/playersbarelimit—
/v1/setsbare, plus filter[status]per_page—
/v1/cardsbracketedper_pagepage
/v1/sets/{set}/checklist—per_pagepage

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.

Confirm the prefix on a filter you have not used before

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.

Known issue with like: on set names

A 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.

Keep per_page small on player lookups

Player-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.total matches 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/cards may 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​