TRAINTRACKER API
Submit train sightings programmatically from railcams and other automated sources.
Getting Started
To use the TrainTracker API, you need an API key. Contact us at api@traintrackerapp.com to request access.
Include your API key in every request using the X-API-Key header. Your key will start with tt_.
Authentication
Submit a Sighting
/api/v1/sightingsRequest Body
{
"locomotive_entries": [
{
"number": "4014",
"railroad": "BNSF"
},
{
"number": "7226",
"railroad": "UP"
}
],
"railroad": "BNSF",
"train_symbol": "Q-CHILA1",
"direction": "E",
"description": "Spotted on railcam",
"photo_url": "https://example.com/photo.jpg",
"latitude": 41.05,
"longitude": -95.97,
"location_name": "Galesburg, IL",
"sighted_at": "2026-06-11T12:00:00Z",
"axle_count": 450,
"milepost": 152.3
}Fields
| Field | Type | Required | Description |
|---|---|---|---|
| locomotive_entries | array | Required | Ordered list of locomotives, leading engine first. Each entry: { number, railroad }. If railroad is omitted on an entry, it defaults to the top-level railroad. |
| railroad | string | Optional | Default railroad short name (e.g. BNSF, UP, CSX). Used for entries without a railroad specified. Falls back to API key default. |
| latitude | number | Required | Latitude of the camera/sighting location. |
| longitude | number | Required | Longitude of the camera/sighting location. |
| train_symbol | string | Train symbol (e.g. Q-CHILA1). Max 50 characters. Auto-normalized. | |
| direction | string | Direction of travel: N, NE, E, SE, S, SW, W, NW | |
| description | string | Description text. Max 500 characters. | |
| photo_url | string | URL to a photo of the sighting. | |
| youtube_url | string | YouTube video URL (e.g. livestream clip). Max 500 characters. | |
| location_name | string | Human-readable location name (e.g. "Belen, NM"). Recommended. | |
| sighted_at | string | ISO 8601 timestamp. Defaults to current time. Cannot be in the future. | |
| axle_count | integer | Total axle count of the train. Must be between 1 and 9999. | |
| milepost | number | Milepost marker at the sighting location (e.g. 152.3). | |
| parent_sighting_id | string | UUID of a previous sighting to link as a re-spot. | |
| tag_ids | array | Array of tag UUIDs to apply to the sighting. |
Response
{
"id": "uuid-of-new-sighting",
"status": "created"
}Locomotive Entries
The locomotive_entries array is how you submit locomotive numbers. List them in order, with the leading engine first.
Entry Fields
| Field | Type | Required | Description |
|---|---|---|---|
| number | string | Required | The locomotive number (e.g. "4014"). |
| railroad | string | Optional | Railroad short name for this locomotive (e.g. "UP"). If omitted, defaults to the top-level railroad field. |
Single Railroad
When all locomotives belong to the same railroad, set railroad at the top level. You don't need to repeat it on each entry.
{
"railroad": "BNSF",
"locomotive_entries": [
{
"number": "4014"
},
{
"number": "7226"
},
{
"number": "1221"
}
]
}Mixed Railroads (Foreign Power)
When locomotives from different railroads are in the consist, specify railroad on each entry that differs from the top-level railroad.
{
"railroad": "BNSF",
"locomotive_entries": [
{
"number": "4014"
},
{
"number": "8102",
"railroad": "UP"
},
{
"number": "3288",
"railroad": "CSX"
}
]
}In this example, 4014 is BNSF (from the top-level railroad), while 8102 is UP and 3288 is CSX.
Example (cURL)
Upload a Photo
/api/v1/uploadUpload a photo and receive a URL to include in your sighting submission. Send as multipart/form-data.
Request
| Field | Type | Description |
|---|---|---|
| file | file | Image file (jpg, jpeg, png, webp, gif). Max 10MB. |
Response
{
"url": "https://...supabase.co/storage/v1/object/public/train-sighting-photos/..."
}Example
Response Codes
| Status | Meaning |
|---|---|
| 201 | Sighting created successfully |
| 400 | Invalid request (missing fields, bad data) |
| 401 | Invalid or missing API key |
| 429 | Rate limit exceeded |
| 500 | Server error |
Rate Limits
Default rate limit is 60 requests per hour. This can be adjusted per API key based on your needs. Contact us if you need a higher limit.
Contact
Questions about the API? Contact us at api@traintrackerapp.com