OpenAPI reference · Posts
List posts for a project
/postsCursor-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
AuthorizationstringrequiredBearer token in the Authorization header.
Pass
Authorization: Bearer scripe_sk_live_<...>(orscripe_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 exceptwebhooks:manage, which is grantable to OAuth tokens only today — the webhook-endpoint operations answer403 scope_missingto every API key. Operations that name no scope accept any valid token of the workspace.
Query parameters
projectIdstringrequiredstatusstringComma-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.draftalso returns posts in aninProgresscolumn, because both render asdraft; usestatusCategoryto separate them.statusCategorystringComma-separated list of kanban categories to include — the vocabulary
GET /v1/post-statusesreturns. Valid values:suggested,draft,inProgress,review,scheduled,published. This is the only way to ask for theinProgresscolumn; nostatusvalue can express it. Combined withstatus, both must match.statusIdstringComma-separated list of custom status ids (board columns) to include — the
idvaluesGET /v1/post-statusesreturns and every post row reports asstatusId. Use this when a workspace runs several columns inside one category, whichstatusCategorycannot tell apart. An id this workspace does not own is a400 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.deliverystringComma-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 itsscheduledstatus forever, sostatuscannot express it and there is no filter overscheduledAt. The five states partition the project, sodelivery=missedreturning nothing means nothing is stuck.reviewstringComma-separated list of approval states to include — see
PostReview.state.review=pendingis the answer to "what is waiting for my approval?";statusCategory=reviewis 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.qstringFree-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.searchandqueryare accepted as aliases. Max 200 characters; longer is a400 invalid_requestrather than a silent truncation.contentstringHow much of each post body to return.
preview(default) — a ~280-character excerpt, cut on a word boundary, withcontentTruncated: true. Whenqis 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
fullon 2026-08-14, matchingGET /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,noneDefault: "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" usepublishedFrom/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 withdateFrom/dateTo, both windows must match.publishedTostring <date-time>Latest PUBLICATION date (inclusive).
cursorstringOpaque pagination cursor returned by the previous page.
limitintegerPage 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-VersionstringPin the API version. Format
YYYY-MM-DD. Omit to receive the currently rolling default. Unknown versions return400 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
curl --request GET \
--url 'https://api.scripe.io/v1/posts?projectId=<projectId>' \
--header 'Authorization: Bearer <token>'Example response (200)
{
"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
}
}