Count the orbits: a census you can defend
Count every orbit regime in seven requests instead of a hundred and sixty-five, assert the classes actually add up to the catalog total, chart it, and then see why taking the first page of a class is not a sample of that class.
- Runtime
- 30 min
- API requests
- 13
Download
.ipynb file. Nothing executes until you run it.
Anonymous callers share 50 requests per day per network. A free account raises that to 1,000 per day. Each notebook states what one full run costs.
How to run it
- 1Install the packages listed above, for example: pip install requests pandas matplotlib
- 2Open Jupyter (jupyter lab) with the downloaded .ipynb, or upload it to any hosted notebook service.
- 3Run the cells top to bottom. No API key is needed; add one in the first cell if you want the higher limit.
What you will learn
- Use a filtered page total to count a category without downloading its rows.
- Write a reconciliation assertion that fails loudly when the categories stop partitioning the data.
- Choose a log axis honestly when one category is two orders of magnitude larger than another.
- Recognise ordering bias in a paginated API, and say what your sample actually is.
Before you start
- pandas Series and groupby, and a first matplotlib figure.
- The first-API-request notebook, or equivalent.
Python packages
pip install requests pandas matplotlib
Which data
The live catalog. On 2026-08-04 the seven classes summed to exactly the /stats total of 16,476 objects; the assertion in the notebook is what tells a future reader whether that still holds.
Endpoints it calls
Every request the notebook makes, and why. Nothing here is illustrative: these are the calls it runs.
GET /api/v1/satellitesOne request per orbit class, reading meta.total; then one page per class for the sampling section.GET /api/v1/statsAn independently computed catalogue total, to reconcile the census against.GET /api/v1/operatorsShows how much of the largest orbit class is a single operator.
From the notebook
Excerpts copied verbatim from the file. A test fails if the notebook changes and these do not.
total_objects = get("/stats")["data"]["total_objects"]
summed = int(counts.sum())
print("sum of orbit classes:", summed)
print("total from /stats: ", total_objects)
print("difference: ", summed - total_objects)What you end up with
- A census table of all seven orbit classes, with a passing reconciliation assertion.
- A labelled log-scale bar chart of the catalog by orbit class.
- An apogee/perigee scatter whose title states, on the chart itself, that it is not a random sample.
- A statement of what the catalog excludes, so the number is quotable without being misleading.
How to cite
Quote the filter with the number, never the number alone: "OrbitalWiki, /api/v1/satellites?orbit_class=LEO, 11,709 tracked objects, retrieved 2026-08-04". A satellite count without its filter is not reproducible.
Sources
- OrbitalWiki API, machine-readable OpenAPI 3.1 spec (endpoints and parameters)Retrieved 2026-08-04Confirmed
- OrbitalWiki catalogue coverage: built from CelesTrak’s active group, so debris is essentially absent (measured 2026-08-03: 16,470 payloads, 3 debris, 2 rocket bodies)Retrieved 2026-08-04Confirmed