Skip to content

fix(espn): send an identifying User-Agent so ESPN stops returning 403 - #436

Merged
ChuckBuilds merged 1 commit into
mainfrom
fix/espn-user-agent-403
Aug 5, 2026
Merged

fix(espn): send an identifying User-Agent so ESPN stops returning 403#436
ChuckBuilds merged 1 commit into
mainfrom
fix/espn-user-agent-403

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Around 11:00 EDT on 2026-08-04, ESPN's site.api began rejecting the User-Agent strings this repo sends. Every scoreboard that goes through the shared data sources returned 403 Client Error: Forbidden — standings, game summaries and scoreboards alike. A device that had been running fine logged 287 ESPN errors in a day.

The filter is the opposite of the usual one

Probed site.api across agents and libraries, using requests as the plugins do:

User-Agent result
bare LEDMatrix/1.0 403 (with or without Accept)
bare LEDMatrix-Common/1.0 403
browser string (Chrome/Safari) 403 — no header rescues it
LEDMatrix/1.0 (+https://github.com/ChuckBuilds/LEDMatrix) 200
requests / urllib / curl defaults 200

ESPN rejects browser-style strings outright and bare custom tokens, while accepting honest client tokens or an agent that identifies the client and links to it. The reflex fix — "send a browser User-Agent" — is now the one change guaranteed to stay blocked.

Confirmed independently: ha-teamtracker#355 hit the same wall and switched from a browser agent to curl/8.20.0 (merged 2026-08-05).

The library matters too, which is worth recording. Under urllib, a bare token plus an Accept header still returned 200; under requests it does not. The data sources here were already sending Accept: application/json and were still refused.

The change

Two call sites sent a bare token:

  • src/base_classes/data_sources.pyLEDMatrix/1.0, the agent behind the failing standings and summary calls
  • src/common/api_helper.pyLEDMatrix-Common/1.0

Both now send an agent carrying the project URL. That form is accepted with or without Accept, on /scoreboard, /standings and /summary, and it gives ESPN a client to identify and contact rather than an anonymous token to rate-limit — which seems the likelier way to stay working than blending in as curl.

LEDMatrix-Plugin-Manager/1.0 is left alone: it talks to the GitHub API, not ESPN.

Verification

On a live 512x64 device, across a service restart:

  • ESPN errors: 287 in the preceding day → 0
  • live MLB games fetching again (mlb_live=True, MLB=4 live)
  • standings and game summaries no longer 403

Companion PR

The plugins monorepo carries the same bug in 24 files across 14 plugins and needs to land too, otherwise plugins that bundle their own data_sources.py stay broken: ChuckBuilds/ledmatrix-plugins#254.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Udr6MfaFLUPhX5Fgo67Jf5

Summary by CodeRabbit

  • Improvements
    • Updated network request identification to include the project URL in the User-Agent.
    • Added documentation clarifying the client identification required for data-source requests.

Around 11:00 EDT on 2026-08-04 ESPN's site.api began rejecting the agents
this repo sends. Every scoreboard that goes through the shared data
sources returned `403 Client Error: Forbidden` — standings, game
summaries and scoreboards alike. A device that had been running fine
logged 287 ESPN errors in a day.

The filter is not the familiar one. Probing site.api across agents and
libraries, using requests as the plugins do:

    bare 'LEDMatrix/1.0'                     403   (with or without Accept)
    browser string                           403   (no header rescues it)
    'LEDMatrix/1.0 (+https://github.com/...)' 200
    requests / urllib / curl defaults        200

So it rejects browser-style strings outright and bare custom tokens, and
accepts honest client tokens or an agent that identifies the client and
links to it. The instinct to "just send a browser User-Agent" is now
exactly backwards — that is the one thing guaranteed to stay blocked.

Both call sites here sent a bare token: `LEDMatrix/1.0` in the sports
data sources and `LEDMatrix-Common/1.0` in the API helper. The Accept
header the data sources already sent does not save it. Both now send an
agent carrying the project URL, which also gives ESPN someone to contact
rather than an anonymous token to rate-limit.

Verified on a live device: ESPN errors went from a steady stream to zero
across a restart, with live MLB games fetching again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Udr6MfaFLUPhX5Fgo67Jf5
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ca1379e9-3548-4aa3-912d-cf3565f54c42

📥 Commits

Reviewing files that changed from the base of the PR and between d305be6 and 0c612e1.

📒 Files selected for processing (2)
  • src/base_classes/data_sources.py
  • src/common/api_helper.py

📝 Walkthrough

Walkthrough

The request header helpers now send project-identifying User-Agent values that include the repository URL. DataSource.get_headers also documents the client identification requirement.

Changes

Request header identification

Layer / File(s) Summary
Update request User-Agent values
src/base_classes/data_sources.py, src/common/api_helper.py
Both request paths include the project URL in the User-Agent. DataSource.get_headers retains the JSON Accept header and adds client identification documentation.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the ESPN User-Agent change and its purpose of preventing 403 responses.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/espn-user-agent-403

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@ChuckBuilds
ChuckBuilds merged commit 5b81cca into main Aug 5, 2026
9 checks passed
@ChuckBuilds
ChuckBuilds deleted the fix/espn-user-agent-403 branch August 5, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant