Zernio
Zernio
API Reference

Posts

List postsGETCreate postPOSTGet postGETUpdate post metadataPOSTUpdate postPUTDelete postDELETEUnpublish postPOSTBulk upload from CSVPOSTRetry failed postPOSTEdit published postPOST
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources
Posts

Create post

Create a post, and optionally publish it in the same request. A post published immediately (publishNow: true) comes back with platformPostUrl in the response.

content is optional in four cases:

  • media is attached
  • all platforms have customContent
  • every platform entry is an X Article (platformSpecificData.article)
  • every platform entry is a LinkedIn text-free reshare (platformSpecificData.reshareUrl with no text)

See each platform's schema for media constraints.

Scheduling

Pick one of:

  • scheduledFor: publish at the scheduled time
  • publishNow: true: publish synchronously, inside this request
  • queuedFromProfile: publish in the profile's next queue slot

With none of them and isDraft unset, the post is saved as a draft. platforms is required unless the post is a draft.

Precedence: isDraft: true wins over publishNow and scheduledFor (the post is saved, never published), and publishNow: true wins over scheduledFor. A scheduledFor already in the past is not rejected: the post is published synchronously in the same request, exactly like publishNow.

Idempotency

Two layers of duplicate-protection apply, so safe-to-retry callers (network blips, n8n / Zapier retries, etc.) don't accidentally double-post.

1. Same-request idempotency (5-minute window). Pass an x-request-id header to mark a logical request. If a second request arrives with the same x-request-id while the first is in-flight (or within ~5 minutes of completion), we return HTTP 200 with the original post in the existingPost field, and no new post is created.

The official Zernio SDKs auto-generate a unique x-request-id per call. On a generic HTTP client (curl, n8n's HTTP node, Zapier, custom code), either:

  • Set a unique x-request-id per logical call (recommended, UUIDv4 is fine)
  • Or omit the header, and we'll treat each request as new

Common pitfall: if your workflow tool uses a single execution-level request ID and reuses it across multiple HTTP nodes (e.g. one ID for the whole run, shared across 6 different platform calls), every call after the first will look like a retry of the first and return its post. Generate a fresh ID per node.

2. Content-hash dedup (24-hour window). Independently, we hash (platform, accountId, content + media URLs) and reject duplicates within 24 hours with HTTP 409. This catches genuine "same content posted twice to the same account" cases regardless of x-request-id. The response carries error, accountId, platform, and existingPostId so you can find the original.

To intentionally re-post identical content within 24h, change something (the caption, the media, the account), because the dedup is keyed on the full content fingerprint.

Order: same-x-request-id retries (200) are checked first; if no idempotency match, the content-hash dedup (409) runs.


POST
/v1/posts

Authorization

bearerAuth
AuthorizationBearer <token>

API key authentication: send your Zernio API key in the Authorization header, prefixed with Bearer.

In: header

Header Parameters

x-request-id?string

Optional client-generated request identifier for safe retry (idempotency). When two requests carry the same value, the second is treated as a retry of the first and returns the original post (HTTP 200) instead of creating a duplicate. Window is ~5 minutes from the first request. Generate a UUID per logical call. SDKs do this automatically; HTTP clients should set it themselves or omit it. See the operation description for the full idempotency contract.

Formatuuid

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

{  "dryRun": true,  "canPublish": true,  "tiktok": [    {      "accountId": "string",      "canPublish": true,      "alreadyPostedToday": true,      "reason": "string"    }  ]}
Was this page helpful?

List posts

Returns a paginated list of posts. Published posts include platformPostUrl with the public URL on each platform.

Get post

Fetch a single post by ID. For published posts, this returns platformPostUrl for each platform.

title?string

Stored on the post for reference/display only. This field is NOT used as the video title when publishing. To set a YouTube video title, use platformSpecificData.title on the youtube platform target (falls back to the first line of content when omitted).

content?string

Post caption/text. Optional when media is attached, all platforms have customContent, every platform entry is an X Article (platformSpecificData.article), or every platform entry is a LinkedIn text-free reshare (platformSpecificData.reshareUrl with no text). Required for other text-only posts.

mediaItems?array<>

Media attached to every platform in the request (a platform entry can override it with customMedia). Each entry needs a publicly reachable HTTPS url; type (image, video, gif, document) is inferred from the URL extension when omitted and a type that contradicts the extension is rejected with 400. Upload files with POST /v1/media/presign first; per-platform size, duration and format limits are listed on each platform schema.

platforms?array<>

Target platforms and accounts for this post. Required for non-draft posts (returns 400 if empty). Drafts can omit platforms.

scheduledFor?string

When to publish. Required unless publishNow is true, queuedFromProfile is set, or the post is a draft. An ISO 8601 value with a Z or offset (2026-01-15T10:00:00Z, 2026-01-15T11:00:00+01:00) is taken as-is; a value without one (2026-01-15T10:00:00 or 2026-01-15 10:00) is read as local time in timezone. A value already in the past is published synchronously in the same request. Ignored when publishNow is true.

Formatdate-time
publishNow?boolean

Publish to every platform synchronously in this request instead of scheduling; the response then carries each platform result and platformPostUrl, with HTTP 207 when some platforms failed. Takes precedence over scheduledFor; ignored when isDraft is true.

Defaultfalse
isDraft?boolean

When true, saves the post as a draft. When none of scheduledFor, publishNow, or queuedFromProfile are provided, the post defaults to draft automatically.

Defaultfalse
dryRun?boolean

TikTok only. Preview whether each tiktok entry in platforms could publish right now under the TikTok Direct Post daily limits, without creating, scheduling or publishing anything: no post is persisted and no upload slot is claimed, so it can be repeated freely. The request still goes through auth, the payment gate and body validation, then returns HTTP 200 with { dryRun: true, canPublish, tiktok: [...] } instead of 201. Only tiktok entries are evaluated; other platforms in the body are ignored, and a body with no tiktok entry is rejected with 400 invalid_field_value on platforms. An entry with platformSpecificData.tiktokSettings.draft: true (Creator Inbox upload) is not subject to the limit and always reports canPublish: true. Accounts connected through the TikTok for Business app do not go through these limits at all and also always report canPublish: true, so on those accounts a dry run confirms the request is well-formed rather than gating it.

Defaultfalse
timezone?string

IANA timezone (Europe/Madrid, America/New_York) used to interpret a scheduledFor (root or per-platform) that carries no Z or offset. Has no effect on values that already carry one. An unknown name returns 400 when scheduledFor is set.

Default"UTC"
tags?array<string>

Tags/keywords. YouTube constraints: each tag max 100 chars, combined max 500 chars, duplicates auto-removed.

hashtags?array<string>

Stored for reference only. Hashtags are NOT automatically appended to the caption when publishing. Include hashtags directly in the content field (platforms like Instagram only support hashtags as caption text). For YouTube keywords, use the tags field instead.

mentions?array<string>

Stored for reference only. This field does NOT automatically create @mentions when publishing. For LinkedIn @mentions, use the /v1/accounts/{accountId}/linkedin-mentions endpoint to resolve profile URLs to URNs, then embed the returned mentionFormat directly in the post content field.

crosspostingEnabled?boolean

Stored on the post and echoed back on reads. Publishing does not branch on it: every entry in platforms is published regardless, so treat it as a label for your own tooling.

Defaulttrue
metadata?

Free-form key/value pairs of your own, stored on the post and returned on reads and in webhook payloads. Zernio also writes the bookkeeping keys usageCounted, usageRefunded and hidden into this object; do not set them, and they are stripped from webhook payloads.

tiktokSettings?

Root-level TikTok settings applied to the TikTok platforms sent in the same request. Merged into each platform's platformSpecificData, with platform-specific settings taking precedence.

facebookSettings?

Facebook options that must be nested under platformSpecificData.facebookSettings, or sent at the request root as facebookSettings. The remaining Facebook options sit directly on platformSpecificData, see FacebookPlatformData.

recycling?

Configure automatic post recycling (reposting at regular intervals). After the post is published, the system creates new scheduled copies at the specified interval until expiration conditions are met. Supports weekly or monthly intervals. Maximum 10 active recycling posts per account. YouTube and TikTok platforms are excluded from recycling. Content variations are recommended for X and Pinterest to avoid duplicate flags.

queuedFromProfile?string

Profile ID to schedule via queue. When provided without scheduledFor, the post is auto-assigned to the next available slot. Do not call /v1/queue/next-slot and use that time in scheduledFor, as that bypasses queue locking.

queueId?string

Specific queue ID to use when scheduling via queue. Only used when queuedFromProfile is also provided. If omitted, uses the profile's default queue.