Posts

Create a new post

Create a new post. You can attach media by uploading first (media/media_ids) or by passing external URLs (media_urls) which will be downloaded and processed automatically. Both can be combined.

Required media: Instagram and TikTok posts need at least one image/video. Pinterest posts need an image and a board_id. Posts without required media will be rejected with a 400 error.

Stories (type: "story"): 1 to 10 media items. Each item is one slide and publishes as its own story on Instagram and Facebook, in the order given. Per-platform objects can give each platform its own slide list. Videos max 60 s per slide. After publishing, story_slides lists every slide's native id and URL; published_urls keeps the first slide.

Per-platform media caps (enforced at submit, not at publish time):

  • bluesky — max 4 images per post; cannot mix images and video
  • mastodon — max 4 media items per post; cannot mix images and video
  • x — max 4 images per post; cannot mix images and video
  • instagram — max 10 items in a carousel; images and videos can be mixed in one carousel
  • threads — max 10 items in a carousel; images and videos can be mixed in one carousel
  • tiktok — max 35 items per photo post; cannot mix photos and videos in one post
  • pinterest — max 5 images per carousel pin (videos are exempt — they publish as single video pins)
  • facebook, linkedin, linkedin_page — cannot mix images and video in one post (their APIs accept images only or a single video)

Exceeding any cap returns 400 { error: { code: "validation_error", message: "<Platform> allows up to N media items per post. You attached M, please remove M-N." } } (Pinterest wording: "Pinterest carousel pins support a maximum of 5 images. Please remove some images and try again."). The Pinterest carousel aspect-ratio constraint (2–5 same-ratio images) is enforced the same way; see the Pinterest section for the mismatched_slides response shape.

Per-platform video file-size caps (validated via ffprobe; drafts exempt): Mastodon 99 MB · Bluesky 100 MB · Instagram 300 MB · X 512 MB free tier, 16 GB with Premium / Premium+ (in practice bounded by the 1 GB URL-ingest / 100 MB direct-upload limits) · Threads / Reddit 1 GB · Pinterest 2 GB · Facebook / TikTok 4 GB · LinkedIn 5 GB · YouTube 256 GB. Upload requests are capped at 100 MB on top of these — anything bigger is rejected before media processing with code: file_too_large.

Per-platform video duration caps (validated via ffprobe; drafts exempt): Facebook Post 240 min / Reel 90 s · Instagram 15 min (Post + Reel) · TikTok 10 min (Post + Reel) · YouTube Short 3 min · X 140 s free tier, 125 min when the connected X account has Premium or Premium+ (tier detected automatically; reconnect X after upgrading) · Bluesky 180 s · Threads 5 min · LinkedIn 10 min · Pinterest 15 min · Reddit 15 min · Mastodon (instance-dependent).

Cap violations return 400 { error: { code: "validation_error", message: "<Platform> only allows videos up to <cap>; yours is <duration>. Trim the video or deselect <Platform>." } } (duration) or "This video (<size>) is too large for <Platform>. ..." (size). When multiple platforms reject the same upload, each contributes one sentence to the message. See API_DEVELOPER_GUIDE.md → "Per-Platform Media Limits" and "Per-Platform Video Duration Caps" for the full table with source citations.

X link-post credits: X's API bills posts whose text contains a URL at a premium ($0.20 vs $0.015), and OmniSocials passes that fee through as prepaid credits at X's rate card (20 credits per URL-containing tweet; threads are billed per part that contains a link). Credits are purchased in euros: 1 credit = €0.01, top-ups of €5-500 in the dashboard. When a create/schedule targets X and the text contains a URL, the 201 response includes a warnings array entry (code: "x_url_post_credits") with credits_required, the company's current credits_balance, and the enforcement state. At publish time the credits are debited; if the balance can't cover it, the X target alone fails with an INSUFFICIENT_CREDITS message in its error_message (other platforms in the post publish normally) and the post can be retried after topping up in Settings → Organisation → Billing → Credits. Posts without links, analytics, and everything else on X stay free.

Schedule-time gate: every scheduled X link post reserves its cost until it publishes. Scheduling (or publishing) a new X link post that would push the total reserved past the balance is refused with 402 x_credits_insufficient (details carries credits_required, credits_balance, credits_reserved). Drafts are never gated; posts publishing before the enforcement date are never gated.

post/posts/create

Request body

contentobject required

Post content. Use 'default' for all platforms, or platform-specific keys: instagram, facebook, linkedin, linkedin_page, youtube, tiktok, pinterest, x, threads, bluesky, mastodon, google_business. Coming soon: reddit, snapchat.

accountsstring[] required

Social accounts to post to. Each entry is either:

  • A bare platform name (e.g. "youtube", "linkedin_page"), or
  • The composite ID returned by GET /v1/accounts in the form "<workspace_id>_<platform>" (e.g. "844008_youtube").

The composite form's workspace prefix must match the workspace your API key belongs to. Mismatched or unknown ids return 400 validation_error with an "Unknown account/platform" message — always source ids from GET /v1/accounts for the API key in use.

type'post' | 'story' | 'reel'

Post type: 'post' (feed), 'story' (Instagram/Facebook), 'reel' (Instagram/Facebook/YouTube/TikTok). A 'reel' must have video media (MP4/MOV); an image is rejected with 400 validation_error at schedule/publish — use 'post' to share an image. A 'story' takes 1 to 10 media items: each item is one slide, published as its own story in the order given (Instagram and Facebook have no multi-slide story container). More than 10 returns 400 validation_error.

schedule_atstring date-time

ISO 8601 datetime to schedule. Omit for draft.

publish_nowboolean

Set true to publish immediately

link_urlstring uri

URL to share as a rich preview card on platforms that support link-share posts (currently LinkedIn and Facebook). When set on a text post, the URL renders as a preview tile with thumbnail / title / description instead of plain text. Ignored on platforms that don't support link shares (Instagram, TikTok, etc.) and on posts that already have media attached — media takes precedence.

link_titlestring

Optional title for the link-share preview. LinkedIn uses this directly when set. Facebook ignores this — it fetches OG metadata server-side. Omit to let LinkedIn auto-fetch the page title.

link_descriptionstring

Optional description for the link-share preview. LinkedIn uses this when set; Facebook auto-fetches the OG description. Omit to defer to OG metadata.

link_thumbnail_urlstring uri

Optional thumbnail image URL for the preview card. Currently not yet applied to LinkedIn (would require uploading to LinkedIn's image API first); included for forward compatibility.

sourcestring

Self-reported origin of the integration creating this post. Defaults to 'api' if omitted. Use 'zapier', 'make', 'n8n', etc. to identify your integration.

location_idstring

Instagram only. Facebook Place/Page ID to tag the post's location with (e.g. a dealership or storefront). Applied to single-image and carousel feed posts via Instagram's content-publishing location_id. Find a Place's Page ID via Facebook's Pages Search API. Ignored by other platforms. Send an empty string on PATCH to clear it.

collaboratorsstring[]

Instagram only. Up to 3 public Instagram usernames to invite as co-authors (the 'Collab' feature). Applied to image, carousel, and reel posts — NOT Stories. Invited users get an invitation in the Instagram app; once accepted, the post also appears on their profile and feed. Usernames are case-insensitive and a leading '@' is stripped. Private accounts and non-existent usernames are rejected by Instagram at publish time. Ignored by other platforms. Send an empty array on PATCH to clear collaborators.

hashtag_setstring

Name of a saved hashtag set (see GET /hashtag-sets) to apply to this post, matched case-insensitively. The set's tags are merged in ONCE at create time — the post stores plain text, so later edits to the set never change this post. Tags already present in a caption are skipped (case-insensitive), and hard platform caps (Instagram 30) are enforced with a 400 hashtag_limit_exceeded before the platform can reject the post. Mutually interchangeable with hashtag_set_id.

hashtag_set_idstring

Id of a saved hashtag set to apply. Same behavior as hashtag_set; pass one or the other.

hashtag_placement'caption_append' | 'first_comment'

Where the set's tags land. caption_append (default) appends them to each target platform's caption after a blank line. first_comment puts them in the auto first comment on comment-capable platforms (Instagram, Facebook, LinkedIn Profile/Page, YouTube, TikTok with comments enabled) — appended after any first_comment you passed explicitly — and falls back to caption_append on platforms without a comment API. Stories always use captions (first comments are never posted on Stories).

hashtag_platformsstring[]

Optional subset of the post's selected platforms to apply the set to (e.g. only ["instagram", "tiktok"]). Defaults to all selected platforms.

Example request

{
  "content": {
    "default": "Hello world!",
    "instagram": "Hello from IG! #awesome"
  },
  "media_urls": {
    "default": [
      {
        "url": "https://example.com/photo.jpg",
        "alt": "A red bicycle leaning against a brick wall"
      }
    ],
    "instagram": [
      "https://example.com/square.jpg"
    ]
  },
  "link_url": "https://goodlightai.com/blog/new-feature",
  "location_id": "1234567890",
  "collaborators": [
    "therock",
    "kevinhart4real"
  ],
  "user_tags": [
    {
      "username": "therock",
      "x": 0.5,
      "y": 0.8
    }
  ],
  "hashtag_set": "Fitness Brand",
  "instagram": {
    "audio_id": "587784541076604"
  },
  "x": {
    "thread_parts": [
      {
        "text": "1/3 — our results are in 👇",
        "media_ids": [
          "12345"
        ]
      },
      {
        "text": "2/3 — grab your free pick here: https://example.com/signup"
      },
      {
        "text": "3/3 — and here's the slip 🎁",
        "media_ids": [
          "12346"
        ]
      }
    ]
  },
  "bluesky": {
    "thread_parts": [
      {
        "text": "1/3 — our results are in 👇"
      },
      {
        "text": "2/3 — read the full writeup: https://example.com/post"
      },
      {
        "text": "3/3 — thanks for reading! #buildinpublic"
      }
    ]
  },
  "mastodon": {
    "thread_parts": [
      {
        "text": "1/3 — our picks are in 👇"
      },
      {
        "text": "2/3 — read the full writeup: https://example.com/post"
      },
      {
        "text": "3/3 — thanks for reading! #fediverse"
      }
    ]
  },
  "threads": {
    "location_id": "17841400000000000",
    "location": {
      "id": "17841400000000000",
      "name": "Griffith Observatory",
      "city": "Los Angeles",
      "country": "United States"
    },
    "thread_parts": [
      {
        "text": "1/3: our results are in"
      },
      {
        "text": "2/3: read the full writeup: https://example.com/post"
      },
      {
        "text": "3/3: thanks for reading! #buildinpublic"
      }
    ]
  }
}

Response

Post created

Example response

{
  "data": {
    "app_url": "https://app.omnisocials.com/create-post/1024",
    "published_urls": {
      "facebook": "https://www.facebook.com/123456789/posts/987654321",
      "instagram": "https://www.instagram.com/p/Cxyz123ABC/",
      "linkedin": "https://www.linkedin.com/feed/update/urn:li:share:7191234567890",
      "x": "https://x.com/yourhandle/status/1791234567890123456"
    },
    "errors": {
      "facebook": "Already Posted: These photos were already posted.",
      "instagram": "Instagram couldn't accept one of your images. Check that every image is JPEG/PNG, at least 320×320, has an aspect ratio between 4:5 and 1.91:1, and isn't visually flagged by Instagram. Re-upload or replace the offending image and try again."
    },
    "threads": {
      "location": {
        "id": "17841400000000000",
        "name": "Griffith Observatory",
        "city": "Los Angeles",
        "country": "United States"
      }
    }
  },
  "warnings": [
    {
      "code": "x_url_post_credits",
      "credits_required": 20,
      "enforce_from": "2026-08-14"
    }
  ]
}

Changes

No recorded changes to this endpoint across all 1 revision of this API.