Publish to Social

Publish images or videos directly to connected social accounts

Published ·Updated


Publish content to your connected social media accounts. Use this when you already have a media URL (e.g., from a previous render) and want to post it without rendering again.

To publish as part of a render in a single call, use the publish parameter on Render from Studio Template instead.

Endpoint#

POST /social/publish#

https://api.orshot.com/v1/social/publish

Request Example#

await fetch("https://api.orshot.com/v1/social/publish", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    accounts: [1, 2],
    content: "Check out our latest design!",
    media_url: "https://storage.orshot.com/.../image.png",
  }),
});

Find your account IDs using the List Social Accounts endpoint, or from the Social Accounts page in your workspace sidebar.

Choosing when it goes out#

One field decides this: status. It takes the same three values the API gives back to you, so what you ask for and what you read back are the same vocabulary.

statusWhat happens
publishedPosts immediately. This is the default when you omit status
draftHeld by Orshot. Reaches no platform until you publish it explicitly
scheduledQueued for scheduled_for. Requires that field

Schedule a Post#

await fetch("https://api.orshot.com/v1/social/publish", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    accounts: [1],
    content: "This will be posted later!",
    media_url: "https://storage.orshot.com/.../image.png",
    status: "scheduled",
    scheduled_for: "2030-01-15T10:00:00Z",
    timezone: "America/New_York",
  }),
});

scheduled_for must be at least 60 seconds in the future, and is only valid with status: "scheduled" — pairing it with a draft or an immediate post is rejected rather than quietly ignored.

Save as Draft#

await fetch("https://api.orshot.com/v1/social/publish", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    accounts: [1, 2, 3],
    content: "Draft post — will publish later from the dashboard",
    media_url: "https://storage.orshot.com/.../image.png",
    status: "draft",
  }),
});

A draft is held by Orshot and reaches no platform until you publish it. From there you can read it back, edit it, publish it or discard it — see List Social Posts for finding a post's ID.

Platform-Specific Options#

await fetch("https://api.orshot.com/v1/social/publish", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    accounts: [1, 2, 3],
    content: "New product launch!",
    media_url: "https://storage.orshot.com/.../image.png",
    platformOptions: {
      "1": { firstComment: "Follow us for more updates!" },
      "2": { title: "Product Launch", link: "https://acme.com/launch" },
    },
    tiktokSettings: { autoAddMusic: true },
  }),
});

Request Parameters#

ParameterTypeRequiredDescription
accountsArrayYesArray of account IDs to publish to (see List Social Accounts)
contentStringNoCaption/text for the post (max 5000 characters)
media_urlStringNoURL of the image or video to publish
media_urlsArrayNoMultiple media URLs for carousel posts (use instead of media_url)
statusStringNopublished (default), draft or scheduled — see above
scheduled_forStringNoISO 8601 timestamp, at least 60 seconds ahead. Required with status: "scheduled", invalid otherwise
timezoneStringNoIANA timezone string for scheduling (e.g., "America/New_York", "Europe/London")
platformOptionsObjectNoPer-account options keyed by account ID (see below). platform_options also accepted
tiktokSettingsObjectNoRoot-level TikTok options. For photo carousels, { "autoAddMusic": true } asks TikTok to add recommended music.

Platform Options#

Pass platform-specific options keyed by account ID:

PlatformOptionDescription
LinkedInfirstCommentAuto-post a first comment
LinkedIndisableLinkPreviewDisable the link preview card
PinteresttitlePin title
PinterestlinkDestination URL for the pin

TikTok recommended music is set at the root level via tiktokSettings, not per account — it applies to every TikTok photo post in the request.

Earlier field names#

Before status existed, timing was expressed with isDraft: true and schedule: { scheduledFor }. Both still work and need no migration. status takes precedence if you send both.

The one combination that no longer passes is isDraft: true together with a schedule — it used to be accepted and stored a draft carrying a schedule that could never fire. Say which one you mean instead.

Response Example#

{
  "data": {
    "post_id": 42,
    "status": "published",
    "platforms": [
      {
        "account_id": 1,
        "platform": "twitter",
        "username": "acmehq",
        "name": "Acme HQ",
        "status": "published",
        "url": "https://twitter.com/acmehq/status/1234567890"
      },
      {
        "account_id": 2,
        "platform": "linkedin",
        "username": "acme-inc",
        "name": "Acme Inc",
        "status": "published",
        "url": "https://linkedin.com/feed/update/urn:li:share:1234567890"
      }
    ]
  }
}

Response Fields#

FieldTypeDescription
post_idIntegerUnique identifier for the post
post_idsArrayProvider post IDs, one per created post
statusStringOverall status: published, scheduled, drafted, partial, processing, or failed
platformsArrayPer-account results
platforms[].account_idIntegerExact connected account targeted by this result
platforms[].platformStringPlatform name
platforms[].usernameStringAccount username
platforms[].nameStringAccount display name
platforms[].statusStringpublished, scheduled, drafted, failed, or an in-flight provider status like pending or publishing
platforms[].urlStringLink to the published post (when available)
platforms[].errorStringError message (only on failure)
platforms[].actionStringSuggested recovery action on failure (e.g. reconnect)

A partial status means some accounts succeeded and others failed — check individual platforms[].status for details. A processing status means all targeted platforms publish asynchronously (e.g. Instagram, TikTok) and the post is still in flight.

Format Compatibility#

Not all formats work on every platform:

FormatUnsupported Platforms
PDFAll platforms (not supported)
MP4, WebM, GIFGoogle Business
PNG, JPG, WebPYouTube (video-only platform)

If some accounts are incompatible with the media format, they are skipped and returned with status: "failed" and a reason in the error field.

Rate Limits#

This endpoint is rate limited to 20 requests per minute per workspace. Rate limit headers are included in every response:

HeaderDescription
X-RateLimit-LimitMaximum requests per minute
X-RateLimit-RemainingRemaining requests in the current window
Retry-AfterSeconds to wait (only on 429 responses)

Error Responses#

CodeDescription
400Missing or empty accounts array
400Content exceeds 5000 characters
400Invalid schedule.scheduledFor timestamp
400Scheduled time must be in the future
400No valid connected accounts found for the given IDs
401Invalid or expired API key
403Missing Authorization header
403Social publishing requires a paid plan
422No compatible platforms for the media format
422Publish attempted but failed on every targeted account
429Rate limit exceeded

Ready to automate?

Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.

Get your API key
  • Image, PDF and video generation via API
  • Visual editor with AI and smart layouts
  • Zapier, Make, MCP and 50+ integrations
  • White-label embed for your own app
  • 30 free credits — no credit card required