TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1/** OpenAPI 3.1 document for the public API (§157). Served at /v1/openapi.json and written to docs/openapi.json by scripts/gen-openapi.ts. */23const paging = [4 { name: 'limit', in: 'query', schema: { type: 'integer', minimum: 1, maximum: 200, default: 50 } },5 { name: 'cursor', in: 'query', schema: { type: 'string' }, description: 'Opaque cursor from meta.cursor' },6 { name: 'format', in: 'query', schema: { type: 'string', enum: ['json', 'csv'] }, description: 'csv returns a flat export of the rows' },7];8const range = [9 { name: 'from', in: 'query', schema: { type: 'string', format: 'date' } },10 { name: 'to', in: 'query', schema: { type: 'string', format: 'date' } },11 { name: 'format', in: 'query', schema: { type: 'string', enum: ['json', 'csv'] } },12];13const envelopeOf = (ref: string, list = true) => ({14 type: 'object',15 properties: {16 data: list ? { type: 'array', items: { $ref: ref } } : { $ref: ref },17 meta: { $ref: '#/components/schemas/Meta' },18 },19});20const ok = (ref: string, list = true) => ({ 200: { description: 'OK', content: { 'application/json': { schema: envelopeOf(ref, list) } } } });21const errors = {22 400: { description: 'Invalid query', content: { 'application/problem+json': { schema: { $ref: '#/components/schemas/Problem' } } } },23 401: { description: 'Invalid API key', content: { 'application/problem+json': { schema: { $ref: '#/components/schemas/Problem' } } } },24 404: { description: 'Not found', content: { 'application/problem+json': { schema: { $ref: '#/components/schemas/Problem' } } } },25 429: { description: 'Rate limit or quota exceeded', content: { 'application/problem+json': { schema: { $ref: '#/components/schemas/Problem' } } } },26};2728export function openapiDocument() {29 return {30 openapi: '3.1.0',31 info: {32 title: 'RareIndex Public API',33 version: '1.0.0',34 description:35 'Structured market data for collectible assets: canonical assets, observed sales, live listings, price history, category markets and the RareIndex indices. Valuations are estimates with confidence and sample size; listing prices are not confirmed transactions. Attribution to RareIndex and the original sources is required.',36 contact: { name: 'RareIndex', url: 'https://www.rareindex.io/api-docs', email: 'api@rareindex.io' },37 termsOfService: 'https://www.rareindex.io/about#terms',38 },39 servers: [{ url: 'https://www.rareindex.io/api', description: 'Production (proxied by the web app)' }, { url: 'http://localhost:8211', description: 'Local API service' }],40 security: [{ bearerAuth: [] }, {}],41 tags: [42 { name: 'assets' },43 { name: 'markets' },44 { name: 'indices' },45 { name: 'sales' },46 { name: 'reference' },47 { name: 'auctions' },48 ],49 paths: {50 '/v1/assets/search': { get: { tags: ['assets'], summary: 'Search canonical assets', parameters: [{ name: 'q', in: 'query', schema: { type: 'string' }, description: 'Natural language query, e.g. "1999 Charizard PSA 10"' }, { name: 'category', in: 'query', schema: { type: 'string' }, description: 'Category or family slug' }, ...paging], responses: { ...ok('#/components/schemas/AssetSummary'), ...errors } } },51 '/v1/assets/{id}': { get: { tags: ['assets'], summary: 'Asset detail with variants, latest valuation and sources', parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' }, description: 'Asset id (rare_…) or slug' }], responses: { ...ok('#/components/schemas/AssetDetail', false), ...errors } } },52 '/v1/assets/{id}/sales': { get: { tags: ['assets', 'sales'], summary: 'Observed sales for an asset', parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }, { name: 'variant', in: 'query', schema: { type: 'string' } }, { name: 'include_flagged', in: 'query', schema: { type: 'boolean' } }, ...paging], responses: { ...ok('#/components/schemas/Sale'), ...errors } } },53 '/v1/assets/{id}/listings': { get: { tags: ['assets'], summary: 'Listings for an asset (asks, not transactions)', parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }, { name: 'availability', in: 'query', schema: { type: 'string', enum: ['available', 'sold', 'ended', 'removed'], default: 'available' } }, ...paging], responses: { ...ok('#/components/schemas/Listing'), ...errors } } },54 '/v1/assets/{id}/depth': { get: { tags: ['assets'], summary: 'Market depth (asks around RIV, ±5/10/20 %, RareIndex spread), days on market, time-to-sale by asking band and fair buy/sell ladder — model estimates', parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }, { name: 'variant', in: 'query', schema: { type: 'string' } }], responses: { ...ok('#/components/schemas/AssetDepth', false), ...errors } } },55 '/v1/assets/{id}/history': { get: { tags: ['assets'], summary: 'Daily price history (RIV, latest sale, median, volume, listings)', parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }, { name: 'variant', in: 'query', schema: { type: 'string' } }, ...range], responses: { ...ok('#/components/schemas/PricePoint'), ...errors } } },56 '/v1/categories': { get: { tags: ['reference'], summary: 'Taxonomy', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/Category'), ...errors } } },57 '/v1/indices': { get: { tags: ['indices'], summary: 'RARE and subindices with latest values and changes', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/Index'), ...errors } } },58 '/v1/indices/{ticker}/history': { get: { tags: ['indices'], summary: 'Index daily history', parameters: [{ name: 'ticker', in: 'path', required: true, schema: { type: 'string' }, example: 'RARE-TCG' }, ...range], responses: { ...ok('#/components/schemas/IndexPoint'), ...errors } } },59 '/v1/markets': { get: { tags: ['markets'], summary: 'Category markets overview (latest snapshot per family)', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/Market'), ...errors } } },60 '/v1/markets/{slug}': { get: { tags: ['markets'], summary: 'Category market detail: movers, most valuable, most liquid, recent sales, history', parameters: [{ name: 'slug', in: 'path', required: true, schema: { type: 'string' }, example: 'pokemon' }], responses: { ...ok('#/components/schemas/MarketDetail', false), ...errors } } },61 '/v1/trending': { get: { tags: ['markets'], summary: 'Trending assets', parameters: [{ name: 'category', in: 'query', schema: { type: 'string' } }, ...paging], responses: { ...ok('#/components/schemas/AssetSummary'), ...errors } } },62 '/v1/sales/latest': { get: { tags: ['sales'], summary: 'Latest observed sales across the platform', parameters: [{ name: 'category', in: 'query', schema: { type: 'string' } }, { name: 'min_usd', in: 'query', schema: { type: 'number' } }, ...paging], responses: { ...ok('#/components/schemas/SaleWithAsset'), ...errors } } },63 '/v1/assets/{id}/auctions': { get: { tags: ['assets', 'auctions'], summary: 'Auction lots for an asset with all-in (buyer-pays) assessment vs RIV', parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }, { name: 'status', in: 'query', schema: { type: 'string', enum: ['live', 'upcoming', 'ended'] } }, ...paging], responses: { ...ok('#/components/schemas/AuctionLot'), ...errors } } },64 '/v1/auctions/lots': { get: { tags: ['auctions'], summary: 'Live/upcoming auction lots across houses with all-in bid or estimate vs RIV (§33–§35)', parameters: [{ name: 'status', in: 'query', schema: { type: 'string', enum: ['live', 'upcoming', 'ended'] } }, { name: 'ending_within_hours', in: 'query', schema: { type: 'integer', minimum: 1 } }, { name: 'category', in: 'query', schema: { type: 'string' } }, { name: 'house', in: 'query', schema: { type: 'string' } }, { name: 'below_riv', in: 'query', schema: { type: 'boolean' }, description: 'Only lots whose buyer-pays bid is ≥ 10 % below the variant RIV (verdict deal)' }, { name: 'asset', in: 'query', schema: { type: 'string' }, description: 'Asset id or slug' }, { name: 'sort', in: 'query', schema: { type: 'string', enum: ['ending', 'discount'] } }, ...paging], responses: { ...ok('#/components/schemas/AuctionLot'), ...errors } } },65 '/v1/records': { get: { tags: ['sales'], summary: 'Record sale per family (verified transactions only)', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/SaleWithAsset'), ...errors } } },66 '/v1/stats': { get: { tags: ['reference'], summary: 'Platform counts', responses: { ...ok('#/components/schemas/Stats', false) } } },67 },68 components: {69 securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer', description: 'API key `ri_live_…`. Requests without a key use the public tier (20 req/min).' } },70 schemas: {71 Meta: { type: 'object', properties: { count: { type: 'integer' }, cursor: { type: ['string', 'null'] }, as_of: { type: 'string', format: 'date-time' }, attribution: { type: 'string' } }, additionalProperties: true },72 Problem: { type: 'object', properties: { type: { type: 'string' }, title: { type: 'string' }, status: { type: 'integer' }, detail: { type: 'string' }, instance: { type: 'string' }, request_id: { type: 'string' } } },73 AssetSummary: {74 type: 'object',75 properties: {76 id: { type: 'string' }, slug: { type: 'string' }, title: { type: 'string' }, name: { type: 'string' }, category_slug: { type: 'string' }, family_slug: { type: 'string' }, brand: { type: ['string', 'null'] }, franchise: { type: ['string', 'null'] }, set_name: { type: ['string', 'null'] }, number: { type: ['string', 'null'] }, year: { type: ['integer', 'null'] }, variant: { type: ['string', 'null'] }, hero_image_url: { type: ['string', 'null'] },77 riv_usd: { type: ['number', 'null'], description: 'RareIndex Valuation (estimate)' }, riv_low_usd: { type: ['number', 'null'] }, riv_high_usd: { type: ['number', 'null'] }, riv_confidence: { type: ['number', 'null'] }, riv_sample_size: { type: 'integer' },78 latest_sale_usd: { type: ['number', 'null'] }, latest_sale_at: { type: ['string', 'null'] }, change_7d: { type: ['number', 'null'] }, change_30d: { type: ['number', 'null'] }, change_1y: { type: ['number', 'null'] }, sales_count: { type: 'integer' }, sales_30d: { type: 'integer' }, active_listings: { type: 'integer' }, min_ask_usd: { type: ['number', 'null'] }, liquidity_score: { type: ['number', 'null'] }, rarity_score: { type: ['number', 'null'] }, trending_score: { type: ['number', 'null'] },79 },80 },81 AssetDetail: { allOf: [{ $ref: '#/components/schemas/AssetSummary' }, { type: 'object', properties: { description: { type: ['string', 'null'] }, identifiers: { type: 'object' }, variants: { type: 'array', items: { type: 'object' } }, valuation: { type: ['object', 'null'] }, sources: { type: 'array', items: { type: 'object' } } } }] },82 Sale: { type: 'object', properties: { id: { type: 'string' }, source_id: { type: 'string' }, source_url: { type: 'string' }, sale_type: { type: 'string' }, sale_date: { type: 'string' }, price: { type: 'number' }, currency: { type: 'string' }, price_usd: { type: 'number' }, grader: { type: ['string', 'null'] }, grade: { type: ['string', 'null'] }, condition: { type: ['string', 'null'] }, status: { type: 'string' }, confidence: { type: 'number' } } },83 SaleWithAsset: { allOf: [{ $ref: '#/components/schemas/Sale' }, { type: 'object', properties: { asset_id: { type: 'string' }, asset_slug: { type: 'string' }, title: { type: 'string' } } }] },84 Listing: { type: 'object', properties: { id: { type: 'string' }, source_id: { type: 'string' }, source_url: { type: 'string' }, listing_type: { type: 'string' }, price: { type: ['number', 'null'] }, currency: { type: ['string', 'null'] }, price_usd: { type: ['number', 'null'] }, availability: { type: 'string' }, discount_to_riv: { type: ['number', 'null'] } } },85 PricePoint: { type: 'object', properties: { date: { type: 'string', format: 'date' }, riv_usd: { type: ['number', 'null'] }, latest_sale_usd: { type: ['number', 'null'] }, median_usd: { type: ['number', 'null'] }, sales_count: { type: 'integer' }, volume_usd: { type: ['number', 'null'] }, listings_count: { type: 'integer' }, min_ask_usd: { type: ['number', 'null'] } } },86 Category: { type: 'object', properties: { slug: { type: 'string' }, parent_slug: { type: ['string', 'null'] }, family_slug: { type: 'string' }, name: { type: 'string' }, level: { type: 'integer' }, phase: { type: 'integer' }, index_ticker: { type: ['string', 'null'] }, tracked_assets: { type: 'integer' } } },87 Index: { type: 'object', properties: { ticker: { type: 'string' }, name: { type: 'string' }, is_flagship: { type: 'boolean' }, as_of: { type: ['string', 'null'] }, value: { type: ['number', 'null'] }, published: { type: 'boolean' }, change_1d: { type: ['number', 'null'] }, change_7d: { type: ['number', 'null'] }, change_30d: { type: ['number', 'null'] }, change_ytd: { type: ['number', 'null'] }, change_1y: { type: ['number', 'null'] }, constituents_count: { type: ['integer', 'null'] }, transactions: { type: ['integer', 'null'] }, market_cap_est_usd: { type: ['number', 'null'] }, market_cap_confidence: { type: ['string', 'null'] } } },88 IndexPoint: { type: 'object', properties: { date: { type: 'string', format: 'date' }, value: { type: 'number' }, constituents_count: { type: 'integer' }, transactions: { type: 'integer' }, volume_usd: { type: ['number', 'null'] } } },89 Market: { type: 'object', properties: { slug: { type: 'string' }, name: { type: 'string' }, as_of: { type: ['string', 'null'] }, index_value: { type: ['number', 'null'] }, tracked_assets: { type: ['integer', 'null'] }, sales: { type: ['integer', 'null'] }, volume_usd: { type: ['number', 'null'] }, change_30d: { type: ['number', 'null'] } } },90 AuctionLot: { type: 'object', properties: { id: { type: 'string' }, auction_id: { type: 'string' }, auction_house: { type: 'string' }, auction_name: { type: ['string', 'null'] }, asset_id: { type: ['string', 'null'] }, asset_slug: { type: ['string', 'null'] }, asset_title: { type: ['string', 'null'] }, variant_id: { type: ['string', 'null'] }, lot_number: { type: ['string', 'null'] }, title: { type: 'string' }, url: { type: 'string' }, estimate_low: { type: ['number', 'null'] }, estimate_high: { type: ['number', 'null'] }, current_bid: { type: ['number', 'null'] }, hammer_price: { type: ['number', 'null'] }, currency: { type: ['string', 'null'] }, bid_count: { type: ['integer', 'null'] }, ends_at: { type: ['string', 'null'] }, status: { type: 'string' }, grader: { type: ['string', 'null'] }, grade: { type: ['string', 'null'] }, estimate_low_usd: { type: ['number', 'null'] }, estimate_high_usd: { type: ['number', 'null'] }, current_bid_usd: { type: ['number', 'null'] }, hammer_price_usd: { type: ['number', 'null'] }, fx_rate: { type: ['number', 'null'] }, fx_date: { type: ['string', 'null'] }, buyer_premium_rate: { type: ['number', 'null'], description: 'Effective buyer premium applied (0.27 = 27 %)' }, fee_basis: { type: ['string', 'null'], enum: ['included', 'added_published', 'added_approximate', 'added_default', 'none', 'unknown', null] }, all_in_bid_usd: { type: ['number', 'null'], description: 'Current bid + premium (only when ≥ 1 bid)' }, all_in_estimate_low_usd: { type: ['number', 'null'] }, all_in_estimate_high_usd: { type: ['number', 'null'] }, riv_usd: { type: ['number', 'null'], description: 'RIV of the lot’s variant at assessment time' }, bid_vs_riv: { type: ['number', 'null'], description: '(all-in bid − RIV) / RIV; negative = below RIV; null when ungated' }, estimate_vs_riv: { type: ['number', 'null'] }, assessment_verdict: { type: ['string', 'null'], enum: ['deal', 'fair', 'premium', 'review', 'anomaly', 'ungated', null] }, assessed_at: { type: ['string', 'null'] } } },91 AssetDepth: { type: 'object', properties: { asset_id: { type: 'string' }, variant_id: { type: ['string', 'null'] }, scope: { type: 'string', enum: ['variant', 'asset'] }, riv_usd: { type: ['number', 'null'] }, depth: { type: ['object', 'null'], properties: { asks: { type: 'integer' }, within5: { type: 'integer' }, within10: { type: 'integer' }, within20: { type: 'integer' }, belowRiv: { type: 'integer' }, aboveRiv: { type: 'integer' }, lowestAsk: { type: ['number', 'null'] }, medianAsk: { type: ['number', 'null'] }, askRivSpread: { type: ['number', 'null'], description: '(best ask − RIV) / RIV' } } }, liquidation: { type: ['object', 'null'], properties: { level: { type: 'string', enum: ['asset', 'category'] }, lifecycles: { type: 'integer' }, sold: { type: 'object' }, withdrawn: { type: 'object' }, bands: { type: 'array', items: { type: 'object' } }, sellThrough: { type: ['number', 'null'] } } }, fair_prices: { type: ['object', 'null'] }, note: { type: 'string' } } },92 MarketDetail: { type: 'object', properties: { category: { type: 'object' }, snapshot: { type: ['object', 'null'] }, counts: { type: 'object' }, gainers: { type: 'array', items: { $ref: '#/components/schemas/AssetSummary' } }, losers: { type: 'array', items: { $ref: '#/components/schemas/AssetSummary' } }, most_valuable: { type: 'array', items: { $ref: '#/components/schemas/AssetSummary' } }, most_liquid: { type: 'array', items: { $ref: '#/components/schemas/AssetSummary' } }, recent_sales: { type: 'array', items: { type: 'object' } }, history: { type: 'array', items: { type: 'object' } } } },93 Stats: { type: 'object', properties: { assets: { type: 'integer' }, sales: { type: 'integer' }, listings: { type: 'integer' }, sources: { type: 'integer' }, connectors: { type: 'integer' }, categories: { type: 'integer' } } },94 },95 },96 };97}98