clous 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.
clous-0.1.0/.gitignore ADDED
@@ -0,0 +1,65 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ .Python
9
+ build/
10
+ develop-eggs/
11
+ dist/
12
+ downloads/
13
+ eggs/
14
+ .eggs/
15
+ lib/
16
+ lib64/
17
+ parts/
18
+ sdist/
19
+ var/
20
+ wheels/
21
+ share/python-wheels/
22
+ *.egg-info/
23
+ .installed.cfg
24
+ *.egg
25
+ MANIFEST
26
+
27
+ # Unit test / coverage reports
28
+ htmlcov/
29
+ .tox/
30
+ .nox/
31
+ .coverage
32
+ .coverage.*
33
+ .cache
34
+ nosetests.xml
35
+ coverage.xml
36
+ *.cover
37
+ *.py,cover
38
+ .hypothesis/
39
+ .pytest_cache/
40
+
41
+ # Environments
42
+ .env
43
+ .venv
44
+ env/
45
+ venv/
46
+ ENV/
47
+ env.bak/
48
+ venv.bak/
49
+
50
+ # Type checkers / linters
51
+ .mypy_cache/
52
+ .dmypy.json
53
+ dmypy.json
54
+ .ruff_cache/
55
+ .pyre/
56
+ .pytype/
57
+
58
+ # IDEs
59
+ .idea/
60
+ .vscode/
61
+ *.swp
62
+
63
+ # OS
64
+ .DS_Store
65
+ Thumbs.db
clous-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Clous
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.
clous-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,273 @@
1
+ Metadata-Version: 2.4
2
+ Name: clous
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the Clous SEC/EDGAR API — entity-resolved filings, insider & institutional ownership, financials, events, and monitors.
5
+ Project-URL: Homepage, https://clous.ai
6
+ Project-URL: Documentation, https://docs.clous.ai
7
+ Project-URL: Repository, https://github.com/clousai/clous-python
8
+ Project-URL: Issues, https://github.com/clousai/clous-python/issues
9
+ Author-email: Clous <support@clous.ai>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: 13f,clous,edgar,filings,finance,insider,sdk,sec,xbrl
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Financial and Insurance Industry
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Office/Business :: Financial
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.9
28
+ Requires-Dist: httpx<1,>=0.23
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7; extra == 'dev'
31
+ Requires-Dist: respx>=0.20; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Clous Python SDK
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/clous.svg)](https://pypi.org/project/clous/)
37
+ [![Python](https://img.shields.io/pypi/pyversions/clous.svg)](https://pypi.org/project/clous/)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
39
+
40
+ The official Python client for the [**Clous**](https://clous.ai) SEC/EDGAR API — entity-resolved
41
+ filings, insider & institutional ownership, structured XBRL financials, a typed business-events feed,
42
+ grounded Q&A, and standing monitors with webhooks.
43
+
44
+ ```bash
45
+ pip install clous
46
+ ```
47
+
48
+ Requires Python 3.9+. The only runtime dependency is [`httpx`](https://www.python-httpx.org/).
49
+
50
+ ## Quickstart
51
+
52
+ ```python
53
+ from clous import Clous
54
+
55
+ client = Clous() # reads CLOUS_API_KEY from the environment
56
+
57
+ # Search the EDGAR filing index
58
+ page = client.filings.search(form_type="8-K", limit=10)
59
+ for filing in page:
60
+ print(filing["accession"], filing.get("company_name"))
61
+
62
+ # Structured XBRL financials for one company
63
+ revenue = client.financials.get("0000320193", concept="Revenues")
64
+
65
+ # Grounded Q&A over filings
66
+ answer = client.answer("What was Apple's most recent annual revenue?", ticker="AAPL")
67
+ print(answer["answer"])
68
+ ```
69
+
70
+ ## Authentication
71
+
72
+ Pass your key explicitly or set the `CLOUS_API_KEY` environment variable:
73
+
74
+ ```python
75
+ client = Clous(api_key="sk_live_...")
76
+ # or
77
+ import os; os.environ["CLOUS_API_KEY"] = "sk_live_..."; client = Clous()
78
+ ```
79
+
80
+ The key is sent as `Authorization: Bearer <CLOUS_API_KEY>`. The base URL defaults to
81
+ `https://api.clous.ai` and can be overridden with `base_url=` or the `CLOUS_BASE_URL` env var.
82
+ `/v1/sources` works without a key.
83
+
84
+ ## The response envelope
85
+
86
+ Every endpoint returns a JSON envelope:
87
+
88
+ ```json
89
+ {
90
+ "data": [ ... ],
91
+ "page": { "limit": 25, "next_cursor": "…", "has_more": true },
92
+ "as_of": "2026-06-13T00:00:00Z",
93
+ "source": "edgar",
94
+ "query_echo": { ... },
95
+ "warnings": []
96
+ }
97
+ ```
98
+
99
+ The SDK wraps this in a `Page` object. For the common list case a `Page` behaves like the `data`
100
+ list (iterate, index, `len()`), while the envelope metadata and response headers stay available as
101
+ attributes:
102
+
103
+ ```python
104
+ page = client.events.list(ticker="NVDA", importance="high")
105
+
106
+ for event in page: # iterate over page.data
107
+ print(event["event_type"])
108
+
109
+ first = page[0] # index into page.data
110
+ n = len(page) # number of records on this page
111
+
112
+ page.next_cursor # cursor for the next page (or None)
113
+ page.has_more # bool
114
+ page.as_of # snapshot timestamp
115
+ page.source # upstream source
116
+ page.warnings # list of warnings
117
+ page.raw # the full untouched envelope dict
118
+
119
+ # Response headers
120
+ page.request_id # X-Request-Id
121
+ page.credits_cost # X-Credits-Cost
122
+ page.credits_remaining # X-Credits-Remaining
123
+ ```
124
+
125
+ Single-object endpoints (e.g. `client.account()`, `client.financials.get(...)`) expose the object
126
+ via `page.data`; `len(page)` is then `1`.
127
+
128
+ ## Pagination
129
+
130
+ Use `?cursor=` / `limit=` manually, or let the SDK follow `page.next_cursor` for you with the
131
+ `iterate(...)` helper available on every list resource. It yields individual records across all
132
+ pages and accepts an optional `max_items` cap:
133
+
134
+ ```python
135
+ # Manual
136
+ page = client.filings.search(form_type="8-K", limit=100)
137
+ while page.has_more:
138
+ page = client.filings.search(form_type="8-K", limit=100, cursor=page.next_cursor)
139
+
140
+ # Auto — yields every record, stops after max_items
141
+ for filing in client.filings.iterate(cik="0000320193", form_type="8-K", max_items=500):
142
+ print(filing["accession"])
143
+ ```
144
+
145
+ `limit` must be ≤ 100.
146
+
147
+ ## Token-efficient projections
148
+
149
+ Every list/get method accepts `fields=` (comma-separated dot-paths) or `output_schema=` (a JSON
150
+ Schema dict) to project each record server-side:
151
+
152
+ ```python
153
+ client.filings.search(form_type="10-K", fields="accession,company_name,filed_date")
154
+ client.events.list(ticker="NVDA", output_schema={"type": "object", "properties": {"event_type": {}, "title": {}}})
155
+ ```
156
+
157
+ ## Resources & methods
158
+
159
+ The client groups endpoints by resource. Each `search`/`list` method also has an `iterate(...)`
160
+ auto-paginator.
161
+
162
+ | Resource | Methods |
163
+ | --- | --- |
164
+ | `client.filings` | `search`, `iterate`, `documents`, `extract(item=…)`, `insiders`, `events`, `subsidiaries`, `crowdfunding`, `proxy_votes`, `briefing` |
165
+ | `client.full_text` | `search(q=…)`, `iterate` |
166
+ | `client.entities` | `search` / `resolve`, `iterate` |
167
+ | `client.insider` | `search`, `iterate`, `form144`, `iterate_form144` |
168
+ | `client.ownership` | `search`, `iterate` — 13D/13G |
169
+ | `client.holdings` | `search`, `iterate` — 13F holdings |
170
+ | `client.managers` | `search`, `iterate` — 13F managers |
171
+ | `client.funds` | `holdings`, `providers`, `iterate_holdings`, `iterate_providers` — N-PORT / N-CEN |
172
+ | `client.advisers` | `search`, `iterate`, `get(crd)` — Form ADV |
173
+ | `client.private_funds` / `client.private_fund_stats` | `search`, `iterate` |
174
+ | `client.broker_dealers` / `client.form_crs` / `client.iapd_individuals` | `search`, `iterate` |
175
+ | `client.raises` | `search`, `iterate` — Form D |
176
+ | `client.financials` | `search`, `iterate`, `get(cik, concept=…)` |
177
+ | `client.financial_statements` | `search`, `iterate` |
178
+ | `client.board` / `client.compensation` | `search`, `iterate` |
179
+ | `client.proxy` | `officers`, `iterate_officers` — DEF 14A |
180
+ | `client.enforcement` / `client.litigation` / `client.nt_late` / `client.trading_suspensions` / `client.whistleblower` | `search`, `iterate` |
181
+ | `client.cyber_incidents` | `search`, `iterate` — 8-K Item 1.05 |
182
+ | `client.patents` | `search`, `iterate` — USPTO grants |
183
+ | `client.events` | `list`, `iterate`, `get(event_id)` |
184
+ | `client.monitors` | `list`, `create`, `get`, `update`, `delete` |
185
+ | `client.webhooks` | `list_endpoints`, `create_endpoint`, `list_deliveries` |
186
+ | top-level | `client.account()`, `client.sources()`, `client.answer(q, …)`, `client.briefing(accession)`, `client.chat(messages=…)` |
187
+
188
+ Method parameters mirror the REST endpoints. Any extra keyword arguments are passed straight through
189
+ as query params (or body fields for POST/PATCH), so new API params work without an SDK upgrade. There
190
+ are also raw escape hatches: `client.get(path, params=…)` and `client.post(path, body=…)`.
191
+
192
+ ### Examples
193
+
194
+ ```python
195
+ # Insider transactions (Form 4) — purchases over $1M
196
+ client.insider.search(ticker="TSLA", trans_code="P", min_value_usd=1_000_000)
197
+
198
+ # 13F institutional holdings of a security
199
+ client.holdings.search(cusip="037833100", min_value=5_000_000)
200
+
201
+ # Extract Risk Factors from a 10-K
202
+ client.filings.extract("0000320193-23-000106", item="1A")
203
+
204
+ # AI briefing for a filing
205
+ brief = client.briefing("0000320193-23-000106")
206
+
207
+ # Create a monitor that webhooks on high-importance NVDA events
208
+ ep = client.webhooks.create_endpoint(url="https://example.com/hook")
209
+ mon = client.monitors.create(
210
+ name="NVDA watch", target_type="ticker", target_value="NVDA",
211
+ materiality="high", webhook_endpoint_id=ep.data["id"],
212
+ )
213
+
214
+ # Stream the events feed
215
+ for ev in client.events.iterate(ticker="NVDA", importance="high", max_items=100):
216
+ print(ev["event_type"])
217
+ ```
218
+
219
+ ## OpenAI-compatible endpoint
220
+
221
+ Clous exposes an OpenAI-compatible chat endpoint. Use the built-in helper:
222
+
223
+ ```python
224
+ out = client.chat(messages=[{"role": "user", "content": "Summarize Apple's latest 8-K"}])
225
+ print(out["choices"][0]["message"]["content"])
226
+ ```
227
+
228
+ …or point the official `openai` SDK at Clous directly:
229
+
230
+ ```python
231
+ from openai import OpenAI
232
+
233
+ oai = OpenAI(base_url="https://api.clous.ai/v1", api_key="<CLOUS_API_KEY>")
234
+ resp = oai.chat.completions.create(model="clous", messages=[{"role": "user", "content": "…"}])
235
+ ```
236
+
237
+ The Clous SDK also accepts `base_url="https://api.clous.ai/v1"`; the trailing `/v1` is normalized so
238
+ resource paths still resolve.
239
+
240
+ ## Errors, timeouts, retries
241
+
242
+ Non-2xx responses raise a typed exception carrying `status_code`, `request_id`, `message`, and the
243
+ parsed `body`:
244
+
245
+ ```python
246
+ from clous import APIError, AuthenticationError, RateLimitError, NotFoundError
247
+
248
+ try:
249
+ client.filings.search()
250
+ except AuthenticationError:
251
+ ... # 401
252
+ except RateLimitError as e:
253
+ ... # 429
254
+ except NotFoundError:
255
+ ... # 404
256
+ except APIError as e:
257
+ print(e.status_code, e.request_id, e.message)
258
+ ```
259
+
260
+ The client times out after 30s (configurable via `timeout=`) and retries transient failures
261
+ (429 + 5xx + network errors) up to `max_retries` (default 3) with exponential backoff that honors
262
+ `Retry-After`.
263
+
264
+ ## Development
265
+
266
+ ```bash
267
+ pip install -e ".[dev]"
268
+ pytest -q
269
+ ```
270
+
271
+ ## License
272
+
273
+ [MIT](LICENSE)
clous-0.1.0/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # Clous Python SDK
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/clous.svg)](https://pypi.org/project/clous/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/clous.svg)](https://pypi.org/project/clous/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ The official Python client for the [**Clous**](https://clous.ai) SEC/EDGAR API — entity-resolved
8
+ filings, insider & institutional ownership, structured XBRL financials, a typed business-events feed,
9
+ grounded Q&A, and standing monitors with webhooks.
10
+
11
+ ```bash
12
+ pip install clous
13
+ ```
14
+
15
+ Requires Python 3.9+. The only runtime dependency is [`httpx`](https://www.python-httpx.org/).
16
+
17
+ ## Quickstart
18
+
19
+ ```python
20
+ from clous import Clous
21
+
22
+ client = Clous() # reads CLOUS_API_KEY from the environment
23
+
24
+ # Search the EDGAR filing index
25
+ page = client.filings.search(form_type="8-K", limit=10)
26
+ for filing in page:
27
+ print(filing["accession"], filing.get("company_name"))
28
+
29
+ # Structured XBRL financials for one company
30
+ revenue = client.financials.get("0000320193", concept="Revenues")
31
+
32
+ # Grounded Q&A over filings
33
+ answer = client.answer("What was Apple's most recent annual revenue?", ticker="AAPL")
34
+ print(answer["answer"])
35
+ ```
36
+
37
+ ## Authentication
38
+
39
+ Pass your key explicitly or set the `CLOUS_API_KEY` environment variable:
40
+
41
+ ```python
42
+ client = Clous(api_key="sk_live_...")
43
+ # or
44
+ import os; os.environ["CLOUS_API_KEY"] = "sk_live_..."; client = Clous()
45
+ ```
46
+
47
+ The key is sent as `Authorization: Bearer <CLOUS_API_KEY>`. The base URL defaults to
48
+ `https://api.clous.ai` and can be overridden with `base_url=` or the `CLOUS_BASE_URL` env var.
49
+ `/v1/sources` works without a key.
50
+
51
+ ## The response envelope
52
+
53
+ Every endpoint returns a JSON envelope:
54
+
55
+ ```json
56
+ {
57
+ "data": [ ... ],
58
+ "page": { "limit": 25, "next_cursor": "…", "has_more": true },
59
+ "as_of": "2026-06-13T00:00:00Z",
60
+ "source": "edgar",
61
+ "query_echo": { ... },
62
+ "warnings": []
63
+ }
64
+ ```
65
+
66
+ The SDK wraps this in a `Page` object. For the common list case a `Page` behaves like the `data`
67
+ list (iterate, index, `len()`), while the envelope metadata and response headers stay available as
68
+ attributes:
69
+
70
+ ```python
71
+ page = client.events.list(ticker="NVDA", importance="high")
72
+
73
+ for event in page: # iterate over page.data
74
+ print(event["event_type"])
75
+
76
+ first = page[0] # index into page.data
77
+ n = len(page) # number of records on this page
78
+
79
+ page.next_cursor # cursor for the next page (or None)
80
+ page.has_more # bool
81
+ page.as_of # snapshot timestamp
82
+ page.source # upstream source
83
+ page.warnings # list of warnings
84
+ page.raw # the full untouched envelope dict
85
+
86
+ # Response headers
87
+ page.request_id # X-Request-Id
88
+ page.credits_cost # X-Credits-Cost
89
+ page.credits_remaining # X-Credits-Remaining
90
+ ```
91
+
92
+ Single-object endpoints (e.g. `client.account()`, `client.financials.get(...)`) expose the object
93
+ via `page.data`; `len(page)` is then `1`.
94
+
95
+ ## Pagination
96
+
97
+ Use `?cursor=` / `limit=` manually, or let the SDK follow `page.next_cursor` for you with the
98
+ `iterate(...)` helper available on every list resource. It yields individual records across all
99
+ pages and accepts an optional `max_items` cap:
100
+
101
+ ```python
102
+ # Manual
103
+ page = client.filings.search(form_type="8-K", limit=100)
104
+ while page.has_more:
105
+ page = client.filings.search(form_type="8-K", limit=100, cursor=page.next_cursor)
106
+
107
+ # Auto — yields every record, stops after max_items
108
+ for filing in client.filings.iterate(cik="0000320193", form_type="8-K", max_items=500):
109
+ print(filing["accession"])
110
+ ```
111
+
112
+ `limit` must be ≤ 100.
113
+
114
+ ## Token-efficient projections
115
+
116
+ Every list/get method accepts `fields=` (comma-separated dot-paths) or `output_schema=` (a JSON
117
+ Schema dict) to project each record server-side:
118
+
119
+ ```python
120
+ client.filings.search(form_type="10-K", fields="accession,company_name,filed_date")
121
+ client.events.list(ticker="NVDA", output_schema={"type": "object", "properties": {"event_type": {}, "title": {}}})
122
+ ```
123
+
124
+ ## Resources & methods
125
+
126
+ The client groups endpoints by resource. Each `search`/`list` method also has an `iterate(...)`
127
+ auto-paginator.
128
+
129
+ | Resource | Methods |
130
+ | --- | --- |
131
+ | `client.filings` | `search`, `iterate`, `documents`, `extract(item=…)`, `insiders`, `events`, `subsidiaries`, `crowdfunding`, `proxy_votes`, `briefing` |
132
+ | `client.full_text` | `search(q=…)`, `iterate` |
133
+ | `client.entities` | `search` / `resolve`, `iterate` |
134
+ | `client.insider` | `search`, `iterate`, `form144`, `iterate_form144` |
135
+ | `client.ownership` | `search`, `iterate` — 13D/13G |
136
+ | `client.holdings` | `search`, `iterate` — 13F holdings |
137
+ | `client.managers` | `search`, `iterate` — 13F managers |
138
+ | `client.funds` | `holdings`, `providers`, `iterate_holdings`, `iterate_providers` — N-PORT / N-CEN |
139
+ | `client.advisers` | `search`, `iterate`, `get(crd)` — Form ADV |
140
+ | `client.private_funds` / `client.private_fund_stats` | `search`, `iterate` |
141
+ | `client.broker_dealers` / `client.form_crs` / `client.iapd_individuals` | `search`, `iterate` |
142
+ | `client.raises` | `search`, `iterate` — Form D |
143
+ | `client.financials` | `search`, `iterate`, `get(cik, concept=…)` |
144
+ | `client.financial_statements` | `search`, `iterate` |
145
+ | `client.board` / `client.compensation` | `search`, `iterate` |
146
+ | `client.proxy` | `officers`, `iterate_officers` — DEF 14A |
147
+ | `client.enforcement` / `client.litigation` / `client.nt_late` / `client.trading_suspensions` / `client.whistleblower` | `search`, `iterate` |
148
+ | `client.cyber_incidents` | `search`, `iterate` — 8-K Item 1.05 |
149
+ | `client.patents` | `search`, `iterate` — USPTO grants |
150
+ | `client.events` | `list`, `iterate`, `get(event_id)` |
151
+ | `client.monitors` | `list`, `create`, `get`, `update`, `delete` |
152
+ | `client.webhooks` | `list_endpoints`, `create_endpoint`, `list_deliveries` |
153
+ | top-level | `client.account()`, `client.sources()`, `client.answer(q, …)`, `client.briefing(accession)`, `client.chat(messages=…)` |
154
+
155
+ Method parameters mirror the REST endpoints. Any extra keyword arguments are passed straight through
156
+ as query params (or body fields for POST/PATCH), so new API params work without an SDK upgrade. There
157
+ are also raw escape hatches: `client.get(path, params=…)` and `client.post(path, body=…)`.
158
+
159
+ ### Examples
160
+
161
+ ```python
162
+ # Insider transactions (Form 4) — purchases over $1M
163
+ client.insider.search(ticker="TSLA", trans_code="P", min_value_usd=1_000_000)
164
+
165
+ # 13F institutional holdings of a security
166
+ client.holdings.search(cusip="037833100", min_value=5_000_000)
167
+
168
+ # Extract Risk Factors from a 10-K
169
+ client.filings.extract("0000320193-23-000106", item="1A")
170
+
171
+ # AI briefing for a filing
172
+ brief = client.briefing("0000320193-23-000106")
173
+
174
+ # Create a monitor that webhooks on high-importance NVDA events
175
+ ep = client.webhooks.create_endpoint(url="https://example.com/hook")
176
+ mon = client.monitors.create(
177
+ name="NVDA watch", target_type="ticker", target_value="NVDA",
178
+ materiality="high", webhook_endpoint_id=ep.data["id"],
179
+ )
180
+
181
+ # Stream the events feed
182
+ for ev in client.events.iterate(ticker="NVDA", importance="high", max_items=100):
183
+ print(ev["event_type"])
184
+ ```
185
+
186
+ ## OpenAI-compatible endpoint
187
+
188
+ Clous exposes an OpenAI-compatible chat endpoint. Use the built-in helper:
189
+
190
+ ```python
191
+ out = client.chat(messages=[{"role": "user", "content": "Summarize Apple's latest 8-K"}])
192
+ print(out["choices"][0]["message"]["content"])
193
+ ```
194
+
195
+ …or point the official `openai` SDK at Clous directly:
196
+
197
+ ```python
198
+ from openai import OpenAI
199
+
200
+ oai = OpenAI(base_url="https://api.clous.ai/v1", api_key="<CLOUS_API_KEY>")
201
+ resp = oai.chat.completions.create(model="clous", messages=[{"role": "user", "content": "…"}])
202
+ ```
203
+
204
+ The Clous SDK also accepts `base_url="https://api.clous.ai/v1"`; the trailing `/v1` is normalized so
205
+ resource paths still resolve.
206
+
207
+ ## Errors, timeouts, retries
208
+
209
+ Non-2xx responses raise a typed exception carrying `status_code`, `request_id`, `message`, and the
210
+ parsed `body`:
211
+
212
+ ```python
213
+ from clous import APIError, AuthenticationError, RateLimitError, NotFoundError
214
+
215
+ try:
216
+ client.filings.search()
217
+ except AuthenticationError:
218
+ ... # 401
219
+ except RateLimitError as e:
220
+ ... # 429
221
+ except NotFoundError:
222
+ ... # 404
223
+ except APIError as e:
224
+ print(e.status_code, e.request_id, e.message)
225
+ ```
226
+
227
+ The client times out after 30s (configurable via `timeout=`) and retries transient failures
228
+ (429 + 5xx + network errors) up to `max_retries` (default 3) with exponential backoff that honors
229
+ `Retry-After`.
230
+
231
+ ## Development
232
+
233
+ ```bash
234
+ pip install -e ".[dev]"
235
+ pytest -q
236
+ ```
237
+
238
+ ## License
239
+
240
+ [MIT](LICENSE)
@@ -0,0 +1,35 @@
1
+ """Pull structured XBRL financials and ask a grounded question.
2
+
3
+ Run with: CLOUS_API_KEY=... python examples/financials_and_answer.py
4
+ """
5
+
6
+ from clous import Clous
7
+
8
+ APPLE_CIK = "0000320193"
9
+
10
+
11
+ def main() -> None:
12
+ client = Clous()
13
+
14
+ # Structured XBRL facts for a single concept.
15
+ facts = client.financials.get(APPLE_CIK, concept="Revenues")
16
+ print(f"got {len(facts)} revenue facts; source={facts.source}")
17
+ for fact in list(facts)[:3]:
18
+ print(" ", fact)
19
+
20
+ # Token-efficient projection with `fields`.
21
+ trimmed = client.financials.get(APPLE_CIK, concept="Revenues", fields="concept,value,fy,fp")
22
+ print("trimmed first row:", trimmed[0] if len(trimmed) else None)
23
+
24
+ # Grounded Q&A over filings (returns the raw answer envelope).
25
+ answer = client.answer(
26
+ "What was the most recently reported annual revenue?",
27
+ ticker="AAPL",
28
+ max_sources=4,
29
+ )
30
+ print("\nANSWER:\n", answer.get("answer"))
31
+ print("SOURCES:", len(answer.get("sources", [])))
32
+
33
+
34
+ if __name__ == "__main__":
35
+ main()
@@ -0,0 +1,42 @@
1
+ """Create a monitor with a webhook and stream the events feed.
2
+
3
+ Run with: CLOUS_API_KEY=... python examples/monitor_and_events.py
4
+ """
5
+
6
+ from clous import Clous
7
+
8
+
9
+ def main() -> None:
10
+ client = Clous()
11
+
12
+ # 1. Register a webhook endpoint to receive matches.
13
+ endpoint = client.webhooks.create_endpoint(
14
+ url="https://example.com/clous/webhook",
15
+ description="demo endpoint",
16
+ )
17
+ endpoint_id = endpoint.data["id"]
18
+ print("created webhook endpoint:", endpoint_id)
19
+
20
+ # 2. Create a monitor that fires on high-importance NVDA events.
21
+ monitor = client.monitors.create(
22
+ name="NVDA high-importance watch",
23
+ target_type="ticker",
24
+ target_value="NVDA",
25
+ signals=["sec.8k.executive_change", "sec.form4.insider_sell"],
26
+ materiality="high",
27
+ webhook_endpoint_id=endpoint_id,
28
+ )
29
+ print("created monitor:", monitor.data["id"])
30
+
31
+ # 3. Stream recent high-importance events for NVDA.
32
+ print("\n--- recent high-importance NVDA events ---")
33
+ for event in client.events.iterate(ticker="NVDA", importance="high", max_items=20):
34
+ print(event.get("event_type"), "-", event.get("title") or event.get("summary"))
35
+
36
+ # 4. Tidy up.
37
+ client.monitors.delete(monitor.data["id"])
38
+ print("deleted monitor")
39
+
40
+
41
+ if __name__ == "__main__":
42
+ main()