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
- Sign up or log in.
- Open API keys and click Create API key.
- 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_KEYCreate 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:
| Platform | API Value | Guide |
|---|---|---|
| X | twitter | X Guide |
instagram | Instagram Guide | |
facebook | Facebook Guide | |
linkedin | LinkedIn Guide | |
| TikTok | tiktok | TikTok Guide |
| YouTube | youtube | YouTube Guide |
pinterest | Pinterest Guide | |
reddit | Reddit Guide | |
| Bluesky | bluesky | Bluesky Guide |
| Threads | threads | Threads Guide |
| Google Business Profile | googlebusiness | Google Business Profile Guide |
| Telegram | telegram | Telegram Guide |
| SnapchatClosed beta | snapchat | Snapchat Guide |
whatsapp | WhatsApp Guide | |
| Discord | discord | Discord Guide |
| Slack | slack | Slack Guide |
| Shopify | shopify | Shopify 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 set | Result |
|---|---|
scheduledFor and timezone | Published at that time |
publishNow: true | Published immediately |
| Neither | Saved 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.