> Source: https://ashareapi.com/en/docs/guides/python-sdk/  ·  Markdown version for LLMs / AI agents

Tutorial

# 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

 bash
```
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**.

 Python (runnable)
```
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 breadth
```

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

 `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

 Python
```
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**

 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

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

 `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

 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
 Is the SDK free?
 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.

 Do I have to install pandas?
 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]"`.

 Why does 600667.SH work too?
 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).

 What is the difference between EmptyResultError and UpstreamError?
 **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.

 How do I upgrade the SDK?
 `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.

 Read next

-
[Endpoint reference (32 endpoints, fields and samples)](/en/endpoints)

-
[Node.js / TypeScript SDK (npm install ashareapi)](/en/docs/guides/nodejs-ashare-quotes)

-
[Python quotes: 3 approaches compared](/en/docs/guides/python-ashare-quotes)

-
[Migrating from Tushare (interface mapping + code rewrite)](/en/docs/migrate-from-tushare)

-
[Error codes](/en/docs/errors)

 [← All tutorials](/en/docs/guides)
