Meet the new Scripe, live on October 7.Register

OpenAPI reference · Posts

List posts for a project

GET/posts

Cursor-paginated list of posts scoped to a project. Filter by comma-separated status (the lifecycle value each row reports) or statusCategory (the kanban column), date range (createdAt), and custom limit.

Both filters match on the post's custom status, the same source the status field is derived from, and fall back to the stored enum only for a post that carries no custom status.

Authorization

  • Authorizationstringrequired

    Bearer token in the Authorization header.

    Pass Authorization: Bearer scripe_sk_live_<...> (or scripe_sk_test_<...> for test keys) on every request. Keys are scoped to a single workspace and can be revoked from the Scripe dashboard.

    The same header also accepts an OAuth 2.1 access token (scripe_oat_*); both credentials share one scope vocabulary and every operation below documents the scope it requires. An API key can hold every scope named on this surface except webhooks:manage, which is grantable to OAuth tokens only today — the webhook-endpoint operations answer 403 scope_missing to every API key. Operations that name no scope accept any valid token of the workspace.

Query parameters

  • projectIdstringrequired
  • statusstring

    Comma-separated list of lifecycle statuses to include. Valid values: waitingProcessing, draft, waitingApproval, approved, rejected, published, scheduled, suggested. Repeating the query key (?status=draft&status=published) also works. draft also returns posts in an inProgress column, because both render as draft; use statusCategory to separate them.

  • statusCategorystring

    Comma-separated list of kanban categories to include — the vocabulary GET /v1/post-statuses returns. Valid values: suggested, draft, inProgress, review, scheduled, published. This is the only way to ask for the inProgress column; no status value can express it. Combined with status, both must match.

  • statusIdstring

    Comma-separated list of custom status ids (board columns) to include — the id values GET /v1/post-statuses returns and every post row reports as statusId. Use this when a workspace runs several columns inside one category, which statusCategory cannot tell apart. An id this workspace does not own is a 400 invalid_request, never an empty page: "there is nothing in that column" and "there is no such column" are the two answers a caller cannot distinguish.

  • deliverystring

    Comma-separated list of delivery states to include — see PostDelivery.state. This is the only way to ask "which of my posts never went out?": a post the publisher has given up on keeps its scheduled status forever, so status cannot express it and there is no filter over scheduledAt. The five states partition the project, so delivery=missed returning nothing means nothing is stuck.

  • reviewstring

    Comma-separated list of approval states to include — see PostReview.state. review=pending is the answer to "what is waiting for my approval?"; statusCategory=review is not, because a review column also holds already-approved posts, rejected ones, and posts nobody was assigned to (77% of production's review columns). The four states partition the project.

  • qstring

    Free-text search over the post body, title and hook — the same three fields the dashboard's own post search matches. Case- and accent-insensitive substring match (the column collation is utf8mb4_0900_ai_ci); whitespace-separated words are AND-ed and a "quoted phrase" is matched whole. % and _ in the query are matched literally, not as wildcards. Results stay newest-first — this narrows the page, it does not rank by relevance. search and query are accepted as aliases. Max 200 characters; longer is a 400 invalid_request rather than a silent truncation.

  • contentstring

    How much of each post body to return.

    • preview (default) — a ~280-character excerpt, cut on a word boundary, with contentTruncated: true. When q is set the excerpt is centred on the first matching term, so the row shows why it matched rather than only its opening.
    • full — the whole body.
    • none — omit it (content: null), for counting or for listing titles and statuses.

    The default changed from full on 2026-08-14, matching GET /v1/analytics/posts: a page of 50 full bodies is tens of thousands of characters — ~14k tokens spent before the caller has read a single field it filtered on. GET /v1/posts/{postId} serves one whole body for the price of one row and is the cheaper way to read a specific post.

    Allowed values: preview, full, none

    Default: "preview"

  • dateFromstring <date-time>

    Earliest CREATION date (ISO 8601 or YYYY-MM-DD, inclusive) — when the draft was written, not when it went live. For "what did I publish last week" use publishedFrom / publishedTo: on production a post goes live an average of 6.3 days after it is drafted, and 51% of the posts published in a given week were drafted before that week began.

  • dateTostring <date-time>

    Latest CREATION date (inclusive). See dateFrom.

  • publishedFromstring <date-time>

    Earliest PUBLICATION date (ISO 8601 or YYYY-MM-DD, inclusive) — the day the LinkedIn copy went live, which is what "what did I publish last week / in July" means. Only ever matches published posts: a post that never went out has no publication date. Combined with dateFrom/dateTo, both windows must match.

  • publishedTostring <date-time>

    Latest PUBLICATION date (inclusive).

  • cursorstring

    Opaque pagination cursor returned by the previous page.

  • limitinteger

    Page size. Default 50, max 200. Values above the max are clamped silently; only a non-integer or a value below 1 is rejected with bad_pagination.

    Default: 50

Header parameters

  • Scripe-Api-Versionstring

    Pin the API version. Format YYYY-MM-DD. Omit to receive the currently rolling default. Unknown versions return 400 version_unsupported.

Responses

  • 200

    Page of posts.

  • 400

    Malformed request (bad cursor, bad limit, etc.).

  • 401

    Missing, malformed, expired, or revoked API key.

  • 403

    Plan not eligible, scope missing, or workspace mismatch.

  • 404

    Resource not found in this workspace.

  • 429

    Sliding-window rate limit exceeded.

Example request

bash
curl --request GET \
  --url 'https://api.scripe.io/v1/posts?projectId=<projectId>' \
  --header 'Authorization: Bearer <token>'

Example response (200)

json
{
  "data": [
    {
      "id": "post_a1b2c3d4e5f6g7h8",
      "projectId": "string",
      "status": "waitingProcessing",
      "statusId": "string",
      "statusTitle": "string",
      "statusCategory": "suggested",
      "platform": "string",
      "contentType": "string",
      "funnelStage": "REACH",
      "title": "string",
      "content": "string",
      "contentTruncated": true,
      "scheduledAt": "2026-08-10T09:00:00Z",
      "publishedAt": "2026-08-10T09:00:00Z",
      "lastPublishError": "string",
      "lastPublishAttemptAt": "2026-08-10T09:00:00Z",
      "delivery": {
        "state": "not_scheduled",
        "willRetry": true,
        "retryUntil": "2026-08-10T09:00:00Z",
        "detail": "string"
      },
      "review": {
        "state": "not_requested",
        "awaitingDecision": true,
        "reviewerUserId": "string",
        "deadline": "2026-08-10T09:00:00Z",
        "overdue": true,
        "decidedAt": "2026-08-10T09:00:00Z",
        "notes": "string",
        "requiredByWorkspace": true,
        "detail": "string"
      },
      "media": {},
      "createdAt": "2026-08-10T09:00:00Z",
      "updatedAt": "2026-08-10T09:00:00Z"
    }
  ],
  "pagination": {
    "next_cursor": "string",
    "has_more": true,
    "total": 128
  }
}