FlightRadar24 API踩坑日志

FlightRadar24 API踩坑日志

Flightradar24 作为目前全球主流的航班信息查询软件,其丰富的数据量一直备受航空爱好者和开发者的青睐。但当你满怀期待地花费不菲的价格订阅了它的官方 API,准备大干一场时,现实往往会给你泼一盆冷水。

免责声明 / Disclaimer

此页面中的所有内容都不代表 Flightradar24 的官方立场。文中关于服务条款和计费相关的内容为作者根据撰文时的内容编写,具体以官方最新文档为准。

Nothing herein should be construed as representing the official views or endorsement of Flightradar24. All references to Terms of Service and billing structures reflect the author’s understanding at the time of composition and remain subject to the prevailing official documentation.

订阅

Flightradar24的API服务是按月订阅 + 积分扣除制,提供Explorer、Essential和Advanced三档。具体信息请参阅官方定价

三者的主要区别在于每分钟可发起的请求数量上限以及每月分配的额度(Credits)。每次请求都会消耗不等的积分。

除此之外,Explorer中还砍掉了:

  • Airports full
  • Live flight positions count
  • Historic flight positions count
  • Flight summary count

这四个API,不过这个倒是影响有限。

合规

此外还要注意的是尽管FR24 API允许你把数据用于商业行为,但是FR24并不允许你直接对这些数据进行再分发。具体参见官方的Terms of Service

6.3 Commercial Use

6.3.1 Commercial use and the creation of derivative works from data sourced via the FR24 API is permitted at each subscription level in line with the limitation specified below, and in conjunction with these Terms and any other legal notices as documented within the FR24 API Portal, including but not limited to specified caching restrictions.
a. Data Resale and Manipulation
You are prohibited from reselling, transferring, redistributing, lending, leasing, sublicensing or manipulating raw data accessed via the FR24 API. This includes, but is not limited to:
i. Selling or transferring enriched datasets which contain raw FR24 data
ii. Manipulating, modifying, or obfuscating any FR24 API data in an attempt to materially change the raw, original content and integrity of the data, or mask the origins of the data sourced from the FR24 API
iii. Using the FR24 API to supplement or backfill any other near-time or real-time flight data sourced from an alternative data provider for any commercial or non-commercial use

此外根据FR24 API文档的说法:

All endpoints have the same storage duration rule: All data accumulated from the FR24 API should not be stored for more than 30 days from the date it was first received. After this period, all stored data must be permanently deleted.

用户最多可以保留原始数据30天,之后必须删除。
开发者需要自行评估合规风险并建立数据生命周期管理策略。

薛定谔的数据

尽管FR24 API在其产品页面上宣称你可以通过FR24 API来访问全球最大的陆地ADS-B网络,获取实时航班位置。这难免会让人误以为你可以通过API请求获得和Flightradar24应用里看到的一样的内容。

问题就出在这个全球最大的陆地ADS-B网络上。FR24 API实际上只提供两种数据:

  1. Flightradar24自有ADS-B接收机的数据
    对应的data_sources参数为ADSBUATMLAT
  2. Flightradar24通过算法预估出来的飞机位置
    对应的data_sources参数为ESTIMATED

这意味着,Flightradar24 应用中那些通过第三方合作伙伴获取的数据,是无法通过 API 获取的。然而麻烦的是,FR24 API根本没有针对这种情况做任何额外的错误提示或状态码返回。

1
2
3
4
5
6
❯ curl -X GET "https://fr24api.flightradar24.com/api/live/flight-positions/full?callsigns=DKH1660" \
-H 'accept: application/json'\
-H 'accept-version: v1'\
-H 'authorization: Bearer What are you thinking about'

{"data":[]}

这里展示了一个例子,尽管可以在Flightradar24的网页版应用上看到这个呼号为DKH1660的航班,但是API的返回里只有一个空荡荡的[]

FR24 API倒是在这方面一直含糊其辞。尽管他们在FAQ里有一个问题叫做为什么有一些数据仅在Flightradar24网站可见。但这个里面主要是在讲诸如时刻表或者天气图层之类的功能。不过他们在另一个问题的回答里谈到了这种数据缺失:

The API historic endpoint does not return data for aircraft X at timestamp Y. Why?
There are two possible reasons:

  1. Aircraft Inactivity: The aircraft you are looking for was not operating at the provided timestamp.
  2. Data Source Limitations: The aircraft was active but was not captured by any of the data sources available via the Flightradar24 API (ADS-B, MLAT, ESTIMATION). Please verify these possibilities to determine why the data is not available.

其中的数据源限制就明确的提及了这一点。不过他们能在产品推销和服务订阅页面把产品里包含哪些API列个遍,却唯独不明确的告诉你我们有大量的航班位置数据是不面向公共API的,这多少有点挂羊头卖狗肉的味道。

一言难尽的官方SDK

首先官方提供了Python和Node.js这两个SDK固然还是值得称赞,毕竟FR24 API不是他家的主营业务。但是这两个SDK还是藏了不少坑。(另外,你们的Github仓库里怎么还藏着个.DS_Store)

axios

我实在不能理解,为什么一个在 2025 年还在更新的 Node.js SDK 项目,还要强依赖 axios 作为 HTTP 请求库?尤其是在 Node.js 18+ 以及众多边缘计算 JavaScript 运行时都已经原生支持 fetch 的今天。

1
2
3
4
5
6
7
8
9
10
11
12
13
// fr24api-sdk-js/src/transport.js
this.client =
httpClient ||
axios.create({
baseURL: `${this.baseUrl}`,
timeout: this.timeout,
paramsSerializer: (params) =>
qs.stringify(params, { arrayFormat: "brackets" }),
headers: {
Authorization: `Bearer ${this.apiToken}`,
"Accept-version": `${this.apiVersion}`,
},
});

如果只是没用最新的原生 API 倒不是什么致命问题。但在代码托管和边缘计算大行其道的今天,axios无端增加了依赖的包体积,在各种托管环境里也更容易出现问题。例如Cloudflare Worker的运行时在运行到axios时就会报错。

虽然FR24 API SDK确实允许用户在实例化时,通过httpClient参数传入任意其他的HTTP请求库来绕过axios,但这也就意味着你需要自己去重新配置超时、Token 以及 apiVersion。既然如此,那这个 SDK 存在的唯一意义似乎就只剩下那几个参数列表了。

缺失的功能

如前文所述,FR24 API是按积分计费的。因此它的响应头里包含了两个自定义Header,分别显示此次请求消耗了多少积分以及还剩余多少积分:

1
2
x-fr24-credits-consumed: 1
x-fr24-credits-remaining: 48589

官方SDK的返回值里根本没有暴露这些信息。如果你使用默认的axios实例,由于SDK内部的封装逻辑,这些响应头早早地就被丢弃了。

最佳实践

在 AI 大行其道的今天,我的建议是:直接抛弃官方那套半成品的 SDK,让 AI 结合官方文档帮你手搓一个基于 fetch 的请求库。

作为参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
export interface FR24ClientConfig {
token: string;
apiVersion?: string;
timeoutMs?: number;
baseUrl?: string;
}

export interface FR24Response<T = unknown> {
data: T;
meta: {
creditsConsumed: number | null;
creditsRemaining: number | null;
};
}

export class FR24Error extends Error {
constructor(
message: string,
public readonly status?: number,
public readonly responseBody?: string,
) {
super(message);
this.name = "FR24Error";
}
}

type ParamValue = string | number;
type QueryParams = Record<string, ParamValue | undefined | null>;

export function createFR24Client(config: FR24ClientConfig) {
const {
token,
apiVersion = "v1",
timeoutMs = 10_000,
baseUrl = "https://fr24api.flightradar24.com/api/",
} = config;

const headers: HeadersInit = {
Accept: "application/json",
"Accept-Version": apiVersion,
Authorization: `Bearer ${token}`,
};

return async function fr24Fetch<T = unknown>(
endpoint: string,
params: QueryParams = {},
): Promise<FR24Response<T>> {
const url = new URL(endpoint, baseUrl);
for (const [key, value] of Object.entries(params)) {
if (value != null) {
url.searchParams.append(key, String(value));
}
}

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

try {
const response = await fetch(url.toString(), {
method: "GET",
headers,
signal: controller.signal,
});

if (!response.ok) {
const body = await response.text().catch(() => "");
throw new FR24Error(
`FR24 API ${response.status} ${response.statusText}: ${body || "(empty body)"}`,
response.status,
body,
);
}

let data: T;
try {
data = (await response.json()) as T;
} catch {
throw new FR24Error(
`FR24 API returned non-JSON response for ${endpoint}`,
response.status,
);
}

const consumed = response.headers.get("x-fr24-credits-consumed");
const remaining = response.headers.get("x-fr24-credits-remaining");

return {
data,
meta: {
creditsConsumed: consumed ? Number(consumed) : null,
creditsRemaining: remaining ? Number(remaining) : null,
},
};
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
throw new FR24Error(
`Request to ${endpoint} timed out after ${timeoutMs}ms`,
);
}
throw error;
} finally {
clearTimeout(timeoutId);
}
};
}

async function main() {
const fr24 = createFR24Client({
token: process.env.FR24_TOKEN!,
timeoutMs: 5_000,
});

try {
const result = await fr24("live/flight-positions/full", {
callsigns: "DKH1660",
});
console.log("航班数据:", result.data);
console.log(`本次消耗积分: ${result.meta.creditsConsumed}`);
console.log(`剩余积分: ${result.meta.creditsRemaining}`);
} catch (error) {
if (error instanceof FR24Error) {
console.error(`API 错误 [${error.status ?? "N/A"}]:`, error.message);
} else {
console.error("未知错误:", error);
}
}
}

FlightRadar24 API踩坑日志

https://blog.vxd.sh/FR24API-Gotchas/

作者

VXD

发布于

2026-03-13

更新于

2026-03-14

许可协议

评论