docs: 18 guides MDX (quickstart, auth, rate limits, formats avec benchmark réel, futures contrats + schémas SVG, options, fuseaux, bulk, 3 recettes, 6 pages fundamentals autour du bilan Apple T2 FY2024, versioning) + changelog v2.0.0 ; exemples réels capturés (content/examples), échantillon P/E AAPL, scripts fetch-examples / bench ; remark-gfm pour les tableaux, manifest Vite, corrections de débordement mobile
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
41 changed files +3,314 −65
modified
hfmarketdata/web/content/changelog.mdx
+1 −1
@@ -17,7 +17,7 @@ The v2 platform release. Every `/v1/*` endpoint that existed before keeps its pa | ||
| 17 | 17 | - **Uniform error envelope** — `{"error": {"code", "message", "docs", "type?", "details?"}}` on every error; the legacy `detail` field is preserved. All codes are listed on the [Errors](/docs/errors) page. |
| 18 | 18 | - **Response formats** — `format=parquet` on tabular v2 endpoints (counts half against the rows quota), `X-Row-Count` header, ETag / `If-None-Match` (304 responses are free). |
| 19 | 19 | - **OpenAPI 3.1** at `/openapi.json` with tags, markdown descriptions, examples and per-operation error codes. This documentation is generated from it. |
| 20 | −- **Website** — new documentation (3-column reference generated from the spec, guides, instant search with ⌘K), playground with deep links (`/playground?ep=…`), pricing/limits page, live status page, dashboard, light theme, MCP server and skills pack under Integrations. | |
| 20 | +- **Website** — new documentation (3-column reference generated from the spec, guides, instant search with ⌘K), playground with deep links (`/playground?ep=…`), access & limits page, live status page, dashboard, light theme, MCP server and skills pack under Integrations. | |
| 21 | 21 | |
| 22 | 22 | ### Changed |
| 23 | 23 | |
added
hfmarketdata/web/content/examples/bars_multi_v1_bars__asset__get.json
+70 −0
@@ -0,0 +1,70 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "bars_multi_v1_bars__asset__get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/bars/{asset}", | |
| 5 | + "params": { | |
| 6 | + "asset": "stock", | |
| 7 | + "tickers": "AAPL,MSFT,NVDA", | |
| 8 | + "timeframe": "1min", | |
| 9 | + "start": "2024-06-03 09:30:00", | |
| 10 | + "end": "2024-06-03 09:35:00", | |
| 11 | + "limit": "3" | |
| 12 | + }, | |
| 13 | + "url": "https://www.hfmarketdata.io/v1/bars/stock?tickers=AAPL,MSFT,NVDA&timeframe=1min&start=2024-06-03%2009:30:00&end=2024-06-03%2009:35:00&limit=3", | |
| 14 | + "status": 200, | |
| 15 | + "headers": { | |
| 16 | + "content-type": "application/json" | |
| 17 | + }, | |
| 18 | + "captured_at": "2026-09-04T23:37:54.477Z", | |
| 19 | + "body": { | |
| 20 | + "count": 9, | |
| 21 | + "data": [ | |
| 22 | + { | |
| 23 | + "ticker": "AAPL", | |
| 24 | + "datetime": "2024-06-03 09:30:00", | |
| 25 | + "open": 191.3528, | |
| 26 | + "high": 191.6799, | |
| 27 | + "low": 191.1446, | |
| 28 | + "close": 191.6601, | |
| 29 | + "volume": 1521665 | |
| 30 | + }, | |
| 31 | + { | |
| 32 | + "ticker": "AAPL", | |
| 33 | + "datetime": "2024-06-03 09:31:00", | |
| 34 | + "open": 191.6403, | |
| 35 | + "high": 191.7295, | |
| 36 | + "low": 190.9265, | |
| 37 | + "close": 191.1248, | |
| 38 | + "volume": 385549 | |
| 39 | + }, | |
| 40 | + { | |
| 41 | + "ticker": "AAPL", | |
| 42 | + "datetime": "2024-06-03 09:32:00", | |
| 43 | + "open": 191.1198, | |
| 44 | + "high": 191.2338, | |
| 45 | + "low": 190.867, | |
| 46 | + "close": 191.1049, | |
| 47 | + "volume": 225777 | |
| 48 | + }, | |
| 49 | + { | |
| 50 | + "ticker": "MSFT", | |
| 51 | + "datetime": "2024-06-03 09:30:00", | |
| 52 | + "open": 409.0976, | |
| 53 | + "high": 409.8361, | |
| 54 | + "low": 408.3888, | |
| 55 | + "close": 408.5069, | |
| 56 | + "volume": 489654 | |
| 57 | + }, | |
| 58 | + { | |
| 59 | + "ticker": "MSFT", | |
| 60 | + "datetime": "2024-06-03 09:31:00", | |
| 61 | + "open": 408.438, | |
| 62 | + "high": 409.4029, | |
| 63 | + "low": 408.3297, | |
| 64 | + "close": 408.8614, | |
| 65 | + "volume": 60686 | |
| 66 | + } | |
| 67 | + ], | |
| 68 | + "_truncated": "4 more rows omitted in this example" | |
| 69 | + } | |
| 70 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/bars_v1_bars__asset___ticker__get.json
+69 −0
@@ -0,0 +1,69 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "bars_v1_bars__asset___ticker__get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/bars/{asset}/{ticker}", | |
| 5 | + "params": { | |
| 6 | + "asset": "stock", | |
| 7 | + "ticker": "AAPL", | |
| 8 | + "timeframe": "1day", | |
| 9 | + "start": "2024-06-03", | |
| 10 | + "end": "2024-06-07", | |
| 11 | + "limit": "5" | |
| 12 | + }, | |
| 13 | + "url": "https://www.hfmarketdata.io/v1/bars/stock/AAPL?timeframe=1day&start=2024-06-03&end=2024-06-07&limit=5", | |
| 14 | + "status": 200, | |
| 15 | + "headers": { | |
| 16 | + "content-type": "application/json" | |
| 17 | + }, | |
| 18 | + "captured_at": "2026-09-04T23:37:54.341Z", | |
| 19 | + "body": { | |
| 20 | + "count": 5, | |
| 21 | + "data": [ | |
| 22 | + { | |
| 23 | + "ticker": "AAPL", | |
| 24 | + "datetime": "2024-06-03", | |
| 25 | + "open": 191.2434, | |
| 26 | + "high": 193.3154, | |
| 27 | + "low": 190.8666, | |
| 28 | + "close": 192.3637, | |
| 29 | + "volume": 50080500 | |
| 30 | + }, | |
| 31 | + { | |
| 32 | + "ticker": "AAPL", | |
| 33 | + "datetime": "2024-06-04", | |
| 34 | + "open": 192.9684, | |
| 35 | + "high": 193.6426, | |
| 36 | + "low": 191.3722, | |
| 37 | + "close": 192.6809, | |
| 38 | + "volume": 47471400 | |
| 39 | + }, | |
| 40 | + { | |
| 41 | + "ticker": "AAPL", | |
| 42 | + "datetime": "2024-06-05", | |
| 43 | + "open": 193.7219, | |
| 44 | + "high": 195.209, | |
| 45 | + "low": 193.1964, | |
| 46 | + "close": 194.1879, | |
| 47 | + "volume": 54156800 | |
| 48 | + }, | |
| 49 | + { | |
| 50 | + "ticker": "AAPL", | |
| 51 | + "datetime": "2024-06-06", | |
| 52 | + "open": 194.0094, | |
| 53 | + "high": 194.8124, | |
| 54 | + "low": 192.5025, | |
| 55 | + "close": 192.8098, | |
| 56 | + "volume": 41181800 | |
| 57 | + }, | |
| 58 | + { | |
| 59 | + "ticker": "AAPL", | |
| 60 | + "datetime": "2024-06-07", | |
| 61 | + "open": 192.9783, | |
| 62 | + "high": 195.2487, | |
| 63 | + "low": 192.4727, | |
| 64 | + "close": 195.1991, | |
| 65 | + "volume": 53103900 | |
| 66 | + } | |
| 67 | + ] | |
| 68 | + } | |
| 69 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/health_health_get.json
+18 −0
@@ -0,0 +1,18 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "health_health_get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/health", | |
| 5 | + "params": {}, | |
| 6 | + "url": "https://www.hfmarketdata.io/health", | |
| 7 | + "status": 200, | |
| 8 | + "headers": { | |
| 9 | + "content-type": "application/json" | |
| 10 | + }, | |
| 11 | + "captured_at": "2026-09-04T23:37:53.274Z", | |
| 12 | + "body": { | |
| 13 | + "status": "ok", | |
| 14 | + "service": "hfmarketdata-api", | |
| 15 | + "version": "1.0.0", | |
| 16 | + "data_root_present": true | |
| 17 | + } | |
| 18 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/opt_chain_v1_options_chain__ticker__get.json
+119 −0
@@ -0,0 +1,119 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "opt_chain_v1_options_chain__ticker__get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/options/chain/{ticker}", | |
| 5 | + "params": { | |
| 6 | + "ticker": "AAPL", | |
| 7 | + "trade_date": "2024-06-21", | |
| 8 | + "call_put": "c", | |
| 9 | + "strike_min": "200", | |
| 10 | + "strike_max": "210", | |
| 11 | + "limit": "5" | |
| 12 | + }, | |
| 13 | + "url": "https://www.hfmarketdata.io/v1/options/chain/AAPL?trade_date=2024-06-21&call_put=c&strike_min=200&strike_max=210&limit=5", | |
| 14 | + "status": 200, | |
| 15 | + "headers": { | |
| 16 | + "content-type": "application/json" | |
| 17 | + }, | |
| 18 | + "captured_at": "2026-09-04T23:37:55.585Z", | |
| 19 | + "body": { | |
| 20 | + "count": 5, | |
| 21 | + "data": [ | |
| 22 | + { | |
| 23 | + "ticker": "AAPL", | |
| 24 | + "trade_date": "2024-06-21", | |
| 25 | + "strike": 200, | |
| 26 | + "expiry": "2024-06-21", | |
| 27 | + "call_put": "c", | |
| 28 | + "last_price": 10.5, | |
| 29 | + "bid": 10.35, | |
| 30 | + "ask": 10.6, | |
| 31 | + "bid_iv": 0, | |
| 32 | + "ask_iv": 2.5183, | |
| 33 | + "open_interest": 48861, | |
| 34 | + "volume": 11238, | |
| 35 | + "delta": 1, | |
| 36 | + "gamma": 0, | |
| 37 | + "vega": 0, | |
| 38 | + "theta": -0.99, | |
| 39 | + "rho": 0.0003 | |
| 40 | + }, | |
| 41 | + { | |
| 42 | + "ticker": "AAPL", | |
| 43 | + "trade_date": "2024-06-21", | |
| 44 | + "strike": 202.5, | |
| 45 | + "expiry": "2024-06-21", | |
| 46 | + "call_put": "c", | |
| 47 | + "last_price": 8, | |
| 48 | + "bid": 7.9, | |
| 49 | + "ask": 8.1, | |
| 50 | + "bid_iv": 0, | |
| 51 | + "ask_iv": 2.0086, | |
| 52 | + "open_interest": 13402, | |
| 53 | + "volume": 1107, | |
| 54 | + "delta": 1, | |
| 55 | + "gamma": 0, | |
| 56 | + "vega": 0, | |
| 57 | + "theta": -1.0026, | |
| 58 | + "rho": 0.0003 | |
| 59 | + }, | |
| 60 | + { | |
| 61 | + "ticker": "AAPL", | |
| 62 | + "trade_date": "2024-06-21", | |
| 63 | + "strike": 205, | |
| 64 | + "expiry": "2024-06-21", | |
| 65 | + "call_put": "c", | |
| 66 | + "last_price": 5.5, | |
| 67 | + "bid": 5.4, | |
| 68 | + "ask": 5.6, | |
| 69 | + "bid_iv": 0, | |
| 70 | + "ask_iv": 1.4836, | |
| 71 | + "open_interest": 24344, | |
| 72 | + "volume": 17883, | |
| 73 | + "delta": 0.9991, | |
| 74 | + "gamma": 0.0017, | |
| 75 | + "vega": 0.0001, | |
| 76 | + "theta": -1.0674, | |
| 77 | + "rho": 0.0003 | |
| 78 | + }, | |
| 79 | + { | |
| 80 | + "ticker": "AAPL", | |
| 81 | + "trade_date": "2024-06-21", | |
| 82 | + "strike": 207.5, | |
| 83 | + "expiry": "2024-06-21", | |
| 84 | + "call_put": "c", | |
| 85 | + "last_price": 3.02, | |
| 86 | + "bid": 2.96, | |
| 87 | + "ask": 3.1, | |
| 88 | + "bid_iv": 0, | |
| 89 | + "ask_iv": 0.9295, | |
| 90 | + "open_interest": 21853, | |
| 91 | + "volume": 7029, | |
| 92 | + "delta": 0.9658, | |
| 93 | + "gamma": 0.0456, | |
| 94 | + "vega": 0.002, | |
| 95 | + "theta": -2.259, | |
| 96 | + "rho": 0.0003 | |
| 97 | + }, | |
| 98 | + { | |
| 99 | + "ticker": "AAPL", | |
| 100 | + "trade_date": "2024-06-21", | |
| 101 | + "strike": 210, | |
| 102 | + "expiry": "2024-06-21", | |
| 103 | + "call_put": "c", | |
| 104 | + "last_price": 0.85, | |
| 105 | + "bid": 0.83, | |
| 106 | + "ask": 0.87, | |
| 107 | + "bid_iv": 0.5521, | |
| 108 | + "ask_iv": 0.5959, | |
| 109 | + "open_interest": 42604, | |
| 110 | + "volume": 71812, | |
| 111 | + "delta": 0.6401, | |
| 112 | + "gamma": 0.2656, | |
| 113 | + "vega": 0.0091, | |
| 114 | + "theta": -5.9742, | |
| 115 | + "rho": 0.0002 | |
| 116 | + } | |
| 117 | + ] | |
| 118 | + } | |
| 119 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/opt_expirations_v1_options_expirations__ticker__get.json
+33 −0
@@ -0,0 +1,33 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "opt_expirations_v1_options_expirations__ticker__get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/options/expirations/{ticker}", | |
| 5 | + "params": { | |
| 6 | + "ticker": "AAPL", | |
| 7 | + "trade_date": "2024-06-21" | |
| 8 | + }, | |
| 9 | + "url": "https://www.hfmarketdata.io/v1/options/expirations/AAPL?trade_date=2024-06-21", | |
| 10 | + "status": 200, | |
| 11 | + "headers": { | |
| 12 | + "content-type": "application/json" | |
| 13 | + }, | |
| 14 | + "captured_at": "2026-09-04T23:37:55.674Z", | |
| 15 | + "body": { | |
| 16 | + "ticker": "AAPL", | |
| 17 | + "expirations": [ | |
| 18 | + "2024-06-21", | |
| 19 | + "2024-06-28", | |
| 20 | + "2024-07-05", | |
| 21 | + "2024-07-12", | |
| 22 | + "2024-07-19", | |
| 23 | + "2024-07-26", | |
| 24 | + "2024-08-02", | |
| 25 | + "2024-08-16", | |
| 26 | + "2024-09-20", | |
| 27 | + "2024-10-18", | |
| 28 | + "2024-11-15", | |
| 29 | + "2024-12-20", | |
| 30 | + "… 8 more" | |
| 31 | + ] | |
| 32 | + } | |
| 33 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/opt_history_v1_options_history__ticker__get.json
+118 −0
@@ -0,0 +1,118 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "opt_history_v1_options_history__ticker__get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/options/history/{ticker}", | |
| 5 | + "params": { | |
| 6 | + "ticker": "AAPL", | |
| 7 | + "strike": "200", | |
| 8 | + "expiry": "2024-12-20", | |
| 9 | + "call_put": "c", | |
| 10 | + "limit": "5" | |
| 11 | + }, | |
| 12 | + "url": "https://www.hfmarketdata.io/v1/options/history/AAPL?strike=200&expiry=2024-12-20&call_put=c&limit=5", | |
| 13 | + "status": 200, | |
| 14 | + "headers": { | |
| 15 | + "content-type": "application/json" | |
| 16 | + }, | |
| 17 | + "captured_at": "2026-09-04T23:37:55.763Z", | |
| 18 | + "body": { | |
| 19 | + "count": 5, | |
| 20 | + "data": [ | |
| 21 | + { | |
| 22 | + "ticker": "AAPL", | |
| 23 | + "trade_date": "2023-03-08", | |
| 24 | + "strike": 200, | |
| 25 | + "expiry": "2024-12-20", | |
| 26 | + "call_put": "c", | |
| 27 | + "last_price": 10.43, | |
| 28 | + "bid": 8.6, | |
| 29 | + "ask": 11.85, | |
| 30 | + "bid_iv": 0.2279, | |
| 31 | + "ask_iv": 0.2708, | |
| 32 | + "open_interest": 0, | |
| 33 | + "volume": 0, | |
| 34 | + "delta": 0.3525, | |
| 35 | + "gamma": 0.0072, | |
| 36 | + "vega": 0.7844, | |
| 37 | + "theta": -0.0203, | |
| 38 | + "rho": 0.7796 | |
| 39 | + }, | |
| 40 | + { | |
| 41 | + "ticker": "AAPL", | |
| 42 | + "trade_date": "2023-03-09", | |
| 43 | + "strike": 200, | |
| 44 | + "expiry": "2024-12-20", | |
| 45 | + "call_put": "c", | |
| 46 | + "last_price": 10.88, | |
| 47 | + "bid": 8.75, | |
| 48 | + "ask": 13, | |
| 49 | + "bid_iv": 0.2178, | |
| 50 | + "ask_iv": 0.2739, | |
| 51 | + "open_interest": 0, | |
| 52 | + "volume": 0, | |
| 53 | + "delta": 0.3835, | |
| 54 | + "gamma": 0.0074, | |
| 55 | + "vega": 0.7542, | |
| 56 | + "theta": -0.024, | |
| 57 | + "rho": 0.8238 | |
| 58 | + }, | |
| 59 | + { | |
| 60 | + "ticker": "AAPL", | |
| 61 | + "trade_date": "2023-03-21", | |
| 62 | + "strike": 200, | |
| 63 | + "expiry": "2024-12-20", | |
| 64 | + "call_put": "c", | |
| 65 | + "last_price": 12.45, | |
| 66 | + "bid": 11.4, | |
| 67 | + "ask": 14.15, | |
| 68 | + "bid_iv": 0.2452, | |
| 69 | + "ask_iv": 0.2793, | |
| 70 | + "open_interest": 0, | |
| 71 | + "volume": 2, | |
| 72 | + "delta": 0.3927, | |
| 73 | + "gamma": 0.0069, | |
| 74 | + "vega": 0.8087, | |
| 75 | + "theta": -0.0224, | |
| 76 | + "rho": 0.8699 | |
| 77 | + }, | |
| 78 | + { | |
| 79 | + "ticker": "AAPL", | |
| 80 | + "trade_date": "2023-03-22", | |
| 81 | + "strike": 200, | |
| 82 | + "expiry": "2024-12-20", | |
| 83 | + "call_put": "c", | |
| 84 | + "last_price": 12.01, | |
| 85 | + "bid": 10.8, | |
| 86 | + "ask": 13.4, | |
| 87 | + "bid_iv": 0.2369, | |
| 88 | + "ask_iv": 0.2693, | |
| 89 | + "open_interest": 1, | |
| 90 | + "volume": 1, | |
| 91 | + "delta": 0.3836, | |
| 92 | + "gamma": 0.0072, | |
| 93 | + "vega": 0.8187, | |
| 94 | + "theta": -0.0216, | |
| 95 | + "rho": 0.8586 | |
| 96 | + }, | |
| 97 | + { | |
| 98 | + "ticker": "AAPL", | |
| 99 | + "trade_date": "2023-03-23", | |
| 100 | + "strike": 200, | |
| 101 | + "expiry": "2024-12-20", | |
| 102 | + "call_put": "c", | |
| 103 | + "last_price": 11.78, | |
| 104 | + "bid": 10.8, | |
| 105 | + "ask": 12.75, | |
| 106 | + "bid_iv": 0.2463, | |
| 107 | + "ask_iv": 0.2708, | |
| 108 | + "open_interest": 2, | |
| 109 | + "volume": 0, | |
| 110 | + "delta": 0.3692, | |
| 111 | + "gamma": 0.007, | |
| 112 | + "vega": 0.8063, | |
| 113 | + "theta": -0.0204, | |
| 114 | + "rho": 0.8254 | |
| 115 | + } | |
| 116 | + ] | |
| 117 | + } | |
| 118 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/opt_quarters_v1_options_quarters_get.json
+29 −0
@@ -0,0 +1,29 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "opt_quarters_v1_options_quarters_get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/options/quarters", | |
| 5 | + "params": {}, | |
| 6 | + "url": "https://www.hfmarketdata.io/v1/options/quarters", | |
| 7 | + "status": 200, | |
| 8 | + "headers": { | |
| 9 | + "content-type": "application/json" | |
| 10 | + }, | |
| 11 | + "captured_at": "2026-09-04T23:37:54.713Z", | |
| 12 | + "body": { | |
| 13 | + "quarters": [ | |
| 14 | + "2010_q1", | |
| 15 | + "2010_q2", | |
| 16 | + "2010_q3", | |
| 17 | + "2010_q4", | |
| 18 | + "2011_q1", | |
| 19 | + "2011_q2", | |
| 20 | + "2011_q3", | |
| 21 | + "2011_q4", | |
| 22 | + "2012_q1", | |
| 23 | + "2012_q2", | |
| 24 | + "2012_q3", | |
| 25 | + "2012_q4", | |
| 26 | + "… 55 more" | |
| 27 | + ] | |
| 28 | + } | |
| 29 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/opt_tickers_v1_options_tickers_get.json
+31 −0
@@ -0,0 +1,31 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "opt_tickers_v1_options_tickers_get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/options/tickers", | |
| 5 | + "params": { | |
| 6 | + "search": "TSL", | |
| 7 | + "limit": "10" | |
| 8 | + }, | |
| 9 | + "url": "https://www.hfmarketdata.io/v1/options/tickers?search=TSL&limit=10", | |
| 10 | + "status": 200, | |
| 11 | + "headers": { | |
| 12 | + "content-type": "application/json" | |
| 13 | + }, | |
| 14 | + "captured_at": "2026-09-04T23:37:54.794Z", | |
| 15 | + "body": { | |
| 16 | + "quarter": "2026_q3_partial", | |
| 17 | + "count": 10, | |
| 18 | + "tickers": [ | |
| 19 | + "TSL", | |
| 20 | + "TSLA", | |
| 21 | + "TSLG", | |
| 22 | + "TSLL", | |
| 23 | + "TSLP", | |
| 24 | + "TSLQ", | |
| 25 | + "TSLR", | |
| 26 | + "TSLS", | |
| 27 | + "TSLT", | |
| 28 | + "TSLW" | |
| 29 | + ] | |
| 30 | + } | |
| 31 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/snapshot_v1_snapshot__asset__get.json
+49 −0
@@ -0,0 +1,49 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "snapshot_v1_snapshot__asset__get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/snapshot/{asset}", | |
| 5 | + "params": { | |
| 6 | + "asset": "stock", | |
| 7 | + "tickers": "AAPL,MSFT,NVDA", | |
| 8 | + "at": "2024-06-03 10:35:00", | |
| 9 | + "timeframe": "1min" | |
| 10 | + }, | |
| 11 | + "url": "https://www.hfmarketdata.io/v1/snapshot/stock?tickers=AAPL,MSFT,NVDA&at=2024-06-03%2010:35:00&timeframe=1min", | |
| 12 | + "status": 200, | |
| 13 | + "headers": { | |
| 14 | + "content-type": "application/json" | |
| 15 | + }, | |
| 16 | + "captured_at": "2026-09-04T23:37:54.632Z", | |
| 17 | + "body": { | |
| 18 | + "count": 3, | |
| 19 | + "data": [ | |
| 20 | + { | |
| 21 | + "ticker": "AAPL", | |
| 22 | + "datetime": "2024-06-03 10:35:00", | |
| 23 | + "open": 193.1275, | |
| 24 | + "high": 193.1363, | |
| 25 | + "low": 192.8796, | |
| 26 | + "close": 192.8796, | |
| 27 | + "volume": 93654 | |
| 28 | + }, | |
| 29 | + { | |
| 30 | + "ticker": "MSFT", | |
| 31 | + "datetime": "2024-06-03 10:35:00", | |
| 32 | + "open": 408.1722, | |
| 33 | + "high": 408.3888, | |
| 34 | + "low": 408.0294, | |
| 35 | + "close": 408.1229, | |
| 36 | + "volume": 21898 | |
| 37 | + }, | |
| 38 | + { | |
| 39 | + "ticker": "NVDA", | |
| 40 | + "datetime": "2024-06-03 10:35:00", | |
| 41 | + "open": 113.4484, | |
| 42 | + "high": 113.4823, | |
| 43 | + "low": 113.2238, | |
| 44 | + "close": 113.2557, | |
| 45 | + "volume": 870250 | |
| 46 | + } | |
| 47 | + ] | |
| 48 | + } | |
| 49 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/status_v1_status_get.json
+236 −0
@@ -0,0 +1,236 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "status_v1_status_get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/status", | |
| 5 | + "params": {}, | |
| 6 | + "url": "https://www.hfmarketdata.io/v1/status", | |
| 7 | + "status": 200, | |
| 8 | + "headers": { | |
| 9 | + "content-type": "application/json" | |
| 10 | + }, | |
| 11 | + "captured_at": "2026-09-04T23:37:54.183Z", | |
| 12 | + "body": { | |
| 13 | + "datasets": { | |
| 14 | + "stock": { | |
| 15 | + "1day": { | |
| 16 | + "UNADJUSTED": 7665, | |
| 17 | + "adj_split": 7664, | |
| 18 | + "adj_splitdiv": 7664 | |
| 19 | + }, | |
| 20 | + "1hour": { | |
| 21 | + "adj_split": 7669, | |
| 22 | + "adj_splitdiv": 7669 | |
| 23 | + }, | |
| 24 | + "1min": { | |
| 25 | + "UNADJUSTED": 7670, | |
| 26 | + "adj_split": 7670, | |
| 27 | + "adj_splitdiv": 7670 | |
| 28 | + }, | |
| 29 | + "30min": { | |
| 30 | + "adj_split": 7669, | |
| 31 | + "adj_splitdiv": 7669 | |
| 32 | + }, | |
| 33 | + "5min": { | |
| 34 | + "adj_split": 7669, | |
| 35 | + "adj_splitdiv": 7669 | |
| 36 | + } | |
| 37 | + }, | |
| 38 | + "etf": { | |
| 39 | + "1day": { | |
| 40 | + "UNADJUSTED": 5161, | |
| 41 | + "adj_split": 5161, | |
| 42 | + "adj_splitdiv": 5161 | |
| 43 | + }, | |
| 44 | + "1hour": { | |
| 45 | + "adj_split": 5161, | |
| 46 | + "adj_splitdiv": 5161 | |
| 47 | + }, | |
| 48 | + "1min": { | |
| 49 | + "UNADJUSTED": 5161, | |
| 50 | + "adj_split": 5161, | |
| 51 | + "adj_splitdiv": 5161 | |
| 52 | + }, | |
| 53 | + "30min": { | |
| 54 | + "adj_split": 5161, | |
| 55 | + "adj_splitdiv": 5161 | |
| 56 | + }, | |
| 57 | + "5min": { | |
| 58 | + "adj_split": 5161, | |
| 59 | + "adj_splitdiv": 5161 | |
| 60 | + } | |
| 61 | + }, | |
| 62 | + "futures": { | |
| 63 | + "1day": { | |
| 64 | + "contin_UNadj": 132, | |
| 65 | + "contin_adj_absolute": 132, | |
| 66 | + "contin_adj_ratio": 132 | |
| 67 | + }, | |
| 68 | + "1hour": { | |
| 69 | + "contin_UNadj": 131, | |
| 70 | + "contin_adj_absolute": 131, | |
| 71 | + "contin_adj_ratio": 131 | |
| 72 | + }, | |
| 73 | + "1min": { | |
| 74 | + "contin_UNadj": 131, | |
| 75 | + "contin_adj_absolute": 131, | |
| 76 | + "contin_adj_ratio": 131 | |
| 77 | + }, | |
| 78 | + "30min": { | |
| 79 | + "contin_UNadj": 131, | |
| 80 | + "contin_adj_absolute": 131, | |
| 81 | + "contin_adj_ratio": 131 | |
| 82 | + }, | |
| 83 | + "5min": { | |
| 84 | + "contin_UNadj": 131, | |
| 85 | + "contin_adj_absolute": 131, | |
| 86 | + "contin_adj_ratio": 131 | |
| 87 | + } | |
| 88 | + }, | |
| 89 | + "futures_contracts": { | |
| 90 | + "1day": { | |
| 91 | + "archive": 15279, | |
| 92 | + "update": 3288 | |
| 93 | + }, | |
| 94 | + "1hour": { | |
| 95 | + "archive": 14497, | |
| 96 | + "update": 2581 | |
| 97 | + }, | |
| 98 | + "1min": { | |
| 99 | + "archive": 14577, | |
| 100 | + "update": 2581 | |
| 101 | + }, | |
| 102 | + "30min": { | |
| 103 | + "archive": 14497, | |
| 104 | + "update": 2581 | |
| 105 | + }, | |
| 106 | + "5min": { | |
| 107 | + "archive": 14497, | |
| 108 | + "update": 2581 | |
| 109 | + } | |
| 110 | + }, | |
| 111 | + "crypto": { | |
| 112 | + "1day": { | |
| 113 | + "none": 74 | |
| 114 | + }, | |
| 115 | + "1hour": { | |
| 116 | + "none": 74 | |
| 117 | + }, | |
| 118 | + "1min": { | |
| 119 | + "none": 74 | |
| 120 | + }, | |
| 121 | + "30min": { | |
| 122 | + "none": 74 | |
| 123 | + }, | |
| 124 | + "5min": { | |
| 125 | + "none": 74 | |
| 126 | + } | |
| 127 | + }, | |
| 128 | + "index": { | |
| 129 | + "1day": { | |
| 130 | + "none": 125 | |
| 131 | + }, | |
| 132 | + "1hour": { | |
| 133 | + "none": 122 | |
| 134 | + }, | |
| 135 | + "1min": { | |
| 136 | + "none": 123 | |
| 137 | + }, | |
| 138 | + "30min": { | |
| 139 | + "none": 122 | |
| 140 | + }, | |
| 141 | + "5min": { | |
| 142 | + "none": 122 | |
| 143 | + } | |
| 144 | + }, | |
| 145 | + "fx": { | |
| 146 | + "1day": { | |
| 147 | + "none": 79 | |
| 148 | + }, | |
| 149 | + "1hour": { | |
| 150 | + "none": 79 | |
| 151 | + }, | |
| 152 | + "1min": { | |
| 153 | + "none": 79 | |
| 154 | + }, | |
| 155 | + "30min": { | |
| 156 | + "none": 79 | |
| 157 | + }, | |
| 158 | + "5min": { | |
| 159 | + "none": 79 | |
| 160 | + } | |
| 161 | + }, | |
| 162 | + "options": { | |
| 163 | + "quarters": [ | |
| 164 | + "2010_q1", | |
| 165 | + "2010_q2", | |
| 166 | + "2010_q3", | |
| 167 | + "2010_q4", | |
| 168 | + "2011_q1", | |
| 169 | + "2011_q2", | |
| 170 | + "2011_q3", | |
| 171 | + "2011_q4", | |
| 172 | + "2012_q1", | |
| 173 | + "2012_q2", | |
| 174 | + "2012_q3", | |
| 175 | + "2012_q4", | |
| 176 | + "2013_q1", | |
| 177 | + "2013_q2", | |
| 178 | + "2013_q3", | |
| 179 | + "2013_q4", | |
| 180 | + "2014_q1", | |
| 181 | + "2014_q2", | |
| 182 | + "2014_q3", | |
| 183 | + "2014_q4", | |
| 184 | + "2015_q1", | |
| 185 | + "2015_q2", | |
| 186 | + "2015_q3", | |
| 187 | + "2015_q4", | |
| 188 | + "2016_q1", | |
| 189 | + "2016_q2", | |
| 190 | + "2016_q3", | |
| 191 | + "2016_q4", | |
| 192 | + "2017_q1", | |
| 193 | + "2017_q2", | |
| 194 | + "2017_q3", | |
| 195 | + "2017_q4", | |
| 196 | + "2018_q1", | |
| 197 | + "2018_q2", | |
| 198 | + "2018_q3", | |
| 199 | + "2018_q4", | |
| 200 | + "2019_q1", | |
| 201 | + "2019_q2", | |
| 202 | + "2019_q3", | |
| 203 | + "2019_q4", | |
| 204 | + "2020_q1", | |
| 205 | + "2020_q2", | |
| 206 | + "2020_q3", | |
| 207 | + "2020_q4", | |
| 208 | + "2021_q1", | |
| 209 | + "2021_q2", | |
| 210 | + "2021_q3", | |
| 211 | + "2021_q4", | |
| 212 | + "2022_q1", | |
| 213 | + "2022_q2", | |
| 214 | + "2022_q3", | |
| 215 | + "2022_q4", | |
| 216 | + "2023_q1", | |
| 217 | + "2023_q2", | |
| 218 | + "2023_q3", | |
| 219 | + "2023_q4", | |
| 220 | + "2024_q1", | |
| 221 | + "2024_q2", | |
| 222 | + "2024_q3", | |
| 223 | + "2024_q4", | |
| 224 | + "2025_q1", | |
| 225 | + "2025_q2", | |
| 226 | + "2025_q3", | |
| 227 | + "2025_q4", | |
| 228 | + "2026_q1", | |
| 229 | + "2026_q2", | |
| 230 | + "2026_q3_partial" | |
| 231 | + ], | |
| 232 | + "files_latest_quarter": 6056 | |
| 233 | + } | |
| 234 | + } | |
| 235 | + } | |
| 236 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/examples/tickers_v1__asset__tickers_get.json
+29 −0
@@ -0,0 +1,29 @@ | ||
| 1 | +{ | |
| 2 | + "operationId": "tickers_v1__asset__tickers_get", | |
| 3 | + "method": "GET", | |
| 4 | + "path": "/v1/{asset}/tickers", | |
| 5 | + "params": { | |
| 6 | + "asset": "stock", | |
| 7 | + "timeframe": "1day", | |
| 8 | + "search": "AAP", | |
| 9 | + "limit": "10" | |
| 10 | + }, | |
| 11 | + "url": "https://www.hfmarketdata.io/v1/stock/tickers?timeframe=1day&search=AAP&limit=10", | |
| 12 | + "status": 200, | |
| 13 | + "headers": { | |
| 14 | + "content-type": "application/json" | |
| 15 | + }, | |
| 16 | + "captured_at": "2026-09-04T23:37:54.270Z", | |
| 17 | + "body": { | |
| 18 | + "asset": "stock", | |
| 19 | + "timeframe": "1day", | |
| 20 | + "adjustment": "adj_splitdiv", | |
| 21 | + "count": 4, | |
| 22 | + "tickers": [ | |
| 23 | + "AAP", | |
| 24 | + "AAPG", | |
| 25 | + "AAPL", | |
| 26 | + "CAAP" | |
| 27 | + ] | |
| 28 | + } | |
| 29 | +} | |
| \ No newline at end of file | ||
added
hfmarketdata/web/content/guides/authentication.mdx
+119 −0
@@ -0,0 +1,119 @@ | ||
| 1 | +export const meta = { title: 'Authentication & API keys', description: 'Keyless access, free accounts, high-usage tier, and how to send your API key safely.' } | |
| 2 | + | |
| 3 | +export const snippets = [ | |
| 4 | + { | |
| 5 | + title: 'Send the key (header, preferred)', | |
| 6 | + curl: `export HFMD_API_KEY="hfmd_live_…" # from the dashboard, shown once | |
| 7 | + | |
| 8 | +curl "https://www.hfmarketdata.io/v1/bars/stock/AAPL?timeframe=1min&limit=5" \\ | |
| 9 | + -H "Authorization: Bearer $HFMD_API_KEY"`, | |
| 10 | + python: `import os, requests | |
| 11 | + | |
| 12 | +session = requests.Session() | |
| 13 | +session.headers["Authorization"] = f"Bearer {os.environ['HFMD_API_KEY']}" | |
| 14 | + | |
| 15 | +r = session.get("https://www.hfmarketdata.io/v1/bars/stock/AAPL", params={"timeframe": "1min", "limit": 5}) | |
| 16 | +r.raise_for_status() | |
| 17 | +print(r.headers["X-RateLimit-Limit-Requests"]) # 120 with a free key`, | |
| 18 | + javascript: `const headers = { Authorization: \`Bearer \${process.env.HFMD_API_KEY}\` }; | |
| 19 | +const res = await fetch("https://www.hfmarketdata.io/v1/bars/stock/AAPL?timeframe=1min&limit=5", { headers }); | |
| 20 | +console.log(res.headers.get("X-RateLimit-Limit-Requests")); // 120 with a free key`, | |
| 21 | + r: `library(httr2) | |
| 22 | + | |
| 23 | +resp <- request("https://www.hfmarketdata.io/v1/bars/stock/AAPL") |> | |
| 24 | + req_url_query(timeframe = "1min", limit = 5) |> | |
| 25 | + req_auth_bearer_token(Sys.getenv("HFMD_API_KEY")) |> | |
| 26 | + req_perform() | |
| 27 | +resp_header(resp, "X-RateLimit-Limit-Requests")`, | |
| 28 | + }, | |
| 29 | + { | |
| 30 | + title: 'Check who you are and what you have left', | |
| 31 | + curl: `curl "https://www.hfmarketdata.io/v1/limits" -H "Authorization: Bearer $HFMD_API_KEY"`, | |
| 32 | + python: `r = session.get("https://www.hfmarketdata.io/v1/limits") | |
| 33 | +print(r.json()) # tier, windows, remaining requests / rows`, | |
| 34 | + javascript: `const limits = await (await fetch("https://www.hfmarketdata.io/v1/limits", { headers })).json(); | |
| 35 | +console.log(limits);`, | |
| 36 | + r: `request("https://www.hfmarketdata.io/v1/limits") |> | |
| 37 | + req_auth_bearer_token(Sys.getenv("HFMD_API_KEY")) |> | |
| 38 | + req_perform() |> resp_body_json()`, | |
| 39 | + }, | |
| 40 | +] | |
| 41 | + | |
| 42 | +# Authentication & API keys | |
| 43 | + | |
| 44 | +Authentication on HF Market Data is **optional and progressive**. Every data endpoint works without credentials; a key only changes how much you can pull and how fast. | |
| 45 | + | |
| 46 | +## The three tiers | |
| 47 | + | |
| 48 | +| Tier | How you get it | Requests | Rows | Max rows per request | | |
| 49 | +| --- | --- | --- | --- | --- | | |
| 50 | +| **Keyless** — identified by IP | Nothing to do | 30 / hour | 100 000 / hour | 5 000 | | |
| 51 | +| **Free account** — API key | [Create an account](/signup) (e-mail verification) | 120 / minute | 1 000 000 / minute | 50 000 | | |
| 52 | +| **High usage** — API key | E-mail [contact@spboucher.ai](mailto:contact@spboucher.ai?subject=High%20usage%20tier%20request) with your use case | 600 / minute | 10 000 000 / minute | 200 000 | | |
| 53 | + | |
| 54 | +Two details often missed: the keyless window is **one hour**, the keyed windows are **one minute** — so a free key is roughly 240× more requests than keyless, not 4×. And the *rows* budget is the one that bites for bulk pulls: see [rate limits](/docs/rate-limits) for what counts (Parquet counts half, bulk endpoints and `304 Not Modified` count zero). | |
| 55 | + | |
| 56 | +## Keyless access | |
| 57 | + | |
| 58 | +Requests without a key are metered per client IP. It is deliberately small — enough to evaluate the API, read the docs examples, and run the [playground](/playground). Corporate NATs share one IP, so a team behind the same egress shares the 30 requests: if you hit `RATE_LIMIT_EXCEEDED` sooner than expected, that is usually why. The fix is a free key each. | |
| 59 | + | |
| 60 | +## Free account and your first key | |
| 61 | + | |
| 62 | +1. [Sign up](/signup) with an e-mail address and a password, then click the verification link you receive. Sign-up itself is rate limited per IP. | |
| 63 | +2. In the dashboard, open **API keys → Create key**. Give it a name (`laptop`, `airflow-prod`…). | |
| 64 | +3. The full key — `hfmd_live_` followed by 32 base62 characters — is displayed **once**. Copy it into a secret store or an environment variable (`HFMD_API_KEY`). We only keep a salted SHA-256 hash and the 8-character prefix shown in the key list, so it cannot be shown again. | |
| 65 | + | |
| 66 | +You can create several keys (one per environment is a good habit), **rotate** a key (revokes it and issues a new one) or **revoke** it. Revocation is immediate; requests with a revoked key get `401 INVALID_API_KEY`. `last_used_at` in the key list tells you whether a key is still alive before you revoke it. | |
| 67 | + | |
| 68 | +## Sending the key | |
| 69 | + | |
| 70 | +Two ways, in order of preference: | |
| 71 | + | |
| 72 | +```http | |
| 73 | +GET /v1/bars/stock/AAPL?timeframe=1min HTTP/1.1 | |
| 74 | +Host: www.hfmarketdata.io | |
| 75 | +Authorization: Bearer hfmd_live_ab12cd34… | |
| 76 | +``` | |
| 77 | + | |
| 78 | +- **`Authorization: Bearer <key>`** — the header keeps the key out of URLs, proxy logs, browser history and `Referer` headers. Use this everywhere you can. | |
| 79 | +- **`?api_key=<key>`** — for tools that cannot set headers (a spreadsheet `IMPORTDATA`, a quick `pandas.read_csv(url)`). Treat any URL that contains a key as a secret. | |
| 80 | + | |
| 81 | +The snippets on the right show both the header and a call to `GET /v1/limits`, which answers the two questions everybody asks first: *which tier am I on?* and *how much is left in the current window?* | |
| 82 | + | |
| 83 | +<Callout type="warning" title="Never put a key in client-side code or public examples"> | |
| 84 | +A key in a public repository, a shared notebook or a browser bundle is a key you no longer control. Our own documentation only ever shows `$HFMD_API_KEY`. If a key leaks, rotate it from the dashboard — the old value is dead the moment you click. | |
| 85 | +</Callout> | |
| 86 | + | |
| 87 | +## What a key changes — and what it does not | |
| 88 | + | |
| 89 | +A key **raises limits**. It does not unlock hidden data: the same datasets, endpoints and history are available keyless. The one exception is the account surface itself — `/v1/me/*` (your profile, keys, usage) — which naturally requires a key or a signed-in session, and `/v1/admin/*`, which requires the admin role. | |
| 90 | + | |
| 91 | +Every response, keyed or not, carries the limit headers, so you can see the effect immediately: | |
| 92 | + | |
| 93 | +```http | |
| 94 | +X-RateLimit-Limit-Requests: 120 | |
| 95 | +X-RateLimit-Remaining-Requests: 119 | |
| 96 | +X-RateLimit-Limit-Rows: 1000000 | |
| 97 | +X-RateLimit-Remaining-Rows: 999995 | |
| 98 | +X-RateLimit-Reset: 1757030460 | |
| 99 | +X-Row-Count: 5 | |
| 100 | +``` | |
| 101 | + | |
| 102 | +## High usage | |
| 103 | + | |
| 104 | +If you are backfilling a research database, running many workers, or serving an application, ask for the high-usage tier: **600 requests and 10 000 000 rows per minute, 200 000 rows per request**. Send a short e-mail to [contact@spboucher.ai](mailto:contact@spboucher.ai?subject=High%20usage%20tier%20request) from the address of your account — what you are building, roughly how much you need — and the higher limits are switched on your existing keys; nothing to change on your side. Like everything else here, it is free — you only have to ask. | |
| 105 | + | |
| 106 | +## Errors you may see | |
| 107 | + | |
| 108 | +| Status | `error.code` | Meaning | | |
| 109 | +| --- | --- | --- | | |
| 110 | +| 401 | `INVALID_API_KEY` | Malformed, unknown or revoked key. Keyless requests would still work — check the header value. | | |
| 111 | +| 401 | `AUTH_REQUIRED` | Account endpoint called without a key or session. | | |
| 112 | +| 403 | `FORBIDDEN` | Your role or tier does not allow this action (admin endpoints, some features). | | |
| 113 | +| 429 | `RATE_LIMIT_EXCEEDED` | Quota exhausted — `Retry-After` tells you how long to wait. | | |
| 114 | + | |
| 115 | +The full list with remediation lives on the [Errors](/docs/errors) page. | |
| 116 | + | |
| 117 | +## Sessions in the browser | |
| 118 | + | |
| 119 | +The website (dashboard, authenticated playground) uses a cookie session created by `POST /v1/auth/login`, not the API key. Inside the dashboard, the playground runs with your account's quota so you can test at free-tier speed without pasting a key. You never need this flow for programmatic access — use the key. | |
added
hfmarketdata/web/content/guides/bulk-downloads.mdx
+87 −0
@@ -0,0 +1,87 @@ | ||
| 1 | +export const meta = { title: 'Bulk downloads', description: 'Whole-universe Parquet extracts outside the rows quota, and the right way to pull large histories from the regular endpoints.' } | |
| 2 | + | |
| 3 | +export const snippets = [ | |
| 4 | + { | |
| 5 | + title: 'One year of fundamentals for every company', | |
| 6 | + curl: `curl -L "https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet" \\ | |
| 7 | + -H "Authorization: Bearer $HFMD_API_KEY" -o fundamentals_2024.parquet`, | |
| 8 | + python: `import os, requests, pandas as pd, io | |
| 9 | + | |
| 10 | +r = requests.get("https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet", | |
| 11 | + headers={"Authorization": f"Bearer {os.environ['HFMD_API_KEY']}"}, timeout=600) | |
| 12 | +r.raise_for_status() | |
| 13 | +df = pd.read_parquet(io.BytesIO(r.content)) | |
| 14 | +print(df.shape, r.headers.get("X-Row-Count"), "rows — counted as 0 against the rows quota")`, | |
| 15 | + javascript: `import { writeFile } from "node:fs/promises"; | |
| 16 | +const res = await fetch("https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet", { | |
| 17 | + headers: { Authorization: \`Bearer \${process.env.HFMD_API_KEY}\` }, | |
| 18 | +}); | |
| 19 | +await writeFile("fundamentals_2024.parquet", Buffer.from(await res.arrayBuffer()));`, | |
| 20 | + r: `library(httr2); library(arrow) | |
| 21 | +resp <- request("https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet") |> | |
| 22 | + req_auth_bearer_token(Sys.getenv("HFMD_API_KEY")) |> | |
| 23 | + req_perform(path = "fundamentals_2024.parquet") | |
| 24 | +df <- read_parquet("fundamentals_2024.parquet")`, | |
| 25 | + }, | |
| 26 | + { | |
| 27 | + title: 'DuckDB straight from the URL', | |
| 28 | + curl: `duckdb -c "INSTALL httpfs; LOAD httpfs; | |
| 29 | +SELECT ticker, count(*) FROM read_parquet('https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet') | |
| 30 | +GROUP BY 1 ORDER BY 2 DESC LIMIT 10;"`, | |
| 31 | + python: `import duckdb | |
| 32 | +con = duckdb.connect() | |
| 33 | +con.execute("INSTALL httpfs; LOAD httpfs;") | |
| 34 | +con.execute("CREATE SECRET (TYPE HTTP, BEARER_TOKEN getenv('HFMD_API_KEY'));") | |
| 35 | +print(con.sql(""" | |
| 36 | + SELECT ticker, count(*) AS facts | |
| 37 | + FROM read_parquet('https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet') | |
| 38 | + GROUP BY 1 ORDER BY 2 DESC LIMIT 10 | |
| 39 | +""").df())`, | |
| 40 | + javascript: `// duckdb-wasm / @duckdb/node-api | |
| 41 | +const db = await DuckDBInstance.create(); | |
| 42 | +const con = await db.connect(); | |
| 43 | +await con.run("INSTALL httpfs; LOAD httpfs;"); | |
| 44 | +const rows = await con.runAndReadAll("SELECT count(*) FROM read_parquet('https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet')"); | |
| 45 | +console.log(rows.getRows());`, | |
| 46 | + r: `library(duckdb) | |
| 47 | +con <- dbConnect(duckdb()) | |
| 48 | +dbExecute(con, "INSTALL httpfs; LOAD httpfs;") | |
| 49 | +dbGetQuery(con, "SELECT count(*) FROM read_parquet('https://www.hfmarketdata.io/v1/bulk/fundamentals/2024.parquet')")`, | |
| 50 | + }, | |
| 51 | +] | |
| 52 | + | |
| 53 | +# Bulk downloads | |
| 54 | + | |
| 55 | +Some questions are about *everything*: every company's balance sheet in 2024, every contract of a futures root. Answering them one request at a time is slow for you and wasteful for the API. Bulk endpoints hand you the whole thing as a single Parquet file — and **they do not count against your rows quota at all** (one request, zero rows). | |
| 56 | + | |
| 57 | +## What is available | |
| 58 | + | |
| 59 | +| Endpoint | Content | Notes | | |
| 60 | +| --- | --- | --- | | |
| 61 | +| <Endpoint method="GET" path="/v1/bulk/fundamentals/{year}.parquet" /> | Every SEC EDGAR fact filed in `{year}` for every covered company: concept, value, unit, fiscal period, period end, filing date, accession number | Point-in-time friendly: the filing date is in every row. Since 2010. | | |
| 62 | + | |
| 63 | +The bulk family grows with the platform; the [reference](/docs/reference/tag/bulk) is always the authoritative list and the [changelog](/docs/changelog) announces additions. A bulk endpoint requires an API key (a free account is enough) because a single call can move hundreds of megabytes. | |
| 64 | + | |
| 65 | +## Why Parquet | |
| 66 | + | |
| 67 | +Bulk files are Parquet with zstd compression: columnar, typed, splittable. Every serious data tool reads it natively — pandas / pyarrow, Polars, DuckDB, Spark, Arrow in R, duckdb-wasm in the browser. Two habits make it painless: | |
| 68 | + | |
| 69 | +- **Query, don't load.** DuckDB can read a Parquet file *over HTTP* and only fetch the row groups and columns your query touches (second snippet). `SELECT ticker, value FROM read_parquet(url) WHERE concept = 'Assets'` downloads a fraction of the file. | |
| 70 | +- **Cache locally.** A yearly file changes only when restatements arrive. Store it, and revalidate with `If-None-Match` — a `304 Not Modified` is free. | |
| 71 | + | |
| 72 | +## Large histories from the regular endpoints | |
| 73 | + | |
| 74 | +Not everything has a bulk file, and often you want *one* instrument's whole history. The regular endpoints handle that well if you use them right: | |
| 75 | + | |
| 76 | +1. **v1 bars with `format=csv`** accept up to **2 000 000 rows** in one request — a full 1-minute history of a liquid stock (about 200 000 bars per year including extended hours) is roughly 10 years per call. One request, streamed. This is the cheapest path for price history. | |
| 77 | +2. **v2 endpoints with `format=parquet`** count **half** the rows and respect your tier's per-request cap (5 000 keyless, 50 000 free, 200 000 high usage). Page with `from`/`to` windows or with `cursor` from `meta.next_cursor`. | |
| 78 | +3. **Parallelise gently.** The requests window is per minute for keyed access: 120 requests spread over a minute is fine; 120 fired in one second also works but then you wait 59 seconds. Two to four concurrent workers is the sweet spot. | |
| 79 | +4. **Split by natural boundaries** — one call per year, per contract, per quarter — so that partial failures are easy to resume and the files you cache have obvious names. | |
| 80 | + | |
| 81 | +<Callout type="tip" title="Rule of thumb"> | |
| 82 | +Whole universe → bulk. One instrument, long history → v1 CSV (bars) or v2 Parquet windows. Many instruments, short window → the multi-ticker endpoints (`/v1/bars/{asset}?tickers=…`, up to 50 per call). | |
| 83 | +</Callout> | |
| 84 | + | |
| 85 | +## Integrity | |
| 86 | + | |
| 87 | +Every bulk response carries an `ETag` (a hash of the file) and `Content-Length`; verify the length after download, and store the ETag with the file so a later `If-None-Match` can confirm nothing changed. Bulk files are regenerated when the underlying data is updated (new filings, restatements); the ETag changes and the [status page](/status) shows the last update time of each dataset. | |
added
hfmarketdata/web/content/guides/data-formats.mdx
+118 −0
@@ -0,0 +1,118 @@ | ||
| 1 | +export const meta = { title: 'Data formats', description: 'JSON, CSV and Parquet responses compared on real requests — sizes, speed, quota cost, and how to load each in pandas, JavaScript and R.' } | |
| 2 | + | |
| 3 | +export const snippets = [ | |
| 4 | + { | |
| 5 | + title: 'Same request, three formats', | |
| 6 | + curl: `BASE="https://www.hfmarketdata.io/v1/futures/ES/continuous?roll=volume&adjust=back_adjusted&from=2024-01-01&to=2024-12-31" | |
| 7 | +curl "$BASE&format=json" -o es.json | |
| 8 | +curl "$BASE&format=csv" -o es.csv | |
| 9 | +curl "$BASE&format=parquet" -o es.parquet # rows count half against the quota`, | |
| 10 | + python: `import io, requests, pandas as pd | |
| 11 | + | |
| 12 | +base = "https://www.hfmarketdata.io/v1/futures/ES/continuous" | |
| 13 | +params = {"roll": "volume", "adjust": "back_adjusted", "from": "2024-01-01", "to": "2024-12-31"} | |
| 14 | + | |
| 15 | +js = requests.get(base, params={**params, "format": "json"}).json() | |
| 16 | +df_json = pd.DataFrame(js["data"]) # + js["meta"]["roll_dates"] | |
| 17 | + | |
| 18 | +df_csv = pd.read_csv(requests.get(base, params={**params, "format": "csv"}, stream=True).raw, parse_dates=["datetime"]) | |
| 19 | + | |
| 20 | +raw = requests.get(base, params={**params, "format": "parquet"}).content | |
| 21 | +df_pq = pd.read_parquet(io.BytesIO(raw)) # typed columns, UTC timestamps preserved | |
| 22 | +print(len(df_json), len(df_csv), len(df_pq))`, | |
| 23 | + javascript: `const base = "https://www.hfmarketdata.io/v1/futures/ES/continuous?roll=volume&adjust=back_adjusted&from=2024-01-01&to=2024-12-31"; | |
| 24 | + | |
| 25 | +const { data, meta } = await (await fetch(base + "&format=json")).json(); | |
| 26 | +console.log(data.length, meta.roll_dates); | |
| 27 | + | |
| 28 | +const csv = await (await fetch(base + "&format=csv")).text(); | |
| 29 | +const rows = csv.trim().split("\\n").slice(1).map((l) => l.split(",")); | |
| 30 | + | |
| 31 | +// Parquet in the browser/Node: parquet-wasm or duckdb-wasm | |
| 32 | +const bytes = new Uint8Array(await (await fetch(base + "&format=parquet")).arrayBuffer());`, | |
| 33 | + r: `library(httr2); library(arrow) | |
| 34 | +base <- "https://www.hfmarketdata.io/v1/futures/ES/continuous" | |
| 35 | +q <- list(roll = "volume", adjust = "back_adjusted", from = "2024-01-01", to = "2024-12-31") | |
| 36 | + | |
| 37 | +js <- request(base) |> req_url_query(!!!q, format = "json") |> req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 38 | +csv <- request(base) |> req_url_query(!!!q, format = "csv") |> req_perform() |> resp_body_string() |> (\\(s) read.csv(text = s))() | |
| 39 | +pq <- request(base) |> req_url_query(!!!q, format = "parquet") |> req_perform() |> resp_body_raw() |> read_parquet() | |
| 40 | +nrow(js$data); nrow(csv); nrow(pq)`, | |
| 41 | + }, | |
| 42 | +] | |
| 43 | + | |
| 44 | +# Data formats | |
| 45 | + | |
| 46 | +Tabular endpoints return **JSON** by default and accept `format=csv`; v2 endpoints (futures contracts, fundamentals, bulk) also accept `format=parquet`. Same rows, same column names — different bytes, different quota cost, different ergonomics. | |
| 47 | + | |
| 48 | +## Benchmark on real requests | |
| 49 | + | |
| 50 | +Measured on 2026-09-04 against production, from a laptop (single request each, sizes are exact bytes of the response body; gzip is the size the body would have over a compressed connection). Parquet was produced from the identical CSV rows with pandas/pyarrow, zstd codec. | |
| 51 | + | |
| 52 | +| Request | Rows | JSON | JSON gz | CSV | CSV gz | Parquet (zstd) | | |
| 53 | +| --- | --- | --- | --- | --- | --- | --- | | |
| 54 | +| AAPL 1-minute, one week (`start=2024-06-03&end=2024-06-08`) | 3 621 | 474 142 B | 61 169 B | 246 039 B | 50 428 B | 69 370 B | | |
| 55 | +| AAPL daily, full history (`timeframe=1day`) | 6 688 | 812 954 B | 155 894 B | 391 630 B | 132 535 B | 233 456 B | | |
| 56 | +| AAPL option chain, one day (`trade_date=2024-06-21`) | 2 126 | 580 480 B | 74 404 B | 227 355 B | 60 618 B | 89 162 B | | |
| 57 | + | |
| 58 | +Reading the table: | |
| 59 | + | |
| 60 | +- **CSV is about half the size of JSON** before compression (no repeated keys per row) and slightly smaller after — and it was the faster of the two to serve in every run (roughly 30–45 % lower latency on these calls, dominated by serialisation). | |
| 61 | +- **Parquet is competitive with gzipped CSV in bytes** and wins on everything else: column types are preserved (timestamps are real timestamps, not strings), it loads 5–10× faster than parsing text, and **it counts half against your rows quota**. When the client can read it, it is the best choice for anything beyond a few thousand rows. | |
| 62 | +- Compressed sizes converge because price series compress well whatever the container. The gap that matters is the *uncompressed* one when your HTTP client does not negotiate compression. | |
| 63 | + | |
| 64 | +## JSON | |
| 65 | + | |
| 66 | +```json | |
| 67 | +{ | |
| 68 | + "count": 2, | |
| 69 | + "data": [ | |
| 70 | + { "ticker": "AAPL", "datetime": "2024-06-03", "open": 191.2434, "high": 193.3154, "low": 190.8666, "close": 192.3637, "volume": 50080500 }, | |
| 71 | + { "ticker": "AAPL", "datetime": "2024-06-04", "open": 192.9684, "high": 193.6426, "low": 191.3722, "close": 192.6809, "volume": 47471400 } | |
| 72 | + ] | |
| 73 | +} | |
| 74 | +``` | |
| 75 | + | |
| 76 | +- **v1 bars & options endpoints**: `{"count", "data"}` — unchanged since 1.0 and guaranteed by the [versioning policy](/docs/versioning). | |
| 77 | +- **v2 endpoints**: `{"data", "meta"}` where `meta` carries what you would otherwise have to infer — `symbol`, `interval`, `count`, `next_cursor` for pagination, `roll_dates` on continuous series, `as_of` on point-in-time queries, `coverage` notes when something is null. | |
| 78 | +- Numbers are JSON numbers (never quoted); missing values are `null`, never `0` or `NaN`. Timestamps are strings — see [time zones](/docs/time-zones) for their meaning per endpoint. | |
| 79 | + | |
| 80 | +Best for: small responses, browsers, anything where you want `meta` without a second call. | |
| 81 | + | |
| 82 | +## CSV | |
| 83 | + | |
| 84 | +```csv | |
| 85 | +ticker,datetime,open,high,low,close,volume | |
| 86 | +AAPL,2024-06-03,191.3528,193.7175,191.0053,193.0455,50080539 | |
| 87 | +AAPL,2024-06-04,193.0157,193.6976,191.4025,193.3638,47471445 | |
| 88 | +``` | |
| 89 | + | |
| 90 | +- Header row, comma separator, `\n` line endings, UTF-8, RFC 4180 quoting where needed. Empty field = null. | |
| 91 | +- On the v1 bars endpoints `format=csv` lifts the per-request ceiling to **2 000 000 rows** — the way to pull a full 1-minute history in one call. | |
| 92 | +- The `meta` information is not in the body; on v2 endpoints it moves to headers (`X-Row-Count`, and `X-Next-Cursor` when paginated). | |
| 93 | + | |
| 94 | +Best for: `pandas.read_csv(url)`, spreadsheets, shell pipelines, any language. | |
| 95 | + | |
| 96 | +## Parquet | |
| 97 | + | |
| 98 | +- Available on v2 tabular endpoints (`/v1/futures/*`, `/v1/fundamentals/*`) and it *is* the format of the [bulk downloads](/docs/bulk-downloads). | |
| 99 | +- Columnar, typed, zstd-compressed. Timestamps are `TIMESTAMP(UTC)`, prices `DOUBLE`, volumes `BIGINT`, so nothing is lost or re-parsed. | |
| 100 | +- **Quota incentive: rows count ½.** A 50 000-row Parquet response is counted as 25 000 rows. | |
| 101 | +- Readers: pandas / pyarrow, Polars, DuckDB (`SELECT * FROM read_parquet('es.parquet')` — or straight from the URL with the `httpfs` extension), Arrow in R, Spark, parquet-wasm and duckdb-wasm in JavaScript. | |
| 102 | + | |
| 103 | +Best for: research pipelines, anything you will store, anything above a few thousand rows. | |
| 104 | + | |
| 105 | +## Choosing | |
| 106 | + | |
| 107 | +| Situation | Use | | |
| 108 | +| --- | --- | | |
| 109 | +| Exploring in a browser, playground, quick check | JSON | | |
| 110 | +| A DataFrame from a URL in one line | CSV | | |
| 111 | +| More than 50 000 rows from a v1 bars endpoint | CSV (up to 2 M rows) | | |
| 112 | +| Any v2 endpoint you will store or reload | Parquet | | |
| 113 | +| The whole universe for a year | [Bulk](/docs/bulk-downloads) (Parquet, outside the rows quota) | | |
| 114 | +| Re-reading something you already fetched | Anything + `If-None-Match` → 304 is free | | |
| 115 | + | |
| 116 | +## Content negotiation | |
| 117 | + | |
| 118 | +`format=` in the query string is the only switch; the `Accept` header is not used for selection (so links stay shareable). The response `Content-Type` is `application/json`, `text/csv; charset=utf-8` or `application/vnd.apache.parquet`, and CSV/Parquet responses carry a `Content-Disposition: attachment; filename="…"` so a browser download gets a sensible name. | |
added
hfmarketdata/web/content/guides/fundamentals-balance-sheet.mdx
+100 −0
@@ -0,0 +1,100 @@ | ||
| 1 | +export const meta = { title: 'Reading a balance sheet through the API', description: 'Apple\'s Q2 fiscal 2024 balance sheet (period ending 2024-03-30), line by line, as XBRL concepts you can pull from /v1/fundamentals.' } | |
| 2 | + | |
| 3 | +export const snippets = [ | |
| 4 | + { | |
| 5 | + title: 'The whole statement in one call', | |
| 6 | + curl: `curl "https://www.hfmarketdata.io/v1/fundamentals/AAPL/statements?statement=balance_sheet&period=quarterly&from=2024-01-01&to=2024-06-30"`, | |
| 7 | + python: `import requests, pandas as pd | |
| 8 | + | |
| 9 | +BASE = "https://www.hfmarketdata.io" | |
| 10 | +r = requests.get(f"{BASE}/v1/fundamentals/AAPL/statements", | |
| 11 | + params={"statement": "balance_sheet", "period": "quarterly", "from": "2024-01-01", "to": "2024-06-30"}) | |
| 12 | +r.raise_for_status() | |
| 13 | +bs = pd.DataFrame(r.json()["data"]) | |
| 14 | +q2 = bs[bs["period_end"] == "2024-03-30"] | |
| 15 | +print(q2[["concept", "label", "value"]].to_string(index=False))`, | |
| 16 | + javascript: `const url = "https://www.hfmarketdata.io/v1/fundamentals/AAPL/statements?statement=balance_sheet&period=quarterly&from=2024-01-01&to=2024-06-30"; | |
| 17 | +const { data } = await (await fetch(url)).json(); | |
| 18 | +const q2 = data.filter((f) => f.period_end === "2024-03-30"); | |
| 19 | +console.table(q2.map(({ concept, value }) => ({ concept, usd_millions: value / 1e6 })));`, | |
| 20 | + r: `library(httr2) | |
| 21 | +bs <- request("https://www.hfmarketdata.io/v1/fundamentals/AAPL/statements") |> | |
| 22 | + req_url_query(statement = "balance_sheet", period = "quarterly", from = "2024-01-01", to = "2024-06-30") |> | |
| 23 | + req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 24 | +subset(bs$data, period_end == "2024-03-30", c(concept, value))`, | |
| 25 | + }, | |
| 26 | + { | |
| 27 | + title: 'One concept through time', | |
| 28 | + curl: `curl "https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/Assets?from=2023-01-01"`, | |
| 29 | + python: `assets = pd.DataFrame(requests.get(f"{BASE}/v1/fundamentals/AAPL/facts/Assets", params={"from": "2023-01-01"}).json()["data"]) | |
| 30 | +print(assets[["period_end", "filed", "value"]])`, | |
| 31 | + javascript: `const { data } = await (await fetch("https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/Assets?from=2023-01-01")).json(); | |
| 32 | +console.table(data.map(({ period_end, filed, value }) => ({ period_end, filed, value })));`, | |
| 33 | + r: `assets <- request("https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/Assets") |> | |
| 34 | + req_url_query(from = "2023-01-01") |> req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 35 | +assets$data[, c("period_end", "filed", "value")]`, | |
| 36 | + }, | |
| 37 | +] | |
| 38 | + | |
| 39 | +# Reading a balance sheet through the API | |
| 40 | + | |
| 41 | +The fundamentals endpoints serve what companies file with the SEC — their XBRL-tagged financial statements — **as filed, point-in-time, since 2010**. This section of the docs uses one running example throughout: **Apple's balance sheet at the end of its second fiscal quarter of 2024, 30 March 2024**, reported in the Form 10-Q filed on 3 May 2024. If you can read that one statement through the API, you can read any of them. | |
| 42 | + | |
| 43 | +## The statement, as Apple filed it | |
| 44 | + | |
| 45 | +Figures in millions of US dollars, from the 10-Q for the quarter ended March 30, 2024 (accession `0000320193-24-000069`, CIK `320193`). The right-hand column is the XBRL concept (`us-gaap` taxonomy) under which the value is tagged — that is the name you use in `/facts/{concept}`. | |
| 46 | + | |
| 47 | +| Line | USD millions | Concept | | |
| 48 | +| --- | --- | --- | | |
| 49 | +| **Assets** | | | | |
| 50 | +| Cash and cash equivalents | 32,695 | `CashAndCashEquivalentsAtCarryingValue` | | |
| 51 | +| Marketable securities (current) | 34,455 | `MarketableSecuritiesCurrent` | | |
| 52 | +| Accounts receivable, net | 21,837 | `AccountsReceivableNetCurrent` | | |
| 53 | +| Vendor non-trade receivables | 19,313 | `NontradeReceivablesCurrent` | | |
| 54 | +| Inventories | 6,232 | `InventoryNet` | | |
| 55 | +| Other current assets | 13,884 | `OtherAssetsCurrent` | | |
| 56 | +| **Total current assets** | **128,416** | `AssetsCurrent` | | |
| 57 | +| Marketable securities (non-current) | 91,240 | `MarketableSecuritiesNoncurrent` | | |
| 58 | +| Property, plant and equipment, net | 43,546 | `PropertyPlantAndEquipmentNet` | | |
| 59 | +| Other non-current assets | 74,209 | `OtherAssetsNoncurrent` | | |
| 60 | +| **Total non-current assets** | **208,995** | `AssetsNoncurrent` | | |
| 61 | +| **Total assets** | **337,411** | `Assets` | | |
| 62 | +| **Liabilities** | | | | |
| 63 | +| Accounts payable | 45,753 | `AccountsPayableCurrent` | | |
| 64 | +| Other current liabilities | 55,335 | `OtherLiabilitiesCurrent` | | |
| 65 | +| Deferred revenue | 8,053 | `ContractWithCustomerLiabilityCurrent` | | |
| 66 | +| Commercial paper | 1,997 | `CommercialPaper` | | |
| 67 | +| Term debt (current) | 12,684 | `LongTermDebtCurrent` | | |
| 68 | +| **Total current liabilities** | **123,822** | `LiabilitiesCurrent` | | |
| 69 | +| Term debt (non-current) | 91,831 | `LongTermDebtNoncurrent` | | |
| 70 | +| Other non-current liabilities | 47,564 | `OtherLiabilitiesNoncurrent` | | |
| 71 | +| **Total non-current liabilities** | **139,395** | `LiabilitiesNoncurrent` | | |
| 72 | +| **Total liabilities** | **263,217** | `Liabilities` | | |
| 73 | +| **Shareholders' equity** | | | | |
| 74 | +| Common stock and additional paid-in capital | 78,815 | `CommonStocksIncludingAdditionalPaidInCapital` | | |
| 75 | +| Accumulated deficit | (4,726) | `RetainedEarningsAccumulatedDeficit` | | |
| 76 | +| Accumulated other comprehensive income | 105 | `AccumulatedOtherComprehensiveIncomeLossNetOfTax` | | |
| 77 | +| **Total shareholders' equity** | **74,194** | `StockholdersEquity` | | |
| 78 | +| **Total liabilities and equity** | **337,411** | `LiabilitiesAndStockholdersEquity` | | |
| 79 | + | |
| 80 | +The identity holds: 263,217 + 74,194 = 337,411. Note the *accumulated deficit* — Apple has returned more to shareholders through buybacks than it has retained, so retained earnings are negative while the company is enormously profitable. Reading the concept name (`RetainedEarningsAccumulatedDeficit`) rather than the label avoids the surprise. | |
| 81 | + | |
| 82 | +## Pulling it from the API | |
| 83 | + | |
| 84 | +**The whole statement**: <Endpoint method="GET" path="/v1/fundamentals/{ticker}/statements" /> with `statement=balance_sheet&period=quarterly` and a date window returns every tagged line of the balance sheets whose period end falls in the window — one row per concept per period, with the value in the unit reported by the filer (`USD`, so `32695000000`, not millions), the fiscal period, the period end date, the filing date and the accession number. The first snippet filters the 30 March 2024 column. | |
| 85 | + | |
| 86 | +**One line through time**: <Endpoint method="GET" path="/v1/fundamentals/{ticker}/facts/{concept}" /> — `facts/Assets` returns total assets for every period Apple has reported since 2010, each with its `filed` date. This is the shape you want for charts and models. | |
| 87 | + | |
| 88 | +**What is covered**: <Endpoint method="GET" path="/v1/fundamentals/{ticker}/coverage" /> tells you which statements, periods and concepts exist for a company, and where the API knows something is missing (a concept the filer never tagged, a period filed under a custom extension) — so a `null` is always explainable. | |
| 89 | + | |
| 90 | +## Things that trip people up | |
| 91 | + | |
| 92 | +1. **Units are as filed.** Values are in dollars (`USD`) or shares (`shares`) or pure numbers (`pure` for ratios like EPS in `USD/shares`). Divide by 1e6 yourself for millions. Never assume a scale. | |
| 93 | +2. **Custom extensions.** Apple tags most lines with standard `us-gaap` concepts, but some filers use company-specific extensions (`aapl:…`) for lines that do not fit. `statements` returns them too, marked with their namespace; `facts/{concept}` needs the exact name. | |
| 94 | +3. **The same fact appears in several filings.** The 30 March 2024 balance sheet is reported in the Q2 FY2024 10-Q *and* again as the comparative column of the Q2 FY2025 10-Q a year later. The API keeps both observations with their own `filed` dates — that is what makes [point-in-time](/docs/fundamentals/point-in-time) queries possible. | |
| 95 | +4. **Instant vs duration.** Balance sheet concepts are *instants* (a value at `period_end`); income statement and cash flow concepts are *durations* (`period_start` to `period_end`, and a 10-K's Q4 is often only derivable by subtraction, which the API does for you where the pieces exist and marks as derived). | |
| 96 | +5. **Fiscal calendars.** Apple's Q2 ends in late March, not June. See [fiscal vs calendar](/docs/fundamentals/fiscal-vs-calendar). | |
| 97 | + | |
| 98 | +## From lines to ratios | |
| 99 | + | |
| 100 | +With the lines above you can compute the classic liquidity and leverage ratios by hand — current ratio 128,416 / 123,822 = **1.04**, quick ratio (32,695 + 34,455 + 21,837) / 123,822 = **0.72**, total debt (1,997 + 12,684 + 91,831 = 106,512) over equity = **1.44**, net cash (158,390 of cash and securities minus 106,512 of debt) = **+51,878**. The API computes these and many more for you — <Endpoint method="GET" path="/v1/fundamentals/{ticker}/ratios" /> for each period, `ratios/daily` for market-based ones that move with the price. Every formula is on the [ratios page](/docs/fundamentals/ratios), where the same Apple quarter is worked through. | |
added
hfmarketdata/web/content/guides/fundamentals-fiscal-vs-calendar.mdx
+81 −0
@@ -0,0 +1,81 @@ | ||
| 1 | +export const meta = { title: 'Fiscal vs calendar periods', description: 'Why Apple\'s Q2 ends in March, how fiscal years and quarters are labelled in the API, and how to align companies on a calendar grid without look-ahead.' } | |
| 2 | + | |
| 3 | +export const snippets = [ | |
| 4 | + { | |
| 5 | + title: 'Fiscal labels and period ends side by side', | |
| 6 | + curl: `curl "https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/RevenueFromContractWithCustomerExcludingAssessedTax?from=2023-10-01&to=2024-09-30"`, | |
| 7 | + python: `import requests, pandas as pd | |
| 8 | + | |
| 9 | +BASE = "https://www.hfmarketdata.io" | |
| 10 | +rev = pd.DataFrame(requests.get( | |
| 11 | + f"{BASE}/v1/fundamentals/AAPL/facts/RevenueFromContractWithCustomerExcludingAssessedTax", | |
| 12 | + params={"from": "2023-10-01", "to": "2024-09-30"}).json()["data"]) | |
| 13 | +print(rev[["fiscal_year", "fiscal_period", "period_start", "period_end", "filed", "value"]]) | |
| 14 | +# FY2024 Q2 → period 2023-12-31 … 2024-03-30, filed 2024-05-03`, | |
| 15 | + javascript: `const url = "https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/RevenueFromContractWithCustomerExcludingAssessedTax?from=2023-10-01&to=2024-09-30"; | |
| 16 | +const { data } = await (await fetch(url)).json(); | |
| 17 | +console.table(data.map(({ fiscal_year, fiscal_period, period_end, filed, value }) => ({ fiscal_year, fiscal_period, period_end, filed, value })));`, | |
| 18 | + r: `library(httr2) | |
| 19 | +rev <- request("https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/RevenueFromContractWithCustomerExcludingAssessedTax") |> | |
| 20 | + req_url_query(from = "2023-10-01", to = "2024-09-30") |> req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 21 | +rev$data[, c("fiscal_year", "fiscal_period", "period_end", "filed", "value")]`, | |
| 22 | + }, | |
| 23 | + { | |
| 24 | + title: 'Align on calendar quarters (by period end)', | |
| 25 | + python: `# Assign each fiscal quarter to the calendar quarter in which its period ENDS. | |
| 26 | +rev["period_end"] = pd.to_datetime(rev["period_end"]) | |
| 27 | +rev["calendar_q"] = rev["period_end"].dt.to_period("Q") # 2024-03-30 → 2024Q1 | |
| 28 | +print(rev.groupby("calendar_q")["value"].sum()) | |
| 29 | +# Compare across companies on calendar_q, but only use rows whose 'filed' date is ≤ your as-of date`, | |
| 30 | + curl: `# Alignment is a client-side step — see the Python tab`, | |
| 31 | + javascript: `const calQ = (d) => \`\${d.slice(0, 4)}Q\${Math.ceil(Number(d.slice(5, 7)) / 3)}\`; | |
| 32 | +console.log(data.map((f) => [f.fiscal_year, f.fiscal_period, calQ(f.period_end)])); // ["2024","Q2","2024Q1"]`, | |
| 33 | + r: `rev$data$calendar_q <- paste0(substr(rev$data$period_end, 1, 4), "Q", ceiling(as.integer(substr(rev$data$period_end, 6, 7)) / 3)) | |
| 34 | +rev$data[, c("fiscal_year", "fiscal_period", "period_end", "calendar_q")]`, | |
| 35 | + }, | |
| 36 | +] | |
| 37 | + | |
| 38 | +# Fiscal vs calendar periods | |
| 39 | + | |
| 40 | +Apple's **second fiscal quarter of 2024 ended on 30 March 2024**. Its fiscal year 2024 ended on 28 September 2024. Neither matches the calendar, and Apple is not unusual: Microsoft's year ends in June, Walmart's in January, Nvidia's in late January, Oracle's in May. Every number in the fundamentals endpoints is labelled the way the *company* labels it, because that is how it was filed and how analysts talk about it. This page explains the labels and how to put companies on a common grid. | |
| 41 | + | |
| 42 | +## How a fiscal calendar works | |
| 43 | + | |
| 44 | +A **fiscal year** (FY) is the twelve-month accounting period a company chooses. It is named after the calendar year in which it *ends*: Apple's FY2024 ran from 1 October 2023 to 28 September 2024. Quarters are numbered inside it, so Apple's Q1 FY2024 is October–December 2023 — the holiday quarter, its largest. | |
| 45 | + | |
| 46 | +Some companies, Apple included, use a **52/53-week** year that ends on a fixed weekday (Apple: the last Saturday of September). That is why the period end is 30 March 2024 and not 31 March, and why every sixth year or so a fiscal year has 53 weeks — a fact that matters when you annualise a quarter. | |
| 47 | + | |
| 48 | +| Apple label | Period | Filed (10-Q / 10-K) | | |
| 49 | +| --- | --- | --- | | |
| 50 | +| Q1 FY2024 | 2023-10-01 → 2023-12-30 | 2024-02-02 | | |
| 51 | +| **Q2 FY2024** | **2023-12-31 → 2024-03-30** | **2024-05-03** | | |
| 52 | +| Q3 FY2024 | 2024-03-31 → 2024-06-29 | 2024-08-02 | | |
| 53 | +| Q4 FY2024 (in the 10-K) | 2024-06-30 → 2024-09-28 | 2024-11-01 | | |
| 54 | + | |
| 55 | +## How the API labels periods | |
| 56 | + | |
| 57 | +Every fact carries four period fields: | |
| 58 | + | |
| 59 | +- **`fiscal_year`** and **`fiscal_period`** — as declared in the filing (`2024`, `Q2`; `FY` for annual figures). These are the labels you see in press releases. | |
| 60 | +- **`period_start`** / **`period_end`** — the actual calendar dates covered (an *instant* concept such as `Assets` only has `period_end`). | |
| 61 | +- **`filed`** — when the SEC accepted the document. This is the date the information became public, and the only date that matters for [point-in-time](/docs/fundamentals/point-in-time) work. | |
| 62 | + | |
| 63 | +Query parameters `from` / `to` on `/facts` and `/statements` filter on **`period_end`**; `as_of` (where available) filters on **`filed`**. Keep the distinction in mind: “Apple's revenue for the quarter ending in March 2024” (`period_end`) versus “what was known about Apple's revenue on 15 April 2024” (`filed`). | |
| 64 | + | |
| 65 | +## Putting companies on one grid | |
| 66 | + | |
| 67 | +There is no perfect way to compare a March-ending quarter with a December-ending one; there are two honest ones. | |
| 68 | + | |
| 69 | +**By period end (most common).** Assign each fiscal quarter to the calendar quarter in which its period *ends*. Apple's Q2 FY2024 (ending 30 March) becomes calendar 2024Q1; Microsoft's Q3 FY2024 (ending 31 March) also 2024Q1. Companies whose quarter ends mid-quarter (Nvidia, late January → 2024Q1 for Q4 FY2024) land where most of their quarter's activity is. The second snippet does this with `to_period("Q")`. | |
| 70 | + | |
| 71 | +**By filing date (for backtests).** If a signal is computed on day *D*, only facts with `filed ≤ D` may be used, whatever their fiscal label. A calendar-2024Q1 cross-section built on 15 April 2024 contains Apple's *Q1* FY2024 (filed 2 February), not its Q2 (filed 3 May). This is not a nuisance to be engineered away — it is the truth of what an investor knew. | |
| 72 | + | |
| 73 | +<Callout type="warning" title="The classic mistake"> | |
| 74 | +Joining fundamentals to prices on `period_end` gives every backtest a five-to-six-week look into the future — a company's quarter ends on 30 March but the numbers are only public on 3 May. Join on `filed` (or use `as_of`), always. | |
| 75 | +</Callout> | |
| 76 | + | |
| 77 | +## Trailing twelve months | |
| 78 | + | |
| 79 | +Because fiscal years are staggered, the cleanest cross-company measure of “the last year” is the **trailing twelve months** (TTM): the sum of the last four reported quarters of a *duration* concept (revenue, net income, cash flow), or the latest value of an *instant* concept (assets, equity). Apple's TTM net income as of the Q2 FY2024 filing is Q3 FY2023 + Q4 FY2023 + Q1 FY2024 + Q2 FY2024. The [ratios](/docs/fundamentals/ratios) endpoint uses TTM for every flow-over-stock ratio (ROE, margins on daily ratios, P/E), and the ratio row tells you the four periods it summed. | |
| 80 | + | |
| 81 | +One wrinkle the API handles for you: annual filers report the **fourth quarter only implicitly** — the 10-K contains the full year, and Q4 = FY − (Q1 + Q2 + Q3). Where the three quarters exist, the API derives Q4 and flags it as derived; where they do not (a company that just went public), the value is `null` with the reason in `coverage`, never a guess. | |
added
hfmarketdata/web/content/guides/fundamentals-point-in-time.mdx
+92 −0
@@ -0,0 +1,92 @@ | ||
| 1 | +export const meta = { title: 'Point-in-time & backtests without look-ahead bias', description: 'How the fundamentals data preserves every observation with its filing date, what as_of does, and how to build a survivorship- and look-ahead-free backtest.' } | |
| 2 | + | |
| 3 | +export const snippets = [ | |
| 4 | + { | |
| 5 | + title: 'What was known on 15 April 2024?', | |
| 6 | + curl: `# as_of filters on the FILING date: Q2 FY2024 (filed 2024-05-03) is not visible yet | |
| 7 | +curl "https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/Assets?as_of=2024-04-15&from=2023-01-01"`, | |
| 8 | + python: `import requests, pandas as pd | |
| 9 | + | |
| 10 | +BASE = "https://www.hfmarketdata.io" | |
| 11 | +def facts(concept, as_of): | |
| 12 | + r = requests.get(f"{BASE}/v1/fundamentals/AAPL/facts/{concept}", params={"as_of": as_of, "from": "2023-01-01"}) | |
| 13 | + return pd.DataFrame(r.json()["data"])[["period_end", "filed", "value"]] | |
| 14 | + | |
| 15 | +print(facts("Assets", "2024-04-15").tail(2)) # latest visible: 2023-12-30 (filed 2024-02-02) | |
| 16 | +print(facts("Assets", "2024-05-15").tail(2)) # now 2024-03-30 (filed 2024-05-03) appears`, | |
| 17 | + javascript: `const facts = async (asOf) => (await (await fetch(\`https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/Assets?as_of=\${asOf}&from=2023-01-01\`)).json()).data; | |
| 18 | +console.log((await facts("2024-04-15")).at(-1)); // period_end 2023-12-30 | |
| 19 | +console.log((await facts("2024-05-15")).at(-1)); // period_end 2024-03-30`, | |
| 20 | + r: `library(httr2) | |
| 21 | +facts <- function(as_of) request("https://www.hfmarketdata.io/v1/fundamentals/AAPL/facts/Assets") |> | |
| 22 | + req_url_query(as_of = as_of, from = "2023-01-01") |> req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 23 | +tail(facts("2024-04-15")$data[, c("period_end", "filed", "value")], 1) | |
| 24 | +tail(facts("2024-05-15")$data[, c("period_end", "filed", "value")], 1)`, | |
| 25 | + }, | |
| 26 | + { | |
| 27 | + title: 'A universe cross-section on one date', | |
| 28 | + curl: `curl "https://www.hfmarketdata.io/v1/fundamentals/frames/Assets?fiscal_year=2024&fiscal_period=Q2"`, | |
| 29 | + python: `# Every company's Q2 FY2024 total assets, with each row's own filing date → filter filed <= your date | |
| 30 | +frame = pd.DataFrame(requests.get(f"{BASE}/v1/fundamentals/frames/Assets", | |
| 31 | + params={"fiscal_year": 2024, "fiscal_period": "Q2"}).json()["data"]) | |
| 32 | +known_by_june = frame[frame["filed"] <= "2024-06-30"] | |
| 33 | +print(len(frame), "filers;", len(known_by_june), "had filed by 2024-06-30")`, | |
| 34 | + javascript: `const { data } = await (await fetch("https://www.hfmarketdata.io/v1/fundamentals/frames/Assets?fiscal_year=2024&fiscal_period=Q2")).json(); | |
| 35 | +console.log(data.length, "filers;", data.filter((f) => f.filed <= "2024-06-30").length, "filed by 2024-06-30");`, | |
| 36 | + r: `frame <- request("https://www.hfmarketdata.io/v1/fundamentals/frames/Assets") |> | |
| 37 | + req_url_query(fiscal_year = 2024, fiscal_period = "Q2") |> req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 38 | +sum(frame$data$filed <= "2024-06-30")`, | |
| 39 | + }, | |
| 40 | +] | |
| 41 | + | |
| 42 | +# Point-in-time & backtests without look-ahead bias | |
| 43 | + | |
| 44 | +A fundamentals dataset can be *right* and still ruin a backtest. The trap is not wrong numbers; it is **numbers that were not yet public** on the day your model pretends to use them. This page explains how the API stores filings so that this cannot happen by accident, and the discipline you still need on your side. | |
| 45 | + | |
| 46 | +## Two dates for every number | |
| 47 | + | |
| 48 | +Apple's total assets at 30 March 2024 were $337,411 million. That sentence contains one date, and it is the wrong one for a backtest. The number **became public on 3 May 2024**, when the 10-Q was accepted by the SEC. Between 30 March and 3 May, an investor knew Apple's *December* balance sheet, not March's. | |
| 49 | + | |
| 50 | +Every fact in the API therefore carries both: | |
| 51 | + | |
| 52 | +- `period_end` — what period the number describes (30 March 2024). | |
| 53 | +- `filed` — when it became public (3 May 2024), plus the acceptance timestamp and the accession number of the document. | |
| 54 | + | |
| 55 | +And every fact is stored **once per observation**. When Apple's Q2 FY2025 10-Q (May 2025) repeats the 30 March 2024 balance sheet as a comparative column, that is a second observation with `filed = 2025-05-02`. If the value had been restated, both values exist: the original one (what was known in 2024) and the restated one (what is known now). Nothing is overwritten. | |
| 56 | + | |
| 57 | +## `as_of`: the point-in-time switch | |
| 58 | + | |
| 59 | +`as_of=YYYY-MM-DD` on `/facts`, `/statements` and `/ratios` restricts the result to observations with `filed ≤ as_of`, and — where several observations of the same period qualify — returns the **latest filed before `as_of`**. The first snippet shows the effect: asking for Apple's assets `as_of=2024-04-15` returns the December 2023 balance sheet as the most recent; `as_of=2024-05-15` returns March 2024. | |
| 60 | + | |
| 61 | +Without `as_of`, you get the current best view — latest observation of each period, restatements included. That is what you want for a company profile page; it is exactly what you do *not* want for a historical simulation. | |
| 62 | + | |
| 63 | +<Callout type="danger" title="The rule"> | |
| 64 | +In a backtest, at simulation date D, every fundamentals query must carry `as_of=D` (or you must filter `filed ≤ D` yourself on bulk data). Joining on `period_end` alone gives your strategy about five weeks of clairvoyance every quarter — and more after a restatement. | |
| 65 | +</Callout> | |
| 66 | + | |
| 67 | +## Cross-sections: `frames` | |
| 68 | + | |
| 69 | +For universe-wide work you need the same concept for every company at once. <Endpoint method="GET" path="/v1/fundamentals/frames/{concept}" /> returns one row per filer for a fiscal year and period — `frames/Assets?fiscal_year=2024&fiscal_period=Q2` — each with its own `filed` date, because companies report at different times (a March-ending quarter is filed anywhere between late April and mid-May). Filter on `filed ≤ D` client-side, or pass `as_of` and let the API do it. The second snippet counts how many Q2 FY2024 filers were actually visible by 30 June 2024. | |
| 70 | + | |
| 71 | +The [bulk file](/docs/bulk-downloads) `/v1/bulk/fundamentals/{year}.parquet` is the same idea for a whole year of filings: every observation with its `filed` date, ready for a `WHERE filed <= D` in DuckDB. | |
| 72 | + | |
| 73 | +## Survivorship | |
| 74 | + | |
| 75 | +Point-in-time is half the story; the other half is the universe. A backtest that only considers companies that *exist today* has already excluded every bankruptcy and delisting — an optimistic bias of its own. The fundamentals coverage includes companies that stopped filing (they simply have no facts after their last filing), and the bars datasets keep delisted tickers' history. Build your universe on each date D from what was filing *then* (a `frames` call `as_of=D` is a decent proxy), not from today's index constituents. | |
| 76 | + | |
| 77 | +## A minimal correct loop | |
| 78 | + | |
| 79 | +```python | |
| 80 | +for D in rebalance_dates: # e.g. month ends | |
| 81 | + frame = get(f"/v1/fundamentals/frames/NetIncomeLoss", fiscal_year=..., fiscal_period=..., as_of=D) | |
| 82 | + ratios = get("/v1/fundamentals/screener", as_of=D, ...) # or compute from frames | |
| 83 | + prices = get("/v1/snapshot/stock", tickers=universe(D), at=f"{D} 15:59:00", timeframe="1min") | |
| 84 | + weights = rank_and_weight(ratios, prices) # only uses data filed ≤ D | |
| 85 | + pnl.append(hold(weights, D, next_D)) | |
| 86 | +``` | |
| 87 | + | |
| 88 | +Everything on the right-hand side of `weights` was public at D. The price snapshot uses the v1 endpoint with an explicit time (US/Eastern, see [time zones](/docs/time-zones)), so even intraday you cannot accidentally read the close before it happened. | |
| 89 | + | |
| 90 | +## What the API does not do | |
| 91 | + | |
| 92 | +It does not decide *when* the market “knew” a number beyond the SEC acceptance time. Earnings press releases (Form 8-K) often precede the 10-Q by a day and contain the headline figures; the API's `filed` for a 10-Q is the 10-Q's acceptance, and 8-K facts, where tagged, carry their own. If your strategy trades on the earnings release, use the 8-K observation. The daily ratios (`ratios/daily`) follow the same rule — a P/E for 2 May 2024 uses the Q1 FY2024 EPS, and switches to Q2 only from 3 May onwards. | |
added
hfmarketdata/web/content/guides/fundamentals-ratios.mdx
+145 −0
@@ -0,0 +1,145 @@ | ||
| 1 | +export const meta = { title: 'All ratio formulas', description: 'Every ratio computed by the fundamentals endpoints, with its exact formula and the inputs\' XBRL concepts — worked on Apple\'s Q2 fiscal 2024.' } | |
| 2 | + | |
| 3 | +export const snippets = [ | |
| 4 | + { | |
| 5 | + title: 'Ratios per fiscal period', | |
| 6 | + curl: `curl "https://www.hfmarketdata.io/v1/fundamentals/AAPL/ratios?period=quarterly&from=2024-01-01&to=2024-06-30"`, | |
| 7 | + python: `import requests, pandas as pd | |
| 8 | + | |
| 9 | +BASE = "https://www.hfmarketdata.io" | |
| 10 | +r = requests.get(f"{BASE}/v1/fundamentals/AAPL/ratios", params={"period": "quarterly", "from": "2024-01-01", "to": "2024-06-30"}) | |
| 11 | +ratios = pd.DataFrame(r.json()["data"]) | |
| 12 | +print(ratios.set_index("period_end")[["current_ratio", "quick_ratio", "debt_to_equity", "gross_margin", "operating_margin", "net_margin", "roe_ttm"]].T)`, | |
| 13 | + javascript: `const { data } = await (await fetch("https://www.hfmarketdata.io/v1/fundamentals/AAPL/ratios?period=quarterly&from=2024-01-01&to=2024-06-30")).json(); | |
| 14 | +console.table(data.map(({ period_end, current_ratio, net_margin, roe_ttm }) => ({ period_end, current_ratio, net_margin, roe_ttm })));`, | |
| 15 | + r: `library(httr2) | |
| 16 | +ratios <- request("https://www.hfmarketdata.io/v1/fundamentals/AAPL/ratios") |> | |
| 17 | + req_url_query(period = "quarterly", from = "2024-01-01", to = "2024-06-30") |> req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 18 | +ratios$data[, c("period_end", "current_ratio", "quick_ratio", "debt_to_equity", "net_margin")]`, | |
| 19 | + }, | |
| 20 | + { | |
| 21 | + title: 'Market ratios, every trading day', | |
| 22 | + curl: `curl "https://www.hfmarketdata.io/v1/fundamentals/AAPL/ratios/daily?from=2024-04-01&to=2024-05-31"`, | |
| 23 | + python: `daily = pd.DataFrame(requests.get(f"{BASE}/v1/fundamentals/AAPL/ratios/daily", params={"from": "2024-04-01", "to": "2024-05-31"}).json()["data"]) | |
| 24 | +daily["date"] = pd.to_datetime(daily["date"]) | |
| 25 | +print(daily.set_index("date")[["close", "eps_ttm", "pe", "pb", "ps", "ev_ebitda"]].loc["2024-05-01":"2024-05-06"]) | |
| 26 | +# pe switches from the Q1 FY2024 TTM EPS to the Q2 one on 2024-05-03 — the filing date, not the period end`, | |
| 27 | + javascript: `const { data } = await (await fetch("https://www.hfmarketdata.io/v1/fundamentals/AAPL/ratios/daily?from=2024-04-01&to=2024-05-31")).json(); | |
| 28 | +console.table(data.filter((d) => d.date >= "2024-05-01" && d.date <= "2024-05-06").map(({ date, close, pe, pb }) => ({ date, close, pe, pb })));`, | |
| 29 | + r: `daily <- request("https://www.hfmarketdata.io/v1/fundamentals/AAPL/ratios/daily") |> | |
| 30 | + req_url_query(from = "2024-04-01", to = "2024-05-31") |> req_perform() |> resp_body_json(simplifyVector = TRUE) | |
| 31 | +subset(daily$data, date >= "2024-05-01" & date <= "2024-05-06", c(date, close, pe, pb))`, | |
| 32 | + }, | |
| 33 | +] | |
| 34 | + | |
| 35 | +# All ratio formulas | |
| 36 | + | |
| 37 | +Two endpoints compute ratios so you do not have to: <Endpoint method="GET" path="/v1/fundamentals/{ticker}/ratios" /> gives **statement ratios per fiscal period** (liquidity, leverage, profitability, efficiency), and <Endpoint method="GET" path="/v1/fundamentals/{ticker}/ratios/daily" /> gives **market ratios per trading day** (P/E, P/B, EV/EBITDA…), which combine the latest *filed* fundamentals with that day's close. This page is the complete list of definitions. Where a company lacks an input (a bank has no inventory, a pre-revenue biotech has no gross margin), the ratio is `null` and `coverage` says which concept was missing — never a substituted value. | |
| 38 | + | |
| 39 | +Conventions used below: **TTM** = sum of the last four reported quarters for flow items (income statement, cash flow); **avg** = average of the current and the year-ago balance; instant items (balance sheet) are taken at `period_end`. Percentages are returned as decimals (`0.466`, not `46.6`). | |
| 40 | + | |
| 41 | +## The worked example | |
| 42 | + | |
| 43 | +Apple, Q2 FY2024 (period ending 2024-03-30, filed 2024-05-03). Balance sheet lines are on the [balance sheet page](/docs/fundamentals/balance-sheet); the quarter's income statement, in USD millions: net sales **90,753** (products 66,886 + services 23,867), cost of sales 48,482, **gross margin 42,271**, R&D 7,903, SG&A 6,468, **operating income 27,900**, other income 158, pre-tax income 28,058, tax 4,422, **net income 23,636**, diluted EPS **1.53** on 15,406 million diluted shares. TTM net income (Q3 FY2023 19,881 + Q4 FY2023 22,956 + Q1 FY2024 33,916 + Q2 FY2024 23,636) = **100,389**; TTM diluted EPS (1.26 + 1.46 + 2.18 + 1.53) = **6.43**. | |
| 44 | + | |
| 45 | +## Liquidity | |
| 46 | + | |
| 47 | +| Ratio | Formula | Concepts | Apple Q2 FY2024 | | |
| 48 | +| --- | --- | --- | --- | | |
| 49 | +| `current_ratio` | current assets ÷ current liabilities | `AssetsCurrent` / `LiabilitiesCurrent` | 128,416 / 123,822 = **1.037** | | |
| 50 | +| `quick_ratio` | (cash + marketable securities + receivables) ÷ current liabilities | (`CashAndCashEquivalentsAtCarryingValue` + `MarketableSecuritiesCurrent` + `AccountsReceivableNetCurrent`) / `LiabilitiesCurrent` | 88,987 / 123,822 = **0.719** | | |
| 51 | +| `cash_ratio` | (cash + marketable securities) ÷ current liabilities | (`Cash…` + `MarketableSecuritiesCurrent`) / `LiabilitiesCurrent` | 67,150 / 123,822 = **0.542** | | |
| 52 | +| `working_capital` | current assets − current liabilities | `AssetsCurrent` − `LiabilitiesCurrent` | **4,594** | | |
| 53 | + | |
| 54 | +## Leverage & solvency | |
| 55 | + | |
| 56 | +| Ratio | Formula | Concepts | Apple Q2 FY2024 | | |
| 57 | +| --- | --- | --- | --- | | |
| 58 | +| `total_debt` | short-term borrowings + current portion of long-term debt + long-term debt | `CommercialPaper` (+ `ShortTermBorrowings`) + `LongTermDebtCurrent` + `LongTermDebtNoncurrent` | 1,997 + 12,684 + 91,831 = **106,512** | | |
| 59 | +| `net_debt` | total debt − cash − marketable securities (current and non-current) | above − (`Cash…` + `MarketableSecuritiesCurrent` + `MarketableSecuritiesNoncurrent`) | 106,512 − 158,390 = **−51,878** (net cash) | | |
| 60 | +| `debt_to_equity` | total debt ÷ shareholders' equity | / `StockholdersEquity` | 106,512 / 74,194 = **1.436** | | |
| 61 | +| `liabilities_to_equity` | total liabilities ÷ equity | `Liabilities` / `StockholdersEquity` | 263,217 / 74,194 = **3.548** | | |
| 62 | +| `debt_to_assets` | total debt ÷ total assets | / `Assets` | 106,512 / 337,411 = **0.316** | | |
| 63 | +| `equity_ratio` | equity ÷ total assets | `StockholdersEquity` / `Assets` | 74,194 / 337,411 = **0.220** | | |
| 64 | +| `interest_coverage` | EBIT (TTM) ÷ interest expense (TTM) | `OperatingIncomeLoss` / `InterestExpense` | null when interest expense is not separately tagged | | |
| 65 | +| `net_debt_to_ebitda` | net debt ÷ EBITDA (TTM) | see EBITDA below | negative for net-cash companies | | |
| 66 | + | |
| 67 | +## Profitability | |
| 68 | + | |
| 69 | +| Ratio | Formula | Concepts | Apple Q2 FY2024 | | |
| 70 | +| --- | --- | --- | --- | | |
| 71 | +| `gross_margin` | gross profit ÷ revenue | `GrossProfit` / `Revenues` (or `RevenueFromContractWithCustomerExcludingAssessedTax`) | 42,271 / 90,753 = **0.466** | | |
| 72 | +| `operating_margin` | operating income ÷ revenue | `OperatingIncomeLoss` / revenue | 27,900 / 90,753 = **0.307** | | |
| 73 | +| `net_margin` | net income ÷ revenue | `NetIncomeLoss` / revenue | 23,636 / 90,753 = **0.260** | | |
| 74 | +| `ebitda` | operating income + depreciation & amortisation | `OperatingIncomeLoss` + `DepreciationDepletionAndAmortization` | quarterly figure; TTM used in multiples | | |
| 75 | +| `ebitda_margin` | EBITDA ÷ revenue | | | | |
| 76 | +| `effective_tax_rate` | income tax ÷ pre-tax income | `IncomeTaxExpenseBenefit` / `IncomeLossFromContinuingOperationsBeforeIncomeTaxes…` | 4,422 / 28,058 = **0.158** | | |
| 77 | +| `roe_ttm` | net income (TTM) ÷ avg equity | `NetIncomeLoss` TTM / avg `StockholdersEquity` | 100,389 / avg(74,194; 62,158) ≈ **1.47** — Apple's buybacks make equity tiny | | |
| 78 | +| `roa_ttm` | net income (TTM) ÷ avg total assets | / avg `Assets` | 100,389 / avg(337,411; 332,160) ≈ **0.30** | | |
| 79 | +| `roic_ttm` | operating income × (1 − tax rate), TTM ÷ (equity + total debt − cash) | | | | |
| 80 | + | |
| 81 | +## Efficiency | |
| 82 | + | |
| 83 | +| Ratio | Formula | Concepts | | |
| 84 | +| --- | --- | --- | | |
| 85 | +| `asset_turnover` | revenue (TTM) ÷ avg total assets | revenue / avg `Assets` | | |
| 86 | +| `inventory_turnover` | cost of revenue (TTM) ÷ avg inventory | `CostOfRevenue` (or `CostOfGoodsAndServicesSold`) / avg `InventoryNet` | | |
| 87 | +| `days_inventory` | 365 ÷ inventory turnover | | | |
| 88 | +| `receivables_turnover` | revenue (TTM) ÷ avg receivables | / avg `AccountsReceivableNetCurrent` | | |
| 89 | +| `days_sales_outstanding` | 365 ÷ receivables turnover | | | |
| 90 | +| `payables_turnover` | cost of revenue (TTM) ÷ avg payables | / avg `AccountsPayableCurrent` | | |
| 91 | +| `days_payables_outstanding` | 365 ÷ payables turnover | | | |
| 92 | +| `cash_conversion_cycle` | DIO + DSO − DPO | | | |
| 93 | + | |
| 94 | +## Cash flow | |
| 95 | + | |
| 96 | +| Ratio | Formula | Concepts | | |
| 97 | +| --- | --- | --- | | |
| 98 | +| `operating_cash_flow` | as reported (TTM) | `NetCashProvidedByUsedInOperatingActivities` | | |
| 99 | +| `capex` | purchases of PP&E (TTM) | `PaymentsToAcquirePropertyPlantAndEquipment` | | |
| 100 | +| `free_cash_flow` | operating cash flow − capex | | | |
| 101 | +| `fcf_margin` | FCF ÷ revenue | | | |
| 102 | +| `cash_flow_to_debt` | operating cash flow ÷ total debt | | | |
| 103 | +| `dividends_paid` | as reported (TTM) | `PaymentsOfDividends` (or `PaymentsOfDividendsCommonStock`) | | |
| 104 | +| `buybacks` | as reported (TTM) | `PaymentsForRepurchaseOfCommonStock` | | |
| 105 | +| `payout_ratio` | dividends ÷ net income (TTM) | | | |
| 106 | +| `shareholder_yield_inputs` | dividends + buybacks (TTM) | used by the daily `shareholder_yield` | | |
| 107 | + | |
| 108 | +## Per share | |
| 109 | + | |
| 110 | +| Ratio | Formula | Concepts | Apple | | |
| 111 | +| --- | --- | --- | --- | | |
| 112 | +| `eps_diluted` | as reported | `EarningsPerShareDiluted` | 1.53 (Q2 FY2024) | | |
| 113 | +| `eps_ttm` | sum of the last four quarters' diluted EPS | | **6.43** | | |
| 114 | +| `book_value_per_share` | equity ÷ diluted shares | `StockholdersEquity` / `WeightedAverageNumberOfDilutedSharesOutstanding` | 74,194 / 15,406 ≈ **4.82** | | |
| 115 | +| `revenue_per_share_ttm` | revenue (TTM) ÷ diluted shares | | | | |
| 116 | +| `fcf_per_share_ttm` | FCF (TTM) ÷ diluted shares | | | | |
| 117 | + | |
| 118 | +## Market ratios (`ratios/daily`) | |
| 119 | + | |
| 120 | +Computed for every trading day from the **split-adjusted close** of that day (`/v1/bars/stock/{ticker}`, `adj_split`) and the fundamentals **filed on or before that day** — the point-in-time rule, so the P/E on 2 May 2024 uses Q1 FY2024's TTM EPS and the one on 3 May uses Q2's. Shares outstanding come from the cover page of the latest filing (`EntityCommonStockSharesOutstanding`) when available, else diluted weighted shares. | |
| 121 | + | |
| 122 | +| Ratio | Formula | Apple, 3 May 2024 (close ≈ 183.4) | | |
| 123 | +| --- | --- | --- | | |
| 124 | +| `market_cap` | close × shares outstanding | ≈ 183.4 × 15,334 M ≈ **2.81 T** | | |
| 125 | +| `pe` | close ÷ `eps_ttm` | 183.4 / 6.43 ≈ **28.5** | | |
| 126 | +| `pb` | market cap ÷ equity | 2.81 T / 74.2 B ≈ **37.9** | | |
| 127 | +| `ps` | market cap ÷ revenue (TTM) | | | |
| 128 | +| `enterprise_value` | market cap + total debt − cash & securities | ≈ 2.81 T − 51.9 B ≈ **2.76 T** | | |
| 129 | +| `ev_ebitda` | EV ÷ EBITDA (TTM) | | | |
| 130 | +| `ev_sales` | EV ÷ revenue (TTM) | | | |
| 131 | +| `earnings_yield` | 1 ÷ P/E | ≈ **3.5 %** | | |
| 132 | +| `fcf_yield` | FCF (TTM) ÷ market cap | | | |
| 133 | +| `dividend_yield` | dividends per share (TTM) ÷ close | | | |
| 134 | +| `shareholder_yield` | (dividends + buybacks, TTM) ÷ market cap | | | |
| 135 | +| `peg` | P/E ÷ EPS growth (TTM vs prior TTM, %) | | | |
| 136 | + | |
| 137 | +Each daily row also repeats its inputs (`close`, `eps_ttm`, `shares_outstanding`, `fundamentals_as_of` = the filing date used), so every multiple is auditable to the dollar. | |
| 138 | + | |
| 139 | +<Callout type="info" title="Why our P/E may differ from your screener's"> | |
| 140 | +Three usual culprits: (1) GAAP diluted EPS here versus “adjusted” EPS elsewhere; (2) the point-in-time rule — many sites apply new EPS on the *earnings release* day (the 8-K, one day earlier) rather than the 10-Q; (3) shares outstanding from the cover page versus the weighted diluted count. All three inputs are in the row, so you can reconcile. | |
| 141 | +</Callout> | |
| 142 | + | |
| 143 | +## Missing values | |
| 144 | + | |
| 145 | +A ratio is `null` when any input is missing or when the denominator is zero or negative where that makes the ratio meaningless (negative equity → `pb` null, negative TTM earnings → `pe` null, with `earnings_yield` still reported). `coverage` on each period lists the concepts that were not found. Banks and insurers lack `InventoryNet`, `CostOfRevenue` and usually `GrossProfit`; their efficiency ratios are null by design rather than computed from proxies. | |
added
hfmarketdata/web/content/guides/fundamentals-screener.mdx
+75 −0
Large diff (75 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/fundamentals-stream.mdx
+107 −0
Large diff (107 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/futures-contracts.mdx
+176 −0
Large diff (176 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/options.mdx
+94 −0
Large diff (94 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/quickstart.mdx
+114 −0
Large diff (114 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/rate-limits.mdx
+176 −0
Large diff (176 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/recipe-crude-term-structure.mdx
+98 −0
Large diff (98 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/recipe-custom-continuous.mdx
+112 −0
Large diff (112 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/recipe-pandas-backtest.mdx
+106 −0
Large diff (106 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/time-zones.mdx
+105 −0
Large diff (105 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/guides/versioning.mdx
+52 −0
Large diff (52 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/content/samples/aapl-pe.json
+1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
modified
hfmarketdata/web/package-lock.json
+295 −51
Large diff (686 lines) is not loaded by default to keep the page fast.Load diff
modified
hfmarketdata/web/package.json
+9 −3
Large diff (25 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/scripts/bench-formats.mjs
+26 −0
Large diff (26 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/scripts/bench-parquet.py
+18 −0
Large diff (18 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/scripts/fetch-examples.mjs
+141 −0
Large diff (141 lines) is not loaded by default to keep the page fast.Load diff
added
hfmarketdata/web/scripts/make-aapl-pe-sample.mjs
+51 −0
Large diff (51 lines) is not loaded by default to keep the page fast.Load diff
modified
hfmarketdata/web/src/components/components.css
+7 −5
Large diff (31 lines) is not loaded by default to keep the page fast.Load diff
modified
hfmarketdata/web/src/docs/spec.js
+2 −2
Large diff (11 lines) is not loaded by default to keep the page fast.Load diff
modified
hfmarketdata/web/src/pages/docs/docs.css
+2 −2
Large diff (16 lines) is not loaded by default to keep the page fast.Load diff
modified
hfmarketdata/web/vite.config.js
+3 −1
Large diff (20 lines) is not loaded by default to keep the page fast.Load diff