Skip to main content
🚀 Now live

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.

Request
GET /v1/quote/AAPL.US
Status200 OKRound tripMilliseconds
Response
{
  "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.

Historical data response
Milliseconds
Not measured this time — shown without a number
Closing quote response
Milliseconds
End of day (T+1 or earlier)
Service availability
Healthy
Result of this probe
Symbols covered
13,193
7,284 with history loaded

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

¥0no sign-up
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
¥0free sign-up
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

¥99/ month
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

🔐 Authentication

Three caller identities. Switching between them means changing one request header.

Guest mode

No sign-up

Call 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

Recommended

Send 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

Sign in for a key

Pro

Higher limits

Same 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

See full pricing, including Enterprise

Request examples

As a guest
GET https://us2.ninequantai.com/v1/quote/AAPL.US

No headers at all; quota is counted per source IP.

With an API key
GET https://us2.ninequantai.com/v1/quote/AAPL.US
Authorization: Bearer nq_your_api_key_here
Accept-Language: zh-CN

Replace 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.

StatusMeaningWhat to do
400Invalid parameters: date format, period value, too many symbols in a batch, and so onFix the parameter the message points at. Messages are localized via Accept-Language and are safe to show to your own users.
401Credential invalid or expiredCheck 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 usYour identity is not allowed to do thisTwo 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 usSymbol or record does not existConfirm the spelling with /v1/symbols. The canonical form is QQQ.US; an omitted market is read as US.
429Rate limit hitBack off using Retry-After. If you need more headroom permanently, upgrade the plan or add another key — quota is per key.
502Upstream data source temporarily unavailableWe 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.

Success
{
  "code": 0,
  "message": "ok",
  "data": { }
}
Failure
{
  "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

Python
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.

GET/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.

Counts against: QuoteGuest · 30/minRegistered · 60/min

Parameters

NameInTypeRequiredDefaultDescription
symbolpathstringRequiredSymbol; QQQ and QQQ.US both work, an omitted market is read as US

Request

cURL
# 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

JSON
{
  "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 on
  • 404Symbol or record does not exist
  • 429Rate limit hit
  • 502Upstream 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.

GET/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.

Counts against: CandlesGuest · 30/minRegistered · 60/min

Parameters

NameInTypeRequiredDefaultDescription
symbolpathstringRequiredSymbol
periodquerystringOptionaldayday / week / month, default day
startquerystringOptionalStart date, YYYY-MM-DD
endquerystringOptionalEnd date, YYYY-MM-DD; clamped to the distributable range
daysqueryintegerOptional250Return the most recent N bars (counted in period units); start wins when both are given
adjustquerystringOptionalnonenone (default) or qfq for forward adjustment

Request

cURL
# 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

JSON
{
  "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 on
  • 404Symbol or record does not exist
  • 429Rate limit hit
  • 502Upstream 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.

GET/v1/intraday/{symbol}Unavailable on this deployment

Intraday

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.

Counts against: IntradayGuest · Not availableRegistered · 60/min

Parameters

NameInTypeRequiredDefaultDescription
symbolpathstringRequiredSymbol
datequerystringOptionalSession date, YYYY-MM-DD; defaults to the latest distributable session
intervalquerystringOptional1mBar size, e.g. 1m

Request

cURL
# 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

JSON
{
  "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 on
  • 403Your identity is not allowed to do this
  • 404Symbol or record does not exist
  • 429Rate limit hit
  • 502Upstream 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.

This endpoint is unavailable on this deploymentYou can still press send — the server returns a real 403, which is a good look at the error envelope.

No request sent yet. Use the button above.

GET/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.

Counts against: Static infoGuest · 30/minRegistered · 60/min

Parameters

NameInTypeRequiredDefaultDescription
symbolpathstringRequiredSymbol

Request

cURL
# 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

JSON
{
  "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 on
  • 404Symbol or record does not exist
  • 429Rate limit hit
  • 502Upstream 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.

POST/v1/batch

Batch 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.

Counts against: Batch lookupGuest · 10/minRegistered · 20/min

Parameters

NameInTypeRequiredDefaultDescription
symbolsbodystring[]RequiredArray of symbols, counted after de-duplication
typebodystringOptionalquotequote / static / kline
periodbodystringOptionaldayApplies when type=kline
adjustbodystringOptionalnoneApplies when type=kline
daysbodyintegerOptionalBars per symbol when type=kline

Request

cURL
# 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

JSON
{
  "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 on
  • 429Rate limit hit
  • 502Upstream 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.

GET/v1/symbols

Symbol 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.

Counts against: Symbol searchGuest · 30/minRegistered · 60/min

Parameters

NameInTypeRequiredDefaultDescription
qquerystringOptionalQuery; matches a code prefix or a fragment of the name
marketquerystringOptionalUSMarket code; only US is supported today
limitqueryintegerOptional20Maximum rows, default 20, max 100

Request

cURL
# 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

JSON
{
  "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 on
  • 429Rate limit hit
  • 502Upstream 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.

POST/v1/backtest

Run 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.

Counts against: BacktestGuest · 5/minRegistered · 20/min

Parameters

NameInTypeRequiredDefaultDescription
symbolbodystringRequiredSymbol
strategybodystringOptionalchandelierStrategy code — see GET /v1/strategies
time_rangebodystringOptional1YPreset range: YTD / 1M / 3M / 6M / 1Y / 2Y / 3Y / 4Y / 5Y / 6Y / 7Y / 8Y / 10Y / MAX
startbodystringOptionalStart date, YYYY-MM-DD; overrides time_range when given
endbodystringOptionalEnd date, YYYY-MM-DD
paramsbodyobjectOptionalStrategy parameter object, validated against params_schema
execute_onbodystringOptionalcloseFill timing: close = the signal day's close (default); next_open = the next session's open (closer to live trading)
costbodyobjectOptionalTrading 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_klinesbodybooleanOptionaltrueReturn the candle array
include_tradesbodybooleanOptionaltrueReturn the trade list
include_equitybodybooleanOptionaltrueReturn the equity curve

Request

cURL
# 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

JSON
{
  "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 on
  • 404Symbol or record does not exist
  • 429Rate limit hit
  • 502Upstream 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_only

We 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.

Data current to 09/04/2026Consolidated daily bars loaded from 07/01/2024

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

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.

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
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.

Technical support

Usage questions, bug reports, API integration help

support@ninequantai.com

Business

Custom strategies, data partnerships, commercial enquiries

business@ninequantai.com

WeChat

Official account and support handle are still being registered

Not yet available

We usually reply within 1-3 business days.Go to contact page