MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
SDKs

Python SDK

Publish articles, manage series, and run AI tools with the MisarBlog Python SDK, plus post-embed helpers.

The Python SDK ships a full client for the developer API (https://api.misar.io/blog/v1) plus the iframe embed helpers, all in the misarblog package (v1.0.0).

Installation

pip install misarblog

API Client

MisarBlogClient authenticates with an mbk_ key and defaults to https://api.misar.io/blog/v1 (the gateway strips /api). Every method has a sync form and an a-prefixed async form (alist, apublish, …).

from misarblog import MisarBlogClient

blog = MisarBlogClient(api_key="mbk_YOUR_KEY")

# Override the base URL if needed
staging = MisarBlogClient(
    api_key="mbk_YOUR_KEY",
    base_url="https://api.misar.io/blog/v1",
)

Articles

# List your articles
result = blog.articles.list(status="published", limit=20)
for a in result.articles:
    print(a.title, a.url)

# Get one by slug or UUID
article = blog.articles.get("my-first-article")

# Publish
created = blog.articles.publish(
    title="Getting Started with the MisarBlog API",
    body_markdown="## Introduction\n\nThis guide covers…",
    tags=["python", "api"],
    cover_image_url="https://example.com/cover.jpg",
    visibility="public",
)

# Save a draft, then update / publish it
draft = blog.articles.create_draft(title="Work in progress", body_markdown="## Soon…")
blog.articles.update(draft.slug, title="Ready", publish=True)

# Search + recommendations
hits = blog.articles.search(q="react", type="articles", sort="newest")
recs = blog.articles.recommendations(article.id, limit=5)

Async

import asyncio
from misarblog import MisarBlogClient

async def main():
    blog = MisarBlogClient(api_key="mbk_YOUR_KEY")
    result = await blog.articles.alist(status="published")
    print(result.total)

asyncio.run(main())

Other resources

blog.series.list()
blog.series.create(title="A Series", description="Optional")

blog.ai.titles(action="seo", prompt="best AI writing tools 2026")
blog.ai.complete(prompt="Tighten this intro: …", system="You are an editor.", max_tokens=300)

blog.images.generate(prompt="A minimalist cover", size="1792x1024")

blog.analytics.summary(days=30)   # views, revenue_cents, active_subscribers
blog.me.get()                     # your profile
blog.plan.get()                   # plan + quota usage
blog.trial.status()               # self-serve trial status

blog.reactions.get(article.id)
blog.reactions.add(article.id, "like")     # "like" | "clap" | "bookmark"
blog.reactions.remove(article.id, "like")

Resources

ResourceMethods (each has an async a… twin)
blog.articleslist · get · publish · update · create_draft · search · recommendations
blog.serieslist · create · add_article
blog.aititles · complete
blog.imagesgenerate · upload
blog.analyticssummary
blog.meget
blog.planget
blog.trialstatus · start
blog.reactionsget · add · remove

Error handling

from misarblog import MisarBlogError

try:
    blog.articles.get("missing-slug")
except MisarBlogError as err:
    print(err.status, err)

Embed helpers

The package also exports the no-key iframe helpers:

from misarblog import embed_url, refresh_token

url = embed_url("johndoe")                                   # profile embed
post = embed_url("johndoe", slug="my-first-post", theme="dark")

# For gated (paywalled) embeds, refresh the session token
result = refresh_token("current-session-token")
print(result["token"], result["expiresAt"])

embed_url(username, slug=None, theme="auto") returns a URL for the public embed viewer — drop it into an <iframe src="…">. refresh_token(token, base_url="https://misar.blog") returns a dict with the new token and its expiresAt, and raises on a non-2xx response.