A RESTful API built with TypeScript and Deno/Wrangler to query and analyze the NASA Meteorites Landings dataset.
Normally, this dataset comes as a CSV file with over 40,000 entries. I've compiled and cleaned it into a JSON format containing only the data I need for a simple visualization application.
You can found the dataset here and by: NASA Open Data Portal
-
CORS: You're free to use the API in your website or any other project, but daily rate limits still apply.
-
No sign-up, no credit card, or other personal information required.
-
No logs are maintained to track user activity (logs are only for debugging and performance).
-
Rate limiting implemented to prevent API abuse.
-
GDPR compliant: IP addresses are hashed using
SHA-256with a strong, secure key and stored for max a day. -
Accurate Search: You can apply multiple filters to tailor the request as precisely as needed.
The API is available in two versions, each with its own usage details:
- No public online instance is available at the moment.
- Rate limit: 1 request per second, up to 15 requests per day.
- Privacy policy: privacy.md
- Source code: GitHub repository
- No public online instance is available at the moment.
- Rate limit: 1 request per minute, up to 15 requests per day.
- Privacy policy: privacy.md (cf-workers branch)
- Source code: GitHub repository (cf-workers branch)
Search meteorites using various filters, including name, class, date, mass, and geographic location.
| Parameter | Type | Description |
|---|---|---|
recclass |
string | Meteorite classification |
fall |
string | Fall status (Fell or Found) |
year |
number | Exact year the meteorite fell or was found |
minYear |
number | Minimum year for filtering |
maxYear |
number | Maximum year for filtering |
mass |
number | Exact mass in grams |
minMass |
number | Minimum mass in grams |
maxMass |
number | Maximum mass in grams |
centerLatitude |
number | Latitude of the center point for location filtering (required with radius) |
centerLongitude |
number | Longitude of the center point (required with radius) |
radius |
number | Radius in kilometers for location filtering (required with center coords) |
limit |
number | Maximum number of search results (min: 1, max: MAX_RETURNED_SEARCH_RESULTS) |
Invalid, non-required parameters will be completely ignored.
-
200 OK: Successful query with results -
400 Bad Request: Missing or invalid parameters -
404 Not Found: No meteorite data available -
429 Too Many Requests: Rate limit exceeded
curl "http://localhost:8000/search?minYear=1998¢erLatitude=45.0¢erLongitude=5.0&radius=200"{
"success": {
"count": 1,
"meteorites": [
{
"fall": "Fell",
"id": "458",
"latitude": "45.821330",
"longitude": "6.015330",
"mass": "252",
"name": "Alby sur Chรฉran",
"recclass": "Eucrite-mmict",
"year": "2002"
}
]
}
}Retrieve detailed information about a single meteorite by either its unique id or its exact name.
| Parameter | Type | Description |
|---|---|---|
id |
string | Unique identifier of the meteorite |
name |
string | Exact name of the meteorite (case-insensitive, normalized) |
Note: You must provide either
idorname. Supplying both parameters will result in an error. If neither is provided, the request will be rejected.
-
200 OK: Meteorite found and returned -
400 Bad Request: Both parameters are missing or invalid -
404 Not Found: No meteorite matches the given identifier -
429 Too Many Requests: Rate limit exceeded (per second or daily)
Get meteorite by id:
curl "http://localhost:8000/get?id=12345"Get meteorite by name:
curl "http://localhost:8000/get?name=Kopjes%20Vlei"{
"success": {
"meteorite": {
"fall": "Found",
"id": "12345",
"latitude": "-29.300000",
"longitude": "21.150000",
"mass": "13600",
"name": "Kopjes Vlei",
"recclass": "Iron, IIAB",
"year": "1914"
}
}
}Get a random selection of meteorites.
Returns a randomly selected subset of meteorites, limited by a configurable maximum.
| Parameter | Type | Description |
|---|---|---|
count |
number | Number of random meteorites to return. Defaults to config.DEFAULT_RANDOM_NUMBER_OF_METEORITES. Cannot exceed config.MAX_RANDOM_METEORITES. |
Invalid
countparameters will be ignored, and the default value will be applied.
-
200 OK: Successfully returns a random list of meteorites. -
400 Bad Request: Thecountparameter exceeds the maximum allowed number of meteorites. -
404 Not Found: No meteorites data available. -
429 Too Many Requests: Rate limit exceeded.
If the requested count exceeds the maximum allowed, the result will be limited and a note will be included in the response.
curl "http://localhost:8000/random?count=3"{
"success": {
"count": 3,
"meteorites": [
{
"fall": "Fell",
"id": "18013",
"latitude": "38.716670",
"longitude": "-7.066670",
"mass": "150000",
"name": "Olivenza",
"recclass": "LL5",
"year": "1924"
},
{
"fall": "Found",
"id": "7653",
"latitude": "25.223670",
"longitude": "0.845600",
"mass": "1388",
"name": "Djebel Chaab 001",
"recclass": "L/LL6",
"year": "2003"
},
{
"fall": "Fell",
"id": "22793",
"latitude": "23.083330",
"longitude": "91.666670",
"mass": "478",
"name": "Sabrum",
"recclass": "LL6",
"year": "1999"
}
]
}
}Retrieve aggregated statistics about the meteorite dataset stored.
Returns useful insights such as year ranges, mass stats, classification counts, and geolocation information.
| Field | Type | Description |
|---|---|---|
meteorites_count |
number | Total number of meteorites |
min_year, max_year |
string | Earliest and latest year of meteorite fall/found |
min_mass_g, max_mass_g |
number | Smallest and largest mass in grams |
avg_mass_g |
number | Average mass in grams (rounded to 2 decimal places) |
years |
string[] | Sorted list of all available years in the dataset |
years_distribution |
object | Frequency of each classification based on year |
recclasses |
string[] | Sorted list of unique meteorite classifications |
recclasses_distribution |
object | Frequency of each classification based on recclass |
geolocated_count |
number | Number of meteorites with valid latitude and longitude |
fall_counts |
object | Breakdown of meteorites by fall type: fell vs found |
Note: Some meteorites are recorded with a mass of 0 grams. This is not an error, but rather a reflection of specific characteristicsโsuch as extreme alteration, fossilization, or missing recoverable fragments. It's important to recognize that these cases do occur.
-
200 OK: Statistics successfully returned -
404 Not Found: No meteorite data available -
429 Too Many Requests: Rate limit exceeded
curl "http://localhost:8000/stats"{
"success": {
"meteorites_count": 5057,
"min_year": "860",
"max_year": "2013",
"min_mass_g": 0.1,
"max_mass_g": 60000000,
"avg_mass_g": 116343.79,
"years": [
"860",
"920",
"1399",
"1490",
"1491",
...
],
"years_distribution": {
"860": 1,
"920": 1,
"1399": 1,
"1490": 1,
"1491": 1,
...
}
"recclasses": [
"Acapulcoite",
"Achondrite-ung",
"Angrite",
"Aubrite",
"Aubrite-an",
...
],
"recclasses_distribution": {
"L6": 866,
"H5": 778,
"H6": 373,
"H4": 368,
"L5": 341,
...
},
"geolocated_count": 31963,
"fall_counts": {
"fell": 1091,
"found": 3966
}
}
}git clone https://github.com/Nde-Code/meteorites-api.git
cd meteorites-apiCreate a .env file in the root folder with:
FIREBASE_HOST_LINK="YOUR_FIREBASE_URL"
FIREBASE_HIDDEN_PATH="YOUR_SECRET_PATH"
HASH_KEY="THE_KEY_USED_TO_HASH_IPS"- FIREBASE_URL & FIREBASE_HIDDEN_PATH: Firebase database connection info. Make sure
"YOUR_SECRET_PATH"is strong, safe and secure. - HASH_KEY: Key used for IP hashing in rate limiting.
export const config: Config = {
FIREBASE_URL: Deno.env.get("FIREBASE_HOST_LINK") ?? "",
FIREBASE_HIDDEN_PATH: Deno.env.get("FIREBASE_HIDDEN_PATH") ?? "",
HASH_KEY: Deno.env.get("HASH_KEY") ?? "",
RATE_LIMIT_INTERVAL_S: 1, // min: 1
MAX_READS_PER_DAY: 15, // min: 5
IPS_PURGE_TIME_DAYS: 1, // min: 1
FIREBASE_TIMEOUT_MS: 10000, // min: 6000
MAX_RANDOM_METEORITES: 1000, // min: 100
MAX_RETURNED_SEARCH_RESULTS: 300, // min: 100
MIN_RADIUS: 1, // min: 1
MAX_RADIUS: 5000, // min: 1000
DEFAULT_RANDOM_NUMBER_OF_METEORITES: 100 // min: 1000
};-
FIREBASE_URL, FIREBASE_HIDDEN_PATH, HASH_KEY: These are values read from the
.envfile, so please do not modify them. -
RATE_LIMIT_INTERVAL_S in [second]: This is the rate limit based on requests. Currently: one request per second.
-
MAX_READS_PER_DAY in [day]: Daily reading rate limit. Currently: 15 reads per day.
-
IPS_PURGE_TIME_DAYS in [day]: The number of days before purging the
Deno.kvstore that contains hashed IPs used for rate limiting. Currently: 1 day. -
FIREBASE_TIMEOUT_MS in [millisecond]: The timeout limit for HTTP requests to the Firebase Realtime Database. Currently: 10 seconds.
-
MAX_RANDOM_METEORITES: The maximum number of meteorites retrieved from
/random. Currently: 1000 meteorites. -
MAX_RETURNED_SEARCH_RESULTS: The maximum number of meteorites retrieved from
/searchwhen the result set is large. -
MIN_RADIUS & MAX_RADIUS: The minimum and maximum radius values allowed by the API to define the circular search area. Currently:
min = 1andmax = 500. -
DEFAULT_RANDOM_NUMBER_OF_METEORITES: In
/random, if nocountparameter is provided, this is the default number of meteorites retrieved. Currently: 100 meteorites.
Ensure that you respect the min value specified in the comment; otherwise, you will get an error message with your configuration.
-
Go to firebase.google.com and create an account.
(If you already have a Google account, you're good to go.)
-
Create a project and set up a
Realtime Database.๐ If you get stuck, feel free to check out the official Firebase documentation, or search on Google, YouTube, etc.
-
Once your database is ready, go to the
Rulestab and paste the following code in the editor:
{
"rules": {
".read": false,
".write": false,
"YOUR_SECRET_PATH": {
".read": true,
".write": false
}
}
}Here is a brief summary of these rules:
| Rule | Effect |
|---|---|
".read": false |
โ Default: Deny read access to entire database |
".write": false |
โ Default: Deny write access to entire database |
YOUR_SECRET_PATH |
๐ Allows read access under that specific path only |
| โ Still denies write access under that path |
3'. If you choose to complete now (not recommended): go to data/db.json, take the line 2 and replace
meteoritesbyYOUR_SECRET_PATH. In the end, upload you file on your firebase RTDB.โ ๏ธ Be careful: this method will erase everything in your database before loading the new data. If you have other items stored, please make a backup first !!
- (Optional) If you'd like to compile the data yourself, visit NASA's Meteorite Landings dataset and download the
.csvfile.
The compiler.ts file is designed to handle all possible use cases.
If you want to include the entire dataset, you can simply compile everything.
When I originally built this project, it was meant purely for visualization purposes, so I only needed to keep a subset of the data.
To achieve a more balanced distribution and avoid overlapping points, I used a grid-based filtering method.
Just take a look at:
// --- Options: ---
const USE_GRID = true; // Set to false to keep all data (disable grid filtering)
const USE_LIMIT = false; // Set to false to disable record limit
const MAX_RECORDS = 8000; // Maximum number of records if USE_LIMIT = true
const GRID_SIZE = 0.07; // Grid cell size in degrees if USE_GRID = true
// -------------------------------If everything looks good to you, run the following command (be sure that the meteorites.csv file is in data/):
deno task compileIf everything works correctly (with the current meteorites.csv file), you should see:
========================================
Export completed successfully
----------------------------------------
- Output file path : data/db.json
- Records exported : 5057 meteorites
- Records skipped : 40659 meteorites
- Grid filtering : Enabled
- Record limit : Unlimited
========================================
- To store data without importing it through the Firebase interface, you can use:
Before running this command, update your Firebase rules (see 3.): temporarily change the ".write" permission in "YOUR_SECRET_PATH" from false to true to allow the operation. Once complete, revert it back to false to restore write protection.
deno task hostwith this command, data will be added to /YOUR_SECRET_PATH. If your database already contains other entries at this path, they may be overwritten or modified. Be careful, and always keep a backup of your data before proceeding.
First of all, make sure you have the correct
db.jsonfile either downloaded from data/db.json or compiled usingdeno task compile(see 4. for more information).
deno task devThis project is licensed under the Apache License v2.0.
Created and maintained by Nde-Code.
Feel free to reach out for questions or collaboration, or open an issue or pull request and I'll be happy to help.