PostChart - Facebook page management SaaS case study
A Facebook page-management system built end to end for one client's pod-page operation — five hundred pages, one engineer, ten months.
- Role
- Solo — design, build, ship
- Timeline
- Ten months — 2025-26
- Sector
- Social media SaaS
- Stack
- React · Node · MongoDB · Redis
What it runs in production
- 27,736
- Uploads pushed through the scheduler
- 500
- Facebook pages under management
- 10K
- Posts scheduled a month
- 100+
- Operator accounts
Context
Where it started
Eric Bihr runs pod pages — Facebook pages that push hundreds of reaction-driven videos a month, with several operators working the same page on a rotation. When he found me the whole operation lived in a Google Drive folder, a spreadsheet of posting times, and a rotation only he could see. Every post was hand-placed: download the clip, open the page, write the caption, set the time, log it somewhere else.
| Requirement | Buffer | Meta Business Suite |
|---|---|---|
| Bulk upload straight from a Drive folder | No | No |
| Evergreen recycling on an interval | Partial | No |
| Per-operator attribution on a shared page | No | No |
| Collision locks inside one page | No | No |
| Cost at five hundred pages | Per page | Free, manual |
Nothing he had tried covered it. The tools that came closest price per page, and at five hundred pages that arithmetic ends the conversation on its own. He hired me off Upwork to build the one that would, and the brief was blunt: five hundred pages, several operators, no spreadsheet.
Approach
Two weeks before any code
I spent the first two weeks not writing code. I watched the actual workflow — where a clip came from, who touched it, what got typed twice — and the problem was never missing information. It was missing sequence. Every tool in the stack was fine on its own; the operation lost its hours in the seams between them.
The lifecycle one clip travels, end to end
- Drive folderwhere editors drop clips
- Bulk importthe whole folder, one screen
- Rulesspacing, windows, recycling
- Queueone slot per page per minute
- PublishedGraph API, on the minute
- Recycledevergreen, back in the pool
So the product target became a single sentence: an operator should take a folder of fifty clips and have them scheduled, spaced and attributed without opening a second tab. Everything below follows from that one line.
The system
What shipped
PostChart is built around the folder, not the post. The screen that matters takes a Drive folder and turns it into a schedule; a single-post composer exists, but it is the exception path rather than the main one. Three decisions carried most of the weight.
Bulk-first
instead of a composer per post
An operator arrives with fifty clips, not one. The primary screen takes the whole folder, applies a rule and shows the resulting calendar before anything is committed.
Rules, not timestamps
instead of picking a time per post
Nobody wants to choose four hundred times. Spacing, daily windows and a recycling interval are set once; the system derives every slot and shows you what it produced.
Roles on the page
instead of one shared login
Pod pages are worked by several operators. Permissions attach to the page, so a post carries the name of whoever queued it and attribution survives the rotation.

The scheduler
Redis, not a queue
Underneath, the scheduler is one Redis sorted set per page. Scores are epoch milliseconds; a poller pops everything past its score once a second and hands it to the Graph API. MongoDB stays the source of truth — Redis only ever holds what is about to happen.
One post, from committed slot to published
- MongoDBsource of truth
- Redis ZSETscore = epoch ms
- Pollerpops due, every second
- Graph APIpublishes to the page
- MongoDBresult written back
I rejected BullMQ for this. Eric pays for RAM by the gigabyte, and a million scheduled posts fit in roughly fifty megabytes of sorted set — a fraction of what a general-purpose queue carries per job. The queue only has to answer one question, what is due, and a sorted set answers it in log time. The collision rule lives at the same layer: one page cannot take two posts inside the same minute boundary, so operators dump thousands of slots without checking each other's work.
The wall
What Meta wouldn't allow
The most expensive feature I built never shipped. Story uploads worked end to end in testing and died in production — the endpoint burns a quota scoped to the whole app rather than the page, so one operator's burst starves every other page on the platform. No plan tier raises it. The ceiling was Meta's, not ours.
- Built
- Story uploads, end to end
- Blocked by
- App-level rate quota
- Shipped
- Never
So I ripped it out and told the client it was not coming back. A narrow browser-automation version surfaced months later and I turned it down — it would have worked for a handful of power users and broken the moment the fleet grew. Saying that plainly, with the reasoning, is part of the job.
Billing
Making it a business
Then the parts nobody demos. Stripe Checkout with webhooks and a subscription state that survives whatever Stripe decides to send. The Meta app review: screen recordings, use-case justifications, one rejection, a resubmission with a tighter narrative, approved. Small shops do not have that level of API access, and getting it is slow, unglamorous work.
I also walked Eric through Google Cloud credentials, his own Facebook developer account and the live Stripe keys. The product is his, in his name, on his accounts — which is the part of delivering a SaaS nobody mentions until you have shipped one.
The handoff
Leaving well
Ten months in, Eric brought on a junior developer to keep the monthly burn manageable. I reviewed his pull requests for a month and walked him through the scheduler and the non-obvious corners of the Facebook integration — the things that would have cost him weeks to find alone.
“If you have a chance to work with him, I would absolutely pick him over anybody else.”
Eric BihrFounder — PostChartTwo days after my last commit the scheduler broke. I took an hour-long call, screen-shared with the junior, narrowed it to his regression and we fixed it. I did not bill for the hour. Leaving well is worth more than the invoice.
Today
Still running
PostChart is live at postchart.com and still ingesting — the newest upload in its bucket landed days before this page was written. The product has since grown YouTube support; that part is not my build. The Facebook core is: the scheduler, the recycling, the roles, the billing.
- Delivered
- Full-stack app — React, Node, MongoDB, Redis
- Delivered
- Custom scheduler and collision engine
- Delivered
- Stripe billing, Meta review, handover
