spoken-alpha 0.1.0__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.
Files changed (29) hide show
  1. spoken_alpha-0.1.0/.gitignore +11 -0
  2. spoken_alpha-0.1.0/PKG-INFO +119 -0
  3. spoken_alpha-0.1.0/README.md +88 -0
  4. spoken_alpha-0.1.0/pyproject.toml +57 -0
  5. spoken_alpha-0.1.0/src/spoken_alpha/__init__.py +101 -0
  6. spoken_alpha-0.1.0/src/spoken_alpha/_client.py +142 -0
  7. spoken_alpha-0.1.0/src/spoken_alpha/_exceptions.py +117 -0
  8. spoken_alpha-0.1.0/src/spoken_alpha/_http.py +312 -0
  9. spoken_alpha-0.1.0/src/spoken_alpha/_models.py +311 -0
  10. spoken_alpha-0.1.0/src/spoken_alpha/_pagination.py +106 -0
  11. spoken_alpha-0.1.0/src/spoken_alpha/resources/__init__.py +24 -0
  12. spoken_alpha-0.1.0/src/spoken_alpha/resources/analysts.py +38 -0
  13. spoken_alpha-0.1.0/src/spoken_alpha/resources/calls.py +49 -0
  14. spoken_alpha-0.1.0/src/spoken_alpha/resources/companies.py +153 -0
  15. spoken_alpha-0.1.0/src/spoken_alpha/resources/executives.py +50 -0
  16. spoken_alpha-0.1.0/src/spoken_alpha/resources/language_patterns.py +145 -0
  17. spoken_alpha-0.1.0/src/spoken_alpha/resources/text.py +61 -0
  18. spoken_alpha-0.1.0/src/spoken_alpha/resources/transcripts.py +62 -0
  19. spoken_alpha-0.1.0/tests/__init__.py +0 -0
  20. spoken_alpha-0.1.0/tests/conftest.py +19 -0
  21. spoken_alpha-0.1.0/tests/test_async_client.py +117 -0
  22. spoken_alpha-0.1.0/tests/test_auth.py +107 -0
  23. spoken_alpha-0.1.0/tests/test_calls.py +108 -0
  24. spoken_alpha-0.1.0/tests/test_companies.py +178 -0
  25. spoken_alpha-0.1.0/tests/test_executives_analysts.py +80 -0
  26. spoken_alpha-0.1.0/tests/test_language_patterns.py +98 -0
  27. spoken_alpha-0.1.0/tests/test_retry.py +114 -0
  28. spoken_alpha-0.1.0/tests/test_text.py +66 -0
  29. spoken_alpha-0.1.0/tests/test_transcripts.py +86 -0
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ *.egg
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: spoken-alpha
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Spoken Alpha API — structured earnings-call data for AI agents.
5
+ Project-URL: Homepage, https://spokenalpha.com
6
+ Project-URL: Documentation, https://api.spokenalpha.com/v1/docs
7
+ Project-URL: Repository, https://github.com/Spoken-Alpha/earnings-signal
8
+ Project-URL: Bug Tracker, https://github.com/Spoken-Alpha/earnings-signal/issues
9
+ Author-email: Spoken Alpha <hello@spokenalpha.com>
10
+ License: MIT
11
+ Keywords: api,earnings,finance,nlp,sdk
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: httpx<1,>=0.27
24
+ Requires-Dist: pydantic<3,>=2.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: respx>=0.21; extra == 'dev'
29
+ Requires-Dist: ruff>=0.4; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # spoken-alpha · Python SDK
33
+
34
+ [![PyPI](https://img.shields.io/pypi/v/spoken-alpha)](https://pypi.org/project/spoken-alpha/)
35
+ [![Python](https://img.shields.io/pypi/pyversions/spoken-alpha)](https://pypi.org/project/spoken-alpha/)
36
+
37
+ Structured earnings-call data for AI agents.
38
+
39
+ Full API reference: <https://api.spokenalpha.com/v1/docs>
40
+
41
+ > **Note:** The PyPI package is `spoken-alpha` (hyphen). The npm scope uses an underscore (`@spoken_alpha/sdk`) — see `sdks/README.md` for the reason.
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install spoken-alpha
47
+ ```
48
+
49
+ ## Quick start
50
+
51
+ ```python
52
+ from spoken_alpha import Client
53
+
54
+ with Client(api_key="sa-...") as client:
55
+ ir = client.companies.get_ir_page("AAPL")
56
+ print(ir.ir_page.confidence)
57
+
58
+ for call in client.companies.list_earnings_calls("AAPL").iterate_all():
59
+ print(call.call_date, call.fiscal_quarter)
60
+
61
+ summary = client.calls.get_summary("call_AAPL_2026Q2_earnings")
62
+ print(summary.summary)
63
+ ```
64
+
65
+ The API key can also be set via the `SPOKEN_ALPHA_API_KEY` environment variable — `Client()` picks it up automatically.
66
+
67
+ ## Async
68
+
69
+ ```python
70
+ from spoken_alpha import AsyncClient
71
+
72
+ async with AsyncClient() as client:
73
+ results = await client.language_patterns.search("supply chain normalization")
74
+ for r in results.results:
75
+ print(r.ticker, r.snippet)
76
+ ```
77
+
78
+ ## Error handling
79
+
80
+ ```python
81
+ from spoken_alpha import Client, SpokenAlphaNotFoundError, SpokenAlphaRateLimitError
82
+
83
+ with Client() as client:
84
+ try:
85
+ ir = client.companies.get_ir_page("ZZZZ")
86
+ except SpokenAlphaNotFoundError:
87
+ print("ticker not found")
88
+ except SpokenAlphaRateLimitError as e:
89
+ print(f"rate limited; resets at {e.reset_at}")
90
+ ```
91
+
92
+ All exceptions inherit from `SpokenAlphaAPIError`. Subclasses:
93
+
94
+ | Class | HTTP |
95
+ |---|---|
96
+ | `SpokenAlphaAuthError` | 401 |
97
+ | `SpokenAlphaForbiddenError` | 403 |
98
+ | `SpokenAlphaNotFoundError` | 404 |
99
+ | `SpokenAlphaValidationError` | 422 |
100
+ | `SpokenAlphaRateLimitError` | 429 |
101
+ | `SpokenAlphaScaffoldedError` | 503 (planned endpoint) |
102
+ | `SpokenAlphaServerError` | 5xx |
103
+ | `SpokenAlphaTimeoutError` | timeout |
104
+ | `SpokenAlphaNetworkError` | network |
105
+
106
+ ## Configuration
107
+
108
+ ```python
109
+ Client(
110
+ api_key="sa-...", # default: SPOKEN_ALPHA_API_KEY env var
111
+ base_url="https://...", # default: https://api.spokenalpha.com/v1
112
+ timeout=30.0, # seconds, default 30
113
+ max_retries=2, # auto-retry on 5xx, default 2
114
+ )
115
+ ```
116
+
117
+ ## License
118
+
119
+ MIT
@@ -0,0 +1,88 @@
1
+ # spoken-alpha · Python SDK
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/spoken-alpha)](https://pypi.org/project/spoken-alpha/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/spoken-alpha)](https://pypi.org/project/spoken-alpha/)
5
+
6
+ Structured earnings-call data for AI agents.
7
+
8
+ Full API reference: <https://api.spokenalpha.com/v1/docs>
9
+
10
+ > **Note:** The PyPI package is `spoken-alpha` (hyphen). The npm scope uses an underscore (`@spoken_alpha/sdk`) — see `sdks/README.md` for the reason.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install spoken-alpha
16
+ ```
17
+
18
+ ## Quick start
19
+
20
+ ```python
21
+ from spoken_alpha import Client
22
+
23
+ with Client(api_key="sa-...") as client:
24
+ ir = client.companies.get_ir_page("AAPL")
25
+ print(ir.ir_page.confidence)
26
+
27
+ for call in client.companies.list_earnings_calls("AAPL").iterate_all():
28
+ print(call.call_date, call.fiscal_quarter)
29
+
30
+ summary = client.calls.get_summary("call_AAPL_2026Q2_earnings")
31
+ print(summary.summary)
32
+ ```
33
+
34
+ The API key can also be set via the `SPOKEN_ALPHA_API_KEY` environment variable — `Client()` picks it up automatically.
35
+
36
+ ## Async
37
+
38
+ ```python
39
+ from spoken_alpha import AsyncClient
40
+
41
+ async with AsyncClient() as client:
42
+ results = await client.language_patterns.search("supply chain normalization")
43
+ for r in results.results:
44
+ print(r.ticker, r.snippet)
45
+ ```
46
+
47
+ ## Error handling
48
+
49
+ ```python
50
+ from spoken_alpha import Client, SpokenAlphaNotFoundError, SpokenAlphaRateLimitError
51
+
52
+ with Client() as client:
53
+ try:
54
+ ir = client.companies.get_ir_page("ZZZZ")
55
+ except SpokenAlphaNotFoundError:
56
+ print("ticker not found")
57
+ except SpokenAlphaRateLimitError as e:
58
+ print(f"rate limited; resets at {e.reset_at}")
59
+ ```
60
+
61
+ All exceptions inherit from `SpokenAlphaAPIError`. Subclasses:
62
+
63
+ | Class | HTTP |
64
+ |---|---|
65
+ | `SpokenAlphaAuthError` | 401 |
66
+ | `SpokenAlphaForbiddenError` | 403 |
67
+ | `SpokenAlphaNotFoundError` | 404 |
68
+ | `SpokenAlphaValidationError` | 422 |
69
+ | `SpokenAlphaRateLimitError` | 429 |
70
+ | `SpokenAlphaScaffoldedError` | 503 (planned endpoint) |
71
+ | `SpokenAlphaServerError` | 5xx |
72
+ | `SpokenAlphaTimeoutError` | timeout |
73
+ | `SpokenAlphaNetworkError` | network |
74
+
75
+ ## Configuration
76
+
77
+ ```python
78
+ Client(
79
+ api_key="sa-...", # default: SPOKEN_ALPHA_API_KEY env var
80
+ base_url="https://...", # default: https://api.spokenalpha.com/v1
81
+ timeout=30.0, # seconds, default 30
82
+ max_retries=2, # auto-retry on 5xx, default 2
83
+ )
84
+ ```
85
+
86
+ ## License
87
+
88
+ MIT
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "spoken-alpha"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the Spoken Alpha API — structured earnings-call data for AI agents."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Spoken Alpha", email = "hello@spokenalpha.com" }]
13
+ keywords = ["earnings", "finance", "nlp", "api", "sdk"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Financial and Insurance Industry",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = [
27
+ "httpx>=0.27,<1",
28
+ "pydantic>=2.0,<3",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest>=8.0",
34
+ "pytest-asyncio>=0.23",
35
+ "respx>=0.21",
36
+ "ruff>=0.4",
37
+ ]
38
+
39
+ [project.urls]
40
+ Homepage = "https://spokenalpha.com"
41
+ Documentation = "https://api.spokenalpha.com/v1/docs"
42
+ Repository = "https://github.com/Spoken-Alpha/earnings-signal"
43
+ "Bug Tracker" = "https://github.com/Spoken-Alpha/earnings-signal/issues"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/spoken_alpha"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
50
+ asyncio_mode = "auto"
51
+
52
+ [tool.ruff]
53
+ line-length = 100
54
+ target-version = "py310"
55
+
56
+ [tool.ruff.lint]
57
+ select = ["E", "F", "I", "UP"]
@@ -0,0 +1,101 @@
1
+ """
2
+ Spoken Alpha Python SDK.
3
+
4
+ Usage::
5
+
6
+ from spoken_alpha import Client
7
+
8
+ with Client(api_key="sa-...") as client:
9
+ ir = client.companies.get_ir_page("AAPL")
10
+ print(ir.ir_page.url)
11
+
12
+ # Or read key from SPOKEN_ALPHA_API_KEY environment variable:
13
+ with Client() as client:
14
+ summary = client.calls.get_summary("call_AAPL_2026Q2_earnings")
15
+
16
+ Async::
17
+
18
+ from spoken_alpha import AsyncClient
19
+
20
+ async with AsyncClient() as client:
21
+ results = await client.language_patterns.search("supply chain normalization")
22
+ """
23
+
24
+ from ._client import AsyncClient, Client
25
+ from ._exceptions import (
26
+ SpokenAlphaAPIError,
27
+ SpokenAlphaAuthError,
28
+ SpokenAlphaForbiddenError,
29
+ SpokenAlphaNetworkError,
30
+ SpokenAlphaNotFoundError,
31
+ SpokenAlphaRateLimitError,
32
+ SpokenAlphaScaffoldedError,
33
+ SpokenAlphaServerError,
34
+ SpokenAlphaTimeoutError,
35
+ SpokenAlphaValidationError,
36
+ )
37
+ from ._models import (
38
+ Analyst,
39
+ CallAnalysis,
40
+ CallScore,
41
+ CallSummary,
42
+ EarningsCall,
43
+ EarningsCallListResponse,
44
+ Executive,
45
+ ExecutiveTrackRecord,
46
+ IRPageResponse,
47
+ LanguagePatternAggregate,
48
+ LanguagePatternOutcome,
49
+ LanguagePatternResult,
50
+ LanguagePatternSearchResponse,
51
+ LanguagePatternWarning,
52
+ Pagination,
53
+ ParsedTranscript,
54
+ PressRelease,
55
+ PressReleaseListResponse,
56
+ TextScoreResponse,
57
+ TranscriptResponse,
58
+ )
59
+ from ._pagination import Page, PageIterator
60
+
61
+ __version__ = "0.1.0"
62
+
63
+ __all__ = [
64
+ "Client",
65
+ "AsyncClient",
66
+ # Exceptions
67
+ "SpokenAlphaAPIError",
68
+ "SpokenAlphaAuthError",
69
+ "SpokenAlphaForbiddenError",
70
+ "SpokenAlphaNetworkError",
71
+ "SpokenAlphaNotFoundError",
72
+ "SpokenAlphaRateLimitError",
73
+ "SpokenAlphaScaffoldedError",
74
+ "SpokenAlphaServerError",
75
+ "SpokenAlphaTimeoutError",
76
+ "SpokenAlphaValidationError",
77
+ # Models
78
+ "Analyst",
79
+ "CallAnalysis",
80
+ "CallScore",
81
+ "CallSummary",
82
+ "EarningsCall",
83
+ "EarningsCallListResponse",
84
+ "Executive",
85
+ "ExecutiveTrackRecord",
86
+ "IRPageResponse",
87
+ "LanguagePatternAggregate",
88
+ "LanguagePatternOutcome",
89
+ "LanguagePatternResult",
90
+ "LanguagePatternSearchResponse",
91
+ "LanguagePatternWarning",
92
+ "Pagination",
93
+ "ParsedTranscript",
94
+ "PressRelease",
95
+ "PressReleaseListResponse",
96
+ "TextScoreResponse",
97
+ "TranscriptResponse",
98
+ # Pagination
99
+ "Page",
100
+ "PageIterator",
101
+ ]
@@ -0,0 +1,142 @@
1
+ """
2
+ Main client entry point — sync and async variants.
3
+
4
+ Usage::
5
+
6
+ from spoken_alpha import Client
7
+
8
+ client = Client(api_key="sa-...")
9
+ # or
10
+ client = Client() # reads SPOKEN_ALPHA_API_KEY from environment
11
+
12
+ summary = client.calls.get_summary("call_AAPL_2026Q2_earnings")
13
+ print(summary.summary)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import os
19
+ from typing import Any
20
+
21
+ from ._exceptions import SpokenAlphaAuthError
22
+ from ._http import DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT, AsyncTransport, Transport
23
+ from .resources import (
24
+ AnalystsResource,
25
+ AsyncAnalystsResource,
26
+ AsyncCallsResource,
27
+ AsyncCompaniesResource,
28
+ AsyncExecutivesResource,
29
+ AsyncLanguagePatternsResource,
30
+ AsyncTextResource,
31
+ AsyncTranscriptsResource,
32
+ CallsResource,
33
+ CompaniesResource,
34
+ ExecutivesResource,
35
+ LanguagePatternsResource,
36
+ TextResource,
37
+ TranscriptsResource,
38
+ )
39
+
40
+
41
+ def _resolve_api_key(api_key: str | None) -> str:
42
+ if api_key is not None:
43
+ return api_key
44
+ env = os.environ.get("SPOKEN_ALPHA_API_KEY", "").strip()
45
+ if env:
46
+ return env
47
+ raise SpokenAlphaAuthError(
48
+ "No API key provided. Pass api_key= or set SPOKEN_ALPHA_API_KEY. "
49
+ "Get a key at https://spokenalpha.com/pricing."
50
+ )
51
+
52
+
53
+ class Client:
54
+ """Synchronous Spoken Alpha API client.
55
+
56
+ Args:
57
+ api_key: Bearer token (``sa-...``). Falls back to ``SPOKEN_ALPHA_API_KEY``.
58
+ base_url: API root. Default: ``https://api.spokenalpha.com/v1``.
59
+ timeout: Per-request timeout in seconds. Default: 30.
60
+ max_retries: Max automatic retries for transient 5xx errors. Default: 2.
61
+ http_client: Inject an existing ``httpx.Client`` (for testing).
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ *,
67
+ api_key: str | None = None,
68
+ base_url: str = DEFAULT_BASE_URL,
69
+ timeout: float = DEFAULT_TIMEOUT,
70
+ max_retries: int = DEFAULT_MAX_RETRIES,
71
+ http_client: Any = None,
72
+ ) -> None:
73
+ resolved_key = _resolve_api_key(api_key)
74
+ self._transport = Transport(
75
+ api_key=resolved_key,
76
+ base_url=base_url,
77
+ timeout=timeout,
78
+ max_retries=max_retries,
79
+ http_client=http_client,
80
+ )
81
+ self.companies = CompaniesResource(self._transport)
82
+ self.calls = CallsResource(self._transport)
83
+ self.transcripts = TranscriptsResource(self._transport)
84
+ self.executives = ExecutivesResource(self._transport)
85
+ self.analysts = AnalystsResource(self._transport)
86
+ self.language_patterns = LanguagePatternsResource(self._transport)
87
+ self.text = TextResource(self._transport)
88
+
89
+ def close(self) -> None:
90
+ self._transport.close()
91
+
92
+ def __enter__(self) -> Client:
93
+ return self
94
+
95
+ def __exit__(self, *_: Any) -> None:
96
+ self.close()
97
+
98
+
99
+ class AsyncClient:
100
+ """Async Spoken Alpha API client.
101
+
102
+ Args:
103
+ api_key: Bearer token. Falls back to ``SPOKEN_ALPHA_API_KEY``.
104
+ base_url: API root. Default: ``https://api.spokenalpha.com/v1``.
105
+ timeout: Per-request timeout in seconds. Default: 30.
106
+ max_retries: Max automatic retries for transient 5xx errors. Default: 2.
107
+ http_client: Inject an existing ``httpx.AsyncClient`` (for testing).
108
+ """
109
+
110
+ def __init__(
111
+ self,
112
+ *,
113
+ api_key: str | None = None,
114
+ base_url: str = DEFAULT_BASE_URL,
115
+ timeout: float = DEFAULT_TIMEOUT,
116
+ max_retries: int = DEFAULT_MAX_RETRIES,
117
+ http_client: Any = None,
118
+ ) -> None:
119
+ resolved_key = _resolve_api_key(api_key)
120
+ self._transport = AsyncTransport(
121
+ api_key=resolved_key,
122
+ base_url=base_url,
123
+ timeout=timeout,
124
+ max_retries=max_retries,
125
+ http_client=http_client,
126
+ )
127
+ self.companies = AsyncCompaniesResource(self._transport)
128
+ self.calls = AsyncCallsResource(self._transport)
129
+ self.transcripts = AsyncTranscriptsResource(self._transport)
130
+ self.executives = AsyncExecutivesResource(self._transport)
131
+ self.analysts = AsyncAnalystsResource(self._transport)
132
+ self.language_patterns = AsyncLanguagePatternsResource(self._transport)
133
+ self.text = AsyncTextResource(self._transport)
134
+
135
+ async def aclose(self) -> None:
136
+ await self._transport.aclose()
137
+
138
+ async def __aenter__(self) -> AsyncClient:
139
+ return self
140
+
141
+ async def __aexit__(self, *_: Any) -> None:
142
+ await self.aclose()
@@ -0,0 +1,117 @@
1
+ """
2
+ Typed exceptions for every HTTP error the Spoken Alpha API can return.
3
+
4
+ Catch ``SpokenAlphaAPIError`` for any API error, or subclasses for
5
+ specific status codes::
6
+
7
+ from spoken_alpha import Client, SpokenAlphaNotFoundError, SpokenAlphaRateLimitError
8
+
9
+ client = Client(api_key="sa-...")
10
+ try:
11
+ ir = client.companies.get_ir_page("ZZZZ")
12
+ except SpokenAlphaNotFoundError:
13
+ print("ticker not found")
14
+ except SpokenAlphaRateLimitError as e:
15
+ print(f"quota exceeded; resets at {e.reset_at}")
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from typing import Any
21
+
22
+
23
+ class SpokenAlphaAPIError(Exception):
24
+ """Base class for all Spoken Alpha API errors.
25
+
26
+ Attributes:
27
+ code: Machine-readable error code from the API (e.g. ``ticker_not_found``).
28
+ status_code: HTTP status code.
29
+ request_id: Opaque request ID from ``X-Request-Id`` response header.
30
+ documentation_url: URL with more detail on the error, when present.
31
+ extra: Any additional fields from the API error envelope.
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ message: str,
37
+ *,
38
+ code: str | None = None,
39
+ status_code: int | None = None,
40
+ request_id: str | None = None,
41
+ documentation_url: str | None = None,
42
+ extra: dict[str, Any] | None = None,
43
+ ) -> None:
44
+ super().__init__(message)
45
+ self.code = code
46
+ self.status_code = status_code
47
+ self.request_id = request_id
48
+ self.documentation_url = documentation_url
49
+ self.extra = extra or {}
50
+
51
+
52
+ class SpokenAlphaAuthError(SpokenAlphaAPIError):
53
+ """401 — missing or invalid API key.
54
+
55
+ Ensure ``SPOKEN_ALPHA_API_KEY`` is set or pass ``api_key`` to the client.
56
+ Get a key at https://spokenalpha.com/pricing (private beta; email hello@spokenalpha.com).
57
+ """
58
+
59
+
60
+ class SpokenAlphaForbiddenError(SpokenAlphaAPIError):
61
+ """403 — the caller's subscription tier does not allow this endpoint or field.
62
+
63
+ Upgrade at https://spokenalpha.com/pricing.
64
+ """
65
+
66
+
67
+ class SpokenAlphaNotFoundError(SpokenAlphaAPIError):
68
+ """404 — resource not found.
69
+
70
+ Could be ``ticker_not_found``, ``ir_page_not_found``, or ``call_not_found``.
71
+ For transcripts, 404 is also returned when the call exists but redistribution
72
+ is not permitted for that source.
73
+ """
74
+
75
+
76
+ class SpokenAlphaValidationError(SpokenAlphaAPIError):
77
+ """422 — invalid request (malformed date, query too short, etc.)."""
78
+
79
+
80
+ class SpokenAlphaRateLimitError(SpokenAlphaAPIError):
81
+ """429 — rate limit or monthly quota exceeded.
82
+
83
+ Attributes:
84
+ reset_at: Unix timestamp (seconds) when the rate-limit window resets,
85
+ parsed from ``X-RateLimit-Reset``. None if the header was absent.
86
+ """
87
+
88
+ def __init__(self, *args: Any, reset_at: int | None = None, **kwargs: Any) -> None:
89
+ super().__init__(*args, **kwargs)
90
+ self.reset_at = reset_at
91
+
92
+
93
+ class SpokenAlphaScaffoldedError(SpokenAlphaAPIError):
94
+ """503 with code ``data_not_yet_available``.
95
+
96
+ The endpoint is intentionally scaffolded — clients can integrate now
97
+ against the locked contract and the data will appear on ``planned_launch``.
98
+
99
+ Attributes:
100
+ planned_launch: Human-readable planned launch date (e.g. ``"Q3 2026"``).
101
+ """
102
+
103
+ def __init__(self, *args: Any, planned_launch: str | None = None, **kwargs: Any) -> None:
104
+ super().__init__(*args, **kwargs)
105
+ self.planned_launch = planned_launch
106
+
107
+
108
+ class SpokenAlphaServerError(SpokenAlphaAPIError):
109
+ """5xx other than 503-scaffolded — unexpected server error."""
110
+
111
+
112
+ class SpokenAlphaTimeoutError(SpokenAlphaAPIError):
113
+ """The request timed out before the server responded."""
114
+
115
+
116
+ class SpokenAlphaNetworkError(SpokenAlphaAPIError):
117
+ """A network-level error (DNS, TCP, TLS) before the server was reached."""