| Title: | Client for the 'OddsRelay' Sports Odds API |
| Version: | 0.1.0 |
| Description: | Provides one function for each read endpoint of the 'OddsRelay' web service https://oddsrelay.io/docs, which serves bookmaker and exchange odds for the UK and Ireland. Requests ask for compressed replies, send entity tags so that unchanged data is not transferred again, wait as the service asks when it is busy, and signal refusals as classed conditions that carry the service's error code and request id. Replies are returned as lists and data frames, with the token and rate limit headers attached as attributes. |
| License: | MIT + file LICENSE |
| URL: | https://github.com/oddsrelay/oddsrelay-r, https://oddsrelay.io/docs |
| BugReports: | https://github.com/oddsrelay/oddsrelay-r/issues |
| Depends: | R (≥ 4.1.0) |
| Imports: | httr2 (≥ 1.2.0), jsonlite, stats, utils |
| Suggests: | testthat (≥ 3.1.7), withr |
| Config/oddsrelay/contract-version: | 2026-07-08 |
| Config/testthat/edition: | 3 |
| Encoding: | UTF-8 |
| Config/roxygen2/version: | 8.1.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-10-09 18:54:39 UTC; omfp |
| Author: | James OddsRelay [aut, cre], OddsRelay [cph] |
| Maintainer: | James OddsRelay <hello@oddsrelay.io> |
| Repository: | CRAN |
| Date/Publication: | 2026-10-21 14:40:02 UTC |
oddsrelay: Client for the 'OddsRelay' Sports Odds API
Description
Provides one function for each read endpoint of the 'OddsRelay' web service https://oddsrelay.io/docs, which serves bookmaker and exchange odds for the UK and Ireland. Requests ask for compressed replies, send entity tags so that unchanged data is not transferred again, wait as the service asks when it is busy, and signal refusals as classed conditions that carry the service's error code and request id. Replies are returned as lists and data frames, with the token and rate limit headers attached as attributes.
Your key
Every function but or_status(), or_coverage(), or_healthz() and
or_openapi() needs a key. Each reads it from the ODDSRELAY_KEY
environment variable, or from its .key argument. Put the key in your
.Renviron rather than in a script. It travels only in the Authorization
header: never in a URL, and never in a printed request, a message or a
condition.
What a call returns
The decoded JSON body, with every field the API sends, including fields it adds after this release. Arrays of records become data frames. Each result carries these attributes:
-
tokens: a list ofcost,used,remaining,limitandreset, from theX-Tokens-*headers.remainingandlimitmay be"unlimited". -
rate_limit: a list oflimit,remainingandreset, from theX-RateLimit-*headers. -
etagandrequest_id. -
not_modified:TRUEwhen the API answered 304 (see below).
A header the reply did not carry reads as NA (or NULL for etag and
request_id). A result that is not a data frame has the class
oddsrelay_result, which prints the body and one line of tokens instead of
every attribute; unclass() gives the plain list or string.
Polling
For each URL it has fetched in this session, the package keeps the reply's
ETag and body, and sends the tag back as If-None-Match. When nothing has
changed the API answers 304, which spends no tokens, and the call returns
the body you already had with attr(x, "not_modified") set to TRUE.
Errors
A refusal signals a condition of class oddsrelay_error, with the classes
oddsrelay_<code> (from the body's error.code, such as
oddsrelay_insufficient_tokens) and oddsrelay_http_<status> in front of
it. Each carries status, code, request_id, error (the whole error
object), tokens and rate_limit. A 402 also carries cost, remaining
and resets_at. Branch on the class or code, never on the message. Quote
request_id when you contact us.
A 429 is retried for you: the package waits the Retry-After seconds and
tries again, three requests in all, with a message before each wait, then
signals oddsrelay_http_429 with retry_after. Network failures are left as
'httr2' signals them.
Author(s)
Maintainer: OddsRelay hello@oddsrelay.io [copyright holder]
Authors:
OddsRelay hello@oddsrelay.io [copyright holder]
See Also
Useful links:
Report bugs at https://github.com/oddsrelay/oddsrelay-r/issues
Matched boards and raw odds
Description
or_odds() reads one matched board, where each selection pairs bookmaker
back prices with exchange lay prices. or_raw() reads bookmaker and
exchange prices by event, unpaired, a page at a time. or_event() reads one
event's raw prices. These calls spend tokens: pass quote = TRUE to learn
what a call costs without making it.
Usage
or_odds(type, ..., .key = NULL)
or_raw(..., .key = NULL)
or_event(id, ..., .key = NULL)
Arguments
type |
The board: |
... |
Filters, named as in the API reference, such as |
.key |
Your API key. Left out, it is read from the |
id |
An event id: |
Value
The decoded JSON body as a list, with arrays of records as data
frames; a format = "bare" call returns the data frame alone. The reply's
token and rate limit headers ride along as attributes: see
oddsrelay-package.
Examples
# What would it cost?
or_odds("standard", region = "uk", sports = "soccer", quote = TRUE)
board <- or_odds("standard", region = "uk", sports = "soccer", markets = "h2h")
board$data[, c("commence_time", "home_team", "away_team")]
attr(board, "tokens")
page <- or_raw(region = "uk", sports = "tennis", limit = 50)
page$meta$next_cursor
Sports, events, bookmakers and regions
Description
The keys the odds calls filter on. or_sports() lists sport and
competition keys and or_events() the upcoming events for one sport key,
both a page at a time (pass meta$next_cursor back as cursor).
or_bookmakers() lists the bookmakers and exchanges with their keys, and
or_regions() the region codes open to your key. These calls spend no
tokens.
Usage
or_sports(..., .key = NULL)
or_events(sport, ..., .key = NULL)
or_bookmakers(..., .key = NULL)
or_regions(.key = NULL)
Arguments
... |
Filters, named as in the API reference, such as |
.key |
Your API key. Left out, it is read from the |
sport |
A sport key from |
Value
The decoded JSON body as a list, with arrays of records as data
frames; a format = "bare" call returns the data frame alone. The reply's
token and rate limit headers ride along as attributes: see
oddsrelay-package.
Examples
sports <- or_sports(region = "uk")
head(sports$data[, c("key", "title")])
or_events("tennis", region = "uk")
or_bookmakers(format = "bare")
or_regions()
Service state
Description
or_status() says whether each product is serving and needs no key.
or_coverage() lists which bookmakers, exchanges and sports each product
carries; it needs no key, and gives more detail with one. or_health()
says when each board was last processed and whether any is behind.
or_healthz() is a plain-text liveness check and or_openapi() returns the
API description; neither needs a key.
Usage
or_status(..., .key = NULL)
or_coverage(..., .key = NULL)
or_health(.key = NULL)
or_healthz()
or_openapi()
Arguments
... |
Filters, named as in the API reference, such as |
.key |
Your API key. Left out, it is read from the |
Value
As for or_odds(). or_healthz() returns a single string and
or_openapi() a list that keeps the description's own structure.
Examples
or_status(region = "uk")
names(or_coverage(region = "uk")$products)
cat(or_healthz())
or_openapi()$info$version
Your account
Description
or_usage() returns the tokens your account has used and has left, and
what your key may read. or_pricing() returns the token rate card. Neither
spends tokens.
Usage
or_usage(.key = NULL)
or_pricing(.key = NULL)
Arguments
.key |
Your API key. Left out, it is read from the |
Value
The decoded JSON body as a list, with arrays of records as data
frames; a format = "bare" call returns the data frame alone. The reply's
token and rate limit headers ride along as attributes: see
oddsrelay-package.
Examples
or_usage()$tokens
or_pricing()