> Source: https://ashareapi.com/en/wiki/batch-fetch/  ·  Markdown version for LLMs / AI agents

Reference

# Batch fetching
 Only some endpoints accept several codes at once, and each one stitches the result back differently — one adds a symbol column, one folds the code into the column names. Check which kind it is before you send a list.

## Three batch behaviours

 Sending `sh600519,sz000001` produces three completely different results:

 
 | 

| 
 | Behaviour 
 | What you get 
 | Endpoints 




 

| 
 | **Proper merge** (extra code column, one row per symbol) 
 | row count doubles and every row **carries a code column** 
 | `quote` (adds `symbol`) · `technical` · `profile` 



| 
 | **Prefixed column names** (wide table, row count unchanged) 
 | column count doubles, names become `sh600519_code` / `sz000001_code` 
 | `chip` 



| 
 | **First code only** 
 | later codes are ignored, **with no error** 
 | `fund` · `dividend` · `block-trade` · `minute` · `snapshot` 






 ⚠️ One special case: **margin-trade ’s description claims batch support**, but sending two codes returned the same structure as one — trust the measurement over the description.


## How to read each behaviour

 **① Proper merge ( quote / technical / profile )** — filter by the code column:

```
rows = resp["data"]
moutai = [r for r in rows if r["code"] == "sh600519"] # technical / profile
# quote's code column is called symbol, not code
moutai_q = [r for r in rows if r["symbol"] == "sh600519"]
```
 ⚠️ **quote uses symbol ; technical / profile use code** — the column names do not match.

 **② Prefixed columns ( chip )** — the row count stays put and the code moves into the column names:

```
rows = resp["data"]
for r in rows:
 code = r.get("sh600519_code") or r.get("sz000001_code")
 if code and code != "-":
 profit = r.get(f"{code}_chipProfitRate")
```
 ⚠️ Half of each row is `-` (the two symbols sit side by side in the same row) — do not read that as missing data.

 **③ First code only** — you must **loop**:

```
import time, requests

CODES = ["sh600519", "sz000001", "sh601318"]
out = {}
for c in CODES:
 r = requests.get("https://api.ashareapi.com/v1/dividend",
 params={"code": c}, headers={"Authorization": f"Bearer {KEY}"},
 timeout=10).json()
 if r.get("ok"):
 out[c] = r["data"]
 time.sleep(0.25) # ← slow down; do not saturate the limit
```
 

- **Add a delay**: the limit is per-minute calls, and a tight loop hits 429 fastest.


- **Check ok before storing**: an empty result is still `ok: true` with an empty array, which is normal (see [When empty is not a failure](/en/wiki/empty-result)).


- **Retry 429s**: read the `Retry-After` header and wait before retrying — **do not retry at full speed**.




## Common misuses

 

- **Assuming every endpoint batches** — only some do; the rest **silently use just the first code** (data lost, no error).


- **Filtering every endpoint by the same column** — `quote` uses `symbol`, `technical` / `profile` use `code`.


- **Reading chip as a row array** — it is a **wide table** with prefixed columns; the row count does not double and half of each row is `-`.


- **Looping with no delay** — straight into 429.


- **Treating 429 as a failure and discarding** — it is a rate limit, not an error; wait and retry.


- **Estimating cost by row count** — billing is per **request**, and one batch request counts as one.




 Last updated: 2026-10-07
 Support and response structure measured endpoint by endpoint on 2026-10-07 by sending one code and then sh600519,sz000001 and comparing row count, column count and column names.

 Read next

-
[Full endpoint list](/en/endpoints)

-
[When empty is not a failure](/en/wiki/empty-result)

 [← All wiki pages](/en/wiki)
