Skip to main content

Utilities for NYC's realtime MTA data feeds.

Project description

Python MTA Utilities

badge PyPI - Python Version PyPI

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

underground-2.0.0.tar.gz (18.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

underground-2.0.0-py3-none-any.whl (18.1 kB view details)

Uploaded Python 3

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

Hashes for underground-2.0.0.tar.gz
Algorithm Hash digest
SHA256 6c427cebd8dc39e6fe281597560a4678867647e69cdba0aea314b6ac9505106a
MD5 b7a04da22d4b1641f32818f307638824
BLAKE2b-256 c71fc452b49af34246b068e783e0696bfed1c00f9fba0ac3ca63c0187b278e97

See more details on using hashes here.

Provenance

The following attestation bundles were made for underground-2.0.0.tar.gz:

Publisher: release_to_pypi.yml on nolanbconaway/underground

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for underground-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c8f06f6a03b26e185c53ce89ba6ea8e6f3cb561a3a6100f12afde96eff6d2f67
MD5 36f9e88da37c7f9bb8b8791be38f7be9
BLAKE2b-256 738b7b733e6d6cbabd1ed68cda1e9d38e2f3af4b480073b6fbecf886eeea24bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for underground-2.0.0-py3-none-any.whl:

Publisher: release_to_pypi.yml on nolanbconaway/underground

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page