物流燃油成本测算 API:按线路查各省市当日柴油汽油指导价
物流报价、车队预算、工程用油核算,最后都会落到一句话:这趟活儿烧多少油、油价多少 。油价部分如果靠人工查表,跨省线路要一个省一个省翻,还经常翻到上一轮的价格。oilprice.realtime 把这件事做成一次 GET:按省 / 市 / 区县 + 油品名查询,返回当前生效的发改委价格,带生效日期与数据更新时间,可以直接进测算模型。
接口速览
| 关键事实 | 说明 |
|---|---|
| 接口地址 | https://api.xujian.tech/openapi/oilprice/realtime |
| 接口编码 | oilprice.realtime |
| 请求方式 | GET(参数放 Query String) |
| 鉴权方式 | 请求头 X-API-Key,不做签名、时间戳或加密 |
| 必填参数 | province(省名称或 6 位 adcode)、oilName(6 种油品之一) |
| 可选参数 | city、district(市 / 区县名称或 adcode) |
| 返回核心字段 | oilName / effectiveDate / price + 命中地区三级名称与代码 |
| 计费方式 | 按次计费,0.01 元/次,鉴权通过即扣费 |
| 计费时机 | 鉴权通过即扣费,之后的业务失败不退还 |
| 不计费场景 | Key 缺失 / 无效、客户停用、接口停用、余额不足 |
| 返回条数 | 固定 1 条(单地区单油品) |
| 数据来源 | 国家发改委公布的成品油最高零售价,运营在调价当日维护入库 |
| 更新频率 | 国家发改委约每 10 个工作日调价一次,实际以 dataUpdateTime 为准 |
| 单次耗时 | 服务端处理通常个位数毫秒,costMs 为真实耗时 |
一、哪些业务需要这一步
| 场景 | 具体用法 |
|---|---|
| 单趟燃油成本核算 | 里程 × 百公里油耗 × 当地柴油价,算出这趟活的油钱 |
| 物流报价单生成 | 报价单附带油价基准与生效日期,减少后续争议 |
| 车队月度预算 | 按常跑线路的各省市油价估算月度燃油预算 |
| 加油卡结算对账 | 按生效日期核对结算单价,避免跨调价日算错 |
| 工程机械台班成本 | 按项目所在地柴油价测算机械用油成本 |
| 加油站挂牌参考 | 以发改委最高零售价为基准制定挂牌与优惠策略 |
| 油价展示 | 物流 App、加油站小程序展示当地当日指导价 |
| 调价监控大屏 | 多省市价格集中展示,配合调价周期做变化追踪 |
| 补能与出行成本估算 | 按出发地 / 目的地油价估算自驾成本 |
| 保险理赔参考 | 燃油类损失按当地油价核定赔偿金额 |
二、请求参数
2.1 请求头
| 参数名 | 必填 | 说明 |
|---|---|---|
X-API-Key |
是 | 开发者 API Key,缺失或无效直接返回失败 |
2.2 查询参数
| 参数名 | 必填 | 类型 | 示例 | 说明 |
|---|---|---|---|---|
province |
是 | String | 重庆市 / 500000 | 省名称或 6 位 adcode |
oilName |
是 | String | 0#柴油 | 油品中文名,取值见下方白名单 |
city |
否 | String | 杭州市 / 330100 | 市名称或 6 位 adcode;不传按省级价格匹配 |
district |
否 | String | 西湖区 / 330106 | 区县名称或 6 位 adcode;不传按市级价格匹配 |
2.3 oilName 取值白名单(6 种,多一个字少一个字都不行)
0#柴油 -10#柴油 -35#柴油 92#汽油 95#汽油 98#汽油
2.4 匹配规则(两个容易踩的坑)
| 规则 | 说明 |
|---|---|
| 油品名比对 | 传入值先 trim() 再精确比对,比对失败直接返回错误,提示里会带上完整取值列表 |
| 地区匹配 | 传 6 位纯数字按 adcode 精确匹配;传中文按标准行政区划名称精确匹配(如 重庆市,不是「重庆」) |
| 不做模糊匹配 | 名称对不上不会模糊兜底,会一路回落到「查不到」并报错,推荐直接传 adcode |
2.5 地区回落顺序
接口先取「当前生效批次」(生效日期 <= 今天 的最新一批),再在该批次内按三级地区匹配:
- 省 + 市 + 区完全命中 → 返回该区县的价;
- 区县没单独定价 → 回落到该市统一价 (
district为null); - 该市也没统一价 → 回落到该省统一价 (
city、district均为null); - 该省按区县分别定价 → 返回该市下辖第一条区县记录;
- 兜底 → 返回该省第一条记录。
只传
province不传city时要小心 :如果该省按市 / 区县分别定价,返回的是该省排序后的第一条记录,不等于省会城市的价格,也不等于该省均价 。要准确就传全city。
三、返回字段
3.1 顶层与 data
| 字段 | 类型 | 说明 |
|---|---|---|
code |
int | 0 成功,非 0 失败(统一为 500) |
msg |
String | 成功为 success,失败为具体原因 |
data |
Object | 业务数据,失败时为 null |
data 字段:
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
oilName |
String | 0#柴油 | 商品名,与入参一致 |
effectiveDate |
String | 2026-09-24 | 价格生效日期 yyyy-MM-dd,当天 0 点起生效 |
price |
BigDecimal | 7.28 | 价格(元) |
province |
String | 重庆市 | 命中记录的省名称 |
provinceCode |
String | 500000 | 省级 6 位 adcode |
city |
String | null | 地市名称;null 表示该省是全省统一价 |
cityCode |
String | null | 地市代码 |
district |
String | null | 区县名称;null 表示该市是全市统一价 |
districtCode |
String | null | 区县代码 |
dataUpdateTime |
String | 2026-09-24 09:00:00 | 数据更新时间 yyyy-MM-dd HH:mm:ss |
apiCode |
String | oilprice.realtime | 接口编码 |
apiName |
String | 实时发改委价格查询 | 接口名称 |
chargeType |
String | PER_CALL | 本次计费方式 |
balance |
BigDecimal | 99.9900 | 扣费后的账户余额(元) |
costMs |
long | 6 | 服务端处理耗时(毫秒,不含公网传输时间) |
3.2 三个字段口径
price不会是null。某油品没维护价格时,接口直接返回code=500,而不是返回price=null。city/district为null是正常结果,表示用了上一级统一价,展示时可写成「全省统一价」「全市统一价」。effectiveDate与dataUpdateTime一起存。前者判断价格属于哪一批,后者判断数据新旧,比自己记「上次什么时候拉过」可靠。
四、调用示例
4.1 curl
bash
curl -s -G "https://api.xujian.tech/openapi/oilprice/realtime" \
--data-urlencode "province=500000" \
--data-urlencode "oilName=0#柴油" \
-H "X-API-Key: 你的APIKey"
4.2 Java(Hutool)
java
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
public class OilPriceRealtimeClient {
private static final String API_URL = "https://api.xujian.tech/openapi/oilprice/realtime";
/**
* 查询某地区某油品的当前生效价格
*
* @param apiKey 开发者 API Key
* @param province 省名称或 6 位 adcode
* @param oilName 油品名,如 0#柴油 / 92#汽油
* @param city 市名称或 adcode,可为空
* @return 价格(元);失败返回 null
*/
public static java.math.BigDecimal price(String apiKey, String province,
String oilName, String city) {
HttpRequest req = HttpRequest.get(API_URL)
.header("X-API-Key", apiKey)
.form("province", province)
.form("oilName", oilName)
.timeout(10000);
if (city != null && !city.isEmpty()) {
req.form("city", city);
}
JSONObject json = JSONUtil.parseObj(req.execute().body());
if (json.getInt("code") == null || json.getInt("code") != 0) {
System.out.println("查询失败:" + json.getStr("msg"));
return null;
}
return json.getJSONObject("data").getBigDecimal("price");
}
public static void main(String[] args) {
System.out.println(price("你的APIKey", "500000", "0#柴油", null));
}
}
4.3 Python
python
import requests
def oil_price(api_key: str, province: str, oil_name: str, city: str = None):
"""返回 (price, effective_date);失败返回 None"""
params = {"province": province, "oilName": oil_name}
if city:
params["city"] = city
resp = requests.get(
"https://api.xujian.tech/openapi/oilprice/realtime",
params=params,
headers={"X-API-Key": api_key},
timeout=10,
)
result = resp.json()
if result.get("code") != 0:
print("查询失败:", result.get("msg"))
return None
return result["data"]["price"], result["data"]["effectiveDate"]
if __name__ == "__main__":
print(oil_price("你的APIKey", "500000", "0#柴油"))
4.4 JavaScript
javascript
async function oilPrice(apiKey, province, oilName, city) {
const params = new URLSearchParams({ province, oilName });
if (city) params.set("city", city);
const resp = await fetch(
`https://api.xujian.tech/openapi/oilprice/realtime?${params}`,
{ headers: { "X-API-Key": apiKey } }
);
const result = await resp.json();
if (result.code !== 0) {
throw new Error(result.msg);
}
return result.data;
}
五、返回示例
全省统一价:
json
{
"code": 0,
"msg": "success",
"data": {
"oilName": "0#柴油",
"effectiveDate": "2026-09-24",
"price": 7.28,
"province": "重庆市",
"provinceCode": "500000",
"city": null,
"cityCode": null,
"district": null,
"districtCode": null,
"dataUpdateTime": "2026-09-24 09:00:00",
"apiCode": "oilprice.realtime",
"apiName": "实时发改委价格查询",
"chargeType": "PER_CALL",
"balance": 99.9900,
"costMs": 6
}
}
按市定价(区县回落到市级统一价):
json
{
"code": 0,
"msg": "success",
"data": {
"oilName": "95#汽油",
"effectiveDate": "2026-09-24",
"price": 8.28,
"province": "浙江省",
"provinceCode": "330000",
"city": "杭州市",
"cityCode": "330100",
"district": null,
"districtCode": null,
"dataUpdateTime": "2026-09-24 09:00:00",
"apiCode": "oilprice.realtime",
"apiName": "实时发改委价格查询",
"chargeType": "PER_CALL",
"balance": 99.9800,
"costMs": 9
}
}
失败示例:
json
{
"code": 500,
"msg": "未查询到该地区的发改委价格",
"data": null
}
json
{
"code": 500,
"msg": "oilName 取值只能是:0#柴油 / -10#柴油 / -35#柴油 / 92#汽油 / 95#汽油 / 98#汽油",
"data": null
}
六、可直接复用的两段代码
6.1 单趟运输燃油成本
python
def trip_fuel_cost(api_key: str, distance_km: float, l_per_100km: float,
provinces: list, oil_name: str = "0#柴油"):
"""多段线路按各段所在省油价分别计算,返回总油费与明细"""
total, detail = 0.0, []
for prov in provinces:
hit = oil_price(api_key, prov, oil_name)
if not hit:
continue
price, eff = hit
seg = distance_km / len(provinces) / 100 * l_per_100km * float(price)
total += seg
detail.append({"province": prov, "price": price,
"effective_date": eff, "cost": round(seg, 2)})
return round(total, 2), detail
6.2 缓存到「批次」而不是「时间」
python
import json
import os
CACHE = "oil_price_cache.json"
def cached_price(api_key: str, province: str, oil_name: str):
"""同一个 (地区, 油品, 生效日期) 只查一次"""
cache = {}
if os.path.exists(CACHE):
cache = json.load(open(CACHE, encoding="utf-8"))
key = f"{province}|{oil_name}"
hit = oil_price(api_key, province, oil_name)
if not hit:
return cache.get(key)
price, eff = hit
if cache.get(key, {}).get("effective_date") == eff and "price" in cache[key]:
return cache[key]["price"]
cache[key] = {"price": float(price), "effective_date": eff}
json.dump(cache, open(CACHE, "w", encoding="utf-8"), ensure_ascii=False)
return price
七、实践建议
- 用 adcode 而不是中文名 。名称必须完全匹配标准行政区划名(如
重庆市),传简称会查不到;adcode 更稳。 - 传全
city。只传省时可能返回该省第一条记录,不等于省会价也不等于均价。 - 按批次缓存 。以
effectiveDate为缓存键,同批次不重复调用,比按「24 小时过期」更准确。 - 跨调价日要按日期归属。价格当天 0 点生效,凌晨的交易要用新价,不能按查询时间判断。
- 注意计费时机 。本接口鉴权通过即扣费 ,参数写错、地区查不到也已经计费,调用前先校验
oilName与地区值。 price不会是 null。查不到是报错,不是返回空值,解析时不需要额外判空。- 不要用它做全国批量 。几百个地区逐个查不划算,批量场景用
oilprice.all(5 元/次,一次拉全)。 - 配合调价周期排程 。
oilprice.cycle免费,用它知道什么时候该刷新价格。
八、错误码与排查
| code | msg | 是否扣费 |
|---|---|---|
| 0 | success | 已扣费 |
| 500 | 缺少请求头 X-API-Key | 否 |
| 500 | API Key 无效 / API Key 已停用 | 否 |
| 500 | 客户不存在或已停用 | 否 |
| 500 | 接口不存在或已停用 | 否 |
| 500 | 余额不足,请先充值 | 否 |
| 500 | province 不能为空 / oilName 不能为空 | 是(鉴权已通过) |
| 500 | oilName 取值只能是:0#柴油 / -10#柴油 / -35#柴油 / 92#汽油 / 95#汽油 / 98#汽油 | 是 |
| 500 | 未查询到该地区的发改委价格 | 是 |
| 500 | 未查询到该地区「xx」的发改委价格 | 是 |
这一条要特别留意:本接口与企业查询、地址解析那类「查不到不收费」的接口不同,鉴权通过后即使业务失败也不退还。调用前把参数校验做在本地,能省掉不少无效扣费。
九、计费与接入
| 项目 | 说明 |
|---|---|
| 单价 | 0.01 元/次 |
| 计费方式 | 按次计费,调用前校验余额;采用行锁 + 条件式原子扣减,不会把余额扣成负数 |
| 计费时机 | 鉴权通过即扣费,之后的业务失败不退还 |
| 不计费场景 | Key 缺失 / 无效、客户停用、接口停用、余额不足 |
| 返回条数 | 固定 1 条(单地区单油品) |
接入流程:注册开发者账号 → 控制台创建 API Key → 请求头带上 X-API-Key 即可调用,无需签名或加密。控制台可查看调用量、扣费流水与余额。
服务站点:
api.xujian.tech(纯文本域名,不做跳转)。接口试用、数据与充值咨询可在控制台提交工单,或联系 Vxujian_cq。
十、小结
对物流、车队、工程用油这类场景来说,油价数据的难点不是「查到」,而是查得准、知道它属于哪一批、并且别在错误的时候刷新 。oilprice.realtime 一次一分钱,把这三个问题都解决了:
- 价格精确到市 / 区县 ,并且明确告诉你用了哪一级统一价(
city/district为null); - 带生效日期与更新时间,可以按批次缓存、按日期归属;
- 固定返回 1 条 ,解析简单,
price不会是 null。
四个配套接口按需选用:oilprice.all(5 元/次,一次拉全国)、oilprice.cycle(免费,调价日期列表)、oilprice.advance(按年付费,提前 2 小时拿新价)。