Metadata-Version: 2.5
Name: misarblog
Version: 1.1.0
Summary: Official Python SDK for Misar.Blog — all 25 developer-API operations: articles, drafts, series, comments, follows, reactions, AI, images, search, analytics, plan
Project-URL: Homepage, https://misar.blog
Project-URL: Documentation, https://docs.misar.io/blog
Project-URL: Repository, https://github.com/Misar-AI/misarblog-sdks
Author-email: Misar AI <hello@misar.io>
License: MIT
License-File: LICENSE
Keywords: api,blog,content,misarblog,publishing,sdk
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
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
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# Misar.Blog Python SDK

Official Python client for the Misar.Blog developer API. Sync and async, typed models, retry with back-off.

## Install

```bash
pip install misarblog
```

## Quick start

```python
from misarblog import MisarBlogClient, MisarBlogPlanLimitError

blog = MisarBlogClient("mbk_...")

me = blog.me.get()
article = blog.articles.publish(title="Hello", body_markdown="# Hi")
thread = blog.comments.list(article["id"], limit=50)
follows = blog.follows.status(me["id"])

try:
    blog.ai.complete(prompt="Draft an intro paragraph")
except MisarBlogPlanLimitError as err:
    print(f"{err.plan} plan is out of credits — upgrade at {err.upgrade_url}")
```

Every method has an `a`-prefixed async twin:

```python
me = await blog.me.aget()
await blog.aclose()
```

## Authentication and plan gating

Every call goes through the metered gateway at `https://api.misar.io/blog/v1`
with your developer key as a Bearer token. Mint a key in the dashboard at
<https://www.misar.blog/dashboard/settings/api> — key management is a
cookie-session flow and is deliberately not exposed by this SDK.

Feature access and throughput follow the subscription attached to that key:

| Signal | Meaning |
| --- | --- |
| `401` | Missing, expired or revoked key |
| `403` | The key is scoped and lacks the scope this route needs |
| `429` (plain) | Rate limit — 100 requests/minute per key. The SDK retries with back-off |
| `429` + `plan_limit_exceeded` | A metered allowance is spent. Retrying will not help until it resets |
| `402` + `plan_limit_exceeded` | The feature is not on this plan |

The last two raise ``MisarBlogPlanLimitError`` rather than a generic error, carrying the
plan slug, the pricing URL and (when the API supplies it) seconds until reset.
Show the upgrade URL instead of reporting a bare failure — the SDK does not
retry these, because retrying cannot change the outcome.

## Covered operations

All 25 key-authenticated operations:

| Group | Operations |
| --- | --- |
| Articles | list, get, create, update, create draft, search, recommendations |
| Series | list, create, add article |
| Reactions | get, add, remove |
| Comments | list |
| Follows | status |
| AI | complete, titles |
| Images | generate, upload |
| Account | profile, plan, trial status, start trial |
| Analytics | summary, upsell funnel |

The API exposes no SSE or WebSocket endpoint that accepts an API key, so this
SDK is request/response only. See [`openapi/blog.openapi.json`][spec] for the
machine-readable contract.

[spec]: https://api.misar.io/blog/v1/openapi.json

## Links

- API docs — <https://docs.misar.io/blog>
- OpenAPI spec — <https://api.misar.io/blog/v1/openapi.json>
- Dashboard — <https://www.misar.blog/dashboard/settings/api>

## License

MIT — see [LICENSE](LICENSE).
