v1Stable — read-only, cached at the edge

A blog your writer loves.
And your codebase ignores.

A block editor your team writes in, and seven read-only JSON endpoints your site renders from. No plugin ecosystem, no theme layer, no database of yours involved.

$curl api.blogkit.co/v1/blog/hello -H "Authorization: Bearer …"
app.blogkit.co/editor
published · cache purged
GET /v1/blog/why-we-moved · 200 · 34 ms · HIT
{
  "version": 1,
  "blocks": [
    { "id": "blk_h1a2", "type": "heading",
      "data": { "level": 2, "text": "Why we moved our blog to the edge" } },
    { "id": "blk_p9x1", "type": "paragraph",
      "data": { "html": "Our writers were filing tickets to change a headline. That was the whole problem, and it was not a technical one." } },
    { "id": "blk_l4c7", "type": "list",
      "data": { "style": "bullet", "items": ["No plugins to update", "No theme layer fighting our CSS", "No database of ours involved"] } }
  ]
}
34ms
p95 for a cached post, from every region we serve
6.6KB
gzipped client and renderer, zero runtime deps
14
block types, each one JSON you can render yourself
7
endpoints in the whole API. You’ll read it once
The shape

A post is a list of blocks. The list never changes shape.

Fourteen block types and a typed data object on each. That is the entire vocabulary, and it is versioned.

heading
Levels two to four

Anchors come free, and they stay stable when the text is edited.

paragraph
Whitelisted inline HTML

Sanitised on write by re-serialisation, so you can render it with a clear conscience.

image
Solved on upload

Four variants, dimensions to stop layout shift, and a blur placeholder — same response.

code
Never pre-highlighted

A language tag and the raw source. Shiki, Prism, or nothing — your theme, your call.

quote · table · columns · cta
And ten more

Fourteen types in all, and no arbitrary HTML block waiting to break your layout.

unknown
Skipped, not thrown

A type your renderer has not met yet stays quiet instead of crashing the page.

Your renderer

One component, one file, nothing generated

Drop in the component, or map block types to your own. There is no scaffolding step and nothing to eject from later.

app/blog/[slug]/page.tsx
import { BlogContent } from 'blogkit/react'

export default async function Page({ params }) {
  const post = await blogkit.blogs.get(params.slug)

  return (
    <article>
      <h1>{post.title}</h1>
      <BlogContent blocks={post.blocks} />
    </article>
  )
}
The API

Seven endpoints, then you are done reading

GET/v1/blogslist, filter, paginate
GET/v1/blog/:slugone post, all blocks
GET/v1/blog/:slug/relatedshared tags, then categories
GET/v1/searchfull-text, highlighted
GET/v1/categorieswith published counts
GET/v1/tagsrenames resolve 30 days
GET/v1/media/:idone image, every variant

Read-only, bearer auth, ETags on every response. No GraphQL, no SDK you are forced to keep current — the package is optional and the JSON is documented. Publishing invalidates the cache so the next request is already warm.

Who it’s for

One blog, or one per client

For solo developers

Ship the blog you keep postponing

The blog is the part of your side project that never gets built, because MDX means a rebuild for a typo. This is a real editor, a JSON endpoint, and an afternoon.

  • Free tier is one real project, not a 14-day trial
  • npm i blogkit and one component
  • Write from your phone, publish without a deploy
  • Drafts and preview links before anything goes live
Start free
For agencies

One account, every client site

Each client is a separate project — its own posts, media, categories, authors and quota. Nothing leaks between them, and you sign in once.

  • Separate live and test keys per client — revoke one, the rest keep serving
  • Per-project usage, so you know which client is actually costing you
  • Their content is portable JSON, so a handover is not a hostage negotiation
  • Five projects on Pro, unlimited on Advanced

Not built yet: Webhooks · Scheduled publishing. Listed here rather than in the pricing table, because you should find out now and not after you have moved three clients across.

Pricing

Free is a real tier, not a trial

One price per plan, not per person — each tier includes its seats rather than billing for them.

Free
$0forever, no card

One blog of your own.

  • 1 project · 5 posts
  • 5k requests a month
  • 512 MB media · 1 API key
  • Just you
  • Last 5 revisions per post
Start free
Starter
$9.99per month

A personal blog, or a side project.

  • 1 project · 100 posts
  • 50k requests a month
  • 5 GB media · 3 API keys
  • Just you
  • 30-day revision history
Choose Starter
Pro
most popular
$19.99per month

A few sites, or your first clients.

  • 5 projects · unlimited posts
  • 500k requests a month
  • 25 GB media · 10 API keys
  • 5 members per project
  • 90-day revision history
Start with Pro
Advanced
$49.99per month

An agency running a roster.

  • Unlimited projects
  • 2M requests a month
  • 100 GB media · unlimited keys
  • Unlimited members
  • 1-year revision history
Choose Advanced

Cache hits count as a fifth of a request. Over quota we keep serving to 110% and email you — your blog never goes dark mid-launch.

Questions

The ones we actually get

No. Export gives you every post as block JSON plus a media manifest, from a button, any time. The block schema is documented, so the export is renderable without us.

Hand it over. Keep the JSON.

Sign up, write one post, and see the JSON. If the integration takes more than five minutes, tell us and we’ll fix the docs.