# Python MTA Utilities
[](https://github.com/nolanbconaway/underground/actions)
[](https://pypi.org/project/underground/)
[](https://pypi.org/project/underground/)
This is a set of Python utilities that I use to deal with [real-time NYC subway data](https://datamine.mta.info/).
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.
## Install
``` sh
pip install underground
```
Or if you'd like to live dangerously:
``` sh
pip install git+https://github.com/nolanbconaway/underground.git#egg=underground
```
## Python API
Use the Python API like:
``` python
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:
```python
>>> 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).
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] [H|M|D|1|Z|A|N|GS|SI|J|G|Q|L|B|R|F|E|2|7|W|
6|4|C|5|FS]
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.
--help Show this message and exit.
```
Stops are printed to stdout in the format `stop_id t1 t2 ... tn` .
``` sh
$ 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](http://web.mta.info/developers/data/nyct/subway/google_transit.zip)), you can grep the results:
``` sh
$ 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.
--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](http://web.mta.info/developers/data/nyct/subway/google_transit.zip) more carefully.
Raw data
{
"_id": null,
"home_page": "https://github.com/nolanbconaway/underground",
"name": "underground",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "nyc, transit, subway, command-line, cli",
"author": "Nolan Conaway",
"author_email": "nolanbconaway@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/ef/09/65ae15d24b7a30365f3e813a4deabe2ffb0f1ea508cc153d6e42280c65af/underground-1.0.1.tar.gz",
"platform": null,
"description": "# Python MTA Utilities\n\n[](https://github.com/nolanbconaway/underground/actions)\n[](https://pypi.org/project/underground/)\n[](https://pypi.org/project/underground/)\n\nThis is a set of Python utilities that I use to deal with [real-time NYC subway data](https://datamine.mta.info/).\n\nI 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.\n\n## Install\n\n``` sh\npip install underground\n```\n\nOr if you'd like to live dangerously:\n\n``` sh\npip install git+https://github.com/nolanbconaway/underground.git#egg=underground\n```\n\n## Python API\n\nUse the Python API like:\n\n``` python\nimport os\n\nfrom underground import metadata, SubwayFeed\n\nROUTE = 'Q'\nfeed = SubwayFeed.get(ROUTE)\n\n# under the hood, the Q route is mapped to a URL. This call is equivalent:\nURL = 'https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-nqrw'\nfeed = SubwayFeed.get(URL)\n\n# or\nURL = metadata.resolve_url(ROUTE)\nfeed = SubwayFeed.get(URL)\n```\n\n### List train stops on each line\n\n`feed.extract_stop_dict` will return a dictionary of dictionaries, like:\n\n```python\n>>> feed.extract_stop_dict()\n\n{\n\n \"route_1\": {\n \"stop_1\": [datetime.datetime(...), datetime.datetime(...)], \n \"stop_2\": [datetime.datetime(...), datetime.datetime(...)], \n ...\n }, \n \"route_2\": {\n \"stop_1\": [datetime.datetime(...), datetime.datetime(...)], \n \"stop_2\": [datetime.datetime(...), datetime.datetime(...)], \n ...\n }\n\n}\n```\n\n## CLI\n\nThe `underground` command line tool is also installed with the package.\n\n### `feed` \n```\n$ underground feed --help\nUsage: underground feed [OPTIONS] ROUTE_OR_URL\n\n Request an MTA feed via a route or URL.\n\n ROUTE_OR_URL may be either a feed URL or a route (which will be used to\n look up the feed url).\n\n Examples (both access the same feed):\n\n underground feed Q --json > feed_nrqw.json\n\n URL='https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-nqrw' &&\n underground feed $URL --json > feed_nrqw.json\n\nOptions:\n --json Option to output the feed data as JSON. Otherwise\n output will be bytes.\n\n -r, --retries INTEGER Retry attempts in case of API connection failure.\n Default 100.\n\n --help Show this message and exit.\n```\n\n### `stops` \n\n```\n$ underground stops --help\nUsage: underground stops [OPTIONS] [H|M|D|1|Z|A|N|GS|SI|J|G|Q|L|B|R|F|E|2|7|W|\n 6|4|C|5|FS]\n \n Print out train departure times for all stops on a subway line.\n\nOptions:\n\n -f, --format TEXT strftime format for stop times. Use `epoch` for a\n unix timestamp.\n -r, --retries INTEGER Retry attempts in case of API connection failure.\n Default 100.\n -t, --timezone TEXT Output timezone. Ignored if --epoch. Default to NYC\n time.\n -s, --stalled-timeout INTEGER Number of seconds between the last movement\n of a train and the API update before\n considering a train stalled. Default is 90 as\n recommended by the MTA. Numbers less than 1\n disable this check.\n --help Show this message and exit.\n```\n\nStops are printed to stdout in the format `stop_id t1 t2 ... tn` .\n\n``` sh\n$ underground stops Q | tail -2\nQ05S 19:01 19:09 19:16 19:25 19:34 19:44 19:51 19:58\nQ04S 19:03 19:11 19:18 19:27 19:36 19:46 19:53 20:00\n```\n\nIf you know your stop id (stop IDs can be found in [stops.txt](http://web.mta.info/developers/data/nyct/subway/google_transit.zip)), you can grep the results:\n\n``` sh\n$ underground stops Q | grep Q05S\nQ05S 19:09 19:16 19:25 19:34 19:44 19:51 19:58\n```\n\nIf you don't know your stop, see below for a handy tool!\n\n### `findstops` \n\n```\n$ underground findstops --help\nUsage: underground findstops [OPTIONS] QUERY...\n\n Find your stop ID.\n\n Query a location and look for your stop ID, like:\n\n $ underground findstops parkside av\n\nOptions:\n\n --json Option to output the data as JSON. Otherwise will be human readable\n table.\n\n --help Show this message and exit.\n```\n\nEnter the name of your stop and a table of stops with matching names will be returned.\n\n```\n$ underground findstops parkside\nID: D27N Direction: NORTH Lat/Lon: 40.655292, -73.961495 Name: PARKSIDE AV\nID: D27S Direction: SOUTH Lat/Lon: 40.655292, -73.961495 Name: PARKSIDE AV\n```\n\nSome names are ambiguous (try \"fulton st\"), for these you'll have to dig into the [metadata](http://web.mta.info/developers/data/nyct/subway/google_transit.zip) more carefully.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Utilities for NYC's realtime MTA data feeds.",
"version": "1.0.1",
"project_urls": {
"Homepage": "https://github.com/nolanbconaway/underground"
},
"split_keywords": [
"nyc",
" transit",
" subway",
" command-line",
" cli"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "11cf170a13dc8bbc221e4217152baa12c718f6e4a339880b817dfdd8aef66de3",
"md5": "c95449adb345c4518b6ca1f8e960659b",
"sha256": "058525b57e762a36fbb527b8e805726417ef5dc0be8ec8ec771c4d2e2bd8fe35"
},
"downloads": -1,
"filename": "underground-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c95449adb345c4518b6ca1f8e960659b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 16232,
"upload_time": "2025-02-17T22:52:47",
"upload_time_iso_8601": "2025-02-17T22:52:47.256802Z",
"url": "https://files.pythonhosted.org/packages/11/cf/170a13dc8bbc221e4217152baa12c718f6e4a339880b817dfdd8aef66de3/underground-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "ef0965ae15d24b7a30365f3e813a4deabe2ffb0f1ea508cc153d6e42280c65af",
"md5": "e9c10fb5f9642fc05dfb76a1d6e13237",
"sha256": "09b54fb74b13fc978aa7ce4682e3110ff79a97a87aa3e0d6bea683e719864dff"
},
"downloads": -1,
"filename": "underground-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "e9c10fb5f9642fc05dfb76a1d6e13237",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 16117,
"upload_time": "2025-02-17T22:52:48",
"upload_time_iso_8601": "2025-02-17T22:52:48.587993Z",
"url": "https://files.pythonhosted.org/packages/ef/09/65ae15d24b7a30365f3e813a4deabe2ffb0f1ea508cc153d6e42280c65af/underground-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-17 22:52:48",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "nolanbconaway",
"github_project": "underground",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "underground"
}