Vibe code your own Buffer

Vibe code your own Buffer as a draft queue and one publisher. Every network API, their rate limits, and the analytics suite are the other product.

  • teardown
  • vibe-coding
  • scope

You can vibe code your own Buffer. Start with a draft queue and one publisher. The catalog calls Buffer that loop, not an analytics suite.

You pay $18–$36 a month. The verdict is “Worth a Saturday”.

Earlier today I wrote about how to write a coding agent prompt. That was a regular backlog post. This one is a teardown.

Vibe code your own Buffer as a queue

The Buffer page splits the work into three rows.

The first row is 8 hours. Post queue, one network, scheduled publish. That is a draft, a dueAt, a cron that sends, and a log that says sent or failed. You can vibe-code this.

The second row is 3 days. Calendar drag-reschedule, previews, retries, a second network. Still your accounts. More screens, a character warning, a retry button.

The third row is 1 month and up. Every network API, approvals, analytics, AI writing. Skip that row.

If you tell an agent to “clone Buffer”, it aims at the third row. The useful v1 is the first one.

Here is the shape of the data. Nothing else has to exist at 8 hours.

const channel = {
  id: 'ch-x-flavio',
  service: 'x',
  handle: '@flaviocopes',
}

const post = {
  id: 'post-queue-24',
  channelId: 'ch-x-flavio',
  text: 'New post: vibe code your own Buffer as a queue.',
  dueAt: '2026-10-24T15:00:00.000Z',
  status: 'queued',
}

const attempt = {
  postId: 'post-queue-24',
  startedAt: '2026-10-24T15:00:02.000Z',
  result: 'sent',
  remoteId: '1982400111222333444',
}

A channel, a post with four states (draft, queued, published, failed), and one publish attempt. Previews can wait until Tuesday.

The audience line on the page is one creator publishing to two social accounts. If that is you, a queue and one publisher are enough.

The same v1 also replaces Typefully, Hypefury, and Later. Postiz is the cross-post cousin at $29–$99 a month. When the question is “did anyone see the launch tweet”, that is Plausible at $9–$19. I wrote about how Plausible works yesterday.

How does Buffer work

Buffer publishes a lot about the product shape. I am summarizing those posts and their public API, not reconstructing today’s cluster.

Sunil Sadasivan’s April 2014 write-up is the public picture of the scheduler. Joel’s first cron, commit 13, ran every minute on one Linode box. It loaded due updates and posted them in the same process. By June 2012 they had moved that send onto Amazon SQS. A minute job found due posts. Workers on other boxes called Twitter and Facebook, then marked the row sent.

That split is the product. Scheduling is “which row is due”. Sending is “call a network and survive the retry”. They wrote that this was still the foundation two years later. Treat “SQS is still the 2026 queue” as an educated guess. Peter Emil’s July 2025 note only says Buffer now runs interdependent backend microservices and workers.

The 2014 post is honest about the failure mode that matters. Two workers can pick the same due row. You get a duplicate tweet. They added a state machine (buffer, pending, processing, sent) and used MongoDB findAndModify so the claim is atomic. A second worker sees processing and leaves the row alone.

Their public GraphQL API shows a shorter lifecycle. The posts and scheduling guide exposes scheduled, sent, and error. A post belongs to one channelId. dueAt is UTC. addToQueue fills the next slot in your posting schedule. customScheduled takes an exact time. Treat the extra internal states as an educated guess. Persist a claim before you call the network.

Joe Birch’s Hive case study says they closed the old REST API in 2019 and later opened a public GraphQL view of the internal schema. Amanda Marochko’s July 2025 rebuild post gives the reason: after Cambridge Analytica, Meta, Pinterest, LinkedIn, and X tightened terms, and Buffer stopped taking new API apps. The new API is GraphQL plus TypeScript. Their OAuth guide issues an access token with expires_in: 3600 and a single-use refresh token if you asked for offline_access.

A v1 does not need GraphQL or Kubernetes. One Worker, one D1 table, one Cron Trigger. That is the stack on the card. Use Rails if that is your weekday language.

QUEUE THE POST Draft, then a UTC dueAt CRON FINDS DUE ROWS Every minute, look ahead CLAIM THE ROW queued to sending, once CALL THE NETWORK Refresh token, then POST WRITE THE ATTEMPT sent, failed, remote id Same job never sends twice
The public path: queue a due time, claim the row, call one network, log the attempt.

How Buffer scheduling works

The 2014 post has a second problem you will hit on day one.

People schedule on the hour. Their Thursday morning graph jumped from a few hundred due posts to about 5,000 at 8:00 Pacific, then fell back. A cron that only looks at “due right now” times out in the spike. Posts that should have gone out at 8:00 landed at 8:04.

They started enqueueing anything due in the next 15 minutes, with an SQS delay so the worker woke at dueAt. The cron left the real-time path. If the user then dragged the post to 9:00, they had to cancel the delayed message. That cancel path is why the catalog wants retries and a readable log on the second row, not the first.

A v1 can stay dumber. Cloudflare Cron Triggers run on UTC. * * * * * is every minute. Look up queued rows whose dueAt is in the past, claim one, send it.

export default {
  async scheduled(controller, env) {
    const now = new Date(controller.scheduledTime).toISOString()
    const post = await env.db.prepare(`
      update posts
      set status = 'sending'
      where id = (
        select id from posts
        where status = 'queued' and due_at <= ?
        order by due_at
        limit 1
      )
      returning *
    `).bind(now).first()

    if (!post) return
    await publish(post, env)
  },
}

The update … returning is the claim. Two overlapping crons cannot send the same post-queue-24. If publish throws, write a failed attempt and leave a retry button. Flipping the row back to queued from the same job lets the next cron send it again.

Refresh the network token before the POST. Buffer’s own access tokens die in an hour. Social networks expire tokens the same way. Store the refresh token encrypted. The card’s first hard rule is encrypt provider tokens and make publishing jobs idempotent.

Hourly slots are enough for a creator queue. A weekly calendar with drag-to-reschedule is the 3-day row.

Every network API and their rate limits

The skip row is Buffer the platform.

Their public API can create posts for Instagram, Threads, LinkedIn, X, Facebook, Google Business Profiles, Mastodon, YouTube, Pinterest, and Bluesky. The Hive write-up calls that “10+ social networks”. Each channel has its own metadata: first comments on LinkedIn, stories and reels on Instagram, a board on Pinterest, threads on X. The compose box looks like one post. Under it you keep a variant per service.

Instagram is the example that stops a weekend clone. Meta’s content publishing docs only talk to professional accounts. You create a container on POST /{ig-id}/media, wait until it is ready, then call POST /{ig-id}/media_publish. They cURL your image, so the file has to sit on a public URL. JPEG only. Accounts are limited to 100 API-published posts in a rolling 24 hours. A carousel still counts as one. They tell you to enforce that cap yourself if people schedule ahead.

X, LinkedIn, and the rest publish their own write endpoints, OAuth scopes, and 429 windows. I am not restating those tables here, because they change. Keep one adapter per service and one idempotency key per attempt. A 429 writes failed plus a retry-after time, then stops.

Buffer already lived through a terms change. The 2019 API freeze in their rebuild post is the same class of risk you take when your publisher is a guest on someone else’s platform. A token that worked in March can be a 403 in April.

Metrics are the other product. Once a post is sent, each network reports reactions and impressions with different names. Buffer normalizes those onto Post.metrics. Approvals, Ideas, and AI-assisted captions sit next to that. The catalog’s non-goal already names them: do not add AI writing, social listening, approval workflows, or every network in v1.

Postiz is the same hard half with a different logo. Its skip row is every network API and the token zoo. If you want two networks on purpose, start there after the one-channel queue works for a week.

A Buffer alternative for one channel

8-HOUR V1 Draft queue and due times One social network API Cron publish and a log SKIP THIS HALF Every network API Approvals and analytics AI writing and listening
Ship the queue and one publisher. Leave the network zoo, approvals, and analytics on the skip row.

I would rebuild Buffer for the two accounts I already post to by hand, where the current “scheduler” is a notes file and a calendar alert. One channel row, encrypted tokens, a queue with dueAt, and a cron that claims then sends.

A second network comes after the first log stays clean for a week. A calendar can wait until I miss a drag. I would not build a first-comment composer or a reel uploader.

Host it next to the product, on Astro, Workers, and R2, because that is the stack on the card. Put images in R2 if the network needs a public URL, the way Instagram does. Use Rails if that is your weekday language.

If the rent is “which tweet became a customer”, that is DataFast, 10 hours for pageviews, UTM, and a payment webhook. If the rent is “did the launch page move”, that is Plausible.

Start with the first row

Go to Build your own Buffer. Copy the AHA prompt, or the stack you already write.

Tell the agent the first estimate only: post queue, one network, scheduled publish. Eight hours.

If the company already lives in ten channels, approvals, and a shared analytics board, keep paying.