Metadata-Version: 2.4
Name: tathaastu
Version: 0.1.0
Summary: Hindu calendar intelligence API — panchang, festivals and muhurat with explainable derivations
Author: TathaAstu
License: MIT
Project-URL: Homepage, https://tathaastuapi.com
Project-URL: Documentation, https://docs.tathaastuapi.com
Keywords: panchang,hindu-calendar,festivals,muhurat,tithi,nakshatra
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov>=4; extra == "dev"
Requires-Dist: responses>=0.24; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"

# tathaastu — Python SDK

Hindu calendar intelligence: panchang, festivals and muhurat, with the
derivation behind every answer.

Generated from the [OpenAPI 3.1 document](https://api.tathaastuapi.com/openapi.json)
that serves the API, so the client cannot drift from the endpoints it wraps.

## Install

Not yet published to PyPI. Install from source:

```bash
pip install -e packages/python-sdk
```

## First call, no signup

The `/v1/demo/` endpoints are public. This runs the moment the package is
installed, so you can see the shape of the data before deciding anything:

```python
from tathaastu import TathaAstu

day = TathaAstu().demo.panchang_get(date="2026-11-08")
print(day["tithi"]["name"])           # Chaturdashi (14th)
```

## Quickstart

Everything else needs a key. Demo responses are limited to a fixed location and
a narrow date window; the keyed endpoints are the real surface.

```python
from tathaastu import TathaAstu

client = TathaAstu()                      # reads TATHAASTU_API_KEY

day = client.panchang.get(date="2026-11-08", location_id=1)
print(day["tithi"]["name"], day["tithi"]["period"])

for f in client.festivals.list(date="2026-11-08", location_id=1)["festivals"]:
    print(f["festival_key"], f["confidence"], f["confidence_label"])
```

## Ask why

The reason this SDK exists. Every festival carries the rule that produced it:

```python
why = client.festivals.explain(date="2026-11-08", festival="FESTIVAL_DIWALI")
print(why["human_readable"])
```

## Errors

Status codes map to distinct exceptions, because the fixes differ:

| Exception | Status | What to do |
|---|---|---|
| `AuthenticationError` | 401 | Check `TATHAASTU_API_KEY` |
| `PlanError` | 402 | The request was valid; your plan excludes this endpoint. Upgrade. |
| `ValidationError` | 400, 422 | `.fields` names the offending parameters |
| `RateLimitError` | 429 | `.retry_after` when the server supplies it |
| `NotFoundError` | 404 | No data for that input |
| `ServerError` | 5xx | Retried automatically |

`PlanError` is deliberately not a subclass of `AuthenticationError`. A 402 is not
an auth problem, and treating it as one sends you looking at the wrong thing.

```python
from tathaastu import PlanError, RateLimitError

try:
    client.timings.hora_get(date="2026-11-08", lat=28.6139, lon=77.2090)
except PlanError:
    ...          # upgrade required
except RateLimitError as e:
    time.sleep(e.retry_after or 60)
```

## Retries

Idempotent failures — connection errors, 5xx and 429 — are retried with
exponential backoff and full jitter. A 4xx is never retried: repeating it cannot
change the answer and only spends quota.

```python
from tathaastu import RetryPolicy, TathaAstu

client = TathaAstu(retry=RetryPolicy(attempts=5, max_backoff=20))
```

## Rate limits

```python
client.panchang.get(date="2026-11-08")
print(client.rate_limit)      # {'limit': 1000, 'remaining': 998}
```

Empty when the API sends no rate-limit headers — the SDK reports what the server
said, never an estimate.

## Sweeping a range

```python
for d in client.iter_dates("2026-11-01", "2026-11-30"):
    ...
```

For anything wide prefer `client.panchang.get_range(...)` or `client.bulk.*` —
one request rather than N.

## Namespaces

`panchang` · `festivals` · `muhurat` · `calendar` · `timings` · `astronomy` ·
`eclipses` · `calculators` · `conditions` · `events` · `astrology` · `locations` ·
`i18n` · `bulk` · `aggregated` · `shastric` · `world_festivals` · `system` · `demo`

## Development

```bash
python generate_client.py --spec ../openapi.json   # regenerate endpoint methods
python -m pytest tests/ -q
```

`_endpoints.py` is generated. Edit `generate_client.py`, not the output.
