Blobify publishes plain JSON to your bucket. Your app reads it directly. No API, no rate limits, no vendor lock-in.
Traditional headless CMSes removed the frontend but kept the content on their own infrastructure. Blobify publishes into storage you control.
We charge for the editing experience and user management - not for storing or delivering your content.
A bucket is just a folder in the cloud, hosted by Amazon (S3) or Cloudflare (R2). You sign up, get a bucket, and Blobify writes your content into it as plain JSON. Your app reads it directly. We never store your content on our servers.
Generous free tiers:
Most content sites fit entirely within these free tiers.
Optional content prefix (advanced)
You can add a per-space content prefix to add an extra folder segment for content JSON, summaries, indexes, and the asset catalog JSON, making direct bucket URLs harder to guess.
Asset files still stay in assets/media/.
Path hardening only. If your app fetches content JSON in the browser, the prefix can still be visible in network requests. SSR/ISR keeps it less obvious.
Use Blobify to manage images, audio, video, PDFs, and uploaded files in the same bucket-first model. Think Dropbox-style asset workflows, but the files live in your own bucket.
Asset Library
main / press-kit
Press hero image
image/jpeg2.4 MBMetadata, display names, folders, and direct asset links
Podcast intro take
audio/wav87 MBPublic player links, shareable asset URLs, and drop zone uploads
A complete content platform, from modeling to delivery.
Define models for your content types and reusable blocks like Hero, CTA, or FAQ
AST-based editor with embedded blocks, links, and references
Multi-language content with per-locale publishing
Link content together with validated cross-model references
Track changes and restore previous versions
Preview changes before going live
Images and files with metadata and transforms
Dropzones, public player links, and shareable asset URLs
Generated types and typed client from your schema
Cloudflare Images for resize, crop, and format conversion
Notify external services when content changes
Roles and per-space access control
From raw JSON files in your bucket to fully resolved content in one SDK call.
Each model gets summary shards with only the fields you pick. One manifest plus shard fetch gives you everything needed for list pages, search indexes, and sitemaps.
{
"items": [
{ "id": "c4f2e8a1", "slug": "hello-world", "title": "Hello World" },
{ "id": "d7a1c3b9", "slug": "getting-started", "title": "Getting Started" },
{ "id": "e9b4d2f7", "slug": "advanced-tips", "title": "Advanced Tips" },
// ... 512 more items
]
}Full content documents with localized fields, rich text, and references. One JSON file per content entry. If you index a field like slug, Blobify can jump straight to the entry by that field and then fetch the full JSON only for the detail page.
{
"id": "c4f2e8a1",
"model": "article",
"fields": {
"title": { "en": "Hello World", "de": "Hallo Welt" },
"slug": { "en": "hello-world", "de": "hallo-welt" },
"content": {
"en": {
"type": "root",
"children": [
{ "type": "heading", "depth": 1,
"children": [{ "type": "text", "value": "Hello, world!" }] },
{ "type": "paragraph",
"children": [
{ "type": "text", "value": "This is an " },
{ "type": "text", "value": "example", "marks": ["bold"] },
{ "type": "text", "value": " richtext." }
] }
]
}
},
"heroImage": { "type": "asset", "assetId": "img_a1b2" },
"author": { "model": "author", "id": "auth_x7y8" }
}
}Blobify supports both read paths. For indexed fields like slug, it can jump straight through field index shards to find the entry ID. For list pages and non-indexed queries, it uses summary manifests and shards.
// Indexed lookup path (for fields like slug)
GET /indexes/article/slug/en/s3.json
{
"items": [
{ "key": "hello-world", "id": "c4f2e8a1" },
{ "key": "getting-started", "id": "d7a1c3b9" }
]
}
// List page path
GET /summaries/published/index.json
GET /summaries/published/article/s000.jsonURL patterns defined per model with per-locale overrides. English gets /blog/:slug, German gets /de/blog/:slug.
{
"article": {
"path": "/:locale/blog/:slug",
"locales": {
"en": "/blog/:slug"
}
},
"page": {
"path": "/:locale/:slug",
"locales": {
"en": "/:slug"
}
}
}One SDK call triggers parallel fetches. For indexed lookups like slug, Blobify can jump straight through field index shards. For list pages and non-indexed lookups, it uses summary manifests and shards. Then it loads content and assets in parallel, with dependent summaries for references fetched automatically and cached.
const article = await cms.findOne('article', { slug: 'hello-world' }, 'en');
// Behind the scenes:
// 1. Fetch summary manifest + shards (cached after first call)
GET /summaries/published/index.json // shard discovery
GET /summaries/published/article/s000.json // article summary shard
// 2. Find by slug in merged summary → id: "c4f2e8a1"
// 3. Fetch content + assets in parallel
GET /content/article/c4f2e8a1/published.json // full document
GET /assets/catalog.json // asset URLs (logical key)
// when contentPrefix is enabled:
// GET /_blobify/{prefix}/assets/catalog.json
// 4. Resolve references (fetches dependent summaries)
GET /summaries/published/author/s000.json
// 5. Resolve asset refs → URL strings
// 6. Return resolved contentThe SDK resolves references automatically. Asset references become URL strings from your bucket, content references become resolved summary entries. Use localize() and resolveUrl() helpers for the rest.
const article = await cms.findOne(
"article",
{ slug: "hello-world" },
"en"
);
resolveUrl("article", article, "en") // "/blog/hello-world"
resolveUrl("article", article, "de") // "/de/blog/hello-world"
localize(article.fields.title, "en") // "Hello World"
localize(article.fields.title, "de") // "Hallo Welt"
// resolved article
{
"id": "c4f2e8a1",
"model": "article",
"fields": {
"title": { "en": "Hello World", "de": "Hallo Welt" },
"slug": { "en": "hello-world" },
"content": {
"en": {
"type": "root",
"children": [
{
"type": "heading",
"depth": 1,
"children": [{ "type": "text", "value": "Hello, world!" }]
},
{
"type": "paragraph",
"children": [
{ "type": "text", "value": "This is an " },
{ "type": "text", "value": "example", "marks": ["bold"] },
{ "type": "text", "value": " richtext." }
]
}
]
}
},
"heroImage": "https://cdn.example.com/media/img_a1b2.jpg",
"author": {
"id": "auth_x7y8",
"fields": { "name": "Jane Smith" }
}
}
}Two ways to read content. Use whatever fits. Both read the same JSON files from your bucket.
// No API needed - fetch directly from your bucket
const manifest = await fetch('.../summaries/published/index.json').then(res => res.json());
const shards = await Promise.all(
manifest.models.article.shards.map(shard =>
fetch('.../summaries/published/article/' + shard + '.json').then(res => res.json())
)
);
const articles = shards.flatMap(shard => shard.items);
articles.forEach(article => console.log(article.fields.title.en, article.fields.slug.en));Works in any runtime. No dependencies, no auth, no SDK.
import { createClient } from './blobify';
const cms = createClient({
baseUrl: 'https://my-bucket.s3.amazonaws.com',
spaceId: 'main',
});
// Full TypeScript autocomplete
const articles = await cms.getAll('article', 'en');
const page = await cms.findOne('page', { slug: 'about' }, 'en');Generated types from your schema. References resolved. Localized helpers built in.
Automatic image optimization via Cloudflare Images. Resize, convert to WebP/AVIF, and serve from the edge - all with a simple helper function.
import { cfImage } from './blobify';
// Transform images via Cloudflare Images
const optimized = cfImage(article.fields.heroImage, {
width: 800,
height: 600,
quality: 85,
format: 'webp'
});
// Output: https://your-site.com/cdn-cgi/image/w=800,h=600,q=85,f=webp/...// Typed client fetches summary manifests and shards once, builds hash maps
const cms = createClient({
baseUrl,
spaceId: 'main',
});
// Find by any summary field (slug, category, etc.)
const article = await cms.findOne(
'article', { slug: 'my-post' }, 'en'
);
const frontpage = await cms.findOne(
'page', { isFrontpage: true }, 'en'
);
const techPosts = await cms.findMany(
'article', { category: 'tech' }, 'en'
);
// References resolved automatically (O(1) lookup)
article.fields.author // → Author object
article.fields.heroImage // → { url, alt, width, … }References and assets are resolved automatically from summaries. The typed client fetches summary manifests and shards at build time, then resolves IDs instantly with O(1) lookups.
Define URL patterns directly in your model schema. Support for per-locale overrides means German URLs can use German paths while English uses English.
// Define routes in your model schema
{
"model": "article",
"routing": {
"path": "/:locale/blog/:slug",
"locales": {
// en without the locale prefix
"en": "/blog/:slug"
}
}
}
// Resolve URLs with the typed client
import { resolveUrl } from './blobify';
resolveUrl('article', article, 'en');
// → "/blog/hello-world"
resolveUrl('article', article, 'de');
// → "/de/blog/hello-world"Connect Claude, ChatGPT, Codex, or Cursor and let them do the work. Design a content schema from a prompt. Fix a typo from your phone over breakfast. Bulk-import 5,000 entries from your old CMS. All without opening the dashboard.
Model Context Protocol
Blobify ships a built-in MCP server so AI agents can use it as a tool. Design schemas, find entries, edit fields, publish locales, bulk-import from another CMS, generate variants. One URL, one bearer token.
"blobify": {
"url": "api.blobify.io/v1/mcp/YOUR_ORG",
"auth": "Bearer YOUR_API_KEY"
}Ask “Make me a blog schema like X.com” and the AI generates models, fields, and validation. You review and approve before anything ships.
Open ChatGPT or Claude on your phone, ask it to update a blog post or add a CTA, hit publish. No laptop. No dashboard.
Migrate from Contentful, generate localized variants, or normalize 5,000 entries by asking the AI to do it. Up to 100 items per call.
Plug in Okta, Auth0, Microsoft Entra, Google Workspace, or any OIDC-compliant identity provider. Optional enforced mode blocks password sign-in entirely.
Most CMSes hide SSO behind a 5x “enterprise” tier. We don't. Security shouldn't be a paywall.
Org admins configure SSO under Settings → SSO.
Pure usage pricing. Start free, then scale by team, content volume, and write activity.
Adjust each slider to match your needs.
Did you know?
We don't charge for requests, storage, or bandwidth. Content lives in your own S3/R2 bucket. You pay your cloud provider directly at their rates.
For teams that need a more tailored setup, pricing shape, or rollout.
Cancel anytime. Your content stays yours.
If you cancel or we shut down, your app keeps reading the same JSON from your bucket. No data export step, no migration window, no lock-in.