# Using the CardSight AI API - Pricing Search (Free-Text Title)

## Overview
Search historical pricing by free-text listing title when you don't have a card ID. This is a fuzzy search over marketplace listing titles that returns a **flat, relevance-ranked list spanning many cards** — completed auction sales (the "bid" side) and Buy It Now asking prices (the "ask" side, not necessarily a completed sale). Results may include listings our AI **isn't confident in matching to a canonical card** — useful for surfacing those listings, or sellers who use unusual titles. Each result carries the canonical card it matched (when our AI is confident) and grade context for graded listings.

This differs from the single-card pricing endpoint (`GET /v1/pricing/{card_id}`), which returns a nested `raw`/`graded` structure for one known card. Use pricing search when you only have text (a player's name, year, and set) rather than a UUID.

## Endpoint Details
- **Method**: GET
- **URL**: `https://api.cardsight.ai/v1/pricing/search`
- **Authentication**: API Key required (X-Api-Key header)

## Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| q | string | Yes | Free-text search over marketplace listing titles. 3–300 characters. |
| period | string | No | Lookback period. Examples: "7d", "14d", "2w", "3m", "1y", "all". Default "all" (no time limit). |
| listing_type | string | No | Filter by listing type: "auction" (completed auction sales / bid side), "fixed" (Buy It Now asking prices / ask side), or "both" (default). |
| limit | integer | No | Maximum number of records to return. Default 100, hard cap 500. |

## Using the CardSight AI SDK (Recommended)

### Node.js / TypeScript
```typescript
import { CardSightAI } from 'cardsightai'

const client = new CardSightAI({ apiKey: 'your-api-key' })

// Search historical listings by title — no card ID needed
const response = await client.pricing.search({
  q: 'Ken Griffey Jr 1989 Upper Deck', // Required, 3–300 characters
  period: '90d',                        // Optional: "7d", "2w", "3m", "1y", "all"
  listing_type: 'both',                 // Optional: auction, fixed, both
  limit: 25,                            // Optional: default 100, max 500
})

// Always check for errors
if (response.error) {
  console.error('Error:', response.error)
} else {
  const data = response.data

  console.log(`Found ${data.meta.total_records} listings`)

  for (const listing of data.results) {
    const type = listing.listing_type === 'auction' ? 'Auction (bid)' : 'BIN (ask)'
    console.log(`$${listing.price.toFixed(2)} ${type} - ${listing.title ?? 'Untitled'} (${listing.source})`)

    // matched_card is present only when the listing was matched to a canonical card
    if (listing.matched_card) {
      console.log(`  ${listing.matched_card.name} - ${listing.matched_card.set.release}`)
    }
    // grade is present only for graded listings
    if (listing.grade) {
      console.log(`  ${listing.grade.company_name} ${listing.grade.grade_value}`)
    }
  }

  // Breakdown by data source
  for (const source of data.meta.sources) {
    console.log(`${source.source}: ${source.count}`)
  }
}
```

### Python
```python
from cardsightai import CardSightAI

client = CardSightAI(api_key='your-api-key')

response = client.pricing.search(
    q='Ken Griffey Jr 1989 Upper Deck',
    period='90d',
    listing_type='both',
    limit=25
)

print(f"Found {response.meta.total_records} listings")

for listing in response.results:
    listing_type = "Auction" if listing.listing_type == "auction" else "BIN"
    print(f"  ${listing.price:.2f} {listing_type}: {listing.title}")
    if listing.matched_card:
        print(f"    {listing.matched_card.name} - {listing.matched_card.set.release}")
```

## Direct API Call (cURL)
```bash
curl -X GET "https://api.cardsight.ai/v1/pricing/search?q=ken%20griffey%20jr%201989%20upper%20deck&period=90d&listing_type=both" \
  -H "X-Api-Key: your-api-key"
```

## Example Response
```json
{
  "query": {
    "q": "ken griffey jr 1989 upper deck",
    "listing_type": "both",
    "period": "90d",
    "limit": 25,
    "as_of_date": "2026-06-30"
  },
  "results": [
    {
      "title": "1989 Upper Deck #1 Ken Griffey Jr RC Rookie PSA 10",
      "price": 1250.00,
      "date": "2026-06-15T00:00:00Z",
      "source": "ebay",
      "listing_type": "auction",
      "url": "https://www.ebay.com/itm/...",
      "image_url": "https://i.ebayimg.com/...",
      "parallel_id": null,
      "parallel_name": null,
      "matched_card": {
        "card_id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Ken Griffey Jr.",
        "number": "1",
        "set": {
          "set_id": "550e8400-e29b-41d4-a716-446655440002",
          "name": "Base Set",
          "year": "1989",
          "release": "1989 Upper Deck"
        }
      },
      "grade": {
        "grade_id": "550e8400-e29b-41d4-a716-446655440030",
        "grade_value": "10",
        "company_name": "PSA",
        "company_id": "550e8400-e29b-41d4-a716-446655440020"
      }
    },
    {
      "title": "Griffey 1989 UD rookie raw nice card",
      "price": 39.99,
      "date": "2026-06-10T00:00:00Z",
      "source": "ebay",
      "listing_type": "fixed",
      "url": "https://www.ebay.com/itm/...",
      "image_url": null,
      "parallel_id": null,
      "parallel_name": null
    }
  ],
  "meta": {
    "sources": [{ "source": "ebay", "count": 42 }],
    "total_records": 42
  }
}
```

> Note: in the second result, `matched_card` and `grade` are **absent** — the listing was found by title, but our AI isn't confident in matching it to a canonical card. Always check for their presence before reading them.

## Response Fields

### Top-Level Response

| Field | Type | Description |
|-------|------|-------------|
| query | object | Echo of the query parameters that were applied |
| results | array | Flat list of matched listings, ranked by title relevance. Spans multiple cards and may include unmatched listings. |
| meta | object | Response metadata |

### Query Echo Object

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| q | string | Yes | Search query applied |
| listing_type | string | Yes | Listing type filter applied |
| period | string | No | Period filter applied |
| limit | integer | No | Result limit applied |
| as_of_date | string | Yes | Date the data was retrieved |

### Pricing Search Record Object

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| price | number | Yes | Price in USD. For auctions this is the final sale price (bid); for fixed/Buy It Now this is the seller's asking price (ask) and is NOT necessarily a completed sale. |
| source | string | Yes | Data source (e.g., "ebay") |
| title | string | No | Listing title from marketplace |
| date | string | No | Date the listing ended, in ISO 8601 format |
| listing_type | string | No | "auction" (completed auction sale / bid) or "fixed" (Buy It Now asking price / ask) |
| url | string | No | URL to the original listing |
| image_url | string | No | Primary image URL for the listing |
| parallel_id | UUID | No | Parallel variant UUID. Null for base card listings. |
| parallel_name | string | No | Parallel variant name. Null for base card listings. |
| matched_card | object | No | Canonical card this listing matched. **Omitted when the listing is unmatched.** |
| grade | object | No | Grade context for graded listings. **Omitted for ungraded listings.** |

### Matched Card Object (matched_card)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| card_id | UUID | Yes | Card UUID — use this to call other endpoints (single-card pricing, marketplace, population, etc.) |
| name | string | Yes | Card name/subject |
| number | string | No | Card number in set |
| set | object | Yes | Set context: set_id, name, year, release |

### Grade Object (grade)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| grade_id | UUID | Yes | Grade UUID |
| grade_value | string | Yes | Grade value (e.g., "10", "9.5") |
| company_name | string | Yes | Grading company name (e.g., "PSA") |
| company_id | UUID | Yes | Grading company UUID |

### Meta Object

| Field | Type | Description |
|-------|------|-------------|
| sources | array | Breakdown by data source (each item: source name + count) |
| total_records | number | Total records returned |

## Common Use Cases
- Look up historical listing prices when you have a card's name/year/set as text but not its UUID
- Price listings our AI isn't yet confident in matching, or sellers who use unusual titles
- Power a "what did this sell for?" search box that takes plain text
- Compare auction prices (bid) vs Buy It Now (ask) across many listings at once
- Bridge text → catalog: read `matched_card.card_id` to then call the single-card pricing, marketplace, or population endpoints

## Tips for AI Assistants
- **Working with IDs:** The `matched_card.card_id` returned here (and other release/set/card UUIDs) let you look up catalog entities and chain calls together. Caching them briefly to fulfill a user's request is expected and fine — it's why the image and other free endpoints exist. If you need to save cards for the long term, add them to a **list or collection**: those are your own stable resources, purpose-built for holding cards. If you need a bulk or offline copy of catalog data, reach out to us at `support@cardsight.ai` so we can help with your use case.
- The SDK returns `{ data, error }` — always check for errors first
- `q` is **required** and must be 3–300 characters
- Results are a **flat list spanning multiple cards**, ranked by title relevance — this is NOT scoped to one card like `GET /v1/pricing/{card_id}`
- `matched_card` is **omitted** when our AI isn't confident in matching a listing to a canonical card — always check for its presence before reading `matched_card.card_id`
- `grade` is **omitted** for ungraded listings — check before reading
- Think of `listing_type: "auction"` as "bid" (what buyers paid) and `"fixed"` as "ask" (what sellers asked)
- `period` uses format number + unit suffix — "7d", "2w", "3m", "1y", "all". Do NOT pass plain numbers like "90"
- `limit` defaults to 100 and is capped at 500
- Use `matched_card.card_id` to pivot from a text search into the precise single-card pricing/marketplace/population endpoints
- Coverage is expanding — as our AI grows more confident over time, more listings will match to canonical cards automatically
- Available for Baseball, Basketball, Football, Hockey, Pokémon, Magic: The Gathering, and One Piece, with more segments coming soon
