Utilities for NYC's realtime MTA data feeds.
Project description
Python MTA Utilities
This is a set of Python utilities that I use to deal with real-time NYC subway data.
I usually want to know when trains are going to depart a specific stop along a specific train line, so right now the tools are mostly for that. But I tried to write them to support arbitrary functionality.
The same code can also load the bus time data, if you forgive
the mismatched naming conventions (e.g., a class called SubwayFeed holding a bus route).
Install
pip install underground
Or if you'd like to live dangerously:
pip install git+https://github.com/nolanbconaway/underground.git#egg=underground
Python API
Use the Python API like:
import os
from underground import metadata, SubwayFeed
ROUTE = 'Q'
feed = SubwayFeed.get(ROUTE)
# under the hood, the Q route is mapped to a URL. This call is equivalent:
URL = 'https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-nqrw'
feed = SubwayFeed.get(URL)
# or
URL = metadata.resolve_url(ROUTE)
feed = SubwayFeed.get(URL)
List train stops on each line
feed.extract_stop_dict will return a dictionary of dictionaries, like:
>>> feed.extract_stop_dict()
{
"route_1": {
"stop_1": [datetime.datetime(...), datetime.datetime(...)],
"stop_2": [datetime.datetime(...), datetime.datetime(...)],
...
},
"route_2": {
"stop_1": [datetime.datetime(...), datetime.datetime(...)],
"stop_2": [datetime.datetime(...), datetime.datetime(...)],
...
}
}
CLI
The underground command line tool is also installed with the package.
feed
$ underground feed --help
Usage: underground feed [OPTIONS] ROUTE_OR_URL
Request an MTA feed via a route or URL.
ROUTE_OR_URL may be either a feed URL or a route (which will be used to look
up the feed url). ROUTE_OR_URL may also be "BUS" to access the bus feed.
Examples (both access the same feed):
underground feed Q --json > feed_nrqw.json
URL='https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-nqrw' &&
underground feed $URL --json > feed_nrqw.json
Options:
--json Option to output the feed data as JSON. Otherwise
output will be bytes.
-r, --retries INTEGER Retry attempts in case of API connection failure.
Default 100.
--help Show this message and exit.
stops
$ underground stops --help
Usage: underground stops [OPTIONS] ROUTE
Print out train departure times for all stops on a subway line.
Options:
-f, --format TEXT strftime format for stop times. Use `epoch`
for a unix timestamp.
-r, --retries INTEGER Retry attempts in case of API connection
failure. Default 100.
-t, --timezone TEXT Output timezone. Ignored if --epoch. Default
to NYC time.
-s, --stalled-timeout INTEGER Number of seconds between the last movement
of a train and the API update before
considering a train stalled. Default is 90 as
recommended by the MTA. Numbers less than 1
disable this check.
--bus Set if the route is a bus route.
--help Show this message and exit.
Stops are printed to stdout in the format stop_id t1 t2 ... tn .
$ underground stops Q | tail -2
Q05S 19:01 19:09 19:16 19:25 19:34 19:44 19:51 19:58
Q04S 19:03 19:11 19:18 19:27 19:36 19:46 19:53 20:00
If you know your stop id (stop IDs can be found in stops.txt), you can grep the results:
$ underground stops Q | grep Q05S
Q05S 19:09 19:16 19:25 19:34 19:44 19:51 19:58
If you don't know your stop, see below for a handy tool!
findstops
$ underground findstops --help
Usage: underground findstops [OPTIONS] QUERY...
Find your stop ID.
Query a location and look for your stop ID, like:
$ underground findstops parkside av
Options:
--json Option to output the data as JSON. Otherwise will be human readable
table.
--buses Option to also search bus stops. Slower.
--help Show this message and exit.
Enter the name of your stop and a table of stops with matching names will be returned.
$ underground findstops parkside
ID: D27N Direction: NORTH Lat/Lon: 40.655292, -73.961495 Name: PARKSIDE AV
ID: D27S Direction: SOUTH Lat/Lon: 40.655292, -73.961495 Name: PARKSIDE AV
Some names are ambiguous (try "fulton st"), for these you'll have to dig into the metadata more carefully.
Bus support
underground was initially written for the MTA subway feeds. However, contributors to the package identified that some level of bus support could be achieved with minimal maintenance burden. Currently, underground supports the bus feed on a best-effort basis.
Python API
All bus lines share the same feed, but lines can be ephemeral so underground cannot list them all in advance. So special care needs to be taken when resolving routes. Use the special BUS route to get the feed, and then find your route in the stops:
from underground import SubwayFeed
feed = SubwayFeed.get("BUS")
stops = feed.extract_stop_dict()['BX30']
CLI
Use the BUS identifier in the feed cli to obtain the bus feed. Querying a bus route will not work!
underground feed BUS
Similarly, use a --bus flag when obtaining stops to let underground know that it needs to retrieve the bus feed:
➜ underground stops --bus BX30 | tail -2
101735 18:42 18:57 19:12
104619 18:43 18:58 19:13
The bus stops lister in findstops is more expensive/time-consuming than the subway stops list, so use the --bus flag to indicate bus stops are desired:
➜ underground findstops --bus parkside
ID: D27N Direction: NORTH Data Source: subway Lat/Lon: 40.655292,-73.961495 Name: PARKSIDE AV
ID: D27S Direction: SOUTH Data Source: subway Lat/Lon: 40.655292,-73.961495 Name: PARKSIDE AV
ID: 301309 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.655129,-73.961122 Name: PARKSIDE AV/OCEAN AV
ID: 301310 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.655518,-73.960028 Name: PARKSIDE AV/FLATBUSH AV
ID: 301311 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.655815,-73.956542 Name: PARKSIDE AV/BEDFORD AV
ID: 303307 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.655257,-73.959894 Name: FLATBUSH AV/PARKSIDE AV
ID: 303981 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.655541,-73.956442 Name: BEDFORD AV/PARKSIDE AV
ID: 307645 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.654503,-73.961946 Name: OCEAN AV/PARKSIDE AV
ID: 308349 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.655139,-73.961837 Name: OCEAN AV/PARKSIDE AV
ID: 901089 Direction: (BUS) Data Source: buses_bk Lat/Lon: 40.654848,-73.961754 Name: PARKSIDE AV/OCEAN AV
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file underground-2.0.0.tar.gz.
File metadata
- Download URL: underground-2.0.0.tar.gz
- Upload date:
- Size: 18.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c427cebd8dc39e6fe281597560a4678867647e69cdba0aea314b6ac9505106a
|
|
| MD5 |
b7a04da22d4b1641f32818f307638824
|
|
| BLAKE2b-256 |
c71fc452b49af34246b068e783e0696bfed1c00f9fba0ac3ca63c0187b278e97
|
Provenance
The following attestation bundles were made for underground-2.0.0.tar.gz:
Publisher:
release_to_pypi.yml on nolanbconaway/underground
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
underground-2.0.0.tar.gz -
Subject digest:
6c427cebd8dc39e6fe281597560a4678867647e69cdba0aea314b6ac9505106a - Sigstore transparency entry: 840008936
- Sigstore integration time:
-
Permalink:
nolanbconaway/underground@5afd4a5fd2896d72baef35f676bd25b5d3fcf5ef -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/nolanbconaway
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release_to_pypi.yml@5afd4a5fd2896d72baef35f676bd25b5d3fcf5ef -
Trigger Event:
release
-
Statement type:
File details
Details for the file underground-2.0.0-py3-none-any.whl.
File metadata
- Download URL: underground-2.0.0-py3-none-any.whl
- Upload date:
- Size: 18.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8f06f6a03b26e185c53ce89ba6ea8e6f3cb561a3a6100f12afde96eff6d2f67
|
|
| MD5 |
36f9e88da37c7f9bb8b8791be38f7be9
|
|
| BLAKE2b-256 |
738b7b733e6d6cbabd1ed68cda1e9d38e2f3af4b480073b6fbecf886eeea24bf
|
Provenance
The following attestation bundles were made for underground-2.0.0-py3-none-any.whl:
Publisher:
release_to_pypi.yml on nolanbconaway/underground
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
underground-2.0.0-py3-none-any.whl -
Subject digest:
c8f06f6a03b26e185c53ce89ba6ea8e6f3cb561a3a6100f12afde96eff6d2f67 - Sigstore transparency entry: 840008978
- Sigstore integration time:
-
Permalink:
nolanbconaway/underground@5afd4a5fd2896d72baef35f676bd25b5d3fcf5ef -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/nolanbconaway
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release_to_pypi.yml@5afd4a5fd2896d72baef35f676bd25b5d3fcf5ef -
Trigger Event:
release
-
Statement type: