Search trading cards

Search tokenized trading cards across issuers by franchise, grader, grade, and other normalized attributes.

Tokenized trading cards come from many issuers, and each one describes its cards with its own trait names. One issuer records the grader in a "Grading Company" trait, another in a "Grader" trait. The collection group endpoints map those traits onto a shared set of attributes, so a single query such as "Pokemon cards graded PSA 10" covers every issuer in the trading_cards group.

There are two endpoints, and they are meant to be used together:

  1. GET /api/v2/nfts/group/{group}/facets returns the values each attribute takes and how many NFTs match each one.
  2. GET /api/v2/nfts/group/{group} returns the NFTs that match the values you pick.

An unknown group returns 404.

Prerequisites

  • An OpenSea API key (get one here, or use the instant API key endpoint)
  • For the SDK examples: Node.js 22 or later and @opensea/sdk 12.12.0 or later
  • For the CLI examples: @opensea/cli 2.12.0 or later

Attributes

Each attribute is a query parameter on both endpoints. The example values come from the trading_cards group.

ParameterExample values
franchisePokemon, One Piece, Football, Baseball
graderPSA, CGC, BGS
grade10, 9, 8.5
subjectPikachu, Charizard, Monkey D. Luffy
year2025, 2024, 2023
set151, Topps, Panini Prizm
languageEnglish, Japanese, Korean
edition1st Edition, Holo, Reverse Holo
product_typeGraded Card, Raw Card, Sealed Product

Values are matched exactly, so read them from the facets endpoint rather than guessing at spelling.

How filters combine

  • Repeat a parameter to match any of its values: grader=PSA&grader=CGC returns cards graded by either.
  • Different parameters are AND-combined: franchise=Pokemon&grade=10 returns Pokemon cards graded 10.
  • Do not join values with commas. grader=PSA,CGC is read as a single value named "PSA,CGC" and matches nothing.

Discover values with facets

Ask for the attributes you want to offer as filters, along with any filters the user has already applied. Leave out facet_types to get every attribute.

curl -G "https://api.opensea.io/api/v2/nfts/group/trading_cards/facets" \
  --data-urlencode "facet_types=grader" \
  --data-urlencode "facet_types=grade" \
  --data-urlencode "franchise=Pokemon" \
  -H "X-API-KEY: YOUR_API_KEY"
{
  "facets": [
    {
      "type": "grade",
      "values": [
        { "value": "10", "count": 207205 },
        { "value": "9", "count": 113884 },
        { "value": "8", "count": 30723 }
      ]
    },
    {
      "type": "grader",
      "values": [
        { "value": "PSA", "count": 223391 },
        { "value": "CGC", "count": 187906 },
        { "value": "BGS", "count": 2844 }
      ]
    }
  ]
}

Counts change as cards are minted and redeemed, so expect different numbers. The type of each facet is also the query parameter that filters on it.

Each facet's counts are narrowed by the filters on the other attributes, but not by its own. With grader=PSA applied, the grader facet still lists CGC and BGS with their counts, so a filter UI can show the alternatives next to a selected value. The grade facet, on the other hand, only counts PSA cards.

Search with filters

Pass the values you picked to the search endpoint. This finds the cheapest listed Pokemon cards graded 10 by PSA or CGC:

curl -G "https://api.opensea.io/api/v2/nfts/group/trading_cards" \
  --data-urlencode "franchise=Pokemon" \
  --data-urlencode "grader=PSA" \
  --data-urlencode "grader=CGC" \
  --data-urlencode "grade=10" \
  --data-urlencode "is_listed=true" \
  --data-urlencode "sort_by=price" \
  --data-urlencode "sort_direction=asc" \
  --data-urlencode "limit=20" \
  -H "X-API-KEY: YOUR_API_KEY"

The search endpoint also accepts:

ParameterDescription
queryFull-text match on the NFT name, up to 100 characters
is_listedtrue for listed NFTs only, false for unlisted only
min_estimated_value_usd, max_estimated_value_usdEstimated value range in USD, inclusive
collection_slugsRestrict results to up to 10 collections in the group. Repeat it like an attribute. The facets endpoint accepts it too.
sort_bybest_deal (default), price, top_offer, last_sale, last_sale_date, created_date, received_date, listing_created_date, or estimated_value
sort_directiondesc (default) or asc
limit, nextPage size (1 to 100, default 50) and the cursor from the previous response

The response

Results come from every collection in the group, so one page can mix issuers and chains (for example Courtyard on Polygon and Phygitals on Solana). Each NFT includes its collection, contract, and identifier, the issuer's original traits, and two price fields:

{
  "nfts": [
    {
      "identifier": "DW4kFcx3PyZotvXwfcxKAXjF5c4FuygQqxqwdp6EJVoC",
      "collection": "collector-crypt",
      "contract": "DW4kFcx3PyZotvXwfcxKAXjF5c4FuygQqxqwdp6EJVoC",
      "token_standard": "metaplex_core",
      "name": "2023 #322 Toedscruel EX PSA 10 Japanese Sv4a-Shiny Treasure EX",
      "opensea_url": "https://opensea.io/assets/solana/DW4kFcx3PyZotvXwfcxKAXjF5c4FuygQqxqwdp6EJVoC/DW4kFcx3PyZotvXwfcxKAXjF5c4FuygQqxqwdp6EJVoC",
      "traits": [
        { "trait_type": "Grading Company", "value": "PSA", "display_type": null, "max_value": null }
      ],
      "estimated_value_usd": "15",
      "best_listing_price": { "amount": "18", "currency": "USDC", "usd": "17.99613" }
    }
  ],
  "next": "..."
}

The example is trimmed. estimated_value_usd is OpenSea's estimate of the card's value and best_listing_price is the cheapest active listing. Either can be null: not every card has an estimate, and unlisted cards have no listing price. Pass next back to get the following page. It is absent or null on the last page.

SDK

import { OpenSeaAPI } from "@opensea/sdk";

const api = new OpenSeaAPI({ apiKey: "YOUR_API_KEY" });

const { facets } = await api.nfts.getNFTGroupFacets("trading_cards", {
  facetTypes: ["grader", "grade"],
  franchise: ["Pokemon"],
});

const { nfts, next } = await api.nfts.getNFTsByGroup("trading_cards", {
  franchise: ["Pokemon"],
  grader: ["PSA", "CGC"],
  grade: ["10"],
  isListed: true,
  sortBy: "price",
  sortDirection: "asc",
  limit: 20,
});

for (const nft of nfts) {
  console.log(nft.name, nft.collection, nft.bestListingPrice?.usd);
}

Attribute filters are arrays, and the SDK sends each value as its own query parameter. Fields come back in camelCase (estimatedValueUsd, bestListingPrice). On an OpenSeaSDK instance the same methods are at sdk.api.nfts.

CLI

Every attribute flag, as well as --collection-slug and --facet-type, can be repeated:

# Grader and grade values for Pokemon cards
opensea nfts group-facets trading_cards --facet-type grader --facet-type grade --franchise Pokemon

# Cheapest listed Pokemon cards graded 10 by PSA or CGC
opensea nfts list-by-group trading_cards \
  --franchise Pokemon --grader PSA --grader CGC --grade 10 \
  --is-listed true --sort-by price --sort-direction asc --limit 20

Next steps