{ "model": "article", "fields": [...] }
{ "type": "asset", "id": "img_a1b2" }
{ "title": { "en": "Hello", "de": "Hallo" } }
{ "slug": "hello-world", "state": "published" }
{ "model": "author", "id": "auth_x7y8" }
{ "path": "/:locale/blog/:slug" }
{ "type": "hero-block", "fields": {...} }
{ "items": [{ "id": "c4f2" }, ...] }
{ "locale": "en", "state": "published" }
{ "assetId": "img_a1b2", "alt": "..." }
Currently in private beta

Blobify Your Content

Headless & Contentless

The headless CMS that doesn't want your content. We provide the editing experience. Your S3 bucket stores the data. No vendor lock-in. No content hostage. Just JSON blobs you own.

What does “Contentless” mean?

Traditional headless CMSes removed the frontend but kept the content on their own infrastructure. Blobify publishes into storage you control.

Traditional Headless CMS

  • Content stored on their servers
  • Charged for storage ($/GB)
  • Charged for bandwidth ($/API call)
  • API rate limits & quotas
  • Vendor lock-in, proprietary formats

Blobify (Contentless)

  • Content in YOUR S3/R2 bucket
  • S3 storage is dirt cheap (free tier is generous)
  • No bandwidth fees through us
  • No API needed - fetch from S3
  • Plain JSON, walk away anytime

We charge for the editing experience and user management - not for storing or delivering your content.

Bring Your Own Bucket

Your content lives in S3 or Cloudflare R2 buckets that you own and control. We never store your content on our servers.

BBlobifyDashboardCMS interface</>Your AppNext / Astro / etc.SSR · SSG · CSRYour BucketS3 / Cloudflare R2{ .. }.json{ .. }.json{ .. }.jsonwritereadfetchno API neededYou own it. You control it.Plain JSON files in your cloud storage
  • Data sovereignty - Content stays in your AWS account or Cloudflare zone
  • No vendor lock-in - Plain JSON files, readable by any system
  • Generous free tiers - Both AWS and Cloudflare offer free storage to get started
  • Compliance ready - Choose your region, your encryption, your rules

Generous free tiers:

  • Cloudflare R2: 10GB storage + 10M reads/mo, plus zero egress fees
  • AWS S3: 5GB storage + 100GB transfer/mo (first 12 months only)

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.

Your BucketS3 / Cloudflare R2
s3://your-bucket/
Assets

Blobify is also an asset manager

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.

  • Upload directly to your S3 or R2 bucket with progress tracking
  • Organize files into folders, give them names, and keep useful metadata with each asset
  • Give teammates a clean dashboard to browse, upload, and manage assets without sending them into AWS or R2 consoles
  • Assets have a fast catalog index for listing plus a separate JSON document for each asset by ID
  • Use public drop zones when you want people outside the dashboard to upload
  • Open audio files in a public player for listening and sharing
  • No Blobify lock-in — serve files directly from your bucket or CDN, stop using Blobify at any time, and still own your data
  • Asset Library

    main / press-kit

    247 files3 spaces

    Press hero image

    image/jpeg2.4 MB

    Metadata, display names, folders, and direct asset links

    Podcast intro take

    audio/wav87 MB

    Public player links, shareable asset URLs, and drop zone uploads

    Uploads go straight to your bucketAsset linksMedia playerDropzonesFiles stay in your bucket

    Everything you need

    A complete content platform — from modeling to delivery.

    Schemas

    Define models for your content types and reusable blocks like Hero, CTA, or FAQ

    Rich Text

    AST-based editor with embedded blocks, links, and references

    Localization

    Multi-language content with per-locale publishing

    References

    Link content together with validated cross-model references

    Version History

    Track changes and restore previous versions

    Draft/Published

    Preview changes before going live

    Asset Management

    Images and files with metadata and transforms

    Sharing

    Dropzones, public player links, and shareable asset URLs

    TypeScript SDK

    Generated types and typed client from your schema

    Image Transforms

    Cloudflare Images for resize, crop, and format conversion

    Webhooks

    Notify external services when content changes

    User Management

    Roles and per-space access control

    How It All Works Together

    From raw JSON files in your bucket to fully resolved content in one SDK call.

    Summaries → List Pages

    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.

    article-s000.json512 items
    {
      "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
      ]
    }

    Content → Detail Pages

    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.

    content/article/c4f2e8a1/published.json
    {
      "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" }
      }
    }

    Indexes → Fast Lookups

    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.

    indexes/article/slug/en/s3.json
    // 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.json

    Routing → Final URLs

    URL patterns defined per model with per-locale overrides. English gets /blog/:slug, German gets /de/blog/:slug.

    routing/published.json
    {
      "article": {
        "path": "/:locale/blog/:slug",
        "locales": {
          "en": "/blog/:slug"
        }
      },
      "page": {
        "path": "/:locale/:slug",
        "locales": {
          "en": "/:slug"
        }
      }
    }

    Behind the Scenes

    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.

    findOne: network requests
    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 content

    Typed Client → Resolved

    The 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.

    blobify.ts
    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" }
        }
      }
    }

    Developer Experience First

    Content lives on S3 - fetch it however you want. Or use our typed client with full autocomplete for your content types and fields.

    Fetch from S3 directly

    // 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));

    Or use the typed client

    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');

    Cloudflare Images Integration

    Automatic image optimization via Cloudflare Images. Resize, convert to WebP/AVIF, and serve from the edge - all with a simple helper function.

    • On-the-fly resizing
    • WebP/AVIF conversion
    • Edge caching
    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, … }

    Automatic Reference Resolution

    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.

    • Summaries fetched once at build time
    • Hash maps for O(1) lookups
    • References, assets, links all resolved
    • Scales to millions of items

    Model-Based Routing

    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 patterns in schema
    • Per-locale URL overrides
    • Resolve with typed helper
    • SEO-friendly localized paths
    // 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"

    Simple, transparent pricing

    Pure usage pricing. Start free, then scale by team, content volume, and write activity.

    Build your plan

    Adjust each slider to match your needs.

    Editors?
    1free
    12381530
    Entries?
    1,000free
    1,00010k25k100k500k1m5m10m
    Spaces?
    1free
    1241020
    Locales?
    1free
    12481020
    Free
    No credit card required
    i

    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.

    Custom

    For teams that need a more tailored setup, pricing shape, or rollout.

    • Unlimited editors, records, locales, and write ops
    • Flexible onboarding and migration support
    • Architecture guidance for your content model and delivery setup
    • Custom integrations and workflow polish
    • A plan shaped around your team rather than fixed tiers
    Contact us
    Private beta: billing is not active yet. Calculator reflects launch-target pricing.