---
title: "List posts"
method: GET
path: "/api/businesses/{business_id}/posts"
tags: ["posts"]
---

# List posts

`GET /api/businesses/{business_id}/posts`

## Request

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

List posts for a business, newest first. Use cursor for pagination.

Pass `view=summary` for a lightweight payload (caption preview + media
fingerprint, no publish_results/overrides/queue) when auditing or de-duping a
big history — a full-view page of 100 posts can be too large for an agent's
tool budget.

Errors:
    400 invalid_value: The business_id is not a valid UUID.
    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 business with that ID exists.
    429 rate_limit_exceeded: You sent too many requests this minute.

## Path parameters

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

## Query parameters

- `status` string[], nullable: Filter by post status. Repeat it to get several at once (`status=NEW&status=DRAFT` returns both). NEW = draft awaiting your team's review (incl. freshly generated posts), 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.
- `post_ids` string[], nullable: Return only these posts (up to 100; repeat the param). With the default `view=full` each item is exactly what `get_post` returns, so this is the way to read several posts in one call. Ids from another business are left out, not an error. Combines with `status`; cannot be combined with `q`.
- `limit` integer: Max results per page (1-100)
- `cursor` string, nullable: The `next` value from the previous page's response, passed back verbatim. Leave it out for the first page. When `next` is null you have every result.
- `q` string, nullable: Keyword search. Returns only posts whose caption, title, or attached media's description/transcript contains this text (case-insensitive). Searching the media description is what finds 'the post about my dog' when the dog is in the image and the caption never says 'dog'. Trashed (REJECTED) posts are left out — pass `status=REJECTED` to search them. Combines with `status`, and pages with `cursor` just like an unfiltered listing.
- `view` 'full' | 'summary': Response detail. `full` (default) returns each post's whole caption plus publish_results, platform_overrides, and queue placement. `summary` returns a lean row — caption PREVIEW, media count + fingerprint, status, schedule, and metadata — with none of the heavy fields, for cheaply scanning or de-duping a large history without blowing your token budget.

## Headers

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

## Response `200`

Successful Response

- union
  - PagePostResponse
    - `items` PostResponse[], required: This page of results.
      - `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.
    - `next` string, nullable: Opaque cursor for the next page; null when there are no more. Pass it back verbatim as ?cursor=. Do not construct or decode it.
  - PagePostSummaryResponse
    - `items` PostSummaryResponse[], required: This page of results.
      - `id` string, required: Post ID
      - `status` string, nullable: Post status — same values as the list_posts status filter (NEW/DRAFT/REJECTED/SCHEDULED/PUBLISHED).
      - `caption_preview` string, nullable: First ~140 characters of the caption. Omit `view` (or pass view=full) for the whole caption plus publish_results and overrides.
      - `media_count` integer: Number of attached media items.
      - `media_fingerprint` string, nullable: Short stable hash of the attached media URLs. Equal fingerprints mean the same media set — spot duplicate media across posts without pulling every URL. Null when the post has no media.
      - `restrict_publish_to` string[], nullable: Platforms this post is restricted to. Null means ALL connected platforms.
      - `scheduled_publish_time` string, nullable: When this post is scheduled to publish. Null for drafts, queued posts, and already-published posts.
      - `published_at` string, nullable: When the post was actually published. Null until it publishes.
      - `metadata` object, nullable: Caller-defined string key/value pairs set at create/update time. Returned verbatim.
      - `created_at` string, required: When the post was created
    - `next` string, nullable: Opaque cursor for the next page; null when there are no more. Pass it back verbatim as ?cursor=. Do not construct or decode it.

## 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)
