NASA Near Earth Objects¶
NASA's Near Earth Object Web Service provides data about asteroids and comets that pass close to Earth. This dataset is excellent for learning jpx because it has:
- Deeply nested data structures
- Multiple unit systems (km, miles, AU, lunar distances)
- Boolean flags for hazard classification
- Time series data (close approach dates)
- Complex orbital mechanics data
Getting the Data¶
# Browse all NEOs (paginated)
curl -s "https://api.nasa.gov/neo/rest/v1/neo/browse?api_key=DEMO_KEY" > neo_browse.json
# Get NEOs by close approach date (today)
curl -s "https://api.nasa.gov/neo/rest/v1/feed?api_key=DEMO_KEY" > neo_feed.json
# Get specific date range
curl -s "https://api.nasa.gov/neo/rest/v1/feed?start_date=2024-01-01&end_date=2024-01-07&api_key=DEMO_KEY" > neo_week.json
# Look up specific asteroid
curl -s "https://api.nasa.gov/neo/rest/v1/neo/2000433?api_key=DEMO_KEY" > eros.json
Note: Use
DEMO_KEYfor testing (limited rate). Get a free API key at api.nasa.gov.
Data Structure¶
The browse endpoint returns:
{
"links": {"self": "...", "next": "..."},
"page": {
"size": 20,
"total_elements": 41839,
"total_pages": 2092,
"number": 0
},
"near_earth_objects": [
{
"id": "2000433",
"name": "433 Eros (A898 PA)",
"name_limited": "Eros",
"nasa_jpl_url": "https://ssd.jpl.nasa.gov/...",
"absolute_magnitude_h": 10.38,
"estimated_diameter": {
"kilometers": {"estimated_diameter_min": 22.31, "estimated_diameter_max": 49.89},
"meters": {...},
"miles": {...},
"feet": {...}
},
"is_potentially_hazardous_asteroid": false,
"close_approach_data": [
{
"close_approach_date": "2024-01-15",
"relative_velocity": {
"kilometers_per_second": "5.57",
"kilometers_per_hour": "20083.02",
"miles_per_hour": "12478.81"
},
"miss_distance": {
"astronomical": "0.314",
"lunar": "122.5",
"kilometers": "47112732",
"miles": "29274494"
},
"orbiting_body": "Earth"
}
]
}
]
}
Basic Queries¶
List Asteroid Names¶
Get Short Names¶
Output:
Count NEOs in Response¶
Output: 41839 (total known NEOs!)
Filtering¶
Find Potentially Hazardous Asteroids (PHAs)¶
jpx 'near_earth_objects[?is_potentially_hazardous_asteroid == `true`].{
name: name_limited,
diameter_km: estimated_diameter.kilometers.estimated_diameter_max
}' neo_browse.json
Find Large Asteroids (> 1km)¶
jpx 'near_earth_objects[?estimated_diameter.kilometers.estimated_diameter_max > `1`].{
name: name,
max_diameter_km: estimated_diameter.kilometers.estimated_diameter_max,
hazardous: is_potentially_hazardous_asteroid
}' neo_browse.json
Find Non-Hazardous Objects¶
jpx 'near_earth_objects[?is_potentially_hazardous_asteroid == `false`].name_limited' neo_browse.json
Working with Nested Data¶
Extract Diameter Estimates¶
The API provides diameters in multiple units:
jpx 'near_earth_objects[*].{
name: name_limited,
min_km: estimated_diameter.kilometers.estimated_diameter_min,
max_km: estimated_diameter.kilometers.estimated_diameter_max
}' neo_browse.json
Calculate Average Diameter¶
jpx 'near_earth_objects[*].{
name: name_limited,
avg_diameter_km: divide(
add(
estimated_diameter.kilometers.estimated_diameter_min,
estimated_diameter.kilometers.estimated_diameter_max
),
`2`
)
}' neo_browse.json
Get Diameter in Different Units¶
jpx 'near_earth_objects[0].{
name: name,
in_km: estimated_diameter.kilometers,
in_meters: estimated_diameter.meters,
in_miles: estimated_diameter.miles
}' neo_browse.json
Close Approach Data¶
Get Next Close Approach¶
jpx 'near_earth_objects[*].{
name: name_limited,
next_approach: close_approach_data[0].close_approach_date,
miss_distance_km: close_approach_data[0].miss_distance.kilometers
}' neo_browse.json
Find Closest Approaches (in Lunar Distances)¶
One lunar distance = 384,400 km (distance to the Moon):
jpx 'near_earth_objects[*].close_approach_data[*].{
asteroid: @.@.@.name_limited,
date: close_approach_date,
lunar_distances: miss_distance.lunar
} | flatten(@)' neo_browse.json
Sort by Miss Distance¶
jpx 'near_earth_objects[*].{
name: name_limited,
miss_km: to_number(close_approach_data[0].miss_distance.kilometers)
} | sort_by(@, &miss_km) | [:10]' neo_browse.json
Find High-Speed Approaches¶
jpx 'near_earth_objects[*].{
name: name_limited,
speed_kph: to_number(close_approach_data[0].relative_velocity.kilometers_per_hour),
date: close_approach_data[0].close_approach_date
} | [?speed_kph > `50000`]' neo_browse.json
Statistics¶
Size Statistics¶
jpx '{
count: length(near_earth_objects),
smallest_km: min(near_earth_objects[*].estimated_diameter.kilometers.estimated_diameter_min),
largest_km: max(near_earth_objects[*].estimated_diameter.kilometers.estimated_diameter_max),
avg_max_km: round(avg(near_earth_objects[*].estimated_diameter.kilometers.estimated_diameter_max), `2`)
}' neo_browse.json
Hazard Statistics¶
jpx '{
total: length(near_earth_objects),
hazardous: length(near_earth_objects[?is_potentially_hazardous_asteroid == `true`]),
safe: length(near_earth_objects[?is_potentially_hazardous_asteroid == `false`]),
hazard_percentage: multiply(
divide(
length(near_earth_objects[?is_potentially_hazardous_asteroid == `true`]),
length(near_earth_objects)
),
`100`
)
}' neo_browse.json
Magnitude Distribution¶
Absolute magnitude (H) indicates brightness/size:
jpx '{
magnitudes: near_earth_objects[*].absolute_magnitude_h,
min_mag: min(near_earth_objects[*].absolute_magnitude_h),
max_mag: max(near_earth_objects[*].absolute_magnitude_h),
avg_mag: round(avg(near_earth_objects[*].absolute_magnitude_h), `2`)
}' neo_browse.json
Sorting and Ranking¶
Top 5 Largest Asteroids¶
jpx 'sort_by(near_earth_objects, &estimated_diameter.kilometers.estimated_diameter_max)
| reverse(@)
| [:5]
| [*].{
name: name,
max_diameter_km: round(estimated_diameter.kilometers.estimated_diameter_max, `2`),
hazardous: is_potentially_hazardous_asteroid
}' neo_browse.json
Brightest Objects (Lowest Magnitude)¶
jpx 'sort_by(near_earth_objects, &absolute_magnitude_h) | [:5] | [*].{
name: name_limited,
magnitude: absolute_magnitude_h,
diameter_km: estimated_diameter.kilometers.estimated_diameter_max
}' neo_browse.json
Unit Conversions¶
Convert AU to Kilometers¶
1 AU (Astronomical Unit) = ~149,597,870.7 km:
jpx 'near_earth_objects[*].{
name: name_limited,
miss_au: to_number(close_approach_data[0].miss_distance.astronomical),
miss_km: to_number(close_approach_data[0].miss_distance.kilometers)
}' neo_browse.json
Velocity Comparisons¶
jpx 'near_earth_objects[*].{
name: name_limited,
km_per_sec: to_number(close_approach_data[0].relative_velocity.kilometers_per_second),
km_per_hour: to_number(close_approach_data[0].relative_velocity.kilometers_per_hour),
mph: to_number(close_approach_data[0].relative_velocity.miles_per_hour)
}' neo_browse.json
Data Transformation¶
Flatten for Analysis¶
jpx 'near_earth_objects[*].{
id: id,
name: name_limited,
magnitude: absolute_magnitude_h,
diameter_min_km: estimated_diameter.kilometers.estimated_diameter_min,
diameter_max_km: estimated_diameter.kilometers.estimated_diameter_max,
is_hazardous: is_potentially_hazardous_asteroid,
next_approach_date: close_approach_data[0].close_approach_date,
miss_distance_km: close_approach_data[0].miss_distance.kilometers,
velocity_kph: close_approach_data[0].relative_velocity.kilometers_per_hour,
jpl_url: nasa_jpl_url
}' neo_browse.json
Export to CSV¶
jpx --csv 'near_earth_objects[*].{
name: name_limited,
magnitude: absolute_magnitude_h,
max_diameter_km: estimated_diameter.kilometers.estimated_diameter_max,
is_hazardous: is_potentially_hazardous_asteroid,
approach_date: close_approach_data[0].close_approach_date
}' neo_browse.json
Advanced Pipelines¶
Risk Assessment Report¶
jpx '{
report_title: `Near Earth Object Risk Assessment`,
total_objects: page.total_elements,
sample_size: length(near_earth_objects),
potentially_hazardous: {
count: length(near_earth_objects[?is_potentially_hazardous_asteroid == `true`]),
objects: near_earth_objects[?is_potentially_hazardous_asteroid == `true`] | [*].{
name: name,
diameter_km: round(estimated_diameter.kilometers.estimated_diameter_max, `2`),
next_approach: close_approach_data[0].close_approach_date,
miss_lunar: close_approach_data[0].miss_distance.lunar
}
},
largest_objects: sort_by(near_earth_objects, &estimated_diameter.kilometers.estimated_diameter_max)
| reverse(@)
| [:3]
| [*].{name: name_limited, km: round(estimated_diameter.kilometers.estimated_diameter_max, `1`)}
}' neo_browse.json
Pipeline: Filter, Sort, Transform¶
jpx 'near_earth_objects
| [?estimated_diameter.kilometers.estimated_diameter_max > `0.5`]
| [?is_potentially_hazardous_asteroid == `true`]
| sort_by(@, &estimated_diameter.kilometers.estimated_diameter_max)
| reverse(@)
| [*].{
name: name,
size_category: `LARGE PHA`,
diameter_range: concat(
to_string(round(estimated_diameter.kilometers.estimated_diameter_min, `1`)),
` - `,
to_string(round(estimated_diameter.kilometers.estimated_diameter_max, `1`)),
` km`
)
}' neo_browse.json
Working with the Feed Endpoint¶
The feed endpoint returns NEOs by date:
curl -s "https://api.nasa.gov/neo/rest/v1/feed?start_date=2024-01-15&end_date=2024-01-16&api_key=DEMO_KEY" > neo_day.json
Count by Date¶
List All Dates with Objects¶
Get Objects for Specific Date¶
Using Query Libraries¶
Instead of typing these queries repeatedly, save them in a .jpx query library. See examples/nasa-neo.jpx for a ready-to-use library:
# List available queries
jpx -Q examples/nasa-neo.jpx --list-queries
# Run common analyses
jpx -Q examples/nasa-neo.jpx:hazardous neo_browse.json
jpx -Q examples/nasa-neo.jpx:risk-report neo_browse.json
jpx -Q examples/nasa-neo.jpx:top-5-largest neo_browse.json
# Output as table
jpx -Q examples/nasa-neo.jpx:closest-approaches -t neo_browse.json
See Query Files for more on creating and using query libraries.
Try It Yourself¶
-
Get your free API key: api.nasa.gov
-
Interesting queries to try:
- Find the closest approach ever recorded
- List all PHAs larger than 500 meters
-
Calculate average velocity of approaching objects
-
API endpoints:
/neo/browse- Paginated list of all NEOs/neo/rest/v1/feed- NEOs by approach date-
/neo/{id}- Specific asteroid details -
API Documentation: NASA NEO API