List posts
Returns a paginated list of posts. Published posts include platformPostUrl with the public URL on each platform.
API key authentication - use your Zernio API key as a Bearer token
In: header
Query Parameters
Page number (1-based)
11 <= valuePage size. Values above the maximum return 400 rather than being clamped.
101 <= value <= 500Which collection to read. zernio (default) returns posts authored through Zernio. external returns posts synced from the platform (existing/historical posts that were published outside Zernio). Combine with accountId and paginate via page/limit to walk the full synced history (we keep up to the last ~12 months per account).
"zernio""zernio" | "external""draft" | "scheduled" | "published" | "failed"Filter posts to a specific profile (24-char hex ObjectId). Omit it, or send all or an empty value, to list posts across every profile.
Filter posts to those created by a specific team user (24-char hex ObjectId).
Zero-padded YYYY-MM-DD, or a full ISO 8601 datetime. An empty value means no date filter; any other malformed value returns 400.
dateZero-padded YYYY-MM-DD, or a full ISO 8601 datetime. An empty value means no date filter; any other malformed value returns 400.
datefalseSearch posts by text content.
Sort order for results.
"scheduled-desc""scheduled-desc" | "scheduled-asc" | "created-desc" | "created-asc" | "status" | "platform"Filter posts to those published via a specific social account (24-char hex ObjectId).
Response Body
application/json
application/json
application/json
import Zernio from '@zernio/node';const zernio = new Zernio({ apiKey: process.env.ZERNIO_API_KEY });const { data } = await zernio.posts.listPosts();console.log(data);{
"posts": [
{
"_id": "65f1c0a9e2b5af0012ab34cd",
"title": "Launch post",
"content": "We just launched!",
"status": "scheduled",
"scheduledFor": "2024-11-01T10:00:00Z",
"timezone": "UTC",
"platforms": [
{
"platform": "twitter",
"accountId": {
"_id": "64e1f0...",
"platform": "twitter",
"username": "@acme",
"displayName": "Acme Corp",
"isActive": true
},
"status": "pending"
}
],
"tags": [
"launch"
],
"createdAt": "2024-10-01T12:00:00Z",
"updatedAt": "2024-10-01T12:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 1,
"pages": 1
}
}{
"error": "string",
"type": "invalid_request_error",
"code": "string",
"param": "string",
"platform": "string",
"platformError": {},
"details": {}
}{
"error": "Unauthorized"
}Connect a Shopify store with a custom-app Admin token POST
Token-paste alternative to the OAuth flow: connect a store using the Admin API access token of a custom app the merchant created in their own Shopify admin (Settings → Apps and sales channels → Develop apps, with the `read_content`/`write_content` scopes). Use this when the one-click OAuth connect is unavailable or when your users prefer not to install a third-party app on their store. The token is validated against the store before anything is saved; custom-app tokens do not expire. Connecting the same profile to a store again replaces the stored token in place.
Create post POST
Create and optionally publish a post. Immediate posts (`publishNow: true`) include `platformPostUrl` in the response. Content is 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). See each platform's schema for media constraints. ## 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 — no new post is created. The official Zernio SDKs auto-generate a unique `x-request-id` per call. If you're using 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 simply omit the header — 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`. Returns `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) — 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.