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 (with ~30k of entries) to make it more useful and portable.
You can found the dataset here and by: NASA Open Data Portal
Iโve used this API in one of my projects: https://nde-code.github.io/online/meteorites-map (this is in French, which is my native language...)
-
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. -
Data Purge: The
db.jsonfile stored in Firebase Realtime Database has been cleaned to remove incomplete records (e.g., missing location, ...). -
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
- Public endpoint: https://meteorites.nde-code.workers.dev/
- 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 "https://meteorites.nde-code.workers.dev/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"
}
]
}
}The
MAX_RETURNED_SEARCH_RESULTSparameter is set to1000on my instance.
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 "https://meteorites.nde-code.workers.dev/get?id=12345"Get meteorite by name:
curl "https://meteorites.nde-code.workers.dev/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 "https://meteorites.nde-code.workers.dev/random?count=3"{
"success": {
"count": 3,
"meteorites": [
{
"fall": "Found",
"id": "20816",
"latitude": "-84.000000",
"longitude": "168.000000",
"mass": "8.9",
"name": "Queen Alexandra Range 97358",
"recclass": "L6",
"year": "1997"
},
{
"fall": "Found",
"id": "1738",
"latitude": "-76.716670",
"longitude": "159.666670",
"mass": "18.399999999999999",
"name": "Allan Hills A78123",
"recclass": "H5",
"year": "1978"
},
{
"fall": "Found",
"id": "19196",
"latitude": "-84.573660",
"longitude": "162.249660",
"mass": "5.4",
"name": "Queen Alexandra Range 93107",
"recclass": "H6",
"year": "1993"
}
]
}
}Retrieve aggregated statistics about the entire meteorite dataset.
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. In such instances, the API automatically excludes entries with a mass of 0 from statistical analyses.
-
200 OK: Statistics successfully returned -
404 Not Found: No meteorite data available -
429 Too Many Requests: Rate limit exceeded
curl "https://meteorites.nde-code.workers.dev/stats"{
"success": {
"meteorites_count": 31963,
"min_year": "860",
"max_year": "2013",
"min_mass_g": 0.01,
"max_mass_g": 60000000,
"avg_mass_g": 18659.45,
"years": [
"860",
"920",
"1399",
"1490",
"1491",
"1495",
...
],
"years_distribution": {
"860": 1,
"920": 1,
"1399": 1,
"1490": 1,
...
}
"recclasses": [
"Acapulcoite",
"Acapulcoite/Lodranite",
"Achondrite-ung",
"Angrite",
"Aubrite",
"Aubrite-an",
"Brachinite",
...
],
"recclasses_distribution": {
"L6": 6598,
"H5": 5598,
"H4": 3343,
"H6": 3055,
"L5": 2771,
"LL5": 1899,
"LL6": 963,
...
},
"geolocated_count": 31963,
"fall_counts": {
"fell": 1095,
"found": 30868
}
}
}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
meteorites_keybyYOUR_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.
Then, 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 : 31,963 meteorites
- Records skipped : 13,753 meteorites
========================================
- 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.