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.
- spoken_alpha-0.1.0/.gitignore +11 -0
- spoken_alpha-0.1.0/PKG-INFO +119 -0
- spoken_alpha-0.1.0/README.md +88 -0
- spoken_alpha-0.1.0/pyproject.toml +57 -0
- spoken_alpha-0.1.0/src/spoken_alpha/__init__.py +101 -0
- spoken_alpha-0.1.0/src/spoken_alpha/_client.py +142 -0
- spoken_alpha-0.1.0/src/spoken_alpha/_exceptions.py +117 -0
- spoken_alpha-0.1.0/src/spoken_alpha/_http.py +312 -0
- spoken_alpha-0.1.0/src/spoken_alpha/_models.py +311 -0
- spoken_alpha-0.1.0/src/spoken_alpha/_pagination.py +106 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/__init__.py +24 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/analysts.py +38 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/calls.py +49 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/companies.py +153 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/executives.py +50 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/language_patterns.py +145 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/text.py +61 -0
- spoken_alpha-0.1.0/src/spoken_alpha/resources/transcripts.py +62 -0
- spoken_alpha-0.1.0/tests/__init__.py +0 -0
- spoken_alpha-0.1.0/tests/conftest.py +19 -0
- spoken_alpha-0.1.0/tests/test_async_client.py +117 -0
- spoken_alpha-0.1.0/tests/test_auth.py +107 -0
- spoken_alpha-0.1.0/tests/test_calls.py +108 -0
- spoken_alpha-0.1.0/tests/test_companies.py +178 -0
- spoken_alpha-0.1.0/tests/test_executives_analysts.py +80 -0
- spoken_alpha-0.1.0/tests/test_language_patterns.py +98 -0
- spoken_alpha-0.1.0/tests/test_retry.py +114 -0
- spoken_alpha-0.1.0/tests/test_text.py +66 -0
- spoken_alpha-0.1.0/tests/test_transcripts.py +86 -0
|
@@ -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
|
+
[](https://pypi.org/project/spoken-alpha/)
|
|
35
|
+
[](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
|
+
[](https://pypi.org/project/spoken-alpha/)
|
|
4
|
+
[](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."""
|