SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
6.9 KB · 124 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { PageHeader } from '@/components/ui/page-header';4import { Card, CardHeader, Table, th, td, Badge } from '@/components/ui/primitives';56export const metadata: Metadata = { title: 'API', description: 'RareIndex public API: assets, sales, listings, price history, categories, indices, markets and trending — with tiers and rate limits.' };78const ENDPOINTS: Array<{ method: string; path: string; desc: string; params?: string; tier: string }> = [9  { method: 'GET', path: '/v1/assets/search', desc: 'Hybrid search over canonical assets with natural-language filters.', params: 'q, category, grader, grade, min, max, limit, offset', tier: 'free' },10  { method: 'GET', path: '/v1/assets/:id', desc: 'Canonical asset with variants, RIV, scores and evidence (sample size, confidence, updated_at).', tier: 'free' },11  { method: 'GET', path: '/v1/assets/:id/sales', desc: 'Verified sales with source attribution, native currency and USD at historical FX.', params: 'variant, from, to, limit, offset, format=json|csv', tier: 'hobby' },12  { method: 'GET', path: '/v1/assets/:id/listings', desc: 'Active listings with discount to RIV.', params: 'variant, availability, limit', tier: 'hobby' },13  { method: 'GET', path: '/v1/assets/:id/history', desc: 'Daily price snapshots (RIV, median, lowest ask, volume, listings).', params: 'variant, from, to, format', tier: 'hobby' },14  { method: 'GET', path: '/v1/categories', desc: 'The taxonomy tree with live coverage counts.', tier: 'free' },15  { method: 'GET', path: '/v1/indices', desc: 'All indices with latest value, period returns and build status.', tier: 'free' },16  { method: 'GET', path: '/v1/indices/:ticker/history', desc: 'Daily index values with coverage, transactions and liquidity.', params: 'from, to, format', tier: 'professional' },17  { method: 'GET', path: '/v1/markets', desc: 'Category market rows: index, changes, sales, volume, listings, market cap estimate + confidence.', params: 'parent', tier: 'free' },18  { method: 'GET', path: '/v1/trending', desc: 'Trending assets and categories with momentum breakdown.', params: 'category, limit', tier: 'free' },19];2021const TIERS = [22  ['free', '60 req/min · 1,000 req/day', 'Search, asset overview, categories, indices, markets, trending'],23  ['hobby', '120 req/min · 10,000 req/day', '+ sales, listings, history; CSV exports'],24  ['professional', '600 req/min · 100,000 req/day', '+ index history, bulk comps, webhooks (alerts)'],25  ['research', 'custom', 'Historical backfills, raw provenance, academic use'],26  ['enterprise', 'custom', 'SLA, dedicated support, custom connectors'],27];2829export default function ApiDocsPage() {30  return (31    <div>32      <PageHeader kicker="Developers" title="API" description="Programmatic access to the same data that powers the site. Every response includes evidence fields (sample size, confidence, updated_at) and source attribution — never a bare number." />33      <Card className="p-5">34        <h2 className="text-sm font-semibold">Base URL & authentication</h2>35        <pre className="mt-2 overflow-x-auto rounded-md bg-sunken p-3 text-[12px] leading-relaxed"><code>{`https://www.rareindex.io/api/v1        # same-origin path36https://api.rareindex.io/v1            # dedicated host (when enabled)3738Authorization: Bearer ri_live_xxxxxxxxxxxxxxxxxxxx3940curl -s "https://www.rareindex.io/api/v1/assets/search?q=1999+charizard+psa+10&limit=5" \\41  -H "Authorization: Bearer $RAREINDEX_API_KEY"`}</code></pre>42        <p className="mt-2 text-[12px] text-muted">43          Keys are created from your account (44          <Link href="/account" className="underline">45            Account → API keys46          </Link>47          ). Rate limits are enforced per key and returned in <code className="rounded-sm bg-inset px-1">X-RateLimit-*</code> headers. All timestamps are ISO-8601 UTC; monetary fields carry <code className="rounded-sm bg-inset px-1">currency</code> and <code className="rounded-sm bg-inset px-1">price_usd</code>.48        </p>49      </Card>50      <Card className="mt-4 overflow-hidden">51        <CardHeader title="Endpoints" subtitle="v1 — stable contract; fields are only ever added" />52        <Table>53          <thead>54            <tr>55              <th className={th}>Method</th>56              <th className={th}>Path</th>57              <th className={th}>Description</th>58              <th className={th}>Query params</th>59              <th className={th}>Tier</th>60            </tr>61          </thead>62          <tbody>63            {ENDPOINTS.map((e) => (64              <tr key={e.path}>65                <td className={td}>66                  <Badge tone="index">{e.method}</Badge>67                </td>68                <td className={td}>69                  <code className="text-[12px]">{e.path}</code>70                </td>71                <td className={`${td} whitespace-normal text-muted`}>{e.desc}</td>72                <td className={`${td} whitespace-normal text-[11px] text-subtle`}>{e.params ?? '—'}</td>73                <td className={td}>74                  <Badge>{e.tier}+</Badge>75                </td>76              </tr>77            ))}78          </tbody>79        </Table>80      </Card>81      <div className="mt-4 grid gap-4 lg:grid-cols-2">82        <Card className="overflow-hidden">83          <CardHeader title="Tiers" subtitle="Limits are configurable per key" />84          <Table>85            <thead>86              <tr>87                <th className={th}>Tier</th>88                <th className={th}>Limits</th>89                <th className={th}>Access</th>90              </tr>91            </thead>92            <tbody>93              {TIERS.map(([t, l, a]) => (94                <tr key={t}>95                  <td className={`${td} font-medium`}>{t}</td>96                  <td className={`${td} text-muted`}>{l}</td>97                  <td className={`${td} whitespace-normal text-muted`}>{a}</td>98                </tr>99              ))}100            </tbody>101          </Table>102        </Card>103        <Card className="p-5">104          <h2 className="text-sm font-semibold">Response shape</h2>105          <pre className="mt-2 overflow-x-auto rounded-md bg-sunken p-3 text-[11px] leading-relaxed"><code>{`{106  "data": {107    "id": "rare_…", "slug": "…", "title": "…",108    "category": "pokemon",109    "valuation": {110      "riv_usd": 8450, "low_usd": 7600, "high_usd": 9300,111      "confidence": 0.82, "confidence_label": "high",112      "sample_size": 37, "updated_at": "2026-09-07T10:12:00Z"113    },114    "scores": { "liquidity": 71, "rarity": 64, "momentum_30d": 0.04 }115  },116  "meta": { "source_attribution": [...], "generated_at": "…" }117}`}</code></pre>118          <p className="mt-2 text-[12px] leading-relaxed text-muted">Missing evidence is returned as <code className="rounded-sm bg-inset px-1">null</code> with a reason, never as a fabricated value. Valuations are estimates; listing prices are not confirmed transactions.</p>119        </Card>120      </div>121    </div>122  );123}124