US Market Data API
Free · Fast · Reliable
Closing quotes (T+1), historical candles, static info, batch lookups and backtests for US equities and ETFs. One JSON envelope, callable without signing up.
Base URL: https://us2.ninequantai.com
Every endpoint lives under this address. Paths are case-sensitive; symbols are not.
GET /v1/quote/AAPL.US{
"code": 0,
"message": "ok",
"data": {
"symbol": "AAPL.US",
"name": "苹果",
"market": "US",
"currency": "USD",
"last": 328.21,
"open": 324.87,
"high": 330.81,
"low": 324.11,
"prev_close": 324.96,
"change": 3.25,
"change_pct": 1.0001,
"volume": 37225838,
"ts": "2026-09-03T16:00:00-04:00",
"as_of": "2026-09-03",
"delayed": true,
"venue_mode": "consolidated",
"timeliness": { "mode": "eod_only", "delayed": true, "label": "T+1 收盘数据" },
"source": "eod"
}
}Taken from a real call. delayed and as_of are left untouched, so the timeliness of the data is visible at a glance.
We currently serve daily bars for T+1 and earlier. Intraday and real-time data are not open yet. Data current to 09/04/2026
At a glance
All four numbers were measured against the server when you opened this page — not marketing figures. Reload and you can reproduce them. Anything we could not measure shows as "Milliseconds" or "—": we do not publish numbers we cannot back up.
Why use our API?
Every claim below can be checked elsewhere on this page: the measured numbers, the rate-limit table, the live try-it panels. We do not write slogans that cannot be verified.
Fast responses
Candles and quotes served from our own store come back in milliseconds. The four cards above are what your browser measured just now.
Reliable
When an upstream source fails we return an explicit 502 — never stale or partial data dressed up as fresh.
Sensible rate limits
Sliding-window counters. Every 429 carries Retry-After and X-RateLimit-* headers so clients can back off on their own.
RESTful
Every endpoint shares one code / message / data envelope, so your client parses it in exactly one place.
Broad coverage
Quotes, daily/weekly/monthly candles, static info, symbol search, batch lookups and backtests in a single API.
Genuinely free
Guests can call without signing up; registering raises the limits. The free tier is a tier, not a trial, and we do not quietly cut quotas.
💰 Usage and pricing
Three usage tiers. Every number on these cards is read live from the server-side plan configuration — none of it is hard-coded in the front end.
Guest
- Single endpoint
- 30/min
- Batch lookup
- 10/min
- Backtest
- 5/min
- API keys
- Not available
- Rate-limited by source IP — open the page and start calling
- Quotes, candles, static info, symbol search, batch and backtest all open
- No API key issued
- No call history or usage stats
- No support
Registered
Recommended- Single endpoint
- 60/min
- Batch lookup
- 20/min
- Backtest
- 20/min
- API keys
- 1
- Rate-limited per API key, independent of everyone else
- Daily usage stats in the dashboard
- Backtests can be saved and shared
- Email support
Pro
- Single endpoint
- 300/min
- Batch lookup
- 100/min
- Backtest
- 60/min
- API keys
- 5
- Dedicated quota; issue several keys to spread the load
- Per-endpoint, per-day usage breakdown
- Parameter optimization, portfolio backtests and CSV export
- Dedicated support with priority response
Backtests: 5/min for guests, 20/min once registered. These limits come from GET /api/plans — the same source as the pricing page and the dashboard, so the numbers can never contradict each other. See full pricing, including Enterprise →
| Category | Guest | Registered | Pro | Enterprise |
|---|---|---|---|---|
| Quote | 30/min | 60/min | 300/min | 3000/min |
| Candles | 30/min | 60/min | 300/min | 3000/min |
| Intraday | Not available | 60/min | 300/min | 3000/min |
| Static info | 30/min | 60/min | 300/min | 3000/min |
| Symbol search | 30/min | 60/min | 300/min | 3000/min |
| Batch lookup | 10/min | 20/min | 100/min | 1000/min |
| Backtest | 5/min | 20/min | 60/min | 600/min |
| Optimization | Not available | Not available | 10/hour | 200/hour |
| Account API | Not available | 120/min | 300/min | 600/min |
Each market-data endpoint has its own counter — they do not share one bucket, so calling quotes and candles together does not halve your quota.
Rate-limit headers
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 27
X-RateLimit-Reset: 1788451260
Retry-After: 12Every response carries these, so a client can throttle itself instead of guessing.
Once you are over the limit
X-RateLimit-Limit— Quota for this windowX-RateLimit-Remaining— Calls left in this windowX-RateLimit-Reset— Unix seconds when the quota resetsRetry-After— Seconds to wait; present only on a 429
You get a 429 with retry_after in the body. Back off using Retry-After rather than hammering at a fixed interval — that only keeps you inside the limited window.
🔐 Authentication
Three caller identities. Switching between them means changing one request header.
Guest mode
No sign-upCall with no credentials at all; quota is counted per source IP.
Single-endpoint quota: 30/min
- Good for trying the API, writing a demo or running a one-off script
- Every call from the same egress IP shares one quota — watch out on office networks, cloud functions and CI
- No call history is kept, so nothing shows up in the dashboard
Registered user
RecommendedSend an API key in the request header; quota is counted per key.
Single-endpoint quota: 60/min
- Sign-up is an email code — there is no password — and a key is issued automatically
- Keys start with nq_ followed by 64 hex characters and can be reset from the dashboard at any time
- Daily usage is visible in the dashboard; backtests can be saved and shared
Pro
Higher limitsSame API key mechanism; the quota and the available features follow the Pro tier.
Single-endpoint quota: 300/min
- Issue several keys — quota is per key, so they never compete with each other
- Per-endpoint and per-day usage breakdown
- Parameter optimization, portfolio backtests and CSV export unlock with the tier
Request examples
GET https://us2.ninequantai.com/v1/quote/AAPL.USNo headers at all; quota is counted per source IP.
GET https://us2.ninequantai.com/v1/quote/AAPL.US
Authorization: Bearer nq_your_api_key_here
Accept-Language: zh-CNReplace nq_your_api_key_here with the key from your dashboard. Accept-Language decides which language error messages come back in.
A bad credential is a 401, never a silent downgrade
If you send a key that has expired or been reset you get a 401 rather than being treated as a guest — otherwise you would only notice "the data got thinner" instead of "the key needs replacing". Sending no credential at all is what makes you a guest.
⚠️ Never ship a key in front-end code
Anything running in a browser is public. Keep keys on your server or in local scripts; if one leaks, reset it in the dashboard and the old key dies immediately.
⚠️ Error codes
Only the six you will actually meet — not a full HTTP status table.
| Status | Meaning | What to do |
|---|---|---|
400 | Invalid parameters: date format, period value, too many symbols in a batch, and so on | Fix the parameter the message points at. Messages are localized via Accept-Language and are safe to show to your own users. |
401 | Credential invalid or expired | Check the header reads Bearer nq_xxx; if the key was reset, take the new one from the dashboard. Sending no credential never produces a 401 — you are simply treated as a guest. |
403Added by us | Your identity is not allowed to do this | Two causes: the distribution mode is not open yet (intraday under eod_only), or your plan does not include the feature. Wait for the former, upgrade for the latter. |
404Added by us | Symbol or record does not exist | Confirm the spelling with /v1/symbols. The canonical form is QQQ.US; an omitted market is read as US. |
429 | Rate limit hit | Back off using Retry-After. If you need more headroom permanently, upgrade the plan or add another key — quota is per key. |
502 | Upstream data source temporarily unavailable | We would rather return 502 than dirty data. Retry later, or use a historical range already in our store. |
403 and 404 are ours, on top of the four standard status codes: 403 means either "this distribution mode is not open yet" (the intraday endpoint today) or "your plan does not include this", and 404 means the symbol or record does not exist. Without them a caller cannot tell whether to wait for licensing, upgrade a plan, or just fix a typo.
500 and 503 follow the same envelope. No error response ever leaks a stack trace; if something looks wrong, send us the X-Request-Id.
Response envelope
Every /v1 and /api endpoint returns the same envelope, so a client only parses it once.
{
"code": 0,
"message": "ok",
"data": { }
}{
"code": 429,
"message": "请求过于频繁,请在 12 秒后重试",
"data": null,
"retry_after": 12
}Every response carries an X-Request-Id header. Include it when reporting a problem and we can find that exact call.
📦 SDK examples
Python (requests), JavaScript (fetch) and cURL — copy and run. Swap YOUR_API_KEY for the key in your dashboard; leaving it as-is still works, you just get guest limits.
Base URL: https://us2.ninequantai.com
import requests
BASE = "https://us2.ninequantai.com"
# Drop this line to call as a guest
headers = {"Authorization": "Bearer YOUR_API_KEY"}
r = requests.get(f"{BASE}/v1/quote/QQQ.US", headers=headers, timeout=20)
r.raise_for_status()
body = r.json() # One envelope: code / message / data
if body["code"] != 0: # HTTP 200 can still be a business failure — always check code
raise RuntimeError(body["message"])
print(body["data"])We do not ship an official SDK and do not plan to: seven endpoints behind one envelope take about twenty lines to wrap, and an SDK would only add a version to keep in sync.
API endpoints
Seven endpoints: quote / candles / intraday / static / batch, plus symbol search and backtest. Each one comes with a parameter table, a real request and response example, and a panel that fires an actual request.
/v1/quote/{symbol}Quote
The latest distributable quote for one symbol
Timeliness follows the deployment's distribution mode: under eod_only you get the most recent session that has passed T+1, with as_of naming the date. The timeliness field in the response states which one you actually received.
Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
symbol | path | string | Required | — | Symbol; QQQ and QQQ.US both work, an omitted market is read as US |
Request
# Drop this line to call as a guest
curl -s "https://us2.ninequantai.com/v1/quote/QQQ.US" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"code": 0,
"message": "ok",
"data": {
"symbol": "QQQ.US",
"name": "纳斯达克100",
"market": "US",
"currency": "USD",
"last": 717.67,
"open": 710.85,
"high": 718.915,
"low": 709.69,
"prev_close": 709.24,
"change": 8.43,
"change_pct": 1.1886,
"volume": 29449781,
"ts": "2026-09-03T16:00:00-04:00",
"as_of": "2026-09-03",
"delayed": true,
"venue_mode": "consolidated",
"venue_mode_label": "合并口径(全市场)",
"timeliness": {
"mode": "eod_only",
"delayed": true,
"delay_minutes": null,
"label": "T+1 收盘数据",
"max_date": "2026-09-03"
},
"source": "eod"
}
}Taken from a real call; arrays trimmed for readability.
Possible errors
400Invalid parameters: date format, period value, too many symbols in a batch, and so on404Symbol or record does not exist429Rate limit hit502Upstream data source temporarily unavailable
Try it
Fill in the parameters and fire a real request. It consumes the quota of whatever identity you are using, exactly like a production call.
No request sent yet. Use the button above.
/v1/kline/{symbol}Historical candles
Daily / weekly / monthly candles
Weekly and monthly bars are resampled from daily ones rather than stored separately. adjust=qfq applies forward adjustment: factors are multiplied at query time instead of storing adjusted prices, so the latest price stays a real traded price, and adjust_applied tells you honestly whether any factor actually applied to the range you asked for.
Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
symbol | path | string | Required | — | Symbol |
period | query | string | Optional | day | day / week / month, default day |
start | query | string | Optional | — | Start date, YYYY-MM-DD |
end | query | string | Optional | — | End date, YYYY-MM-DD; clamped to the distributable range |
days | query | integer | Optional | 250 | Return the most recent N bars (counted in period units); start wins when both are given |
adjust | query | string | Optional | none | none (default) or qfq for forward adjustment |
Request
# Drop this line to call as a guest
curl -s "https://us2.ninequantai.com/v1/kline/QQQ.US?period=day&days=30&adjust=none" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"code": 0,
"message": "ok",
"data": {
"symbol": "QQQ.US",
"name": "纳斯达克100",
"market": "US",
"currency": "USD",
"period": "day",
"adjust": "none",
"adjust_applied": false,
"venue_mode": "consolidated",
"venue_mode_label": "合并口径(全市场)",
"delayed": true,
"as_of": "2026-09-03",
"timeliness": { "mode": "eod_only", "delayed": true, "label": "T+1 收盘数据" },
"start": "2026-07-24",
"end": "2026-09-03",
"count": 30,
"bars": [
{ "ts": "2026-07-24", "open": 690.41, "high": 692.63, "low": 682.48, "close": 684.23, "volume": 42876658 },
{ "ts": "2026-07-27", "open": 691.68, "high": 692.3, "low": 675.945, "close": 682.12, "volume": 42681473 },
{ "...": "共 30 根,其余略" },
{ "ts": "2026-09-03", "open": 710.85, "high": 718.915, "low": 709.69, "close": 717.67, "volume": 29449781 }
]
}
}Taken from a real call; arrays trimmed for readability.
Possible errors
400Invalid parameters: date format, period value, too many symbols in a batch, and so on404Symbol or record does not exist429Rate limit hit502Upstream data source temporarily unavailable
Try it
Fill in the parameters and fire a real request. It consumes the quota of whatever identity you are using, exactly like a production call.
No request sent yet. Use the button above.
/v1/intraday/{symbol}Unavailable on this deploymentIntraday
Intraday candles for the session
Redistributing intraday data requires a separate exchange licence. While the distribution mode is eod_only this endpoint returns 403 — your parameters are fine, this deployment simply has not opened intraday distribution. Once licensed we switch the mode to delayed or realtime and neither the path nor the fields change.
The current distribution mode is eod_only, so this endpoint returns 403. Intraday redistribution needs a separate exchange licence — this is not a mistake in your parameters. Once that licence is in place we switch the mode to delayed or realtime; the path and the fields stay exactly the same.
Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
symbol | path | string | Required | — | Symbol |
date | query | string | Optional | — | Session date, YYYY-MM-DD; defaults to the latest distributable session |
interval | query | string | Optional | 1m | Bar size, e.g. 1m |
Request
# Drop this line to call as a guest
curl -s "https://us2.ninequantai.com/v1/intraday/QQQ.US?interval=1m" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"code": 403,
"message": "分时数据暂不对外提供",
"data": null
}Taken from a real call; arrays trimmed for readability.
Possible errors
400Invalid parameters: date format, period value, too many symbols in a batch, and so on403Your identity is not allowed to do this404Symbol or record does not exist429Rate limit hit502Upstream data source temporarily unavailable
Try it
Fill in the parameters and fire a real request. It consumes the quota of whatever identity you are using, exactly like a production call.
No request sent yet. Use the button above.
/v1/static/{symbol}Static info
Static attributes plus the locally covered history range
Beyond name, type and primary venue it returns the daily range already loaded locally (history_start / history_end), so you can check there is enough data before firing a backtest. Delisted symbols are returned too, flagged in note.
Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
symbol | path | string | Required | — | Symbol |
Request
# Drop this line to call as a guest
curl -s "https://us2.ninequantai.com/v1/static/QQQ.US" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"code": 0,
"message": "ok",
"data": {
"symbol": "QQQ.US",
"raw_symbol": "QQQ",
"market": "US",
"name": "纳斯达克100",
"asset_type": "etf",
"primary_venue": "XNAS",
"currency": "USD",
"listed_on": null,
"delisted_on": null,
"is_delisted": false,
"is_popular": true,
"lot_size": 40,
"status": "active",
"history_start": "2024-07-01",
"history_end": "2026-09-03",
"venue_mode": "consolidated",
"note": null
}
}Taken from a real call; arrays trimmed for readability.
Possible errors
400Invalid parameters: date format, period value, too many symbols in a batch, and so on404Symbol or record does not exist429Rate limit hit502Upstream data source temporarily unavailable
Try it
Fill in the parameters and fire a real request. It consumes the quota of whatever identity you are using, exactly like a production call.
No request sent yet. Use the button above.
/v1/batchBatch lookup
Query several symbols in one call
One bad symbol does not fail the whole call: successes land in results and each failure is listed in errors, so you can see which code was wrong instead of retrying the entire batch. The per-call symbol cap comes from your plan — see the quota table above.
Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
symbols | body | string[] | Required | — | Array of symbols, counted after de-duplication |
type | body | string | Optional | quote | quote / static / kline |
period | body | string | Optional | day | Applies when type=kline |
adjust | body | string | Optional | none | Applies when type=kline |
days | body | integer | Optional | — | Bars per symbol when type=kline |
Request
# Drop this line to call as a guest
curl -s -X POST "https://us2.ninequantai.com/v1/batch" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"symbols": [
"QQQ.US",
"SPY.US",
"NOPE.US"
],
"type": "quote"
}'Response
{
"code": 0,
"message": "ok",
"data": {
"type": "quote",
"requested": 3,
"succeeded": 2,
"failed": 1,
"distribution_mode": "eod_only",
"results": {
"QQQ.US": { "symbol": "QQQ.US", "last": 717.67, "change_pct": 1.1886, "as_of": "2026-09-03", "...": "字段与 /v1/quote 相同" },
"SPY.US": { "...": "同上" }
},
"errors": [
{ "symbol": "NOPE.US", "code": 404, "message": "未找到标的 NOPE.US" }
]
}
}Taken from a real call; arrays trimmed for readability.
Possible errors
400Invalid parameters: date format, period value, too many symbols in a batch, and so on429Rate limit hit502Upstream data source temporarily unavailable
Try it
Fill in the parameters and fire a real request. It consumes the quota of whatever identity you are using, exactly like a production call.
No request sent yet. Use the button above.
/v1/symbolsSymbol search
Fuzzy search by code or name
Exact code matches rank first, then code prefixes, then name matches. Omit q to get the popular list, which makes a good initial state for a search box. Delisted symbols are included but ranked below active ones.
Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
q | query | string | Optional | — | Query; matches a code prefix or a fragment of the name |
market | query | string | Optional | US | Market code; only US is supported today |
limit | query | integer | Optional | 20 | Maximum rows, default 20, max 100 |
Request
# Drop this line to call as a guest
curl -s "https://us2.ninequantai.com/v1/symbols?q=QQ&market=US&limit=2" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"code": 0,
"message": "ok",
"data": {
"query": "QQ",
"market": "US",
"total": 30,
"count": 2,
"items": [
{
"symbol": "QQQ.US",
"raw_symbol": "QQQ",
"market": "US",
"name": "纳斯达克100",
"asset_type": "etf",
"primary_venue": "XNAS",
"currency": "USD",
"listed_on": null,
"delisted_on": null,
"is_delisted": false,
"is_popular": true
},
{ "...": "共 2 条,其余略" }
]
}
}Taken from a real call; arrays trimmed for readability.
Possible errors
400Invalid parameters: date format, period value, too many symbols in a batch, and so on429Rate limit hit502Upstream data source temporarily unavailable
Try it
Fill in the parameters and fire a real request. It consumes the quota of whatever identity you are using, exactly like a production call.
No request sent yet. Use the button above.
/v1/backtestRun a backtest
Run one backtest with a built-in strategy
Same engine as the web backtester, so identical parameters on the same version reproduce identical results. Strategy parameters may be nested under params or flattened at the top level (params wins if both appear). Candles and trades come back by default; set include_klines / include_equity to false when you only need the summary metrics and you save most of the bandwidth.
Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
symbol | body | string | Required | — | Symbol |
strategy | body | string | Optional | chandelier | Strategy code — see GET /v1/strategies |
time_range | body | string | Optional | 1Y | Preset range: YTD / 1M / 3M / 6M / 1Y / 2Y / 3Y / 4Y / 5Y / 6Y / 7Y / 8Y / 10Y / MAX |
start | body | string | Optional | — | Start date, YYYY-MM-DD; overrides time_range when given |
end | body | string | Optional | — | End date, YYYY-MM-DD |
params | body | object | Optional | — | Strategy parameter object, validated against params_schema |
execute_on | body | string | Optional | close | Fill timing: close = the signal day's close (default); next_open = the next session's open (closer to live trading) |
cost | body | object | Optional | — | Trading costs {commission_rate, min_commission, slippage_bps}. Omit it and all three are 0 — the default run charges nothing, and the response notes carry no_cost_model to say so |
include_klines | body | boolean | Optional | true | Return the candle array |
include_trades | body | boolean | Optional | true | Return the trade list |
include_equity | body | boolean | Optional | true | Return the equity curve |
Request
# Drop this line to call as a guest
curl -s -X POST "https://us2.ninequantai.com/v1/backtest" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"symbol": "QQQ.US",
"strategy": "chandelier",
"time_range": "1Y",
"params": {
"sma_period": 100
},
"execute_on": "close",
"include_klines": false,
"include_trades": true,
"include_equity": false
}'Response
{
"code": 0,
"message": "ok",
"data": {
"symbol": "QQQ.US",
"time_range": "1Y",
"strategy": "chandelier",
"engine_version": "1.1.0",
"venue_mode": "consolidated",
"start_date": "2025-09-08",
"end_date": "2026-09-04",
"num_trades": 8,
"win_rate": 50.0,
"total_pnl_pct": 8.56,
"annualized_pct": 8.66,
"max_drawdown_pct": 11.12,
"sharpe": 0.624,
"profit_factor": 1.84,
"buy_hold_pnl_pct": 24.2,
"excess_return_pct": -15.64,
"params": { "sma_period": 100, "atr_period": 14, "entry_mult": 2.0, "exit_mult": 3.0, "entry_mode": "chandelier", "trail_mode": "rolling", "...": "其余略" },
"cost": {
"commission_rate": 0.0,
"min_commission": 0.0,
"slippage_bps": 0.0,
"initial_capital": 100000.0,
"execute_on": "close"
},
"data_range": { "actual_start": "2025-09-08", "actual_end": "2026-09-04", "bars": 251, "truncated": false },
"data_freshness": { "distribution_mode": "eod_only", "as_of": "2026-09-04", "delayed": true },
"notes": [
{ "code": "no_cost_model", "level": "info", "message": "本次未计成本。实盘会有手续费与滑点…" }
],
"trades": [{ "seq": 1, "entry_date": "2025-09-08", "entry_price": 578.87, "exit_date": "2025-10-10", "exit_price": 589.5, "pnl_pct": 1.8363363103978347, "exit_reason": "stop", "...": "其余略" }],
"disclaimer": "数据与回测结果仅供研究参考,不构成任何投资建议。"
}
}Taken from a real call; arrays trimmed for readability.
Possible errors
400Invalid parameters: date format, period value, too many symbols in a batch, and so on404Symbol or record does not exist429Rate limit hit502Upstream data source temporarily unavailable
Try it
Fill in the parameters and fire a real request. It consumes the quota of whatever identity you are using, exactly like a production call.
No request sent yet. Use the button above.
Machine-readable docs
The OpenAPI description ships with the service, so you can import it straight into Postman or Insomnia, or generate a client from it. The rate limits in that document are rendered from the same plan configuration, so it cannot drift from this page.
Data timeliness
Distribution mode:eod_onlyWe currently serve daily bars for T+1 and earlier. Intraday and real-time data are not open yet.
Exchanges are explicit about redistribution: closing data at T+1 or older may be distributed freely, while real-time and intraday data need a separate licence. Until that licence is in place we would rather serve nothing than serve something whose provenance we cannot explain. When it is, this line changes to delayed or real-time and no path or field changes with it.
Every market-data response carries a timeliness field stating exactly what you received — whether it is usable for a live decision is visible in the response, not something you have to infer from the docs.
The venue mode travels with the data
Every bar carries venue_mode. Consolidated aggregates all trading venues; single_venue is one exchange only. We measured closing prices differing by up to 7.1% between them, with single-venue volume at 23%–34% of consolidated and drifting by quarter — single_venue is therefore always refused for backtesting. The market data endpoints serve consolidated bars only. When a backtest reaches back past 2024-07 for US history, it splices in the primary-session basis (primary_session: the close is the exchange official close, measured equal to consolidated day for day; open/high/low are rebuilt from primary-venue minute bars, within 0.03%; volume is single-venue), and says so in the result via mixed_session_source.
The catalogue keeps delisted symbols
Delisted symbols stay queryable and are flagged with is_delisted. Backtests do not filter them out: dropping the companies that failed makes historical returns look better than they were.
The platform cannot place orders
We are not connected to any broker, and there are no order, position or funding endpoints in this API.
Free and open
The endpoints below are callable by guests with no sign-up; quota is counted per source IP.
Available endpoints
- GET/v1/quote/{symbol}Quote
- GET/v1/kline/{symbol}Historical candles
- GET/v1/intraday/{symbol}IntradayUnavailable on this deployment
- GET/v1/static/{symbol}Static info
- POST/v1/batchBatch lookup
- GET/v1/symbolsSymbol search
- POST/v1/backtestRun a backtest
Rate limits (guest tier)
- Quote30/min
- Candles30/min
- IntradayNot available
- Static info30/min
- Symbol search30/min
- Batch lookup10/min
- Backtest5/min
- OptimizationNot available
Each category has its own counter. These numbers come from the guest tier in GET /api/plans — the same source as the pricing page.
Registering raises your limits
- Quota moves to your API key, so you no longer share it with everyone behind the same egress IP
- Single-endpoint, batch and backtest limits rise to the registered tier — exact figures in the quota table above
- Daily usage becomes visible in the dashboard, and backtests can be saved and shared
FAQ
Candles and quotes served from our own store come back in milliseconds. The four cards at the top of this page are the round trips your browser measured when you opened it — reload and you can reproduce them yourself. We do not publish "under 50 ms" style promises with nothing behind them.
US equities and US-listed ETFs only. Hong Kong and mainland China are planned but not implemented; the API accepts market=US and nothing else today. We will update this page honestly when that changes rather than promise a date we cannot keep.
No. Every /v1 endpoint is callable by guests, rate-limited per source IP (30/min single endpoint, 10/min batch). Signing up takes an email code; after that quota is counted per API key (60/min single endpoint, 20/min batch) and your calls show up in the dashboard.
Daily, after the US close, available the next day (T+1). We currently serve daily bars for T+1 and earlier. Intraday and real-time data are not open yet. Data is current to 09/04/2026, and consolidated daily bars go back to 07/01/2024. Intraday and real-time data need a separate exchange licence and are not open yet.
Get started
No credentials needed — copy the command on the right and you have your first response. For higher limits, usage statistics and saved backtests, sign up with an email code; a key is issued for you at sign-up.
curl -s "https://us2.ninequantai.com/v1/quote/AAPL.US"Runs as a guest; quota is counted per source IP.
Contact us
Integration questions, quota requests and data partnerships all reach us by email.
Official account and support handle are still being registered
Not yet available