How to Read NASA's Meteorite Landings Data in Python

On this page

Problem statement

NASA's Meteorite Landings table is the most downloaded list of meteorites there is: 45,716 rows, one per meteorite recognised by The Meteoritical Society, with its name, class, mass in grams, whether it was seen to fall or found later, a year and a position. It looks like a clean CSV, and it is easy to map straight away. But nearly a third of the positions are missing or set to 0,0, one meteorite lies on Mars, the years run from 860 to 2101, and NASA last updated the file in May 2013. This page reads it, checks each column and keeps the rows a map can use.

Quick answer

Read the CSV, then drop rows without a position, at 0,0, or with a longitude outside Β±180 before building points:

ok = met.reclat.notna() & ~((met.reclat == 0) & (met.reclong == 0)) & met.reclong.between(-180, 180)
g = gpd.GeoDataFrame(met[ok], geometry=gpd.points_from_xy(met.reclong[ok], met.reclat[ok]), crs=4326)
print(f"usable positions: {len(g):,} of {len(met):,}")
print(g[["name", "recclass", "mass_g", "fall", "year", "reclat", "reclong"]].head(4).to_string(index=False))
usable positions: 32,186 of 45,716
    name    recclass   mass_g fall   year   reclat    reclong
  Aachen          L5     21.0 Fell 1880.0 50.77500    6.08333
  Aarhus          H6    720.0 Fell 1951.0 56.18333   10.23333
    Abee         EH4 107000.0 Fell 1952.0 54.21667 -113.00000
Acapulco Acapulcoite   1914.0 Fell 1976.0 16.88333  -99.90000

32,186 of the 45,716 meteorites have a position a map can use. The first rows are typical of the old, witnessed falls: Aachen, an L5 chondrite of 21 g that fell in 1880, and Abee, an enstatite chondrite of 107,000 g that fell in Alberta in 1952. Mass is in grams, and year is a float because some years are missing.

World map of 31,962 meteorites with a usable position: found meteorites crowd Antarctica's coast, the Sahara, Oman and the US Southwest, witnessed falls spread over Europe, India, Japan and the eastern United States. World map of 31,962 meteorites with a usable position: found meteorites crowd Antarctica's coast, the Sahara, Oman and the US Southwest, witnessed falls spread over Europe, India, Japan and the eastern United States.
Every meteorite with a usable position: finds in blue, witnessed falls in red.

The columns, one by one

1. Names, ids and status

print(met.columns.tolist())
print(f"ids unique: {met.id.is_unique}; names unique: {met.name.is_unique}")
print(met.nametype.value_counts().to_string())
print(met.fall.value_counts().to_string())
['name', 'id', 'nametype', 'recclass', 'mass_g', 'fall', 'year', 'reclat', 'reclong', 'GeoLocation']
ids unique: True; names unique: True
nametype
Valid     45641
Relict       75
fall
Found    44609
Fell      1107

Both the name and the id are unique, so either is a key. nametype is "Valid" for 45,641 meteorites and "Relict" for 75, objects that were once meteorites but have been weathered beyond classification. fall separates the 1,107 meteorites seen to fall, and collected soon after, from the 44,609 found later with no record of when they arrived.

2. Years

print(met.year.describe().round(1).loc[["count", "min", "50%", "max"]].to_string())
print(met.loc[(met.year > 2013) | (met.year < 1500), ["name", "fall", "year"]].sort_values("year").to_string(index=False))
count    45425.0
min        860.0
50%       1998.0
max       2101.0
                 name  fall   year
               Nogata  Fell  860.0
                Narni  Fell  920.0
              Elbogen  Fell 1399.0
     Rivolta de Bassi  Fell 1490.0
            Ensisheim  Fell 1491.0
            Valdinoce  Fell 1495.0
Northwest Africa 7701 Found 2101.0

45,425 rows have a year, with a median of 1998. Six falls date from the Middle Ages: Nogata in Japan in 860, Narni in Italy in 920, and Ensisheim, which fell in Alsace in 1491. At the other end is Northwest Africa 7701, dated 2101, a typing error, since the file stops in 2013. For a find, the year is when it was found or classified, not when it fell.

3. Masses

print(f"no mass: {met.mass_g.isna().sum()}; mass 0: {(met.mass_g == 0).sum()}")
print(met.mass_g.describe(percentiles=[0.25, 0.5, 0.75, 0.99]).round(1).to_string())
no mass: 131; mass 0: 19
count       45585.0
mean        13278.1
std        574988.9
min             0.0
25%             7.2
50%            32.6
75%           202.6
99%         50600.0
max      60000000.0

131 meteorites have no mass and 19 a mass of 0, which means unknown, not weightless. The rest are extremely skewed. The median meteorite weighs 32.6 g, three quarters weigh 202.6 g or less, and the 99th percentile is 50,600 g, while the largest, Hoba, weighs 60 tonnes. The mean of 13,278 g describes almost no meteorite in the table. Work on a log scale or with medians.

4. Positions

print(f"no position: {met.reclat.isna().sum():,}; at 0,0: {((met.reclat == 0) & (met.reclong == 0)).sum():,}; longitude beyond 180: {(met.reclong.abs() > 180).sum()}")
print(met.loc[met.reclong.abs() > 180, ["name", "recclass", "reclat", "reclong", "GeoLocation"]].to_string(index=False))
geo = met.GeoLocation.str.strip("()").str.split(", ", expand=True).astype(float)
print(f"GeoLocation equals reclat, reclong: {(np.isclose(geo[0], met.reclat) & np.isclose(geo[1], met.reclong)).sum():,} of {met.GeoLocation.notna().sum():,}")
no position: 7,315; at 0,0: 6,214; longitude beyond 180: 1
            name          recclass   reclat   reclong           GeoLocation
Meridiani Planum Iron, IAB complex -1.94617 354.47333 (-1.94617, 354.47333)
GeoLocation equals reclat, reclong: 38,401 of 38,401

7,315 meteorites have no position and 6,214 sit at exactly 0,0, the placeholder for "not given". One row has a longitude of 354.5: Meridiani Planum, an iron meteorite found on Mars by the Opportunity rover, with Martian coordinates. GeoLocation repeats reclat and reclong as text for all 38,401 rows that have it, so it can be ignored. A further 224 rows have only one coordinate at 0, which the quick answer's filter lets through; the 0,0 page deals with them.

Horizontal bars of position quality: 31,962 usable, 7,315 missing, 6,214 at 0,0, 224 with one coordinate at 0 and one on Mars. Horizontal bars of position quality: 31,962 usable, 7,315 missing, 6,214 at 0,0, 224 with one coordinate at 0 and one on Mars.
What the position columns hold; the 224 rows with one zero are on the 0,0 page.

Records over time, and where the names come from

The data used on this page

NASA's Meteorite Landings table, compiled from The Meteoritical Society's records and last updated by NASA on 14 May 2013, read from the CSV NASA still serves. It is saved on this site with the rest of the meteorite and fireball data, downloaded on 28 October 2026, so the pages keep working if NASA moves the file again, as it did when its open-data portal was rebuilt. Masses are in grams, positions in decimal degrees.

from pathlib import Path
import json
import zipfile

import geopandas as gpd
import numpy as np
import pandas as pd
import requests

Path("data").mkdir(exist_ok=True)
# NASA's Meteorite Landings table (The Meteoritical Society's records as NASA last updated them, May 2013), every
# fireball in NASA/JPL's Fireball Data API, and Natural Earth's countries and land. Saved 28 October 2026.
# Live: https://data.nasa.gov/docs/legacy/meteorite_landings/Meteorite_Landings.csv
#       https://ssd-api.jpl.nasa.gov/fireball.api?vel-comp=true
ARCHIVE = "nasa-meteorites-2026-10-28.zip"
if not Path(f"data/{ARCHIVE}").exists():
    Path(f"data/{ARCHIVE}").write_bytes(requests.get(f"https://data.spatialworkflow.io/{ARCHIVE}", timeout=600).content)
Z = zipfile.ZipFile(f"data/{ARCHIVE}")
met = pd.read_csv(Z.open("Meteorite_Landings.csv")).rename(columns={"mass (g)": "mass_g"})
for f in ["ne_110m_admin_0_countries.zip", "ne_50m_land.zip"]:
    Path(f"data/{f}").write_bytes(Z.read(f))
print(f"Meteorite Landings: {len(met):,} meteorites")
Meteorite Landings: 45,716 meteorites

Example 1: records per year

per = met[met.year.between(1970, 2013)].groupby("year").size()
print(per.loc[[1970, 1979, 1988, 1998, 2003, 2006, 2010, 2012, 2013]].to_string())
print(f"recorded before 1970: {(met.year < 1970).sum():,}; 1970-2013: {per.sum():,}")
for y in [1979, 2003]:
    print(y, met[met.year == y].name.str.replace(r"\s*[A-Z]?\d+.*$", "", regex=True).value_counts().head(1).to_dict())
year
1970.0      48
1979.0    3046
1988.0    2296
1998.0    2697
2003.0    3323
2006.0    2456
2010.0    1005
2012.0     234
2013.0      11
recorded before 1970: 2,294; 1970-2013: 43,130
1979 {'Yamato': 2940}
2003 {'Grove Mountains': 1312}

Only 2,294 meteorites carry a year before 1970, and 43,130 from 1970 to 2013. The years jump around: 48 in 1970, 3,046 in 1979, 3,323 in 2003. Those peaks are field seasons, not showers. 2,940 of the 1979 meteorites are named Yamato, collected by a Japanese expedition on the blue ice near the Yamato Mountains in Antarctica, and 1,312 of 2003's come from China's Grove Mountains expedition. The last years are thin, 234 in 2012 and 11 in 2013, because classification lags years behind collection and NASA stopped updating the file.

Bars of meteorites per year from 1970 to 2013, with peaks of 3,046 in 1979 and 3,323 in 2003 and only 11 in 2013. Bars of meteorites per year from 1970 to 2013, with peaks of 3,046 in 1979 and 3,323 in 2003 and only 11 in 2013.
Meteorites per year: the peaks are Antarctic collecting seasons.

Example 2: where the names come from

met["place"] = met.name.str.replace(r"\s*[A-Z]?\d+.*$", "", regex=True)
top = met.place.value_counts().head(8)
print(top.to_string())
print(f"meteorites named after these 8 places: {top.sum():,} ({top.sum() / len(met):.1%})")
place
Yamato                   7269
Northwest Africa         4499
Queen Alexandra Range    3444
Grove Mountains          2494
Elephant Moraine         2187
Miller Range             2038
Lewis Cliff              1870
Allan Hills              1754
meteorites named after these 8 places: 25,555 (55.9%)

A meteorite is named after the place where it fell or was found, and where many are found together they get the place name and a number, such as Yamato 791197. Stripping the number shows how concentrated the collection is: eight places supply 25,555 meteorites, 55.9% of the table. Seven are in Antarctica. The eighth, Northwest Africa, is not a place but a label for meteorites bought from dealers in Morocco and nearby countries, whose find sites are unknown.

Horizontal bars of the eight most common name prefixes: Yamato 7,269, Northwest Africa 4,499, Queen Alexandra Range 3,444 and five more Antarctic fields. Horizontal bars of the eight most common name prefixes: Yamato 7,269, Northwest Africa 4,499, Queen Alexandra Range 3,444 and five more Antarctic fields.
The eight place names behind more than half the table.