Configuration reference
August 23, 2026 ยท View on GitHub
node-GTFS accepts configuration objects in JavaScript and JSON configuration files on the command line. Relative paths are resolved from the directory where the process is started.
The repository includes three starting points:
config-sample.jsonfor a minimal static importconfig-sample-full.jsonfor every current, recommended JSON optionconfig-sample-rtupdates.jsonfor a realtime-only update
Replace the example URLs, credentials, and paths before using the full samples.
The db and logFunction options are JavaScript values and cannot be stored in
JSON. The deprecated verbose option is omitted; use logLevel instead.
Below is a minimal configuration:
{
"agencies": [
{
"path": "./data/gtfs.zip"
}
],
"sqlitePath": "./data/gtfs.sqlite"
}
agencies is required for static imports. sqlitePath is strongly recommended
for command-line use because the default is an in-memory database.
Warning: A static import drops and recreates the GTFS tables in its destination database. It does not preserve changes made directly to those tables. Use a new database or create a backup before importing.
Top-level options
| Option | Type | Default | Used by | Description |
|---|---|---|---|---|
agencies | array | required | import, realtime | GTFS feeds to process |
sqlitePath | string | :memory: | SQLite operations | SQLite database path |
db | better-sqlite3 database | none | JavaScript SQLite operations | Existing connection to use |
csvOptions | object | {} | static import | Options passed to csv-parse |
downloadTimeout | number | 30000 | import, realtime | Download timeout in milliseconds |
exportPath | string | generated | export | Directory for exported files |
gtfsRealtimeExpirationSeconds | number | 0 | realtime | Age at which old realtime rows may be deleted |
ignoreDuplicates | boolean | false | static import | Skip rows that violate a unique key |
ignoreErrors | boolean | false | import, realtime | Log an error and continue where supported |
includeImportReport | boolean | false | static import | Return an ImportReport from JavaScript |
logLevel | string | info | all operations | silent, error, warning, or info |
logFunction | function | console | JavaScript operations | Receive non-progress log messages |
verbose | boolean | none | all operations | Deprecated; use logLevel |
You can add your own unrelated config options - unknown keys are ignored, although likely misspellings of known options produce a warning.
Feed options
Each static feed must have exactly one url or path.
| Option | Type | Description |
|---|---|---|
url | string | URL of a GTFS ZIP file |
path | string | Local ZIP file or directory containing GTFS files |
headers | object | HTTP request headers for the static feed |
prefix | string | Prefix applied to identifiers when feeds are merged |
exclude | string array | Database table names to skip, without .txt |
fillEmptyAgencyId | boolean | Fill missing agency_id values in a single-agency feed |
agencyId | string | Fallback ID used with fillEmptyAgencyId |
realtimeAlerts | object | Alert endpoint and optional headers |
realtimeTripUpdates | object | Trip update endpoint and optional headers |
realtimeVehiclePositions | object | Vehicle position endpoint and optional headers |
Import from a URL
{
"agencies": [
{
"url": "https://www.bart.gov/dev/schedules/google_transit.zip"
}
],
"sqlitePath": "./gtfs.sqlite"
}
Import from a ZIP file or directory
{
"agencies": [
{
"path": "./data/gtfs.zip"
}
],
"sqlitePath": "./data/gtfs.sqlite"
}
Change path to a directory such as ./data/unzipped-gtfs when the feed has
already been extracted.
Send HTTP headers
{
"agencies": [
{
"url": "https://example.com/gtfs.zip",
"headers": {
"Authorization": "Bearer replace-with-your-token"
}
}
],
"sqlitePath": "./gtfs.sqlite"
}
Do not commit credentials to source control. Load configuration from a secure location in production.
Merge multiple feeds
Use a different prefix for each feed when their identifiers may overlap:
{
"agencies": [
{
"path": "./data/north.zip",
"prefix": "north"
},
{
"path": "./data/south.zip",
"prefix": "south"
}
],
"sqlitePath": "./regional.sqlite"
}
Only schema fields marked applyFeedPrefix are changed. The prefix is also
applied to corresponding references.
Exclude large or unused tables
exclude uses database table names, not filenames:
{
"agencies": [
{
"path": "./data/gtfs.zip",
"exclude": ["shapes", "fare_attributes"]
}
],
"sqlitePath": "./gtfs.sqlite"
}
Fill empty agency IDs
Some valid single-agency feeds leave agency_id blank throughout the GTFS.
Set fillEmptyAgencyId to fill those values:
{
"agencies": [
{
"path": "./data/gtfs.zip",
"fillEmptyAgencyId": true,
"agencyId": "metro-transit"
}
],
"sqlitePath": "./gtfs.sqlite"
}
An ID found in agency.txt takes precedence over agencyId.
Import GTFS-realtime data too
{
"agencies": [
{
"url": "https://svc.metrotransit.org/mtgtfs/gtfs.zip",
"realtimeAlerts": {
"url": "https://svc.metrotransit.org/mtgtfs/alerts.pb"
},
"realtimeTripUpdates": {
"url": "https://svc.metrotransit.org/mtgtfs/tripupdates.pb"
},
"realtimeVehiclePositions": {
"url": "https://svc.metrotransit.org/mtgtfs/vehiclepositions.pb"
}
}
],
"sqlitePath": "./gtfs.sqlite"
}
See the GTFS-Realtime guide for more details.
Existing SQLite connection
Pass a caller-owned better-sqlite3 connection with db. This is available
only from JavaScript, not JSON:
import Database from 'better-sqlite3';
import { importGtfs } from 'gtfs';
const db = new Database('./data/gtfs.sqlite');
try {
await importGtfs({
agencies: [{ path: './data/gtfs.zip' }],
db,
});
} finally {
db.close();
}
The caller remains responsible for the connection lifecycle.
CSV parser options
csvOptions is passed to csv-parse.
For example:
{
"agencies": [{ "path": "./data/gtfs.zip" }],
"sqlitePath": "./gtfs.sqlite",
"csvOptions": {
"skip_empty_lines": true
}
}
Parser options can change which data is accepted. Validate the resulting record counts when relaxing parsing behavior.
Error handling
With ignoreErrors: false, an import stops at the first error. With
ignoreErrors: true, node-GTFS logs errors and continues with later work where
possible. A parser or database error can cause the rest of the current file or
batch to be skipped, and previously committed batches remain in the database.
The result may therefore be partial.
Use includeImportReport in JavaScript to inspect the result:
import { importGtfs } from 'gtfs';
const report = await importGtfs({
agencies: [{ path: './data/gtfs.zip' }],
sqlitePath: './gtfs.sqlite',
ignoreErrors: true,
includeImportReport: true,
});
console.log(report.errors);
In TypeScript, the literal includeImportReport: true changes the return type
to Promise<ImportReport>.
ignoreDuplicates skips records that violate a declared unique key. It does
not make other parse or database errors non-fatal.
Logging
logLevel accepts silent, error, warning, or info. Errors and warnings
use stderr; normal status output uses stdout.
JavaScript applications can redirect non-progress messages:
import { importGtfs } from 'gtfs';
await importGtfs({
agencies: [{ path: './data/gtfs.zip' }],
sqlitePath: './gtfs.sqlite',
logLevel: 'warning',
logFunction(message, level) {
console.log(`[${level}] ${message}`);
},
});
The level argument is optional, so existing logFunction(message) callbacks
continue to work.
verbose remains available for compatibility. Use logLevel in new code.
Export path
If exportPath is omitted, exports go to
gtfs-export/<first-agency-name> under the current directory. The export
directory is cleared before files are written.
{
"sqlitePath": "./gtfs.sqlite",
"exportPath": "./output/gtfs"
}
Command-line precedence
The commands look for ./config.json in the current working directory unless
--configPath is supplied. Direct command-line options override values with
the same name in the selected configuration file.
npx gtfs-import \
--configPath ./config.json \
--sqlitePath ./temporary.sqlite \
--logLevel warning
Run a command with --help for its current options.