Fetch sports betting odds from odds-api.net in R. Discover sports, leagues and bookmakers, retrieve events and odds, and turn sports odds snapshots into data frames for analysis.
Data provider: Odds API · GitHub: odds-api/odds-api · API reference: Public REST API · Support: support@odds-api.net
Requires R 4.1 or later. Install from the Odds API R-Universe repository:
install.packages("oddsapi", repos = c(
"https://odds-api.r-universe.dev",
"https://cloud.r-project.org"
))
The package is also available directly from the Odds API GitHub repository:
install.packages("remotes")
remotes::install_github("odds-api/odds-api", subdir = "sdks/r",
dependencies = NA, upgrade = "never")
The package has not been submitted to CRAN. For local development from a checkout containing this package, run in the repository root:
install.packages("remotes")
remotes::install_local("sdks/r", dependencies = NA, upgrade = "never")
For a downloaded oddsapi_0.1.0.tar.gz source archive:
install.packages("httr2")
install.packages("oddsapi_0.1.0.tar.gz", repos = NULL, type = "source")
Create an odds-api.net account and obtain an API key.
Put ODDS_API_KEY=your_key in your user .Renviron, then restart R.
Keep that file out of version control.
library(oddsapi)
client <- odds_client() # reads ODDS_API_KEY
odds_sports(client)
odds_leagues(client, sport = "basketball")
odds_bookmakers(client, country_code = c("US", "AU"))
events <- odds_events(client, sport = "basketball", league = "NBA", limit = 10)
if (length(events$items)) {
event_id <- events$items[[1]]$event_id
snapshot <- odds_snapshot(client, event_id, types = "moneyline")
prices <- odds_table(snapshot)
print(prices[, c("bookmaker", "selection_name", "odds", "is_available")])
}
Responses remain nested lists with the API's original metadata and cursors.
odds_table() converts a sports odds response into a base R data frame and
preserves missing prices as NA. Line values remain text, so +3.5 is kept
exactly. Unavailable records remain visible. Snapshot as_of_ts_ms and
ttl_seconds are retained as data-frame attributes.
The package includes synthetic example odds. This example runs entirely offline:
library(oddsapi)
fixture <- system.file("extdata", "odds-snapshot.json", package = "oddsapi")
response <- httr2::response(
status_code = 200,
headers = list(`content-type` = "application/json"),
body = readBin(fixture, "raw", n = file.info(fixture)$size)
)
prices <- odds_table(httr2::resp_body_json(response, simplifyVector = FALSE))
prices[, c("bookmaker", "selection_name", "line", "odds", "is_available")]
The fixture uses invented events and bookmakers. It is not a live price feed. For a public network check without credentials, use:
odds_status(odds_client(api_key = ""))
Ordinary endpoint calls fetch one page. To collect events or sports odds across
pages, use odds_collect():
events <- odds_collect(client, "/events", league = "NBA", limit = 100,
max_pages = 10)
if (length(events$items)) {
event_id <- events$items[[1]]$event_id
all_odds <- odds_collect(
client, paste0("/events/", event_id, "/odds/snapshot"),
bookmakers = c("pinnacle", "betfair"), max_pages = 20
)
prices <- odds_table(all_odds)
}
Each page makes a separate API request and may consume credits. A repeated
cursor or exhausted max_pages raises an error so incomplete results are not
mistaken for a full response. Empty pages with a continuation cursor are followed.
Collecting changing odds over multiple requests does not produce an atomic
snapshot; retain the single-page response for its freshness and stream metadata.
Use the exact selection_key from an odds snapshot to request line movement:
if (length(events$items)) {
event_id <- events$items[[1]]$event_id
snapshot <- odds_snapshot(client, event_id, limit = 10)
if (length(snapshot$items) && !is.null(snapshot$items[[1]]$selection_key)) {
history <- odds_history(client, event_id,
selection_key = snapshot$items[[1]]$selection_key,
price_type = "odds")
}
results <- odds_results(client, event_id)
}
| Function | Data |
|---|---|
odds_sports(), odds_leagues()
|
Sports and league catalogs |
odds_bookmakers(), odds_event_bookmakers()
|
Bookmaker catalogs |
odds_events(), odds_event()
|
Sports events |
odds_snapshot() |
Sports odds and snapshot metadata |
odds_history() |
Selection price history and line movement |
odds_results() |
Event results |
odds_bets() |
API bet opportunities; filter with strategies
|
odds_racing_events(), odds_racing_event()
|
Racing events |
odds_racing_snapshot() |
Racing odds as nested bookmaker/runner lists |
odds_me(), odds_usage(), odds_limits()
|
Account identity and usage |
odds_status() |
Public service status |
odds_get() |
GET any public JSON endpoint |
odds_collect() |
Bounded cursor pagination |
odds_table() |
Sports odds as a data frame |
Pass named filters through ...; vectors become comma-separated API filters,
logical values become lowercase true/false, and NULL values are omitted.
See ?odds_endpoints or the API reference
for details. This release uses REST; SSE and WebSocket subscriptions are not
implemented.
tryCatch(
odds_events(client, league = "NBA"),
odds_api_error = function(e) {
message(conditionMessage(e))
if (identical(e$status, 429L)) {
message("Rate limited; Retry-After: ", e$retry_after)
}
}
)
The default timeout is 30 seconds with at most three attempts. HTTP 429, 502,
503, 504 and connection failures are retried with bounded backoff; Retry-After
is respected. Use odds_client(max_tries = 1) to disable retries.
API keys are redacted in printed requests. Redirects are rejected rather than
forwarding credentials. HTTPS is required except for local loopback tests.
From sdks/r with R and the development dependencies installed:
Rscript -e 'install.packages(c("httr2", "testthat", "roxygen2"))'
Rscript -e 'roxygen2::roxygenise(); testthat::test_local()'
R CMD build .
R CMD check --no-manual oddsapi_0.1.0.tar.gz
Tests use synthetic responses and require no API key. GitHub Actions checks the R package on Linux, Windows and macOS. The package contains only R code, so it does not need compilation tools for installation.
The client and synthetic fixtures are licensed under Apache-2.0. API access and data usage follow the terms at odds-api.net. When sharing research using the feed, cite the data provider with a link: Odds API — odds-api.net.
For betting research, account for stale prices, suspensions, voids, limits, delays, jurisdictional availability and execution risk. No opportunity guarantees a profit.