curl --request GET \
--url https://api.scripe.io/v1/viral-posts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scripe.io/v1/viral-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/viral-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/viral-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/viral-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/viral-posts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scripe.io/v1/viral-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": "<string>",
"content": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"linkedin_post_id": "<string>",
"permalink": "<string>",
"content_type": "<string>",
"post_type": "<string>",
"media_format": "carousel",
"outlier_multiplier": 123,
"outlier_baseline": "AUTHOR",
"metrics": {
"likes": 123,
"comments": 123,
"reactions": {
"appreciation": 123,
"empathy": 123,
"interest": 123,
"maybe": 123
},
"total_engagement": 123,
"views": 123
},
"author": {
"name": "<string>",
"slogan": "<string>",
"profile_url": "<string>",
"handle": "<string>"
}
}
],
"meta": {
"query": "<string>",
"returned": 123,
"sort_by": "<string>",
"language": "<string>",
"min_engagement": 123,
"limit": 123,
"published_within_days": 123,
"min_outlier_multiplier": 123,
"media_formats": [
"<string>"
],
"narrowed_to": {
"min_outlier_multiplier": 123,
"media_formats": [
"<string>"
],
"published_within_days": 123
}
}
}{
"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>"
}
}Search viral posts (inspiration)
Semantic search over the inspiration feed for high-performing LinkedIn posts relevant to a topic. Returns posts from other authors (the project’s own posts are excluded) with engagement metrics.
By default only OUTLIERS are returned: posts that beat their
own author’s median engagement by at least 2x
(outlier_multiplier), so a large account’s ordinary post is not
reported as viral. minOutlierMultiplier moves that floor (0 opts
out and serves every post above minEngagement, including posts
with no score yet), mediaFormats narrows to a media shape, and
publishedWithinDays sets the recency window.
The feed is searched over meta.published_within_days, restricted
to the resolved language and to posts above minEngagement total
engagement, then narrowed by the outlier floor and any media
shapes. A short or empty data may therefore be those filters at
work rather than a topic with no coverage —
meta.narrowed_to is present when the caller narrowed beyond the
default and the answer is short, and names what to widen; one of
those filters may be why, so offer to widen rather than conclude
the topic is unwritten.
metrics.views is null whenever LinkedIn did not expose
impressions, which is the usual case for another author’s post —
it is not a zero. permalink and author.profile_url are
browsable URLs; id is the internal feed row id and is not
accepted by any other endpoint.
Required scope: analytics:read.
curl --request GET \
--url https://api.scripe.io/v1/viral-posts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scripe.io/v1/viral-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/viral-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/viral-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/viral-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/viral-posts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scripe.io/v1/viral-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": "<string>",
"content": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"linkedin_post_id": "<string>",
"permalink": "<string>",
"content_type": "<string>",
"post_type": "<string>",
"media_format": "carousel",
"outlier_multiplier": 123,
"outlier_baseline": "AUTHOR",
"metrics": {
"likes": 123,
"comments": 123,
"reactions": {
"appreciation": 123,
"empathy": 123,
"interest": 123,
"maybe": 123
},
"total_engagement": 123,
"views": 123
},
"author": {
"name": "<string>",
"slogan": "<string>",
"profile_url": "<string>",
"handle": "<string>"
}
}
],
"meta": {
"query": "<string>",
"returned": 123,
"sort_by": "<string>",
"language": "<string>",
"min_engagement": 123,
"limit": 123,
"published_within_days": 123,
"min_outlier_multiplier": 123,
"media_formats": [
"<string>"
],
"narrowed_to": {
"min_outlier_multiplier": 123,
"media_formats": [
"<string>"
],
"published_within_days": 123
}
}
}{
"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"
Free-text topic / theme.
"personal branding for founders"
relevance, engagement, recent all, english, german x >= 0Minimum outlier ratio — how far a post beat its OWN author's median engagement. Defaults to 2. Pass 0 to opt out of the outlier floor entirely. The stored ratio is capped at 10, so a value above 10 matches nothing.
x >= 0Comma-separated media shapes to keep. carousel is a swipeable
deck (a document post), multi_image several photos in one
post, image at most one. An unrecognised value is refused
rather than ignored.
"carousel,multi_image"
How far back to search, in days.
1 <= x <= 180Page size. Default 12, max 30. Values above the max are clamped silently; only a non-integer or a value below 1 is rejected with bad_pagination.
1 <= x <= 30