Skip to content

fix(bref): retry on HTTP 429 rate-limit with exponential backoff (#344) - #417

Open
cissel wants to merge 1 commit into
BillPetti:masterfrom
cissel:fix/bref-rate-limit-retry
Open

fix(bref): retry on HTTP 429 rate-limit with exponential backoff (#344)#417
cissel wants to merge 1 commit into
BillPetti:masterfrom
cissel:fix/bref-rate-limit-retry

Conversation

@cissel

@cissel cissel commented Aug 8, 2026

Copy link
Copy Markdown

Pull Request

Table of Contents generated with DocToc

Summary

Baseball Reference rate-limits scrapers with HTTP 429 (Too Many Requests). The four Bref functions — bref_standings_on_date(), bref_daily_batter(), bref_daily_pitcher(), and bref_team_results() — used xml2::read_html() directly, which has no retry. When a 429 was returned, the tryCatch swallowed it into a misleading "Invalid arguments or no data available" message, the function returned NULL, and the user saw Error: object 'x' not found. This adds a bref_read_html() helper using httr2::req_retry() with exponential backoff (up to 3 tries) for 429/503, mirroring the retry pattern already established for NCAA scrapers (request_with_proxy() in the same file).

Type of Change

  • feat -- New feature (new endpoint wrapper, new exported function)
  • fix -- Bug fix (non-breaking change that fixes an issue)
  • docs -- Documentation only (roxygen, README, vignettes, NEWS)
  • test -- Adding or updating tests
  • refactor -- Code change that neither fixes a bug nor adds a feature
  • chore -- Tooling, build, or maintenance change
  • perf -- Performance improvement

Related Issues

Closes #344

Background & Context

Data source: Baseball Reference (baseball-reference.com).

Multiple users report HTTP 429 when calling bref_standings_on_date() sequentially for all six divisions (#344). The error is silently swallowed — the tryCatch error handler prints a generic "Invalid arguments or no data available" message and the function returns NULL, producing the confusing object 'x' not found downstream. The existing Sys.sleep(5) was placed after the scrape (not before a retry), so it couldn't help.

The package already has a retry pattern for NCAA scrapers in R/utils.R (request_with_proxy() uses httr2::req_retry with is_transient = 429/503). This PR extends the same pattern to Baseball Reference.

Changes Made

File / Function Change
R/utils.R / bref_read_html() New internal helper: httr2::request → req_retry(max_tries=3, is_transient=429/503) → req_timeout(30) → User-Agent header → 5s courtesy delay → xml2::read_html(resp_body_string(resp))
R/bref_standings_on_date.R Replace xml2::read_html(url) with bref_read_html(url); remove Sys.sleep(5) (moved to helper); error handler now prints conditionMessage(e)
R/bref_daily_batter.R Same changes
R/bref_daily_pitcher.R Same changes
R/bref_team_results.R Same changes
tests/testthat/test-bref_read_html.R New: verifies bref_read_html returns XML on valid page, errors on 404 (gated behind skip_if_offline / skip_on_cran)
NEWS.md Bug fix entry referencing #344

Submission Checklist

  • Code follows tidyverse style and the conventions in CLAUDE.md
    (native pipe |>, return-value initialized before tryCatch, cli
    messaging, any_of() column-drift guards).
  • I ran devtools::document() and committed the regenerated man/ and
    NAMESPACE (no hand edits).
  • Roxygen blocks are complete (@param, @return, @export, @family,
    runnable @examples).
  • I added or updated tests, respecting the live-API gating and the NCAA
    IP-ban caveat (NCAA endpoints exercised sparingly / against cached data).
  • devtools::check() passes with no new errors, warnings, or notes.
  • I updated NEWS.md, and reflected user-visible changes in
    cran-comments.md and _pkgdown.yml where applicable.
  • Commits follow Conventional Commits and contain no AI co-author
    trailers.

Testing

  • devtools::test() passes locally.
  • New/changed behavior is covered by a test using subset-direction column
    assertions.

Execution time before:
Execution time after:

Screenshots / Output

Reviewer Checklist

  • Function naming uses the correct data-source prefix.
  • Return value is initialized before tryCatch; error path returns an
    empty value with a cli message rather than erroring.
  • man/ and NAMESPACE are regenerated and consistent with the source.
  • Tests are appropriately gated; no test hammers the NCAA stats site.
  • NEWS.md / cran-comments.md / _pkgdown.yml are consistent for any
    API-surface change.
  • Commit messages are conventional and free of AI co-author trailers.

Additional Notes

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.

Rate limit issue with "bref_standings_on_date"

1 participant