# London Tfl Status Scraper (`zerobreak/london-tfl-status-scraper`) Actor

London TfL Status Scraper is a Python tool that extracts real-time transit status, line disruptions, and station updates from Transport for London (TfL) into clean CSV or JSON formats for commuting analytics.

- **URL**: https://apify.com/zerobreak/london-tfl-status-scraper.md
- **Developed by:** [ZeroBreak](https://apify.com/zerobreak) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## London TfL Status Scraper: Tube Line Status and Arrivals

<p align="center">
  <img src="https://imgur.com/UMIF4cu.png" alt="London TfL Status Scraper" width="800" />
</p>

Collect and organize London Underground line status, live arrival predictions, and station search results. Export structured records to JSON, CSV, or Excel for transport monitoring, service dashboards, operational reporting, and scheduled data collection.

The actor supports three modes: `status`, `arrivals`, and `stoppoints`. The default mode collects Tube line status, producing one row per line. Arrivals mode collects predictions for a specified line, while stop search finds matching stations and stop points.

**Attribution:** When reusing the data, credit it as Powered by TfL Open Data.

### Use Cases

- **Service status dashboards:** Track Tube lines, service severity, and reported disruptions.
- **Arrival monitoring:** Collect arrival predictions, platforms, destinations, and station information.
- **Station lookup:** Find stop points and organize station identifiers, zones, and coordinates when available.
- **Scheduled exports:** Repeat the same collection at regular intervals and compare results over time.
- **Operational records:** Keep source references and collection timestamps with each record.

### What Data Does the London TfL Status Scraper Extract?

The actor supports three collection modes:

- **Status:** One row per Tube line, including line identifiers, names, status descriptions, and disruption information.
- **Arrivals:** One row per arrival prediction, including station, line, platform, and destination information.
- **Stop points:** One row per matching search result, including available stop identifiers and location details.

Each output row includes `scrapedAt`. The optional app key is never stored in the dataset, and unused source fields are omitted.

#### Example Line Status Record

```json id="4q9y1c"
{
  "$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
  "id": "bakerloo",
  "name": "Bakerloo",
  "modeName": "tube",
  "disruptions": [],
  "created": "2026-09-22T09:53:41.037Z",
  "modified": "2026-09-22T09:53:41.037Z",
  "lineStatuses": [
    {
      "id": 0,
      "statusSeverity": 10,
      "statusSeverityDescription": "Good Service"
    }
  ],
  "routeSections": [],
  "serviceTypes": [
    {
      "name": "Regular",
      "uri": "LINE_ROUTE_REFERENCE"
    }
  ],
  "crowding": {},
  "scrapedAt": "2026-10-12T00:00:00+00:00"
}
```

### Input Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `status` | Collection mode: `status`, `arrivals`, or `stoppoints`. |
| `lineId` | string | `victoria` | Tube line identifier for arrivals mode, such as `victoria`. |
| `query` | string | `oxford circus` | Station or stop-point search text for `stoppoints` mode. |
| `maxItems` | integer | `100` | Maximum output rows, clamped to 1–1,000. |
| `timeoutSecs` | integer | `300` | Maximum time for starting requests, in seconds. |
| `requestTimeoutSecs` | integer | `30` | Timeout for an individual request. |

### Example Input

#### Tube Line Status

```json id="9l7b5d"
{
  "mode": "status",
  "maxItems": 100,
  "timeoutSecs": 300,
  "requestTimeoutSecs": 30
}
```

#### Line Arrivals

```json id="w8m2za"
{
  "mode": "arrivals",
  "lineId": "victoria",
  "maxItems": 100
}
```

#### Stop Search

```json id="5n2xqp"
{
  "mode": "stoppoints",
  "query": "oxford circus",
  "maxItems": 20
}
```

### Output Fields

| Field | Type | Description |
|---|---|---|
| `$type` | string | Source entity type, when available. |
| `id` | string | Line ID, prediction ID, or stop-point ID. |
| `name` | string | Line or stop name, when available. |
| `modeName` | string | Transport mode, such as `tube`. |
| `disruptions` | array | Disruption information associated with a line status. |
| `created` | string | Creation timestamp on a line status record. |
| `modified` | string | Modification timestamp on a line status record. |
| `lineStatuses` | array | Status information, including severity and description. |
| `routeSections` | array | Route section information on line status records. |
| `serviceTypes` | array | Service type information, such as regular service. |
| `crowding` | object | Crowding information, when available. |
| `stationName` | string | Station associated with an arrival prediction. |
| `lineName` | string | Line name on an arrival prediction. |
| `platformName` | string | Platform information for an arrival. |
| `destinationName` | string | Destination associated with an arrival. |
| `searchQuery` | string | Search phrase associated with a stop-point result. |
| `scrapedAt` | string | UTC timestamp when the row was saved. |
| `error` | string | Error information, present on error rows. |

Additional fields may be retained when they are relevant to the selected collection mode.

### FAQ

#### How many rows can one run save?

The default is 100 rows, and the maximum is 1,000. The number of Tube lines is generally well below this limit, while arrival predictions may produce more records.

#### What happens if the line ID or mode is incorrect?

Invalid input produces an error row. If a request reaches TfL and is rejected, the actor records the failure and continues without crashing the run.

#### Can I export CSV or Excel?

Yes. The dataset can be exported as JSON, CSV, or Excel using the available Apify dataset export options.

#### Does the actor paginate through arrivals?

No. Each supported mode processes a single response and does not follow a next-page cursor.

#### Do I need to provide attribution?

Yes. When reusing the collected data, credit it as Powered by TfL Open Data and follow the applicable source terms.

### Trademark Notice

Transport for London (TfL) and its related names, logos, and trademarks belong to their respective owners. They are used in this documentation solely to identify the source of the data and do not imply affiliation, sponsorship, endorsement, or partnership.

### Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, sponsored by, or otherwise officially associated with Transport for London. Users are responsible for complying with applicable source terms, attribution requirements, and data-use restrictions.

### Is Web Scraping Legal?

Collecting information that is already public is generally legal. Scraping is not the same thing as hacking, and there is no single law that bans it. The limits are the usual ones: do not break into accounts, do not take private or confidential data, and be careful with personal data and copyrighted works. A site's terms can also restrict how you access it. Facts such as a public name, price, or rating are not the same thing as republishing someone's article or photo.

This actor collects information from public pages. It does not log in and it does not bypass access controls.

For a longer explanation, see Apify's article [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/).

# Actor input Schema

## `mode` (type: `string`):

Which TfL Unified API call to make. status reads tube line status. arrivals reads live arrivals for lineId. stoppoints searches stops with query. Allowed values: status, arrivals, stoppoints.

## `lineId` (type: `string`):

TfL line id used only for arrivals, for example victoria, bakerloo, or hammersmith-city. Letters, digits, and hyphens only. Ignored in status and stoppoints modes.

## `query` (type: `string`):

Stop name searched only in stoppoints mode, for example oxford circus. The actor URL-encodes it and calls StopPoint Search. Ignored in status and arrivals modes.

## `appKey` (type: `string`):

Optional TfL app_key from the TfL API portal. Leave empty for the anonymous public limit. The key is sent as a query parameter and is never written to the dataset or logs.

## `maxItems` (type: `integer`):

Maximum dataset rows for this run, including an error row. Values are clamped to 1 through 1000.

## `timeoutSecs` (type: `integer`):

Stop starting new TfL requests after this many seconds. This does not replace the Apify platform run timeout.

## `requestTimeoutSecs` (type: `integer`):

Seconds to wait for one TfL HTTP response before counting it as a timeout and retrying.

## `proxyConfiguration` (type: `object`):

Optional proxy for TfL requests. The default sends traffic directly, with Apify Proxy off. These public endpoints do not need a proxy, and a proxy must not be used to get around the published rate limit.

## Actor input object example

```json
{
  "mode": "status",
  "lineId": "victoria",
  "query": "oxford circus",
  "maxItems": 100,
  "timeoutSecs": 300,
  "requestTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `datasetUrl` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "status",
    "lineId": "victoria",
    "query": "oxford circus",
    "maxItems": 100,
    "timeoutSecs": 300,
    "requestTimeoutSecs": 30,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zerobreak/london-tfl-status-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "status",
    "lineId": "victoria",
    "query": "oxford circus",
    "maxItems": 100,
    "timeoutSecs": 300,
    "requestTimeoutSecs": 30,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("zerobreak/london-tfl-status-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "status",
  "lineId": "victoria",
  "query": "oxford circus",
  "maxItems": 100,
  "timeoutSecs": 300,
  "requestTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call zerobreak/london-tfl-status-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zerobreak/london-tfl-status-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/jawr1Sxo7LJv5GUft/builds/JPkmwd6yXkccHOw7Z/openapi.json
