---
title: "Publish post now"
method: POST
path: "/api/businesses/{business_id}/posts/{post_id}/publish"
tags: ["posts"]
---

# Publish post now

`POST /api/businesses/{business_id}/posts/{post_id}/publish`

## Request

- Base URL: `https://api.mymarky.ai`
- URL: `https://api.mymarky.ai/api/businesses/{business_id}/posts/{post_id}/publish`
- Auth: HTTP bearer

Publish a post immediately to its connected platforms.

Fires the publish flow in the background and returns the post right away.
The post's publish results land asynchronously in `post_publish_results` —
poll `GET /api/businesses/{business_id}/posts/{post_id}` for the final
`status` (`PUBLISHED` once all targeted platforms complete).

Errors:
    400 NO_CONNECTED_SOCIALS: This business has no connected accounts, or none of the requested platforms are connected.
    401 authentication_required: API key or token is missing, invalid, or revoked.
    403 permission_denied: Your plan does not include API access, or this business is not in your org.
    404 resource_not_found: No post with that ID exists, or it is not in this business.
    409 CAPTION_HAS_PLACEHOLDER: The caption still has an unfilled [[placeholder]] (a fact only the owner knows). The post is NOT published and is unchanged. Fill it in with update_post, then publish it again.
    409 NO_CONNECTED_SOCIALS: No connected account can publish this post (none connected, all need a reconnect, or none can take this content). The post is NOT published and is unchanged. Follow `recovery_hint` (usually: call connect_social), then publish it again. Being rolled out in place of the 400 above.
    429 rate_limit_exceeded: You sent too many requests this minute.

The response's `publish_readiness` lists the platforms the post will
publish to and any connected account that won't get it, with the reason.
`delivery` on a later read says what actually landed.

## Path parameters

- `business_id` string, required: A UUID identifier.
- `post_id` string, required: A UUID identifier.

## Headers

- `Marky-Version` '2026-04-08' | '2026-07-17' | '2026-08-02' | '2026-08-04' | '2026-08-06'

## Request body

- PostPublishRequest
  - `restrict_publish_to` string[], nullable: Restrict publishing to these platforms (e.g. instagram, facebook, linkedIn). Case-insensitive. Omit or set null to publish to ALL connected platforms.
  - `restrict_publish_to_integration_ids` string[], nullable: Restrict publishing to these exact connected accounts (integration ids from `GET /businesses/{business_id}/integrations`). Use instead of restrict_publish_to when a business has multiple accounts of the same platform and you want specific ones. Omit or set null for platform-level targeting. Cannot be combined with restrict_publish_to in one request.

## Response `202`

Publish accepted; firing in background

- PostResponse
  - `id` string, required: Post ID
  - `business_id` string, required: Business this post belongs to
  - `caption` string, nullable: Post caption text
  - `status` string, nullable: Post status. One of: NEW (draft awaiting your team's review, incl. freshly generated), DRAFT (approved by a reviewer on your team), REJECTED (declined by a reviewer), SCHEDULED (queued for a future time), PUBLISHED (already posted). DRAFT/REJECTED are your team's review verdicts — Marky does not gate publishing on them. Same values the `list_posts` status filter accepts.
  - `media_urls` string[], nullable: Attached media URLs
  - `media_details` PostMedia[], nullable: The same attached media as `media_urls`, in the same order, but with each item's description and transcript. Use this when analysing what content performs: the creative is usually the reason a post did well, and a URL alone tells you nothing about what was in it.
    - `id` string, required: Media ID in the business's library
    - `url` string, nullable: Public URL of the media
    - `type` string, nullable: 'image', 'video', or 'audio'
    - `description` string, nullable: What the media shows, generated by a vision model — including any headline or offer text rendered into the graphic, which on a designed post is usually the whole message. Null if not described yet.
    - `transcript` string, nullable: Spoken words in the video or audio, if any.
    - `duration` number, nullable: Length in seconds, for video and audio.
  - `design_id` string, nullable: The design attached to this post, if any. Media is derived from this design; edit it with revise_post_design or create_design + a new link.
  - `cover_media_id` string, nullable: Media id of the cover image shown before this post's video plays. Marky generates one from the first frame when you don't send one.
  - `video_cover_timestamp_ms` integer, nullable: The frame (milliseconds from the start) TikTok uses as its cover. Null when nobody has chosen a cover for this post.
  - `link` string, nullable: Clickable destination URL for platforms that support link posts: the Facebook link attachment, the Google Business CTA button, and the Pinterest pin destination. Other platforms ignore it — put links in the caption there. Must be http(s).
  - `restrict_publish_to` string[], nullable: Platforms this post is restricted to. Null means ALL connected platforms.
  - `restrict_publish_to_integration_ids` string[], nullable: Exact connected accounts (integration ids) this post is restricted to. Null means targeting is platform-level (see restrict_publish_to).
  - `scheduled_publish_time` string, nullable: The exact time this post is scheduled to publish (set via `schedule`). Null for drafts, queued posts, and already-published posts.
  - `scheduled_publish_time_local` string, nullable: The same moment in the business's timezone, ready to show a person, e.g. "Wed Sep 23, 6:00 PM MSK". Quote it as-is instead of converting the UTC value.
  - `timezone` string, nullable: IANA timezone the `*_local` times are in: the business's calendar timezone, or "UTC" when the business hasn't set one (see `timezone_source`).
  - `timezone_source` 'business' | 'unset_utc', nullable: Where `timezone` came from: `business` (the business's own setting) or `unset_utc` (no timezone saved yet, so times are shown in UTC).
  - `published_at` string, nullable: When the post was actually published (past timestamp). Null until it publishes.
  - `metadata` object, nullable: Caller-defined string key/value pairs set at create/update time — your own analytics dimensions (e.g. media_type, format, style, hook, topic). Marky returns them verbatim and never interprets them. Keys starting with `marky_` are Marky's own (e.g. the copy date below); any you send are dropped.
  - `copy_date` string, date, nullable: The day this caption is about, read from the caption when it was written: an upcoming holiday of the business's country, one of the business's events, or an explicit date like 'September 25'. Never 'today'/'tomorrow'. Queueing never places the post after this day. Null when the caption names no date.
  - `copy_moment` string, nullable: The moment the caption names, e.g. 'National Day'. Null for a plain date.
  - `suggested_publish_time` string, nullable: When to publish on `copy_date`: the business's first posting-schedule slot that day, else 10:00 local. Offer it as the post's date; schedule with `at` to use it.
  - `suggested_publish_time_local` string, nullable: The same moment in the business's timezone, ready to show a person, e.g. "Wed Sep 23, 6:00 PM MSK". Quote it as-is instead of converting the UTC value.
  - `platform_overrides` PlatformOverrideModel[], nullable: Per-platform content overrides on this post, if any. Each platform publishes its override fields where set, the post's own fields otherwise.
    - `platform` string, required: Platform this override applies to (e.g. instagram, linkedIn, twitter). Case-insensitive.
    - `caption` string, nullable: Caption this platform gets instead of the post's caption. Tailor length, hashtag count, and link placement to the platform.
    - `media_urls` string[], nullable: Media this platform gets instead of the post's media_urls.
    - `title` string, nullable: Title, on platforms that have one (Pinterest pin title, YouTube video title).
    - `link` string, nullable: Destination link, on platforms that support one (e.g. Pinterest, Google Business).
    - `first_comment` string, nullable: Comment auto-posted right after publishing on this platform — the usual home for hashtags and links on Instagram/LinkedIn.
  - `publish_results` PublishResultResponse[]: Per-platform publish outcomes — one entry per platform this post was sent to. Poll this until every entry has a terminal status (`success` or `failed`) to know publishing is complete.
    - `platform` string, required: Platform identifier (facebook, instagram, etc.)
    - `status` string, required: Publish status on this platform: success, failed, in_progress, etc.
    - `publish_id` string, nullable: External post ID on the platform — use with `GET /businesses/{business_id}/integrations/{integration_id}/posts/{external_post_id}/stats`
    - `post_url` string, nullable: Public URL of the published post on the platform
    - `error_type` string, nullable: Error type if status is failed (e.g. REFRESH_TOKEN_EXPIRED, TIMEOUT)
    - `error_message` string, nullable: Human-readable error message if status is failed
    - `integration_id` string, nullable: The exact connected account this result belongs to. Distinguishes accounts when a business has several on one platform.
    - `updated_at` string, nullable: When this result was last updated
  - `queue` QueueInfo: Where a post sits in its business's recurring daily publishing queue. Only present on posts that are in a queue (status SCHEDULED with no `adhoc_publish_time`). Lets an API user confirm a queued post is really queued and see roughly when it will publish.
    - `queue_id` string, required: ID of the daily queue this post belongs to.
    - `position` integer, required: 1-based rank among the queued posts (1 = next to publish).
    - `estimated_publish_time` string, nullable: Best estimate of when this post will publish, projected from the queue's recurring timeslots. Ignores jitter and skipped slots, so treat it as approximate. Null if it can't be computed.
    - `estimated_publish_time_local` string, nullable: The same moment in the business's timezone, ready to show a person, e.g. "Wed Sep 23, 6:00 PM MSK". Quote it as-is instead of converting the UTC value.
    - `queue_runs_dry_at` string, nullable: Estimated publish time of the LAST post in this queue: when it runs dry. Same estimate rules as estimated_publish_time.
    - `queue_runs_dry_at_local` string, nullable: The same moment in the business's timezone, ready to show a person, e.g. "Wed Sep 23, 6:00 PM MSK". Quote it as-is instead of converting the UTC value.
    - `warnings` FieldWarning[]: Advisory notes about WHEN this post goes out; they never block. DATE_BOUND_COPY: the caption says 'today', a weekday, 'this week'... and the post lands on a different day. FAR_FUTURE: it lands more than 90 days out. Quote them to the user.
      - `field` string, required: The field this warning is about, e.g. 'strategy', 'caption' or 'platform_overrides[0].caption'.
      - `code` string, nullable: Stable machine-readable code, e.g. 'cta_without_contact'. Branch on this, never on the message. Null on the older size-budget warnings.
      - `level` 'approaching_limit' | 'over_limit' | 'advisory', required: 'approaching_limit' = past the soft limit, act soon. 'over_limit' = past the hard limit, act now. 'advisory' = worth fixing before this publishes, but it may be intended.
      - `message` string, required: Plain-language explanation of what is wrong and how to fix it.
  - `publish_readiness` PublishReadiness: Where this post will actually go, worked out when it was queued, scheduled or published.
    - `will_publish_to` string[], required: Platforms that will receive the post. Empty means nothing connected can publish it.
    - `blocked` BlockedPlatform[]: Accounts the post was aimed at that won't receive it, each with the reason and fix.
      - `platform` string, required: Platform identifier (facebook, instagram, etc.)
      - `integration_id` string, required: The account that won't receive the post.
      - `reason` string, required: Stable reason code, e.g. INTEGRATION_DELETED (disconnected or needs a reconnect), PAGE_NOT_FOUND (no Page selected), MEDIA_FORMAT_ERROR (this platform needs a photo or video).
      - `message` string, required: Plain-language explanation and fix.
      - `reconnect_url` string, nullable: Where the user reconnects this account, when reconnecting is the fix. Null otherwise.
    - `not_connected` string[]: Platforms the post was aimed at (via restrict_publish_to) that have no account connected. The post will NOT go to these. Connect the account, then schedule or update the post again.
  - `repeating_cron` string, nullable: If set, this post repeats: a standard 5-field cron string (e.g. '0 9 * * 1' = every Monday 9am UTC). When it publishes, Marky auto-creates the next occurrence at the next cron time. Null = a one-off post.
  - `created_at` string, required: When the post was created
  - `warnings` FieldWarning[]: Non-blocking notes about this post's captions, e.g. a call-to-action with no link or phone ('cta_without_contact'), or a caption in a different language from the business ('language_mismatch'). Empty when there is nothing to fix. Fix them with update_post before the post publishes; they never block anything.
    - `field` string, required: The field this warning is about, e.g. 'strategy', 'caption' or 'platform_overrides[0].caption'.
    - `code` string, nullable: Stable machine-readable code, e.g. 'cta_without_contact'. Branch on this, never on the message. Null on the older size-budget warnings.
    - `level` 'approaching_limit' | 'over_limit' | 'advisory', required: 'approaching_limit' = past the soft limit, act soon. 'over_limit' = past the hard limit, act now. 'advisory' = worth fixing before this publishes, but it may be intended.
    - `message` string, required: Plain-language explanation of what is wrong and how to fix it.
  - `updated_at` string, nullable: Last update time
  - `revision_id` string, nullable: Id of the undo record this write created. Pass it to revert_revisions to put the record back exactly as it was before this call. Set only on the response to a write; null when you read the record.
  - `url` string, required: Link to view this post in the Marky app (openable/shareable — show it to the user). This is where the post lives in Marky; `link` is the outbound URL published with the post.
  - `delivery` 'pending' | 'delivered' | 'partial' | 'failed', nullable, required: What actually reached the platforms, from publish_results: 'delivered' (every platform succeeded), 'partial' (some succeeded, some failed), 'failed' (every platform failed), 'pending' (still in progress). Null when no publish has been attempted. Prefer this over `status` to tell the user whether a post went out.
  - `scheduled_posts_url` string, nullable, required: Link to the workspace's scheduled-posts list in the Marky app — where the user sees this post waiting in the lineup. Present once the post is SCHEDULED; null otherwise.

## Other responses

- `400`: Bad request — the input was malformed or otherwise invalid.
- `401`: Authentication failed — the API key is missing or invalid.
- `403`: Forbidden — the key is valid but may not access this resource.
- `404`: Not found — no resource matches the id in the path.
- `422`: Validation error — one or more fields failed validation.
- `429`: Rate limited — retry after the window resets (see Retry-After).
- `500`: Server error — something failed on our side; retry or quote X-Request-Id.

---

[API](https://skmtc.dev/mymarky/apis/marky-public-api.md) · [All operations](https://skmtc.dev/mymarky/apis/marky-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mymarky/apis/marky-public-api/revisions/25aeacae455c?raw)
