# F1 / MLB / NHL / Bundesliga Scores — $0.001/result (`plainapi/f1-mlb-nhl-bundesliga-sports-scores-scraper`) Actor

You pay only for results actually returned. Nothing found, nothing charged. Fixtures, live status and final results for Formula 1, MLB, the NHL and the Bundesliga. Reads openly published APIs — Jolpica, MLB StatsAPI, the NHL API and OpenLigaDB — with no key and no browser.

- **URL**: https://apify.com/plainapi/f1-mlb-nhl-bundesliga-sports-scores-scraper.md
- **Developed by:** [Shun Furu](https://apify.com/plainapi) (community)
- **Categories:** Sports, Agents, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 events

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Sports Scores & Fixtures — F1, MLB, NHL, Bundesliga

Fixtures, live status and final results, read from **openly published APIs**. No key, no browser,
no HTML parsing.

```
Input:   ["f1", "mlb", "nhl", "bundesliga"]
Output:  one row per event — teams or drivers, score, status, venue, kick-off time
```

### Where the data comes from

This Actor does not scrape anybody's website. Each competition is read from a source that
deliberately publishes its data for anyone to use, without a key:

| Competition | Source | What it is |
|---|---|---|
| **Formula 1** | [Jolpica](https://api.jolpi.ca) | The community successor to Ergast, the open F1 results API that ran for over a decade |
| **MLB** | [MLB StatsAPI](https://statsapi.mlb.com) | Major League Baseball's own public endpoint |
| **NHL** | [NHL API](https://api-web.nhle.com) | The NHL's own public endpoint |
| **Bundesliga** | [OpenLigaDB](https://api.openligadb.de) | A community project publishing German football as open data |

Every row carries a `source` field, so you can always see which API a given fact came from and
check it yourself.

#### One source deliberately left out

ESPN's scoreboard endpoint would have added a dozen more leagues. It answers `200` to a request
from `curl`, and `403` to a request whose user agent honestly says what the client is.

Finding a string that gets through would be working around a refusal, so this Actor does not use
ESPN at all. Fewer competitions, and no argument about whether it should be reading them.

### Input

Every field is optional. Run it with the defaults and it works.

| Field | Type | Default | What it does |
|---|---|---|---|
| `competitions` | array of strings | `["f1", "bundesliga", "mlb"]` | Which to read: `f1`, `bundesliga`, `mlb`, `nhl`. Names work too — `"Formula 1"` |
| `season` | string | current | Season year, e.g. `2026`. Applies to F1 and the Bundesliga |
| `date` | string | current | `YYYY-MM-DD`. Applies to MLB and the NHL |
| `team` | string | — | Keep only events involving a matching team or driver |
| `completedOnly` | boolean | `false` | Drop scheduled events, keeping only final results |

#### Examples

Every finished race of the current F1 season:

```json
{ "competitions": ["f1"], "completedOnly": true }
```

Yesterday's baseball, one particular team:

```json
{ "competitions": ["mlb"], "date": "2026-08-21", "team": "Yankees" }
```

A full Bundesliga season, fixtures and results together:

```json
{ "competitions": ["bundesliga"], "season": "2026" }
```

### Output

One flat row per event. The same fields are used for every sport, so results from a race weekend
and a baseball game sit in the same table without any post-processing.

| Field | Type | Description |
|---|---|---|
| `competition` | string | `f1` | `mlb` | `nhl` | `bundesliga` |
| `competitionLabel` | string | Formatted for display |
| `sport` | string | `motorsport`, `baseball`, `ice hockey`, `football` |
| `source` | string | Which public API this row came from |
| `eventId` | string | Stable identifier |
| `name` | string | `Away @ Home`, or the race name |
| `season` | string | Season the event belongs to |
| `round` | number | null | Round number, where the competition has one |
| `startsAt` | string | ISO 8601 |
| `status` | string | `Scheduled`, `Final`, or the source's own wording |
| `completed` | boolean | Whether the event has finished |
| `homeTeam` / `awayTeam` | string | Empty for Formula 1 |
| `homeScore` / `awayScore` | number | null | Null before an event finishes |
| `winner` | string | null | Team, driver, or `draw` |
| `winnerTeam` | string | Winning constructor — Formula 1 only |
| `venue` / `venueCity` / `venueCountry` | string | As published |
| `url` | string | More information, where the source gives a link |
| `fetchedAt` | string | When the row was produced |

A `SUMMARY` record is written to the key-value store: which competitions were read, how many rows
each produced, and any that could not be reached.

### Pricing

| | Price |
|---|---|
| Starting a run | **$0.00001** — one US cent per 1,000 runs |
| Each event returned | **$0.001** — $1 per 1,000 events |

Events that do not match your filters produce no rows, and errors are written to the log and to
`SUMMARY` rather than to the dataset — so a run that finds nothing costs nothing.

### Typical uses

- **Fantasy and prediction tools** — a schedule and a result feed without a paid data contract.
- **Dashboards and bots** — post final scores as they land.
- **Analysis** — pull a whole season in one run and work on it offline.
- **AI agents** — flat JSON, no key to manage, sensible defaults that work on the first call.

### Notes on data and compliance

- Only public, unauthenticated endpoints are used. Nothing behind a login is touched.
- Fixtures and results are public facts about public events. No personal data is collected.
- No bot protection is bypassed and no rate limits are circumvented; the Actor backs off on `429`.
- Sources that decline programmatic access are not used, rather than worked around.

### Limitations — stated up front

- **Four competitions**, for the reason given above. If a competition you need has an openly
  published API, open an issue and it can be added — that is the only bar it has to clear.
- Formula 1 rows carry the race winner and constructor, not a full classification.
- The Bundesliga endpoint returns a whole season at once, so `date` does not apply to it.
- Live in-progress scores are as fresh as the source publishes them; there is no separate
  low-latency feed.

### Not affiliated

This is an independent tool. It is not affiliated with, endorsed by, or sponsored by Formula 1,
Major League Baseball, the National Hockey League, the Deutsche Fußball Liga, or any of the
organisations whose published data it reads. All names and trademarks belong to their respective
owners.

# Actor input Schema

## `competitions` (type: `array`):

Which competitions to read: f1, bundesliga, mlb, nhl. Names work too — "Formula 1", "Bundesliga".

## `season` (type: `string`):

Season year, e.g. 2026. Applies to Formula 1 and Bundesliga. Leave empty for the current season.

## `date` (type: `string`):

YYYY-MM-DD. Applies to MLB and NHL. Leave empty for the current schedule.

## `team` (type: `string`):

Keep only events involving a team or driver matching any of these words.

## `completedOnly` (type: `boolean`):

Drop scheduled events, keeping only final results.

## Actor input object example

```json
{
  "competitions": [
    "f1",
    "bundesliga",
    "mlb",
    "nhl"
  ],
  "completedOnly": false
}
```

# Actor output Schema

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

Events and, when requested, league table rows.

## `resultsCsv` (type: `string`):

The same rows as CSV, for spreadsheets.

## `summary` (type: `string`):

Which leagues were read and how many rows each produced.

# 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 = {
    "competitions": [
        "f1",
        "bundesliga",
        "mlb",
        "nhl"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("plainapi/f1-mlb-nhl-bundesliga-sports-scores-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 = { "competitions": [
        "f1",
        "bundesliga",
        "mlb",
        "nhl",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("plainapi/f1-mlb-nhl-bundesliga-sports-scores-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 '{
  "competitions": [
    "f1",
    "bundesliga",
    "mlb",
    "nhl"
  ]
}' |
apify call plainapi/f1-mlb-nhl-bundesliga-sports-scores-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plainapi/f1-mlb-nhl-bundesliga-sports-scores-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/ENmFxtn91hAs3Vsl9/builds/861LesOAdK1opDP7I/openapi.json
