A fast and flexible Node.js CLI tool for batch reverse geocoding geographic coordinates from CSV files using the Geoapify Reverse Geocoding API.
🧰 Use as a command-line utility or import as a Node.js module.
- 🚀 Features
- 📦 Installation & Dependencies
- 📁 Usage
- 📄 CSV Format
- 📝 Output Formats
⚠️ Error Handling & Fallbacks- 📊 Sample Logs
- 🔐 API Access Requirements
- Read coordinates from CSV files with customizable column mapping (
lat,lon, etc.) - Built-in rate limiting using
@geoapify/request-rate-limiter(default: 5 requests/second) - Automatically retries failed requests with configurable retry limits
- Supports multiple output formats: JSON, NDJSON, or console
- Preserves the original row order in the output
- Skips invalid or empty coordinates with clear warning logs
- Modular architecture with reusable utility modules
- Can be used via CLI or as a Node.js module
- Node.js v18+ — Required for native
fetch()support - Geoapify API Key — Get one at https://www.geoapify.com
This project uses the following NPM packages (already listed in package.json):
@geoapify/request-rate-limiter– Handles request throttlingcommander– CLI argument parsingcsv-parser– Efficient CSV file parsing
In the project root, run:
npm installYou can use the reverse geocoder as a command-line tool or as a Node.js module in your own scripts.
node reverse-geocoder.js -k YOUR_API_KEY -i coordinates.csvnode reverse-geocoder.js \
--api-key YOUR_API_KEY \
--input coordinates.csv \
--output results.ndjson \
--rate-limit 5 \
--output-format ndjson \
--max-retries 3| Option | Short | Description | Default |
|---|---|---|---|
--api-key |
-k |
(Required) Your Geoapify API key | – |
--input |
-i |
(Required) Path to CSV file with coordinates | – |
--output |
-o |
Path to write reverse geocoding results | – |
--rate-limit |
-r |
Requests per second (RPS) | 5 |
--output-format |
-f |
Output format: json, ndjson, console |
ndjson |
--no-retry |
Disable automatic retries for failed requests | false | |
--max-retries |
Max retry attempts per failed request | 3 |
You can also import the geocoder into your own Node.js script:
const { reverseGeocodeCoordinates } = require('./reverse-geocoder');
async function main() {
const results = await reverseGeocodeCoordinates({
inputFile: 'coordinates.csv',
outputFile: 'results.json',
apiKey: 'YOUR_API_KEY',
rateLimit: 5,
outputFormat: 'json',
retryFailedRequests: true,
maxRetries: 3
});
console.log(`Geocoded ${results.length} coordinates`);
}The input CSV file should contain latitude and longitude values for each point to be reverse geocoded.
By default, the script expects the following column headers:
lat,lon
40.7128,-74.0060
34.0522,-118.2437
41.8781,-87.6298If your file uses different column names (e.g., latitude, longitude), you can change the mapping in the configuration section of the script or modify the CLI version to support dynamic mapping.
Only valid numeric values within the coordinate range will be processed:
- Latitude: -90 to 90
- Longitude: -180 to 180
Invalid or empty coordinates will be skipped and logged with a warning.
You can choose between three output formats using the --output-format (-f) option: json, ndjson, or console.
A single JSON array containing all results. Recommended for smaller datasets or downstream systems expecting full structured data.
[
{
"success": true,
"originalCoordinates": [40.7128, -74.0060],
"result": {
"formatted": "New York, NY 10007, United States of America",
"city": "New York",
"state": "New York",
"country": "United States",
"postcode": "10007",
"lat": 40.7128,
"lon": -74.0060
},
"originalRow": { "lat": "40.7128", "lon": "-74.0060" }
}
]Each result is written on a separate line as a standalone JSON object. Ideal for streaming or processing large datasets.
{"success":true,"originalCoordinates":[40.7128,-74.0060],"result":{...}}
{"success":false,"originalCoordinates":[null,null],"error":"Invalid coordinates"}
If no --output path is specified and --output-format is set to console, results are printed to stdout.
The script includes robust mechanisms to handle common issues and ensure consistent results, even when individual lookups fail.
-
Empty or invalid coordinates Rows with missing or invalid
lat/lonvalues are skipped and logged with a warning. -
Network or API errors Failures due to timeouts, DNS errors, or API availability are retried (up to
--max-retriestimes). -
No results from API If no address is found for given coordinates, the result is logged with a descriptive error and included in the output with
"success": false. -
Rate limiting (HTTP 429) Automatically throttled using the
@geoapify/request-rate-limiterto avoid exceeding Geoapify API limits. -
File read/write errors Clear fatal errors are logged if input/output files are inaccessible or invalid.
- Output files (JSON or NDJSON) always match the order of the original input rows.
- Even failed entries preserve
originalRowandoriginalCoordinateswith anerrorfield describing the issue. - Retry attempts are logged individually and limited by
--max-retries.
The script logs each step with timestamps and clear status indicators. Below is an example log from a typical reverse geocoding run:
[2025-07-14T12:00:00.000Z] INFO: Read 3 rows from coordinates.csv
[2025-07-14T12:00:00.050Z] INFO: Starting reverse geocoding of 3 coordinates at 5 requests per second
[2025-07-14T12:00:00.200Z] INFO: ✓ Geocoded: 40.7128,-74.0060
[2025-07-14T12:00:00.400Z] INFO: ✓ Geocoded: 34.0522,-118.2437
[2025-07-14T12:00:00.600Z] WARNING: ✗ Failed: Invalid coordinates - No results found
[2025-07-14T12:00:00.700Z] INFO: Retry attempt 1/3: 1 failed requests...
[2025-07-14T12:00:01.000Z] WARNING: ✗ Retry failed: Invalid coordinates - No results found
[2025-07-14T12:00:01.100Z] INFO: Geocoding complete: 2 successful, 1 failed
[2025-07-14T12:00:01.200Z] INFO: Results written to results.ndjson
INFO— Standard messages: file read, request success, summaryWARNING— Non-fatal issues: failed lookups, skipped rows, retriesERROR— Critical problems that stop the script: file not found, invalid input, API key missing
This tool uses the Geoapify Reverse Geocoding API.
-
Geoapify API Key — Required for authenticating all requests Get your free API key at geoapify.com
-
Rate Limits:
- Free plan allows up to 5 requests per second (soft limit)
- Daily quota: up to 3,000 requests per day on the Free plan
- Use the
--rate-limitoption to adjust request frequency
-
Node.js version Requires Node.js 18+ for native
fetch()support
- Avoid hardcoding your API key in public code repositories
- Use environment variables or CLI flags (
--api-key) to pass your key securely - If processing large datasets, consider:
- Increasing
--rate-limitwith a paid plan - Splitting the workload into multiple batches
- Scheduling jobs to avoid hitting daily limits
- Increasing