Skip to content
Sign in Waitlist

Create post

POST /v1/posts scope write

Creates a post with one item per network (queue, fixed time or draft). Each item may carry channel (the account on that network; without it, the default channel). With kind: "image" it is a single-photo post, Instagram only (through Buffer or directly through Meta).

HeadersAuthorization: Bearer praeko_… and X-Praeko-Contract: en. Always send both.

Parameters

Name In Description
Idempotency-Key header Idempotency key (1 to 255 visible characters, for example a UUID). Repeating the SAME request with the same key within 24 h returns the stored response (with Idempotent-Replayed: true) without running it again; a different request with the same key returns 422 idempotency_key_reuse; a request still in progress returns 409 idempotency_in_progress. 5xx errors are not stored. The key is scoped per workspace and per route. See Retry a request safely.

Request body

JSON. Required fields must be sent; the others can be left out. The server returns 422 invalid_request with the full list of problems in details.errors. The media must be ready (completed). Image post (kind: image): a single photo on Instagram, through Buffer or directly through Meta. The photo is a type: image media item (JPEG); Instagram takes JPEG up to 8 MB with an aspect ratio from 4:5 to 1.91:1 (use 1080x1350). The item may come back with warnings (stable code): alt_ignored (Buffer has no alt text), image_small, image_will_be_resized, image_dimensions_unknown. The post comes back with kind and media.images.

Field Type Description
title (optional) string Post title. Required unless texts.youtube.title is present. Up to 200 characters.
baseText (optional) string Base text, stored with the post.
kind (optional) string video (default) or image: a single photo, Instagram only. YouTube and TikTok refuse image (422 kind_not_supported_by_network). Values: video, image; default: video.
media required object Media of the post (ids from POST /v1/media, already completed).
media.video (optional) string Id of the video media item. Required when kind is video.
media.cover (optional) string Id of the cover media item (PNG or JPEG). Video posts only.
media.images (optional) object[] Only with kind: image: the photo of the post, ONE for now (several would be a carousel, which arrives in a later phase: 422 carousel_not_supported). Up to 1 items.
media.images[].media required string Id of a completed type: image media item.
media.images[].alt (optional) string Alternative text (up to 1000 characters). Sent to Instagram when the item publishes through Meta (via: meta); through Buffer it is ignored and the item comes back with the alt_ignored warning. Up to 1000 characters.
texts required object Texts per network. Each network in items needs its own text.
texts.youtube (optional) object YouTube texts.
texts.youtube.title (optional) string Video title (up to 100 characters; longer text is truncated).
texts.youtube.text required string Description.
texts.youtube.pinnedComment (optional) string Pinned comment, posted after the video becomes public.
texts.instagram (optional) object Instagram texts.
texts.instagram.text required string Caption: up to 2200 characters and 30 hashtags.
texts.instagram.pinnedComment (optional) string Pinned comment.
texts.tiktok (optional) object TikTok texts.
texts.tiktok.text required string Caption: up to 2200 characters.
items required object[] One item per network (a network cannot repeat).
items[].network required string Network of the item. Values: youtube, instagram, tiktok.
items[].channel (optional) string Id of the account on that network (from GET /v1/channels). Without it, the default channel.
items[].mode (optional) string How to publish. YouTube: private, scheduled or public. TikTok: draft or direct. Instagram: now. Values: private, scheduled, public, draft, direct, now; default: private on YouTube, draft on TikTok, now on Instagram.
items[].scheduledFor (optional) string Fixed publishing time. ISO 8601 date and time in UTC, for example 2026-11-14T20:30:00Z. At least 30 s ahead (Instagram: 5 min through Buffer, 25 min through Meta; YouTube scheduled: 5 min).
items[].queue (optional) boolean true uses the channel's next free time slot (see PUT /v1/channels/:network/slots).
items[].via (optional) string Instagram only: which route to publish through. Values: meta, buffer.
items[].replaces (optional) object Instagram with via: meta only: deletes this post from Buffer when publishing.
items[].replaces.provider required string Always buffer. Values: buffer.
items[].replaces.id required string Id of the post in Buffer.
items[].media (optional) object Media for this item only (replace the post's media). Only video and cover: images per item arrive in a later phase.
items[].media.video (optional) string Id of the video media item.
items[].media.cover (optional) string Id of the cover media item.
queue (optional) boolean Applies to every item that has neither queue nor scheduledFor.
draft (optional) boolean true creates everything as a draft (no time required). Default: false.
options (optional) object Per-network and general options.
options.format (optional) string long marks the post as a long-form video (without it, it is a Short). Values: long.
options.youtube (optional) object YouTube options.
options.youtube.mode (optional) string Same as items[].mode. Values: private, scheduled, public.
options.youtube.categoryId (optional) string Video category. Default: 28.
options.youtube.tags (optional) string[] Video tags.
options.youtube.notifySubscribers (optional) boolean Notify subscribers.
options.youtube.comment (optional) boolean false skips posting the pinned comment.
options.tiktok (optional) object TikTok options.
options.tiktok.mode (optional) string Same as items[].mode. Values: draft, direct.
options.tiktok.privacy (optional) string Who can view it. Values: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY; default: SELF_ONLY.
options.tiktok.coverFrameMs (optional) integer Video frame used as the cover, in milliseconds. Minimum 0.
options.tiktok.disableComments (optional) boolean Turn off comments.
options.tiktok.disableDuet (optional) boolean Turn off Duet.
options.tiktok.disableStitch (optional) boolean Turn off Stitch.
options.tiktok.commercialContent (optional) boolean Commercial content (requires ownBrand and/or thirdPartyBrand).
options.tiktok.ownBrand (optional) boolean Promotes your own brand.
options.tiktok.thirdPartyBrand (optional) boolean Promotes a third-party brand (cannot be SELF_ONLY in direct mode).
options.instagram (optional) object Instagram options.
options.instagram.via (optional) string Which route to publish through. Values: meta, buffer.
options.instagram.shareToFeed (optional) boolean Also show it in the feed. Default: true.
options.instagram.coverFrameMs (optional) integer Video frame used as the cover, in milliseconds. Minimum 0.
tags (optional) string[] Post tags (up to 40 characters each). Each up to 40 characters.
id (optional) string An id you choose (3 to 64 characters among letters, digits, - and _). Without it, the server generates one. A repeated id returns 409 id_exists.
origin (optional) string Who created the post (free text; default api).
Body example
{
  "title": "The test that only failed on Mondays",
  "media": {
    "video": "01KA3VQ7M2T9XE4HB8N5RZ6C0D"
  },
  "texts": {
    "youtube": {
      "title": "The test that only failed on Mondays",
      "text": "A test that passed from Tuesday to Sunday. What it was hiding.\n\n#programming #testing"
    }
  },
  "items": [
    {
      "network": "youtube",
      "mode": "scheduled",
      "scheduledFor": "2026-11-10T20:30:00.000Z"
    }
  ],
  "tags": [
    "behind-the-scenes"
  ]
}

Example

curl -X POST "https://praeko.app/v1/posts" \
  -H "Authorization: Bearer $PRAEKO_KEY" \
  -H "X-Praeko-Contract: en" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "The test that only failed on Mondays",
  "media": {
    "video": "01KA3VQ7M2T9XE4HB8N5RZ6C0D"
  },
  "texts": {
    "youtube": {
      "title": "The test that only failed on Mondays",
      "text": "A test that passed from Tuesday to Sunday. What it was hiding.\n\n#programming #testing"
    }
  },
  "items": [
    {
      "network": "youtube",
      "mode": "scheduled",
      "scheduledFor": "2026-11-10T20:30:00.000Z"
    }
  ],
  "tags": [
    "behind-the-scenes"
  ]
}'

Response

Success status When
201 Success.
Response 201
{
  "id": "01KA3W2Y6H1RC5ZQ8M0T4VXE7N",
  "items": [
    {
      "id": "01KA3W2Y6J9D3BNF7G2K5PQS8A",
      "network": "youtube",
      "channel": "01KA3V0R5D8M2WQ7X4HT9NZ1BF",
      "state": "scheduled",
      "scheduledFor": "2026-11-10T20:30:00.000Z",
      "custom": true,
      "mode": "scheduled",
      "runAt": "2026-11-10T19:30:00.000Z"
    }
  ]
}

Errors

Status code When
400 empty_body The request body is empty.
400 invalid_json The request body is not a JSON object.
413 body_too_large The request body is larger than 1 MB.
422 invalid_request Something is missing; the full list comes in details.errors.
422 invalid_field Invalid id, channel, mode or tag.
422 time_in_past The time has already passed or is too close.
422 missing_schedule Missing scheduledFor, queue or draft.
409 channel_not_connected The network is not connected.
409 no_slots queue on a channel without time slots.
409 id_exists A post with this id already exists.
409 media_not_ready The media upload has not been completed yet.
422 media_not_found The media item does not exist.
422 media_type_mismatch The media item has the wrong type (kind: image needs type: image).
422 kind_not_supported_by_network kind: image on a YouTube or TikTok item.
422 carousel_not_supported More than one image in media.images (carousel arrives later).
422 invalid_image The image is not a JPEG (Instagram only takes JPEG).
422 image_too_large The image is over 8 MB (Instagram limit).
422 image_aspect_ratio The image aspect ratio is outside 4:5 to 1.91:1 (Instagram would refuse it; use 1080x1350).
422 idempotency_key_reuse The Idempotency-Key was already used with a different request (different method, path, query or body).
409 idempotency_in_progress A request with the same Idempotency-Key is still in progress (retry in a moment).
409 idempotency_response_too_large The original response was larger than 256 KB and was not stored; fetch the resource with GET instead.

Errors on any route

Status code When
401 unauthorized The Authorization: Bearer … header is missing.
401 invalid_api_key The key does not exist.
401 revoked_api_key The key has been revoked.
401 expired_api_key The key has expired.
403 forbidden The key's scope is not enough for this route (see the scope badge).
429 rate_limited The per-minute request limit was exceeded.

The body of every error is { "error": { "code", "message", "details"? } }. See all error codes.

Try it

Try it

The live test uses your key and your account. It stays off when you are not signed in.

Sign in to try it
Question or mistake on this page? contato@praeko.appView .md

Type to search pages, routes and error codes.