Migrating from Tushare
Most Tushare calls have an equivalent endpoint: migration means changing the URL and four field names. Below is the mapping table, a runnable rewrite, and the parts you cannot migrate (we say so up front).
Quotes / K-line / three financial statements / financial indicators / money flow / top-trader boards / margin trading / block trades / dividends / shareholders / convertible-bond terms / ETFs / IPOs / earnings events all have equivalents — change the URL. Minute bars, news / filings / full research reports, index daily bars and the trading calendar we do not offer — keep Tushare for those; the two work fine side by side.
What moves, what does not
- Quote snapshot · daily / weekly / monthly K-line
- Income statement / balance sheet / cash-flow statement (multi-period)
- Main money flow (today / 5 / 10 / 20-day)
- Top-trader boards (institutions / hot money / active seats) · margin trading · block trades
- Dividends · top-10 shareholders · holder counts · chip distribution
- Convertible-bond terms · ETF overview · IPO calendar · event tags (earnings, lock-up…)
- Free endpoints need no key — not even signup
- Minute-level bars (historical or real-time)
- News / filings / full research reports and policy libraries (we only offer research digests)
- Index daily bars (we have market profile and valuation percentiles, not index candlesticks)
- Trading calendar (our /v1/calendar is an economic calendar of macro releases — a different thing)
- Bulk export of the full symbol list (we offer keyword search)
Four steps
Tushare uses suffixes (600667.SH); we use prefixes (sh600667). 00700.HK → hk00700; AAPL → usAAPL.
# Tushare # us
# 600667.SH -> sh600667
# 000001.SZ -> sz000001
# 830799.BJ -> bj830799
# 00700.HK -> hk00700
# AAPL -> usAAPL
# one-liner (Python)
def to_code(ts_code: str) -> str:
"""600667.SH -> sh600667 · 00700.HK -> hk00700 · AAPL -> usAAPL"""
if "." not in ts_code:
return "us" + ts_code.upper()
num, mkt = ts_code.split(".")
return {"SH": "sh", "SZ": "sz", "BJ": "bj", "HK": "hk"}[mkt] + numpro.daily → /v1/kline; pro.moneyflow → /v1/fund; pro.fina_indicator → /v1/finance (see the table below).
# Tushare
# pro.daily(ts_code="600667.SH", start_date="20260101", end_date="20260921")
# us (no date range: take the last N bars, filter locally)
curl "https://api.ashareapi.com/v1/kline?code=sh600667&period=day&count=30"Only four common fields differ (close→last · trade_date→date · code style), the rest (open / high / low / volume / amount) keep the same meaning.
{
"ok": true, "endpoint": "kline", "tier": "free",
"elapsed_ms": 615, "source": "multi",
"data": [
{"date": "2026-09-21", "open": "20.64", "last": "20.30",
"high": "20.78", "low": "20.12",
"volume": "2213526", // lots (same unit as Tushare vol)
"amount": "4510589168", // CNY
"turnover": "10.58"} // turnover rate (%)
]
}Every endpoint returns the same envelope (ok / endpoint / tier / elapsed_ms / source / data). 429 means rate limited; an upstream failure returns ok=false and is not counted.
import requests
BASE = "https://api.ashareapi.com/v1"
H = {} # free endpoints: no key
# paid endpoints add (header keeps keys out of logs):
# H = {"Authorization": "Bearer ct-your-key"}
def get(path: str, **params):
r = requests.get(f"{BASE}/{path}", params=params, headers=H, timeout=30)
if r.status_code == 429: # anonymous 5/min; 60/min after one PoW challenge
raise RuntimeError("rate limited - slow down, or see /v1/challenge")
r.raise_for_status()
body = r.json()
if not body.get("ok"):
raise RuntimeError(f"upstream failed (not counted): {body}")
return body["data"]Endpoint mapping
Left column: interface names from Tushare official docs. Right column: the equivalent (or nearest) endpoint. Where there is no equivalent we say so instead of forcing one.
| Tushare Pro | ashareapi | Notes |
|---|---|---|
| pro.stock_basic | GET /v1/search | Keyword search and disambiguation; no bulk export of the full symbol list |
| pro.daily / weekly / monthly | GET /v1/kline | period=day|week|month, count for the last N bars (no date range) |
| pro.daily_basic | GET /v1/snapshot (PE/PB/turnover/market cap/shares + limit price, all in one call) | Essentially 1:1 (A-shares only; for historical valuation series beyond PE/PB use /v1/screen factors) |
| pro.fina_indicator | GET /v1/finance | Three statements, multi-period (num controls periods); ROE / margins included |
| pro.income / balancesheet / cashflow | GET /v1/finance | One call returns all three statements |
| pro.moneyflow | GET /v1/fund | Main money flow (today / 5 / 10 / 20-day + market-wide rank) |
| pro.top_list / pro.top_inst | GET /v1/lhb | type=institution | hotmoney | activeseat |
| pro.margin_detail | GET /v1/margin-trade | Balance / buys / repayments / short balance |
| pro.block_trade | GET /v1/block-trade | Price / discount / volume / buyer and seller |
| pro.dividend | GET /v1/dividend | years controls the window (3 by default) |
| pro.top10_holders / top10_floatholders | GET /v1/shareholder | Top-10 holders + holder count + institutional holdings |
| pro.stk_holdernumber | GET /v1/shareholder | Holder count (chip concentration) |
| pro.cb_daily / cb_basic | GET /v1/bond | Terms-oriented: conversion price / call trigger / double-low / premium; no bond daily bars |
| pro.fund_daily / fund_basic (ETF) | GET /v1/etf | Quote / size / premium-discount / money flow |
| pro.new_share | GET /v1/ipo | Issue / subscription / allotment / listing calendar (days) |
| pro.forecast / express | GET /v1/events | 42 event tags (earnings / lock-up / buyback / placement / block trades…) |
| pro.concept / ths_index / ths_member | GET /v1/sector · /v1/sector-valuation | Board rankings and board valuation (with percentiles); no standalone constituents endpoint |
| pro.moneyflow_hsgt / hsgt_top10 | GET /v1/fund | Per-stock view includes margin and institutional seats; no market-wide northbound intraday series |
| pro.trade_cal (trading calendar) | ❌ | Our /v1/calendar is an economic calendar (macro release schedule), not a trading calendar |
| pro.index_daily (index bars) | ❌ | We offer /v1/market-overview (market profile / style rotation / valuation percentile), not index candlesticks |
| pro.news / anns_d / report_rc | ❌ | No news or filing text; research is digest only (/v1/dehydrated, key required) |
| Minute bars / real-time minute | ❌ | We do not do minute-level data |
Field mapping
| Tushare | ashareapi | Notes |
|---|---|---|
| ts_code | code | 600667.SH → sh600667 (prefix) |
| trade_date | date | 20260918 → 2026-09-18 (dashed) |
| close | last | Same meaning, different name |
| vol | volume | Both in lots (×100 = shares) |
| amount | amount | CNY |
| pct_chg | (compute it) | Derive from adjacent closes to avoid definition drift |
| turnover_rate | turnover (/v1/quote) | Turnover rate (%) |
| pe_ttm / pb / ps_ttm / total_mv | (/v1/screen factors) | Use PE_TTM / PB / PS_TTM / TotalMV inside expressions |
Full rewrite example
Same task: daily bars + money flow + institutional top-trader board. Tushare first, then ours (the K-line part runs with no key).
import tushare as ts
pro = ts.pro_api("your token")
bars = pro.daily(ts_code="600667.SH",
start_date="20260101", end_date="20260921")
basic = pro.daily_basic(ts_code="600667.SH")
flow = pro.moneyflow(ts_code="600667.SH")
top = pro.top_list(trade_date="20260918")
for r in bars.itertuples():
print(r.trade_date, r.close, r.vol)import requests
BASE = "https://api.ashareapi.com/v1"
H = {} # free endpoints need no key; for paid ones add {"Authorization": "Bearer ct-your-key"}
def get(path, **params):
r = requests.get(f"{BASE}/{path}", params=params, headers=H, timeout=30)
r.raise_for_status()
body = r.json()
if not body.get("ok"):
raise RuntimeError(body)
return body["data"]
# 1) daily bars (free, no key) - equivalent to pro.daily (last 30 bars, no date range)
bars = get("kline", code="sh600667", period="day", count=30)
for r in bars:
print(r["date"], r["last"], r["volume"]) # close -> last
# 2) money flow: main net inflow + top-trader board + block trades + margin (key required)
# funds = get("fund", code="sh600667")
# 3) institutional top-trader board (key required)
# lhb = get("lhb", type="institution")
# 4) date filtering happens locally (the API takes count, not start/end)
bars_2026 = [r for r in bars if r["date"] >= "2026-01-01"]FAQ
Yes, and it is common: Tushare for minute bars and news/filings, us for real-time snapshots, money flow, top-trader boards and MCP access. They do not conflict — no need to pick one.
/v1/kline takes count (last N bars) rather than a date range — fetch a window and filter by date locally. That also avoids "no data in range" being mistaken for an API error.
Four common fields: ts_code→code, trade_date→date, close→last, vol→volume. open / high / low / amount keep the same meaning. Turnover rate lives in the turnover field of /v1/quote.
Free endpoints: anonymous 5/min, up to 60/min after solving one PoW challenge. Paid keys follow the tier (Trial ¥9.9 / 10k calls · Standard ¥29.9 / 100k · Pro ¥99 / 500k · Unlimited ¥199 per month). Upstream failures fail over automatically and are not counted.
Check the endpoint reference (32 endpoints by category) or use /v1/search to resolve a symbol. For what we genuinely lack (minute bars, news and full filings, index bars, trading calendar) keep Tushare — that boundary is written here so you do not migrate for nothing.
Last updated: 2026-09-21
Tushare interface and field names come from the Tushare Pro official docs (tushare.pro/document/2), checked against the official index on 2026-09-21. Our fields come from live responses (quote / kline / hot / market-overview / changedist, verified 2026-09-21). Both sides may change — the official pages govern.
Free endpoints need no key — run one curl call, then decide whether to move your scripts over.