RareIndex Public API (v1)
Base URL (production): https://www.rareindex.io/api/v1 — the web app proxies /api/v1/* to the
Fastify service (apps/api, port API_PORT, default 8211). Direct access in development:
http://localhost:8211/v1. Machine-readable contract: GET /v1/openapi.json (also docs/openapi.json).
Authentication & tiers
Authorization: Bearer ri_live_<prefix>_<secret>. Requests without a key use the public tier.
Only the SHA-256 hash of a key is stored (api_keys.key_hash); mint keys with
pnpm --filter @rareindex/api create-key -- --name "Research desk" --tier professional [--user usr_…]| Tier | req/min | req/day |
|---|---|---|
| public (no key) | 20 | 500 |
| free | 60 | 1 000 |
| hobby | 120 | 10 000 |
| professional | 600 | 100 000 |
| research | 600 | 250 000 |
| enterprise | 3 000 | 5 000 000 |
Rate-limit headers: x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset; every response
carries x-request-id. Usage is counted per key/day/endpoint in api_usage.
Conventions
- Envelope:
{ "data": …, "meta": { "count", "cursor", "as_of", "attribution", … } }. - Pagination:
limit(≤ 200) + opaquecursor(frommeta.cursor,nullwhen exhausted). - Errors: RFC 9457
application/problem+json(title,status,detail,instance,request_id). - Export: add
?format=csvon list endpoints for a flat CSV download. - Caching: read endpoints send
cache-controland weak ETags; gzip/brotli negotiated. - Money:
price+currencyare the native transaction values;price_usduses the FX rate of the transaction date. Valuations (riv_usd,riv_low_usd,riv_high_usd) always come withriv_confidenceandriv_sample_size. Listing prices are asks, never transactions. - Attribution is required (see
meta.attribution).
Endpoints
| Method & path | Purpose |
|---|---|
GET /v1/assets/search?q&category&limit&cursor |
Search canonical assets (FTS + trigram + identifiers) |
GET /v1/assets/{idOrSlug} |
Asset detail: attributes, stats, variants with their own valuations, latest valuation, sources |
GET /v1/assets/{id}/sales?variant&include_flagged |
Observed sales (valid only by default); all_in_usd = buyer-pays price (hammer + premium per fee_basis) |
GET /v1/assets/{id}/listings?availability |
Listings (asks) with discount_to_riv |
GET /v1/assets/{id}/history?variant&from&to |
Daily series: RIV, latest sale, median, sales count, volume, listings, min ask |
GET /v1/assets/{id}/depth?variant |
Market depth (asks within ±5/10/20 % of RIV, below/above, best & median ask, RareIndex spread), days on market (sold vs withdrawn), median time-to-sale by asking band (≤ 90 %, 90–100 %, 100–110 %, > 110 % of RIV; null under 5 observations), fair buy/sell ladder. liquidation.level is category when the asset has < 10 completed lifecycles. Model estimates, not advice |
GET /v1/assets/{id}/auctions?status |
Auction lots for an asset with native, USD and all-in (buyer-pays) amounts, fee_basis, bid_vs_riv / estimate_vs_riv and assessment_verdict |
GET /v1/auctions/lots?status&ending_within_hours&category&house&below_riv&asset&sort |
Live/upcoming lots across houses: current bid or low estimate + the house's buyer premium (published, ≈ estimated or default 22 %) compared with the RIV of the lot's own variant through the ask gates. below_riv=true keeps verdict deal only. Taxes, duties and shipping excluded; bids are not transactions |
GET /v1/categories |
Taxonomy with tracked asset counts |
GET /v1/indices |
RARE + subindices: latest value, 1d/7d/30d/YTD/1y changes, breadth, market cap estimate + confidence; published=false until breadth threshold |
GET /v1/indices/{ticker}/history?from&to |
Index history |
GET /v1/markets |
Latest category snapshot per family |
GET /v1/markets/{slug} |
Market detail: counts, gainers/losers (≥ 3 sales), most valuable, most liquid, recent sales, history |
GET /v1/trending?category |
Assets ranked by trending score |
GET /v1/sales/latest?category&min_usd |
Latest observed sales across the platform |
GET /v1/records |
Highest verified sale per family (bundles and low-confidence excluded) |
GET /v1/stats |
Platform counts |
GET /healthz |
Liveness (DB ping) |
Internal endpoints (loopback only)
POST /internal/lookup { url } and GET /internal/lookup/sources are used by the web Scanner to
resolve a marketplace URL through the connector framework (connector modules are loaded
dynamically, which the Next.js bundler cannot do). Auth: header x-internal-token =
HMAC-SHA256(SESSION_SECRET, "internal"), and the caller must be 127.0.0.1.
Running
pnpm --filter @rareindex/api dev # tsx watch, port 8211
pnpm --filter @rareindex/api start
pnpm --filter @rareindex/api test # fastify.inject integration tests against DATABASE_URL
pnpm --filter @rareindex/api exec tsx scripts/gen-openapi.ts # refresh docs/openapi.jsonEnvironment: DATABASE_URL, API_PORT, API_HOST (default 127.0.0.1 — put it behind the web proxy),
SESSION_SECRET (shared with the web app for internal calls), FIRECRAWL_API_KEY/SCRAPFLY_API_KEY
(URL lookups), LOG_LEVEL.