# ESPN NFL Season-Aware Stats Scraper (`w3crawler/espn-nfl-stats-scraper`) Actor

Extract public ESPN NFL team, player, game, and news records with explicit requested-season, source-season, and season-start-year provenance plus fail-closed diagnostics.

- **URL**: https://apify.com/w3crawler/espn-nfl-stats-scraper.md
- **Developed by:** [w3crawler](https://apify.com/w3crawler) (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 nfl stats

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?

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

### What does this Actor do?

ESPN NFL Season-Aware Stats Scraper collects public ESPN NFL team standings, roster/player metadata, scoreboard events, and news. It uses bounded HTTPS requests to ESPN’s public CDN and public site API routes, normalizes the business fields, and records source-season provenance for every normal row.

The Actor does not log in, use credentials, use a proxy, request arbitrary URLs, bypass CAPTCHA, paywalls, geofences, rate limits, device checks, or WAFs, or invent missing values. A blocked or unreadable source produces an exact diagnostic row instead of a fabricated business record.

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

### Scope and use cases

Use this Actor for season-aware NFL team snapshots, public roster research, scoreboard and schedule collection, and ESPN NFL news discovery. A run can discover teams from the public standings feed or accept bounded public ESPN team slugs, abbreviations, or IDs.

Normal dataset rows are one of nfl-team, nfl-player, nfl-game, or nfl-news. Diagnostics are separate four-field rows with a public ESPN URL, error message, error code, and timestamp.

### Source targets and public-page boundaries

The Actor uses these public ESPN routes:

- [NFL standings and team discovery](https://cdn.espn.com/core/nfl/standings?xhr=1\&season=2026)
- [Team metadata and rosters](https://site.api.espn.com/apis/site/v2/sports/football/nfl/teams/buf)
- [Scoreboard](https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard?limit=100\&dates=2026)
- [NFL news](https://site.api.espn.com/apis/site/v2/sports/football/nfl/news?limit=20)

These are public ESPN web/CDN feeds, not private endpoints. Requests are sequential, response-size limited to 8 MiB, timed out at 15 seconds, and retried only for bounded transient server or transport failures. The Actor stops at the configured record limit and never substitutes a guessed team, player, game, or article.

### Input

#### Complete public input

```json
{
  "teams": [
    "buf"
  ],
  "maxTeams": 1,
  "maxItems": 30,
  "season": 2026,
  "requestDelayMs": 250,
  "includePlayers": true,
  "maxPlayersPerTeam": 12
}
```

#### Input fields

- teams is optional and accepts up to 32 public ESPN team slugs, abbreviations, or numeric IDs. An empty list discovers teams from the public standings feed.
- maxTeams accepts 1 through 32 selected teams and defaults to 8.
- maxItems accepts 1 through 500 total output rows, including diagnostics, and defaults to 120.
- season accepts 2000 through 2035 and defaults to the current NFL season.
- requestDelayMs accepts 0 through 5,000 milliseconds and defaults to 250.
- includePlayers defaults to true and controls public roster retrieval.
- maxPlayersPerTeam accepts 0 through 100 and defaults to 20.
- fixtureFile is an optional local QA-only JSON path inside the Actor directory. Omit it for real ESPN evidence.

Unknown fields, credentials, proxy settings, arbitrary URLs, and email/contact extraction are rejected.

### Runnable input examples

#### Public smoke run

```json
{
  "teams": [
    "buf"
  ],
  "maxTeams": 1,
  "maxItems": 5,
  "season": 2026,
  "includePlayers": false,
  "requestDelayMs": 0
}
```

#### Team and roster run

```json
{
  "teams": [
    "buf",
    "mia"
  ],
  "maxTeams": 2,
  "maxItems": 40,
  "season": 2025,
  "includePlayers": true,
  "maxPlayersPerTeam": 10
}
```

#### Standings discovery with no team filter

```json
{
  "teams": [],
  "maxTeams": 4,
  "maxItems": 23,
  "season": 2026,
  "requestDelayMs": 0
}
```

#### Player-free bounded run

```json
{
  "teams": [
    "buf"
  ],
  "maxTeams": 1,
  "maxItems": 20,
  "season": 2026,
  "includePlayers": false,
  "maxPlayersPerTeam": 0,
  "requestDelayMs": 500
}
```

#### Local fixture QA

```json
{
  "teams": [
    "buf"
  ],
  "maxTeams": 1,
  "maxItems": 5,
  "season": 2026,
  "includePlayers": true,
  "maxPlayersPerTeam": 2,
  "fixtureFile": "fixtures/nfl.json"
}
```

Fixture mode is deterministic local QA only and is not Cloud evidence.

### Output contract

#### Normal team row

Every normal row contains sourceName, sourceKind, sourceUrl, league, season, requestedSeason, seasonLabel, seasonStartYear, and scrapedAt, plus type-specific public fields.

```json
{
  "recordType": "nfl-team",
  "sourceName": "ESPN public NFL feeds",
  "sourceKind": "public-espn-cdn",
  "sourceUrl": "https://cdn.espn.com/core/nfl/standings?xhr=1&season=2026",
  "league": "nfl",
  "season": 2026,
  "requestedSeason": 2026,
  "seasonLabel": "2026 NFL season",
  "seasonStartYear": 2026,
  "teamId": "2",
  "teamName": "Buffalo Bills",
  "teamAbbreviation": "BUF",
  "teamSlug": "buf",
  "wins": 3,
  "losses": 0,
  "winPercent": 1,
  "scrapedAt": "2026-09-09T00:00:00.000Z"
}
```

#### Normal game row

```json
{
  "recordType": "nfl-game",
  "sourceName": "ESPN public NFL feeds",
  "sourceKind": "public-espn-api",
  "sourceUrl": "https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard?limit=100&dates=2026",
  "league": "nfl",
  "season": 2025,
  "requestedSeason": 2026,
  "seasonLabel": "2025 NFL season",
  "seasonStartYear": 2025,
  "eventId": "401000001",
  "eventName": "Buffalo Bills at Fixture Team",
  "eventDate": "2026-01-04T18:00:00.000Z",
  "homeTeam": "Fixture Team",
  "awayTeam": "Buffalo Bills",
  "homeScore": 24,
  "awayScore": 27,
  "gameStatus": "Final",
  "completed": true,
  "scrapedAt": "2026-09-09T00:00:00.000Z"
}
```

#### Diagnostic row

Diagnostics always contain exactly these four fields and never claim a replacement business record.

```json
{
  "url": "https://site.api.espn.com/apis/site/v2/sports/football/nfl/news?limit=20",
  "error": "The public ESPN endpoint rate-limited the request; no replacement record was claimed.",
  "errorCode": "RATE_LIMITED",
  "scrapedAt": "2026-09-09T00:00:00.000Z"
}
```

#### Field semantics

- requestedSeason is the input season.
- season is the source-reported season. For games, this is ESPN event.season.year when present.
- seasonStartYear equals season.
- seasonLabel is normalized as season plus the text NFL season.
- Team fields cover identity and available standings values.
- Player fields cover public roster identity, team, position, jersey, physical values, status, and public profile media.
- Game fields cover event identity, date, season type, competitors, scores, status, venue, and broadcasts.
- News fields cover article identity, headline, description, publication time, image, and public article URL.
- Missing values are omitted. They are never replaced with zero, false, unknown, or a placeholder.

### Run summary and source metadata

#### Normal summary

OUTPUT\_SUMMARY, OUTPUT, and SOURCE\_METADATA keep execution state and provenance outside normal business rows.

```json
{
  "source": "ESPN public NFL feeds",
  "status": "SUCCESS",
  "season": 2026,
  "requestedSeason": 2026,
  "seasonLabelPolicy": "seasonLabel is normalized as \"{season} NFL season\" using the source-reported season.",
  "seasonStartYearPolicy": "seasonStartYear equals the source-reported season.year, or requestedSeason when the source omits it.",
  "requestedItemCount": 5,
  "recordsStored": 5,
  "normalCount": 5,
  "diagnosticCount": 0,
  "duplicateCount": 0,
  "attemptedRequests": 4,
  "typeCounts": {
    "nfl-team": 1,
    "nfl-game": 1,
    "nfl-news": 3
  },
  "fixtureMode": false,
  "completedAt": "2026-09-09T00:00:08.000Z"
}
```

#### Partial and diagnostic outcomes

PARTIAL means at least one normal row and at least one diagnostic row were stored. DIAGNOSTIC means no normal row was available. ERROR is reserved for an unexpected workflow failure. Check diagnostic errorCode and url before retrying.

```json
{
  "source": "ESPN public NFL feeds",
  "status": "PARTIAL",
  "season": 2026,
  "requestedSeason": 2026,
  "requestedItemCount": 5,
  "recordsStored": 2,
  "normalCount": 1,
  "diagnosticCount": 1,
  "duplicateCount": 0,
  "attemptedRequests": 3,
  "typeCounts": {
    "nfl-team": 1
  },
  "fixtureMode": false,
  "completedAt": "2026-09-09T00:00:08.000Z"
}
```

```json
{
  "source": "ESPN public NFL feeds",
  "status": "DIAGNOSTIC",
  "season": 2026,
  "requestedSeason": 2026,
  "requestedItemCount": 1,
  "recordsStored": 1,
  "normalCount": 0,
  "diagnosticCount": 1,
  "duplicateCount": 0,
  "attemptedRequests": 1,
  "typeCounts": {},
  "fixtureMode": false,
  "completedAt": "2026-09-09T00:00:08.000Z"
}
```

#### Source metadata

```json
{
  "sourceName": "ESPN public NFL feeds",
  "sourceWebsite": "https://www.espn.com/nfl/",
  "directRequestsOnly": true,
  "fixtureMode": false,
  "seasonConvention": "ESPN NFL season.year is the season start year; a January or February game can belong to the prior season even when its calendar date is in the requested year.",
  "generatedAt": "2026-09-09T00:00:08.000Z"
}
```

### How to scrape in Apify Console

#### Console steps

1. Open the Actor in Apify Console.
2. Select a season and optionally enter public ESPN team slugs or abbreviations.
3. Set maxItems and decide whether public roster players are needed.
4. Click Start and wait for the run to finish.
5. Review normal rows, diagnostic rows, OUTPUT\_SUMMARY, and SOURCE\_METADATA together.
6. Export the dataset in the format you need.

For a real run, leave fixtureFile empty. Fixture mode is for local deterministic QA and must not be used as evidence of live ESPN availability.

### Cost and performance

Requests are sequential and paced by requestDelayMs. The run is bounded by maxTeams, maxItems, maxPlayersPerTeam, the 15-second per-request timeout, the 8 MiB response limit, and bounded retries. Lower maxItems and set includePlayers to false for a quick smoke check.

The Actor does not use a proxy or browser automation. Exact cost depends on Apify Actor compute, request latency, response size, and the number of selected teams and public roster records.

### Advanced usage

#### Season-aware analysis

Use season for the requested feed year, then group games by the emitted season field. January and February games can belong to the prior NFL season even when their calendar date is in the next year.

#### Team selection

An empty teams array discovers teams from the public standings feed. A non-empty teams array requests only the listed public ESPN keys, bounded by maxTeams. Team records can be followed by roster, scoreboard, and news rows until maxItems is reached.

#### Fail-closed behavior

Access refusal, login prompts, CAPTCHA, paywalls, geofences, WAF checks, invalid JSON, oversized responses, and rate limits become diagnostics. The Actor never turns a blocked source into a zero-valued record.

### API access

Apify API clients can start a run with the same JSON input, then read the default dataset and OUTPUT\_SUMMARY, OUTPUT, and SOURCE\_METADATA key-value records. This accesses Apify storage; it does not provide access to private ESPN systems.

```json
{
  "teams": [
    "buf"
  ],
  "maxTeams": 1,
  "maxItems": 5,
  "season": 2026,
  "includePlayers": false
}
```

Do not place credentials, cookies, private headers, or signed storage URLs in support tickets or public examples.

### Troubleshooting

#### The run contains only diagnostics

Inspect each diagnostic errorCode and url. Retry later for a transient network or upstream error; do not increase access or bypass settings because none are supported.

#### The season appears inconsistent

Compare requestedSeason, season, seasonStartYear, and seasonLabel. ESPN season.year is the NFL season start year, not always the calendar year of eventDate.

#### Fewer records than maxItems

maxItems is an upper bound. The public feed may contain fewer usable teams, players, games, or articles, or a diagnostic may consume one output slot.

#### Fixture data appears in a run

Check SOURCE\_METADATA.fixtureMode and OUTPUT\_SUMMARY.fixtureMode. Remove fixtureFile for real ESPN evidence.

### Issues and support

For support, include the Apify run ID, non-secret input shape, summary status, diagnostic error codes, and public source URL. Do not include cookies, private headers, credentials, or signed storage URLs.

### Privacy and non-affiliation

This Actor is an independent third-party tool. It collects public information from ESPN’s public NFL feeds and does not claim affiliation with, sponsorship by, or endorsement from ESPN. Treat athlete names, profiles, and images as potentially personal data; use them only for a lawful, legitimate purpose and respect ESPN’s terms and applicable law.

# Actor input Schema

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

Optional public ESPN team slugs or abbreviations. An empty list discovers teams from ESPN's public NFL standings feed.

## `maxTeams` (type: `integer`):

Maximum number of teams emitted from the public standings feed.

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

Upper bound for dataset records, including any fail-closed diagnostic record.

## `season` (type: `integer`):

Requested ESPN NFL season year. ESPN season.year is the season start year, so a January or February game can retain the prior season year.

## `requestDelayMs` (type: `integer`):

Minimum pacing delay between public ESPN requests. Access restrictions are never bypassed.

## `includePlayers` (type: `boolean`):

Fetch current public ESPN roster records for each selected team.

## `maxPlayersPerTeam` (type: `integer`):

Bounded number of public roster player records emitted per team.

## `fixtureFile` (type: `string`):

Optional local JSON fixture used only for deterministic verification. Leave empty for public ESPN requests; the path must remain inside the Actor directory.

## Actor input object example

```json
{
  "teams": [],
  "maxTeams": 8,
  "maxItems": 120,
  "season": 2026,
  "requestDelayMs": 250,
  "includePlayers": true,
  "maxPlayersPerTeam": 20
}
```

# Actor output Schema

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

Dataset containing normalized team, game, news, and diagnostic records.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("w3crawler/espn-nfl-stats-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("w3crawler/espn-nfl-stats-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 '{}' |
apify call w3crawler/espn-nfl-stats-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,w3crawler/espn-nfl-stats-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/rQGFQ8srtX9DhtBeU/builds/S5Ac4d9xdYiZvhQcY/openapi.json
