Zernio
Zernio
QuickstartBuild a PlatformSDKsCLIMCPWebhooksWorkflowsGuidesSecurityGlossaryPricingBillingChangelogRefer & earn
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources

Quickstart

Create an API key, connect an account and schedule your first post with 5 calls to the Zernio API.


When you finish this page you have a post scheduled on a connected account, created with 5 API calls. You need a Zernio account and a login for one of the 16 platforms. The first 2 connected accounts are free without a card, except X (platform value twitter), which needs one because X bills every API call (pricing).

If your users connect their own accounts, the calls are the same with one profile per user: read Build a platform after your first post.

Base URL: https://zernio.com/api/v1

Step 1: Create an API key

  1. Sign up or log in.
  2. Open API keys and click Create API key.
  3. Copy the key now. Zernio stores a SHA-256 hash of it and never shows it again.

A key is sk_ followed by 64 hex characters (67 in total). Every request sends it in the Authorization: Bearer header, and the SDKs read it from the ZERNIO_API_KEY environment variable:

npm install @zernio/node
export ZERNIO_API_KEY="sk_..."
import Zernio from '@zernio/node';

const zernio = new Zernio(); // reads ZERNIO_API_KEY

Create one key per app and rotate keys through the API. Go, Ruby, Java, PHP, .NET and Rust SDKs are on the SDKs page.

Step 2: Create a profile

Call POST /v1/profiles with a name. A profile groups accounts: one per brand, or one per user of your app. Every id is a 24-character string in an _id field, and each step returns the id the next one needs.

const { data: created } = await zernio.profiles.createProfile({
  body: { name: 'My first profile' }
});

const profileId = created.profile._id;

Response (201):

{
  "message": "Profile created successfully",
  "profile": {
    "_id": "66a1f0c2a4b9d3e8f1a2b3c4",
    "name": "My first profile"
  }
}

profile._id is the profileId for Step 3.

Step 3: Connect an account

Call GET /v1/connect/{platform} with profileId. The response is the URL where your user authorizes Zernio. This example connects LinkedIn.

const { data: connect } = await zernio.connect.getConnectUrl({
  path: { platform: 'linkedin' },
  query: { profileId }
});

console.log(connect.authUrl);

Response (200):

{
  "authUrl": "https://www.linkedin.com/oauth/v2/authorization?client_id=..."
}

Open authUrl and approve. LinkedIn asks whether to connect your personal profile or an organization, on a screen Zernio hosts, then sends the browser back with the account connected. Pass redirect_url on the same call to land on your own URL; Zernio appends connected=linkedin&profileId=...&accountId=...&username=....

Replace linkedin with any value below:

PlatformAPI ValueGuide
XtwitterX Guide
InstagraminstagramInstagram Guide
FacebookfacebookFacebook Guide
LinkedInlinkedinLinkedIn Guide
TikToktiktokTikTok Guide
YouTubeyoutubeYouTube Guide
PinterestpinterestPinterest Guide
RedditredditReddit Guide
BlueskyblueskyBluesky Guide
ThreadsthreadsThreads Guide
Google Business ProfilegooglebusinessGoogle Business Profile Guide
TelegramtelegramTelegram Guide
SnapchatClosed betasnapchatSnapchat Guide
WhatsAppwhatsappWhatsApp Guide
DiscorddiscordDiscord Guide
SlackslackSlack Guide
ShopifyshopifyShopify Guide

Facebook, Pinterest, Google Business Profile and Snapchat add a similar selection step (Page, board, location or public profile). The connecting accounts guide covers them, plus Bluesky and Telegram, which do not use OAuth.

Step 4: Get the account id

Call GET /v1/accounts. Posts target accounts by _id, not by username.

const { data: listed } = await zernio.accounts.listAccounts();

const accountId = listed.accounts[0]._id;

Response (200):

{
  "accounts": [
    {
      "_id": "66b2e19d8c3f5a7e9d0b1c2d",
      "platform": "linkedin",
      "username": "acme",
      "isActive": true
    }
  ]
}

accounts[].platform and accounts[]._id are the platform and accountId for Step 5.

Step 5: Schedule a post

Call POST /v1/posts with content, scheduledFor, timezone and one platforms entry. scheduledFor is read in the timezone you send.

const { data: scheduled } = await zernio.posts.createPost({
  body: {
    content: 'Hello world. This is my first post from the Zernio API.',
    scheduledFor: '2027-01-01T12:00:00',
    timezone: 'America/New_York',
    platforms: [
      { platform: 'linkedin', accountId }
    ]
  }
});

const postId = scheduled.post._id;

Response (201):

{
  "message": "Post scheduled successfully",
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "status": "scheduled",
    "scheduledFor": "2027-01-01T17:00:00Z",
    "platforms": [
      { "platform": "linkedin", "status": "pending" }
    ]
  }
}

The same body covers every posting mode:

You setResult
scheduledFor and timezonePublished at that time
publishNow: truePublished immediately
NeitherSaved as a draft

To post to several accounts, add entries to platforms. Per-platform options go in each entry's platformSpecificData (platform pages).

Step 6: Check the status

Call GET /v1/posts/{postId}. status is scheduled until the scheduled time, then publishing, then published, or failed or partial when a platform rejects it (error handling).

const { data: fetched } = await zernio.posts.getPost({
  path: { postId }
});

console.log(fetched.post.status);

Response (200):

{
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "status": "scheduled",
    "scheduledFor": "2027-01-01T17:00:00Z",
    "platforms": [
      { "platform": "linkedin", "status": "pending" }
    ]
  }
}

Once published, each platforms[] entry carries platformPostUrl, the live link. Webhooks push each status change to you instead of polling.

If it fails

A 409 means the same content is already scheduled or was posted to this account in the last 24 hours, as when you run Step 5 twice:

{
  "error": "This exact content is already scheduled, publishing, or was posted to this account within the last 24 hours.",
  "details": {
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "platform": "linkedin",
    "existingPostId": "65f1c0a9e2b5af0012ab34cd"
  }
}

Change the content, or send an x-request-id header so a retry returns the original post instead (idempotency). Every error uses the same envelope, described in error handling.

Related

The same key serves the inbox, WhatsApp, phone numbers, SMS and voice, ads, comment-to-DM automations, broadcasts, workflows and analytics.

  • Media uploads: images and videos in a post.
  • Queue scheduling: recurring time slots, so posts need no scheduledFor.
  • Build a platform: one profile per user, scoped keys, webhook routing.
  • Rate limits, idempotency and error handling before production.
  • CLI: the same API from a terminal.
Was this page helpful?

Build a Platform

Give each of your customers a profile, connect their accounts into it, post on their behalf and route webhooks back to the right customer.

On this page

Step 1: Create an API keyStep 2: Create a profileStep 3: Connect an accountStep 4: Get the account idStep 5: Schedule a postStep 6: Check the statusIf it failsRelated