This document describes the HTTP transport behavior of the current release, version 1.0.0.
Version 0.8.0 introduced shared Sessions, explicit timeouts, bounded retries, and structured exceptions. Version 0.9.0 introduced configurable strict behavior and compatibility warnings. Version 1.0.0 makes strict handling the default and defines the stable public contract.
The public client remains synchronous. Ordinary usage does not need to configure sessions or retries.
See the 1.0.0 release notes for a shorter summary of what changed. For the authoritative public API boundary see the public API contract.
Everything this document describes is reachable from the package root:
from mlbstatsapi import (
Mlb,
MlbDataAdapter,
MlbDecodeError,
MlbHttpCompatibilityWarning,
MlbHttpError,
MlbTimeoutError,
MlbTransportError,
TheMlbStatsApiException,
create_retry_policy,
)Names that are not exported from mlbstatsapi are internal and may change
without a deprecation cycle. See public-api.md for the
complete stability classification.
Existing construction continues to work:
import mlbstatsapi
mlb = mlbstatsapi.Mlb()
player = mlb.get_person(664034)In version 1.0.0 that construction uses strict HTTP handling by default. The client remains synchronous. Async support is not part of version 1.0.0.
Prefer a context manager when you want automatic cleanup of a library-created Session:
import mlbstatsapi
with mlbstatsapi.Mlb() as mlb:
player = mlb.get_person(664034)Exiting the block closes a Session created by the library.
It does not close a caller-owned injected Session.
You can also close the client explicitly:
mlb = mlbstatsapi.Mlb()
try:
player = mlb.get_person(664034)
finally:
mlb.close()Repeated close() calls are safe.
Every request uses an explicit timeout.
The default is:
(3.05, 30.0)
That means:
3.05 seconds: connection timeout
30 seconds: read timeout
The read timeout is the maximum wait while reading response data. It is not one total wall-clock duration for the complete request.
A scalar applies the same value to both connect and read phases:
mlb = mlbstatsapi.Mlb(timeout=10)A tuple provides separate connect and read values:
mlb = mlbstatsapi.Mlb(
timeout=(5.0, 60.0),
)One requests.Session is shared by the client's adapters:
Mlb client
├── v1 adapter ────┐
│ ├── shared requests.Session
└── v1.1 adapter ──┘
A Session manages:
- Reusable connection pools
- Shared HTTP configuration
- Mounted retry adapters on library-created Sessions
A Session is not:
- A response cache
- One guaranteed permanent TCP connection
- An async transport
Session ownership is the single most important rule in this document. Whoever creates the Session configures it and closes it.
Library-created Session
Configured and closed by the library
Receives retry adapters
Receives the package User-Agent
Caller-injected Session
Configured and closed by the caller
Existing adapters remain untouched
Existing headers remain untouched
The library never installs adapters, replaces headers, or closes a Session it
did not create. Mlb.close() and exiting with Mlb(session=session) both
leave an injected Session open.
The version 1.0 strict default does not change Session ownership, injection, or cleanup behavior.
Advanced callers may inject a Session:
import requests
import mlbstatsapi
session = requests.Session()
try:
mlb = mlbstatsapi.Mlb(session=session)
player = mlb.get_person(664034)
finally:
session.close()Callers who inject a Session control its retry, TLS, proxy, header, and adapter configuration. See Reusing the retry policy on a caller-managed Session for opting in to the library's tested retry policy.
Library-created Sessions send a package-specific User-Agent:
python-mlb-statsapi/<installed-version>
With the package version currently declared in project metadata that resolves to:
python-mlb-statsapi/1.0.0
The version comes from the installed package metadata, so it always matches the installed release without a separately maintained version string.
Notes:
- The header helps identify package traffic while debugging
- Other Requests default headers such as
Accept-Encoding,Accept, andConnectionremain intact - Only
User-Agentis set; the full header mapping is never replaced - Caller-injected Sessions are never modified
- Applications using an injected Session may set their own User-Agent
- The header contains no machine identifiers, installation identifiers, hostnames, or user tracking data
- This is not telemetry and sends no analytics
If the distribution metadata is unavailable, for example in an unusual source-only environment, the header falls back to:
python-mlb-statsapi/unknown
Callers who inject a Session control the User-Agent themselves:
import requests
import mlbstatsapi
session = requests.Session()
session.headers.update(
{
"User-Agent": "my-baseball-project/1.0",
}
)
try:
with mlbstatsapi.Mlb(session=session) as mlb:
player = mlb.get_person(664034)
finally:
session.close()Library-created Sessions mount a bounded retry policy for GET requests automatically.
Caller-injected Sessions are never automatically reconfigured. Retry settings on an injected Session remain under the caller's control unless the caller opts in.
Initial request: 1
Maximum retries: 3
Maximum total attempts: 4
Allowed method: GET
Backoff factor: 0.5
Retry-After respected: yes
Retryable HTTP statuses:
429
500
502
503
504
Non-retryable ordinary client statuses:
400
401
403
404
Additional rules:
- Retries are bounded
- Only GET requests are retried
- Invalid JSON is not retried
- Pydantic validation failures are not retried
- Application parsing failures are not retried
- A final 404 preserves existing not-found behavior
- A final non-404 4xx, including a final 429, raises
MlbHttpErrorunder the default - A final 5xx raises
MlbHttpError - Explicit
strict_http=Falsepreserves the historical empty result for final non-404 4xx and warns
Retries improve resilience for transient failures. They do not guarantee success. The version 1.0 strict default does not change retry values or which statuses are retried.
Version 1.0.0 defaults to strict HTTP handling. These constructions are equivalent:
mlb = mlbstatsapi.Mlb()
mlb = mlbstatsapi.Mlb(strict_http=True)Default behavior:
Successful 2xx
Return the normal endpoint result
Final non-404 4xx
Raise MlbHttpError by default
404
Preserve endpoint-specific None, [], or {} behavior
Final 429
Retry first, then raise MlbHttpError under the default
Final 5xx
Retry where configured, then raise MlbHttpError
Timeout
Raise MlbTimeoutError
Transport failure
Raise MlbTransportError
Successful malformed JSON
Raise MlbDecodeError
"Final" means the response remaining after the bounded retry policy has completed. Intermediate retried responses neither raise nor warn.
| Final response | Default 1.0 behavior | Explicit strict_http=False |
|---|---|---|
| Successful 2xx | Normal result | Normal result |
| Non-404 4xx | MlbHttpError |
Warning and historical empty result |
| 404 | Existing endpoint behavior | Existing endpoint behavior |
| Final 429 | MlbHttpError after retries |
Warning and historical empty result after retries |
| Final 5xx | MlbHttpError |
MlbHttpError |
Notes:
Mlb()andMlb(strict_http=True)are equivalent spellings of the defaultstrict_http=Falseis an explicit compatibility opt-out, not the preferred long-term configuration- Strict handling applies only after retries are exhausted
- Strict handling does not make 404 raise
- Strict handling does not change transport or decode exceptions
- Raised
MlbHttpErrorinstances include the richer response context attributes
Recommended default usage:
import mlbstatsapi
try:
with mlbstatsapi.Mlb() as mlb:
player = mlb.get_person(664034)
except mlbstatsapi.MlbHttpError as exc:
print(exc.method)
print(exc.status_code)
print(exc.reason)
print(exc.url)
print(exc.response_data)
print(exc.body_excerpt)mlb = mlbstatsapi.Mlb(strict_http=False)is an explicit compatibility opt-out. It:
- Preserves the historical empty result for final non-404 4xx responses
- Emits
MlbHttpCompatibilityWarningexactly once per suppressed final response - Does not change 404 behavior
- Does not suppress final 5xx errors
- Does not alter timeout, transport, or decode failures
- Runs only after retry exhaustion
Compatibility mode is a temporary migration path and an explicit request for historical 0.9 behavior. It is not the recommended long-term 1.0 configuration.
When strict_http=False converts a final non-404 4xx response into the
historical empty result, the library emits MlbHttpCompatibilityWarning.
The warning means the default strict path would have raised MlbHttpError for
the same response. It is emitted once per suppressed final response and is
attributed to the public caller frame outside the mlbstatsapi package
namespace.
The warning's semantic content includes:
Status code
Request URL
strict_http=False selected compatibility mode
Historical empty result was returned
Strict handling is the version 1.0 default
How to receive MlbHttpError instead
Warning messages contain only the status code and request URL from the response. Response bodies, headers, credentials, cookies, and tokens are never included. Do not treat the complete prose string as a stable public contract; filter and handle the warning by category.
The category inherits from FutureWarning so the migration notice stays
visible under default Python warning filters.
A warning is emitted only when all three of the following are true:
- Compatibility mode is active (
strict_http=False) - The final response is a non-404 4xx
- The default strict path would have raised
MlbHttpErrorfor the same response
No warning is emitted for:
Successful responses
404
Default strict handling
Intermediate retries
Final 5xx
Timeouts
Transport failures
Decode failures
Pydantic validation failures
When the warning is emitted:
| Response | Warning |
|---|---|
Non-404 4xx, strict_http=False |
Yes |
Final 429, strict_http=False, after retries |
Yes |
Non-404 4xx, default / strict_http=True |
No, MlbHttpError is raised instead |
| 404, either mode | No |
| Successful 2xx | No |
| Final 5xx | No, MlbHttpError is raised in both modes |
| Timeout, transport, decode, or validation failure | No |
Additional rules:
- The warning never changes the return value; compatibility mode still returns the historical empty result
- A final 404 remains warning-free and keeps existing
None/[]/{}behavior - Warnings are emitted only after retries are exhausted, so a retried 429 warns once
- Default strict handling does not warn because it raises
MlbHttpErrordirectly
Applications may treat warnings as exceptions:
import warnings
import mlbstatsapi
warnings.filterwarnings(
"error",
category=mlbstatsapi.MlbHttpCompatibilityWarning,
)Under strict_http=False, this can turn a suppressed 4xx into a warning
exception. The preferred migration is to remove strict_http=False and catch
MlbHttpError. A temporary targeted warning filter is acceptable.
Temporary targeted ignore, labeled as migration-only behavior:
import warnings
import mlbstatsapi
warnings.filterwarnings(
"ignore",
category=mlbstatsapi.MlbHttpCompatibilityWarning,
)Ignoring the warning is temporary migration behavior. Disabling every warning
or every FutureWarning is not recommended; that would also hide unrelated
notices from other libraries. Filter by
mlbstatsapi.MlbHttpCompatibilityWarning specifically.
Recommended process:
- Identify code that relied on empty results for failed non-404 4xx responses
- Add handling for
MlbHttpError - Distinguish 404 domain results from other HTTP failures
- Use
strict_http=Falseonly where migration cannot happen immediately - Test warning-as-error configurations
- Remove
strict_http=False - Confirm injected Session and retry behavior remain correct
Version 0.9-style compatibility:
import mlbstatsapi
with mlbstatsapi.Mlb(strict_http=False) as mlb:
player = mlb.get_person(664034)Recommended 1.0 state:
import mlbstatsapi
try:
with mlbstatsapi.Mlb() as mlb:
player = mlb.get_person(664034)
except mlbstatsapi.MlbHttpError as exc:
print(exc.status_code)
print(exc.url)A missing person may still return None on a 404 and is not necessarily an
exception. Catch MlbHttpError for unexpected HTTP failures; continue treating
endpoint-specific 404 empty results as domain-level not-found outcomes.
Version 1.0.0 documents the stable public API in public-api.md. That contract covers package-root imports, constructor signatures, the exception hierarchy, Session ownership, documented endpoint methods, Python support, and the boundary between public and internal APIs.
This transport guide does not duplicate that contract. In particular, version 1.0 does not promise that:
- Every upstream MLB response field is frozen
- Every class in
mlbstatsapi.modelsis permanently stable - The unofficial MLB API itself will never change
- Private underscore-prefixed names are public
create_retry_policy() returns a new instance of the same tested retry policy
used internally for library-created Sessions.
Callers who inject a Session must mount the policy themselves. The library does not install or replace adapters on caller-injected Sessions.
Callers retain control over connection-pool sizes and other HTTPAdapter
options. The caller remains responsible for closing an injected Session.
import requests
import mlbstatsapi
session = requests.Session()
adapter = requests.adapters.HTTPAdapter(
max_retries=mlbstatsapi.create_retry_policy(),
pool_connections=10,
pool_maxsize=20,
)
session.mount("https://", adapter)
session.mount("http://", adapter)
try:
with mlbstatsapi.Mlb(session=session) as mlb:
player = mlb.get_person(664034)
finally:
session.close()Mounting the same adapter instance for both schemes is valid. Callers may also mount separate adapters when they need different settings for HTTP and HTTPS.
TheMlbStatsApiException
├── MlbTransportError
│ └── MlbTimeoutError
├── MlbHttpError
└── MlbDecodeError
Imports:
from mlbstatsapi import (
MlbDecodeError,
MlbHttpError,
MlbTimeoutError,
MlbTransportError,
TheMlbStatsApiException,
)Precise handling:
try:
player = mlb.get_person(664034)
except MlbTimeoutError:
print("The MLB API timed out")
except MlbTransportError:
print("The request could not reach the MLB API")
except MlbHttpError as exc:
print(
exc.status_code,
exc.reason,
exc.url,
)
except MlbDecodeError:
print("The MLB API returned invalid JSON")Backward-compatible handling remains valid because all new errors inherit from
TheMlbStatsApiException:
try:
player = mlb.get_person(664034)
except TheMlbStatsApiException:
print("The MLB request failed")Notes:
MlbTimeoutErroris a subtype ofMlbTransportError- All new errors inherit from
TheMlbStatsApiException - Existing broad exception handling remains valid
- Original Requests or JSON decoding failures are preserved through exception chaining
MlbHttpError exposes:
method
status_code
reason
url
response_data
body_excerpt
Existing attributes remain compatible:
status_code
reason
url
Additional context:
methodis the HTTP method when the adapter raises the error (GETtoday). Manually constructed exceptions without a method leavemethodasNone.response_datacontains a decoded JSON dictionary or list when the error body is valid JSON of that shape.response_dataisNonefor invalid JSON, HTML, plain text, empty bodies, or JSON scalars such as strings, numbers, booleans, or null.body_excerptcontains at most 500 characters of the response text for non-empty bodies.body_excerptisNonefor an empty body.- Error-context extraction is best-effort. A parsing or decoding failure while collecting context must not replace the original HTTP error.
- Complete
requests.Responseobjects are not exposed. - Complete large response bodies are not preserved beyond the excerpt limit.
- Response bodies are not automatically logged.
str(exc)remains concise, for example500: Internal Server Error, and does not include the response body, excerpt, orresponse_data.
Usage:
try:
player = mlb.get_person(664034)
except mlbstatsapi.MlbHttpError as exc:
print(exc.method)
print(exc.status_code)
print(exc.reason)
print(exc.url)
print(exc.response_data)
print(exc.body_excerpt)Version 1.0.0 preserves endpoint-specific not-found behavior under both the
default and strict_http=False.
Depending on the endpoint, a 404 may become:
None
[]
{}
Not every 404 raises MlbHttpError. The strict default does not change this,
and a 404 never emits MlbHttpCompatibilityWarning.
Shared Sessions pool network connections. They do not cache MLB response bodies.
The client has no default response cache.
The client remains synchronous.
Async support is not part of version 1.0.0.