{
  "openapi": "3.1.0",
  "info": {
    "title": "Art Market API",
    "version": "2.0.0",
    "summary": "Structured art market data: auction results, artist analytics, comparable sales, valuations, and market indices.",
    "description": "REST API for structured art market data operated by ArtMarketAPI LLC. All prices in responses are whole USD dollars unless a field description says otherwise (the database stores cents; responses convert). Authentication uses an API key sent as a bearer token: `Authorization: Bearer amapi_live_...`. Create keys at https://app.artmarketapi.com/register. Access is credit-based; endpoint costs vary (lookups 1-3 credits, data queries 3-5, comparable sales 8-10, AI/valuation 15-40).",
    "termsOfService": "https://artmarketapi.com/terms",
    "contact": {
      "name": "Art Market API team",
      "email": "team@artmarketapi.com",
      "url": "https://artmarketapi.com/contact"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://artmarketapi.com/terms"
    }
  },
  "servers": [
    { "url": "https://api.artmarketapi.com/api/v1", "description": "Production" }
  ],
  "externalDocs": {
    "description": "Human-readable documentation",
    "url": "https://artmarketapi.com/docs"
  },
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Artists", "description": "Artist profiles, market metrics, and per-artist auction history" },
    { "name": "Auctions", "description": "Auction sales and calendar data" },
    { "name": "Auction Houses", "description": "Auction house directory and performance metrics" },
    { "name": "Auction Records", "description": "Individual lot records with prices, estimates, and sale outcomes" },
    { "name": "Search", "description": "Name search across artists" },
    { "name": "Price Database", "description": "Historical price database queries with optional semantic search" },
    { "name": "Comparable Sales", "description": "Comparable sales with built-in statistics for valuation work" },
    { "name": "Valuation", "description": "AI-assisted artwork valuations" },
    { "name": "Analytics", "description": "Price predictions, artist comparison, timing, and portfolio analysis" },
    { "name": "Market Trends", "description": "Heat indices, emerging artists, category performance, and sentiment" },
    { "name": "Indexes", "description": "Composite market indexes and time series" },
    { "name": "Exports", "description": "CSV/JSON exports and enterprise bulk data" },
    { "name": "Natural Language", "description": "Plain-English queries over the auction database" },
    { "name": "Chat", "description": "Conversational research over art market data" }
  ],
  "paths": {
    "/artists/{id}": {
      "get": {
        "tags": ["Artists"],
        "operationId": "getArtist",
        "summary": "Get artist details with comprehensive market data",
        "description": "Returns the artist profile plus market metrics, category breakdown, yearly performance, top auction houses, and recent/highest sales. Monetary values are whole USD dollars.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" }
        ],
        "responses": {
          "200": {
            "description": "Artist profile and market data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string" },
                    "data": {
                      "type": "object",
                      "properties": {
                        "artist": { "$ref": "#/components/schemas/Artist" },
                        "marketMetrics": {
                          "type": ["object", "null"],
                          "properties": {
                            "totalSales": { "type": "integer" },
                            "totalVolume": { "type": "number", "description": "USD" },
                            "averagePrice": { "type": "number", "description": "USD" },
                            "medianPrice": { "type": "number", "description": "USD" },
                            "highestPrice": { "type": "number", "description": "USD" },
                            "lowestPrice": { "type": "number", "description": "USD" }
                          }
                        },
                        "categoryBreakdown": { "type": "array", "items": { "type": "object" } },
                        "yearlyPerformance": { "type": "array", "items": { "type": "object" } },
                        "topAuctionHouses": { "type": "array", "items": { "type": "object" } },
                        "recentSales": { "type": "array", "items": { "type": "object" } },
                        "highestSales": { "type": "array", "items": { "type": "object" } }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/artists/{artist}/past_auction_records": {
      "get": {
        "tags": ["Artists"],
        "operationId": "listArtistPastAuctionRecords",
        "summary": "List past (sold) auction records for an artist",
        "description": "Paginated sold lots (hammer price > 0, sale date in the past) for an artist, 25 records per page, with auction house name on each record. Price fields (hammer_price, estimate_low, estimate_high) are whole USD dollars.",
        "parameters": [
          { "name": "artist", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 }, "description": "Page number (25 records per page)" },
          { "name": "sortBy", "in": "query", "schema": { "type": "string", "enum": ["hammer_price", "sale_date"], "default": "sale_date" }, "description": "Sort field, descending" }
        ],
        "responses": {
          "200": { "description": "Paginated records with mongoose-paginate metadata (totalDocs, page, totalPages, hasNextPage)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/artists/{artist}/sell_through_rates": {
      "get": {
        "tags": ["Artists"],
        "operationId": "getArtistSellThroughRates",
        "summary": "Sell-through rates by year for an artist",
        "parameters": [
          { "name": "artist", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" }
        ],
        "responses": {
          "200": { "description": "Yearly sell-through rate data", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/artists/{artist}/slpae": {
      "get": {
        "tags": ["Artists"],
        "operationId": "getArtistSlpae",
        "summary": "Sale lot performance at auction by year (SLPAE) for an artist",
        "parameters": [
          { "name": "artist", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" }
        ],
        "responses": {
          "200": { "description": "Yearly lot performance data", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auctions": {
      "get": {
        "tags": ["Auctions"],
        "operationId": "listAuctions",
        "summary": "List auctions with filtering, sorting, and pagination",
        "parameters": [
          { "name": "auction_house", "in": "query", "schema": { "type": "string" }, "description": "Filter by auction house ID" },
          { "name": "start_date", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Auctions starting on or after this date" },
          { "name": "end_date", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Auctions starting on or before this date" },
          { "name": "state", "in": "query", "schema": { "type": "string" }, "description": "Filter by auction state" },
          { "name": "city", "in": "query", "schema": { "type": "string" }, "description": "Case-insensitive city match" },
          { "name": "country", "in": "query", "schema": { "type": "string" }, "description": "Case-insensitive country match" },
          { "name": "has_results", "in": "query", "schema": { "type": "string", "enum": ["true"] }, "description": "Only auctions with published sales totals" },
          { "name": "sort_by", "in": "query", "schema": { "type": "string", "enum": ["start_date", "end_date", "sales_total", "number_of_auction_records", "created_at"], "default": "start_date" } },
          { "name": "sort_order", "in": "query", "schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 25, "maximum": 100 } }
        ],
        "responses": {
          "200": { "description": "Auctions with pagination metadata", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedList" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auctions/upcoming": {
      "get": {
        "tags": ["Auctions"],
        "operationId": "listUpcomingAuctions",
        "summary": "List upcoming auctions, soonest first",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10, "maximum": 50 } }
        ],
        "responses": {
          "200": { "description": "Upcoming auctions", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auctions/recent": {
      "get": {
        "tags": ["Auctions"],
        "operationId": "listRecentAuctions",
        "summary": "List recently completed auctions with results",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10, "maximum": 50 } },
          { "name": "days", "in": "query", "schema": { "type": "integer", "default": 90, "minimum": 1, "maximum": 365 }, "description": "Lookback window in days" }
        ],
        "responses": {
          "200": { "description": "Recent auctions with sales totals", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auctions/{id}": {
      "get": {
        "tags": ["Auctions"],
        "operationId": "getAuction",
        "summary": "Get auction details with lot statistics and top sales",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Auction ID (MongoDB ObjectId)" },
          { "name": "include_records", "in": "query", "schema": { "type": "string", "enum": ["true", "false"], "default": "false" }, "description": "Include lot records sorted by price" }
        ],
        "responses": {
          "200": { "description": "Auction detail with statistics, category breakdown, and top sales", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auction-houses": {
      "get": {
        "tags": ["Auction Houses"],
        "operationId": "listAuctionHouses",
        "summary": "List auction houses with summary market metrics",
        "description": "All auction houses with lots, sell-through rate, total/average hammer value (USD), performance vs estimate, and a market strength label. Served from a one-hour cache when warm. Also mounted at /auction_houses.",
        "responses": {
          "200": { "description": "Auction house directory with metrics", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auction-houses/{slug}": {
      "get": {
        "tags": ["Auction Houses"],
        "operationId": "getAuctionHouse",
        "summary": "Get auction house metrics by slug",
        "description": "Overall, yearly, and per-category metrics plus top 20 sales for one auction house. Also mounted at /auction_houses/{slug}.",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Auction house slug (lowercase, hyphenated)" }
        ],
        "responses": {
          "200": { "description": "Auction house detail", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auction-records": {
      "get": {
        "tags": ["Auction Records"],
        "operationId": "listAuctionRecords",
        "summary": "List auction records with filtering and optional analytics",
        "description": "Filter by artist, category, date range, price range, and lot performance. Prices in responses are whole USD dollars. Also mounted at /auction_records.",
        "parameters": [
          { "name": "artist", "in": "query", "schema": { "type": "string" }, "description": "Filter by artist ID" },
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "start_date", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Sale date on or after" },
          { "name": "end_date", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Sale date on or before" },
          { "name": "min_price", "in": "query", "schema": { "type": "integer" }, "description": "Minimum hammer price in USD dollars" },
          { "name": "max_price", "in": "query", "schema": { "type": "integer" }, "description": "Maximum hammer price in USD dollars" },
          { "name": "lot_performance", "in": "query", "schema": { "type": "string", "enum": ["upcoming", "pulled", "above", "within", "below", "not_sold"] }, "description": "Lot outcome vs estimate" },
          { "name": "include_analytics", "in": "query", "schema": { "type": "string", "enum": ["true", "false"], "default": "false" }, "description": "Include artist market metrics (requires artist)" },
          { "name": "sort_by", "in": "query", "schema": { "type": "string", "enum": ["sale_date", "usd_hammer_price", "created_at"], "default": "sale_date" } },
          { "name": "sort_order", "in": "query", "schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10, "maximum": 50 } }
        ],
        "responses": {
          "200": { "description": "Records with populated artist and auction, plus pagination", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedList" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/auction-records/{auctionRecordId}": {
      "get": {
        "tags": ["Auction Records"],
        "operationId": "getAuctionRecord",
        "summary": "Get one auction record with metrics and comparables",
        "description": "Full lot detail with artist, auction house, performance metrics, artist market stats, and up to six comparable sales. Prices are whole USD dollars. Also mounted at /auction_records/{auctionRecordId}.",
        "parameters": [
          { "name": "auctionRecordId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Auction record ID (MongoDB ObjectId)" }
        ],
        "responses": {
          "200": { "description": "Record detail", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/search/artist": {
      "get": {
        "tags": ["Search"],
        "operationId": "searchArtists",
        "summary": "Search artists by name",
        "parameters": [
          { "name": "name", "in": "query", "schema": { "type": "string" }, "description": "Artist name query" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10, "maximum": 20 } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1, "minimum": 1 }, "description": "Page number (1-based, consistent with all other endpoints)" }
        ],
        "responses": {
          "200": {
            "description": "Matching artists",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } } } },
                    "pagination": { "type": "object" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/price_database": {
      "get": {
        "tags": ["Price Database"],
        "operationId": "queryPriceDatabase",
        "summary": "Query the price database with filters or semantic search",
        "description": "Filter verified auction records by artist, category, price, and dimensions; or pass `description` for semantic vector search over artwork descriptions. Each range bound (min/max) may be supplied independently. Aggregate stats are USD dollars; record price fields on this endpoint are raw stored cents.",
        "parameters": [
          { "name": "artist", "in": "query", "schema": { "type": "string" }, "description": "Filter by artist ID" },
          { "name": "description", "in": "query", "schema": { "type": "string" }, "description": "Free-text artwork description; triggers semantic search" },
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "is_edition", "in": "query", "schema": { "type": "string" } },
          { "name": "lot_performance", "in": "query", "schema": { "type": "string", "enum": ["upcoming", "pulled", "above", "within", "below", "not_sold"] } },
          { "name": "min_hammer_price", "in": "query", "schema": { "type": "integer" }, "description": "USD dollars; supply with max_hammer_price" },
          { "name": "max_hammer_price", "in": "query", "schema": { "type": "integer" }, "description": "USD dollars; supply with min_hammer_price" },
          { "name": "min_width", "in": "query", "schema": { "type": "number" }, "description": "cm; supply with max_width" },
          { "name": "max_width", "in": "query", "schema": { "type": "number" }, "description": "cm; supply with min_width" },
          { "name": "min_height", "in": "query", "schema": { "type": "number" }, "description": "cm; supply with max_height" },
          { "name": "max_height", "in": "query", "schema": { "type": "number" }, "description": "cm; supply with min_height" },
          { "name": "sort_option", "in": "query", "schema": { "type": "string", "enum": ["sale_date_desc", "sale_date_asc", "hammer_price_desc", "hammer_price_asc"], "default": "sale_date_desc" } },
          { "name": "include_analytics", "in": "query", "schema": { "type": "string", "enum": ["true"] } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 25, "maximum": 50 } }
        ],
        "responses": {
          "200": { "description": "Matching records with aggregate stats and pagination", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedList" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/comparable_sales": {
      "get": {
        "tags": ["Comparable Sales"],
        "operationId": "listComparableSales",
        "summary": "Find comparable auction sales for an artwork",
        "description": "Verified sold records matching artist, category, medium, dimensions, price, and time window (default two-year lookback), with aggregate price statistics. All prices are whole USD dollars. Requires a plan with comparables access.",
        "parameters": [
          { "name": "artist", "in": "query", "schema": { "type": "string" }, "description": "Filter by artist ID" },
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "medium", "in": "query", "schema": { "type": "string" }, "description": "Case-insensitive medium match" },
          { "name": "min_width", "in": "query", "schema": { "type": "number" }, "description": "cm" },
          { "name": "max_width", "in": "query", "schema": { "type": "number" }, "description": "cm" },
          { "name": "min_height", "in": "query", "schema": { "type": "number" }, "description": "cm" },
          { "name": "max_height", "in": "query", "schema": { "type": "number" }, "description": "cm" },
          { "name": "min_price", "in": "query", "schema": { "type": "integer" }, "description": "USD dollars" },
          { "name": "max_price", "in": "query", "schema": { "type": "integer" }, "description": "USD dollars" },
          { "name": "start_date", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Overrides years_back" },
          { "name": "end_date", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Overrides years_back" },
          { "name": "years_back", "in": "query", "schema": { "type": "integer", "default": 2, "minimum": 1, "maximum": 10 } },
          { "name": "sort_by", "in": "query", "schema": { "type": "string", "enum": ["sale_date", "usd_hammer_price", "price_per_cm_squared"], "default": "sale_date" } },
          { "name": "sort_order", "in": "query", "schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10, "maximum": 25 } }
        ],
        "responses": {
          "200": { "description": "Comparable sales with statistics, filters applied, and pagination", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedList" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/comparable_sales/summary": {
      "get": {
        "tags": ["Comparable Sales"],
        "operationId": "getComparableSalesSummary",
        "summary": "Aggregate comparable-sales statistics without individual records",
        "parameters": [
          { "name": "artist", "in": "query", "schema": { "type": "string" } },
          { "name": "category", "in": "query", "schema": { "type": "string" }, "description": "When omitted, response includes a category breakdown" },
          { "name": "medium", "in": "query", "schema": { "type": "string" } },
          { "name": "min_width", "in": "query", "schema": { "type": "number" } },
          { "name": "max_width", "in": "query", "schema": { "type": "number" } },
          { "name": "min_height", "in": "query", "schema": { "type": "number" } },
          { "name": "max_height", "in": "query", "schema": { "type": "number" } },
          { "name": "years_back", "in": "query", "schema": { "type": "integer", "default": 2, "minimum": 1, "maximum": 10 } }
        ],
        "responses": {
          "200": { "description": "Aggregate statistics (USD dollars); data is null when nothing matches", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/valuation": {
      "post": {
        "tags": ["Valuation"],
        "operationId": "createValuation",
        "summary": "Calculate an artwork valuation from artist and dimensions",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["artist", "width", "height"],
                "properties": {
                  "artist": { "type": "string", "description": "Artist ID (MongoDB ObjectId)" },
                  "width": { "type": "number", "description": "cm" },
                  "height": { "type": "number", "description": "cm" },
                  "depth": { "type": "number", "description": "cm, default 1" },
                  "category": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Low/average/high valuation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string" },
                    "data": {
                      "type": "object",
                      "properties": {
                        "currency": { "type": "string", "const": "USD" },
                        "low": { "type": "string", "description": "USD, two-decimal string" },
                        "average": { "type": "string", "description": "USD, two-decimal string" },
                        "high": { "type": "string", "description": "USD, two-decimal string" }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/valuation_analysis": {
      "post": {
        "tags": ["Valuation"],
        "operationId": "createValuationAnalysis",
        "summary": "Advanced valuation from comparable sales",
        "description": "Averages up to 20 comparable sales from the last five years and returns an estimated value (USD dollars) with the comparable records.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["artist", "width", "height"],
                "properties": {
                  "artist": { "type": "string", "description": "Artist ID (MongoDB ObjectId)" },
                  "width": { "type": "number", "description": "cm" },
                  "height": { "type": "number", "description": "cm" },
                  "depth": { "type": "number", "description": "cm, default 1" },
                  "category": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Estimated value with comparable auction records", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/analytics/predict": {
      "post": {
        "tags": ["Analytics"],
        "operationId": "predictPrices",
        "summary": "One/three/five-year price projections for an artwork",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["artist", "width", "height", "category"],
                "properties": {
                  "artist": { "type": "string", "description": "Artist ID (MongoDB ObjectId)" },
                  "width": { "type": "number", "description": "cm" },
                  "height": { "type": "number", "description": "cm" },
                  "category": { "type": "string" },
                  "currentPrice": { "type": "number", "description": "USD dollars; estimated from recent sales when omitted" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Conservative/moderate/optimistic projections (USD dollars) with model details", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/analytics/compare/{artistId}": {
      "get": {
        "tags": ["Analytics"],
        "operationId": "compareArtist",
        "summary": "Compare an artist's market metrics",
        "parameters": [
          { "name": "artistId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" },
          { "name": "category", "in": "query", "schema": { "type": "string", "default": "painting" } }
        ],
        "responses": {
          "200": { "description": "Target artist metrics", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/analytics/timing/{artistId}": {
      "get": {
        "tags": ["Analytics"],
        "operationId": "getMarketTiming",
        "summary": "Buy/sell timing signal for an artist",
        "description": "Requires at least three years of price history.",
        "parameters": [
          { "name": "artistId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" },
          { "name": "category", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Timing signal with yearly price history (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/analytics/portfolio": {
      "post": {
        "tags": ["Analytics"],
        "operationId": "analyzePortfolio",
        "summary": "Analyze a portfolio of artworks",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["artworks"],
                "properties": {
                  "artworks": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": ["artist", "category", "purchasePrice"],
                      "properties": {
                        "artist": { "type": "string", "description": "Artist ID (MongoDB ObjectId)" },
                        "category": { "type": "string" },
                        "purchasePrice": { "type": "number", "description": "USD dollars" },
                        "purchaseDate": { "type": "string", "format": "date" }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Portfolio summary, per-holding values, and insights (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/market_trends/overview": {
      "get": {
        "tags": ["Market Trends"],
        "operationId": "getMarketOverview",
        "summary": "Comprehensive market overview",
        "description": "Heat index, 12-month volume/price summary, emerging artists, category performance, and top recent sales.",
        "parameters": [
          { "name": "category", "in": "query", "schema": { "type": "string" }, "description": "Case-insensitive category filter" }
        ],
        "responses": {
          "200": { "description": "Market overview (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/market_trends/heat": {
      "get": {
        "tags": ["Market Trends"],
        "operationId": "getMarketHeat",
        "summary": "Market heat index (0-100)",
        "parameters": [
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "months", "in": "query", "schema": { "type": "integer", "default": 6, "minimum": 1, "maximum": 24 }, "description": "Comparison period length" }
        ],
        "responses": {
          "200": { "description": "Heat index with trend and change metrics", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/market_trends/emerging": {
      "get": {
        "tags": ["Market Trends"],
        "operationId": "listEmergingArtists",
        "summary": "Emerging artists by volume growth",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10, "maximum": 25 } }
        ],
        "responses": {
          "200": { "description": "Emerging artists with growth metrics (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/market_trends/categories": {
      "get": {
        "tags": ["Market Trends"],
        "operationId": "getCategoryPerformance",
        "summary": "Category performance rankings (last two years)",
        "responses": {
          "200": { "description": "Categories with market share and volume change", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/market_trends/records": {
      "get": {
        "tags": ["Market Trends"],
        "operationId": "listPriceRecords",
        "summary": "Highest hammer-price records of all time",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10, "maximum": 25 } }
        ],
        "responses": {
          "200": { "description": "Top records (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/market_trends/regime/{artistId}": {
      "get": {
        "tags": ["Market Trends"],
        "operationId": "getMarketRegime",
        "summary": "Market regime for an artist",
        "description": "Requires at least twelve months of price data.",
        "parameters": [
          { "name": "artistId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" },
          { "name": "category", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Regime state with monthly price history", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/market_trends/sentiment": {
      "get": {
        "tags": ["Market Trends"],
        "operationId": "getMarketSentiment",
        "summary": "Composite market sentiment score",
        "parameters": [
          { "name": "category", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Sentiment score (0-100) with label and interpretation", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/nl": {
      "get": {
        "tags": ["Natural Language"],
        "operationId": "naturalLanguageQuery",
        "summary": "Query the auction database in plain English",
        "description": "Parses the query into a structured intent (artist search, record search, price lookup, market stats, valuation, comparable sales, market heat) and executes it. Result prices are USD dollars.",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Natural language query, e.g. 'Show me Picasso paintings over $1M'" }
        ],
        "responses": {
          "200": { "description": "Parsed intent and intent-specific result with summary", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "post": {
        "tags": ["Natural Language"],
        "operationId": "naturalLanguageQueryPost",
        "summary": "Query in plain English (request body variant)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["query"],
                "properties": { "query": { "type": "string", "description": "Natural language query" } }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Same shape as GET /nl", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/indexes": {
      "get": {
        "tags": ["Indexes"],
        "operationId": "listIndexes",
        "summary": "Composite market indexes with current values and changes",
        "parameters": [
          { "name": "category", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Indexes (base 100) with 1m/3m/1y changes and methodology metadata", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/indexes/time-series": {
      "get": {
        "tags": ["Indexes"],
        "operationId": "getIndexTimeSeries",
        "summary": "Historical market index time series",
        "parameters": [
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "period", "in": "query", "schema": { "type": "string", "enum": ["monthly", "quarterly", "yearly"], "default": "monthly" } },
          { "name": "startDate", "in": "query", "schema": { "type": "string", "format": "date" } },
          { "name": "endDate", "in": "query", "schema": { "type": "string", "format": "date" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 24 }, "description": "Max data points" }
        ],
        "responses": {
          "200": { "description": "Time series with avg/median price, volume, and period-over-period change (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/indexes/snapshot": {
      "get": {
        "tags": ["Indexes"],
        "operationId": "getMarketSnapshot",
        "summary": "Market snapshot for a calendar month",
        "parameters": [
          { "name": "date", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "Any date in the target month; defaults to now" },
          { "name": "category", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Overview stats, top five sales, and category breakdown (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/indexes/artist/{artistId}": {
      "get": {
        "tags": ["Indexes"],
        "operationId": "getArtistIndex",
        "summary": "Price index for a specific artist",
        "description": "Normalized to base 100 at the earliest period, with per-period prices and CAGR.",
        "parameters": [
          { "name": "artistId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" },
          { "name": "period", "in": "query", "schema": { "type": "string", "enum": ["monthly", "quarterly", "yearly"], "default": "yearly" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10 }, "description": "Max periods" }
        ],
        "responses": {
          "200": { "description": "Artist index series with CAGR (USD dollars)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/exports/auction-records": {
      "get": {
        "tags": ["Exports"],
        "operationId": "exportAuctionRecords",
        "summary": "Export filtered auction records as JSON or CSV",
        "description": "Sold lots only. Prices are whole USD dollars. With format=csv the response is a text/csv attachment.",
        "parameters": [
          { "name": "artist", "in": "query", "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" },
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "startDate", "in": "query", "schema": { "type": "string", "format": "date" } },
          { "name": "endDate", "in": "query", "schema": { "type": "string", "format": "date" } },
          { "name": "minPrice", "in": "query", "schema": { "type": "integer" }, "description": "USD dollars" },
          { "name": "maxPrice", "in": "query", "schema": { "type": "integer" }, "description": "USD dollars" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 1000, "maximum": 5000 } },
          { "name": "format", "in": "query", "schema": { "type": "string", "enum": ["csv", "json"], "default": "json" } }
        ],
        "responses": {
          "200": {
            "description": "Exported records",
            "content": {
              "application/json": { "schema": { "type": "object" } },
              "text/csv": { "schema": { "type": "string" } }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/exports/artist-report/{artistId}": {
      "get": {
        "tags": ["Exports"],
        "operationId": "exportArtistReport",
        "summary": "Comprehensive artist report",
        "description": "Bio, summary stats, ten-year yearly performance, category breakdown, and twenty most recent sales. CSV format exports the recent sales table.",
        "parameters": [
          { "name": "artistId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Artist ID (MongoDB ObjectId)" },
          { "name": "format", "in": "query", "schema": { "type": "string", "enum": ["csv", "json"], "default": "json" } }
        ],
        "responses": {
          "200": {
            "description": "Artist report",
            "content": {
              "application/json": { "schema": { "type": "object" } },
              "text/csv": { "schema": { "type": "string" } }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/exports/bulk/auction-records": {
      "get": {
        "tags": ["Exports"],
        "operationId": "bulkExportAuctionRecords",
        "summary": "Bulk export sold auction records (Enterprise)",
        "description": "Cursor-paginated bulk export with incremental sync via updatedSince. Requires an Enterprise plan.",
        "parameters": [
          { "name": "cursor", "in": "query", "schema": { "type": "string" }, "description": "nextCursor from the previous page" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 1000, "maximum": 10000 } },
          { "name": "updatedSince", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "Only records updated on or after this timestamp" }
        ],
        "responses": {
          "200": { "description": "Records with cursor pagination (count, hasMore, nextCursor)", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/exports/bulk/artists": {
      "get": {
        "tags": ["Exports"],
        "operationId": "bulkExportArtists",
        "summary": "Bulk export artists (Enterprise)",
        "description": "Cursor-paginated bulk export with incremental sync via updatedSince. Requires an Enterprise plan.",
        "parameters": [
          { "name": "cursor", "in": "query", "schema": { "type": "string" }, "description": "nextCursor from the previous page" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 500, "maximum": 5000 } },
          { "name": "updatedSince", "in": "query", "schema": { "type": "string", "format": "date-time" } }
        ],
        "responses": {
          "200": { "description": "Artists with cursor pagination", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/chat": {
      "post": {
        "tags": ["Chat"],
        "operationId": "chat",
        "summary": "Conversational research over art market data",
        "description": "AI chat that answers art market questions by querying the database, returning a text reply plus structured display cards.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["message"],
                "properties": {
                  "message": { "type": "string", "description": "Your question or message" },
                  "conversationHistory": {
                    "type": "array",
                    "description": "Prior messages in OpenAI chat format for multi-turn conversations",
                    "items": { "type": "object" }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI reply with structured cards",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string" },
                    "cards": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": { "type": "string", "enum": ["artist", "auction_record", "market_stats", "category_stats"] },
                          "data": { "type": "object" }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key as bearer token: Authorization: Bearer amapi_live_... Create keys at https://app.artmarketapi.com/register."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "message": { "type": "string" },
          "code": { "type": "string", "description": "Machine-readable error code, e.g. INVALID_API_KEY, SUBSCRIPTION_REQUIRED, ARTIST_NOT_FOUND" }
        },
        "required": ["error", "message"]
      },
      "Artist": {
        "type": "object",
        "properties": {
          "_id": { "type": "string" },
          "name": { "type": "string" },
          "biography": { "type": ["string", "null"] },
          "birthYear": { "type": ["integer", "null"] },
          "deathYear": { "type": ["integer", "null"] },
          "nationality": { "type": ["string", "null"] },
          "gender": { "type": ["string", "null"] },
          "imageUrl": { "type": ["string", "null"] },
          "auctionRecordCount": { "type": ["integer", "null"] }
        }
      },
      "PaginatedList": {
        "type": "object",
        "properties": {
          "message": { "type": "string" },
          "data": { "type": "array", "items": { "type": "object" } },
          "pagination": {
            "type": "object",
            "properties": {
              "page": { "type": "integer" },
              "limit": { "type": "integer" },
              "count": { "type": "integer" },
              "hasMore": { "type": "boolean" }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request parameters",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "Plan or subscription does not allow this endpoint, or account suspended",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
