curl --request GET \
--url https://api.scripe.io/v1/posts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scripe.io/v1/posts"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.scripe.io/v1/posts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scripe.io/v1/posts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.scripe.io/v1/posts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.scripe.io/v1/posts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scripe.io/v1/posts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "post_a1b2c3d4e5f6g7h8",
"status": "waitingProcessing",
"statusCategory": "suggested",
"platform": "<string>",
"contentType": "<string>",
"title": "<string>",
"content": "<string>",
"contentTruncated": true,
"publishedAt": "2023-11-07T05:31:56Z",
"delivery": {
"state": "not_scheduled",
"willRetry": true,
"retryUntil": "2023-11-07T05:31:56Z",
"detail": "<string>"
},
"review": {
"state": "not_requested",
"awaitingDecision": true,
"reviewerUserId": "<string>",
"deadline": "2023-11-07T05:31:56Z",
"overdue": true,
"decidedAt": "2023-11-07T05:31:56Z",
"notes": "<string>",
"requiredByWorkspace": true,
"detail": "<string>"
},
"media": {
"kind": "images",
"images": [
{
"key": "rrb7bw8pfuc.png",
"alt": "<string>",
"name": "<string>"
}
]
},
"createdAt": "2023-11-07T05:31:56Z",
"projectId": "<string>",
"statusId": "<string>",
"statusTitle": "<string>",
"funnelStage": "REACH",
"scheduledAt": "2023-11-07T05:31:56Z",
"lastPublishError": "<string>",
"lastPublishAttemptAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
],
"pagination": {
"next_cursor": "<string>",
"has_more": true,
"total": 128
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}List posts for a project
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.
curl --request GET \
--url https://api.scripe.io/v1/posts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scripe.io/v1/posts"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.scripe.io/v1/posts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scripe.io/v1/posts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.scripe.io/v1/posts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.scripe.io/v1/posts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scripe.io/v1/posts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "post_a1b2c3d4e5f6g7h8",
"status": "waitingProcessing",
"statusCategory": "suggested",
"platform": "<string>",
"contentType": "<string>",
"title": "<string>",
"content": "<string>",
"contentTruncated": true,
"publishedAt": "2023-11-07T05:31:56Z",
"delivery": {
"state": "not_scheduled",
"willRetry": true,
"retryUntil": "2023-11-07T05:31:56Z",
"detail": "<string>"
},
"review": {
"state": "not_requested",
"awaitingDecision": true,
"reviewerUserId": "<string>",
"deadline": "2023-11-07T05:31:56Z",
"overdue": true,
"decidedAt": "2023-11-07T05:31:56Z",
"notes": "<string>",
"requiredByWorkspace": true,
"detail": "<string>"
},
"media": {
"kind": "images",
"images": [
{
"key": "rrb7bw8pfuc.png",
"alt": "<string>",
"name": "<string>"
}
]
},
"createdAt": "2023-11-07T05:31:56Z",
"projectId": "<string>",
"statusId": "<string>",
"statusTitle": "<string>",
"funnelStage": "REACH",
"scheduledAt": "2023-11-07T05:31:56Z",
"lastPublishError": "<string>",
"lastPublishAttemptAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
],
"pagination": {
"next_cursor": "<string>",
"has_more": true,
"total": 128
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}Authorizations
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.
Headers
Pin the API version. Format YYYY-MM-DD. Omit to receive the
currently rolling default. Unknown versions return 400 version_unsupported.
"2026-08-10"
Query Parameters
"proj_a1b2c3d4e5f6g7h8"
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.
"draft,scheduled"
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.
"review,inProgress"
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.
"22e074194ca5450e"
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.
"missed"
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.
"pending"
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.
200"pricing"
How 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 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.
preview, full, none 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.
Latest CREATION date (inclusive). See dateFrom.
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.
Latest PUBLICATION date (inclusive).
Opaque pagination cursor returned by the previous page.
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.
1 <= x <= 200