I wanted one thing: publish a post to every social network from a single call. It turned into a much bigger rabbit hole than I expected — and taught me more about the major platform APIs than I ever wanted to know.
This is the honest version of what I found, and the decisions that fell out of it. If you've ever thought "how hard can multi-platform posting be," this is for you.
The problem: nine platforms, nine completely different APIs
There's no shared standard for "post some text and an image." Each platform has its own auth, its own formats, its own limits, and — the part nobody warns you about — its own access gates. Here's the reality I mapped out:
| Platform | Access | The catch |
|---|---|---|
| X / Twitter | Open, but paid | Pure pay-per-use — ~$0.01 per text post, more with a link. No free tier. |
| App review req. | Business accounts only; ~100 posts/day | |
| Partner Program | Strict review; messaging APIs are enterprise-only | |
| TikTok | App review req. | ~15 posts/day |
| App review req. | ~25 posts/day | |
| Threads | Free | Generous (~250/day) |
| Bluesky | Open | The easiest one, by far |
| YouTube | Quota-based | ~6 uploads/day effective |
| App review req. | Generous once approved |
So "just call nine APIs" actually means: register nine developer apps, pass several rounds of platform app review, handle nine auth flows, learn nine media-upload dances, and — for X — wire up billing, because X literally charges per post.
That's before you write a single line of your own product.
Decision 1: one normalized response
The thing that makes multi-platform code miserable is branching. if (platform === 'twitter') … else if (platform === 'instagram') … metastasizes fast.
So the core contract is: one call, one response shape, every platform. You post to many accounts at once and get back a single object — one post id, and a targets array with one entry per account telling you exactly what happened and where:
{
"id": "post_a1b2c3",
"state": "published",
"targets": [
{ "platform": "bluesky", "state": "published", "url": "https://bsky.app/…" },
{ "platform": "threads", "state": "published", "url": "https://threads.net/…" }
]
}
One target can fail while the others succeed — over a character limit, say — and you see that per-target instead of the whole call blowing up. Your code never branches per platform.
Decision 2: idempotency keys, because retries are inevitable
Networks flake. Workers retry. The nightmare scenario for a posting API is a retry that double-posts to someone's real audience.
Every write takes an Idempotency-Key. Send the same key twice and the second call returns the original result instead of posting again. This turned out to matter even more once AI agents entered the picture (more on that below) — an agent that retries on a timeout should never post twice.
Decision 3: managed keys, so you don't repeat my pain
Remember those nine developer apps and app-review queues? I did that work so you don't have to. The platform credentials are managed centrally, so you connect an account with OAuth and post — no per-platform app registration, no review gates to babysit. (The one exception is X, where the per-post cost is baked into pricing rather than hidden.)
The part I didn't expect: making it agent-first
Halfway through, it became obvious that the biggest near-term user of "post to everywhere with one call" isn't a human clicking buttons — it's an AI agent. "Post this to LinkedIn and Bluesky, and schedule a recap for Friday" is a natural thing to ask an assistant.
So alongside the REST API there's a hosted MCP server (Model Context Protocol — the open standard that lets agents call external tools). Point Claude, ChatGPT, Cursor, or any MCP client at it, approve once over OAuth, and the agent gets a full toolset — publish, schedule, upload media, read each platform's real limits, pull analytics — all speaking that same normalized response it can reason over. No API key pasted into a chat.
This is where the earlier decisions pay off twice: idempotency means an agent's retry is safe, and the normalized response means the model doesn't need per-platform special-casing to understand what happened.
What I'd tell my past self
- The APIs aren't the hard part — the access is. Budget most of your time for app review and auth, not for HTTP calls.
- Normalize early. The response shape is the most important design decision; everything downstream depends on it.
- Assume retries from day one. Idempotency isn't a nice-to-have once anything automated is calling you.
- Bluesky is a joy; X will bill you. Plan accordingly.
Try it
I turned this into PostLake — one API and a hosted MCP server for every major network, with a free tier if you want to poke at it. The docs have the full flow (create a key → connect an account → post), and if you're building agents, the MCP setup is a one-liner.
Being straight with you on status: Bluesky and Threads are live today; the rest are rolling out as each clears the platform-review gauntlet I mentioned above. So it's early — but the integration for all of them is built, and you can watch them flip to live.
If you've fought any of these platform APIs yourself, I'd genuinely love to hear which one broke you — reply and let's compare scars.
Top comments (0)