API and developers

Social Media API in JavaScript: A 2026 Guide

Learn how to publish social media posts from JavaScript using the OmniSocials API and its Node.js SDK. One client, 13 platforms, no OAuth headaches.

Robert LigthartFounder of OmniSocialsPublished Updated 12 min read
Social Media API in JavaScript: A 2026 Guide

Integrating social media publishing into a JavaScript application sounds straightforward until you actually try it. Each platform has its own OAuth flow, its own rate limits, its own media format requirements, and its own app review process before you can even go live.

On this page
  1. What Is the OmniSocials API?
  2. Step 1: Get Your API Key
  3. Step 2: Make Your First API Call
  4. Step 3: Schedule or Publish Now
  5. Step 4: Post with an Image
  6. Step 5: Handle the Response Properly
  7. Without the SDK: Plain fetch()
  8. The Python Equivalent
  9. Why Not Use Each Platform's Native API?
  10. Common Pitfalls
  11. Endpoint Reference
  12. Frequently Asked Questions
  13. Sources

Instagram requires you to submit your app for Business Verification. TikTok's Content Posting API requires a separate application approval. LinkedIn's API scopes changed twice in 2024. And you need a different integration for each one.

The OmniSocials API sidesteps all of that. One API, one API key, 13 platforms. This tutorial walks you through posting to social media from JavaScript in about 15 minutes, using the official OmniSocials Node.js SDK.


What Is the OmniSocials API?

The OmniSocials API is a unified social media publishing API. You connect your social accounts once through the OmniSocials dashboard, and from that point forward your JavaScript code talks to one base URL: https://api.omnisocials.com/v1.

No per-platform OAuth. No separate SDK per network. No app review required per platform. You create one API key in your dashboard and you're ready to publish. On top of the REST API there's an official TypeScript SDK, @omnisocials/sdk, with typed responses, automatic retries, and webhook verification built in.

The API costs $10/mo (billed annually) as part of the standard OmniSocials plan. That includes API access, the scheduling dashboard, unified inbox, and analytics. Compare that to Ayrshare, which charges $49/mo for API-only access to fewer platforms.


Step 1: Get Your API Key

Sign up at omnisocials.com and start a 14-day free trial.

Once you're in the dashboard:

  1. Connect your social accounts. You only do this once, and every API call can target whichever platforms you've connected.
  2. Go to Settings > API
  3. Click Create API Key, pick the scopes it needs (for example posts:write and media:write), and save
  4. Copy the key. It starts with omsk_live_ and is shown only once.

Important: Never put your API key in client-side JavaScript. All calls must go through a Node.js server, a serverless function, or a backend script. The key can post on your behalf.

Store it as an environment variable. The SDK reads OMNISOCIALS_API_KEY automatically:

# .env
OMNISOCIALS_API_KEY=omsk_live_your_key_here

There are no test keys, so every key posts to your real accounts. While you experiment, create drafts (leave out scheduled_at) and nothing goes out.


Step 2: Make Your First API Call

Install the SDK. It needs Node.js 18 or newer and ships both ESM and CommonJS builds:

npm install @omnisocials/sdk

Here's the core request. This schedules one text post to LinkedIn, Bluesky, and Threads:

// post.ts
import { OmniSocials } from "@omnisocials/sdk";

const client = new OmniSocials(); // reads OMNISOCIALS_API_KEY

const { data: post } = await client.posts.create({
  content: "Just shipped our new feature. Check it out at omnisocials.com.",
  channels: ["linkedin", "bluesky", "threads"],
  scheduled_at: "2027-03-02T09:00:00Z",
});

console.log(post.id, post.status); // "1019" "scheduled"

That's the whole thing. One call, three platforms. Every response is wrapped in a data key, and in TypeScript post is fully typed, so your editor knows post.status, post.published_urls, and the rest.

Instagram and TikTok always need an image or video. If you add them to a text-only post, the API answers 400, so attach media for those two (see Step 4).


Step 3: Schedule or Publish Now

scheduled_at takes an ISO 8601 timestamp, and it has to be in the future (a time in the past returns a 400):

const tomorrow9am = new Date();
tomorrow9am.setUTCDate(tomorrow9am.getUTCDate() + 1);
tomorrow9am.setUTCHours(9, 0, 0, 0);

const { data: post } = await client.posts.create({
  content: "Good morning from our automated pipeline.",
  channels: ["facebook", "threads", "linkedin"],
  scheduled_at: tomorrow9am.toISOString(),
});

console.log(post.status); // "scheduled"

Leave out scheduled_at and the post is saved as a draft, not published. To publish right away, use createAndPublish:

const { data: post } = await client.posts.createAndPublish({
  content: "We're live. The new docs are up.",
  channels: ["linkedin", "bluesky"],
});

console.log(post.status); // "posting"

The post starts as posting and moves to published (or failed) once each platform has answered.


Step 4: Post with an Image

The shortest route is media_urls. OmniSocials downloads the file from a public URL and stores it with the post:

await client.posts.create({
  content: "We just launched. Here is what we built.",
  channels: ["instagram", "linkedin", "x"],
  media_urls: ["https://your-cdn.com/launch-banner.jpg"],
  scheduled_at: "2027-03-02T09:00:00Z",
});

For a local file, upload it to your media library first and pass the id:

const { data: image } = await client.media.upload({
  file: "./launch-banner.jpg", // a path, Buffer, or Blob
});

const { data: post } = await client.posts.create({
  content: "We just launched. Here is what we built.",
  channels: ["instagram", "linkedin", "x"],
  media_ids: [image.id],
  scheduled_at: "2027-03-02T09:00:00Z",
});

Direct uploads go up to 100 MB. For bigger videos, use client.media.uploadFromUrl({ url }), which takes files up to 1 GB.

Media rules still come from each platform. OmniSocials checks your file against every connected platform and tells you in the upload response (compatibility) which ones would reject it. For video, upload a version that fits the strictest platform you post to (for example a vertical 9:16 MP4 for Reels, TikTok, and Shorts).


Step 5: Handle the Response Properly

Successful calls return the post in data. Here is a trimmed example after a post has published:

{
  "data": {
    "id": "1019",
    "status": "warning",
    "type": "post",
    "content": { "default": "Weekly update is live." },
    "schedule_at": "2027-03-02T09:00:00.000Z",
    "published_urls": {
      "linkedin": "https://www.linkedin.com/feed/update/urn:li:share:...",
      "mastodon": "https://mastodon.social/@you/..."
    },
    "errors": {
      "bluesky": "Bluesky session expired. Reconnect the account."
    },
    "app_url": "https://app.omnisocials.com/posts/1019"
  }
}

Failed calls throw. Each HTTP error has its own class, with status, code, and message on it:

{
  "error": {
    "code": "invalid_api_key",
    "message": "Invalid or expired API key."
  }
}

A post can succeed on some platforms and fail on others. That's the warning status: published_urls lists where it went live, and errors explains each platform that failed. Always check errors, not just the top-level status.

Here's a reusable helper that wraps this cleanly:

// lib/omnisocials.ts
import {
  OmniSocials,
  RateLimitError,
  ValidationError,
} from "@omnisocials/sdk";

// Retries 429s, 5xx and network errors; 2 retries by default.
const client = new OmniSocials({ maxRetries: 3 });

export async function schedulePost(
  content: string,
  channels: string[],
  scheduledAt: string,
) {
  try {
    const { data } = await client.posts.create({
      content,
      channels,
      scheduled_at: scheduledAt,
    });
    return data;
  } catch (err) {
    if (err instanceof ValidationError) {
      // e.g. "platform_not_connected" or a caption that is too long
      console.error("Rejected:", err.code, err.message);
    } else if (err instanceof RateLimitError) {
      console.error(`Still rate limited, retry in ${err.retryAfter}s`);
    }
    throw err;
  }
}

And to check on a post later:

const { data: post } = await client.posts.get("1019");

if (post.errors) {
  console.warn("Failed on:", Object.keys(post.errors).join(", "));
}

Without the SDK: Plain fetch()

The SDK is a thin layer over a REST API, so fetch() works too (built into Node.js 18+, Deno, Bun, and Cloudflare Workers):

const response = await fetch("https://api.omnisocials.com/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.OMNISOCIALS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Weekly update is live.",
    channels: ["linkedin", "bluesky", "mastodon"],
    scheduled_at: "2027-03-05T16:00:00Z",
  }),
});

const json = await response.json();
if (!response.ok) throw new Error(json.error?.message);

console.log("Scheduled:", json.data.id);

You give up typed responses and the automatic retries, so the SDK is the better default for anything you'll run more than once.


The Python Equivalent

For teams running Python alongside Node.js, there's an official Python SDK with the same methods (pip install omnisocials):

from omnisocials import OmniSocials

client = OmniSocials()  # reads OMNISOCIALS_API_KEY

post = client.posts.create(
    content="Just shipped our new feature.",
    channels=["linkedin", "bluesky", "threads"],
    scheduled_at="2027-03-02T09:00:00Z",
)

print(post["data"]["id"], post["data"]["status"])

The full Python tutorial is in our guide to using the social media API with Python. Official SDKs also exist for Go, Rust, .NET, Java, PHP, and Ruby; see the SDK overview.


Why Not Use Each Platform's Native API?

Here's what a basic Instagram post actually requires without OmniSocials:

  1. Register a Meta Developer App and go through Business Verification
  2. Implement OAuth 2.0 to get a User Access Token
  3. Exchange it for a Page Access Token
  4. Call the Instagram Graph API /media endpoint to create a container
  5. Call the /media_publish endpoint to publish it
  6. Repeat steps 2-5 for every other platform, each with a different OAuth flow

That's 3 separate API calls for Instagram alone, plus an app review process that takes anywhere from 2 days to several weeks.

OmniSocials replaces all of that with the single client.posts.create() call (or POST /v1/posts) shown above. You connect accounts once through the dashboard, and the API handles everything underneath.


Common Pitfalls

Putting the API key in the browser. Your API key can post on your behalf. It must live in a server-side environment variable, never in client-side code or a public repo. Use .env locally and your hosting provider's secret management in production.

Expecting a post without scheduled_at to go live. It won't. That's a draft. Use createAndPublish() (or publish_now: true in a raw request) to publish immediately.

Ignoring per-platform results. A 201 on create only means the post was accepted. A platform can still fail at publish time (disconnected account, expired token, a platform-side rule). Check errors on the post, or subscribe to the post.failed webhook.

Sending an unsupported file type. OmniSocials accepts JPEG, PNG, GIF, WebP, HEIC (converted to JPEG on upload), MP4, MOV, AVI, and PDF. Anything else, AVIF for example, returns invalid_file_type.

Not handling rate limits. The OmniSocials API allows 100 requests per minute per API key. The SDK retries a 429 for you and waits for the Retry-After header. If you call the REST API directly, read that header yourself before retrying.

Hardcoding the wrong platform strings. Channel identifiers are lowercase: instagram, facebook, linkedin, linkedin_page, x, tiktok, youtube, pinterest, bluesky, threads, mastodon, google_business. Passing Instagram or twitter returns a validation error.


Endpoint Reference

EndpointMethodSDK methodDescription
Endpoint: /v1/postsMethod: POSTSDK method: posts.create()Description: Create a draft or scheduled post
Endpoint: /v1/posts/create-and-publishMethod: POSTSDK method: posts.createAndPublish()Description: Create a post and publish it now
Endpoint: /v1/postsMethod: GETSDK method: posts.list()Description: List posts, filter by status
Endpoint: /v1/posts/:idMethod: GETSDK method: posts.get()Description: Get a single post with per-platform URLs and errors
Endpoint: /v1/posts/:idMethod: DELETESDK method: posts.delete()Description: Remove a post from OmniSocials (a live post stays live on the platform)
Endpoint: /v1/media/uploadMethod: POSTSDK method: media.upload()Description: Upload a file up to 100 MB
Endpoint: /v1/media/upload-from-urlMethod: POSTSDK method: media.uploadFromUrl()Description: Import a file from a URL, up to 1 GB
Endpoint: /v1/accountsMethod: GETSDK method: accounts.list()Description: List connected social accounts
Endpoint: /v1/analytics/posts/:idMethod: GETSDK method: analytics.post()Description: Metrics for one post, per platform
Endpoint: /v1/analytics/overviewMethod: GETSDK method: analytics.overview()Description: Workspace totals for a period
Endpoint: /v1/inbox/conversationsMethod: GETSDK method: inbox.listConversations()Description: Comments and messages from connected accounts

The full reference is at docs.omnisocials.com.


Frequently Asked Questions

Can I use the OmniSocials API without a backend server?

No. You must never expose your API key in client-side JavaScript. Always make OmniSocials API calls from a Node.js backend, a serverless function (Vercel, Cloudflare Workers, AWS Lambda), or a scheduled server-side script. Exposing the key in the browser lets anyone post on your behalf.

Does the OmniSocials API support posting images and videos with JavaScript?

Yes. Pass public file URLs in media_urls when you create the post, or upload first with client.media.upload() (up to 100 MB) or client.media.uploadFromUrl() (up to 1 GB) and pass the returned id in media_ids.

How do I schedule a post at a future time using the OmniSocials API?

Add a scheduled_at field with a future ISO 8601 timestamp, for example '2027-03-02T09:00:00Z'. The post comes back with status "scheduled". Without scheduled_at the post is saved as a draft; to publish immediately, use client.posts.createAndPublish().

What platforms does the OmniSocials API support?

OmniSocials supports 13 platforms: Instagram, Facebook, LinkedIn, YouTube, TikTok, X (Twitter), Pinterest, Bluesky, Threads, Mastodon, Google Business Profile, Telegram, and Discord. Pass any combination of connected channels in the channels array of your request.

How much does the OmniSocials API cost?

API access is included in the standard OmniSocials plan at $10/mo (annual). There is no separate API tier. The rate limit is 100 requests per minute per API key, which covers most automation use cases comfortably.


Start posting from JavaScript in minutes at omnisocials.com. The 14-day trial is free.


Sources

Written byRobert LigthartFounder of OmniSocials

Try OmniSocials for 14 days

Plan, schedule and answer on 13 platforms from one calendar, or let your AI agent do it. The first charge comes when the trial ends.