免费接口也限流:印刷体OCR识别的 10 并发与档位优化指南
# 免费接口也限流:印刷体OCR识别的 10 并发与档位优化指南
> 接口 926(接入点 926-1) · 免费 · POST/GET · JSON · 适用人群:中高频调用/架构开发者 · 阅读时间:约 7 分钟
## 核心要点
- 接口明确标注**并发限制为 10**;瞬时超过易触发 `80 服务超时`,需做并发控制。
- 免费接口按「使用档次」限流防滥用,高调用量可用平台积分兑换更高档位(具体档位以官方说明为准)。
- 客户端用「并发池 + 指数退避重试」即可在限制内稳定运行,避免雪崩。
## Why:免费不等于无限
因为是免费接口,很多人会一口气并发展开几百张图,结果超时、失败率飙升,反而比「慢一点但稳」更慢。理解并发与档位限制,才能在免费额度内把任务跑完、跑稳。
## What:限制与计费事实
| 项目 | 说明 |
|------|------|
| 并发限制 | 10(接口接入点说明原文) |
| 像素建议 | < 1200×1200 |
| 档位 | 免费使用档次;可用平台积分兑换更高调用档位(档位明细以官方档位说明为准) |
| 计费 | 免费,无按次/按单费用 |
> 文档未给出各档位的具体 QPS/日调用量数字,故此处不列举,以免编造;请以[免费档位说明](https://www.showapi.com/free-api)为准。
## How:客户端并发控制(Python 示例)
用线程池把并发压在 10 以内,并对 `80/90` 等可重试错误做指数退避:
```python
import time, requests
from concurrent.futures import ThreadPoolExecutor
APPKEY = "YOUR_APPKEY"
ENDPOINT = "https://route.showapi.com/926-1"
MAX_WORKERS = 8 # 留余量,低于并发上限 10
def ocr_one(img_url: str) -> dict:
params = {"appKey": APPKEY}
data = {"img_url": img_url, "need_all_region": "1"}
for attempt in range(4): # 最多重试 4 次
try:
r = requests.post(ENDPOINT, params=params, data=data, timeout=10)
rb = r.json()["showapi_res_body"]
if rb["ret_code"] == 0:
return {"ok": True, "text": rb.get("list") or rb.get("str")}
if rb["ret_code"] in (80, 90): # 超时/异常:可重试
sleep = 0.5 * (2 ** attempt)
time.sleep(sleep)
continue
return {"ok": False, "ret_code": rb["ret_code"], "remark": rb.get("remark")}
except requests.RequestException:
time.sleep(0.5 * (2 ** attempt))
return {"ok": False, "remark": "重试后仍失败"}
urls = ["https://your-cdn.example.com/a.png", "https://your-cdn.example.com/b.png"]
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as ex:
results = list(ex.map(ocr_one, urls))
print(results)
```
**Node.js(p-limit 思路,手写简易信号量)**
```javascript
const ENDPOINT = "https://route.showapi.com/926-1?appKey=YOUR_APPKEY";
const MAX = 8;
let active = 0;
const queue = [];
function run(task) {
if (active >= MAX) return new Promise((res) => queue.push(() => run(task).then(res)));
active++;
return task().finally(() => {
active--;
if (queue.length) queue.shift()();
});
}
async function ocrOne(imgUrl) {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ img_url: imgUrl, need_all_region: "1" }),
});
const rb = (await res.json()).showapi_res_body;
return rb.ret_code === 0 ? rb.list || rb.str : null;
}
// 用法:await run(() => ocrOne(url))
```
## 返回示例与解析
同《返回字段全解》:成功 `ret_code=0`,取 `list`/`str`;失败按 `ret_code` 区分(限流/超时多为 80)。
## 重试与降级策略
| 现象 | ret_code | 处理 |
|------|----------|------|
| 服务超时 | 80 | 指数退避重试(0.5/1/2/4s) |
| 识别异常 | 90 | 退避重试 1~2 次,仍失败则记录人工 |
| 文件内容过大 | 50 | 缩小图片再提交,不重试同样参数 |
| 文件下载失败 | 40 | 换 `img_base64` 或修正 URL 后重试 |
## 进阶 / 边界
- **并发别顶满 10**:留 20% 余量(如 8),应对抖动,减少 `80` 超时。
- **图片预处理降本**:先验缩放 < 1200×1200、压到大小上限内,既能避开 `50/60`,也加快响应、降低超时概率。
- **不要伪造档位数字**:档位与积分兑换规则以[官方档位说明](https://www.showapi.com/free-api)为准,本文不臆造具体额度。
## FAQ
**Q1:并发 10 是指每秒 10 次吗?**
A1:「并发」指同时进行的请求数(in-flight),不是 QPS;实际吞吐还受响应耗时与档位影响,以官方档位说明为准。
**Q2:超过并发会返回什么?**
A2:可能表现为 `80 服务超时` 或请求排队变慢;建议客户端主动限流而不是靠服务端兜底。
**Q3:免费档位不够用怎么办?**
A3:用平台积分兑换更高调用档位(明细见官方档位说明);架构上也可做预处理降量、缓存命中结果(见下一篇批量处理)。
**Q4:重试会加重限流吗?**
A4:盲目高频重试会加重超时;务必配合退避与并发上限,失败任务落库异步重试。
**Q5:接口支持批量一次传多张图吗?**
A5:文档未提供原生批量接口;多图需业务侧循环调用并做并发控制(见《批量处理图片》)。
## 相关能力 / 下一步阅读
- [票据/文档电子化:如何用印刷体OCR识别批量处理图片?](https://www.showapi.com/guides/printed-ocr-doc-digitize-926)
- [印刷体OCR识别错误码排查:10 参数错误到 90 识别异常逐条对照](https://www.showapi.com/guides/printed-ocr-error-handling-926)
- [印刷体OCR识别:img_base64 与 img_url 两种入参怎么选?](https://www.showapi.com/guides/printed-ocr-base64-vs-url-926)
- **本系列共 12 篇**:查看[印刷体OCR识别指南总目录](https://www.showapi.com/guides/printed-ocr-guides-926)