sharpapi wraps the SharpAPI REST API, which normalizes
real-time sports betting odds from 45+ sportsbooks into one schema. Four
functions cover the common cases:
| Function | Endpoint | Minimum tier |
|---|---|---|
sharpapi_sports() |
/sports |
any |
sharpapi_odds() |
/odds |
any |
sharpapi_arbitrage() |
/opportunities/arbitrage |
Hobby |
sharpapi_ev() |
/opportunities/ev |
Pro |
Each returns a plain data frame, so the result goes straight into
whatever you normally use — dplyr, data.table,
base subsetting.
Every call needs an API key. A free tier is available at https://sharpapi.io. The
package reads credentials from the SHARPAPI_KEY environment
variable rather than function arguments. Avoid storing real keys in
source files:
For anything beyond a one-off session, put it in
~/.Renviron instead (usethis::edit_r_environ()
opens the file) so it is set for every session and stays out of your
project directory:
SHARPAPI_KEY=your-key-here
If the variable is unset, every function stops with a message telling you so rather than making an unauthenticated request.
library(sharpapi)
sports <- sharpapi_sports()
head(sports[order(-sports$live_count), c("id", "name", "event_count", "live_count")])#> id name event_count live_count
#> 1 soccer Soccer 812 37
#> 2 baseball Baseball 51 4
sharpapi_odds() passes any named argument through to the
API as a filter. Useful ones are sport,
league, sportsbook, market_type,
is_live and limit:
odds <- sharpapi_odds(sport = "baseball", market_type = "moneyline", limit = 500)
str(odds[1, c("sportsbook", "home_team", "away_team", "selection", "odds_american")])#> 'data.frame': 1 obs. of 5 variables:
#> $ sportsbook : chr "pinnacle"
#> $ home_team : chr "Yankees"
#> $ away_team : chr "Red Sox"
#> $ selection : chr "Yankees"
#> $ odds_american: int -145
Because the result is a data frame, comparing one market across books is ordinary R:
sharpapi_arbitrage() (Hobby tier and above) returns
cross-book arbitrage. The individual sides live in a nested
legs column rather than as top-level fields:
arb <- sharpapi_arbitrage(sport = "baseball")
arb[, c("event_id", "market_type", "profit_percent")]
# the two sides of the first opportunity
arb$legs[[1]]sharpapi_ev() (Pro tier and above) returns positive
expected value bets, priced against the sharp reference books:
ev <- sharpapi_ev(sport = "baseball")
head(ev[order(-ev$ev_percentage), c("sportsbook", "selection", "ev_percentage")])A key below the required tier raises an HTTP error rather than
returning an empty frame, so wrap these in try() if you are
probing what a key can reach.
If you want to work through the data shapes before signing up, sample snapshots (MLB and 2026 World Cup) are published as CSVs at https://github.com/Sharp-API/SharpAPI-Sample-Data. The snapshots use SharpAPI odds field names and provide offline examples for exploring the data. Live responses may include additional fields or nested objects:
HTTP errors are raised by httr2 with the response status
and API message. Common status codes include:
| Status | Meaning |
|---|---|
| 401 | key missing or invalid |
| 403 | key is valid but below the tier this endpoint requires |
| 429 | rate limit for your tier exceeded |