sharpapi 0.2.5__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sharpapi-0.2.5 → sharpapi-0.3.1}/.gitignore +6 -0
- sharpapi-0.3.1/CHANGELOG.md +58 -0
- sharpapi-0.3.1/LICENSE +21 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/PKG-INFO +3 -2
- sharpapi-0.3.1/SECURITY.md +25 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/pyproject.toml +21 -2
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/__init__.py +9 -1
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/async_client.py +37 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/client.py +49 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/exceptions.py +19 -10
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/models.py +158 -15
- sharpapi-0.2.5/.github/dependabot.yml +0 -22
- sharpapi-0.2.5/.github/workflows/publish.yml +0 -69
- sharpapi-0.2.5/.github/workflows/test.yml +0 -40
- sharpapi-0.2.5/tests/__init__.py +0 -0
- sharpapi-0.2.5/tests/conftest.py +0 -275
- sharpapi-0.2.5/tests/test_async_client.py +0 -238
- sharpapi-0.2.5/tests/test_client.py +0 -407
- sharpapi-0.2.5/tests/test_dataframe.py +0 -83
- sharpapi-0.2.5/tests/test_models.py +0 -193
- sharpapi-0.2.5/tests/test_utils.py +0 -75
- {sharpapi-0.2.5 → sharpapi-0.3.1}/README.md +0 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/_base.py +0 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/_utils.py +0 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/py.typed +0 -0
- {sharpapi-0.2.5 → sharpapi-0.3.1}/src/sharpapi/streaming.py +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `sharpapi` Python SDK are documented here.
|
|
4
|
+
|
|
5
|
+
## 0.3.1 — 2026-05-06
|
|
6
|
+
|
|
7
|
+
### Added — TeamRef metadata
|
|
8
|
+
|
|
9
|
+
`TeamRef` now exposes five additional optional fields:
|
|
10
|
+
|
|
11
|
+
- `logo` — full CDN URL. ~93% of teams are populated.
|
|
12
|
+
- `city` — e.g. `"Arizona"` for the Diamondbacks.
|
|
13
|
+
- `mascot` — e.g. `"Diamondbacks"`.
|
|
14
|
+
- `conference` — e.g. `"NL"`, `"AFC"`, `"Western"`.
|
|
15
|
+
- `division` — e.g. `"West Division"`, `"NL East"`, `"Pacific Division"`.
|
|
16
|
+
|
|
17
|
+
All five default to `None` and are additive — existing 0.3.0 code keeps
|
|
18
|
+
working unchanged.
|
|
19
|
+
|
|
20
|
+
## 0.3.0 — 2026-05-06
|
|
21
|
+
|
|
22
|
+
### Added — nested refs
|
|
23
|
+
|
|
24
|
+
Every odds row, opportunity row, and reference-list row may now carry
|
|
25
|
+
optional structured reference objects alongside the existing flat fields.
|
|
26
|
+
All new fields are **optional and additive** — clients on older API
|
|
27
|
+
versions (or talking to older API servers) see `None` and behave
|
|
28
|
+
identically.
|
|
29
|
+
|
|
30
|
+
New models:
|
|
31
|
+
|
|
32
|
+
- `TeamRef` — `id`, `numerical_id`, `name`, `abbreviation` (latter only on
|
|
33
|
+
team-sport competitors)
|
|
34
|
+
- `SportRef` — `id`, `name`, `numerical_id`
|
|
35
|
+
- `EntityRef` — `id`, `label`, `numerical_id` (used for league / market /
|
|
36
|
+
sportsbook refs)
|
|
37
|
+
|
|
38
|
+
New optional fields:
|
|
39
|
+
|
|
40
|
+
- `OddsLine`, `EVOpportunity`, `ArbitrageOpportunity`, `MiddleOpportunity`,
|
|
41
|
+
`LowHoldOpportunity` — all gain `home`, `away`, `sport_ref`, `league_ref`,
|
|
42
|
+
`market_ref`, `sportsbook_ref` (legs / opps without a single book skip
|
|
43
|
+
`sportsbook_ref`).
|
|
44
|
+
- `ArbitrageLeg` — gains `sportsbook_ref`.
|
|
45
|
+
- `ClosingOddsLine` — gains `market_ref`, `sportsbook_ref`.
|
|
46
|
+
- `ClosingSnapshot` — gains `home`, `away`, `sport_ref`, `league_ref`.
|
|
47
|
+
- `Sport`, `League`, `Sportsbook`, `Market` — gain `numerical_id`.
|
|
48
|
+
- `Event` — gains `home`, `away`, `sport_ref`, `league_ref`.
|
|
49
|
+
|
|
50
|
+
New reference model:
|
|
51
|
+
|
|
52
|
+
- `Team` — for the `/teams` reference endpoint, includes optional
|
|
53
|
+
`abbreviation` and `numerical_id`.
|
|
54
|
+
|
|
55
|
+
### Backward compatibility
|
|
56
|
+
|
|
57
|
+
No existing field was renamed, retyped, or removed. Code that does not
|
|
58
|
+
reference the new attributes continues to work without changes.
|
sharpapi-0.3.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SharpAPI LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharpapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Official Python SDK for the SharpAPI real-time sports betting odds API
|
|
5
5
|
Project-URL: Homepage, https://sharpapi.io
|
|
6
6
|
Project-URL: Documentation, https://docs.sharpapi.io/sdks/python
|
|
7
7
|
Project-URL: Repository, https://github.com/Sharp-API/sharpapi-python
|
|
8
8
|
Project-URL: Changelog, https://github.com/Sharp-API/sharpapi-python/releases
|
|
9
|
-
Author-email: SharpAPI <
|
|
9
|
+
Author-email: SharpAPI <hello@sharpapi.io>
|
|
10
10
|
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
11
12
|
Keywords: api,arbitrage,ev,odds,pinnacle,real-time,sports-betting
|
|
12
13
|
Classifier: Development Status :: 4 - Beta
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
If you believe you have found a security vulnerability in this SDK or in
|
|
6
|
+
the SharpAPI service, please report it privately to:
|
|
7
|
+
|
|
8
|
+
**hello@sharpapi.io** (subject line: `[SECURITY] <short summary>`)
|
|
9
|
+
|
|
10
|
+
Please do not open a public GitHub issue for security reports.
|
|
11
|
+
|
|
12
|
+
We will acknowledge receipt within 72 hours and aim to provide a status
|
|
13
|
+
update within 7 days. If the issue is confirmed, we will work with you on
|
|
14
|
+
disclosure timing.
|
|
15
|
+
|
|
16
|
+
## Scope
|
|
17
|
+
|
|
18
|
+
In scope:
|
|
19
|
+
- This SDK package and its published artifact on PyPI
|
|
20
|
+
- The SharpAPI HTTP and WebSocket APIs (`api.sharpapi.io`, `ws.sharpapi.io`)
|
|
21
|
+
|
|
22
|
+
Out of scope:
|
|
23
|
+
- Findings in third-party dependencies (please report those upstream)
|
|
24
|
+
- Denial of service via brute-force or volumetric attacks against the API
|
|
25
|
+
- Issues that require physical access to a user's device
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sharpapi"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.1"
|
|
8
8
|
description = "Official Python SDK for the SharpAPI real-time sports betting odds API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
|
-
authors = [{ name = "SharpAPI", email = "
|
|
12
|
+
authors = [{ name = "SharpAPI", email = "hello@sharpapi.io" }]
|
|
13
13
|
keywords = ["sports-betting", "odds", "arbitrage", "ev", "api", "real-time", "pinnacle"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 4 - Beta",
|
|
@@ -41,6 +41,25 @@ Changelog = "https://github.com/Sharp-API/sharpapi-python/releases"
|
|
|
41
41
|
[tool.hatch.build.targets.wheel]
|
|
42
42
|
packages = ["src/sharpapi"]
|
|
43
43
|
|
|
44
|
+
# Explicit sdist whitelist. Hatchling's default sdist would otherwise
|
|
45
|
+
# include everything not gitignored (CI workflows, dependabot config,
|
|
46
|
+
# .gitignore itself), all of which would land on PyPI on every release.
|
|
47
|
+
# Keep this in sync with what we actually want users who do
|
|
48
|
+
# `pip install --no-binary :all: sharpapi` to receive.
|
|
49
|
+
[tool.hatch.build.targets.sdist]
|
|
50
|
+
include = [
|
|
51
|
+
"src/sharpapi",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"CHANGELOG.md",
|
|
55
|
+
"SECURITY.md",
|
|
56
|
+
"pyproject.toml",
|
|
57
|
+
]
|
|
58
|
+
exclude = [
|
|
59
|
+
".gitignore",
|
|
60
|
+
".github",
|
|
61
|
+
]
|
|
62
|
+
|
|
44
63
|
[tool.ruff]
|
|
45
64
|
target-version = "py310"
|
|
46
65
|
line-length = 100
|
|
@@ -38,6 +38,7 @@ from .models import (
|
|
|
38
38
|
ArbitrageOpportunity,
|
|
39
39
|
ClosingOddsLine,
|
|
40
40
|
ClosingSnapshot,
|
|
41
|
+
EntityRef,
|
|
41
42
|
Event,
|
|
42
43
|
EVOpportunity,
|
|
43
44
|
GameState,
|
|
@@ -53,11 +54,14 @@ from .models import (
|
|
|
53
54
|
RateLimitInfo,
|
|
54
55
|
ResponseMeta,
|
|
55
56
|
Sport,
|
|
57
|
+
SportRef,
|
|
56
58
|
Sportsbook,
|
|
59
|
+
Team,
|
|
60
|
+
TeamRef,
|
|
57
61
|
)
|
|
58
62
|
from .streaming import EventStream
|
|
59
63
|
|
|
60
|
-
__version__ = "0.
|
|
64
|
+
__version__ = "0.3.1"
|
|
61
65
|
|
|
62
66
|
__all__ = [
|
|
63
67
|
# Clients
|
|
@@ -71,6 +75,7 @@ __all__ = [
|
|
|
71
75
|
"ArbitrageOpportunity",
|
|
72
76
|
"ClosingOddsLine",
|
|
73
77
|
"ClosingSnapshot",
|
|
78
|
+
"EntityRef",
|
|
74
79
|
"EVOpportunity",
|
|
75
80
|
"Event",
|
|
76
81
|
"GameState",
|
|
@@ -86,7 +91,10 @@ __all__ = [
|
|
|
86
91
|
"RateLimitInfo",
|
|
87
92
|
"ResponseMeta",
|
|
88
93
|
"Sport",
|
|
94
|
+
"SportRef",
|
|
89
95
|
"Sportsbook",
|
|
96
|
+
"Team",
|
|
97
|
+
"TeamRef",
|
|
90
98
|
# Streaming
|
|
91
99
|
"EventStream",
|
|
92
100
|
# Exceptions
|
|
@@ -29,6 +29,7 @@ from .models import (
|
|
|
29
29
|
ClosingSnapshot,
|
|
30
30
|
Event,
|
|
31
31
|
EVOpportunity,
|
|
32
|
+
GameState,
|
|
32
33
|
League,
|
|
33
34
|
LowHoldOpportunity,
|
|
34
35
|
Market,
|
|
@@ -103,6 +104,7 @@ class AsyncSharpAPI:
|
|
|
103
104
|
self.arbitrage = _AsyncArbitrageResource(self)
|
|
104
105
|
self.middles = _AsyncMiddlesResource(self)
|
|
105
106
|
self.low_hold = _AsyncLowHoldResource(self)
|
|
107
|
+
self.gamestate = _AsyncGameStateResource(self)
|
|
106
108
|
self.sports = _AsyncSportsResource(self)
|
|
107
109
|
self.leagues = _AsyncLeaguesResource(self)
|
|
108
110
|
self.sportsbooks = _AsyncSportsbooksResource(self)
|
|
@@ -426,6 +428,41 @@ class _AsyncLowHoldResource:
|
|
|
426
428
|
return parse_response(data, LowHoldOpportunity)
|
|
427
429
|
|
|
428
430
|
|
|
431
|
+
class _AsyncGameStateResource:
|
|
432
|
+
"""Async access to live game state — scores, period, clock —
|
|
433
|
+
merged across sportsbooks.
|
|
434
|
+
|
|
435
|
+
Requires the Game State add-on ($79/mo) or Enterprise tier.
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
def __init__(self, client: AsyncSharpAPI):
|
|
439
|
+
self._client = client
|
|
440
|
+
|
|
441
|
+
async def get(self, sport: str | None = None) -> dict[str, dict[str, GameState]]:
|
|
442
|
+
"""Fetch the current game state.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
sport: Limit to a single sport (e.g. ``"basketball"``).
|
|
446
|
+
Omit to fetch every sport at once.
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
Nested mapping ``{sport: {event_id: GameState}}``.
|
|
450
|
+
"""
|
|
451
|
+
path = f"/gamestate/{sport}" if sport else "/gamestate"
|
|
452
|
+
data = await self._client._get(path)
|
|
453
|
+
raw = data.get("data", {}) or {}
|
|
454
|
+
result: dict[str, dict[str, GameState]] = {}
|
|
455
|
+
for sport_key, events in raw.items():
|
|
456
|
+
if not isinstance(events, dict):
|
|
457
|
+
continue
|
|
458
|
+
result[sport_key] = {
|
|
459
|
+
eid: GameState.model_validate(state)
|
|
460
|
+
for eid, state in events.items()
|
|
461
|
+
if isinstance(state, dict)
|
|
462
|
+
}
|
|
463
|
+
return result
|
|
464
|
+
|
|
465
|
+
|
|
429
466
|
class _AsyncSportsResource:
|
|
430
467
|
def __init__(self, client: AsyncSharpAPI):
|
|
431
468
|
self._client = client
|
|
@@ -29,6 +29,7 @@ from .models import (
|
|
|
29
29
|
ClosingSnapshot,
|
|
30
30
|
Event,
|
|
31
31
|
EVOpportunity,
|
|
32
|
+
GameState,
|
|
32
33
|
League,
|
|
33
34
|
LowHoldOpportunity,
|
|
34
35
|
Market,
|
|
@@ -111,6 +112,7 @@ class SharpAPI:
|
|
|
111
112
|
self.arbitrage = _ArbitrageResource(self)
|
|
112
113
|
self.middles = _MiddlesResource(self)
|
|
113
114
|
self.low_hold = _LowHoldResource(self)
|
|
115
|
+
self.gamestate = _GameStateResource(self)
|
|
114
116
|
self.sports = _SportsResource(self)
|
|
115
117
|
self.leagues = _LeaguesResource(self)
|
|
116
118
|
self.sportsbooks = _SportsbooksResource(self)
|
|
@@ -542,6 +544,44 @@ class _LowHoldResource:
|
|
|
542
544
|
return _parse_response(data, LowHoldOpportunity)
|
|
543
545
|
|
|
544
546
|
|
|
547
|
+
class _GameStateResource:
|
|
548
|
+
"""Live game state — scores, period, clock — merged across sportsbooks.
|
|
549
|
+
|
|
550
|
+
Requires the Game State add-on ($79/mo) or Enterprise tier.
|
|
551
|
+
Pair with EV / arb / low-hold rows: those endpoints no longer carry
|
|
552
|
+
``game_state`` themselves — look up the row's ``event_id`` here.
|
|
553
|
+
"""
|
|
554
|
+
|
|
555
|
+
def __init__(self, client: SharpAPI):
|
|
556
|
+
self._client = client
|
|
557
|
+
|
|
558
|
+
def get(self, sport: str | None = None) -> dict[str, dict[str, GameState]]:
|
|
559
|
+
"""Fetch the current game state.
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
sport: Limit to a single sport (e.g. ``"basketball"``,
|
|
563
|
+
``"football"``). Omit to fetch every sport at once.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
Nested mapping ``{sport: {event_id: GameState}}``. Look up an
|
|
567
|
+
opportunity's state with
|
|
568
|
+
``result.get(opp.sport, {}).get(opp.event_id)``.
|
|
569
|
+
"""
|
|
570
|
+
path = f"/gamestate/{sport}" if sport else "/gamestate"
|
|
571
|
+
data = self._client._get(path)
|
|
572
|
+
raw = data.get("data", {}) or {}
|
|
573
|
+
result: dict[str, dict[str, GameState]] = {}
|
|
574
|
+
for sport_key, events in raw.items():
|
|
575
|
+
if not isinstance(events, dict):
|
|
576
|
+
continue
|
|
577
|
+
result[sport_key] = {
|
|
578
|
+
eid: GameState.model_validate(state)
|
|
579
|
+
for eid, state in events.items()
|
|
580
|
+
if isinstance(state, dict)
|
|
581
|
+
}
|
|
582
|
+
return result
|
|
583
|
+
|
|
584
|
+
|
|
545
585
|
class _SportsResource:
|
|
546
586
|
def __init__(self, client: SharpAPI):
|
|
547
587
|
self._client = client
|
|
@@ -781,6 +821,15 @@ class _StreamResource:
|
|
|
781
821
|
"market": market,
|
|
782
822
|
})
|
|
783
823
|
|
|
824
|
+
def gamestate(self) -> EventStream:
|
|
825
|
+
"""Stream live game state updates (scores, period, clock).
|
|
826
|
+
|
|
827
|
+
Emits ``gamestate:snapshot`` (initial dump on connect) and
|
|
828
|
+
``gamestate:update`` / ``gamestate:removed`` events. Requires the
|
|
829
|
+
Game State add-on or Enterprise tier.
|
|
830
|
+
"""
|
|
831
|
+
return self._build_stream("/stream/gamestate")
|
|
832
|
+
|
|
784
833
|
|
|
785
834
|
# =============================================================================
|
|
786
835
|
# Helpers
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"""SharpAPI exceptions and canonical error-code registry.
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
|
|
5
|
-
sync when new codes are added upstream.
|
|
3
|
+
The codes here mirror the canonical set the SharpAPI server emits.
|
|
4
|
+
Keep this file in sync when new codes are added upstream.
|
|
6
5
|
"""
|
|
7
6
|
|
|
8
7
|
from __future__ import annotations
|
|
@@ -65,9 +64,10 @@ class StreamError(SharpAPIError):
|
|
|
65
64
|
# =============================================================================
|
|
66
65
|
# Canonical error-code registry
|
|
67
66
|
#
|
|
68
|
-
# Mirrors
|
|
69
|
-
# add it here too and update the matching description. Each
|
|
70
|
-
# Python exception class that ``handle_errors`` (in
|
|
67
|
+
# Mirrors the canonical SharpAPI server error-code set. When upstream adds
|
|
68
|
+
# a new code, add it here too and update the matching description. Each
|
|
69
|
+
# code maps to the Python exception class that ``handle_errors`` (in
|
|
70
|
+
# ``_base.py``) raises for it.
|
|
71
71
|
# =============================================================================
|
|
72
72
|
|
|
73
73
|
# HTTP error codes — emitted via REST handlers (httputil.WriteJSONError).
|
|
@@ -82,6 +82,8 @@ INVALID_TOKEN = "invalid_token"
|
|
|
82
82
|
METHOD_NOT_ALLOWED = "method_not_allowed"
|
|
83
83
|
MISSING_API_KEY = "missing_api_key"
|
|
84
84
|
NOT_FOUND = "not_found"
|
|
85
|
+
NOT_READY = "not_ready"
|
|
86
|
+
OFFSET_TOO_LARGE = "offset_too_large"
|
|
85
87
|
RATE_LIMITED = "rate_limited"
|
|
86
88
|
SERVICE_UNAVAILABLE = "service_unavailable"
|
|
87
89
|
TIER_RESTRICTED = "tier_restricted"
|
|
@@ -113,6 +115,11 @@ ERROR_CODE_DESCRIPTIONS: dict[str, str] = {
|
|
|
113
115
|
METHOD_NOT_ALLOWED: "HTTP method not allowed on this endpoint.",
|
|
114
116
|
MISSING_API_KEY: "No API key provided.",
|
|
115
117
|
NOT_FOUND: "Resource not found.",
|
|
118
|
+
NOT_READY: "A required backing store is not yet ready to serve this request; retry shortly.",
|
|
119
|
+
OFFSET_TOO_LARGE: (
|
|
120
|
+
"offset exceeds the per-endpoint maximum; "
|
|
121
|
+
"use cursor-based pagination or advance `since`."
|
|
122
|
+
),
|
|
116
123
|
RATE_LIMITED: "Rate limit exceeded; see Retry-After header.",
|
|
117
124
|
SERVICE_UNAVAILABLE: "Service is temporarily unavailable.",
|
|
118
125
|
TIER_RESTRICTED: "Current subscription tier does not include this feature.",
|
|
@@ -148,6 +155,7 @@ ERROR_CODE_TO_EXCEPTION: dict[str, type[SharpAPIError]] = {
|
|
|
148
155
|
TOO_MANY_STREAMS: RateLimitedError,
|
|
149
156
|
# Validation
|
|
150
157
|
VALIDATION_ERROR: ValidationError,
|
|
158
|
+
OFFSET_TOO_LARGE: ValidationError,
|
|
151
159
|
# Streaming frames
|
|
152
160
|
WS_ALREADY_AUTHENTICATED: StreamError,
|
|
153
161
|
WS_INVALID_MESSAGE: StreamError,
|
|
@@ -160,15 +168,16 @@ ERROR_CODE_TO_EXCEPTION: dict[str, type[SharpAPIError]] = {
|
|
|
160
168
|
INTERNAL_ERROR: SharpAPIError,
|
|
161
169
|
METHOD_NOT_ALLOWED: SharpAPIError,
|
|
162
170
|
NOT_FOUND: SharpAPIError,
|
|
171
|
+
NOT_READY: SharpAPIError,
|
|
163
172
|
SERVICE_UNAVAILABLE: SharpAPIError,
|
|
164
173
|
UNKNOWN_ENDPOINT: SharpAPIError,
|
|
165
174
|
UPSTREAM_ERROR: SharpAPIError,
|
|
166
175
|
}
|
|
167
176
|
|
|
168
|
-
# Deprecated aliases. ``bad_request`` and ``invalid_request`` were both
|
|
169
|
-
# into ``validation_error``
|
|
170
|
-
# responses (or user code still checking these strings) resolve
|
|
171
|
-
#
|
|
177
|
+
# Deprecated aliases. ``bad_request`` and ``invalid_request`` were both
|
|
178
|
+
# collapsed into ``validation_error`` server-side. Kept here so that older
|
|
179
|
+
# API responses (or user code still checking these strings) resolve
|
|
180
|
+
# correctly. Will be removed after 2026-10.
|
|
172
181
|
DEPRECATED_CODE_ALIASES: dict[str, str] = {
|
|
173
182
|
"bad_request": VALIDATION_ERROR,
|
|
174
183
|
"invalid_request": VALIDATION_ERROR,
|