Skip to content

CelesTrak Data Source

Respectful Usage

CelesTrak is freely available for public use, but users should be respectful of the service. Avoid excessive automated requests, and take advantage of caching to minimize repeated queries. For large-scale or commercial applications, consider downloading once and distributing data internally.

CelesTrak is the simplest entry point for satellite ephemeris data: it is free, requires no account, and provides frequently updated orbital element sets for thousands of satellites. Maintained by T.S. Kelso since 1985, it is a widely used resource for satellite tracking and space situational awareness.

How It Works

Brahe provides a CelestrakClient that talks to celestrak.org and returns structured records. For GP (General Perturbations) queries the client returns GPRecord objects -- the same type used by the SpaceTrack client, so downstream code works interchangeably with either data source. The client also supports Supplemental GP data from constellation operators (CelestrakQuery.sup_gp) and satellite catalog metadata (CelestrakQuery.satcat).

To minimize load on CelesTrak's servers and improve performance, the client caches downloaded data for 2 hours. Cache files are stored in the system cache directory (~/.cache/brahe/celestrak/) and are keyed by query URL. Setting BRAHE_NETWORK_MODE=offline serves cached data without any request; see Environment Variables. Lookups by catalog number, name, or international designator follow a different order, described in Single-Object Lookups and the Active Catalog below.

Customizing Cache

Pass cache_max_age=0.0 to disable caching, or a custom value in seconds to change the TTL.

Querying Satellite Data

The client offers two levels of API. Compact convenience methods handle the most common lookups -- pass a catalog number, group name, object name, or international designator directly to get_gp or get_satcat. For more control, the CelestrakQuery builder lets you compose queries with filtering, sorting, output format selection, and result limits.

The following example retrieves GP data for a single satellite by NORAD catalog number:

import brahe as bh

# Initialize EOP data
bh.initialize_eop()

# Query ISS GP data by NORAD catalog number
client = bh.celestrak.CelestrakClient()
records = client.get_gp(catnr=25544)
record = records[0]

print("ISS GP Data:")
print(f"  Name: {record.object_name}")
print(f"  NORAD ID: {record.norad_cat_id}")
print(f"  Epoch: {record.epoch}")
print(f"  Inclination: {record.inclination:.2f}°")
print(f"  RAAN: {record.ra_of_asc_node:.2f}°")
print(f"  Eccentricity: {record.eccentricity:.6f}")
#[allow(unused_imports)]
use brahe as bh;
use bh::celestrak::CelestrakClient;

fn main() {
    bh::initialize_eop().unwrap();

    // Query ISS GP data by NORAD catalog number
    let client = CelestrakClient::new();
    let records = client.get_gp_by_catnr(25544).unwrap();
    let record = &records[0];

    println!("ISS GP Data:");
    println!("  Name: {}", record.object_name.as_deref().unwrap_or("Unknown"));
    println!("  NORAD ID: {}", record.norad_cat_id.unwrap_or(0));
    println!("  Epoch: {}", record.epoch.as_deref().unwrap_or("Unknown"));
    println!("  Inclination: {:.2}°", record.inclination.unwrap_or(0.0));
    println!("  RAAN: {:.2}°", record.ra_of_asc_node.unwrap_or(0.0));
    println!("  Eccentricity: {:.6}", record.eccentricity.unwrap_or(0.0));

}
Output
1
2
3
4
5
6
7
ISS GP Data:
  Name: ISS (ZARYA)
  NORAD ID: 25544
  Epoch: 2026-08-30T04:13:16.870080
  Inclination: 51.63°
  RAAN: 293.89°
  Eccentricity: 0.000504
1
2
3
4
5
6
7
ISS GP Data:
  Name: ISS (ZARYA)
  NORAD ID: 25544
  Epoch: 2026-08-30T04:13:16.870080
  Inclination: 51.63°
  RAAN: 293.89°
  Eccentricity: 0.000504

Other common lookup patterns include querying by group name (group="stations"), object name (name="ISS"), or international designator (intdes="1998-067A"). All of these are available as keyword arguments to get_gp or as builder methods on CelestrakQuery.gp.

Single-Object Lookups and the Active Catalog

A lookup by catalog number, international designator, or name is answered from a cached copy of the active group whenever the object is in it. The first such lookup downloads active once (about 11,000 records, several megabytes); every later lookup by any client in the same cache reads that file until it passes the cache TTL. The order is: the exact per-object cache file if it is present and servable, then the cached active group, then a request for the object itself. Objects that are not in active -- debris, inactive payloads -- are requested individually, so get_gp(catnr=...) works for any catalog number. catnr and intdes results are unaffected by which step answers them -- resolution only changes where the record comes from, not its content.

A name search follows the same order, so a search that matches in active returns only active objects; the server's own name search would also return inactive objects with the same substring. A name with no match in active is sent to the server. Group, special, and file queries, supplemental GP, and SATCAT are sent to the server exactly as written, though still served from the URL cache when a fresh copy exists; query_raw never consults active. A client with a zero cache age sends every query directly, since the active group copy could not be reused.

1
2
3
4
5
6
import brahe as bh

client = bh.celestrak.CelestrakClient()
iss = client.get_gp(catnr=25544)          # downloads active once
nisar = client.get_gp(name="NISAR")       # served from the cached active group
debris = client.get_gp(catnr=34427)       # not in active: requested directly
1
2
3
4
5
6
use brahe::celestrak::CelestrakClient;

let client = CelestrakClient::new();
let iss = client.get_gp_by_catnr(25544).unwrap();
let nisar = client.get_gp_by_name("NISAR").unwrap();
let debris = client.get_gp_by_catnr(34427).unwrap();

With BRAHE_NETWORK_MODE=offline, a cached active group of any age answers these lookups; an object that is neither in the cached group nor in a per-object cache file is an error. See Environment Variables.

Client-Side Filtering

CelesTrak's API only supports a few server-side filters (group, catalog number, name, international designator). For more complex filtering, brahe provides client-side operators that use the same syntax as the SpaceTrack query interface. These filters are applied after downloading the full dataset, so they work on any field in the response:

import brahe as bh
from brahe.spacetrack import operators as op

client = bh.celestrak.CelestrakClient()
query = (
    bh.celestrak.CelestrakQuery.gp
    .group("stations")
    .filter("INCLINATION", op.greater_than("50"))
    .filter("OBJECT_TYPE", op.not_equal("DEBRIS"))
    .order_by("INCLINATION", False)
    .limit(10)
)
records = client.query(query)

Satellite Groups

CelesTrak organizes satellites into logical groups accessible via simple names such as stations, starlink, gnss, active, and weather. Groups span several categories including temporal (active, last-30-days), communications (starlink, oneweb, iridium-NEXT), navigation (gnss, gps-ops, galileo), earth observation (weather, planet, earth-resources), and scientific or special purpose (science, analyst, visual). Group names and contents evolve as missions launch, deorbit, or change status.

For a complete listing of available groups, see the CelesTrak API Reference or visit CelesTrak GP Element Sets.


See Also