Migration guide

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).

Bottom line

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

Moves (URL swap only)
  • 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
Does not move (keep Tushare)
  • 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

1Turn ts_code into a prefixed code

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] + num
2Swap the URL per the mapping table

pro.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"
3Rename four fields

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 (%)
  ]
}
4Wrap it: one envelope, plus rate handling

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.

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

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).

Before (Tushare)
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)
After (ashareapi)
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

Can I use Tushare and ashareapi together?

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.

I used start_date / end_date. How do I migrate?

/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.

How much renaming is there?

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.

How do rate limits and quotas work?

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.

My endpoint is not in this table. What now?

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.

Not tried it yet?

Free endpoints need no key — run one curl call, then decide whether to move your scripts over.