<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Randall</title>
    <description>The latest articles on DEV Community by Randall (@randallkaire).</description>
    <link>https://dev.to/randallkaire</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4024237%2F9fc8165e-b069-49e0-a966-a4621a3cd13f.jpg</url>
      <title>DEV Community: Randall</title>
      <link>https://dev.to/randallkaire</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/randallkaire"/>
    <language>en</language>
    <item>
      <title>How I built one API to post to every major social network (and made it agent-friendly)</title>
      <dc:creator>Randall</dc:creator>
      <pubDate>Fri, 10 Jul 2026 15:40:24 +0000</pubDate>
      <link>https://dev.to/randallkaire/how-i-built-one-api-to-post-to-every-major-social-network-and-made-it-agent-friendly-4ifa</link>
      <guid>https://dev.to/randallkaire/how-i-built-one-api-to-post-to-every-major-social-network-and-made-it-agent-friendly-4ifa</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: nine platforms, nine completely different APIs
&lt;/h2&gt;

&lt;p&gt;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 &lt;em&gt;access gates&lt;/em&gt;. Here's the reality I mapped out:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Access&lt;/th&gt;
&lt;th&gt;The catch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;X / Twitter&lt;/td&gt;
&lt;td&gt;Open, but paid&lt;/td&gt;
&lt;td&gt;Pure pay-per-use — ~$0.01 per text post, more with a link. No free tier.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instagram&lt;/td&gt;
&lt;td&gt;App review req.&lt;/td&gt;
&lt;td&gt;Business accounts only; ~100 posts/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinkedIn&lt;/td&gt;
&lt;td&gt;Partner Program&lt;/td&gt;
&lt;td&gt;Strict review; messaging APIs are enterprise-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TikTok&lt;/td&gt;
&lt;td&gt;App review req.&lt;/td&gt;
&lt;td&gt;~15 posts/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facebook&lt;/td&gt;
&lt;td&gt;App review req.&lt;/td&gt;
&lt;td&gt;~25 posts/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Generous (~250/day)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bluesky&lt;/td&gt;
&lt;td&gt;Open&lt;/td&gt;
&lt;td&gt;The easiest one, by far&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YouTube&lt;/td&gt;
&lt;td&gt;Quota-based&lt;/td&gt;
&lt;td&gt;~6 uploads/day effective&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinterest&lt;/td&gt;
&lt;td&gt;App review req.&lt;/td&gt;
&lt;td&gt;Generous once approved&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;That's before you write a single line of &lt;em&gt;your own&lt;/em&gt; product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision 1: one normalized response
&lt;/h2&gt;

&lt;p&gt;The thing that makes multi-platform code miserable is branching. &lt;code&gt;if (platform === 'twitter') … else if (platform === 'instagram') …&lt;/code&gt; metastasizes fast.&lt;/p&gt;

&lt;p&gt;So the core contract is: &lt;strong&gt;one call, one response shape, every platform.&lt;/strong&gt; You post to many accounts at once and get back a single object — one post id, and a &lt;code&gt;targets&lt;/code&gt; array with one entry per account telling you exactly what happened and where:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post_a1b2c3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"targets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bluesky"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://bsky.app/…"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"threads"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://threads.net/…"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision 2: idempotency keys, because retries are inevitable
&lt;/h2&gt;

&lt;p&gt;Networks flake. Workers retry. The nightmare scenario for a posting API is a retry that double-posts to someone's real audience.&lt;/p&gt;

&lt;p&gt;Every write takes an &lt;code&gt;Idempotency-Key&lt;/code&gt;. Send the same key twice and the second call returns the original result instead of posting again. This turned out to matter &lt;em&gt;even more&lt;/em&gt; once AI agents entered the picture (more on that below) — an agent that retries on a timeout should never post twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision 3: managed keys, so you don't repeat my pain
&lt;/h2&gt;

&lt;p&gt;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.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I didn't expect: making it agent-first
&lt;/h2&gt;

&lt;p&gt;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 &lt;strong&gt;AI agent&lt;/strong&gt;. "Post this to LinkedIn and Bluesky, and schedule a recap for Friday" is a natural thing to &lt;em&gt;ask&lt;/em&gt; an assistant.&lt;/p&gt;

&lt;p&gt;So alongside the REST API there's a hosted &lt;strong&gt;MCP server&lt;/strong&gt; (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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell my past self
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The APIs aren't the hard part — the access is.&lt;/strong&gt; Budget most of your time for app review and auth, not for HTTP calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Normalize early.&lt;/strong&gt; The response shape is the most important design decision; everything downstream depends on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assume retries from day one.&lt;/strong&gt; Idempotency isn't a nice-to-have once anything automated is calling you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bluesky is a joy; X will bill you.&lt;/strong&gt; Plan accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;I turned this into &lt;a href="https://postlake.dev" rel="noopener noreferrer"&gt;PostLake&lt;/a&gt; — one API and a hosted MCP server for every major network, with a free tier if you want to poke at it. The &lt;a href="https://postlake.dev/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; have the full flow (create a key → connect an account → post), and if you're building agents, the &lt;a href="https://postlake.dev/mcp" rel="noopener noreferrer"&gt;MCP setup&lt;/a&gt; is a one-liner.&lt;/p&gt;

&lt;p&gt;Being straight with you on status: &lt;strong&gt;Bluesky and Threads are live today&lt;/strong&gt;; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>ai</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
