Blog · 2026-05-19 · 9 min read
FITS headers every astrophotographer should know
Every sub-frame in your archive carries a metadata block — the FITS header — that records what was captured, with what gear, when, and at what exposure. Most of us never look at it. But it's the most reliable record of your imaging life, and once you know which keywords to look at, it's the basis of every analysis question worth asking.
What a FITS header actually is
FITS — Flexible Image Transport System — is the file format almost every cooled astrocam writes. Open one in a hex editor and the first thing you see, before any pixel data, is the header: a sequence of fixed-width 80-character "cards", each one a keyword/value pair plus an optional comment. The structure looks like this:
SIMPLE = T / file does conform to FITS standard
BITPIX = 16 / number of bits per data pixel
NAXIS = 2 / number of data axes
NAXIS1 = 6248 / length of data axis 1
NAXIS2 = 4176 / length of data axis 2
OBJECT = 'M31 ' / target name
FILTER = 'Ha ' / filter name
EXPTIME = 300.000 / exposure time in seconds
DATE-OBS= '2024-08-13T22:34:55.123' / UTC start of exposure
IMAGETYP= 'LIGHT ' / frame type
END
Most images carry somewhere between 30 and 80 of these cards. Everything before the binary pixel data is plain ASCII text — you can in principle read it with any text editor, though the binary section after the END card will look like noise.
The point is: the header is not metadata you have to add. It's metadata your camera and sequencer already wrote, every frame, for years. The whole game is reading it.
How to view a header
You don't need to write code to inspect a header. Common ways in:
- PixInsight — the FITS Header dialog (right-click any image in the workspace) shows every keyword and value.
- FITS Liberator — free, cross-platform; shows the header alongside a stretchable preview.
- AstroImageJ — popular in the variable-star / exoplanet community, exposes the full header in the FITS Editor.
- astropy — Python;
fits.getheader(path)returns the header as a dict-like object. Bundledfitsverify/fitsheadercommand-line tools also work. - Any text editor — the header is ASCII. Open the file and read the first few kilobytes. The binary section that follows will be unreadable, but the header itself is human-friendly.
The "must know" headers
If you only learn five header keywords, learn these. They are the spine of every integration-time, per-target, per-filter analysis you'll ever do.
| Header | Type | Example | What it tells you |
|---|---|---|---|
OBJECT | string | M31 | Target name as the sequencer wrote it. One of the most-edited fields — often hand-typed and inconsistent across sessions. |
FILTER | string | Ha | Filter name. Watch for naming drift: the same filter often appears as Ha, H-alpha, H_alpha, or Hydrogen depending on the sequencer and filter-wheel config. |
EXPTIME (or EXPOSURE) | number | 300.000 | Exposure in seconds for this single sub. The multiplier in every integration calculation. |
DATE-OBS | ISO timestamp | 2024-08-13T22:34:55.123 | UTC timestamp of the exposure. The canonical "when". |
IMAGETYP | string | LIGHT | Separates lights from calibration frames. Do not include flats, darks, or biases in integration totals — they're not photons of your target. |
These five keywords answer "how much time have I spent on what". Everything below adds nuance.
The "useful" headers (rig identification)
Once you want to ask "which scope earned me those hours" or "what gain was I running last winter", you reach for the rig keywords. These are the ones that turn a flat integration total into something you can attribute.
| Header | Type | Example | What it tells you |
|---|---|---|---|
INSTRUME | string | ASI2600MM Pro | Camera model. The most reliable rig fingerprint — usually written by the camera driver itself. |
TELESCOP | string | Esprit 100ED | Telescope name. User-configurable in most sequencers, so it's only as reliable as how carefully you set it. |
FOCALLEN | number (mm) | 550.0 | Focal length in millimetres. Useful for distinguishing native vs reducer/extender configurations on the same OTA. |
XPIXSZ / YPIXSZ | number (μm) | 3.76 | Pixel size in microns. Combined with FOCALLEN gives image scale (arcsec/pixel) without guessing. |
GAIN / OFFSET | number | 100 / 50 | Sensor gain and offset settings. Critical when you've changed settings between sessions and are wondering why the stretches don't match. |
CCD-TEMP / SET-TEMP | number (°C) | -10.0 / -10.0 | Actual and target sensor temperature. Mismatches often reveal cooling problems or warm-weather setpoint drift. |
The "advanced" headers
These won't be in every file, but when they are, they unlock the more interesting questions.
| Header | Type | Example | What it tells you |
|---|---|---|---|
OBJCTRA / OBJCTDEC | sexagesimal or degrees | 00 42 44.3 / +41 16 09 | Mount-reported RA/Dec at the time of exposure. Cross-checking these against the OBJECT name is the cheapest way to catch mis-labelled frames. |
XBINNING / YBINNING | integer | 1 | Sensor binning. A 2×2 binned frame has a different image scale and shouldn't be silently mixed into a 1×1 stack. |
BAYERPAT | string | RGGB | Bayer pattern (OSC only). Tells debayering tools which colour order the sensor uses. |
AIRMASS | number | 1.34 | Atmospheric path length at the time of exposure. Often predictive of SNR — lower is better. |
SWCREATE / CREATOR | string | N.I.N.A. 3.1 | Which sequencer wrote the file. Useful when you've used more than one — see NINA vs SGP vs APT integration tracking. |
What FITS headers don't tell you reliably
The header is the best record you have, but it isn't a perfect one. A few honest caveats:
- Target names aren't normalised.
OBJECTmight beM31,Andromeda,NGC 224, orandromeda galaxyacross nights — all the same object. Any per-target total has to handle this. - Filter names drift. The same Ha filter often appears under several names across sequencers, filter wheels, and config edits.
Ha,H-alpha, andHydrogenare common variants. - Framing and rotation aren't in the basics. Two nights labelled
M31might have completely different framing or rotation. The header doesn't tell you "this is the same panel" — you needOBJCTRA/OBJCTDEC, or a plate solve, for that. - Older files are sparser. Early DSLR exports, manually captured frames, and files from older capture software often omit
FILTER,INSTRUME, or evenIMAGETYP. Don't assume every field exists. - "LIGHT" vs "LIGHT FRAME".
IMAGETYPvalues aren't fully standardised. Some sequencers writeLIGHT, othersLight Frame, others nothing at all. A robust reader treats blanks generously.
What you can do with the headers
Once the headers are read and grouped, a long list of analytics that used to need a spreadsheet just works:
- Per-target integration totals — group by
OBJECT, sumEXPTIME. - Per-filter mix — see your L:R:G:B:Ha:OIII:SII split for each target.
- Per-rig hours — group by
INSTRUME/TELESCOPto compare productivity across scopes. - Time-of-night exposure trends — bucket by hour-of-night from
DATE-OBS. - "First imaged" / "last imaged" timestamps per target — min/max of
DATE-OBS. - Dropped-frame detection — gaps in
DATE-OBSsequences flag clouded-out or rejected subs.
How Photon Ledger uses these
Photon Ledger walks your archive, reads each FITS header (only the header — pixel data is never touched), and writes the keyword/value pairs into a local database you can query. The "must know" five are the spine of every report; the "useful" rig fields drive the per-scope and per-camera views; the "advanced" fields surface where they exist and are politely ignored where they don't.
For the full keyword-by-keyword reference of what Photon Ledger reads and how each field is interpreted, see the FITS headers docs page.
Related reading
- FITS headers reference (docs) — the keyword-by-keyword reference of what Photon Ledger reads.
- How to count your astrophotography integration time — why the question is harder than it sounds.
- Total integration time by target — turning per-frame headers into per-target totals.
- NINA vs SGP vs APT integration tracking — how the major sequencers differ in what they write.