Mudwater Cards API

Card search, card facts and live prices for every Mudwater card, digital and printed, as JSON. Free; a credit back is appreciated.

Overview

Every call starts with

https://mudwatercards.com/api/v1/

The API answers with what the site's own pages show: the same copies of OpenSea, Magic Eden, eBay, BAYC P2P and Grimy’s Card Shack, the same sales log, the same counts of copies on the Solana chain. Nothing is worked out for the API alone, so a number here always matches the card page it links to, and a card page is where a reader can see the listings and sales behind it.

It is meant for the things collectors build around the cards: a scanner app that needs a price for the card it just read, a rip stream showing what a pull is worth, a Discord bot answering !price gary, a spreadsheet. Four endpoints:

  • GET /cards find a card by name, number or trait
  • GET /cards/{id} one card's facts, print runs, copies on chain, art
  • GET /cards/{id}/prices its digital and physical prices, last sales and medians, per finish
  • GET /prices a digital and a physical value per card and finish for the whole set, in one file

Quick start, from a shell or a web page:

curl "https://mudwatercards.com/api/v1/cards?q=gary"

fetch("https://mudwatercards.com/api/v1/cards/49/prices")
  .then(r => r.json())
  .then(p => console.log(p.finishes.base.digital.value?.usd, p.finishes.base.physical.lowest?.usd));

Access & limits

No key. Every endpoint is open. Each IP address may make 60 requests an hour that reach the server; a repeat of the same URL within 5 minutes is answered from the cache and does not count. The headers on every answer:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=300
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-RateLimit-Limit, Retry-After
X-RateLimit-Limit: 60

Answers are shared through a cache, so none carries a count of what you have left (it would be someone else’s): the limit is the one figure, and the 429 is the signal. Past the limit:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-RateLimit-Limit, Retry-After
Retry-After: 1380
X-RateLimit-Limit: 60

{
  "error": {
    "code": "rate_limited",
    "message": "60 requests an hour per IP address. Try again in 23 minutes."
  }
}

Retry-After is in seconds. If your app needs more, say so: a key with a higher limit is the planned next step, and a key will never change what an endpoint returns.

Say who you are, if you like. An optional app parameter naming your app (app=tiltrips-overlay), or on a server-side client a User-Agent that names it, is logged and never required. It lets us see what is used and warn the maker before anything changes; to be warned, tell @KaijuTheApe what you built and how to reach you. Keep one spelling of the name: the cache is per URL, app included. There is no custom request header for it, because a browser would have to preflight every call.

From a browser. Every answer carries Access-Control-Allow-Origin: * and exposes X-RateLimit-Limit and Retry-After to scripts, so a web page or an OBS browser overlay can call the API directly, with nothing to preflight. Only GET is served.

Caching. Responses are Cache-Control: public, max-age=300. Prices move about every 5 to 30 minutes on the site itself, so polling faster than that only returns the same answer. A copy of a feed that is past its refresh time is served as it is, with its as_of, never refused.

Conventions

id
A card’s id is its set and number, swamp-origins:049, or apefest-2026:prm-01 for a promo: unique across every set there will be, and the same shape as the card’s page address. The short forms 49, 049 and prm-01 are taken wherever an id is and mean the Swamp Origins card or the ApeFest promo, today and for as long as v1 answers; a later set’s cards are reached by their full id.
finish
base, foil or serialized. Serialized data exists only for the cards that have a Serialized edition on chain; promo cards have one finish, any, since nobody is sure which promos exist in both.
venue
opensea, magiceden, ebay, p2p (BAYC P2P) or grimys (Grimy’s Card Shack).
Case
Ids, parameters and their values are case-insensitive: rarity=legendary, PRM-01. Values in answers are as the card prints them, "Legendary", "Club"; finish, venue and basis keys are lower case.
usd
Dollars as a JSON number (512.0, 498.12), never a string. A native amount sits beside it where there is one: sol for digital copies, ape for BAYC P2P.
estimate
true wherever the site prints a “~”: an APE price at today’s rate, a store that sets shipping at checkout, a seller abroad whose price is converted. false is a price you would pay as printed.
Physical prices
usd is what the copy costs: the item plus its stated shipping, and no sales tax (the site adds a visitor’s own state’s tax, which an API cannot know). item and shipping are its two parts; shipping is 0.00 when it is free or included, and null with shipping_at_checkout true when the seller sets it at checkout, in which case usd is the item alone and an estimate.
Times
ISO 8601 in UTC, 2026-11-11T14:02:00Z; a sale known by its day alone is a date, 2026-11-02. Every priced answer carries as_of: when each feed was last read.
null
A value we do not have is null; the field is still there. A card with nothing listed has "lowest": null and "listed": 0, not a missing key. In CSV, null is an empty field.
Versions
The path carries the version, /api/v1/. Within v1 fields may be added and are never renamed or removed. A change that would break a client becomes /api/v2/, and v1 keeps answering. The same description is machine-readable at /api/v1/openapi.json.
attribution
Every answer with a price carries a credit line and the page to link, for apps that want to show where a figure came from, see Attribution.

Card info

GET/api/v1/cards/{id}

Everything known about one card that is not a price: its traits and edge values from Yuga’s card data, the fixed print run of each finish, how many copies exist on the Solana chain and how many wallets hold one, and the art per finish from this site’s own copies.

ParameterTypeMeaning
idpathswamp-origins:049, or the short forms 49, 049, prm-01. An unknown id answers 404.

GET /api/v1/cards/49

{
  "id": "swamp-origins:049",
  "set": "swamp-origins",
  "number": 49,
  "label": "#049",
  "name": "Good Boy Gary",
  "aliases": [],
  "rarity": "Legendary",
  "level": 5,
  "type": "Club",
  "subset": "Dog Food",
  "artist": "Chomps",
  "edges": { "top": 7, "right": 9, "bottom": 7, "left": 3 },
  "finishes": {
    "base": {
      "printed": 1320,
      "on_chain": 50,
      "holders": 43,
      "image": "https://mudwatercards.com/cards/049-good-boy-gary.webp",
      "thumb": "https://mudwatercards.com/cards/049-good-boy-gary-480.webp"
    },
    "foil": {
      "printed": 136,
      "on_chain": 4,
      "holders": 4,
      "image": "https://mudwatercards.com/cards/049-good-boy-gary-foil.webp",
      "thumb": "https://mudwatercards.com/cards/049-good-boy-gary-foil-480.webp"
    },
    "serialized": {
      "printed": 1,
      "on_chain": 0,
      "holders": 0,
      "image": "https://mudwatercards.com/cards/049-good-boy-gary-serialized.webp",
      "thumb": "https://mudwatercards.com/cards/049-good-boy-gary-serialized-480.webp"
    }
  },
  "on_chain_as_of": "2026-11-11T03:44:00Z",
  "url": "https://mudwatercards.com/swamp-origins/049-good-boy-gary",
  "opensea": "https://opensea.io/collection/mudwater-cards?search[stringTraits][0][name]=Card%20Number&search[stringTraits][0][values][0]=49",
  "attribution": {
    "text": "Card data by MudwaterCards.com",
    "url": "https://mudwatercards.com/swamp-origins/049-good-boy-gary"
  }
}
level
A number, 1 to 5: on every set card it is the rarity’s rank (Common 1 … Legendary 5).
edges
The four edge values printed on the card.
finishes.*.printed
The fixed print run, the official “printed” figure. How many CAN exist.
finishes.*.on_chain
Copies that exist on the Solana chain right now, i.e. pulled from packs so far; on_chain_as_of says when they were counted (every few hours).
finishes.*.holders
Wallets holding at least one copy of that finish.
finishes.*.image
The full-size rendering (1621 × 2250) on this site; thumb the 480-wide copy. A Serialized rendering is present only where Yuga has published one.
Promo cards
Carry "set": "apefest-2026", one finish keyed any with printed, on_chain, holders and image all null (nothing is published, promos are not on chain, and the site has a scan of each rather than a rendering: thumb), opensea null, and the traits read off the printed card.

Card prices

GET/api/v1/cards/{id}/prices

One card’s market, per finish and per side. The digital side is OpenSea and Magic Eden: the lowest copy, the last sale, 7- and 30-day medians, and one value. The physical side is eBay (US sellers), BAYC P2P and Grimy’s Card Shack: the lowest printed copy, the last confirmed sale, the 7-day median, and its own value. The two sides sit far apart on many cards (Gary’s Foil: $1,377 digital against $118 printed), so show the side your users are looking at: the printed card in their hand, or the pack they opened on chain. Pool and self-trades are left out of the sales, as on the card pages.

ParameterTypeMeaning
idpathswamp-origins:049, or the short forms 49, 049, prm-01.
finishstringOptional: base, foil or serialized to get that finish alone. Default: every finish the card has.

GET /api/v1/cards/49/prices

{
  "id": "swamp-origins:049",
  "set": "swamp-origins",
  "number": 49,
  "label": "#049",
  "name": "Good Boy Gary",
  "rarity": "Legendary",
  "rules": {
    "digital": "7-day median sale with 3+ sales; else the 30-day median with 3+; else 1-2 sales in 30 days blended with the lowest listing; else the lowest listing with 3+ listed; else the rarity's median in that finish; never above the lowest listing",
    "physical": "7-day median sale with 3+ sales; else the 30-day median with 3+; else 1-2 sales in 30 days blended with the lowest copy; else the lowest copy; never above it"
  },
  "finishes": {
    "base": {
      "digital": {
        "value": { "usd": 512.0, "basis": "median_7d", "sales": 5 },
        "lowest": {
          "usd": 530.4,
          "sol": 6.1,
          "venue": "opensea",
          "url": "https://opensea.io/item/solana/5aB3…"
        },
        "listed": 3,
        "last_sold": { "usd": 498.12, "sol": 5.73, "at": "2026-11-10T22:08:00Z", "venue": "opensea" },
        "median_7d": { "usd": 512.0, "sales": 5 },
        "median_30d": { "usd": 505.5, "sales": 19 }
      },
      "physical": {
        "value": { "usd": 455.0, "basis": "lowest_listing", "sales": null, "estimate": false },
        "lowest": {
          "usd": 455.0,
          "item": 450.0,
          "shipping": 5.0,
          "shipping_at_checkout": false,
          "estimate": false,
          "venue": "ebay",
          "condition": "Near Mint",
          "country": "US",
          "url": "https://mudwatercards.com/?go=ebay&card=49&base=1"
        },
        "listed": 2,
        "last_sold": { "usd": 425.0, "at": "2026-11-04T19:20:00Z", "venue": "ebay", "estimate": false },
        "median_7d": null
      }
    },
    "foil": {
      "digital": {
        "value": { "usd": 1313.5, "basis": "few_sales", "sales": 1 },
        "lowest": {
          "usd": 1377.0,
          "sol": 15.83,
          "venue": "opensea",
          "url": "https://opensea.io/item/solana/9kQ1…"
        },
        "listed": 3,
        "last_sold": { "usd": 1250.0, "sol": 14.21, "at": "2026-11-02T03:15:00Z", "venue": "opensea" },
        "median_7d": null,
        "median_30d": null
      },
      "physical": {
        "value": { "usd": 118.0, "basis": "lowest_listing", "sales": null, "estimate": true },
        "lowest": {
          "usd": 118.0,
          "item": 118.0,
          "shipping": 0.0,
          "shipping_at_checkout": false,
          "ape": 120.0,
          "estimate": true,
          "venue": "p2p",
          "condition": null,
          "country": null,
          "url": "https://mudwatercards.com/?go=p2p&card=49&foil=1"
        },
        "listed": 1,
        "last_sold": null,
        "median_7d": null
      }
    },
    "serialized": {
      "digital": { "value": null, "lowest": null, "listed": 0, "last_sold": null, "median_7d": null, "median_30d": null },
      "physical": { "value": null, "lowest": null, "listed": 0, "last_sold": null, "median_7d": null }
    }
  },
  "as_of": {
    "digital": "2026-11-11T14:02:00Z",
    "physical": "2026-11-11T13:51:00Z",
    "sales": "2026-11-11T14:02:00Z"
  },
  "url": "https://mudwatercards.com/swamp-origins/049-good-boy-gary",
  "attribution": {
    "text": "Prices by MudwaterCards.com",
    "url": "https://mudwatercards.com/swamp-origins/049-good-boy-gary"
  }
}
rules
The two value rules, as text, in every answer, so a reader of the raw JSON knows what the figures are.
digital.value
One digital figure to show when there is room for one, from sales before asks: the 7-day median sale with three or more sales; else the 30-day median with three or more; else one or two sales in 30 days blended with the lowest listing (the ask counts as one more opinion); else the lowest listing when three or more copies are listed; else the median of that rarity in that finish. Never above the lowest listing, which is what the card costs right now. basis says which step gave it, median_7d, median_30d, few_sales, lowest_listing or rarity_median; sales is the count behind a median or a blend, null otherwise. The whole is null when nothing prices the finish.
physical.value
The printed side’s figure on the same ladder: the 7-day median printed sale with three or more; else the 30-day median; else one or two sales blended with the lowest printed copy; else the lowest copy, however many are listed; never above it. basis median_7d, median_30d, few_sales or lowest_listing; estimate true when the lowest copy’s figure is one and took part. Printed sales are still few, so expect lowest_listing on most cards.
digital.lowest
The cheapest copy on OpenSea or Magic Eden, whichever wins, with its own listing’s address. listed, beside it, counts both markets.
digital.last_sold
The newest sale on chain of that finish, at the SOL rate of the moment it sold.
physical.lowest
The cheapest printed copy of that finish among US eBay sellers, BAYC P2P and Grimy’s: the one the card page’s Live prices row shows. Its url goes through this site’s redirect, which checks the listing is still up and lands on it.
physical.listed
Every printed copy of that finish for sale across those markets, sellers abroad included.
physical.last_sold
The newest confirmed sale of a printed copy: an eBay sale or a BAYC P2P sale. estimate is true when eBay did not state the shipping. A sale the site found through eBay’s sold search is known by its day alone, so at is then a date, 2026-11-02; one the site’s own check caught is timed within about half an hour.
serialized
Present only for the cards with a Serialized edition: digital asks and on-chain sales, no medians and no value (a serial-numbered copy is its own item, and a rule for a crowd fits it badly).
Promo cards
One finish keyed any; digital is there with every field null (promos are printed only) and physical is on the same rules.

All values

GET/api/v1/prices

The whole set in one file: one row per card and finish with its digital and its physical value, the rules that gave them, the lowest digital and printed copies and the last sale of each kind. This is the call for a stream overlay or a bot: fetch it once, look pulls up locally, fetch again when it is a few minutes old. 120 cards × Base and Foil, then the 8 promos as any: 248 rows, about 80 KB.

ParameterTypeMeaning
finishstringOptional: base or foil for the set cards’ rows of that finish, any for the promos alone. Without it, all 248 rows.
formatstringOptional: csv for the same rows as comma-separated text with a header line, for a spreadsheet. A field holding a comma or a quote is quoted (RFC 4180); null is an empty field.

GET /api/v1/prices

{
  "as_of": {
    "digital": "2026-11-11T14:02:00Z",
    "physical": "2026-11-11T13:51:00Z",
    "sales": "2026-11-11T14:02:00Z"
  },
  "rules": {
    "digital": "7-day median sale with 3+ sales; else the 30-day median with 3+; else 1-2 sales in 30 days blended with the lowest listing; else the lowest listing with 3+ listed; else the rarity's median in that finish; never above the lowest listing",
    "physical": "7-day median sale with 3+ sales; else the 30-day median with 3+; else 1-2 sales in 30 days blended with the lowest copy; else the lowest copy; never above it"
  },
  "count": 248,
  "data": [
    {
      "id": "swamp-origins:049", "set": "swamp-origins", "number": 49, "name": "Good Boy Gary", "rarity": "Legendary", "finish": "base",
      "digital_value": 512.0, "digital_basis": "median_7d", "digital_low": 530.4, "digital_last_sold": 498.12,
      "physical_value": 455.0, "physical_basis": "lowest_listing", "physical_low": 455.0, "physical_venue": "ebay", "physical_last_sold": 425.0,
      "url": "https://mudwatercards.com/swamp-origins/049-good-boy-gary"
    },
    {
      "id": "swamp-origins:049", "set": "swamp-origins", "number": 49, "name": "Good Boy Gary", "rarity": "Legendary", "finish": "foil",
      "digital_value": 1313.5, "digital_basis": "few_sales", "digital_low": 1377.0, "digital_last_sold": 1250.0,
      "physical_value": 118.0, "physical_basis": "lowest_listing", "physical_low": 118.0, "physical_venue": "p2p", "physical_last_sold": null,
      "url": "https://mudwatercards.com/swamp-origins/049-good-boy-gary?f=foil"
    },
    {
      "id": "swamp-origins:073", "set": "swamp-origins", "number": 73, "name": "The Spare", "rarity": "Common", "finish": "base",
      "digital_value": 1.2, "digital_basis": "median_7d", "digital_low": 1.18, "digital_last_sold": 1.2,
      "physical_value": 5.91, "physical_basis": "lowest_listing", "physical_low": 5.91, "physical_venue": "ebay", "physical_last_sold": null,
      "url": "https://mudwatercards.com/swamp-origins/073-the-spare"
    },
    {
      "id": "apefest-2026:prm-01", "set": "apefest-2026", "number": null, "name": "Away Game", "rarity": "Uncommon", "finish": "any",
      "digital_value": null, "digital_basis": null, "digital_low": null, "digital_last_sold": null,
      "physical_value": 95.0, "physical_basis": "lowest_listing", "physical_low": 95.0, "physical_venue": "ebay", "physical_last_sold": 80.0,
      "url": "https://mudwatercards.com/apefest-2026/prm-01-away-game"
    }
  ],
  "attribution": {
    "text": "Prices by MudwaterCards.com",
    "url": "https://mudwatercards.com/"
  }
}

GET /api/v1/prices?format=csv

id,set,number,name,rarity,finish,digital_value,digital_basis,digital_low,digital_last_sold,physical_value,physical_basis,physical_low,physical_venue,physical_last_sold,url
swamp-origins:049,swamp-origins,49,Good Boy Gary,Legendary,base,512.00,median_7d,530.40,498.12,455.00,lowest_listing,455.00,ebay,425.00,https://mudwatercards.com/swamp-origins/049-good-boy-gary
swamp-origins:049,swamp-origins,49,Good Boy Gary,Legendary,foil,1313.50,few_sales,1377.00,1250.00,118.00,lowest_listing,118.00,p2p,,https://mudwatercards.com/swamp-origins/049-good-boy-gary?f=foil
digital_value
The card’s digital.value from card prices, on the same rule; digital_basis names it.
physical_value
Its physical.value; physical_basis names the rule.
digital_low
The lowest digital listing in dollars, or null with nothing listed.
physical_low
The cheapest printed copy, item plus stated shipping, no tax; null with none for sale.
physical_venue
Where that copy is: ebay, p2p or grimys. A BAYC P2P or Grimy’s figure is an estimate (the site prints “~”); eBay’s is the price as listed.
digital_last_sold
The newest sale on chain of that card and finish, in dollars.
physical_last_sold
The newest confirmed sale of a printed copy, in dollars.
Serialized
Not in this file: a serial-numbered copy is its own item and a rule for a crowd fits it badly. Card prices has its asks and sales.

Errors

An error is a JSON body of one shape with the matching HTTP status, never an HTML page.

{
  "error": {
    "code": "not_found",
    "message": "No card \"149\". Set cards are 1 to 120, promos prm-01 to prm-08; ids read swamp-origins:049 or apefest-2026:prm-01."
  }
}
StatusCodeWhen
400bad_requestA parameter has a value the endpoint does not take, e.g. finish=gold.
404not_foundNo card has that id, or the path is not an endpoint.
405method_not_allowedAnything but GET (or HEAD).
429rate_limitedOver the hourly limit; Retry-After says how long to wait.
503unavailableNo copy of a feed the answer needs exists yet, which only happens right after a deploy. Retry in a minute. (A copy that is merely old is served, with its as_of.)

Terms

By using the API you agree to these. The prices and listings come from third-party marketplaces and stay subject to their terms, eBay’s API License Agreement included, with eBay as a third-party beneficiary of this agreement. The data is for showing to people: not for training AI models, and not for reselling as a feed. Access may end at any time, for everyone or for one app, as may any one marketplace’s part of it. Listings change by the minute, so nothing here is a guarantee of a price. Mudwater, Swamp Origins and BAYC are trademarks of Yuga Labs; this site is fan-made and affiliated with neither Yuga Labs nor any marketplace.

Attribution

The API is free, with no condition on it. One ask: where a price from it shows, a small “Prices by MudwaterCards.com” with a link to the card’s page would mean a lot. It lets your users see the listings and sales behind the number, and it is how the site that keeps these feeds running gets found. Every priced answer carries the text and the address in its attribution field, so it is one line of code; mark the link however you like (plain, nofollow, sponsored).

And tell @KaijuTheApe what you built: it gets a row on the Links page, and a heads-up before anything here changes.

Not in v1

Left out on purpose, so what ships is small and stays up. Ask if one of these would matter to you.

  • Sealed product: booster box, pack and case prices (the Sealed pages have them).
  • Sales history per card, beyond the last sale and the medians.
  • What a wallet holds.
  • Sales tax: a state parameter for an all-in physical price.
  • Keys with higher limits, webhooks or alerts, anything that writes.

Reference as of Nov 11, 2026. Machine-readable: openapi.json.