# SJ Train Tickets and Connections Scraper (`muhammadafzal/sj-train-tickets-connections-scraper`) Actor

Scrape public SJ train connections, departure and arrival times, changes, live from-prices, travel classes, train services, numbers, and onboard amenities.

- **URL**: https://apify.com/muhammadafzal/sj-train-tickets-connections-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Travel, Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 sj train connections

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## SJ Train Tickets and Connections Scraper

Search public SJ journey pages and export train connections with departure and arrival times, durations, changes, live one-adult from-prices, travel classes, services, train numbers, and onboard amenities.

The Actor is read-only. It does not log in, reserve seats, hold fares, or buy tickets. Prices and availability can change after a run, so users should confirm them on SJ before travel or purchase.

### What it extracts

| Field | Meaning |
|---|---|
| `connectionId` | Stable hash for deduplicating the displayed connection |
| `origin`, `destination`, `travelDate` | Requested route and Sweden-local travel date |
| `departureTime`, `arrivalTime` | Local times displayed by SJ |
| `departureDateTime`, `arrivalDateTime` | ISO 8601 timestamps with Sweden's current UTC offset |
| `durationMinutes`, `changes`, `direct` | Journey duration and transfer count |
| `fromPriceSek`, `currency` | Current one-adult from-price displayed by SJ, or `null` |
| `serviceSummary`, `services` | Displayed train/operator summary and ordered segments |
| `trainNumbers` | Train numbers visible in the journey card |
| `travelClasses` | Classes shown as available, such as 2 class or 1 class |
| `amenities` | Symbols such as bistro, wifi, level boarding, or wheelchair lift |
| `bookingUrl`, `sourceUrl`, `scrapedAt` | Manual continuation link and provenance |

### When to use it

Use this Actor for fare monitoring, route comparison, travel research, schedule analysis, and feeding current SJ options into an automation or AI agent. It works with public stations represented in SJ's journey planner, including routes that contain services run by partner operators.

Do not use it to automate checkout, reserve inventory, guarantee a fare, collect account data, or represent SJ's full retail ticket conditions. The result is a journey-card snapshot for one adult, not a purchased ticket or binding offer.

### Input

| Field | Default | Notes |
|---|---:|---|
| `origin` | `Stockholm Central` | Full public SJ station name |
| `destination` | `Göteborg Central` | Must differ from the origin |
| `travelDate` | tomorrow | `YYYY-MM-DD`, interpreted in `Europe/Stockholm` |
| `travelTime` | `08:00` | Earliest departure or latest arrival |
| `searchBy` | `departure` | `departure` or `arrival` |
| `directOnly` | `false` | Keep zero-change connections only |
| `requirePrice` | `false` | Set true to exclude schedule cards whose price has not loaded |
| `maxResults` | `5` | 1–20 dataset records and result charges |
| `proxyConfiguration` | no proxy | Configure a Swedish residential proxy only if the direct route is blocked |

Evergreen input:

```json
{
  "origin": "Stockholm Central",
  "destination": "Göteborg Central",
  "travelTime": "08:00",
  "searchBy": "departure",
  "directOnly": false,
  "requirePrice": false,
  "maxResults": 5
}
```

Specific arrive-by search:

```json
{
  "origin": "Malmö Central",
  "destination": "Stockholm Central",
  "travelDate": "2026-10-15",
  "travelTime": "18:00",
  "searchBy": "arrival",
  "maxResults": 10
}
```

Run through the Apify API:

```bash
curl -X POST "https://api.apify.com/v2/acts/USERNAME~sj-train-tickets-connections-scraper/runs?token=APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"origin":"Stockholm Central","destination":"Göteborg Central","maxResults":5}'
```

For production automation, send the token in an `Authorization` header rather than a URL.

### Output example

```json
{
  "connectionId": "da32fbb2e75481ec9c2a",
  "origin": "Stockholm Central",
  "destination": "Göteborg Central",
  "travelDate": "2026-10-15",
  "departureTime": "08:17",
  "arrivalTime": "11:47",
  "departureDateTime": "2026-10-15T08:17:00+02:00",
  "arrivalDateTime": "2026-10-15T11:47:00+02:00",
  "durationMinutes": 210,
  "changes": 0,
  "direct": true,
  "fromPriceSek": 445,
  "currency": "SEK",
  "serviceSummary": "SJ High-speed train, X 2000, train 425",
  "services": ["SJ High-speed train, X 2000, train 425"],
  "trainNumbers": ["425"],
  "travelClasses": ["2 class", "2 class Calm", "1 class"],
  "amenities": ["bistro", "wheelchair lift", "wifi"],
  "bookingUrl": "https://www.sj.se/en/search-journey/choose-journey/Stockholm%20Central/G%C3%B6teborg%20Central/2026-10-15",
  "sourceUrl": "https://www.sj.se/en/search-journey/choose-journey/Stockholm%20Central/G%C3%B6teborg%20Central/2026-10-15",
  "scrapedAt": "2026-09-10T03:30:00.000Z"
}
```

The default dataset contains only connection records. `OUTPUT` in the default key-value store reports `DATA`, `EMPTY`, `BLOCKED`, `REJECTED`, or `FAILED`, along with delivered records, charged events, route, source URL, and warnings.

### Pay per event

| Event | Price | When charged |
|---|---:|---|
| Actor start | $0.005 | Once when a run starts |
| SJ train connection | $0.002 | After one validated connection is saved |

A five-result run costs at most $0.015 in configured events: $0.005 + 5 × $0.002. A one-result run costs $0.007. Platform limits can stop charging early; delivered and charged counts are recorded separately in `OUTPUT`.

### Reliability and limitations

The Actor loads SJ's public journey-selection page in a real browser and reads its accessible journey cards. It uses one coherent browser session, bounded retries, a 60-second navigation timeout, and a maximum of one journey-page request per run. The default does not use a proxy because SJ currently serves the public journey page directly. If SJ returns 403 or 429 from Apify Cloud, configure an Apify residential proxy with Sweden as the country.

`requirePrice: true` waits briefly for prices and excludes cards that still show a loading or unavailable price. Set it to `false` when schedules are more important than prices. An empty filtered search returns zero dataset records plus a truthful `OUTPUT` summary; the Actor never fabricates fallback connections. Invalid route combinations and dates are rejected before billable result work.

Station names must match SJ's public URL routing. If a name is not recognized, use the exact suggestion shown by SJ's journey search, such as `Stockholm Central`, `Göteborg Central`, `Malmö Central`, `Uppsala Central`, or `Oslo S`.

### Responsible use

Use the data in accordance with SJ's terms, applicable transport-data rules, and privacy law. Keep request volume modest, cache results when appropriate, and do not use the Actor to interfere with purchasing, access controls, or service availability. This independent Actor is not affiliated with or endorsed by SJ AB.

For a suspected parser or source-page change, include the run ID, redacted input, and expected route in a support report. Do not send account credentials or payment information.

# Actor input Schema

## `origin` (type: `string`):

Use this to choose the journey origin as an SJ station name, for example Stockholm Central. Use the full public station name, not an address or station code.

## `destination` (type: `string`):

Use this to choose the journey destination as an SJ station name, for example Göteborg Central. It must differ from the departure station.

## `travelDate` (type: `string`):

Use this for a specific Sweden-local travel date in YYYY-MM-DD format, for example 2026-10-15. Leave empty to search tomorrow; dates may be at most 330 days ahead.

## `travelTime` (type: `string`):

Use this to filter by a 24-hour Sweden-local departure or arrival time, for example 08:00. Default is 08:00; this is not a journey duration.

## `searchBy` (type: `string`):

Use departure to return connections leaving at or after travelTime, or arrival to return connections arriving at or before it. Default is departure.

## `directOnly` (type: `boolean`):

Use this to keep only journeys with zero changes. Default is false, which includes direct and connecting journeys.

## `requirePrice` (type: `boolean`):

Use this to exclude connections whose live price has not loaded or is unavailable. Default is false; true keeps only results with a displayed price.

## `maxResults` (type: `integer`):

Use this to cap dataset records and result-event charges. Accepted range is 1 to 20; default is 5. It does not change how many journeys SJ displays.

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

Use this only when SJ blocks the direct cloud network. Default is no proxy; if needed, choose an Apify residential proxy in Sweden and keep one session identity.

## Actor input object example

```json
{
  "origin": "Stockholm Central",
  "destination": "Göteborg Central",
  "travelTime": "08:00",
  "searchBy": "departure",
  "directOnly": false,
  "requirePrice": false,
  "maxResults": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Default dataset items containing SJ journey results.

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

Dataset detail page for export and inspection.

## `output` (type: `string`):

Terminal outcome, route, counts, billing parity, and warnings.

# 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 = {
    "origin": "Stockholm Central",
    "destination": "Göteborg Central"
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/sj-train-tickets-connections-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 = {
    "origin": "Stockholm Central",
    "destination": "Göteborg Central",
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/sj-train-tickets-connections-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 '{
  "origin": "Stockholm Central",
  "destination": "Göteborg Central"
}' |
apify call muhammadafzal/sj-train-tickets-connections-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/sj-train-tickets-connections-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/dZBp7THaMxytZfQs0/builds/ieEre68N1R4otWp9n/openapi.json
