# MLB NHL Schedule Scraper (`axiomworks/mlb-nhl-games-scraper`) Actor

Retrieve MLB and NHL schedules for an inclusive date range, with game IDs, season, game type, UTC start time, status, team names, scores, and venue. The Actor maps both leagues into a consistent dataset shape and follows NHL weekly schedule dates as needed.

- **URL**: https://apify.com/axiomworks/mlb-nhl-games-scraper.md
- **Developed by:** [Axiom Works](https://apify.com/axiomworks) (community)
- **Categories:** Sports
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 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

**Returns:** one row per MLB or NHL game with `league`, `gameId`, `startTime`, `status`, `homeTeam`, `awayTeam`, and `tvBroadcasts`.
**Input:** `{"startDate":"2025-03-01","endDate":"2025-03-02","leagues":["MLB","NHL"],"maxItems":20}`
**Best for:** schedule tracking, score analysis, and building game calendars.

```json
{"id":"779129","league":"MLB","gameId":"779129","startTime":"2025-03-01T18:05:00Z","status":"Final","homeTeam":"Baltimore Orioles","awayTeam":"Pittsburgh Pirates","tvBroadcasts":["MASN","SportsNet Pittsburgh","MLBN (out-of-market only)"]}
```

### What does MLB and NHL Games Scraper do?

This MLB and NHL scraper collects game schedules and scores from the public MLB API and NHL schedule API. Give it an inclusive date range, choose either league or both, and it returns one dataset row per game. Each row identifies the league, game, season, game type, scheduled UTC start, status, teams, scores where published, venue, and the exact API URL that supplied the data. It is useful when you need a compact game calendar or a set of historical results without stitching together two different API response formats.

The MLB and NHL APIs have different fields and pagination. This Actor maps the fields into one consistent dataset while preserving each league's game type and status codes. It collects MLB's full requested range with one schedule request. For NHL, it follows the schedule's weekly `nextStartDate` value until it has covered the requested end date. Games returned outside the requested dates are filtered out. No login is needed for these public endpoints. This Actor is an independent tool and is not affiliated with MLB, NHL, or their teams.

### What data can you get?

The dataset contains the following fields. The same shape is used for both leagues, with null values for source fields that were unavailable. NHL team names are assembled from its place name and common name; MLB team names come from its team name fields. The existing `startTime` stays in UTC. The added `localStartTime` converts that instant to the requested `timezone`, using daylight saving rules for that date. It is a wall-clock value without an offset; keep `startTime` when ordering instants or handling an ambiguous autumn clock change.

| Field | Description | Example |
|---|---|---|
| `id` | Stable league game identifier as text, unique within a run | `779129` |
| `league` | Source league | `MLB` |
| `gameId` | League's game identifier as text | `779129` |
| `season` | League season value; NHL uses its combined season number | `2025` |
| `gameType` | League's game type code | `S` |
| `startTime` | Scheduled game start in UTC | `2025-03-01T18:05:00Z` |
| `localStartTime` | Local wall-clock start in the requested timezone | `2025-07-01T15:07:00` |
| `timezone` | Requested IANA timezone | `America/New_York` |
| `status` | League's current game state or detailed status | `Final` |
| `homeTeam` | Home team name | `Baltimore Orioles` |
| `awayTeam` | Away team name | `Pittsburgh Pirates` |
| `homeScore` | Published home score, or null | `2` |
| `awayScore` | Published away score, or null | `5` |
| `venue` | Published venue name, or null | `Ed Smith Stadium` |
| `gameDate` | Schedule calendar date of the game | `2025-03-01` |
| `homeAbbrev` | Home team abbreviation, or null | `BAL` |
| `awayAbbrev` | Away team abbreviation, or null | `PIT` |
| `homeProbablePitcher` | Announced home probable pitcher (MLB only, otherwise null) | `Cade Povich` |
| `awayProbablePitcher` | Announced away probable pitcher (MLB only, otherwise null) | `Paul Skenes` |
| `homeTeamId` | Home team ID from the selected league | `141` |
| `awayTeamId` | Away team ID from the selected league | `147` |
| `tvBroadcasts` | Unique published TV network names, or null | `["Sportsnet", "YES"]` |
| `sourceUrl` | Schedule API endpoint that returned the game | `https://statsapi.mlb.com/api/v1/schedule?...` |

Scores reflect the API at the time of the run. Re-running the same future date range later can fill in scores and update a game's status. A game rescheduled by a league may move to a new date or receive a changed start time. If you store results over time, use `league` and `gameId` together as your key and keep the latest run's values. The Actor deduplicates game IDs within each run, including repeated NHL games around the edges of weekly schedule responses. When the MLB range includes both a postponed listing and a later played listing for the same game, the played listing supplies the status, start time, scores, and other fields. A postponed game without a replacement listing keeps its source status and null scores. Resumed games keep the first listing when neither entry is postponed.

TV broadcasts are copied from MLB TV broadcast entries and NHL network entries. Radio entries are excluded, and repeated network names appear once. A network name does not establish viewing availability, streaming rights, subscription access, or availability in your country. Missing broadcast data is null. Historical and future schedules can carry different broadcast details, so refresh the relevant date range when planning coverage.

The new `homeTeamId` and `awayTeamId` fields support reliable joins when names or abbreviations change. IDs are strings copied from each league's API. Pair them with `league`: MLB and NHL have separate identifier namespaces. Existing team names and abbreviations remain available.

### How to use

Open the Actor on Apify, enter `startDate` and `endDate` as calendar dates in `YYYY-MM-DD` format, and select the leagues you want. Keep `maxItems` at the default for a compact result or increase it for a larger date range. Start the run, then open the dataset to review game rows and export the records. The prefilled dates point to past games so a first run produces stable sample results.

The range includes both endpoints. Leagues run in supplied order, MLB first by default. Once `maxItems` is reached, processing stops before another request. Run each league separately when each needs an independent item limit.

The Actor normally requests the public APIs directly. The optional `proxyConfiguration` input accepts an Apify proxy configuration if a network blocks one of the endpoints. Transient rate limits, server errors, and block responses trigger retries with backoff and a fresh proxy session when a proxy is configured. The Actor does not solve CAPTCHA challenges. Requests are paced to approximately two per second. If an endpoint stays unavailable, the run reports that condition and returns any games that were already collected from the other league.

### Input

| Input field | Required | Description | Default or prefill |
|---|---|---|---|
| `startDate` | Yes | First included game date, `YYYY-MM-DD` | Prefill `2025-03-01` |
| `endDate` | Yes | Last included game date, `YYYY-MM-DD`, on or after start | Prefill `2025-03-02` |
| `leagues` | No | Array containing `MLB`, `NHL`, or both | Both |
| `statuses` | No | Exact source status values, ignoring case, e.g. `["Final", "OFF"]` | All statuses |
| `teams` | No | Team names, abbreviations, or exact numeric IDs as strings; matches home or away | All teams |
| `timezone` | No | IANA timezone for `localStartTime`, e.g. `America/New_York` | `UTC` |
| `maxItems` | No | Exact maximum number of game rows, from 1 to 10000 | 50; prefill 20 |
| `proxyConfiguration` | No | Apify proxy settings for blocked networks | No proxy |

Here is a request for one week of NHL games. The date values are strings; the league choice is an array. You can omit `leagues` to request both leagues. You can omit `maxItems` to use its default. Team names are optional: set `teams` to keep only games where the home or away team matches any supplied name or abbreviation (case-insensitive substring). The filters are combined with OR: \["Yankees", "TOR"] keeps games involving either team, not only games between them. An unmatched filter succeeds with zero rows.

```json
{
  "startDate": "2025-03-01",
  "endDate": "2025-03-07",
  "leagues": ["NHL"],
  "maxItems": 30
}
```

Invalid date formats, impossible calendar dates, an end date before the start date, unknown league values, and an invalid item limit fail with a clear input message. A valid period with no games ends successfully with zero rows. That difference is useful in automation: you can treat bad configuration as an error while accepting an off-season day as an ordinary empty response. The output order follows the league APIs' schedule order. If you need a particular display order, sort the dataset by `startTime` after export.

The optional `statuses` filter compares the original `status` value exactly, ignoring case. For completed MLB games use `["Final"]`; for completed NHL schedules the source commonly uses `["OFF", "FINAL"]`. The Actor preserves these original values rather than changing the meaning of `status`. Multiple entries match with OR. Status filtering combines with the date range and team filtering with AND, and only qualifying games count toward `maxItems`. An unknown status succeeds with no rows, so check an unfiltered sample when choosing values for live or postponed games.

For example, `{"startDate":"2025-07-01","endDate":"2025-07-01","leagues":["MLB"],"teams":["TOR"],"statuses":["Final"],"maxItems":1}` selects a completed Toronto game and includes its team IDs and published TV networks. Leave `statuses` empty to keep the previous behavior. Broadcast information is fetched with the existing MLB schedule request and read from the existing NHL schedule response; this does not add a detail request per game.

The optional `timezone` input defaults to `UTC`. For example, `{"startDate":"2025-07-01","endDate":"2025-07-01","leagues":["MLB"],"teams":["147"],"timezone":"America/New_York","maxItems":1}` selects games involving MLB team 147. Numeric strings match team IDs exactly; names and abbreviations keep their existing substring behavior. IDs are league-specific, so select a league when using them. Invalid timezone names fail input validation before requests begin. The timezone affects only output formatting, not the inclusive schedule date filter.

### Output

Each dataset item is a game, not a team or a day. The following real item came from a local run against the MLB schedule API for July 1, 2025. The `sourceUrl` records the complete endpoint used by that run. `id` and `gameId` are both retained: `id` is the dataset key, while `gameId` makes the league identifier explicit to downstream code.

```json
{
  "id": "777283",
  "league": "MLB",
  "gameId": "777283",
  "season": "2025",
  "gameType": "R",
  "startTime": "2025-07-01T19:07:00Z",
  "localStartTime": "2025-07-01T15:07:00",
  "timezone": "America/New_York",
  "status": "Final",
  "homeTeam": "Toronto Blue Jays",
  "awayTeam": "New York Yankees",
  "homeScore": 12,
  "awayScore": 5,
  "venue": "Rogers Centre",
  "gameDate": "2025-07-01",
  "homeTeamId": "141",
  "awayTeamId": "147",
  "tvBroadcasts": [
    "Sportsnet",
    "YES",
    "MLBN (out-of-market only)",
    "TVA Sports"
  ],
  "homeAbbrev": "TOR",
  "awayAbbrev": "NYY",
  "homeProbablePitcher": "Kevin Gausman",
  "awayProbablePitcher": "Max Fried",
  "sourceUrl": "https://statsapi.mlb.com/api/v1/schedule?sportId=1&startDate=2025-07-01&endDate=2025-07-01&hydrate=team,probablePitcher,broadcasts"
}
```

### How much does it cost?

This Actor is charged per result saved to the dataset. The `result` event is applied once for each game row. Runs with no rows have no result events, and a lower `maxItems` stops output and result charging at that count. The current rate and billing details are on the Actor's Pricing tab on Apify. Check that tab before a large run because the rate can change. The result count includes only saved game rows.

### Use with AI agents (MCP)

Apify's MCP endpoint can expose this Actor to an AI agent. Configure the server URL as `https://mcp.apify.com/?tools=axiomworks/mlb-nhl-games-scraper` in an MCP client that supports Apify. The exact tool path is `axiomworks/mlb-nhl-games-scraper`. The agent can submit the same JSON input described above and read the dataset rows as structured results. Keep the date range and desired league explicit in prompts so the agent selects the intended schedule.

Example prompts include “Get NHL games from March 1 to March 7, 2025 and include scores and venues,” “List MLB games on July 1, 2025 with start times and teams,” and “Collect both MLB and NHL games for March 1, 2025 with at most twenty rows.” The agent should use `homeScore` and `awayScore` only when those fields are populated. For comparative analysis, ask the agent to group by `league` and interpret `gameType` and `status` within that league's own codes.

### Integrations and API

You can run `axiomworks/mlb-nhl-games-scraper` through Apify's API and then use the default dataset endpoint for JSON, CSV, or another supported export format. Set `APIFY_TOKEN` in your environment at runtime. Keep tokens outside source control. The examples show a one-day NHL request with a small item limit; adjust the JSON for your workflow.

Python with `apify-client`:

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('axiomworks/mlb-nhl-games-scraper').call(run_input={
    'startDate': '2025-03-01', 'endDate': '2025-03-01',
    'leagues': ['NHL'], 'maxItems': 10,
})
for game in client.dataset(run['defaultDatasetId']).iterate_items():
    print(game['gameId'], game['homeTeam'], game['awayTeam'])
```

JavaScript with `apify-client`:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('axiomworks/mlb-nhl-games-scraper').call({
  startDate: '2025-03-01', endDate: '2025-03-01',
  leagues: ['NHL'], maxItems: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
process.stdout.write(JSON.stringify(items));
```

cURL using the synchronous dataset item endpoint:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/axiomworks~mlb-nhl-games-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"startDate":"2025-03-01","endDate":"2025-03-01","leagues":["NHL"],"maxItems":10}'
```

### FAQ

**Can I request one date?** Yes. Set `startDate` and `endDate` to the same valid date. The Actor includes games assigned to that date by the source schedule. Timezone boundaries in the source can mean a UTC start falls on an adjacent calendar date; use the schedule date for filtering and `startTime` for precise time calculations.

**Are both leagues always returned?** Both are selected by default in MLB then NHL order, but an item limit can be reached while processing the first league. Use \["NHL", "MLB"] to process NHL first. Select NHL only when you need NHL records within a small limit. A separate run for each league gives each an independent item cap.

**Why is a score null?** A scheduled game may not have started, or the API may omit a score. Null means unavailable in the response. It is different from a published score of zero. Rerun after the game when you need final scores.

**Can I use a large range?** The MLB endpoint accepts a range in one request, and NHL schedules are read week by week. The Actor respects the result limit and a two-request-per-second pace. Large ranges can take longer and return many records, so set a meaningful `maxItems`.

**What happens if an API is unavailable?** A transient block or server response is retried. If one league remains unavailable, the Actor can still return games collected from the other league and logs the failed request. With no games returned, the run suggests choosing dates during the selected league’s season, checking leagues, removing or broadening `teams` filters, and retrying or configuring `proxyConfiguration` when an API is unavailable.

### Is it legal to scrape MLB and NHL?

The Actor reads public schedule API responses without logging in. Public accessibility does not settle every legal question. League data may be subject to copyright, database rights, website terms, or use restrictions depending on your location and purpose. Review the relevant terms and obtain legal advice for a commercial use that needs it. Respect the source services by running at a modest rate and avoiding unnecessary repeated requests. This Actor is independent and does not claim authorization or affiliation with either league.

### Feedback

Report missing fields or changed responses through the Actor’s Apify feedback channel. Include the run input, affected `gameId`, and `sourceUrl` so the source response can be checked.

# Actor input Schema

## `startDate` (type: `string`):

First included schedule date, e.g. "2025-03-01". Required, together with endDate; format YYYY-MM-DD only; use the same date for both to collect one day. Dates are league calendar dates, not UTC start-time filters.

## `endDate` (type: `string`):

Last included schedule date, e.g. "2025-03-02". Required; must be on or after startDate. Both endpoints are inclusive. Short historical ranges give quick, populated samples.

## `leagues` (type: `array`):

League codes (uppercase, case-sensitive) as a nonempty array, e.g. \["NHL"] or \["MLB","NHL"]. Default collects MLB then NHL. Leagues run in the supplied order and share maxItems; a small limit may be filled by the first league.

## `teams` (type: `array`):

Team names, abbreviations, or exact numeric team IDs as strings, e.g. \["Yankees","TOR","147"]. Default \[] keeps all teams. Any entry may match either side; names and abbreviations use case-insensitive substring matching, IDs match exactly. Unmatched filters return zero rows successfully.

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

Each row is one game. Shared result limit across selected leagues after team filtering, 1-10000, default 50; e.g. 7 returns exactly 7 rows when enough games match. It is not a per-league limit. Processing stops when this limit or your run charge limit is reached.

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

Apify proxy configuration, e.g. {"useApifyProxy":false} for direct requests (default). Set {"useApifyProxy":true} to use Apify Proxy for blocked networks. Persistent endpoint failures are logged; other selected leagues continue and an empty run succeeds with guidance.

## `statuses` (type: `array`):

Source status values to keep, e.g. \["Final"] for MLB or \["OFF", "FINAL"] for NHL. Default \[] keeps every status. Matches exactly ignoring case; combined with teams and date filters before the shared maxItems limit. Unknown statuses return zero rows successfully.

## `timezone` (type: `string`):

IANA timezone for the added localStartTime field, e.g. America/New_York or Europe/Paris. Default UTC. startTime stays UTC; timezone identifies the zone used for the local wall-clock value, including daylight saving changes.

## Actor input object example

```json
{
  "startDate": "2025-03-01",
  "endDate": "2025-03-02",
  "leagues": [
    "MLB",
    "NHL"
  ],
  "teams": [],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "statuses": [],
  "timezone": "UTC"
}
```

# Actor output Schema

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

Dataset of games in requested date range.

# 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 = {
    "startDate": "2025-03-01",
    "endDate": "2025-03-02",
    "leagues": [
        "MLB",
        "NHL"
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axiomworks/mlb-nhl-games-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 = {
    "startDate": "2025-03-01",
    "endDate": "2025-03-02",
    "leagues": [
        "MLB",
        "NHL",
    ],
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("axiomworks/mlb-nhl-games-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 '{
  "startDate": "2025-03-01",
  "endDate": "2025-03-02",
  "leagues": [
    "MLB",
    "NHL"
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call axiomworks/mlb-nhl-games-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,axiomworks/mlb-nhl-games-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/lpTdh1rlFX5PyRX2c/builds/etNCL0SlovbWOCkfa/openapi.json
