FOR BOTS, SITES, AND VR TOOLS
Plane API
Pull upcoming Plane events as JSON, check the stream banner, or subscribe a webhook for update notifications. Read endpoints are public and do not need an API key.
Base URL: https://plane.fm/api/v1
Quick start
Upcoming events with curl
curl "https://plane.fm/api/v1/events?limit=10"
Upcoming events with JavaScript
const response = await fetch(
"https://plane.fm/api/v1/events?limit=10"
);
const { data } = await response.json();
Live sample not loaded yet.
Read endpoints
| Method | Path | Use |
|---|---|---|
GET | /api/v1/events | List live and upcoming events by default. |
GET | /api/v1/events/next | Get the current event, or the next one. Cancelled events are skipped. |
GET | /api/v1/stream | Get the public stream banner settings and status. |
GET | /api/v1 | List API links. |
GET | /api/flights | Legacy schedule response: { "flights": [...] }. |
GET | /api/stream | Legacy stream response used by the homepage. |
Event filters
| Parameter | Values | Default |
|---|---|---|
scope | upcoming, live, past, all | upcoming |
limit | Integer from 1 to 100 | 50 |
from | ISO 8601 date or date-time | No start filter |
to | ISO 8601 date or date-time | No end filter |
Events are sorted by departure.
{
"object": "list",
"count": 1,
"scope": "upcoming",
"limit": 50,
"data": [
{
"flightNo": "WF0289",
"destination": "Paw Beach",
"lineup": "Nana_Wunsek, GrizzMango, RileyPie, Valryus",
"eventInfoUrl": null,
"departure": "2026-11-07T06:00:00.000Z",
"landing": "2026-11-07T11:00:00.000Z",
"status": "ON TIME",
"scheduledStatus": "ON TIME",
"state": "upcoming"
}
]
}
Responses are cacheable for 15 seconds and include an ETag. Send If-None-Match on later requests to get 304 Not Modified when nothing changed. Errors use { "error": { "code": "...", "message": "..." } }.
Webhook notifications
Plane can POST JSON to a webhook URL you control when the schedule or stream settings change. Discord and Slack show the content field as a message.
Events
schedule.published- An admin saved a changed schedule.
stream.updated- An admin saved changed stream settings.
How signup works
- Discord and Slack webhook URLs activate after Plane sends a test message.
- Any other HTTPS endpoint receives a one-time code. Enter the code below to activate it.
- Keep the manage token returned after activation. It is required to unsubscribe.
Subscribe
Payload and verification
Example delivery
{
"id": "8e33c396-02c4-4a31-91e8-7988e7630dd0",
"type": "schedule.published",
"createdAt": "2026-11-06T21:30:00.000Z",
"data": {
"eventCount": 18,
"nextEvent": { "flightNo": "WF0289" },
"eventsUrl": "https://plane.fm/api/v1/events"
},
"content": "Plane schedule updated. Next: WF0289, Paw Beach, Sat, 07 Nov 2026 06:00:00 GMT. https://plane.fm/"
}
Delivery rules
- Return any 2xx status to accept a delivery.
- Each attempt times out after 5 seconds.
- Failed deliveries retry every 5 minutes for up to an hour.
- Subscriptions are disabled after 10 deliveries fail after all retries.
Verify the signature
For a custom receiver, verify X-Plane-Signature with the signing secret returned at signup. The value is sha256= plus the HMAC-SHA256 hex digest of the exact request body.
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = "sha256=" + createHmac("sha256", signingSecret)
.update(rawBody)
.digest("hex");
const received = request.headers["x-plane-signature"];
const valid = received.length === expected.length
&& timingSafeEqual(Buffer.from(received), Buffer.from(expected));