> Source: https://ashareapi.com/en/docs/guides/migrate-from-akshare/  ·  Markdown version for LLMs / AI agents

Tutorial

# Migrating from AkShare to ashareapi
 AkShare wins on breadth; we win on not having to maintain it yourself. If you already use AkShare and are tired of "upstream redesign → broken function", migration is three changes: code format, call style, field names. Below is the mapping and runnable code — and the parts that do not migrate.

## First, be clear about why you are migrating
 AkShare is a free, MIT-licensed Python library whose data coverage is **much wider than ours**. The usual reason to migrate is not "more data" but these three:

-
 **You do not want to maintain it**: AkShare scrapes public pages, so an **upstream redesign breaks a function** until you upgrade or patch the code

-
 **You want stability and caching**: every one of our endpoints has multiple sources (auto-failover), caching and health checks

-
 **You need it for AI Agents or outside Python**: we speak HTTP and MCP, so any language works without installing 1,300+ dependencies

 If your budget is zero, you are already in Python and you are doing exploratory research (not production jobs), **AkShare is a great choice** — this page is for people who want to maintain a little less, not to talk anyone out of it. The two also **work fine together**.

## Step 1: Convert the code format from bare digits to prefixed
 This is the easiest trap in the migration. AkShare takes a **bare six-digit `symbol`** (`"600667"`); ours is **prefixed** (`sh600667`). Every endpoint takes this format.

 Python
```
# AkShare # ashareapi
# "600667" (Shanghai A) → sh600667
# "000001" (Shenzhen A) → sz000001
# "830799" (Beijing) → bj830799
# "00700" (Hong Kong) → hk00700
# "AAPL" (US) → usAAPL

# Infer the market from the leading digits (A-share rules) — good enough, not exhaustive
def to_code(symbol: str) -> str:
 """AkShare bare code -> ashareapi prefixed code"""
 if not symbol.isdigit():
 return "us" + symbol.upper() # non-numeric -> US
 if symbol.startswith(("60", "68")): # Shanghai main / STAR
 return "sh" + symbol
 if symbol.startswith(("00", "30")): # Shenzhen main / ChiNext
 return "sz" + symbol
 if symbol.startswith(("83", "87", "43")): # Beijing
 return "bj" + symbol
 return "sh" + symbol # fallback (always verify)
```

-
 **Safest route**: call `/v1/search?q= ` first — it returns the correct prefixed code and disambiguates automatically

-
 The prefix also **removes ambiguity** like "is 000001 Ping An Bank or the SSE index"

## Step 2: Swap the call style (function → HTTP request)
 AkShare is `import akshare as ak` then call a function and get a DataFrame; ours is **one GET request** returning a uniform JSON envelope. Neither is hard, but the shape differs.

 Python (runnable; the K-line part needs no key)
```
# ── Before (AkShare) ─────────────────────────
# import akshare as ak
# df = ak.stock_zh_a_hist(symbol="600667", period="daily",
# start_date="20260101", end_date="20260925",
# adjust="")
# print(df[["日期", "收盘", "成交量"]])

# ── After (ashareapi) ────────────────────────
import requests

BASE = "https://api.ashareapi.com/v1"
H = {} # free endpoints need no key; add {"Authorization": "Bearer ct-your-key"} for paid

def get(path, **params):
 r = requests.get(f"{BASE}/{path}", params=params, headers=H, timeout=30)
 if r.status_code == 429: # rate limited: see section 4
 raise RuntimeError("rate limited")
 r.raise_for_status()
 body = r.json()
 if not body.get("ok"):
 raise RuntimeError(f"upstream failed (not counted): {body}")
 return body["data"]

# Historical K-line (free) — equivalent to stock_zh_a_hist
# (no date-range args: take the last N bars, filter locally)
bars = get("kline", code="sh600667", period="day", count=30)
for r in bars:
 print(r["date"], r["last"], r["volume"]) # 收盘 -> last

# Realtime quote (free) — the single-name version of stock_zh_a_spot_em
q = get("quote", code="sh600667")
print(q[0]["last"], q[0]["turnover"]) # last price / turnover
```

-
 **No `adjust` (price-adjustment) parameter** — the basis is **fixed at forward-adjusted**; ⚠️ do not re-adjust (double adjustment); unadjusted / back-adjusted series are not provided

-
 **No `start_date` / `end_date`** — use `count` for the last N bars and filter locally (this also avoids "no data in range" being mistaken for an API error)

-
 The return is a `list[dict]` (numeric fields are **strings**), not a DataFrame; convert with `import pandas as pd; pd.DataFrame(bars)`

## Step 3: Rename the fields
 Only a few common field names differ; the rest are the same. **Note our numeric fields are strings** (`"19.41"`, not `19.41`) — cast before comparing or computing.
 |
| | Meaning | AkShare | ashareapi | Note

| | Date | 日期 | date | Ours includes dashes (2026-09-24)

| | Close / last | 收盘 | last | Different name, same meaning

| | Open | 开盘 | open | Same

| | High / low | 最高 / 最低 | high / low | Same

| | Volume (lots) | 成交量 | volume | Both in **lots**

| | Turnover (CNY) | 成交额 | amount | CNY

| | Turnover rate (%) | 换手率 | turnover | Renamed from `exchange` on 2026-09-26 (the old name read like "exchange" the venue) · same as `snapshot`

| | Code | 代码 / symbol | code | Prefixed: sh600667

 Date
 日期
 date
 Ours includes dashes (2026-09-24)

 Close / last
 收盘
 last
 Different name, same meaning

 Open
 开盘
 open
 Same

 High / low
 最高 / 最低
 high / low
 Same

 Volume (lots)
 成交量
 volume
 Both in **lots**

 Turnover (CNY)
 成交额
 amount
 CNY

 Turnover rate (%)
 换手率
 turnover
 Renamed from `exchange` on 2026-09-26 (the old name read like "exchange" the venue) · same as `snapshot`

 Code
 代码 / symbol
 code
 Prefixed: sh600667

## Mapping table (AkShare function → our endpoint)
 The left column lists common AkShare functions from its official docs; the right column is the equivalent (or nearest) endpoint. **Where there is no equivalent, we do not force one** — see the notes.
 |
| | AkShare | ashareapi | Note

| | stock_zh_a_spot_em (whole-market snapshot) | GET /v1/quote (single) · /v1/hot (ranking) | **We do not bulk-export a whole-market snapshot**; only per-code quotes

| | stock_zh_a_hist (history) | GET /v1/kline | period=day|week|month, count for last N bars (no date range)

| | stock_zh_a_hist_min_em (minute) | ❌ | **We do not do minute-level data** (an explicit boundary)

| | stock_individual_info_em | GET /v1/profile · /v1/snapshot | profile = listing date / business / industry; snapshot = valuation / market cap / shares / limit price

| | stock_bid_ask_em (order book) | GET /v1/orderbook | Buy 1–5 / sell 1–5 prices and sizes

| | stock_financial_abstract | GET /v1/finance | Three statements, multi-period (num controls periods); ROE / margin included

| | stock_profit_sheet_by_report_em | GET /v1/finance | All three statements in one call

| | stock_individual_fund_flow | GET /v1/fund | **One endpoint covers everything**: money flow + top-trader boards + block trades + margin

| | stock_market_fund_flow (market-wide flow) | ❌ | No market-wide intraday flow; see /v1/changedist (breadth, free)

| | stock_lhb_detail_em | GET /v1/lhb | type=institution / hotmoney / activeseat

| | stock_margin_detail_sse / _szse | GET /v1/margin-trade | Balance / buy / repay / short; code supports batches

| | stock_dzjy_mrmx (block trades) | GET /v1/block-trade | Price / premium / volume / both sides

| | stock_dividend_cninfo | GET /v1/dividend | years controls the window (default 3)

| | stock_gdfx_free_top_10_em | GET /v1/shareholder | Top holders + holder count + institutional holdings

| | stock_zh_a_gdhs (holder count) | GET /v1/shareholder | Holder count (ownership concentration)

| | bond_zh_cov | GET /v1/bond | **Terms-focused**: conversion price / call trigger / double-low / premium; no bond daily bars

| | fund_etf_spot_em | GET /v1/etf | Quote / size / premium-discount / flows (code like sh510300)

| | stock_new_gh_detail_em (IPOs) | GET /v1/ipo | Issue / subscription / allotment / listing calendar (days)

| | stock_board_industry_name_em | GET /v1/sector · /v1/sector-valuation | Sector ranking + valuation (incl. historical percentile)

| | stock_board_concept_name_em | GET /v1/sector · /v1/industry-chain | sector includes concept boards; industry-chain gives supply-chain position and relevance

| | stock_zh_index_daily | ❌ | /v1/market-overview (breadth / style rotation / valuation percentile) is **not an index K-line**

| | stock_news_em / filings / full reports | ❌ | No news or full filings; research reports are **digest only** (/v1/dehydrated)

| | Stock lists / whole-market export | GET /v1/search (by keyword) | **No bulk list export**

 stock_zh_a_spot_em (whole-market snapshot)
 GET /v1/quote (single) · /v1/hot (ranking)
 **We do not bulk-export a whole-market snapshot**; only per-code quotes

 stock_zh_a_hist (history)
 GET /v1/kline
 period=day|week|month, count for last N bars (no date range)

 stock_zh_a_hist_min_em (minute)
 ❌
 **We do not do minute-level data** (an explicit boundary)

 stock_individual_info_em
 GET /v1/profile · /v1/snapshot
 profile = listing date / business / industry; snapshot = valuation / market cap / shares / limit price

 stock_bid_ask_em (order book)
 GET /v1/orderbook
 Buy 1–5 / sell 1–5 prices and sizes

 stock_financial_abstract
 GET /v1/finance
 Three statements, multi-period (num controls periods); ROE / margin included

 stock_profit_sheet_by_report_em
 GET /v1/finance
 All three statements in one call

 stock_individual_fund_flow
 GET /v1/fund
 **One endpoint covers everything**: money flow + top-trader boards + block trades + margin

 stock_market_fund_flow (market-wide flow)
 ❌
 No market-wide intraday flow; see /v1/changedist (breadth, free)

 stock_lhb_detail_em
 GET /v1/lhb
 type=institution / hotmoney / activeseat

 stock_margin_detail_sse / _szse
 GET /v1/margin-trade
 Balance / buy / repay / short; code supports batches

 stock_dzjy_mrmx (block trades)
 GET /v1/block-trade
 Price / premium / volume / both sides

 stock_dividend_cninfo
 GET /v1/dividend
 years controls the window (default 3)

 stock_gdfx_free_top_10_em
 GET /v1/shareholder
 Top holders + holder count + institutional holdings

 stock_zh_a_gdhs (holder count)
 GET /v1/shareholder
 Holder count (ownership concentration)

 bond_zh_cov
 GET /v1/bond
 **Terms-focused**: conversion price / call trigger / double-low / premium; no bond daily bars

 fund_etf_spot_em
 GET /v1/etf
 Quote / size / premium-discount / flows (code like sh510300)

 stock_new_gh_detail_em (IPOs)
 GET /v1/ipo
 Issue / subscription / allotment / listing calendar (days)

 stock_board_industry_name_em
 GET /v1/sector · /v1/sector-valuation
 Sector ranking + valuation (incl. historical percentile)

 stock_board_concept_name_em
 GET /v1/sector · /v1/industry-chain
 sector includes concept boards; industry-chain gives supply-chain position and relevance

 stock_zh_index_daily
 ❌
 /v1/market-overview (breadth / style rotation / valuation percentile) is **not an index K-line**

 stock_news_em / filings / full reports
 ❌
 No news or full filings; research reports are **digest only** (/v1/dehydrated)

 Stock lists / whole-market export
 GET /v1/search (by keyword)
 **No bulk list export**

## Step 4: Rate limits and error handling
 AkShare has no rate limit (it runs locally); over HTTP you must **handle 429 and upstream failures**. That is exactly the trade-off — and the point — of the stability you get.

 Python
```
import time, requests

BASE = "https://api.ashareapi.com/v1"
H = {}

def get_with_retry(path, tries=3, **params):
 for i in range(tries):
 r = requests.get(f"{BASE}/{path}", params=params, headers=H, timeout=30)
 if r.status_code == 429: # rate limited
 time.sleep(2 ** i) # back off (do not hammer)
 continue
 r.raise_for_status()
 body = r.json()
 if not body.get("ok"): # upstream failed -> not counted, retryable
 time.sleep(1)
 continue
 return body["data"]
 raise RuntimeError(f"{path}: failed after {tries} tries")
```

-
 Free endpoints: anonymous **normal quota is 5/min**; solve one PoW challenge (`GET /v1/challenge`) to reach 60/min

-
 **Note**: rapid consecutive calls are temporarily tightened (we measured "anonymous limit 2/min" after six back-to-back calls) — **it recovers after back-off**, it is not a ban

-
 Paid keys are tiered (trial 30 · standard 120 · pro 300 · unlimited 600 per minute)

-
 **Failed upstream calls are not counted** (`ok:false`), so retrying is safe; only 429 needs a back-off

## What moves, what does not (stated up front)

-
 **Moves**: realtime quotes · daily / weekly / monthly K-line · three statements · money flow · top-trader boards · margin · block trades · dividends · shareholders · convertible-bond terms · ETFs · IPOs · sectors · supply chain

-
 **Does not move**: **minute bars** · **news / filings / full reports** · **index daily bars** · **whole-market list export** · **unadjusted / back-adjusted price series** (our K-line is fixed to forward-adjusted)

-
 **Stance**: for those, keep using AkShare — it is stronger there, and using both **side by side** is the pragmatic combination

## Common mistakes

-
 **Passing `"600667"`** → 400 / empty; must be `sh600667` (or confirm via `/v1/search`)

-
 **Treating strings as numbers** → `"19.41" * 2` is not 38.82; cast with `float(...)` first

-
 **Looking for `adjust` / `start_date` / `end_date`** → the adjustment basis is **fixed (forward-adjusted)** and there are no range queries; use `count` and filter locally

-
 **Turnover rate missing** → the field is `turnover` (`quote` / `kline` / `snapshot` — same name and value; it was `exchange` before 2026-09-26)

-
 **"Bad data" that is really undisclosed** → financials / holders / margin update on **disclosure cadence**; before the disclosure date there is simply no data (not a failure)

## FAQ
 AkShare is free — why migrate to you?
 **Not for more data** (AkShare covers more than we do) but for maintenance: AkShare scrapes public pages, so you patch it when upstream changes, whereas we run multi-source failover, caching and health checks. If your budget is zero and you accept the upkeep, AkShare is a great choice — do not migrate just to migrate.

 Can I use both together?
 Yes, and it is common. Typical split: **niche or very broad data** (news, filings, minute bars, indices) from AkShare, and **core data** (realtime quotes, money flow, top-trader boards, sector valuation) from us, where we carry the stability and maintenance cost.

 Can I skip the conversion and pass bare digits?
 **No.** Every endpoint requires the prefixed form (`sh600667`). The laziest correct way is to call `/v1/search?q=Ping+An+Bank` first — it returns the right code and disambiguates same-name / same-number cases.

 Do you return DataFrames?
 The HTTP endpoints return a `list[dict]` (values are mostly strings). One line converts it: `import pandas as pd; df = pd.DataFrame(bars)`. The official Python SDK (`pip install ashareapi`) returns DataFrames directly with the `[pandas]` extra.

 My function is not in this page — what now?
 Start with the **endpoint list** (32 endpoints by category) or search a keyword via `/v1/search`. For what is genuinely absent (minute bars, news / filings, index daily bars, unadjusted / back-adjusted series, whole-market lists) keep using AkShare — that is our boundary, and we say so here so you do not migrate for nothing.

 Last updated: 2026-09-25
 AkShare function names are taken from its official docs (akshare.akfamily.xyz); our endpoint count and tiers come from the official endpoint list endpoints.json (32 = 7 free + 25 paid, generated 2026-09-24); fields and shapes come from live responses (quote / kline measured 2026-09-25). Both sides may change — their official pages govern.

 Read next

-
[Side-by-side comparison with AkShare](/en/compare/akshare)

-
[Migrating from Tushare (the mapping precedent)](/en/docs/migrate-from-tushare)

-
[Getting A-share quotes in Python: 3 methods compared](/en/docs/guides/python-ashare-quotes)

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

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