---
title: "Get Fair Market Value (FMV) for a Card"
method: POST
path: "/v1/cards/card-fmv"
tags: ["Pricing & Valuations"]
---

# Get Fair Market Value (FMV) for a Card

`POST /v1/cards/card-fmv`

## Request

- Base URL: `https://api.cardhedger.com`
- URL: `https://api.cardhedger.com/v1/cards/card-fmv`
- Auth: API key in header `X-API-Key`

Get the Fair Market Value (FMV) for a card at a specific grade.

Requires a `card_id`. If you only have a card name/description, call
`match-card` or `card-search` first to resolve it.

## What FMV is

FMV is CardHedge's smoothed valuation for a card at a specific grade,
computed by an underlying pricing service that:

1. Looks for a direct price for this card+grade, and if found, replaces
   the raw `latest_price` with a **Winsorized-median FMV** computed over a
   7/15/30/90-day window (the shortest window with enough daily prints
   wins). Winsorizing trims outlier sales so a single hot/cold day
   doesn't move the value.
2. If no direct price exists, falls back through a cascade of related
   data: other grades of the same card → other graders → grade
   multipliers → segment baselines.
3. If the chosen base price is stale, overlays a **movement index** to
   project it to today. The index is picked from the most specific level
   with enough sample density (card+grade → card → player → segment).
4. Returns the price, a confidence score, and full metadata so callers
   can decide how much to trust the result.

Compared to `price-estimate`, the FMV response includes the smoothing
window, sample counts, confidence grade, and a human-readable
`price_explanation`.

## FMV calculation methods (the **method** field)

The `method` field tells you which cascade step produced the price. There
are **6 base methods** plus an `_indexed` variant of each non-terminal
method (10 indexed + 1 terminal = 11 possible values).

**Base methods**, in cascade priority:

| Method                | Meaning                                                                                                                                                                              |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `direct`              | Exact card+grade lookup. The 7/15/30/90-day Winsorized-median FMV is used when enough daily prints exist; otherwise raw `latest_price` is returned. See `fmv_window_days` / `fmv_sample_count`. |
| `card_interpolation`  | No direct price; isotonic regression in log-space across the card's OTHER same-provider grades, interpolated/extrapolated to the target grade. Requires 2+ supporting grades.        |
| `cross_provider`      | No same-provider data; translates from another grader (e.g., BGS 9 → PSA 9) using a learned price ratio.                                                                              |
| `anchor_multiplier`   | One same-provider grade is the anchor; a within-provider grade multiplier projects it to the target. Variants: same-provider, chained (e.g., BGS 8.5 → BGS 9 → PSA 9), raw (Raw → graded). |
| `segment_fallback`    | Last resort: uses the (set × year × category) segment baseline price and applies a grade multiplier. Lowest confidence — no card-specific data was used.                              |
| `no_data`             | Terminal: nothing in the cascade produced a usable price. `price` and the range bounds are null.                                                                                      |

**`_indexed` variants** — `direct_indexed`, `card_interpolation_indexed`,
`cross_provider_indexed`, `anchor_multiplier_indexed`,
`segment_fallback_indexed`. The base method produced a stale price (older
than the freshness threshold), so a **movement index** was overlaid to
bring it to today. The index is chosen from a cascade of entities:

- `index_level: "card_grade"` — most specific: this card at this grade
- `index_level: "card"` — blended across all grades of this card
- `index_level: "player"` — player-level (all this player's cards)
- `index_level: "segment"` — (year × category) segment-level
- `index_level: "none"` — stale, but no index met the minimum sample
  threshold (an `un_indexed_penalty` was applied to confidence)
- `index_level: null` — fresh estimate, no adjustment was needed

Examine `raw_price` (pre-adjustment) vs `price` (post-adjustment) and
`index_pct_change` to see exactly what the index changed. Filter low-signal
adjustments by requiring `index_sample_count >= 10`.

## Picking a method by use case

- **Pricing UI for end users** — Trust `confidence_grade` A/B; show
  `price_explanation` on a "How was this calculated?" tooltip. Hide or
  caveat C/D and `no_data`.
- **Portfolio valuation** — Use `price` directly; band it with
  `price_low` / `price_high` for uncertainty visualization.
- **Sensitive financial decisions** — On C-grade or any `_indexed`
  method, also show `raw_price` so the user can see what the movement
  index changed.
- **Bulk analytics** — Aggregate by `method` to see what % of cards
  have direct data vs are interpolated vs fall back to segments.

## Response field reference

See the response schema (CardFMVResponse) for the full field-by-field
description. Highlights:

- `price` / `price_low` / `price_high` — FMV and uncertainty band in USD.
- `confidence` (0-1) and bucketed `confidence_grade` (A/B/C/D) —
  single trust score folding in method, FMV quality, and index quality.
- `raw_price` — pre-adjustment price. Differs from `price` when a
  movement index was applied (`_indexed`), or shows the FMV value for
  `direct` when latest_price was replaced.
- `fmv_window_days` / `fmv_sample_count` — FMV smoothing window
  (7/15/30/90) and sample density.
- `index_level` / `index_pct_change` / `index_sample_count` /
  `index_truncated` / `as_of_date` — movement-index provenance.
- `cross_grade_fmv_count` — for cross-grade methods, how many source
  grades had their `latest_price` FMV-substituted before the cascade ran.
- `price_explanation` — plain-English derivation (debug/audit only).

Any additional fields the upstream returns are passed through unchanged.

**Example curl:**
```bash
curl -X POST -H "X-API-Key: your-api-key-here" \
     -H "Content-Type: application/json" \
     -d '{
       "card_id": "1586812246197x228181943611293700",
       "grade": "PSA 9"
     }' \
     "https://api.cardhedger.com/v1/cards/card-fmv"
```

## Request body

- CardFMVRequest: Request parameters for the card FMV endpoint.
  - `card_id` string, required: Unique CardHedge card identifier. If you only have a card name or description, call `match-card` or `card-search` first to resolve it.
  - `grade` string, required: Card grade label. Supported formats: '<Provider> <Grade>' (e.g., 'PSA 9', 'PSA 10', 'BGS 9.5', 'SGC 9', 'CGC 10'), 'Raw' for ungraded, or '<Provider> Auth' for authenticated-only.

## Response `200`

Successful Response

- CardFMVResponse: Response model for the card FMV endpoint. Mirrors the upstream FMV service response. Any additional fields returned by the upstream are preserved via ``extra='allow'`` so consumers receive the full payload even if the upstream adds new fields.
  - `price` number, nullable: Fair market value in USD (null when no data available)
  - `price_low` number, nullable: Lower bound of the FMV range (null when no data available)
  - `price_high` number, nullable: Upper bound of the FMV range (null when no data available)
  - `confidence` number, nullable: Confidence score (0-1) for the FMV calculation
  - `method` string, nullable: FMV calculation method. One of 11 values from the cascade: **Base methods (in priority order):** - `direct` — Exact card+grade lookup. Latest_price is replaced with a Winsorized-median FMV over a 7/15/30/90-day window when enough daily prints exist; otherwise latest_price is used as-is. - `card_interpolation` — No direct price; isotonic regression in log-space across the card's OTHER same-provider grades, then interpolating/extrapolating to the target grade. - `cross_provider` — No same-provider data; translates from another grader (e.g., BGS 9 → PSA 9) using a learned price ratio. - `anchor_multiplier` — Uses one same-provider grade as an anchor and applies a within-provider grade multiplier. Variants include same-provider, chained (e.g., BGS 8.5 → BGS 9 → PSA 9), and raw (Raw card price → graded). See `anchor_from_raw` / `anchor_chained_from_provider`. - `segment_fallback` — Last resort: uses the set/year × category baseline price and applies a grade multiplier. - `no_data` — No sale data could produce a value; `price` is null. **`_indexed` variants** (`direct_indexed`, `card_interpolation_indexed`, `cross_provider_indexed`, `anchor_multiplier_indexed`, `segment_fallback_indexed`) — the base method produced a stale price, so a movement index was overlaid to bring it to today. See `index_level`, `index_pct_change`, and `raw_price` for the adjustment details.
  - `freshness_days` integer, nullable: Age of the underlying sale data in days (0 = today). On `_indexed` methods this reflects the index's `as_of_date`, not the base price's age. Null on `segment_fallback` and `no_data`.
  - `support_grades` integer, nullable: Number of grade data points contributing to the estimate. 1 for `direct`; 2+ for `card_interpolation` (other grades of this card); 1 for `anchor_multiplier`; 0 for `segment_fallback` / `no_data`.
  - `grade_label` string, nullable: Normalized grade label the FMV was computed for (e.g., 'PSA 9', 'BGS 9.5', 'Raw').
  - `provider` string, nullable: Grading provider (e.g., 'PSA', 'BGS', 'SGC', 'CGC', 'Raw').
  - `grade_value` number, nullable: Numeric grade value (e.g., 9.0, 9.5, 10.0). 0 for Raw; negative values denote authenticated-only grades (e.g., 'PSA Auth').
  - `as_of_date` string, nullable: ISO 8601 date the applied movement index was last computed. Set only on `_indexed` methods; null otherwise.
  - `index_level` string, nullable: Cascade level used for movement adjustment. One of: - `card_grade` — most specific: this card at this grade - `card` — blended across all grades of this card - `player` — player-level index (all cards of this player) - `segment` — (year × category) segment-level index - `none` — stale price, but no index met the minimum sample threshold (an `un_indexed_penalty` is applied to confidence) - `null` — fresh estimate, no movement adjustment needed
  - `index_pct_change` number, nullable: Cumulative percent change applied by the movement index over the FMV's lookback window (decimal, e.g., 0.157 = +15.7%). Set only on `_indexed` methods.
  - `raw_price` number, nullable: Pre-adjustment price in USD. For `direct`: the chosen FMV value (or the original `latest_price` when no FMV qualified). For other cascade methods: the original method result before any movement adjustment. Compare `price` vs `raw_price` to see what the movement index changed.
  - `index_truncated` boolean, nullable: True if the FMV's lookback window was capped at the maximum (730 days) because the underlying price was extremely stale. Treat truncated movement adjustments as lower-confidence.
  - `fmv_window_days` integer, nullable: FMV smoothing window used for the base price. One of 7, 15, 30, or 90 — the upstream picks the shortest window with enough samples. Null when no FMV smoothing was applied (the lookup fell back to `card_grade_prices.latest_price`, or the method is not `direct`).
  - `fmv_sample_count` integer, nullable: Number of distinct daily prints that fed the chosen FMV's Winsorized median. Higher = more reliable. Null when no FMV was applied.
  - `base_sample_count` integer, nullable: Number of sales behind the base latest_price (90-day sale count on the source grade row). Only set on `direct` lookups that did NOT substitute a Winsorized-median FMV — where `fmv_sample_count` is present it describes the FMV instead. Use to tell a well-backed latest_price from a single-sale print; null means the source reported no sale count.
  - `index_sample_count` integer, nullable: Number of observations at the chosen movement-index window. Useful for filtering low-signal adjustments — e.g., ignore `index_pct_change` when `index_sample_count < 10`.
  - `confidence_grade` string, nullable: Bucketed confidence grade — a quick proxy for the numeric `confidence`: - `A` — confidence ≥ 0.42 (~86% of prices within 2× of the actual sale). Trust for valuations. - `B` — confidence ≥ 0.25 (~74% within 2×). Usable; consider showing `raw_price` too. - `C` — confidence ≥ 0.10 (~63% within 2×). Borderline; consider falling back to `latest_price` for sensitive decisions. - `D` — confidence < 0.10 (~49% within 2×). Likely noisy; show with disclaimer. Cutoffs are calibrated to measured accuracy on held-out sales and may be re-tuned as the model improves — rely on the letter grade rather than hard-coding numeric thresholds. Null on `no_data`. The numeric `confidence` already folds in cascade method, FMV signal strength, and movement-index quality, so this single grade reflects total trust in the returned price.
  - `price_explanation` string, nullable: Human-readable description of how the FMV was derived — e.g., 'Used 7d FMV ($416.25, Winsorized median of 3 daily prints) in place of latest_price $416.25. Fresh (1d) — no movement adjustment applied.' Useful for debug UIs and audit trails; not intended for programmatic parsing (wording may change).
  - `cross_grade_fmv_count` integer, nullable: For cross-grade methods (`card_interpolation`, `cross_provider`, `anchor_multiplier`): count of source grades whose `latest_price` was replaced with a Winsorized-median FMV before the cascade ran. Higher = more smoothing applied. Null when the method wasn't used; 0 when used but no FMV qualified for any source.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/cardhedger/apis/card-hedge-api.md) - [All operations](https://skmtc.dev/cardhedger/apis/card-hedge-api/llms.txt) - [OpenAPI document](https://skmtc.dev/cardhedger/apis/card-hedge-api/revisions/6de42f192a4d?raw)
