If you're building a quantitative trading system or market data dashboard for the Chinese A-share market, you'll quickly realize that A-share trading rules are quite different from US or Hong Kong markets — daily price limits, T+1 settlement, call auctions, and the lunch break all shape how the data looks. This guide walks through connecting to A-share real-time quotes with Python, and how those special rules map to the API response fields.
Why You Need a Proper A-Share Data API
When I first started building market tools, I tried scraping financial websites for stock data. It worked for about a week before the page structure changed and everything broke. The latency was also terrible — not usable for real-time monitoring. That's when I switched to a proper financial data API.
When evaluating a stock data API, I look for three things: broad coverage across Shanghai and Shenzhen exchanges, low latency for real-time use cases, and sufficient historical K-line data for backtesting. After trying a few options, I've been using itick — the API design is clean, documentation is clear, and the Python integration took about an hour from sign-up to pulling live quotes.
A-Share Trading Rules and How They Map to Data Fields
Let's get the trading rules straight first, so you know what each field means when you see it in the API response.
Trading hours: 9:30–11:30 AM and 1:00–3:00 PM Beijing time. There's a 1.5-hour lunch break.
Price limits: Main board stocks move ±10%, ChiNext and STAR Market move ±20%, ST stocks move ±5%. At the limit-up price, buy orders pile up and sell orders dry up; the reverse happens at limit-down.
T+1 settlement: Shares bought today can't be sold until tomorrow, but the API returns real-time volume and turnover.
Call auction: 9:15–9:25 AM is the opening call auction period — trades do happen during this window.
How do these show up in the API response? Key fields:
-
ts: Trading status — 0 = normal trading, 1 = suspended, 2 = delisted, 3 = circuit breaker. No trades when suspended. -
ld: Latest price. At limit-up, this is the ceiling price. -
chp: Percent change. ±10% means limit-up or limit-down. -
v: Cumulative daily volume. -
tu: Cumulative daily turnover.
REST API for A-Share Quotes
Start with REST to confirm the data format. For market dashboards or batch snapshots, the REST quote endpoint is the easiest entry point.
import requests API_BASE = "https://api.itick.org" TOKEN = "your_token_here" headers = {"accept": "application/json", "token": TOKEN} # Get A-share real-time quote def get_a_share_quote(code): resp = requests.get( f"{API_BASE}/stock/quote", headers=headers, params={ "region": "CN", "code": code } ) return resp.json()["data"] # Fetch a few blue chips stocks = ["600519", "000001", "300750"] for code in stocks: q = get_a_share_quote(code) sign = "+" if q["chp"] >= 0 else "" print(f"{code}: {q['ld']} ({sign}{q['chp']}%)") The response fields are consistent across markets: ld latest price, chp percent change, o/h/l open/high/low, v volume. That's enough for a dashboard or basic technical analysis.
Note: A-share codes are 6-digit numbers. Pass them as strings (e.g., "600519"), not integers. I wasted half a day passing them as numeric types before realizing the API rejected the request.
WebSocket for Real-Time A-Share Push
For intraday monitoring or price alerts, polling REST every few seconds is wasteful. That's where WebSocket real-time push comes in.
The subscription format is the same as US stocks — two parameters: code$region.
import json import threading import time import websocket WS_URL = "wss://api.itick.org/stock" TOKEN = "your_token_here" authenticated = False def on_message(ws, message): global authenticated payload = json.loads(message) # Wait for auth success before subscribing if payload.get("resAc") == "auth" and payload.get("code") == 1 and not authenticated: authenticated = True # A-share subscription format: code$region ws.send(json.dumps({ "ac": "subscribe", "params": "600519$CN,000001$CN", "types": "quote" })) return if payload.get("resAc") == "pong": return data = payload.get("data") or {} if data.get("type") == "quote": sign = "+" if data["chp"] >= 0 else "" print(f"{data['s']}: {data['ld']} ({sign}{data['chp']}%)") A-share subscriptions use two parameters (600519$CN), same as US stocks — not the three-parameter format used for Indian markets. For real-time market data, WebSocket push is far more efficient than polling.
K-Line (Candlestick) Data
For technical analysis or backtesting, you'll need historical K-line data. The endpoint works the same way as other markets, just with region=CN.
def get_a_share_kline(code, k_type=8, limit=100): """ k_type: 1=1min, 2=5min, 5=1hour, 8=daily, 9=weekly, 10=monthly """ resp = requests.get( f"{API_BASE}/stock/kline", headers=headers, params={ "region": "CN", "code": code, "kType": k_type, "limit": limit } ) return resp.json()["data"] # Pull the last 100 daily candles for Kweichow Moutai daily = get_a_share_kline("600519", k_type=8, limit=100) closes = [bar["c"] for bar in daily] # 20-day moving average ma20 = sum(closes[-20:]) / 20 print(f"600519 latest close: {closes[-1]}, 20-day MA: {ma20:.2f}") K-line responses use the standard OHLCV format: t timestamp, o/h/l/c open/high/low/close, v volume. Ready for technical analysis out of the box.
A-Share-Specific Pitfalls
Lunch break means no data. From 11:30 AM to 1:00 PM Beijing time, there are no trades. When I first set up price alerts, I thought the API had gone down during lunch — turns out the market was just closed. Don't treat the lunch break as a liquidity event.
Detecting limit-up/limit-down. Main board stocks hit the daily limit at ±10%, ChiNext/STAR at ±20%. Your strategy should check whether a stock is at limit before trying to buy — at limit-up you can't get filled. The chp field tells you how close you are.
6-digit codes. A-share codes are 6-digit numbers (600519, 000001), different from Hong Kong's 5 digits (00700) or Japan's 4 digits (7203). I once passed "700" and got an empty response — that's Tencent's HK code, not an A-share.
Region is CN. Pass region=CN for A-shares, not SH or SZ.
Call auction volume. During 9:15–9:25 AM, trades happen but prices swing wildly. Don't mistake call-auction volume for continuous-auction volume in your strategy.
Complete Script: A-Share Market Monitor
Putting it all together:
import json, time, threading, requests, websocket API_BASE = "https://api.itick.org" WS_URL = "wss://api.itick.org/stock" TOKEN = "your_token_here" headers = {"accept": "application/json", "token": TOKEN} # Watchlist watchlist = ["600519", "000001", "300750"] # Initial snapshot print("=== A-Share Snapshot ===") for code in watchlist: r = requests.get(f"{API_BASE}/stock/quote", headers=headers, params={"region": "CN", "code": code}).json()["data"] sign = "+" if r["chp"] >= 0 else "" print(f" {code}: {r['ld']} ({sign}{r['chp']}%)") # WebSocket real-time push authenticated = False def on_message(ws, message): global authenticated payload = json.loads(message) if payload.get("resAc") == "auth" and payload.get("code") == 1 and not authenticated: authenticated = True params = ",".join(f"{c}$CN" for c in watchlist) ws.send(json.dumps({ "ac": "subscribe", "params": params, "types": "quote" })) threading.Thread(target=heartbeat, args=(ws,), daemon=True).start() return if payload.get("resAc") == "pong": return data = payload.get("data") or {} if data.get("type") == "quote": sign = "+" if data["chp"] >= 0 else "" print(f"[live] {data['s']}: {data['ld']} ({sign}{data['chp']}%)") def heartbeat(ws): while True: time.sleep(30) ws.send(json.dumps({"ac": "ping", "params": str(int(time.time() * 1000))})) ws = websocket.WebSocketApp(WS_URL, header=[f"token: {TOKEN}"], on_message=on_message) ws.run_forever() This script gives you an opening snapshot and live price updates through the trading day. From here you can extend it into a dashboard or alert system.
Wrap-Up
The technical architecture for A-share data is the same as other markets — REST for snapshots and historical data, WebSocket for real-time push. The main differences are in the trading rules (price limits, T+1, lunch break) and the code format (6 digits). These are the details that trip you up if you don't know about them going in.
The full API documentation is available here.
GitHub:https://github.com/itick-org/

