# Build a Northern Lights Checker With NOAA's Free Space Weather Data

The northern lights are one of the few natural events you can check with two HTTP requests. NOAA's Space Weather Prediction Center publishes its aurora data as plain JSON files, with no API key and no sign-up. This post covers what causes the glow, then builds a small JavaScript checker that reads the planetary Kp index and the short-term aurora forecast for any point on the map.

## What the Data Describes

![Diagram of the aurora's height above the ground](https://raw.githubusercontent.com/liveatlasearth/liveatlas-media/main/mb-01-1-diagram-of-the-aurora-s-height-above-the-ground.jpg align="center")

*The aurora forms about 80 to 500 km up, where incoming electrons excite oxygen (green and red) and nitrogen (blue and purple). Not to scale.*

According to [NOAA's page on the aurora](https://www.swpc.noaa.gov/phenomena/aurora), electrons accelerated in the magnetotail, on the night side of the planet, follow Earth's magnetic field down toward the poles and collide with oxygen and nitrogen in the upper atmosphere. Each collision lifts an atom or molecule into a higher energy state, and the light comes out when it drops back, much like a neon tube. The glow forms two rings, the auroral ovals, roughly centered on the magnetic poles. In a band from about 60 to 75 degrees of latitude, displays happen on most nights of the year, and a strong geomagnetic storm pushes the ovals far enough to reach parts of the United States, Europe and Asia.

Two numbers capture that behavior, and both are published as JSON.

## Endpoint 1, the Planetary Kp Index

`https://services.swpc.noaa.gov/products/noaa-planetary-k-index.json` returns an array of objects, one per three-hour block over the past several days.

```json
{"time_tag":"2026-10-26T00:00:00","Kp":0.33,"a_running":2,"station_count":8}
```

Kp runs from 0 to 9 and NOAA counts a geomagnetic storm from Kp 5 upward. The [Kp explainer](https://kp-index-explained.carrd.co/) walks through what each step means for the sky. The `time_tag` is in UTC even though it carries no `Z`, so parse it with that in mind.

## Endpoint 2, the OVATION Aurora Forecast

`https://services.swpc.noaa.gov/json/ovation_aurora_latest.json` holds the output of the OVATION model behind NOAA's [30-minute aurora forecast](https://www.spaceweather.gov/products/aurora-30-minute-forecast). The file has four keys, `Observation Time`, `Forecast Time`, `Data Format` and `coordinates`. Each coordinate is a `[Longitude, Latitude, Aurora]` triple, one per whole degree.

```json
[0, -90, 7], [0, -89, 0], [0, -88, 9]
```

The aurora value is an intensity, and NOAA derives a rough viewing probability from it by assuming a linear relationship, so a higher number means a likelier display. The lead time is 30 to 90 minutes, the time the solar wind takes to travel from the L1 observation point to Earth.

One trap is the longitude, which runs from 0 to 359 rather than from -180 to 180. A point at 74 degrees west has to be looked up as 286.

## The Checker

Node 18 or later has `fetch` built in, and the same code runs in a browser.

```js
const KP_URL = "https://services.swpc.noaa.gov/products/noaa-planetary-k-index.json";
const OVATION_URL = "https://services.swpc.noaa.gov/json/ovation_aurora_latest.json";

async function getJson(url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`${url} answered ${res.status}`);
  return res.json();
}

async function latestKp() {
  const rows = await getJson(KP_URL);
  const last = rows[rows.length - 1];
  return { time: last.time_tag, kp: last.Kp, storm: last.Kp >= 5 };
}

async function auroraAt(lat, lon) {
  const data = await getJson(OVATION_URL);
  const x = ((Math.round(lon) % 360) + 360) % 360; // -74 becomes 286
  const y = Math.round(lat);
  const cell = data.coordinates.find(([cx, cy]) => cx === x && cy === y);
  return { forecastTime: data["Forecast Time"], aurora: cell ? cell[2] : null };
}

const [kp, spot] = await Promise.all([latestKp(), auroraAt(69.65, 18.96)]);
console.log(`Kp ${kp.kp} at ${kp.time} UTC${kp.storm ? ", geomagnetic storm" : ""}`);
console.log(`Aurora value near Tromsø ${spot.aurora} (forecast for ${spot.forecastTime})`);
```

Save it as `checker.mjs` and run `node checker.mjs`. Swap the coordinates for your own location.

## Before You Ship It

The OVATION file holds one value for every whole degree of the globe, so it is worth downloading once and reusing for many lookups rather than fetching it per point. Both files say when they were produced, so compare `Forecast Time` and the last `time_tag` with the current time before trusting a cached copy. Finally, a high Kp alone does not promise a show at your location, which is why the checker reads the gridded forecast as well.
