A-Share Data in Node.js / TypeScript with the Official SDK
The official Node SDK is live: `npm install ashareapi`. Five endpoints need no key, it has zero runtime dependencies (native `fetch`), ships full TypeScript types, returns arrays of objects, accepts forgiving symbol formats, and 5 exception types tell you exactly what to do.
Install
npm install ashareapi
# or: pnpm add ashareapi / yarn add ashareapi- Requires Node.js >= 18 (native `fetch`; 20 LTS or newer recommended)
- Zero runtime dependencies — about 12 KB installed, no supply-chain surface
- Both ESM and CommonJS work (`import` and `require`), with bundled `.d.ts` types
- ⚠️ Server-side only (Node scripts / Next.js server routes / backends) — in a browser your API key would be exposed, so no browser build is provided
Up and running in 30 seconds (free endpoints need no key)
Five free data endpoints: `quote` / `kline` / `hot` / `marketOverview` / `changedist` — no signup, no key, works immediately after install.
import { AShareAPI } from "ashareapi";
const cli = new AShareAPI(); // free endpoints need no key
const bars = await cli.quote("sh600667"); // real-time quote
console.log(bars[0]); // { date, open, last, high, low, volume, amount, turnover }
console.log(await cli.kline("600667.SH", "day", 5)); // any symbol format works
console.log(await cli.hot(10)); // hot list
console.log(await cli.changedist()); // up/down distributionCommonJS form (`require`)
const { AShareAPI } = require("ashareapi");
(async () => {
const cli = new AShareAPI();
const rows = await cli.kline("sh600667", "day", 3);
console.log(rows[0].date, rows[0].last);
})();- The `exports` map declares both `import` and `require` entry points — you do not have to change module systems to use this package
Two naming styles, same methods (drop-in from the Python SDK)
Multi-word methods expose snake_case aliases (identical to the Python SDK names) — both spellings are the same method, so code copied from the Python docs works unchanged.
| camelCase (JS convention) | snake_case (same as Python SDK) | Endpoint |
|---|---|---|
| `marketOverview(type)` | `market_overview(type)` | `/v1/market-overview` |
| `marginTrade(code, date)` | `margin_trade(code, date)` | `/v1/margin-trade` |
| `blockTrade(code, date)` | `block_trade(code, date)` | `/v1/block-trade` |
| `sectorValuation(code)` | `sector_valuation(code)` | `/v1/sector-valuation` |
| `industryChain(mode, topic, code)` | `industry_chain(mode, topic, code)` | `/v1/industry-chain` |
Symbol formats: three spellings accepted
| You write | Normalised to | Notes |
|---|---|---|
| `sh600667` | `sh600667` | Native form (market prefix + code) |
| `600667.SH` | `sh600667` | Common suffix style, converted automatically |
| `600667` | `sh600667` | Bare 6 digits inferred from the first digit: 5/6 → Shanghai, 0/3 → Shenzhen, 4/8 → Beijing |
Key-required endpoints
import { AShareAPI } from "ashareapi";
const cli = new AShareAPI({ apiKey: "ct-your-key" }); // or set ASHARE_API_KEY
const rows = await cli.screen("", "low_pe", 10, "ROETTM"); // expr, preset, limit, orderby
console.table(rows);
console.log(await cli.fund("sh600667")); // money flow + boards + block trades + margin
console.log(await cli.finance("sh600667")); // three financial statements
console.log(await cli.lhb("institution")); // top-trader board, institutions- Get a key on the [pricing page](https://ashareapi.com/pricing/) (from ¥9.9)
- 32 endpoints = 32 methods, named after the endpoint (`/v1/margin-trade` → `marginTrade()`)
- Full list in the [endpoint reference](https://ashareapi.com/endpoints/)
Return shape: an array of objects (not a DataFrame)
For the full envelope (with `elapsed_ms` / `source` / `tier`) construct with `new AShareAPI({ raw: true })` — methods then return the raw response object instead of an array.
const rows = await cli.kline("sh600667", "day", 3);
// [
// { date: '2026-09-23', open: '20.10', last: '20.59', high: '20.72', low: '19.45', ... },
// { date: '2026-09-22', open: '20.63', last: '19.87', ... },
// { date: '2026-09-21', ... }
// ]| Python SDK | Node.js SDK | |
|---|---|---|
| Default return | `pandas.DataFrame` (or `list[dict]` without pandas) | `Row[]` (array of objects) — except `finance` (`Row[][]`) and `shareholder` / `calendar` (`{ tables, data }`) |
| Accessing values | `df["last"]` / `df.head()` | `rows[0].last` / `rows.map(...)` |
| Sync or async | Synchronous | Everything returns a Promise (`await`) |
Error handling: 5 exception types that each say what to do
import { AShareAPI, AuthError, RateLimitError,
UpstreamError, EmptyResultError } from "ashareapi";
const cli = new AShareAPI();
try {
const rows = await cli.fund("sh600667");
} catch (e) {
if (e instanceof AuthError) console.log(e.message); // 401 -> key-required endpoint, get a key
else if (e instanceof RateLimitError) console.log(e.message); // 429 -> slow down / solve a PoW challenge / upgrade
else if (e instanceof UpstreamError) console.log(e.message); // upstream fetch failed (failed over, not billed) -> retry once
else if (e instanceof EmptyResultError) console.log(e.message); // no data right now (e.g. no block trades today) -> not billed
else throw e;
}- Built-in retries: `429` / `5xx` / network errors back off exponentially 3 times (0.5s / 1.5s / 4s)
- `ok:false` (upstream fetch failure) is never retried — the backend already failed over
| Exception | When it is thrown | What to do |
|---|---|---|
| `AuthError` | 401 / key-required endpoint called without a key | Get a key, or use the free endpoints |
| `RateLimitError` | 429, tier rate exceeded | Slow down; solve a PoW challenge; or upgrade |
| `UpstreamError` | Upstream fetch failed (we failed over, not billed) | One retry usually fixes it |
| `EmptyResultError` | Request succeeded but there is no data right now | Change conditions or retry later, not billed |
| `APIError` | Anything else (network / 5xx after retries) | Read the message |
Using it in a Next.js server route (server-side only)
import { NextResponse } from "next/server";
import { AShareAPI, AuthError, RateLimitError } from "ashareapi";
const cli = new AShareAPI(); // reads process.env.ASHARE_API_KEY
export async function GET(request: Request) {
const code = new URL(request.url).searchParams.get("code") ?? "sh600667";
try {
const [quote, technical] = await Promise.all([cli.quote(code), cli.technical(code)]);
return NextResponse.json({ ok: true, code, quote: quote[0], technical: technical[0] });
} catch (e) {
if (e instanceof AuthError) return NextResponse.json({ ok: false, error: "Missing or invalid key" }, { status: 401 });
if (e instanceof RateLimitError) return NextResponse.json({ ok: false, error: "Rate limited" }, { status: 429 });
throw e;
}
}- Keep the key in a server-side env var (`.env.local`) — never in `NEXT_PUBLIC_*`, which ships to the browser bundle
- `Promise.all` fetches several endpoints concurrently — much faster than sequential calls
Common mistakes and boundaries
- `401` on a key-required endpoint → no key supplied: only 5 endpoints are free
- `429` → anonymous limits are low (5/min); one PoW challenge (`cli.challenge()`) raises it to 60/min, or upgrade the tier
- `EmptyResultError` is not a failure: it means "there is genuinely no such data right now" (e.g. no block trades today) — not billed, do not treat it as an error
- Forgetting `await`: every method returns a Promise, so `console.log(cli.quote(...))` prints a Promise, not data
- Data date: on a market holiday the date will not say "today"; read the `date` field to know which session the data belongs to
- Units: `volume` is in lots (×100 = shares) · `amount` is CNY · ratio fields are percentages (`20` = 20%)
- Minute bars: only daily / weekly / monthly are provided; minute-level is out of scope (stated plainly)
FAQ
The SDK itself is free (MIT). Five free endpoints (quote / K-line / hot list / market overview / up-down distribution) need no key and no signup. The other 25 endpoints need an API key (from ¥9.9); the SDK charges nothing extra.
No. Zero runtime dependencies — it only uses Node native `fetch` (hence Node >= 18). About 12 KB installed, no transitive dependencies and no supply-chain surface.
Technically our API allows cross-origin calls, but it is not recommended and not supported: calling from a browser puts your API key in front-end code and network requests, which is the same as publishing it. Call from the server (Node / Next.js server route / cloud function) and pass results to the front end. No browser build is provided on purpose.
Yes. Bundled `.d.ts` files: your editor autocompletes all 32 methods and their parameters. Common row types (`Row` / `QuoteRow` / `Envelope`) are exported, so `import type { Row } from "ashareapi"` works.
Same backend, same 32 methods, same 5 exception types, same retry policy. The only differences are language conventions: the JS methods are primarily camelCase (with snake_case aliases), everything returns a Promise, and results are arrays of objects rather than DataFrames.
`npm install ashareapi@latest`. For the latest version and the full change history, see the [changelog](/en/changelog).
Last updated: 2026-09-23
Code and return shapes come from real runs of the SDK (installed 2026-09-23 from the packed tarball and verified in both ESM and CJS: quote returned 30 rows, hot 3 rows, marketOverview("valuation") 8 rows, and a key-required endpoint correctly threw AuthError without a key).