SPB Git

spb/rareindex

Public
14.1 KB · 94 lines typescript
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    ],48    paths: {49      '/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 } } },50      '/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 } } },51      '/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 } } },52      '/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 } } },53      '/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 } } },54      '/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 } } },55      '/v1/categories': { get: { tags: ['reference'], summary: 'Taxonomy', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/Category'), ...errors } } },56      '/v1/indices': { get: { tags: ['indices'], summary: 'RARE and subindices with latest values and changes', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/Index'), ...errors } } },57      '/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 } } },58      '/v1/markets': { get: { tags: ['markets'], summary: 'Category markets overview (latest snapshot per family)', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/Market'), ...errors } } },59      '/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 } } },60      '/v1/trending': { get: { tags: ['markets'], summary: 'Trending assets', parameters: [{ name: 'category', in: 'query', schema: { type: 'string' } }, ...paging], responses: { ...ok('#/components/schemas/AssetSummary'), ...errors } } },61      '/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 } } },62      '/v1/records': { get: { tags: ['sales'], summary: 'Record sale per family (verified transactions only)', parameters: [paging[2]!], responses: { ...ok('#/components/schemas/SaleWithAsset'), ...errors } } },63      '/v1/stats': { get: { tags: ['reference'], summary: 'Platform counts', responses: { ...ok('#/components/schemas/Stats', false) } } },64    },65    components: {66      securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer', description: 'API key `ri_live_…`. Requests without a key use the public tier (20 req/min).' } },67      schemas: {68        Meta: { type: 'object', properties: { count: { type: 'integer' }, cursor: { type: ['string', 'null'] }, as_of: { type: 'string', format: 'date-time' }, attribution: { type: 'string' } }, additionalProperties: true },69        Problem: { type: 'object', properties: { type: { type: 'string' }, title: { type: 'string' }, status: { type: 'integer' }, detail: { type: 'string' }, instance: { type: 'string' }, request_id: { type: 'string' } } },70        AssetSummary: {71          type: 'object',72          properties: {73            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'] },74            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' },75            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'] },76          },77        },78        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' } } } }] },79        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' } } },80        SaleWithAsset: { allOf: [{ $ref: '#/components/schemas/Sale' }, { type: 'object', properties: { asset_id: { type: 'string' }, asset_slug: { type: 'string' }, title: { type: 'string' } } }] },81        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'] } } },82        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'] } } },83        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' } } },84        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'] } } },85        IndexPoint: { type: 'object', properties: { date: { type: 'string', format: 'date' }, value: { type: 'number' }, constituents_count: { type: 'integer' }, transactions: { type: 'integer' }, volume_usd: { type: ['number', 'null'] } } },86        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'] } } },87        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' } } },88        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' } } } },89        Stats: { type: 'object', properties: { assets: { type: 'integer' }, sales: { type: 'integer' }, listings: { type: 'integer' }, sources: { type: 'integer' }, connectors: { type: 'integer' }, categories: { type: 'integer' } } },90      },91    },92  };93}94
Files · b5d6968a47f5d2546937866bdabc967739349047
Menu

Theme