车况查询开放接口 v1
面向渠道(微擎站长、代理商)的接口。渠道用平台下发的 AppKey / AppSecret 调用本接口, 平台再去调上游数据源。上游密钥只存在平台服务器上,渠道永远拿不到。
- 接口根地址:
https://www.ttzy.net - 全部接口都是
POST,路径形如/open/v1/query - 请求体统一
application/json(UTF-8) - 返回统一是
{"code":0,"msg":"ok","data":{...}},成功看的是code=0 - 哪些接口能调、每次扣多少钱,见下方「接口清单」或登录后「接口目录」
一个坑先说在前面:HTTP 状态码永远是 200,出错时也是 200。 所以判断成功必须看响应体里的 code,不能只看 HTTP 状态码。
一、接入三步
- 拿密钥:平台开通代理后会把账号密码和
AppKey/AppSecret一起发你;
也可以自助注册,登录代理后台在「接口凭据」里看。
- 填到模块里:微擎模块后台 →「车况查询 · 设置」→ 填接口地址
https://www.ttzy.net、
AppKey、AppSecret → 保存,再点「测试连通」。
- 按工具码发起查询:
/open/v1/ping通了以后,用/open/v1/query传tool+params。
异步工具(比如车牌查车架号)拿 request_id 调 /open/v1/fetch 取结果。
二、签名
每次请求带 4 个请求头:
| 请求头 | 说明 |
|---|---|
X-AppId | 平台给你的 AppKey |
X-Timestamp | 秒级时间戳,与服务器时间相差超过 300 秒会被拒 |
X-Nonce | 随机串,建议 16 位以上(如 bin2hex(random_bytes(8)))。同一个 nonce 只能用一次,重复会被判为重放 |
X-Sign | 签名,小写 32 位 MD5 |
签名算法(注意 md5(body) 是请求体的原始字节,不是重新 json_encode 一遍):
sign = md5( appKey + timestamp + nonce + md5(请求体原文) + appSecret )
几个容易踩的点:
- 拼接顺序固定,中间没有任何分隔符,最后整体做一次 md5。
md5(请求体原文):发出去什么字节就算什么字节。没有请求体时按md5('')算。- 时间戳是秒,不是毫秒;服务器时间要校准(装了 NTP 就不用管)。
- 四个头都要带,少一个直接 401。
三、接口清单
| 接口 | 路径 | 用途 |
|---|---|---|
| 连通测试 | POST /open/v1/ping | 验密钥、看余额,接完第一步就调它 |
| 工具清单 | POST /open/v1/tools | 当前可调的工具 + 你的进货价 |
| 发起查询 | POST /open/v1/query | 主要接口,一次查询扣一次费 |
| 取异步结果 | POST /open/v1/fetch | 异步工具取结果,只读、不重复扣费 |
| 查余额 | POST /open/v1/balance | 余额、今日用量、每日上限 |
| 调用流水 | POST /open/v1/calls | 最近调用记录(查询对象已脱敏) |
当前开放了哪些工具、每个要传什么参数,以 /open/v1/tools 的返回为准 —— 平台上下架工具会跟着变,文档不重复列,免得写的和实际能调的对不上。 网页版清单见 接口清单。
四、接口详情
1. 连通测试 /open/v1/ping
请求体:{}
{"code":0,"msg":"连通正常","data":{"channel":"某微擎站点","balance":"1000.00","server_time":"2026-09-22 10:00:00"}}
返回 401 一般是:AppSecret 填错、时间戳不对、或者 nonce 重复用了。
2. 工具清单 /open/v1/tools
请求体:{}
{"code":0,"msg":"ok","data":{"list":[
{"tool_code":"vehicle_five","name":"车辆五项查询","cost":"6.00","retail_tip":"8.00",
"params":["vin"],"async":false,"custom":false}
]}}
| 字段 | 含义 |
|---|---|
tool_code | 工具码,调 /query 时传它 |
name | 中文名,给终端用户看的话可以直接用 |
cost | 每次调用扣你的钱(这就是你的进货价) |
retail_tip | 平台建议零售价,仅供参考,卖多少你自己定 |
params | 这个工具要传哪些参数,见下方「参数说明」 |
async | true = 上游要等一会儿,出结果要用 /fetch 取 |
custom | true = 这是平台单独给你的协议价(不是默认价) |
3. 发起查询 /open/v1/query
{"tool":"vehicle_five","params":{"vin":"LSVAM4185E2109876"}}
params 里的键就是 /tools 返回的 params;车架号请传 17 位完整值,程序会自动转大写、去空格。
返回:
{"code":0,"msg":"ok","data":{
"status":"done","tool":"vehicle_five","tool_name":"车辆五项查询",
"fee":"6.00","refunded":"0.00","balance":"994.00","request_id":"",
"msg":"ok",
"data":{"品牌":"大众","车架号":"LSVAM4185E2109876","车型":"...","发动机号":"...","燃料种类":"汽油"}
}}
data.status 有三种,必须按它分支:
| status | 含义 | 钱怎么算 | 你要做什么 |
|---|---|---|---|
done | 查到数据了,data.data 就是结果 | 扣费 | 存下来给用户看 |
nodata | 这台车查不到数据(老车 / 未收录) | 已自动全额退回 | 提示用户「查无数据」,别收用户的钱 |
pending | 上游还在出结果 | 先扣 | 拿 request_id 调 /fetch,出不来结果会退 |
nodata 示例:
{"code":0,"msg":"查无数据","data":{"status":"nodata","fee":"6.00","refunded":"6.00","balance":"1000.00","data":{}}}
工具不存在、参数缺失、余额不够时返回 code 非 0,data 为 null,具体见「错误码」。
4. 取异步结果 /open/v1/fetch
{"request_id":"1276874656273911808"}
返回结构和 /query 一样,status 可能是 pending / done / nodata / fail。 建议 每 5~10 秒取一次,最多取 3~5 分钟,别拿 1 秒的间隔去轮询。
/fetch 是只读接口,反复取不会重复扣费,也不会重复退款:
| 情况 | 返回 | 说明 |
|---|---|---|
| 上游还在出结果 | pending | 继续等 |
| 上游偶发 5xx / 连不上 | pending | 不是失败,过会儿再取 |
| 出结果了 | done + 数据 | 结果存在平台,再取一次原样返回 |
| 上游超过有效期(一般 60 分钟)还没出结果 | fail + 全额退款 | 之后一直回 fail,不会重复退 |
| 已经结束的单子再取 | 原样回最终状态 | 不报错、不动钱,免得你的订单一直卡在「等待中」 |
5. 查余额 /open/v1/balance
{"code":0,"msg":"ok","data":{"balance":"994.00","today_used":12,"daily_limit":0}}
balance:可用余额(元,字符串)today_used:今天已调用次数daily_limit:每日上限,0表示不限;到上限后/query返回429
6. 调用流水 /open/v1/calls
{"page":1,"size":20}
返回:
{"code":0,"msg":"ok","data":{"page":1,"size":20,"total":1,"list":[
{"id":1,"tool_code":"vehicle_five","query":"LSV**********76","fee":"6.00","refund":"0.00",
"status":1,"status_text":"成功","msg":"ok","ms":640,"created_at":"2026-09-22 10:00:00"}
]}}
size最大 100status:0处理中 /1成功 /2失败 /3已退款 /4等上游出结果query是脱敏后的查询对象,完整车架号不返回给渠道
五、参数说明
工具要传哪些参数由 /tools 的 params 决定,常用的有:
| 参数名 | 含义 | 说明 |
|---|---|---|
vin | 车架号 | 17 位,不含字母 I O Q;传进来会转大写、去空格和横杠 |
plate | 车牌号 | 例如 京A12345,会转大写、去空格 |
engine | 发动机号 | 少数工具(如 4S 维保)需要 |
mobile | 手机号 | 手机号在网状态这类工具用 |
url_image | 图片地址 | 行驶证识别这类工具用,传可公网访问的图片 URL |
invoice_no | 发票号码 | 发票查验用,必填 |
invoice_date | 开票日期 | 发票查验用,必填,格式 2020-01-01 |
invoice_code | 发票代码 | 发票查验用,选填(全电发票没有代码,不传即可) |
invoice_amount | 价税合计 | 发票查验用,选填 |
invoice_check | 校验码 | 发票查验用,选填。传完整 20 位也行,程序会自动只取后六位 |
invoice_file | 发票文件 | PDF/OFD 查验用,传文件内容的 base64 |
传少了会返回 400 缺少参数 xxx;车架号格式不对会返回 400 车架号格式不正确。
「选填」的参数,工具定义里标了_optional,不传不会报 400。 发票查验的规则是:金额、价税合计、校验码三者至少填一个,都不填上游会回合计金额、价税合计和校验码都为空,这种情况平台按查无数据处理、自动退款。
六、错误码
判断成功只看 code,0 才算成功。
| code | 含义 | 怎么处理 |
|---|---|---|
| 0 | 成功 | 按 data.status 分支 |
| 400 | 参数错误(缺参数、车架号格式不对) | 检查 tool 和 params,别拿原参数重试 |
| 401 | 签名 / 时间戳 / nonce 有问题 | 核对 AppSecret、服务器时间;nonce 要每次新生成 |
| 402 | 余额不足 | 去代理后台充值;充值到账后恢复 |
| 403 | 渠道被停用 | 联系平台 |
| 404 | 工具不存在或已下架、request_id 找不到 | 重新拉 /tools 再调 |
| 405 | 用了 GET | 改 POST |
| 429 | 超过每日调用上限 | 明天再试或找平台提额 |
| 500 | 服务器或上游异常 | 稍后重试;这一笔已扣的钱会自动退回 |
七、计费与退款
- 先扣后调:发起查询时按工具
cost从余额扣,余额不够直接返回402,不会去调上游 - 查不到就退:
nodata/fail全额退回,余额明细里有一笔「退款」 - 异步只扣一次:
pending时扣一次,之后/fetch不再扣钱;最终没出结果也退 - 不会重复扣费、也不会重复退款:取结果是只读的,平台侧做了幂等
- 余额、扣费、退款都能在
/open/v1/balance、/open/v1/calls和后台「余额明细」里对上
八、接入示例
PHP
<?php
$appKey = '你的 AppKey';
$appSecret = '你的 AppSecret';
$base = 'https://www.ttzy.net';
function ocCall(string $path, array $payload, string $appKey, string $appSecret, string $base): array
{
// 注意:签名用的一定是「将要发出去的这串字节」,别在签名后又改 body
$body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(8));
$sign = md5($appKey . $timestamp . $nonce . md5($body) . $appSecret);
$ch = curl_init($base . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-AppId: ' . $appKey,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Sign: ' . $sign,
],
]);
$raw = curl_exec($ch);
curl_close($ch);
return json_decode((string) $raw, true) ?: ['code' => -1, 'msg' => '响应不是 JSON'];
}
// 1) 连通
var_dump(ocCall('/open/v1/ping', [], $appKey, $appSecret, $base));
// 2) 查一台车
$res = ocCall('/open/v1/query', [
'tool' => 'vehicle_five',
'params' => ['vin' => 'LSVAM4185E2109876'],
], $appKey, $appSecret, $base);
if (($res['code'] ?? -1) === 0 && $res['data']['status'] === 'pending') {
// 3) 异步:过 5~10 秒再来取
$one = ocCall('/open/v1/fetch', ['request_id' => $res['data']['request_id']], $appKey, $appSecret, $base);
}
curl(排障用)
签名要算,所以先用 PHP/脚本算好再填进来:
curl -s -X POST 'https://www.ttzy.net/open/v1/ping' \
-H 'Content-Type: application/json' \
-H 'X-AppId: 你的AppKey' \
-H 'X-Timestamp: 1758500000' \
-H 'X-Nonce: 3f9a1c7b2e8d4056' \
-H 'X-Sign: 算出来的签名' \
-d '{}'
九、常见问题
Q:一直返回 401 签名不正确? 按顺序查:AppSecret 有没有多余空格 → 时间戳是不是秒 → nonce 是不是这次新生成的 → 签名的 body 和实际发出去的 body 是不是同一串字节(用 JSON 库重新序列化过就不一样了)。
Q:pending 一直不出结果怎么办? /fetch 每 5~10 秒取一次,超过工具有效期(一般 60 分钟)平台会自动判失败并退款, 你不用自己处理超时。
Q:nodata 是不是代表接口坏了? 不是。10 年以上的老车、进口车、信息没收录的车都会 nodata,平台不收费,钱已退回。
Q:能不能把车架号完整存到自己库里给用户看? 可以,你查出来的结果归你。但平台侧给渠道的接口和后台一律脱敏, 不存在「平台的接口把你客户的车架号泄出去」这件事。
Q:一次请求能查多台车吗? 不能。一个 request_id / 一次调用对应一台车,批量请自己循环,注意限流和余额。
Q:接口地址能换吗? 不能。模块里默认写的就是 https://www.ttzy.net,改了就调不通。
平台开通代理后可自助查看:接口凭据、接口目录(含你的进货价)、余额明细、调用记录、充值。 文档正文只有这一份,网页版(/docs/)和这里的内容永远一致。