SPB Git

spb/hfmarketdata

Public

Open high-frequency market data platform — FirstRate full-history downloader, DuckDB/Parquet lake, open REST API and React docs platform (www.hfmarketdata.io)

web: pas de tarification — page Limits (/limits, /pricing redirige), libellés « free, higher limits on request », x-pricing OpenAPI

Simon-Pierre Boucher committed (Sep 4, 2026) parent c27e5c5 Browse files

7 changed files +13 −8

modified .gitignore +3 −0
@@ -29,3 +29,6 @@ hfmarketdata/venv/
29 .venv/ 29 .venv/
30 .pytest_cache/ 30 .pytest_cache/
31 .ruff_cache/ 31 .ruff_cache/
32+
33+# worktrees des agents / état local Claude
34+.claude/
modified docs/UPGRADE-PLAN.md +1 −1
@@ -149,7 +149,7 @@ Getting started Guides Reference (OpenAPI)
149 └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 149 └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
150 ``` 150 ```
151 151
152−### 4.3 Pricing / Limits (`/pricing`) 152+### 4.3 Access & limits (`/limits`) — tout est gratuit
153 Tableau 3 colonnes (Keyless · Free account · High usage) avec les 6 chiffres, CTA « Create free account » et « Request high usage → contact@spboucher.ai », encart « incitations » (Parquet = ½ coût, bulk hors quota, ETag/304 gratuits). 153 Tableau 3 colonnes (Keyless · Free account · High usage) avec les 6 chiffres, CTA « Create free account » et « Request high usage → contact@spboucher.ai », encart « incitations » (Parquet = ½ coût, bulk hors quota, ETag/304 gratuits).
154 154
155 ### 4.4 Homepage 155 ### 4.4 Homepage
modified hfmarketdata/api/openapi.py +1 −0
@@ -97,6 +97,7 @@ def build(app: FastAPI) -> dict:
97 responses["422"]["description"] = "`VALIDATION_ERROR` — " + CODES["VALIDATION_ERROR"] 97 responses["422"]["description"] = "`VALIDATION_ERROR` — " + CODES["VALIDATION_ERROR"]
98 responses["422"]["content"] = {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}} 98 responses["422"]["content"] = {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
99 schema["info"]["x-logo"] = {"url": settings.public_url + "/logo.svg"} 99 schema["info"]["x-logo"] = {"url": settings.public_url + "/logo.svg"}
100+ schema["info"]["x-pricing"] = {"free": True, "note": "HF Market Data is free. Higher limits are granted on request (free) — e-mail %s." % settings.contact_email}
100 schema["info"]["x-tiers"] = { 101 schema["info"]["x-tiers"] = {
101 "keyless": {"window": "1h", "requests": 30, "rows": 100_000, "max_rows_per_request": 5_000}, 102 "keyless": {"window": "1h", "requests": 30, "rows": 100_000, "max_rows_per_request": 5_000},
102 "free": {"window": "1m", "requests": 120, "rows": 1_000_000, "max_rows_per_request": 50_000}, 103 "free": {"window": "1m", "requests": 120, "rows": 1_000_000, "max_rows_per_request": 50_000},
modified hfmarketdata/web/src/App.jsx +4 −3
@@ -1,14 +1,14 @@
1 // Route table — one lazy chunk per area. Owners: web-core (home, pricing, docs, status, layout), 1 // Route table — one lazy chunk per area. Owners: web-core (home, pricing, docs, status, layout),
2 // web-app (playground, auth, dashboard, admin), mcp-skills (integrations). 2 // web-app (playground, auth, dashboard, admin), mcp-skills (integrations).
3 import React, { Suspense, lazy } from 'react' 3 import React, { Suspense, lazy } from 'react'
4−import { Route, Routes } from 'react-router-dom' 4+import { Navigate, Route, Routes } from 'react-router-dom'
5 import Layout from './app/Layout.jsx' 5 import Layout from './app/Layout.jsx'
6 6
7 const Home = lazy(() => import('./pages/home/Home.jsx')) 7 const Home = lazy(() => import('./pages/home/Home.jsx'))
8 const Docs = lazy(() => import('./pages/docs/Docs.jsx')) 8 const Docs = lazy(() => import('./pages/docs/Docs.jsx'))
9 const Playground = lazy(() => import('./pages/playground/Playground.jsx')) 9 const Playground = lazy(() => import('./pages/playground/Playground.jsx'))
10 const Integrations = lazy(() => import('./pages/integrations/Integrations.jsx')) 10 const Integrations = lazy(() => import('./pages/integrations/Integrations.jsx'))
11−const Pricing = lazy(() => import('./pages/pricing/Pricing.jsx')) 11+const Limits = lazy(() => import('./pages/limits/Limits.jsx'))
12 const Status = lazy(() => import('./pages/status/Status.jsx')) 12 const Status = lazy(() => import('./pages/status/Status.jsx'))
13 const Auth = lazy(() => import('./pages/auth/Auth.jsx')) 13 const Auth = lazy(() => import('./pages/auth/Auth.jsx'))
14 const Dashboard = lazy(() => import('./pages/dashboard/Dashboard.jsx')) 14 const Dashboard = lazy(() => import('./pages/dashboard/Dashboard.jsx'))
@@ -24,7 +24,8 @@ export default function App() {
24 <Route path="/docs/*" element={<Docs />} /> 24 <Route path="/docs/*" element={<Docs />} />
25 <Route path="/playground" element={<Playground />} /> 25 <Route path="/playground" element={<Playground />} />
26 <Route path="/integrations/*" element={<Integrations />} /> 26 <Route path="/integrations/*" element={<Integrations />} />
27− <Route path="/pricing" element={<Pricing />} /> 27+ <Route path="/limits" element={<Limits />} />
28+ <Route path="/pricing" element={<Navigate to="/limits" replace />} />
28 <Route path="/status" element={<Status />} /> 29 <Route path="/status" element={<Status />} />
29 <Route path="/signin" element={<Auth mode="signin" />} /> 30 <Route path="/signin" element={<Auth mode="signin" />} />
30 <Route path="/signup" element={<Auth mode="signup" />} /> 31 <Route path="/signup" element={<Auth mode="signup" />} />
modified hfmarketdata/web/src/app/Layout.jsx +1 −1
@@ -5,7 +5,7 @@ import { CONTACT_EMAIL } from './api.js'
5 5
6 const NAV = [ 6 const NAV = [
7 ['/docs', 'Docs'], ['/playground', 'Playground'], ['/integrations', 'Integrations'], 7 ['/docs', 'Docs'], ['/playground', 'Playground'], ['/integrations', 'Integrations'],
8− ['/pricing', 'Pricing / Limits'], ['/status', 'Status'], 8+ ['/limits', 'Limits'], ['/status', 'Status'],
9 ] 9 ]
10 10
11 export default function Layout({ children }) { 11 export default function Layout({ children }) {
modified hfmarketdata/web/src/app/api.js +1 −1
@@ -7,7 +7,7 @@ export const CONTACT_EMAIL = 'contact@spboucher.ai'
7 export const TIERS = [ 7 export const TIERS = [
8 { id: 'keyless', name: 'Keyless (per IP)', window: 'hour', requests: 30, rows: 100_000, maxRows: 5_000 }, 8 { id: 'keyless', name: 'Keyless (per IP)', window: 'hour', requests: 30, rows: 100_000, maxRows: 5_000 },
9 { id: 'free', name: 'Free account + API key', window: 'minute', requests: 120, rows: 1_000_000, maxRows: 50_000 }, 9 { id: 'free', name: 'Free account + API key', window: 'minute', requests: 120, rows: 1_000_000, maxRows: 50_000 },
10− { id: 'high_usage', name: 'High usage (on request)', window: 'minute', requests: 600, rows: 10_000_000, maxRows: 200_000 }, 10+ { id: 'high_usage', name: 'Higher limits (free, on request)', window: 'minute', requests: 600, rows: 10_000_000, maxRows: 200_000 },
11 ] 11 ]
12 12
13 export function rateHeaders(res) { 13 export function rateHeaders(res) {
renamed hfmarketdata/web/src/pages/pricing/Pricing.jsx → hfmarketdata/web/src/pages/limits/Limits.jsx +2 −2
@@ -1,5 +1,5 @@
1 import React from 'react' 1 import React from 'react'
2 // Placeholder — implemented by the owning agent (see App.jsx header). Keep the default export name. 2 // Placeholder — implemented by the owning agent (see App.jsx header). Keep the default export name.
3−export default function Pricing() { 3+export default function Limits() {
4− return <main className="page"><h1>Pricing</h1><p className="muted">Coming soon.</p></main> 4+ return <main className="page"><h1>Limits</h1><p className="muted">Coming soon.</p></main>
5 } 5 }
6 6
Menu

Theme