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 misarblogAPI 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
| Resource | Methods (each has an async a… twin) |
|---|---|
blog.articles | list · get · publish · update · create_draft · search · recommendations |
blog.series | list · create · add_article |
blog.ai | titles · complete |
blog.images | generate · upload |
blog.analytics | summary |
blog.me | get |
blog.plan | get |
blog.trial | status · start |
blog.reactions | get · 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.