Posts

Create a post

Create a new post to publish or schedule across multiple social media platforms simultaneously. A post might mean something different depending on the context and the platform - for example, a single X post, a multi-thread X post, a LinkedIn post, or an Instagram Reel or Story. Posts use a container model where each container holds content for one segment (e.g., individual tweets in an X thread, or carousel items). Specify target platforms via the accounts array, referencing each connected account by its id from GET /v1/social-accounts (preferred) or its exact username - network names (e.g. x, linkedin) and nicknames are not identifiers and do not resolve. The request is rejected with 400 only when none of the supplied identifiers resolve, so an entry matching nothing alongside one that matches is dropped silently. Provide the post body either as a single content string or as a containers array of { content, media } objects - one of the two is required. Attach media by adding media objects to a container, each { url, filename } where url is the publicly accessible URL returned by the confirm-upload step (see Media API); media is not referenced by id. Include scheduledAt (ISO 8601 format, e.g. 2025-06-01T12:00:00Z) to schedule the post for future publishing, or omit it to publish immediately. Platform-specific options (e.g., TikTok privacy settings, YouTube categories, Pinterest board IDs) are provided as top-level keys named after the network (tiktok, youtube, instagram, threads, google_business, pinterest, linkedin, facebook, vimeo, reddit, x, bluesky) - not nested under any wrapper object. Every one of those blocks also accepts content: text published on that network instead of the shared body, so one post can carry a professional LinkedIn version, a casual Instagram caption and a short X version while the top-level content remains the fallback for the rest. Only the root text is overridden - media, first comments and thread replies stay shared, and two accounts on the same network get the same override. The response echoes these back nested under networkOverrideConfiguration (e.g. tiktokConfiguration), so the request and response shapes for platform config differ by design. Unrecognised top-level keys are ignored and reported in the response warnings array. Threads limits each container to 500 characters, so a request targeting a Threads account - on its own or cross-posted alongside other networks - is rejected with 400 if any container is longer; when threads.content is set, that override is checked in place of the shared body, which is how a long LinkedIn post cross-posts to Threads. Managed-key YouTube uploads are admitted at execution time from a shared paced daily pool; scheduling never reserves capacity. A denied YouTube destination fails independently and its existing error string (also in webhook results) contains a stable reason code (release_pending, org_limit_reached, daily_limit_reached, provider_quota_exhausted, or quota_unavailable) and an ISO 8601 retryAt when known. retryAt estimates earliest eligibility for a capacity release, organization-cap relaxation, or next Pacific quota day; it is not a reservation. Retry manually. If another destination already published, create a new YouTube-only post targeting the failed account because partially published posts cannot be edited or rescheduled. BYOK bypasses the shared pool; dispatched attempts are not refunded on failure. Google Business EVENT and OFFER posts require an event title, startDate, startTime, endDate and endTime. CALL buttons omit callToAction.url; other action types require it. Google ignores callToAction on OFFER posts. Facebook videoCoverUrl accepts a public HTTP(S) JPEG cover up to 10,000,000 bytes for exactly one video, including Reels. Stories cannot have covers. The image is validated before video upload; applying the cover after publication is best-effort and rejection leaves the video published.

post/v1/posts

Request

  • Base URL: https://api.outstand.so
  • URL: https://api.outstand.so/v1/posts
  • Auth: none declared

Headers

Idempotency-Keystring
Example:b1f6c3f4-8f0e-4a5b-9c2d-7e1a0b3c4d5e

Optional client-generated key (a UUID v4 is ideal, max 255 printable ASCII characters) that makes this create safe to retry. Retrying with the same key and the same request body returns the original response together with an Idempotency-Replayed: true header, instead of creating a second post. Keys are scoped to your organization and to this endpoint, and are remembered for 24 hours.

Request body

contentstring

Simple text content for the post. Use this for single-container posts. Either 'content' or 'containers' must be provided. If you use 'containers', the first container will be the root container and the rest will be published as replies to the root container. Threads rejects content longer than 500 characters, so a post targeting a Threads account (including when cross-posting alongside other networks) is rejected with 400 if it exceeds that - unless threads.content supplies a Threads-only body, which is what gets checked instead. To publish different text on one network, put content inside that network's block (e.g. linkedin.content); this field is then the fallback for every network without one.

accountsstring[] required

Array of social media account identifiers where this post will be published. Each entry is either the account's id as returned by GET /v1/social-accounts (preferred) or the account's exact username. Network names (e.g. 'x', 'linkedin') and nicknames are NOT identifiers and will not resolve. Account ids are opaque - pass back exactly what the API returned and never construct one. A username is matched by exact equality with no network or active-account filter, so a username shared across networks publishes to every account that carries it. The request is rejected with 400 only when NONE of the supplied identifiers resolve; if at least one resolves, the unresolved entries are silently ignored, so compare the returned socialAccounts against what you sent.

scheduledAtstring date-time

ISO 8601 timestamp for when the post should be automatically published. If not provided, the post will be published immediately (unless it's a draft). If a time in the past is provided, the post will be published immediately. The maximum time in the future is 30d - a later timestamp is rejected with 400 and no post is created.

processMediaboolean

Opt in to automatic video processing. When true, video media is inspected and re-encoded to each target network's requirements (container, codec, resolution, frame rate and file size) before publishing, and the correct rendition is sent to each network. Leave unset to publish the file exactly as supplied. Billed per rendition produced, by output duration in 10-second blocks (rounded up, minimum one block per rendition); validation and inspection are always free. Has no effect on posts without video media.

Example request

{
  "containers": [
    {
      "content": "Our newest feature is live! Check it out 🚀",
      "media": [
        {
          "url": "https://example.com/feature.png",
          "filename": "feature.png"
        }
      ]
    }
  ],
  "content": "Excited to announce our latest update!",
  "accounts": [
    "Kx7vQ",
    "Tm2bN"
  ],
  "scheduledAt": "2026-02-20T14:00:00Z",
  "threads": {
    "content": "A more conversational take for Threads",
    "countries": [
      "US",
      "DK"
    ],
    "reply_control": "accounts_you_follow"
  },
  "instagram": {
    "locationId": "1234567890",
    "reelThumbOffset": 3240
  },
  "youtube": {
    "content": "A longer description for the YouTube video.",
    "isShort": true,
    "categoryId": "1234567890",
    "privacyStatus": "public",
    "madeForKids": true,
    "containsSyntheticMedia": true,
    "tags": [
      "tag1",
      "tag2"
    ],
    "title": "My new video",
    "thumbnailUrl": "https://cdn.example.com/video-thumbnail.jpg"
  },
  "tiktok": {
    "content": "Hook-first caption for TikTok",
    "postMode": "MEDIA_UPLOAD",
    "privacyLevel": "PUBLIC_TO_EVERYONE",
    "title": "Summer collection drop",
    "description": "Our new summer collection is here. Swipe through to see every look.",
    "videoCoverTimestampMs": 3500
  },
  "google_business": {
    "content": "Local-audience wording for Google Business.",
    "topicType": "STANDARD",
    "callToAction": {
      "actionType": "LEARN_MORE",
      "url": "https://example.com"
    },
    "event": {
      "title": "Grand Opening",
      "startDate": {
        "year": 2026,
        "month": 3,
        "day": 15
      },
      "startTime": {
        "hours": 9,
        "minutes": 0
      },
      "endDate": {
        "year": 2026,
        "month": 3,
        "day": 15
      },
      "endTime": {
        "hours": 17,
        "minutes": 0
      }
    },
    "offer": {
      "couponCode": "SAVE20",
      "redeemOnlineUrl": "https://example.com/redeem",
      "termsConditions": "Valid until March 31, 2026."
    }
  },
  "pinterest": {
    "content": "Pin description written for Pinterest search.",
    "board_id": "123456789",
    "cover_image_url": "https://example.com/cover.jpg",
    "link": "https://example.com/product",
    "alt_text": "A red dress on a model",
    "title": "New Summer Collection"
  },
  "vimeo": {
    "content": "Description for the Vimeo upload."
  },
  "linkedin": {
    "content": "A more professional, detailed version for LinkedIn.",
    "article": {
      "source": "https://example.com/blog/my-article",
      "title": "How We Scaled to 1M Users",
      "description": "A deep dive into our infrastructure journey.",
      "thumbnailUrl": "https://example.com/og-image.png"
    },
    "mentions": [
      {
        "urn": "urn:li:organization:1337",
        "text": "LinkedIn"
      }
    ],
    "caption": "Behind the scenes at our Berlin office"
  },
  "facebook": {
    "content": "Facebook-specific wording for the Page post.",
    "mentions": [
      {
        "pageId": "123456789",
        "text": "Outstand"
      }
    ],
    "publishAsStory": true,
    "publishAsReel": true
  },
  "reddit": {
    "content": "Markdown body written for the subreddit.",
    "subreddit": "programming",
    "title": "We built a unified social media API",
    "url": "https://example.com/blog/launch",
    "flairId": "c4acd8f2-9e2f-11eb-8000-0e3e6a4e8e8a",
    "flairText": "Discussion",
    "sendReplies": true
  },
  "x": {
    "content": "Short, punchy version for X"
  },
  "bluesky": {
    "content": "A version for Bluesky, within 300 characters"
  },
  "processMedia": true
}

Response

Post created successfully and scheduled for publishing. Carries Idempotency-Replayed: true when this is a replay of an earlier request with the same Idempotency-Key.

successboolean required

Whether the post creation was successful

warningsstring[]

Non-fatal warnings about the request. Used to report keys in the request body that were not recognised and were therefore ignored - both top-level keys (e.g. the deprecated 'socialAccountIds'/'mediaIds' shapes, or the response-only 'networkOverrideConfiguration' wrapper) and keys inside a platform-config block (e.g. 'tiktok': { 'post_mode': ... } when the field is named 'postMode'). Unrecognised keys are dropped before the post is stored, so a warning here means the setting you sent is not in effect. Present only when there is at least one warning.

Example response

{
  "success": true,
  "warnings": [
    "Ignored unrecognised key 'post_mode' in 'tiktok'. Did you mean 'postMode'?"
  ],
  "post": {
    "id": "9dyJS",
    "orgId": "abc123",
    "publishedAt": null,
    "scheduledAt": "2025-09-20T14:00:00Z",
    "isDraft": false,
    "createdAt": "2025-01-15T10:30:00Z",
    "scheduled": true,
    "socialAccounts": [
      {
        "id": "Kx7vQ",
        "nickname": "My Company",
        "network": "x",
        "username": "mycompany"
      }
    ],
    "containers": [
      {
        "id": "8xKmL",
        "content": "Check out our new product launch! #excited",
        "media": [
          {
            "id": 123,
            "url": "https://media.yoursite.com/images/launch.jpg",
            "filename": "launch-photo.jpg"
          }
        ]
      }
    ]
  }
}

Changes

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