LIVE
Server-Sent Events stream of the live station. The first 'telemetry' event carries the full state, including crew and docked vehicles; after that 'telemetry' arrives every second without them, and 'solar', 'crew' and 'docking' arrive on their own when they change.
event: telemetry — { orbital: OrbitalState, telemetry: ISSTelemetry | null, activeEvent: ISSEvent | null,
evaBatteryWindow: boolean, visitorCount: number,
feed: { state: "waiting" | "live" | "silent", silentForMs: number | null } }
event: solar — SolarActivity
event: crew — CrewMember[]
event: docking — DockedSpacecraft[]
event: visitors — numberWhere the station is now, propagated with SGP4 from the current TLE. 503 until a TLE has been fetched.
{
"timestamp": number, "lat": number, "lon": number, "altitude": number, // km
"velocity": number, "speedKmH": number, "period": number, // km/s, km/h, minutes
"inclination": number, "eccentricity": number, "apoapsis": number, "periapsis": number,
"revolutionNumber": number, "isInSunlight": boolean,
"sunriseIn": number | null, "sunsetIn": number | null, "betaAngle": number, "heading": number,
"sunDirection": [x, y, z] | null, "moonDirection": [x, y, z] | null, ...
}The station's systems as NASA's ISSLIVE feed last reported them. 503 when nothing has arrived yet or the latest reading is over 30 s old; the age is in the X-Telemetry-Age-Ms header.
ISSTelemetry — {
"timestamp": number, "powerKw": number | null, "solarArrays": {...}, "temperatureC": ...,
"moduleTemps": {...}, "externalThermal": {...}, "pressurePsi": ..., "oxygenPercent": ...,
"eclss": {...}, "attitudeMode": ..., "attitude": {...}, "cmgs": {...}, "airlock": {...},
"comms": {...}, "lab": {...}, "systems": {...}, "emuBatteries": {...},
"robotics": { "ssrms": {...}, "spdm": {...}, "mtPosition": ..., ... },
"channels": { [channelId]: { "value": string, "status": string, "timestamp": number } }
}Visible passes over a location in the next 48 hours.
| NAME | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| lat | number | yes | Observer latitude, -90 to 90 |
| lon | number | yes | Observer longitude, -180 to 180 |
| minElev | number | no | Lowest peak elevation to include, degrees (default 10, 0–45) |
[
{ "riseTime": number, "riseAzimuth": number, "maxTime": number, "maxElevation": number,
"setTime": number, "setAzimuth": number, "magnitude": number,
"quality": "bright" | "good" | "fair" | "poor" }
]Space weather from NOAA SWPC, cached for a minute.
{
"timestamp": number, "kpIndex": number, "kpLabel": string,
"xrayFlux": number, "xrayClass": string,
"protonFlux1MeV": number, "protonFlux10MeV": number, "protonFlux100MeV": number,
"radiationRisk": "low" | "moderate" | "high" | "severe"
}Events in progress, and the next five scheduled (EVAs, dockings, undockings, berthings, reboosts).
{ "active": ISSEvent[], "upcoming": ISSEvent[] }
ISSEvent — { "id": string, "type": "eva" | "docking" | "undocking" | "berthing" | "reboost" | "maneuver",
"title": string, "description": string, "status": "scheduled" | "active" | "completed" | "cancelled",
"scheduledStart": number, "scheduledEnd": number, "actualStart": number | null, "actualEnd": number | null,
"metadata": { [key]: string } } // metadata.actualSource: "LL2" | "telemetry" once a docking's real time is knownToday's crew day (UTC): a representative routine with the day's real events laid over it. A docking or undocking whose actual time is known is placed at that time.
{
"dayStart": number,
"activities": [ { "name": string, "type": string, "startTime": number, "endTime": number, "isEvent": boolean } ]
}ARCHIVE AND SIM
What the server has recorded. SIM mode replays from these.
The station as archived at a moment: position (with the Sun, Moon and sky rebuilt for it), the telemetry recorded then, space weather, the event under way, and the visiting vehicles docked then. 404 before the archive begins.
| NAME | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| timestamp | ISO 8601 | yes | The moment to replay |
{
"timestamp": number,
"orbital": OrbitalState,
"telemetry": ISSTelemetry | null, // null where nothing was archived
"solar": SolarActivity | null,
"activeEvent": ISSEvent | null,
"docking": DockedSpacecraft[] | null // null before the docking archive begins
}What the archive holds across the retention window, as contiguous segments: live telemetry, a feed that sent nothing, time the server was not running, or not yet computed.
{
"from": number, "to": number, "bucketMs": number,
"segments": [ { "from": number, "to": number, "state": "live" | "silent" | "unrecorded" | "pending" } ]
}One archived orbital metric over a recent window, downsampled to at most `points` values.
| NAME | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| metric | string | yes | altitude, velocity, speed_kmh, latitude, longitude, inclination, beta_angle, iss_mass_kg, momentum_percent, gnc_delta_r_km or gnc_delta_v_ms |
| hours | number | no | Window, hours back from now (default 24, max 720) |
| points | number | no | Most values to return (default 200, max 500) |
[ { "timestamp": number, "value": number } ]Every visiting-vehicle docking on record, from Launch Library 2, with what the Russian segment's port flags said about it. A flag time is used in place of LL2's when the change was seen live — read within two minutes — and the port-flag changes themselves are listed with the window each was read within.
{
"flagExactWithinSeconds": number,
"dockings": [ {
"id": number, "vehicle": string, "port": string,
"ll2": { "docked": ISO, "departed": ISO | null },
"flags": { "docked": { "at": ISO, "withinSeconds": number } | null, "departed": ... | null },
"used": { "docked": ISO, "departed": ISO | null }
} ],
"portFlagChanges": [ {
"port": string | null, "flag": string, "channel": string, "change": "1→0" | "0→1",
"between": [ISO, ISO], "withinSeconds": number, "dockingId": number | null
} ]
}ROBOTICS CALIBRATION
Observations the station view is calibrated from, recorded as they happen.
Canadarm2 base changes the archive has caught. Only a base handover watched live (readings a minute or less apart either side) is usable for calibration; one seen across a gap is listed but marked unusable.
{
"total": number, "usable": number, "status": string,
"handovers": [ { "id": number, "at": ISO, "kind": "base" | "operating_base",
"from": string, "to": string, "gapSeconds": number | null,
"observed": boolean, "usable": boolean, "captured": boolean, "sampleCount": number, "note": string } ]
}The arm's joint readings in the instant before each base handover.
[ { "handover_id": number, "from_base": string, "to_base": string, "at": string,
"channel_id": string, "value": string } ]Mobile Transporter rail calibration: the position reading at each worksite the MT has been seen parked at, and the mapping to the truss once they determine it.
{
"railSpanM": number, "worksiteCount": number,
"observations": [ { "worksite": string, "positionCm": number, "minCm": number, "maxCm": number,
"samples": number, "firstSeen": ISO, "lastSeen": ISO } ],
"worksitesSeen": number, "observedSpanM": number, "fitsRail": boolean,
"calibration": { "metresPerCm": number, "originM": number } | null,
"blockedBy": string | null
}MEDIA
A 4096×2048 equirectangular true-colour Earth, stitched from NASA GIBS imagery (MODIS Terra and Aqua, VIIRS). Rebuilt every six hours; the day used is in X-Imagery-Date.
| NAME | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| date | YYYY-MM-DD | no | Imagery date (default two days ago) |
image/jpeg
One 512 px tile of the sharper ground drawn under the station, from NASA GIBS at level 6 of its geographic grid (4.5° tiles, about 1 km per pixel; row 0–39 from the north pole, col 0–79 from the antimeridian). Day tiles combine MODIS Terra and Aqua and VIIRS from two days ago; night tiles are VIIRS Black Marble.
image/jpeg
A 2048×1024 equirectangular Earth at night — VIIRS Black Marble city lights — the night side of /api/earth-texture.
image/jpeg
The YouTube video currently live on each ISS stream the site embeds, checked every ten minutes.
{ "streams": [ { "id": "nasa" | "sen", "label": string, "videoId": string } ] }Posts from NASA's Space Station blog, a page at a time.
| NAME | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| page | number | no | Page number (default 1) |
{
"page": number, "hasMore": boolean, "cached": boolean, "stale"?: true,
"posts": [ { "id": string, "title": string, "link": string, "publishedAt": number,
"author": string | null, "categories": string[], "excerpt": string } ]
}The running build's id — how an open page learns a new version has been deployed.
{ "buildId": string }ADMIN
Every admin route needs an Authorization: Bearer <ADMIN_TOKEN> header, and refuses all requests when ADMIN_TOKEN is unset.
Every event, most recent first (up to 200).
ISSEvent[]
Create or replace an event (PUT does the same). The body is an ISSEvent.
| NAME | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| id | string | yes | Event id; an existing id is replaced |
| type | string | yes | eva, docking, undocking, berthing, reboost or maneuver |
| title | string | yes | Title |
| description | string | yes | Description |
| status | string | yes | scheduled, active, completed or cancelled |
| scheduledStart | number | yes | Planned start, Unix ms |
| scheduledEnd | number | yes | Planned end, Unix ms |
| actualStart | number | no | Actual start, Unix ms |
| actualEnd | number | no | Actual end, Unix ms |
| metadata | object | no | String key/value pairs |
{ "ok": true, "id": string }Set an event's status. Activating stamps its actual start (keeping one already set); completing stamps its actual end.
| NAME | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
| status | string | no | scheduled, active, completed or cancelled |
| isActive | boolean | no | Shorthand: true for active, false for completed |
{ "ok": true, "id": string, "status": string }Total page views.
{ "pageViews": number }Insert a completed test reboost ending five minutes ago, to check the reboost display.
{ "ok": true, "eventId": string }