---
title: "Get Newest Clean Sales by Text Search and Grade (Commercial Agreement Required)"
method: POST
path: "/v1/cards/sales-by-search"
tags: ["Pricing & Valuations"]
---

# Get Newest Clean Sales by Text Search and Grade (Commercial Agreement Required)

`POST /v1/cards/sales-by-search`

## Request

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

Return the newest N clean sales for a text-described card and grade.

Commercial access is required. The authenticated API accepts Enterprise
tier keys; paid calls use the Agent API adapter for this same handler.

Sports queries require a release year (including seasons such as 2023-24)
before retrieval. Non-sports queries do not require a year. Category and
clear sports product/category words establish whether this rule applies;
ambiguous uncategorized text is not assumed to be sports.
TCG queries require a card number, such as #4, 4/102, or OP01-001.
A year, grade, set code or explicitly labeled serial is not a card number.
sales-by-search is not a general free-text search. Use card identifiers to
specify one card: player/character, set/product, card number and variant
where applicable. Put the grade in the grade field. A catalog card_id is
not required.

The public contract deliberately has no date range. ``last_n`` is filled
*after* filtering: the service keeps scanning newest-first index pages until
N sales survive, the index is exhausted, or the configured safety limit is
reached.

Text retrieval runs concurrently with optional, budgeted ``card-match``
enrichment. A match must validate against the original query before its
header or exact card-ID stream is used. Timeouts and rejected matches fall
back to an inferred header. Inspect ``results.coverage_complete`` and
``partial_reasons`` for deadline, unresolved-listing, and identity ambiguity.

## Request body

- SalesBySearchRequest: Find the newest clean sales for a text-described card and grade.
  - `query` string, required: sales-by-search is not a general free-text search. Use card identifiers to specify one card: player/character, set/product, card number and variant where applicable. Put the grade in the grade field. The card does not need to exist in the Card Hedge catalog. Sports searches require a four-digit release year or season, such as 2025 or 2023-24. Non-sports searches do not require a year. TCG searches require a card number, such as #4, 4/102, or OP01-001.
  - `grade` string, required: Exact grade to return, such as PSA 10, BGS 9.5, or Raw.
  - `last_n` integer, nullable: Number of newest matching sales to return. This operation is available only to Enterprise API keys and paid Agent API calls; eligible callers may request 1-20. When omitted, it defaults to 10.
  - `category` string, nullable: Optional card category used by card-match, such as Basketball or Pokemon. Supply this when the text does not clearly identify sports versus non-sports; non-sports searches do not require a year. Listed TCG names, including Pokemon, Lorcana and Yu-Gi-Oh, require a card number. Names are matched case-insensitively, including documented accent, spacing and hyphen variants.

## Response `200`

Curated card header when card-match succeeds, plus the newest sales that survive grade and sibling-variant filtering

- SalesBySearchResponse: Curated-or-inferred header plus newest clean sales.
  - `header` SalesSearchHeader, required: Curated card data when available, otherwise a clearly inferred identity.
    - `source` 'curated_card' | 'inferred', required
    - `card_match_status` 'matched' | 'not_found' | 'unavailable', required
    - `enrichment_status` 'validated' | 'not_found' | 'unavailable' | 'timeout' | 'rejected' | 'disabled'
    - `card` CardMatchResult: The top matched card with confidence score.
      - `confidence` number, required: Confidence score from 0.0 to 1.0 that this is the correct card
      - `reasoning` string, required: Brief explanation of why this card was selected as the best match
      - `description` string, required: Full card description
      - `player` string, required: Player or character name
      - `set` string, required: Card set name
      - `number` string, required: Card number in set
      - `variant` string, required: Card variant (e.g., 'Base', 'Reverse Foil')
      - `card_id` string, required: Unique card identifier
      - `image` string, required: Card image URL
      - `category` string, required: Card category
      - `prices` CardPrice[]: Price information at different grades
        - `grade` string, required: Card grade (e.g., 'Raw', 'PSA 10')
        - `price` string, required: Price in string format
      - `subset` string, nullable: Card subset within the set (e.g. 'Base Set', 'Chrome'); null when the card has no subset
    - `inferred` SalesSearchInferredIdentity: Non-catalog identity retained when card-match cannot resolve a card.
      - `display_name` string, required: Cleaned text identity searched
      - `query` string, required: Original caller-supplied search text
  - `query` string, required
  - `search_query_used` string, required: Primary cleaned index query; the original query is also searched when cleaning changed it. When both retrieve too few sales, one broader query (subject and card number) is also searched.
  - `grade` string, required
  - `results` SalesSearchResultInfo, required
    - `requested` integer, required
    - `returned` integer, required
    - `candidates_scanned` integer, required
    - `complete` boolean, required: True when last_n sales were returned or the index was exhausted; false when the safety scan limit was reached first.
    - `index_exhausted` boolean, required
    - `scan_limit_reached` boolean, required
    - `classifier_mode` 'off' | 'shadow' | 'enforce', required
    - `coverage_complete` boolean: True only when the newest required prefix is resolved without ambiguity, uncertain candidates, or a safety deadline/limit. Use this, not complete alone, for coverage guarantees.
    - `deadline_reached` boolean
    - `unresolved_candidates` integer: Uncertain or unfinished candidates encountered before satisfying last_n; newer qualifying sales may be missing.
    - `identity_ambiguous` boolean: The query has not established a unique printing; specify year, product, card number and variant.
    - `partial_reasons` string[]
  - `exclusions` SalesSearchExclusions, required: Counts explaining how raw candidates were reduced to clean sales.
    - `duplicate` integer
    - `wrong_grade` integer
    - `wrong_card` integer
    - `sibling_variant` integer
    - `wrong_item` integer
    - `uncertain` integer
  - `sales` SalesSearchSale[]
    - `id` string, required: Stable price-history or index document ID
    - `title` string, required
    - `sale_date` string, required: Authoritative UTC sale timestamp
    - `price` number, required
    - `grade` string, required: Canonical requested grade
    - `grader` string, required
    - `price_source` string, nullable
    - `sale_type` string, nullable
    - `sale_url` string, nullable
    - `image` string, nullable
    - `card_id` string, nullable: Indexed Card Hedge card ID when the sale is curated/linked.
    - `match_method` 'curated_id' | 'deterministic' | 'model', required

## Other responses

- `400`: The grade is unknown or the cleaned query contains no card identity
- `403`: Enterprise tier required on the authenticated API; paid Agent calls use /v1/agent/cards/sales-by-search
- `422`: Request validation error, missing sports year or TCG card number, or last_n exceeds 20
- `503`: The price-history search service is unavailable
- `504`: The request deadline expired before grade resolution

## Changes

- **2026-09-12** `50d06a3a1edc`: 7 info
  - added the non-success response with the status `504`
  - added the optional property `header/enrichment_status` to the response with the `200` status
  - added the optional property `results/coverage_complete` to the response with the `200` status
  - added the optional property `results/deadline_reached` to the response with the `200` status
  - …3 more

[Operation history](https://skmtc.dev/cardhedger/apis/card-hedge-api/changes/v1/cards/sales-by-search/post.md)

---

[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)
