A-Share Data in Python with the Official SDK
The official SDK is live: `pip install ashareapi`. Five endpoints need no key, results come back as pandas DataFrames (plain `list[dict]` without pandas), symbol formats are forgiving, and 5 exception types tell you exactly what to do.
Install
pip install ashareapi # base: returns list[dict]
pip install "ashareapi[pandas]" # add DataFrame support (recommended)- Requires Python 3.9+; the only hard dependency is `requests` — pandas is optional
- Without pandas every method returns `list[dict]` — nothing breaks
Quickstart in 30 seconds (free endpoints need no key)
Five free endpoints: `quote` / `kline` / `hot` / `market_overview` / `changedist` — no signup, no key.
from ashareapi import AShareAPI
cli = AShareAPI() # free endpoints need no key
df = cli.quote("sh600667") # realtime quote -> DataFrame
print(df[["date", "last", "turnover"]])
print(cli.kline("600667.SH", count=5)) # symbol format is forgiving
print(cli.hot(limit=10)) # hot list
print(cli.changedist()) # advance/decline breadthSymbol formats: all three work
`cli.quote("600667.SH")` and `cli.quote("sh600667")` are the same call — code migrated from another data source usually works unchanged.
| You write | Normalized to | Note |
|---|---|---|
| `sh600667` | `sh600667` | Native form (market prefix + code) |
| `600667.SH` | `sh600667` | Common suffix style, converted for you |
| `600667` | `sh600667` | Bare 6 digits: 5/6 -> Shanghai, 0/3 -> Shenzhen, 4/8 -> Beijing |
Paid endpoints: pass a key
from ashareapi import AShareAPI
cli = AShareAPI("ct-your-key") # or set ASHARE_API_KEY
df = cli.screen(preset="low_pe", orderby="ROETTM", limit=10)
print(df.head())
print(cli.fund("sh600667")) # fund flow + boards + block trades + margin
print(cli.finance("sh600667")) # three financial statements
print(cli.lhb("institution")) # top-trader institution board- Keys come from the [pricing page](https://ashareapi.com/pricing/) (from CNY 9.9)
- 32 endpoints = 32 methods, named 1:1 with the HTTP paths (`/v1/margin-trade` -> `margin_trade()`)
- Full list: [endpoint reference](https://ashareapi.com/endpoints/)
Return types: DataFrame or list?
The SDK converts the object array into a DataFrame (it prefers `structured`, falling back to `data`); table lists (`finance`) and sectioned structures (`shareholder` / `calendar`) are returned as-is (not force-wrapped, which would degrade into a 3x1 table); use `raw=True` for the full envelope.
| Environment | Returns | How to use |
|---|---|---|
| pandas installed (`ashareapi[pandas]`) | `pandas.DataFrame` | `df.head()` / `df[["date","last"]]` / plotting |
| No pandas | `list[dict]` | Iterate yourself — the same code does not crash |
Error handling: 5 exception types, each actionable
from ashareapi import (AShareAPI, AuthError, RateLimitError,
UpstreamError, EmptyResultError)
cli = AShareAPI()
try:
df = cli.fund("sh600667")
except AuthError as e: # 401 -> paid endpoint, get a key
print(e)
except RateLimitError as e: # 429 -> slow down / solve one PoW / upgrade
print(e)
except UpstreamError as e: # upstream fetch failed (failover done, not counted) -> retry once
print(e)
except EmptyResultError as e: # no data right now (e.g. no block trades today) -> free, change filters
print(e)| Exception | Raised when | What to do |
|---|---|---|
| `AuthError` | 401 / paid endpoint without a key | Get a key, or use a free endpoint |
| `RateLimitError` | 429 over your tier limit | Slow down; solve a PoW challenge; or upgrade |
| `UpstreamError` | Upstream fetch failed (failover done, not counted) | One retry usually works |
| `EmptyResultError` | Request succeeded but there is no such data now | Change filters or retry later, not billed |
| `APIError` | Anything else (network / retries exhausted) | Read the message |
SDK vs raw HTTP vs Tushare / AkShare
| ashareapi SDK | Raw requests | Tushare | AkShare | |
|---|---|---|---|---|
| Time to first call | pip install and go | Wrap retries/exceptions yourself | Signup + credit thresholds | Install and go |
| Return type | DataFrame (list without pandas) | Parse JSON yourself | DataFrame | DataFrame |
| Free trial | 5 endpoints, no key | Also keyless | Signup required (credits) | Free |
| Errors | 5 exception types ("no data" != failure) | Your own checks | Return values | Your own checks |
| Maintenance | Ours (multi-source failover) | You fix when upstream changes | Vendor maintained | Frequent upstream churn |
Gotchas and boundaries
- `401` on a paid endpoint -> missing key: only 5 endpoints are free
- `429` -> anonymous limits are low; solve one PoW (`cli.challenge()`) for 60 req/min, or upgrade
- `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 outage
- Data dates: holidays do not become "today"; read the `date` field to see which session the data belongs to
- Units: `volume` is in lots (x100 = shares), `amount` in CNY, ratio fields are percentages (`20` = 20%)
- Minute-level K-line is out of scope (daily / weekly / monthly only) — stated plainly
FAQ
The SDK itself is free (MIT), and 5 endpoints (quote / K-line / hot list / market overview / breadth) need no key and no signup. The other 25 endpoints need an API key (from CNY 9.9); the SDK adds no extra charge.
No. pandas is an optional dependency: with it you get `DataFrame` (recommended), without it you get `list[dict]` and nothing breaks. Install both via `pip install "ashareapi[pandas]"`.
The SDK normalizes symbols: `sh600667`, `600667.SH` and `600667` are all accepted and converted to `sh600667` before the request. Bare 6-digit codes infer the market from the first digit (5/6 -> Shanghai, 0/3 -> Shenzhen, 4/8 -> Beijing).
EmptyResultError = there is no such data right now (e.g. no block trades today): the request succeeded and is not billed — change filters or retry later. UpstreamError = the upstream fetch failed (we already failed over, also not counted) — one retry usually works. Splitting them tells you instantly whether to change filters or retry.
`pip install -U ashareapi`. 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 from PyPI and verified 2026-09-23: quote 30 rows / hot 3 rows / RateLimitError raised correctly under anonymous limits). Tushare and AkShare descriptions follow their official docs; their interfaces and thresholds are theirs to change.