Metadata-Version: 2.4
Name: clous
Version: 0.1.0
Summary: Official Python SDK for the Clous SEC/EDGAR API — entity-resolved filings, insider & institutional ownership, financials, events, and monitors.
Project-URL: Homepage, https://clous.ai
Project-URL: Documentation, https://docs.clous.ai
Project-URL: Repository, https://github.com/clousai/clous-python
Project-URL: Issues, https://github.com/clousai/clous-python/issues
Author-email: Clous <support@clous.ai>
License: MIT
License-File: LICENSE
Keywords: 13f,clous,edgar,filings,finance,insider,sdk,sec,xbrl
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.23
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Description-Content-Type: text/markdown

# Clous Python SDK

[![PyPI](https://img.shields.io/pypi/v/clous.svg)](https://pypi.org/project/clous/)
[![Python](https://img.shields.io/pypi/pyversions/clous.svg)](https://pypi.org/project/clous/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

The official Python client for the [**Clous**](https://clous.ai) SEC/EDGAR API — entity-resolved
filings, insider & institutional ownership, structured XBRL financials, a typed business-events feed,
grounded Q&A, and standing monitors with webhooks.

```bash
pip install clous
```

Requires Python 3.9+. The only runtime dependency is [`httpx`](https://www.python-httpx.org/).

## Quickstart

```python
from clous import Clous

client = Clous()  # reads CLOUS_API_KEY from the environment

# Search the EDGAR filing index
page = client.filings.search(form_type="8-K", limit=10)
for filing in page:
    print(filing["accession"], filing.get("company_name"))

# Structured XBRL financials for one company
revenue = client.financials.get("0000320193", concept="Revenues")

# Grounded Q&A over filings
answer = client.answer("What was Apple's most recent annual revenue?", ticker="AAPL")
print(answer["answer"])
```

## Authentication

Pass your key explicitly or set the `CLOUS_API_KEY` environment variable:

```python
client = Clous(api_key="sk_live_...")
# or
import os; os.environ["CLOUS_API_KEY"] = "sk_live_..."; client = Clous()
```

The key is sent as `Authorization: Bearer <CLOUS_API_KEY>`. The base URL defaults to
`https://api.clous.ai` and can be overridden with `base_url=` or the `CLOUS_BASE_URL` env var.
`/v1/sources` works without a key.

## The response envelope

Every endpoint returns a JSON envelope:

```json
{
  "data": [ ... ],
  "page": { "limit": 25, "next_cursor": "…", "has_more": true },
  "as_of": "2026-06-13T00:00:00Z",
  "source": "edgar",
  "query_echo": { ... },
  "warnings": []
}
```

The SDK wraps this in a `Page` object. For the common list case a `Page` behaves like the `data`
list (iterate, index, `len()`), while the envelope metadata and response headers stay available as
attributes:

```python
page = client.events.list(ticker="NVDA", importance="high")

for event in page:            # iterate over page.data
    print(event["event_type"])

first = page[0]               # index into page.data
n = len(page)                 # number of records on this page

page.next_cursor              # cursor for the next page (or None)
page.has_more                 # bool
page.as_of                    # snapshot timestamp
page.source                   # upstream source
page.warnings                 # list of warnings
page.raw                      # the full untouched envelope dict

# Response headers
page.request_id               # X-Request-Id
page.credits_cost             # X-Credits-Cost
page.credits_remaining        # X-Credits-Remaining
```

Single-object endpoints (e.g. `client.account()`, `client.financials.get(...)`) expose the object
via `page.data`; `len(page)` is then `1`.

## Pagination

Use `?cursor=` / `limit=` manually, or let the SDK follow `page.next_cursor` for you with the
`iterate(...)` helper available on every list resource. It yields individual records across all
pages and accepts an optional `max_items` cap:

```python
# Manual
page = client.filings.search(form_type="8-K", limit=100)
while page.has_more:
    page = client.filings.search(form_type="8-K", limit=100, cursor=page.next_cursor)

# Auto — yields every record, stops after max_items
for filing in client.filings.iterate(cik="0000320193", form_type="8-K", max_items=500):
    print(filing["accession"])
```

`limit` must be ≤ 100.

## Token-efficient projections

Every list/get method accepts `fields=` (comma-separated dot-paths) or `output_schema=` (a JSON
Schema dict) to project each record server-side:

```python
client.filings.search(form_type="10-K", fields="accession,company_name,filed_date")
client.events.list(ticker="NVDA", output_schema={"type": "object", "properties": {"event_type": {}, "title": {}}})
```

## Resources & methods

The client groups endpoints by resource. Each `search`/`list` method also has an `iterate(...)`
auto-paginator.

| Resource | Methods |
| --- | --- |
| `client.filings` | `search`, `iterate`, `documents`, `extract(item=…)`, `insiders`, `events`, `subsidiaries`, `crowdfunding`, `proxy_votes`, `briefing` |
| `client.full_text` | `search(q=…)`, `iterate` |
| `client.entities` | `search` / `resolve`, `iterate` |
| `client.insider` | `search`, `iterate`, `form144`, `iterate_form144` |
| `client.ownership` | `search`, `iterate` — 13D/13G |
| `client.holdings` | `search`, `iterate` — 13F holdings |
| `client.managers` | `search`, `iterate` — 13F managers |
| `client.funds` | `holdings`, `providers`, `iterate_holdings`, `iterate_providers` — N-PORT / N-CEN |
| `client.advisers` | `search`, `iterate`, `get(crd)` — Form ADV |
| `client.private_funds` / `client.private_fund_stats` | `search`, `iterate` |
| `client.broker_dealers` / `client.form_crs` / `client.iapd_individuals` | `search`, `iterate` |
| `client.raises` | `search`, `iterate` — Form D |
| `client.financials` | `search`, `iterate`, `get(cik, concept=…)` |
| `client.financial_statements` | `search`, `iterate` |
| `client.board` / `client.compensation` | `search`, `iterate` |
| `client.proxy` | `officers`, `iterate_officers` — DEF 14A |
| `client.enforcement` / `client.litigation` / `client.nt_late` / `client.trading_suspensions` / `client.whistleblower` | `search`, `iterate` |
| `client.cyber_incidents` | `search`, `iterate` — 8-K Item 1.05 |
| `client.patents` | `search`, `iterate` — USPTO grants |
| `client.events` | `list`, `iterate`, `get(event_id)` |
| `client.monitors` | `list`, `create`, `get`, `update`, `delete` |
| `client.webhooks` | `list_endpoints`, `create_endpoint`, `list_deliveries` |
| top-level | `client.account()`, `client.sources()`, `client.answer(q, …)`, `client.briefing(accession)`, `client.chat(messages=…)` |

Method parameters mirror the REST endpoints. Any extra keyword arguments are passed straight through
as query params (or body fields for POST/PATCH), so new API params work without an SDK upgrade. There
are also raw escape hatches: `client.get(path, params=…)` and `client.post(path, body=…)`.

### Examples

```python
# Insider transactions (Form 4) — purchases over $1M
client.insider.search(ticker="TSLA", trans_code="P", min_value_usd=1_000_000)

# 13F institutional holdings of a security
client.holdings.search(cusip="037833100", min_value=5_000_000)

# Extract Risk Factors from a 10-K
client.filings.extract("0000320193-23-000106", item="1A")

# AI briefing for a filing
brief = client.briefing("0000320193-23-000106")

# Create a monitor that webhooks on high-importance NVDA events
ep = client.webhooks.create_endpoint(url="https://example.com/hook")
mon = client.monitors.create(
    name="NVDA watch", target_type="ticker", target_value="NVDA",
    materiality="high", webhook_endpoint_id=ep.data["id"],
)

# Stream the events feed
for ev in client.events.iterate(ticker="NVDA", importance="high", max_items=100):
    print(ev["event_type"])
```

## OpenAI-compatible endpoint

Clous exposes an OpenAI-compatible chat endpoint. Use the built-in helper:

```python
out = client.chat(messages=[{"role": "user", "content": "Summarize Apple's latest 8-K"}])
print(out["choices"][0]["message"]["content"])
```

…or point the official `openai` SDK at Clous directly:

```python
from openai import OpenAI

oai = OpenAI(base_url="https://api.clous.ai/v1", api_key="<CLOUS_API_KEY>")
resp = oai.chat.completions.create(model="clous", messages=[{"role": "user", "content": "…"}])
```

The Clous SDK also accepts `base_url="https://api.clous.ai/v1"`; the trailing `/v1` is normalized so
resource paths still resolve.

## Errors, timeouts, retries

Non-2xx responses raise a typed exception carrying `status_code`, `request_id`, `message`, and the
parsed `body`:

```python
from clous import APIError, AuthenticationError, RateLimitError, NotFoundError

try:
    client.filings.search()
except AuthenticationError:
    ...        # 401
except RateLimitError as e:
    ...        # 429
except NotFoundError:
    ...        # 404
except APIError as e:
    print(e.status_code, e.request_id, e.message)
```

The client times out after 30s (configurable via `timeout=`) and retries transient failures
(429 + 5xx + network errors) up to `max_retries` (default 3) with exponential backoff that honors
`Retry-After`.

## Development

```bash
pip install -e ".[dev]"
pytest -q
```

## License

[MIT](LICENSE)
