Add sportId param to stats endpoint definition
Hi Todd et al.,
I'm unable to retrieve Minor League (MiLB) player data, but can for Major League (MLB) players.
The link I used for Major League players returned data via https://statsapi.mlb.com/api/v1/sports/1/players?season=2024
Using this same link, but replacing sportID with 21 instead of 1, the API returned no players at all. https://statsapi.mlb.com/api/v1/sports/21/players?season=2024
At least using https://statsapi.mlb.com/api/v1/sports/21/ returns a 200 response, but otherwise, using optional fields like players does not work. When I use the wrapper API, it returns MLB players, which doesn't match the sportID I used.
# Set my params
params = {
"stats": "season",
"group": "pitching",
"season": "2024",
"sportId": "21",
"limit": 50,
"offset": 0,
}
# Fetch the stats
response = statsapi.get("stats", params=params)
# Are there stats available?
if "stats" in response and len(response["stats"]) > 0:
splits = response["stats"][0].get("splits", [])
df = pd.json_normalize(splits)
else:
print("No MiLB season data found for the given parameters.")
# What are the stats?
display(df.head())
The above code returns:
Shouldn't MLB have supplied this data for the Google X MLB Hackathon? Otherwise, I'm not sure if any fields were missing or incorrect.
I'd appreciate your help!
Thank you,
Ebod
Hello Ebod. You are getting MLB results because the sportId parameter is not in the supported param list for the stats endpoint. If you set up a debug logger for statsapi, it will log what it does with each parameter when it builds the API URL for your request.
import logging
import statsapi
logger = logging.getLogger('statsapi')
logger.setLevel(logging.DEBUG)
rootLogger = logging.getLogger()
rootLogger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
formatter = logging.Formatter("%(asctime)s - %(levelname)8s - %(name)s(%(thread)s) - %(message)s")
ch.setFormatter(formatter)
rootLogger.addHandler(ch)
# Set my params
params = {
"stats": "season",
"group": "pitching",
"season": "2024",
"sportId": "21",
"limit": 50,
"offset": 0,
}
# Fetch the stats
response = statsapi.get("stats", params=params)
Log:
2025-01-20 20:28:33,416 - DEBUG - statsapi(93252) - URL: [https://statsapi.mlb.com/api/{ver}/stats](https://statsapi.mlb.com/api/%7Bver%7D/stats)
2025-01-20 20:28:33,417 - DEBUG - statsapi(93252) - Found query param: stats
2025-01-20 20:28:33,418 - DEBUG - statsapi(93252) - Found query param: group
2025-01-20 20:28:33,418 - DEBUG - statsapi(93252) - Found query param: season
2025-01-20 20:28:33,419 - DEBUG - statsapi(93252) - Found invalid param, ignoring: sportId
2025-01-20 20:28:33,419 - DEBUG - statsapi(93252) - Found query param: limit
2025-01-20 20:28:33,420 - DEBUG - statsapi(93252) - Found query param: offset
2025-01-20 20:28:33,421 - DEBUG - statsapi(93252) - path_params: {}
2025-01-20 20:28:33,421 - DEBUG - statsapi(93252) - query_params: {'stats': 'season', 'group': 'pitching', 'season': '2024', 'limit': '50', 'offset': '0'}
2025-01-20 20:28:33,422 - DEBUG - statsapi(93252) - Replacing {ver} with default: v1.
2025-01-20 20:28:33,423 - DEBUG - statsapi(93252) - URL: https://statsapi.mlb.com/api/v1/stats
2025-01-20 20:28:33,423 - DEBUG - statsapi(93252) - Adding query parameter stats=season
2025-01-20 20:28:33,424 - DEBUG - statsapi(93252) - URL: https://statsapi.mlb.com/api/v1/stats?stats=season
2025-01-20 20:28:33,425 - DEBUG - statsapi(93252) - Adding query parameter group=pitching
2025-01-20 20:28:33,425 - DEBUG - statsapi(93252) - URL: https://statsapi.mlb.com/api/v1/stats?stats=season&group=pitching
2025-01-20 20:28:33,426 - DEBUG - statsapi(93252) - Adding query parameter season=2024
2025-01-20 20:28:33,426 - DEBUG - statsapi(93252) - URL: https://statsapi.mlb.com/api/v1/stats?stats=season&group=pitching&season=2024
2025-01-20 20:28:33,427 - DEBUG - statsapi(93252) - Adding query parameter limit=50
2025-01-20 20:28:33,427 - DEBUG - statsapi(93252) - URL: https://statsapi.mlb.com/api/v1/stats?stats=season&group=pitching&season=2024&limit=50
2025-01-20 20:28:33,428 - DEBUG - statsapi(93252) - Adding query parameter offset=0
2025-01-20 20:28:33,428 - DEBUG - statsapi(93252) - URL: https://statsapi.mlb.com/api/v1/stats?stats=season&group=pitching&season=2024&limit=50&offset=0
2025-01-20 20:28:33,433 - DEBUG - urllib3.connectionpool(93252) - Starting new HTTPS connection (1): statsapi.mlb.com:443
2025-01-20 20:28:33,555 - DEBUG - urllib3.connectionpool(93252) - [https://statsapi.mlb.com:443](https://statsapi.mlb.com/) "GET /api/v1/stats?stats=season&group=pitching&season=2024&limit=50&offset=0 HTTP/1.1" 200 None
Relevant lines:
2025-01-20 20:28:33,419 - DEBUG - statsapi(93252) - Found invalid param, ignoring: sportId
2025-01-20 20:28:33,428 - DEBUG - statsapi(93252) - URL: https://statsapi.mlb.com/api/v1/stats?stats=season&group=pitching&season=2024&limit=50&offset=0
If you want to force the wrapper to include the parameter that it considers invalid, you can do so by setting the force flag in the get function call:
response = statsapi.get("stats", params=params, force=True)
There is a sportIds (plural) parameter supported by the wrapper, because it's listed in the old MLB StatsAPI documentation that way. I see now that there is actually an example URL in the old documentation that calls the stats endpoint with the sportId parameter, even though that's not included in the supported parameter list. It should work either way (note that sportIds allows either a single value or multiple comma-separated values, while sportId only allows a single value), and I can add sportId to the stats endpoint so it doesn't need to be forced. However, both sportId=21 and sportIds=21 cause the endpoint to return no results with the rest of your parameters.
After all that, I think you may be using a defunct sportId... If I run the same call with sportId or 11, 12, or 13, I get results. Please refer to the sports endpoint for a list of supported sports, and you will hopefully be able to find the data you want.
Summary for future reference: the sportId parameter is supported by the stats endpoint, and the wrapper does not reflect that.
Thank you for getting back to me so soon, Todd!
You're right about the defunct sportId for 21, like you mentioned I also tried 11, 12, 13, etc. which did return data, so this worked out for me in the end. Appreciate you mentioning that.
I did however try to run the force param set to True for sportsId of 21, but the player stats weren't returned, so I agree this may be just defunct MLB source data itself.
I assumed the sportsId was 21 per the documentation from the Google X Hackathon.
I can at least retrieve team data but this is only if I use sportId like before instead of sportsId which logger flags as invalid.
teams_response = statsapi.get("teams", params={"sportId": "21"})
teams = teams_response.get("teams", [])
teams_df = pd.json_normalize(teams)
display(teams_df.head())
Thank you again for resolving this and I will monitor for any updates. Cheers!