全量发改委油价查询 API 实战:一次拉回全国省市区六种油品价格
要在本地建一份成品油价格库,最笨的办法是逐个省去查:30 多个省 × 6 种油品,几十上百次请求,还得自己处理「这个省是统一价还是按市定价」。本文介绍一个全量拉取接口:一次 GET 请求、零业务参数,把系统中当前生效的整批价格(省 / 市 / 区县 + 0#柴油、-10#柴油、-35#柴油、92#、95#、98# 汽油)一次性返回,适合做本地缓存、离线对账与批量核算。
api.xujian.techxujian_cq
一、为什么需要「全量」而不是「逐个查」
| 场景 | 逐个查的问题 | 全量接口的做法 |
|---|---|---|
| 建本地价格库 | 30+ 省 × 6 油品 = 上百次请求,初始化慢且容易漏 | 一次请求拿全批 |
| 批量成本核算 | 每单查一次,调用量与延迟都放大 | 拉一次进内存,本地匹配 |
| 数据对账 | 难以确认「我本地那份是不是最新版」 | 用返回的effectiveDate做版本比对 |
| 地区粒度不确定 | 不知道哪些省按市定价、哪些区县单独定价 | 结果里直接体现(city/district是否为null) |
代价也很直接:这是一次返回全部数据的高单价接口,不适合放在用户请求的链路上高频调用。正确的用法是「低频拉取 + 本地缓存」。
二、接口能力概览
2.1 接口基础信息
| 项目 | 说明 |
|---|---|
| 接口地址 | https://api.xujian.tech/openapi/oilprice/all |
| 接口编码 | oilprice.all |
| 请求方式 | GET |
| 鉴权方式 | 请求头X-API-Key,不做签名、时间戳或加密 |
| 返回格式 | JSON,Content-Type: application/json;charset=UTF-8 |
| 单次费用 | 5 元/次(按次计费) |
| 是否需要业务入参 | 否,无任何查询参数 |
| 返回条数 | 当前生效批次的全部记录,条数取决于库内维护粒度 |
| 数据来源 | 国家发改委公布的成品油最高零售价,由平台运营在调价当日维护入库 |
| 更新频率 | 国家发改委约每 10 个工作日调价一次(实际以返回体dataUpdateTime为准) |
| 在线文档 | https://api.xujian.tech/api/oilprice-all |
价格说明:5 元/次取自本项目的接口初始化配置(后台「接口管理」可随时调价),实际单价请以开发者控制台与在线文档页的显示为准。
2.2 请求参数
请求头:
| 参数名 | 必填 | 说明 |
|---|---|---|
X-API-Key | 是 | 开发者 API Key,缺失或无效直接返回失败 |
查询参数:无。
接口故意不做分页与筛选:全量数据的定位就是「一次拉全、本地处理」,加分页反而容易漏数据。如果你只想查单个地区单油品,请用oilprice.realtime(0.01 元/次),不要浪费一次全量调用。
2.3 计费上需要提前知道的一点(重要)
这个接口是**「先鉴权扣费、再查数据」**的模式:
- API Key 有效、账号正常、接口启用、余额充足 →鉴权通过即扣一次 5 元;
- 由于没有业务参数,鉴权之后基本不会失败,即使系统当前没有维护任何价格(
total = 0),本次也已经扣费。
不扣费的场景只有:请求头缺失X-API-Key、API Key 无效或已停用、客户不存在或已停用、接口不存在或已停用、余额不足。
5 元/次不便宜,接入前建议先确认:
- 你的账户余额是否够(余额不足会直接返回失败,不扣费,不会半途扣一部分);
- 是否真的需要全量 —— 只查一个省的话,
oilprice.realtime便宜几个数量级; - 拉取频率是否有必要 —— 价格通常约 10 个工作日才变一次,每天拉一次已经很充裕。
2.4 数据量大概有多少
返回条数取决于平台当前的维护粒度,不是固定值:
| 维护粒度 | 大致条数 |
|---|---|
| 全省统一价(一个省一条) | 30+ 条 |
| 按地市维护 | 300+ 条量级 |
| 细化到区县 | 更多 |
这是量级估计,不是承诺值。请按total字段做动态处理,别在代码里写死条数;入库时也别假设「一个省只有一条记录」。
三、返回字段详解
3.1 顶层字段
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 0成功,非 0 失败(本接口失败为500) |
msg | String | 结果描述,成功为success,失败为具体原因 |
data | Object | 业务数据,失败时为null |
3.2 data 字段
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
effectiveDate | String | 2026-09-24 | 本批次价格生效日期yyyy-MM-dd;系统暂无数据时为null |
total | int | 312 | 本次返回的价格条数 |
list | Array | […] | 价格明细列表,按province_code→city_code→district_code升序 |
apiCode/apiName | String | oilprice.all / 全量发改委价格查询 | 接口编码与接口名称 |
chargeType | String | PER_CALL | 本次计费方式 |
balance | BigDecimal | 94.9900 | 本次扣费后的账户余额(元) |
costMs | long | 12 | 服务端处理耗时(毫秒,不含公网传输时间) |
3.3 list[] 明细字段
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
effectiveDate | String | 2026-09-24 | 该行价格的生效日期 |
province/provinceCode | String | 浙江省 / 330000 | 省名称与 6 位 adcode |
city/cityCode | String | 杭州市 / 330100 | 地市名称与代码;null表示全省统一价 |
district/districtCode | String | null | 区县名称与代码;null表示全市统一价 |
priceDiesel0 | BigDecimal | 7.25 | 0#柴油价格(元) |
priceDiesel10 | BigDecimal | 7.69 | -10#柴油价格(元) |
priceDiesel35 | BigDecimal | null | -35#柴油价格(元),未维护时为null |
priceGas92 | BigDecimal | 7.83 | 92#汽油价格(元) |
priceGas95 | BigDecimal | 8.28 | 95#汽油价格(元) |
priceGas98 | BigDecimal | 9.32 | 98#汽油价格(元) |
dataUpdateTime | String | 2026-09-24 09:00:00 | 数据更新时间yyyy-MM-dd HH:mm:ss |
与单油品接口的重要差别:全量接口的油品价格可能是
null(该地区未维护该油品,例如南方很多地区不供应 -35#柴油)。处理时请按「无价」处理,不要当成 0。而oilprice.realtime遇到这种情况会直接报错,不会返回null价格。
四、调用示例
4.1 curl
curl-s"https://api.xujian.tech/openapi/oilprice/all"\-H"X-API-Key: 你的APIKey"建议先这样手工跑一次,把返回的
total和effectiveDate看一眼,确认数据粒度符合预期,再写进定时任务。
4.2 Java(Hutool)
importcn.hutool.http.HttpRequest;importcn.hutool.json.JSONArray;importcn.hutool.json.JSONObject;importcn.hutool.json.JSONUtil;publicclassOilPriceAllClient{privatestaticfinalStringAPI_URL="https://api.xujian.tech/openapi/oilprice/all";/** * 拉取当前生效批次的全部发改委价格 * * @param apiKey 开发者 API Key * @return [生效日期, 明细数组];失败返回 null */publicstaticObject[]fetchAll(StringapiKey){JSONObjectjson=JSONUtil.parseObj(HttpRequest.get(API_URL).header("X-API-Key",apiKey).timeout(30000)// 全量数据建议放宽超时.execute().body());if(json.getInt("code")==null||json.getInt("code")!=0){System.out.println("拉取失败:"+json.getStr("msg"));returnnull;}JSONObjectdata=json.getJSONObject("data");JSONArraylist=data.getJSONArray("list");System.out.println("生效日期:"+data.getStr("effectiveDate")+",条数:"+data.getInt("total"));returnnewObject[]{data.getStr("effectiveDate"),list};}publicstaticvoidmain(String[]args){Object[]r=fetchAll("你的APIKey");if(r==null){return;}JSONArraylist=(JSONArray)r[1];for(inti=0;i<list.size();i++){JSONObjectrow=list.getJSONObject(i);// 注意:priceDiesel35 等字段可能为 null,取值前判空System.out.printf("%s %s 92#=%s 0#柴油=%s%n",row.getStr("province"),row.getStr("city"),row.getStr("priceGas92"),row.getStr("priceDiesel0"));}}}4.3 Python
importrequestsdeffetch_all(api_key:str)->dict:""" 拉取当前生效批次的全部发改委价格 Returns: dict: {"effectiveDate": str|None, "total": int, "list": [...]}; 失败返回 None """result=requests.get("https://api.xujian.tech/openapi/oilprice/all",headers={"X-API-Key":api_key},timeout=30,).json()ifresult.get("code")!=0:print("拉取失败:",result.get("msg"))returnNonedata=result["data"]print("生效日期:",data["effectiveDate"],"条数:",data["total"])returndatadefto_price_index(data:dict)->dict:"""把全量结果整理成 {(省, 市, 区): {油品: 价格}} 的本地索引"""FIELD={"0#柴油":"priceDiesel0","-10#柴油":"priceDiesel10","-35#柴油":"priceDiesel35","92#汽油":"priceGas92","95#汽油":"priceGas95","98#汽油":"priceGas98",}index={}forrowindata["list"]:# city / district 为 None 表示上一级统一价,用空串占位便于匹配key=(row["provinceCode"],row.get("cityCode")or"",row.get("districtCode")or"")prices={}forname,fieldinFIELD.items():value=row.get(field)ifvalueisnotNone:# 未维护的油品不写进索引prices[name]=float(value)index[key]={"effectiveDate":row["effectiveDate"],"prices":prices}returnindexif__name__=="__main__":data=fetch_all("你的APIKey")ifdata:idx=to_price_index(data)print("索引条数:",len(idx))4.4 JavaScript(Node 18+)
asyncfunctionfetchAll(apiKey){constres=awaitfetch("https://api.xujian.tech/openapi/oilprice/all",{headers:{"X-API-Key":apiKey},});const{code,msg,data}=awaitres.json();if(code!==0){console.warn("拉取失败:",msg);returnnull;}returndata;}// 按省分组,方便落库或渲染functiongroupByProvince(list){returnlist.reduce((acc,row)=>{(acc[row.province]||=[]).push(row);returnacc;},{});}五、返回示例
5.1 成功(code = 0)
{"code":0,"msg":"success","data":{"effectiveDate":"2026-09-24","total":2,"list":[{"effectiveDate":"2026-09-24","provinceCode":"500000","province":"重庆市","cityCode":null,"city":null,"districtCode":null,"district":null,"priceDiesel0":7.28,"priceDiesel10":7.72,"priceDiesel35":8.05,"priceGas92":7.86,"priceGas95":8.31,"priceGas98":9.36,"dataUpdateTime":"2026-09-24 09:00:00"},{"effectiveDate":"2026-09-24","provinceCode":"330000","province":"浙江省","cityCode":"330100","city":"杭州市","districtCode":null,"district":null,"priceDiesel0":7.25,"priceDiesel10":7.69,"priceDiesel35":null,"priceGas92":7.83,"priceGas95":8.28,"priceGas98":9.32,"dataUpdateTime":"2026-09-24 09:00:00"}],"apiCode":"oilprice.all","apiName":"全量发改委价格查询","chargeType":"PER_CALL","balance":94.9900,"costMs":12}}5.2 成功但系统暂无数据(仍然计费)
{"code":0,"msg":"success","data":{"effectiveDate":null,"total":0,"list":[],"apiCode":"oilprice.all","apiName":"全量发改委价格查询","chargeType":"PER_CALL","balance":94.9900,"costMs":5}}
code = 0但total = 0表示系统当前没有维护任何已生效的价格。此时不要把它当成「上次的价格仍然有效」,应保留本地缓存并稍后重试。
5.3 失败(余额不足,不扣费)
{"code":500,"msg":"余额不足,请先充值。","data":null}六、典型应用场景
6.1 建本地价格库(含版本控制)
用effectiveDate做版本号,避免重复入库与重复付费:
classPriceStore:"""本地价格库:只在生效批次变化时覆盖写入"""def__init__(self):self.version=Noneself.rows=[]defrefresh(self,api_key:str)->bool:data=fetch_all(api_key)ifnotdataordata["total"]==0:returnFalse# 保留旧数据,不覆盖ifdata["effectiveDate"]==self.version:returnFalse# 同一批次,无需写入self.version=data["effectiveDate"]self.rows=data["list"]returnTrue6.2 本地匹配查询(替代高频调用实时接口)
拉一次全量后,在内存里按「区县 → 市 → 省」回落匹配,命中不了再打实时接口:
deflookup(index:dict,province:str,city:str="",district:str="")->dict:"""按 区县 → 市 → 省 逐级回落查找本地价格"""forkeyin[(province,city,district),(province,city,""),(province,"","")]:ifkeyinindex:returnindex[key]returnNone这个回落顺序与实时接口内部的匹配规则一致,本地复现后行为可预期。
6.3 调价日排程(配合免费的调价周期接口)
免费的oilprice.cycle返回当年已登记的调价生效日期列表,用它决定何时拉全量:
fromdatetimeimportdatedefneed_refresh(cycle_dates:list[str],cached_version:str)->bool:today=date.today().isoformat()past=[dfordincycle_datesifd<=today]latest=max(past)ifpastelseNonereturnlatestisnotNoneandlatest!=cached_version这样一年只需要在调价日附近调用约 24 次,而不是每天一次。
6.4 批量对账:确认「我算的和基准价一致」
把一批历史单据的价格与本地库按effectiveDate对齐重算,可快速找出「用了旧价」的异常单据。这也是全量数据相比单条查询最有价值的用法。
七、提升可用性的几条实践建议
- 一定要缓存。价格通常约 10 个工作日才变一次,每天最多拉一次;配合免费的
oilprice.cycle能做到只在调价日拉。 - 别写死条数。返回条数取决于维护粒度,按
total动态处理。 - 油品价格可能是
null。按「无价」处理,不要当 0,否则成本会算成 0。 city/district为null是统一价标记,不是数据缺失。- 超时放宽到 20~30 秒。全量响应体比单条查询大,别用 5 秒超时。
- 失败时保留旧数据。拉取失败或
total = 0时不要清空本地库。 - 不要放在用户请求链路上。这是 5 元/次的重接口,应走后台定时任务,并对任务做幂等(同
effectiveDate不重复写)。 - 不要把它当「实时行情」。这是发改委公布的批次最高零售价,不是加油站挂牌价或成交价。
八、错误码与排查
| code | msg(示例) | 处理建议 |
|---|---|---|
| 0 | success | 调用成功;total为 0 表示系统暂未维护当前批次价格(仍计费) |
| 500 | 缺少请求头 X-API-Key | 在请求头补充X-API-Key(不扣费) |
| 500 | API Key 无效 / API Key 已停用 | 检查 Key 是否正确,或在控制台重新启用(不扣费) |
| 500 | 客户不存在或已停用 | 联系平台确认账号状态(不扣费) |
| 500 | 接口不存在或已停用 | 确认oilprice.all当前是否维护中(不扣费) |
| 500 | 余额不足,请先充值。可xujian_cq | 充值后重试;余额不足时不扣费。本接口单价较高,建议先确认余额 |
九、计费与接入
| 项目 | 说明 |
|---|---|
| 单价 | 5 元/次(取自接口初始化配置,后台可调,以控制台显示为准) |
| 计费方式 | 按次计费,调用前校验余额;行锁 + 条件式原子扣减,不会把余额扣成负数 |
| 计费时机 | 鉴权通过即扣费;total = 0的空结果也计费 |
| 不计费场景 | Key 缺失/无效、客户停用、接口停用、余额不足 |
| 建议频率 | 调价日拉取一次,其余时间用本地缓存 |
接入流程:注册开发者账号 → 控制台创建 API Key → 请求头带上X-API-Key即可调用,无需签名或加密。控制台可查看调用量、扣费流水与余额。
控制台与在线文档:
https://api.xujian.tech;接口接入、数据与充值相关问题可Vxujian_cq。
同系列接口:
oilprice.realtime(实时发改委价格查询):0.01 元/次,单地区单油品,适合高频小查询;oilprice.advance(提前查询发改委价格):按年付费,提前 2 小时拿即将生效的新价;oilprice.cycle(发改委调价周期查询):免费,用于决定何时拉全量。
十、总结
全量接口解决的是「一次性把基准价搬回本地」这件事:没有参数、没有分页、一次拿全批,配合effectiveDate做版本控制,就能搭出一份可离线使用的价格库。
几个关键取舍值得留意:
- 5 元/次,鉴权通过即扣费:只适合低频批量拉取,不适合高频调用,更不要放在用户请求链路上;
- 空结果也计费:系统暂无数据时返回
code = 0+total = 0,费用照扣,所以定时任务要判断total; - 油品价格可能是
null:与单油品接口的「查不到就报错」不同,全量接口用null表示未维护,解析时必须判空; - 条数不固定:取决于平台维护粒度,
total才是唯一可信的数字。