---
title: "List posts"
method: GET
path: "/posts"
tags: ["Posts"]
---

# List posts

`GET /posts`

## Request

- Base URL: `https://api.shortsync.app/v1`
- URL: `https://api.shortsync.app/v1/posts`
- Auth: HTTP bearer

Returns posts ordered by `created_at` descending. Cursor pagination.

## Query parameters

- `status` PostStatus[]
- `platform` Platform[]
- `connection_id` string
- `created_after` string, date-time
- `created_before` string, date-time
- `cursor` string
- `limit` integer

## Response `200`

Posts.

- object
  - `data` object[], required
    - `id` string, required
    - `workspace_id` string, required
    - `upload_id` string, nullable
    - `platform` 'instagram' | 'tiktok' | 'youtube' | 'facebook' | 'snapchat' | 'pinterest' | 'threads' | 'twitter' | 'bluesky' | 'linkedin' | 'reddit' | 'telegram' | 'mastodon' | 'discord' | 'tumblr' | 'pixelfed' | 'google_business', required
    - `connection_id` string, required
    - `status` 'processing' | 'ready' | 'scheduled' | 'published' | 'failed' | 'deleted' | 'skipped_inactive_subscription', required
    - `publish_mode` 'immediate' | 'scheduled' | 'draft', required
    - `scheduled_for` string, date-time, nullable
    - `published_at` string, date-time, nullable
    - `platform_url` string, uri, nullable: Public URL of the published post, when available.
    - `caption` string, nullable
    - `title` string, nullable
    - `description` string, nullable
    - `first_comment` string, nullable
    - `platform_options` PlatformOptions: Per-platform overrides. Keys are platform identifiers; values are platform-specific objects. Only the keys matching the target's platform are read.
      - `tiktok` TikTokOptions
        - `post_mode` 'direct' | 'draft'
        - `privacy_level` string: TikTok privacy level (e.g. `PUBLIC_TO_EVERYONE`).
        - `disable_comment` boolean
        - `disable_duet` boolean
        - `disable_stitch` boolean
        - `commercial_content_disclosure` boolean: Turns on TikTok's commercial content disclosure. Must be true for `is_branded_content` and `is_your_brand` to take effect.
        - `is_branded_content` boolean
        - `is_your_brand` boolean
        - `is_aigc` boolean
        - `auto_add_music` boolean: Photo/carousel posts only. When true, TikTok auto-picks and attaches background music. Ignored for video uploads.
      - `youtube` YouTubeOptions
        - `privacy_status` 'public' | 'unlisted' | 'private'
        - `made_for_kids` boolean: Required when the video is primarily directed at children. Setting to `true` disables comments, personalized ads, and forces `notify_subscribers` to false (YouTube doesn't allow subscriber notifications on kid-directed content).
        - `notify_subscribers` boolean
        - `category_id` string
        - `default_language` string
        - `tags` string[]
        - `playlist_ids` string[]
        - `upload_early_for_review` boolean: Upload as private now and flip to public at `scheduled_for`.
        - `contains_synthetic_media` boolean: YouTube's "Altered content / AI use" disclosure. Enable when the video contains synthetic, altered, or AI-generated content that could be mistaken for a real place, person, or event. Maps to `status.containsSyntheticMedia`.
        - `recording_date` string, date: Date the video was recorded, as `YYYY-MM-DD`. Surfaces in YouTube Studio under "Recording date and location". Maps to `recordingDetails.recordingDate` (pinned to midnight UTC).
        - `has_paid_product_placement` boolean: YouTube's paid promotion disclosure. Enable when the video contains paid product placement or sponsorship. YouTube shows an "Includes paid promotion" label to viewers. Maps to `paidProductPlacementDetails.hasPaidProductPlacement`.
      - `instagram` InstagramOptions
        - `share_to_feed` boolean: Reel also appears in the main feed. Mutually exclusive with `story_only`.
        - `share_to_story` boolean: Cross-post a Story alongside the Reel. Mutually exclusive with `story_only`.
        - `story_only` boolean: Publish only as a Story (no Reel). Mutually exclusive with `share_to_feed` and `share_to_story`.
        - `trial_reel` boolean
        - `trial_graduation_strategy` 'MANUAL' | 'SS_PERFORMANCE'
        - `audio_name` string
        - `collaborators` string[]
        - `user_tags` string[]
        - `location_id` string: Facebook Page ID of the location to tag.
        - `is_paid_partnership` boolean: Shows the "Paid partnership" label.
        - `is_ai_generated` boolean: Adds Instagram's "AI info" label to the post.
      - `facebook` FacebookOptions
        - `share_to_story` boolean: Also post as a Story alongside the Reel. Mutually exclusive with `story_only`.
        - `story_only` boolean: Publish only as a Story (no Reel). Mutually exclusive with `share_to_story`.
        - `is_ai_generated` boolean: Adds Facebook's "AI info" label to the Reel, photo or carousel. Not applied to Stories or text posts.
      - `twitter` TwitterOptions
        - `reply_settings` 'everyone' | 'following' | 'mentionedUsers' | 'subscribers' | 'verified'
        - `community_id` string
        - `text_only` boolean
        - `for_super_followers_only` boolean
      - `threads` ThreadsOptions
        - `text_only` boolean
      - `bluesky` BlueskyOptions
        - `langs` string[]
        - `text_only` boolean
      - `snapchat` SnapchatOptions
        - `formats` string[]: Which Snapchat surfaces to publish to. `spotlight` ships to the Spotlight discovery feed; `story` is a 24-hour broadcast to your followers. Pass both to publish to both at once. Defaults to `["spotlight"]`. Hashtags aren't a separate field on Snapchat — include them inline in the caption. Snapchat rejects Spotlight captions over 160 characters with `SNAPCHAT_DESCRIPTION_TOO_LONG`. Story doesn't use the caption at all.
        - `skip_save_to_profile` boolean: Spotlight-only. When `true`, ask Snapchat not to save the Spotlight to your Public Profile grid; the video still ships to the Spotlight discovery feed. Ignored when `formats` doesn't include `spotlight`.
      - `pinterest` PinterestOptions
        - `board_id` string, required: Destination Pinterest board ID. Required unless the workspace has set a default board (`video_preferences.pinterest_board_id`), in which case the default is used when omitted. Fetch board IDs via `GET /api/v1/platforms/pinterest/boards`.
        - `link` string, uri: Outbound destination URL for the pin. When set, tapping the pin on Pinterest opens this URL. Optional.
        - `alt_text` string: Accessibility alt text for the pin image or video cover. Pinterest caps this at 500 characters; longer values are truncated server-side.
        - `title` string: Override for the pin title. When omitted, falls back to the workspace title, then to a truncated slice of the caption. Pinterest caps titles at 100 characters. The pin's description comes from the top-level `caption` on the post (or the `platform_captions.pinterest` override).
      - `linkedin` LinkedInOptions
        - `visibility` 'PUBLIC' | 'CONNECTIONS' | 'LOGGED_IN': Who can see the post. Company Page authors always post as `PUBLIC`; LinkedIn doesn't support the other values for Pages.
      - `telegram` TelegramOptions
        - `text_only` boolean: Send a text-only message (no media). The upload's bytes are skipped. Ignored for image uploads.
      - `mastodon` MastodonOptions
        - `visibility` 'public' | 'unlisted' | 'private': `public` appears on public timelines, `unlisted` is federated but hidden from public timelines, `private` is followers-only.
        - `language` string: BCP-47 language code (e.g. `en`, `fi`).
        - `text_only` boolean: Publish a text-only post (no media).
      - `discord` DiscordOptions: A workspace can connect several Discord channels (one webhook each). A post goes to the channels in `channel_ids`, or to every channel with `is_default: true` when it's omitted. Caption max 2000 characters, up to 10 attachments and 10 MB per post. No custom thumbnail. `first_comment` (max 2000 characters) is posted as a separate message right after the post in text channels (webhooks can't reply there) and as the first reply inside the new post in forum channels. It never sends its own notification, and a failed comment never fails the post. Saved per-user defaults for `silent`, `mention` and `username` apply when you leave them out.
        - `channel_ids` string[]: Channels to post to, by `id` from `GET /api/v1/discord/channels`. Omit to use the workspace's default channels. Ids that are no longer connected are skipped; if none are left the publish fails with `DISCORD_CHANNELS_NOT_FOUND`.
        - `silent` boolean: Send without push or desktop notifications.
        - `mention` 'none' | 'here' | 'everyone': Ping to add to the message (`@here` or `@everyone`).
        - `thread_name` string: Forum post title. Forum channels require one; when omitted, the first line of the caption is used. Ignored by regular text channels.
        - `username` string: Overrides the webhook's display name for this post. Discord rejects names containing "discord", so they're refused with `INVALID_PLATFORM_OPTIONS`.
        - `text_only` boolean: Send only the caption as a plain message (no media). Applies to video uploads; ignored for image and carousel uploads.
      - `tumblr` TumblrOptions: Tumblr is in beta. One Tumblr login can own several blogs; a post goes to the blog in `blog_name`, or to the account's primary blog when it's omitted. The post text is the post's `description` (up to 5,000 characters, split into paragraphs); when no description is set, the caption is used instead. A per-target `caption` on the Tumblr target wins over both. Video posts take one video (up to 500 MB and 10 minutes); photo posts take up to 10 images (no video mix); text posts need post text or a `title`. No custom thumbnail and no first comment. Tumblr limits each account to 250 posts and 20 videos (60 minutes of video) per day.
        - `blog_name` string: Short name of the blog to post to (e.g. `myblog` for myblog.tumblr.com), one of the connection's blogs. Omit to use the primary blog. A blog that's no longer on the connected account fails the publish with `TUMBLR_BLOG_NOT_FOUND`.
        - `tags` string[]: Tags without the leading "#". Commas aren't allowed inside a tag. Duplicates are dropped.
        - `title` string: Optional heading shown above the post text. Overrides the post's shared `title`.
        - `text_only` boolean: Post only the title and post text as a text post (no media). Applies to video uploads; ignored for image and carousel uploads.
      - `pixelfed` PixelfedOptions: Pixelfed is in beta. It's a federated, Mastodon-compatible photo network; each connection is one account on one server (instance). Every post needs media (video, image or carousel); there are no text-only posts. Carousels can mix photos and videos, up to 20 items, but the connected server's own album cap (often 4) is checked at publish. Caption max 500 characters (some servers allow more). Photos and videos share one file-size cap set by the server (default about 15 MB); oversized videos are compressed, oversized images fail. Video is off by default on Pixelfed servers, so posting video to a photo-only server fails with a clear error. `first_comment` (max 500 characters) is posted as a reply under the post. No custom thumbnail. Saved workspace defaults for `visibility` and `sensitive` apply when you leave them out.
        - `visibility` 'public' | 'unlisted' | 'private': `public` shows the post on public timelines, `unlisted` hides it from them, `private` shows it to followers only.
        - `sensitive` boolean: Blurs the media behind a warning.
        - `spoiler_text` string: Content-warning text shown over the post. Only used when `sensitive` is true.
      - `google_business` GoogleBusinessOptions: Google Business Profile is in beta. One Google login can manage several business locations; a post goes to the locations in `location_ids`, or to every location with `is_default: true` when it's omitted (the first postable location if none is marked), and is published as an update on each. Caption (required) max 1500 characters. One photo (JPG or PNG, 10 KB to 5 MB) or text only: no video, no carousel, no custom thumbnail, no first comment. Saved per-user defaults for `cta_type` and `cta_url` apply when you leave them out.
        - `location_ids` string[]: Locations to post to, by `id` from `GET /api/v1/google-business/locations`. Omit to use the default locations. An id that's no longer on the connected account fails the publish with `GOOGLE_BUSINESS_LOCATION_NOT_FOUND`.
        - `cta_type` 'BOOK' | 'ORDER' | 'SHOP' | 'LEARN_MORE' | 'SIGN_UP' | 'CALL', nullable: Optional button on the post. `CALL` dials the profile's phone number and takes no URL; every other type needs `cta_url`. `null` removes the button.
        - `cta_url` string, uri, nullable: The button's link (http or https). Required with every `cta_type` except `CALL`.
        - `text_only` boolean: Post only the caption (no media). Google Business Profile can't post video, so a video upload needs this. Ignored for image uploads.
    - `error` object, nullable
      - `code` string
      - `message` string
      - `details` unknown
    - `created_at` string, date-time, required
    - `updated_at` string, date-time
  - `pagination` object, required
    - `next_cursor` string, nullable, required: Pass into `cursor` on the next request. `null` when no more pages.

## Other responses

- `401`: Missing, malformed, or revoked token.
- `403`: Token is valid but cannot access this resource. Common cases: - `INSUFFICIENT_SCOPE` - token lacks the required scope. - `WORKSPACE_NOT_ALLOWED` - account token is allowlisted to other workspaces.
- `429`: Per-key sliding-window rate limit (60 req/min), per-org monthly request quota, or per-user upload cap was hit. `RATE_LIMIT_EXCEEDED` carries the window headers; `QUOTA_EXCEEDED` carries `details.quota` (which counter), `used`, `limit`, `resets_at`, and `upgrade_url`. `UPLOAD_QUOTA_EXCEEDED` (Free tier on `POST /posts` only) carries the same shape plus `details.remaining`.

---

[API](https://skmtc.dev/shortsync/apis/shortsync-api.md) · [All operations](https://skmtc.dev/shortsync/apis/shortsync-api/llms.txt) · [OpenAPI document](https://skmtc.dev/shortsync/apis/shortsync-api/revisions/71fdaef1e400?raw)
