Skip to content
Docs

CLI and MCP

Query the data from a terminal with the transitlab command, or give an AI agent the same tables through an MCP server.

transitlab is a small Python program. It reads our public Parquet files with DuckDB on your own computer, so there is no key, no sign-up and nothing to host. The same package runs an MCP server, which lets an AI agent list the tables and run read-only SQL.

You need uv. It fetches and runs the package in one step:

Terminal
uvx transitlab tables

Or install it: uv tool install transitlab or pip install transitlab.

Terminal
transitlab tables
transitlab describe subway_stop_events

tables prints each table’s rows, the months or days it covers and a short description. describe adds the columns, their types, the source and license, and the file URLs. Add --json to either for machine-readable output.

Write the table name as it is. The command finds the names in your query and points each one at the right Parquet files.

Terminal
transitlab sql "select route_id, count(*) as stops
from subway_stop_events
where year = 2026 and month = 10
group by 1 order by 2 desc limit 5"

Pick an output with --format: table (the default), csv, json or parquet. Use -o to write to a file. Parquet needs -o.

Terminal
transitlab sql "select * from bus_lane_streets" --format csv -o streets.csv
transitlab sql -f my-query.sql --format parquet -o result.parquet

Run transitlab examples for starter queries. They are the same ones the SQL console offers.

Big tables are one file a month, or one a day for the feed archives. Two ways to skip files you don’t need:

  • Filter on the year and month columns in your SQL. Every big table has them.
  • Pass --from and --to, as YYYY-MM-DD or YYYY-MM.
Terminal
transitlab sql "select count(*) from subway_stop_events" --from 2026-10-05 --to 2026-10-05

--from and --to choose files. They do not trim rows inside the files they keep, so add a WHERE on a date column when you need exact days. The command tells you on stderr how many files it will read. A trip table for a whole year is several gigabytes; one month is about 70 MB for yellow taxis.

From version 0.2.0, --remote sends the query to the SQL API instead of reading the files on your computer. It needs a free key in TRANSITLAB_API_KEY (or --key). It returns at most 10,000 rows (--limit, default 1,000) and stops after 30 seconds, so it suits answers, not bulk downloads.

Terminal
export TRANSITLAB_API_KEY=tl_live_...
transitlab sql --remote "select count(*) from taxi_yellow_trips where year = 2026 and month = 8"
Terminal
transitlab download taxi_yellow_trips --from 2026-06 --to 2026-08 -o data

This saves the files under data/taxi_yellow_trips/, in the same year=/month= folders as on our server. Files you already have are skipped. Add --dry-run to see what would download. Then query them locally:

SQL
select count(*) from read_parquet('data/taxi_yellow_trips/**/*.parquet', hive_partitioning = true);

Start the server with transitlab mcp. It talks over stdio, so your agent starts it for you. Set it up once.

Claude Code

Terminal
claude mcp add transitlab -- uvx transitlab mcp

Claude Desktop. Open Settings, Developer, Edit Config, add this to claude_desktop_config.json, and restart:

JSON
{
"mcpServers": {
"transitlab": { "command": "uvx", "args": ["transitlab", "mcp"] }
}
}

Cursor. Add the same block to ~/.cursor/mcp.json, or to .cursor/mcp.json in one project.

Run transitlab mcp --config claude-code, claude-desktop or cursor to print these again.

If the agent can’t run programs on your computer, use the remote MCP server at https://api.transitlab.nyc/mcp instead. It has the same tools except example_queries, runs the queries on our servers, and needs a free key. See SQL API.

ToolWhat it does
list_tablesEvery table with rows, dates and a description. The agent calls this first.
describe_tableColumns, types, source, license and how the files split.
run_sqlOne read-only DuckDB query. Optional from_date and to_date pick files.
example_queriesWorking SQL for common questions.

run_sql accepts one SELECT or WITH statement. It rejects anything else, and it rejects functions that read files, such as read_csv. DuckDB also runs with file access switched off and only our URLs allowed, so a query cannot read or change anything on your machine. A query returns at most 1,000 rows and stops after 60 seconds. Tell the agent to group and count in SQL rather than fetch raw rows.

SettingEffect
--catalog URL_OR_FILERead a different catalog. Also set by TRANSITLAB_CATALOG.
--versionPrint the version.

The command sends User-Agent: transitlab-cli/<version> with every request. Our host turns away clients that send Python’s default one.

The program is MIT. The data are CC BY 4.0: credit transitlab.nyc and the original source.