API
Every price on this site is also a file. No key, no account, no signup — just JSON on a CDN, rebuilt daily from the same snapshot that builds the pages. User-Agent policy is the one hardening rule; a real integration is unaffected by it.
If you can read a URL, you can use this.
The coverage numbers on this page are rendered from the current build. The example payloads show the current response shapes; their illustrative values move daily.
First call
curl -s https://palworldprices.com/api/v1/index.json
{
"source": "Palworld Prices",
"url": "https://palworldprices.com/api/v1/index.json",
"license": "free with attribution",
"attribution": "Data: Palworld Prices (palworldprices.com) — completed-sale prices with confidence tiers",
"terms": "Free to use, including commercially, with attribution and a link. Please do not imply endorsement, and please send a descriptive User-Agent. Card images are NOT part of this API.",
"asOf": "2026-09-17T22:30:01.508Z",
"stale": false,
"honesty": "Prices are medians of COMPLETED SALES, never asking prices. …",
"description": "A free, static, key-less JSON API over completed-sale prices for the Palworld trading card game. …",
"endpoints": [ … ],
"coverage": {
"cardsTracked": 280,
"withPublishedPrice": 254,
"confidentlyPriced": 218,
"note": "Most cards in a game this young have no completed sale yet. Those carry no price, by design."
},
"confidenceTiers": { … },
"stability": "Additive changes only within v1: fields may be added, existing fields will not be removed or repurposed. Breaking changes get a new version prefix.",
"updated": "Rebuilt daily. Every payload carries asOf and a stale flag — freshness is disclosed, never promised."
}
index.json describes itself. It is the only URL you need to memorise.
Endpoints
| URL | Returns |
|---|---|
/api/v1/index.json | This directory: endpoints, coverage counts, tier definitions |
/api/v1/cards.json | All 280 tracked printings with identity and headline price |
/api/v1/cards/{cardId}.json | One printing: headline, 7d/30d/90d aggregates, receipt count, current asks |
/api/v1/404.json | The error document. See Missing cards |
/feeds/latest-sales.xml | RSS: newest source-attributed completed sales, with a public listing link when available |
/feeds/movers.xml | RSS: 7d medians that moved against 30d, confidence-gated |
/feeds/changelog.xml | RSS: what changed on the site, in plain language |
/llms.txt | What this site is, in the format assistants read |
All served over HTTPS with Access-Control-Allow-Origin: *. Browser code can fetch them directly.
The envelope
Every JSON payload opens with the same block, so a response is self-describing no matter where it ends up.
| Field | Meaning |
|---|---|
source | "Palworld Prices" |
url | This payload's own canonical address |
license | "free with attribution" |
attribution | The exact line you must publish. See Attribution |
terms | Licence in one sentence |
asOf | ISO timestamp of the build this data came from |
stale | true when the snapshot is older than the freshness bar |
honesty | The one rule you must not break when re-displaying these numbers |
Check stale before you publish anything. It is the site's own judgment on its own data.
The honesty contract, in JSON
This is the part that makes the API different from a price feed.
A card's price lives in headline, and headline.kind has exactly three values. The shape of the object changes with the tier — that is deliberate, so a naive read cannot produce a number the data does not support.
kind: "bare" — 4 or more completed sales
"headline": {
"kind": "bare",
"confidence": "HIGH",
"window": "7d",
"basisWindow": "30d",
"estimator": "decay-median-3d",
"method": "recency-weighted median over the trailing 30-day kept-sale sample",
"medianUsd": 35,
"currency": "USD",
"salesInWindow": 15,
"salesInBasis": 22
}
Safe to display as a plain number. confidence is HIGH (10+ sales) or MEDIUM (4–9). Attach sample claims to basisWindow and salesInBasis, never to the legacy selector fields window and salesInWindow.
kind: "thin" — 1 to 3 completed sales
"headline": {
"kind": "thin",
"confidence": "LOW",
"window": "90d",
"basisWindow": "90d",
"estimator": "window-median",
"method": "plain median of the completed sales in the 90d window",
"medianUsd": 39.9,
"currency": "USD",
"salesInWindow": 1,
"salesInBasis": 1,
"qualifier": "Thin sample (1-3 completed sales) — a rough number, not a settled market price. Do not display this figure without this qualifier."
}
A number is present, and it arrives with a qualifier that is not optional. One sale is a data point, not a price. Display them together or display neither.
Headline estimator and sample basis
window is the schema-v1 adaptive selector and keeps that meaning for backward compatibility. It does not necessarily name the sample behind medianUsd. The additive fields are the price contract:
| Field | Meaning |
|---|---|
basisWindow | Window whose kept-sale sample owns the headline's n, range, dates and receipts |
estimator | decay-median-3d or window-median |
method | Reader-facing description of that estimator |
salesInBasis | Completed sales in basisWindow; this is the count to display beside medianUsd |
window | Legacy adaptive selector; preserved, never repurposed |
salesInWindow | Legacy count for window; preserved, but not necessarily the headline sample count |
decay-median-3d is a recency-weighted median over the trailing 30-day kept pool, so basisWindow is 30d. window-median is the plain median of the selected window, so basisWindow === window.
On an older snapshot where provenance cannot be proved, basisWindow and estimator are null, method and salesInBasis are absent, and no receipt count is presented as the headline sample. The price tier still travels with the number; do not invent a method or sample from window.
kind: "none" — no verified completed sale
"headline": {
"kind": "none",
"confidence": "NONE",
"note": "No verified completed sales yet — tracking live."
}
There is no medianUsd key at all. Not 0, not null — absent. A naive card.headline.medianUsd read gets undefined, which is hard to accidentally render as a price.
26 of 280 printings are in this state. Show them as "no verified sales yet".
Today's census
| Tier | confidence | Printings |
|---|---|---|
bare | HIGH | 172 |
bare | MEDIUM | 46 |
thin | LOW | 36 |
none | NONE | 26 |
Half the catalogue has never sold in a marketplace this project watches. That number is published rather than filled in.
/api/v1/cards.json
The envelope, plus count and a cards array. About 342 KB, one request, everything.
{
"cardId": "pal-BP01-001-OSR",
"code": "BP01-001",
"set": "BP01",
"number": "001",
"variant": "OSR",
"rarity": "OSR",
"rarityTier": "chase",
"names": {
"en": "Jormuntide Ignis – Savage Lava Dragon",
"jp": "荒ぶる溶岩竜 – アグニドラ"
},
"pageUrl": "https://palworldprices.com/cards/bp01/001-jormuntide-ignis-savage-lava-dragon-osr/",
"headline": { … },
"asks": { "n": 24, "lowUsd": 25 }
}
| Field | Notes |
|---|---|
cardId | Stable key and the filename for the detail endpoint |
code | Printed code. Shared by every printing of the card |
set / number | The two halves of code |
variant | OSR, SSP, SP, RR, … or null for the base printing |
rarity | Printed rarity. Can be a dual form such as "RR/SSP" |
rarityTier | chase, super-rare, rare, uncommon, common, promo |
names.en / names.jp | Either can be null |
pageUrl | The human page: price history and available source-backed sale evidence |
asks | Present only when the printing has a fresh listing. See Asks |
Printings are not interchangeable
BP01-001 exists four times, and the four price separately:
| cardId | Printing | Price |
|---|---|---|
pal-BP01-001 | base (RR) | ~$39.90 thin |
pal-BP01-001-RR | RR | $9.99 |
pal-BP01-001-OSR | OSR | $35.00 |
pal-BP01-001-SSP | SSP | $475.00 |
Note that the base printing and the -RR printing both carry printed rarity RR and differ by 4×. Key on cardId, never on code alone, and never average printings together.
/api/v1/cards/{cardId}.json
The card's identity and headline again, plus what sits underneath them.
curl -s https://palworldprices.com/api/v1/cards/pal-BP01-001-OSR.json
{
"source": "Palworld Prices",
…
"cardId": "pal-BP01-001-OSR",
"code": "BP01-001",
"names": { "en": "Jormuntide Ignis – Savage Lava Dragon", "jp": "荒ぶる溶岩竜 – アグニドラ" },
"pageUrl": "https://palworldprices.com/cards/bp01/001-jormuntide-ignis-savage-lava-dragon-osr/",
"headline": {
"kind": "bare",
"confidence": "HIGH",
"window": "7d",
"basisWindow": "30d",
"estimator": "decay-median-3d",
"method": "recency-weighted median over the trailing 30-day kept-sale sample",
"medianUsd": 35,
"currency": "USD",
"salesInWindow": 15,
"salesInBasis": 22
},
"market": "Raw · US completed sales",
"confidence": "HIGH",
"suspect": false,
"windows": {
"7d": {
"sales": 15,
"salesSettled": 15,
"medianUsd": 35,
"lowUsd": 20.5,
"highUsd": 49.99,
"iqrUsd": [29.99, 42.95],
"lastSoldAt": "2026-08-01T12:00:00.000Z",
"datePrecision": "exact",
"suspect": false
},
"30d": { … },
"90d": { … }
},
"receipts": {
"count": 22,
"basisWindow": "30d",
"note": "Source-backed receipt IDs are listed on the card page; public listing links appear when available."
},
"asks": { … }
}
| Field | Notes |
|---|---|
market | Which market the headline came from, in words. null when unpriced |
confidence | Tier of the underlying bucket, HIGH … NONE |
suspect | The build flagged something odd about the sample |
windows | 7d / 30d / 90d. A window with no sales is absent, not zeroed |
receipts.count | Number of fully resolved receipts in the estimator basis; receipts is null rather than overstating an incomplete legacy export |
asks | Current asking prices. Not a price. Absent when there is no fresh listing |
Window fields
| Field | Notes |
|---|---|
sales | Sales in the window |
salesSettled | How many are fully settled |
medianUsd | That window's plain median. Use with the window's own sales count; it may differ from the headline estimator |
lowUsd / highUsd | Range |
iqrUsd | [p25, p75], or null |
lastSoldAt | ISO timestamp of the most recent sale |
datePrecision | exact when the marketplace stated a sale time, observed when it did not and this is when the sale was confirmed |
suspect | Per-window flag |
datePrecision: "observed" means the marketplace did not publish a sale timestamp. Treat those dates as "no later than".
When a card has no verified sale, windows, receipts and market are all null and confidence is "NONE".
Asks — what sellers want
Everything above is a completed sale. asks is the one block on this site that is not, and it lives in its own top-level key for exactly that reason.
"asks": {
"n": 24,
"market": "US marketplace listings",
"lowUsd": 25,
"highUsd": 99.99,
"medianAskUsd": 40,
"windowHours": 72,
"basis": "first-seen",
"asOf": "2026-08-02T20:58:09.650Z",
"listings": [
{ "priceUsd": 25, "marketplace": "eBay", "url": "https://www.ebay.com/itm/398238804180", "firstSeenAt": "2026-08-01T17:18:10.382Z" }
],
"spreadPct": -28.6,
"soldMedianUsd": 35,
"soldWindow": "7d",
"soldBasisWindow": "30d",
"soldEstimator": "decay-median-3d",
"soldMethod": "recency-weighted median over the trailing 30-day kept-sale sample",
"note": "ASKING prices … NOT sales …"
}
| Field | Notes |
|---|---|
n | How many fresh listings were counted |
lowUsd | The cheapest one. Always present — it is one verifiable fact about one listing |
medianAskUsd | null below 4 asks. A median over one to three listings describes nothing |
windowHours | The freshness window, currently 72 |
basis | "first-seen". Read the next paragraph before you word anything |
listings | Up to the five cheapest, ascending, each with a clickable URL |
spreadPct | Optional. The cheapest ask against the completed-sale estimate of the same market |
soldWindow | Legacy selector window; preserved, not the estimator sample |
soldBasisWindow | Sample basis behind soldMedianUsd |
soldEstimator / soldMethod | Machine- and reader-facing estimator provenance |
basis: "first-seen" is load-bearing. A listing is recorded once, when it is first seen, and is not re-checked afterwards. Two departures are subtracted (we later captured the sale, or a recheck labelled it sold); everything else is bounded by the window alone. So this data supports *"24 listed from $25, seen in the last 3 days"*. It does not support "24 available now", "in stock", or "verified active" — and an ask is never a sale, so it must never fill an unpriced card, enter a price average, or be displayed as a price.
spreadPct appears only when the card has a bare-renderable sold estimate in the same market and at least two fresh asks. Missing means missing: no zero, no "flat", and do not re-derive it from the other fields. It is not a JP/US arbitrage spread — that does not exist on this site.
Missing cards
Ask for a cardId that does not exist and you get HTTP 404 with the site's HTML not-found page — served under this API's JSON content type, because the header rule matches the requested path and there is no application server here to notice the difference. The status is honest; the body is not JSON. So do not rely on the content type, and do not assume an error body you can parse. The reliable check is one line:
const r = await fetch(`https://palworldprices.com/api/v1/cards/${id}.json`);
const card = await r.json().catch(() => null);
if (!card || card.cardId !== id) throw new Error(`unknown cardId: ${id}`);
Parse, then check cardId matches what you asked for. That guard is correct whatever the host returns — today's 404 page, a future JSON error, or a stale edge copy — and it also catches the case where a card is retired between builds.
For a canonical example of the error shape, /api/v1/404.json is a real document describing exactly this convention.
Take valid cardIds from cards.json rather than constructing them.
Feeds
All three are RSS 2.0, application/rss+xml.
/feeds/latest-sales.xml — the newest individual completed sales. Each item links both the card page and the original listing.
<item>
<title>$83.21 — Lyleen – Blessing of the Goddess (BP01-049 SSP)</title>
<link>https://palworldprices.com/cards/bp01/049-lyleen-blessing-of-the-goddess-ssp/</link>
<guid isPermaLink="false">pwp-sale-mercarijp-m49105711564</guid>
<pubDate>Mon, 03 Aug 2026 01:33:10 GMT</pubDate>
<description>Last verified 2026-08-03 (the marketplace did not state the sale time, so this is when we confirmed it) on eBay US for $83.21. This is ONE completed sale, not a price — a single data point. …</description>
</item>
Each item is one sale. The wording says so, because a feed of individual sales is the easiest thing in the world to misread as a feed of prices.
/feeds/movers.xml — cards whose 7-day median moved against their 30-day median, published only when both windows rest on enough dated, deduplicated sales.
<title>▼ 5.9% — Jormuntide – Surging Sea Serpent (BP01-027 OSR) at $40.00</title>
<description>7-day median $40.00 across 13 completed sales, against a 30-day median of $42.50 across 14. That is down 5.9%. A move is published only when both windows rest on enough dated, deduplicated sales to back it. …</description>
When nothing qualifies, the feed is not published at all. An empty movers feed would be a feed that says nothing; a missing one says the same thing honestly.
/feeds/changelog.xml — what changed on the site, in plain language. The feed is the changelog's only public form. Subscribe to it if you depend on this data and want to know when the way a number is made changes.
guids are stable and namespaced (pwp-sale-…, pwp-mover-…), so readers deduplicate correctly across rebuilds.
/llms.txt
Plain text, for assistants: what this source is, how it differs from asking-price sites, the rules for quoting a number, the key URLs, and what is deliberately absent.
curl -s https://palworldprices.com/llms.txt
Freshness and caching
Rebuilt daily. Every payload carries asOf and stale; nothing here promises to be current.
Cache-Control: public, max-age=0, s-maxage=3600, stale-while-revalidate=86400
ETag: "d8b7c81c6543e3fe10d7a480495bd0e5"
max-age=0 keeps browsers honest — a price must never be browser-stale — while the edge answers instantly and revalidates behind the request. Send If-None-Match with the ETag and you will get 304s between builds.
There is no key to revoke and no per-user quota — see User-Agent policy for the one enforcement rule that exists, and note that reasonable automated rate limits may apply to any client that requests far past what a real integration needs.
User-Agent policy
Send a descriptive User-Agent — something that names your app, e.g. MyApp/1.0 (+https://example.com). It costs nothing and it is the only thing that lets a maintainer tell "someone's real integration" from "anonymous bulk scrape" when this API gets busy.
On the bulk-ish surface (/api/v1/* and the legacy /search-index.json), requests with no User-Agent at all, or an unmodified HTTP-library default (curl/8.4.0, python-requests/2.31.0, Go-http-client/1.1, and similar), get 403 with a short plain-text body pointing back here. Anything else — a browser, a custom app, this site's own client-side search, the MCP server, a curl -A "MyApp/1.0" — passes through untouched. This is a politeness gate, not a paywall: there is still no key and no account, and every field this API publishes today keeps publishing.
Stability
Additive changes only within v1. Fields may be added; existing fields will not be removed or repurposed. A breaking change gets a new version prefix.
Write your parser to ignore unknown fields.
Attribution
Free with attribution, including commercial use. Publish this line verbatim wherever you display these numbers:
Data: Palworld Prices (palworldprices.com) — completed-sale prices with confidence tiers
Link back to the card's pageUrl so a reader can inspect the available source-backed sale evidence and public listing links. Do not imply endorsement.
Card images are not part of this API, and there is no bulk image download. Card art is hosted under a publisher fan-content policy that does not extend to redistribution. Link to the card page instead.
This site is non-commercial: no ads, no affiliate links.
Licence
What you may and may not do with this data, in plain language, not legalese:
- You may use this data for any purpose, including commercial products, as long as attribution is visible wherever the numbers appear (the exact line above, or a clear equivalent) and it links back to palworldprices.com.
- You may not take this data wholesale and republish it as your own dataset, under your own name, with the attribution stripped or buried. Feeding a handful of prices into your own app with credit is fine; mirroring the whole catalogue and calling it yours is not.
- Source-backed sale evidence stays on our pages. When
receiptsis non-null, the API gives the complete estimator-basis receipt count. Send readers to the card'spageUrlfor the available source IDs and public listing links rather than trying to reproduce that evidence yourself. - Nothing here is financial advice, and nothing here comes with an accuracy or uptime guarantee. This is a free service run by one person.
If you are unsure whether a use case fits, ask: contact@palworldprices.com. That is a faster answer than guessing wrong.
The one rule
If you take one thing from this page: never publish a thin median without its qualifier, never publish a number for a none card, and never publish an ask as a price. Everything else here is a detail.
Recipes
Five most valuable confidently-priced printings, from the current build:
curl -s https://palworldprices.com/api/v1/cards.json | node -e "
let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{
const {cards}=JSON.parse(s);
cards.filter(c=>c.headline.kind==='bare')
.sort((a,b)=>b.headline.medianUsd-a.headline.medianUsd)
.slice(0,5)
.forEach(c=>console.log(c.headline.medianUsd.toFixed(2).padStart(8), c.code, c.variant||'', '—', c.names.en));
})"
2500.00 ESOUL-002 — Soul
785.00 BP01-025 SSP — Chillet – Dragon Whisperer
750.00 TD02-012 TSP — Astegon – Aegis Wyvern of Death
700.00 ESOUL-008 — Soul
450.00 TD01-001 TSP — Grizzbolt – Rumbling Tank
Render a price the honest way, in any language — the branch is always the same three cases:
function priceLabel(card) {
const h = card.headline;
if (h.kind === 'withheld') return 'Sold estimate temporarily unavailable';
if (h.kind === 'none') return 'No verified sales yet';
if (h.kind === 'thin') return `~$${h.medianUsd.toFixed(2)} (thin sample)`;
return `$${h.medianUsd.toFixed(2)}`;
}
Four branches. Write them once and the qualifier or temporary-withhold state can never go missing.
What is not here
Graded (PSA) values, pack expected-value, and JP/US arbitrage spreads. They do not exist on this site — absent rather than estimated.
Individual sale rows are not in the JSON either. When receipts is non-null, receipts.count gives the complete source-backed estimator-basis count. The card page lists the available source evidence and public listing links when supplied.
Questions
Something wrong, something missing, or a use case this does not cover: contact@palworldprices.com.
Prices are informational, from public completed-sale data. Not financial advice. Method: /methodology/. What we refuse to print and why: /accuracy/. Prices inside an assistant instead: the MCP server.