开放数据接口
XZPhotos 是航空摄影爱好者社区。我们把社区多年整理的民航注册号资料、机场 / 拍摄地点数据和摄影师拍摄的航空器图片以 HTTP 接口形式向合作方开放,返回 JSON,按次计费,接入需事先授权。
https://api.xzphotos.cn/api/v1。所有接口均为 GET,响应 application/json; charset=utf-8,一律不缓存。快速开始
- 向我们申请接入,获得
SecretId与SecretKey(密钥仅展示一次,请妥善保存)。 - 每次请求携带四个请求头:
X-SECRET-ID、X-TIMESTAMP(Unix 秒)、X-NONCE(随机串)、X-SIGNATURE(按下文算法计算)。 - 调用接口。响应头
X-Request-Cost为本次扣费(分),X-Account-Balance为扣费后余额(分)。
性能建议:复用连接
接口不可缓存,每次调用都要经 CDN 边缘回到源站。实测同一条 HTTP/2 连接上连续调用,大陆约 60 毫秒一次;每次新建 HTTPS 连接则要 150 到 280 毫秒,港澳与海外更明显。请让 HTTP 客户端保持长连接,不要每次调用都新建连接:
- Python:用
requests.Session()或httpx.Client(http2=True)复用同一个实例发请求。 - Node.js:用
http2.connect()保持会话,或给fetch/axios配置keepAlive: true的 Agent。 - Java / Go / PHP:默认的连接池即可,避免每次调用都创建新的客户端对象。
- curl 调试时把多次请求放在同一条命令里即可复用连接。
签名中的时间戳与 nonce 每次请求都必须重新生成,与连接是否复用无关。
身份签名
签名对象是查询参数加上 timestamp 与 nonce 两项,按键名升序排列后以 key=value&key=value 拼接,用 SecretKey 做 HMAC-SHA256,十六进制小写。时间戳与服务器相差不得超过 5 分钟;同一 nonce 只能使用一次。
# 以查询 B-2001 为例(无查询参数时只签 timestamp 与 nonce)
SID=你的SecretId; KEY=你的SecretKey
TS=$(date +%s); NONCE=$(openssl rand -hex 8)
SIG=$(printf 'nonce=%s×tamp=%s' "$NONCE" "$TS" | openssl dgst -sha256 -hmac "$KEY" | awk '{print $2}')
curl "https://api.xzphotos.cn/api/v1/aircraft/B-2001" \
-H "X-SECRET-ID: $SID" -H "X-TIMESTAMP: $TS" -H "X-NONCE: $NONCE" -H "X-SIGNATURE: $SIG"import time, hmac, hashlib, secrets, requests
SID, KEY = "你的SecretId", "你的SecretKey"
def call(path, params=None):
params = dict(params or {})
ts, nonce = str(int(time.time())), secrets.token_hex(8)
signed = dict(params, timestamp=ts, nonce=nonce)
s = "&".join(f"{k}={signed[k]}" for k in sorted(signed))
sig = hmac.new(KEY.encode(), s.encode(), hashlib.sha256).hexdigest()
r = requests.get("https://api.xzphotos.cn/api/v1" + path, params=params,
headers={"X-SECRET-ID": SID, "X-TIMESTAMP": ts, "X-NONCE": nonce, "X-SIGNATURE": sig})
print(r.status_code, r.headers.get("X-Request-Cost"), r.headers.get("X-Account-Balance"))
return r.json()
print(call("/aircraft/B-2001"))
print(call("/airports", {"q": "上海", "limit": 5}))const crypto = require("crypto");
const SID = "你的SecretId", KEY = "你的SecretKey";
async function call(path, params = {}) {
const ts = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(8).toString("hex");
const signed = { ...params, timestamp: ts, nonce };
const s = Object.keys(signed).sort().map(k => `${k}=${signed[k]}`).join("&");
const sig = crypto.createHmac("sha256", KEY).update(s).digest("hex");
const url = "https://api.xzphotos.cn/api/v1" + path + (Object.keys(params).length ? "?" + new URLSearchParams(params) : "");
const r = await fetch(url, { headers: { "X-SECRET-ID": SID, "X-TIMESTAMP": ts, "X-NONCE": nonce, "X-SIGNATURE": sig } });
console.log(r.status, r.headers.get("x-request-cost"), r.headers.get("x-account-balance"));
return r.json();
}
call("/aircraft/B-2001").then(console.log);limit=5&nonce=…&q=上海×tamp=…接口目录
注册号资料
/aircraft/{registration}按次计费按注册号精确查询,如 /aircraft/B-2001。返回机型、系列、分类、制造商、发动机、航司(含 IATA/ICAO)、国家、序列号、座位布局、交付日期、运营状态、特殊涂装。查无此机号返回 404,不计费。
{
"success": true,
"data": {
"registration": "B-2001", "serial_number": "1234",
"aircraft": { "model": "Boeing 787-9", "series": "787", "classification": "宽体机", "manufacturer": "Boeing", "engine": "GEnx-1B" },
"airline": { "id": 12, "name": "China Southern", "name_cn": "中国南方航空", "iata": "CZ", "icao": "CSN" },
"country": "中国",
"seats": { "total": 293, "first": 0, "business": 28, "premium_economy": 0, "economy": 265 },
"operation_status": "运营中", "delivery_date": "2018-05-20",
"special_livery": { "is_painted": false, "name": null },
"is_active": true, "updated_at": "2026-08-31 10:12:03"
}
}
/aircraft按次计费| 参数 | 说明 |
|---|---|
q | 关键字:注册号 / 序列号前缀,或机型 / 航司名包含匹配;至少 2 个字符 |
airline_id | 航司 ID |
type | 机型包含匹配,如 A350 |
country / status | 国家 / 运营状态精确匹配 |
page / limit | 分页,limit 最大 100,默认 20 |
以上条件至少提供一个。返回 data.items[] 与 data.pagination。
航空器图片
/aircraft/{registration}/photos按次计费按注册号返回本站已通过审核的航空器图片,如 /aircraft/B-2001/photos。图片为带 XZPhotos 水印的展示图和缩略图,不提供原图。一次调用返回一页(最多 20 张),计一次;该注册号没有图片返回 404,不计费。付费各档可用,免费版套餐不含此接口;个人、教育和非营利用途可申请免费使用,见这里。
| 参数 | 说明 |
|---|---|
page / limit | 分页,limit 默认 10、最大 20 |
sort | latest(默认,最新上传在前)或 popular(浏览最多在前) |
{
"success": true,
"data": {
"registration": "B-2001",
"items": [{
"id": 400001, "title": "中国南方航空 B-2001 降落广州白云",
"photographer": { "name": "spotter01", "profile_url": "https://xzphotos.cn/gallery/user-profile.php?id=10001" },
"airline": "中国南方航空", "aircraft": "Boeing 787-9",
"location": { "name": "广州白云国际机场", "iata": "CAN", "icao": "ZGGG" },
"taken_at": "2026-08-30", "uploaded_at": "2026-08-31 20:15:02",
"width": 5000, "height": 3333, "views": 1520, "likes": 36, "is_featured": false,
"image_url": "https://images.xzphotos.net/uploads/watermarked/…jpg",
"thumbnail_url": "https://images.xzphotos.net/uploads/thumbnails/…jpg",
"page_url": "https://xzphotos.cn/gallery/image.php?id=400001"
}],
"pagination": { "total": 57, "page": 1, "limit": 10, "pages": 6 },
"attribution": "图片版权归摄影师所有。展示时请在图片旁注明摄影师和「XZPhotos」,并链接到 page_url;不得去除或遮挡水印,不得二次分发。"
}
}
page_url;不得去除、裁掉或遮挡水印;不得把图片另存后二次分发、转售,或用于训练 AI 模型。width / height 为原图像素尺寸,可用来计算宽高比。机场 / 拍摄地点
/airports/{code}按次计费3 位 IATA 或 4 位 ICAO,大小写不敏感,如 /airports/PEK、/airports/ZBAA。返回中英文名、城市、地区、国家、坐标、时区、类型、站内图片数。
/airports按次计费| 参数 | 说明 |
|---|---|
q | IATA / ICAO 精确,或中英文名 / 城市包含匹配;至少 2 个字符 |
country | 国家精确匹配 |
type | international / domestic / regional / military / private / other |
page / limit | 分页,limit 最大 100 |
账户
/account免费当前密钥的余额、生效单价、今日用量、到期时间。
/account/ledger免费最近流水(limit 默认 50,最多 200):充值、扣费、退款、调整。
图片接口(合作方专用)
航空器精选图片、机场场景图片、餐食图片等接口按合作协议单独开通,路径与字段以接入时提供的说明为准;未开通的密钥调用返回 403。按注册号取带水印的图片,请用上面的 航空器图片 接口。
套餐与计费
按需查询、灵活付费。所有套餐均需审核开通,升级同样经审核。按「次」计费:一次注册号查询计一次;查无此机号(404)与非 2xx 响应不收费,但计入免费版的每日次数。
| 套餐 | 单价 | 返回内容 | 限额 |
|---|---|---|---|
| 免费版 | 0 | 注册号、机型(型号 / 系列 / 制造商)、航空公司 | 每日 800 次;仅限单个注册号查询;实行至 2026-12-31,后续待定 |
| 0.05 元档 | 0.05 元 / 次 | 上一档 + 序列号、国家、发动机、交付日期、运营状态、特殊涂装 | 默认频率限制 |
| 0.08 元档 | 0.08 元 / 次 | 上一档 + 舱位数据(头等 / 公务 / 超经 / 经济 / 总座位数) | 默认频率限制 |
| 0.10 元档 | 0.10 元 / 次 | 上一档 + 舱位图(带水印,返回 24 小时有效的图片链接,过期后重新查询即可) | 默认频率限制 |
GET /aircraft?q=)、机场查询和航空器图片(GET /aircraft/{registration}/photos,一页计一次)按套餐同一单价计费;免费版只能查询单个注册号。未指定套餐的既有合作方按原约定计费、返回全部字段。个人、教育与非营利用途免费
现阶段,XZPhotos 面向个人、教育和非营利用途免费开放航空器图片接口(GET /aircraft/{registration}/photos)。申请接入时请在用途说明中写明项目性质,审核通过后免费开通。使用时仍须遵守图片使用要求:注明摄影师和「XZPhotos」并链接到图片页,不得去除水印,不得二次分发。
用于商业产品、商业推广或有广告收入的项目,按上方套餐计费。本政策可能根据使用情况调整,调整前会提前通知已接入的合作方。
- 预付费:由我们为您的账户充值,余额以「分」计。每次调用前先按单价预扣,余额不足返回 402,不执行查询。
- 只对成功扣费:响应非 2xx(包括查无数据的 404)时预扣金额原路退回,流水中可见 refund 记录。
- 默认频率限制:每分钟 120 次、每小时 1,200 次、每日 12,000 次;超出返回 429,不计费。免费版另有每日 800 次上限。
- IP 白名单:授权时可绑定出口 IP;非白名单来源返回 403。出口 IP 变更请提前告知。
- 当前套餐、余额与用量可在 合作方控制台 或
GET /api/v1/account查看。
错误码
| HTTP | 含义 | 是否计费 |
|---|---|---|
| 400 | 参数缺失或不合法(message 说明原因) | 否 |
| 401 | 缺少签名头、签名错误、时间戳超出 5 分钟、nonce 重复、密钥停用或过期 | 否 |
| 402 | 余额不足(errors.balance_cents / errors.price_cents) | 否 |
| 403 | IP 不在白名单,或无权访问该接口 | 否 |
| 404 | 查无此注册号 / 机场 | 否(预扣已退回) |
| 429 | 超出频率限制 | 否 |
| 500 | 服务端错误,响应含 request_id,反馈时请附上 | 否 |
所有错误响应结构一致:{"success": false, "message": "…", "timestamp": …},部分错误附 errors 对象。
申请接入
接入采用授权制,不提供自助注册。请直接发邮件给创始人 / CEO提交以下信息,我们审核后为您创建密钥并充值:
- 单位 / 项目名称与用途说明(申请免费使用图片接口的,请写明项目是否为个人、教育或非营利用途,有无广告、会员等收入)
- 预计调用量(每日 / 峰值每分钟)
- 出口 IP 列表(如需绑定白名单)
- 技术联系人邮箱
已接入的合作方可登录合作方控制台,用 SecretId 与 SecretKey 查看余额、单价、用量、流水与失败调用。
申请邮箱:创始人 / CEO ceo@xzphotos.cn 邮件标题请写「申请接入 XZPhotos 开放数据接口」。