Listing-aware search
POST /v1/products/search searches the catalogue. Add a listings object to the body and it also reports, for every result, what is currently for sale on the CardNexus marketplace — including the cheapest copy that can be delivered to a given country.
Two different prices
A product carries two marketplace numbers, and they answer different questions.
pricesByFinish.<finish>.cardnexus.low is the marketplace floor: the cheapest live listing anywhere, from any seller, regardless of where they ship. It sits next to cardmarket and tcgplayer and is meant to be compared with them.
availability.cheapest.price is what you pay: the cheapest listing left after your filters, including deliveryCountry.
They differ whenever the cheapest copy in the world can't reach you. A seller in Japan listing at €6 does not lower the price for a buyer in the United Kingdom who can only be served at €13.
Ask for availability
Send listings. An empty object is enough — it decorates every result without filtering anything:
curl -X POST https://public-api.cardnexus.com/v1/products/search \
-H "Authorization: Bearer cnk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Black Lotus",
"listings": { "deliveryCountry": "GB", "condition": ["NM", "LP"] }
}'
{
"data": [
{
"id": 50212,
"name": "Black Lotus",
"availability": {
"inStock": true,
"listingCount": 7,
"cheapest": {
"listingId": "65f3a2b1c8d4e9f7a6b5c4d3",
"price": { "amount": 12.50, "currency": "GBP" },
"priceEur": { "amount": 14.62, "currency": "EUR" },
"quantity": 2,
"finish": "Standard",
"condition": "NM",
"language": "en",
"seller": {
"id": "65f3a2b1c8d4e9f7a6b5c4a1",
"username": "north_arena_tcg",
"country": "GB",
"type": "pro"
}
}
}
}
],
"pagination": { "offset": 0, "limit": 50, "total": 1, "hasMore": false }
}
price is in the seller's own currency; priceEur is the same amount in euros, and is the value listings are compared on. listingCount counts every listing matching your filters, not just the one returned.
When nothing matches, availability comes back as { "inStock": false, "listingCount": 0, "cheapest": null }. Set inStock: true to drop those products from the response entirely.
Filters
| Field | Effect |
|---|---|
deliveryCountry |
Keep only sellers who ship to this country, as an ISO 3166-1 alpha-2 code |
inStock |
When true, return only products with at least one matching listing |
condition |
Keep only listings in any of these conditions |
language |
Keep only listings in any of these languages |
finish |
Keep only listings in any of these finishes |
Sellers ship within their own country and across Europe and North America. A deliveryCountry outside that set matches no listings.
Pricing a batch from the feed
The prices feed gives you every product and its marketplace floor. To turn a page of it into deliverable prices, send up to 200 ids in one call:
curl -X POST https://public-api.cardnexus.com/v1/products/search \
-H "Authorization: Bearer cnk_live_..." \
-H "Content-Type: application/json" \
-d '{
"productIds": [50212, 50213, 50214],
"limit": 200,
"listings": { "deliveryCountry": "GB", "inStock": true }
}'
Buying what you found
availability.cheapest.listingId is the id POST /v1/cart/items accepts. Because deliveryCountry applies the same rule as checkout, a listing returned here can be added to your cart:
curl -X POST https://public-api.cardnexus.com/v1/cart/items \
-H "Authorization: Bearer cnk_live_..." \
-H "Content-Type: application/json" \
-d '{
"deliveryCountry": "GB",
"items": [{ "listingId": "65f3a2b1c8d4e9f7a6b5c4d3", "quantity": 1 }]
}'
To see every listing on one product rather than only the cheapest, use GET /v1/products/{productId}/listings.
Limits
Results are ordered by catalogue relevance, not by price — availability.cheapest is the cheapest listing for each product, not a ranking across products.
offset plus limit cannot exceed 300,000 with listings. To replicate the whole catalogue, use the feeds.
listings is not accepted together with cardmarketId, tcgplayerId, or sortBy: releaseDate; those requests return 400.