API REFERENCE
Get Post Stats
On this page
/api/posts/{id}/statsThe platform's own analytics for one post published with Create Post, read live through the same connection that published it. Currently LinkedIn company-page posts only; other platforms and LinkedIn personal (member) posts return 422. Each call is a fresh read and as_of records when it happened. Each successful read costs $0.001 (1,000 credits), charged to the caller after LinkedIn returns the numbers. Credits are checked first; a request that fails (4xx/5xx) is not charged.
Authentication
x-api-key in header
bearerAuth bearer
Request
curl --request GET \
--url 'https://api.recoupable.dev/api/posts/YOUR_ID/stats' \
--header 'x-api-key: YOUR_API_KEY'Replace the YOUR_ placeholders with your values. Required query parameters are included; optional parameters are listed below.
Try it
Fill in the fields, send the request from your browser, and read the live response. The curl below updates as you type.
Kept in this browser tab only and cleared when it closes.
curl --request GET \
--url 'https://recoup-api.vercel.app/api/posts/{id}/stats'Parameters
Path parameters
idstringrequiredThe post's id, from Create Post.
Responses
200The post's current stats.+
application/json
statusstring · enumrequiredStatus of the request
Values: "success"
statsobjectrequiredProperties for stats
post_idstringrequiredThe post's id.
Example: "3f8e2c1a-5b7d-4e9f-a1c2-6d4b8e0f2a91"
format: uuid
platformstringrequiredThe platform the post was published to.
Example: "linkedin"
post_urlstringrequiredThe live post's URL.
Example: "https://www.linkedin.com/feed/update/urn:li:share:7389012345678901248"
impressionsinteger | nullrequiredTimes the post was shown. null when the platform does not report it.
Example: 412
unique_impressionsinteger | nullrequiredDistinct members the post was shown to. null when the platform does not report it.
Example: 287
clicksinteger | nullrequiredClicks on the post's content, company name or logo. null when the platform does not report it.
Example: 19
likesinteger | nullrequiredLikes (reactions). null when the platform does not report it.
Example: 14
commentsinteger | nullrequiredComments. null when the platform does not report it.
Example: 3
sharesinteger | nullrequiredReposts. null when the platform does not report it.
Example: 2
engagementnumber | nullrequiredLinkedIn's engagement rate for the post, from 0 to 1. null when the platform does not report it.
Example: 0.0922
video_viewsinteger | nullrequiredTimes the post's video was viewed; LinkedIn counts a view at 3 or more seconds. null for non-video posts or when LinkedIn returns none.
Example: 356
viewersinteger | nullrequiredDistinct members who viewed the post's video. null for non-video posts or when LinkedIn returns none.
Example: 241
watch_time_msinteger | nullrequiredTotal time the post's video was watched, in milliseconds. null for non-video posts or when LinkedIn returns none.
Example: 2847000
as_ofstringrequiredServer time of the read.
Example: "2026-10-25T18:04:12Z"
format: date-time
400id is not a valid uuid.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
401Unauthorized.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
402Insufficient credits: the account has less than the $0.001 a read costs. Returned after the 400/401/403/404/409/422 checks and before LinkedIn is called, so nothing is read and nothing is charged.+
application/json
errorstring · enumrequiredValues: "insufficient_credits"
remaining_creditsintegerrequiredExample: 0
required_creditsintegerrequiredExample: 250000
billingUrlstringrequiredStatic link to the Recoup app, where a human can save a card and buy credits. It is a constant, not a freshly minted Stripe Checkout Session, so a credit-gated endpoint that keeps returning 402 creates nothing. To buy credits programmatically, call POST /api/credits/sessions.
Example: "https://app.recoupable.dev"
403Access denied to this post's account.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
404No post with that id.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
409The post cannot be read yet. Either it is not published (its status is not posted or it has no post_url; error Post is not published yet, so it has no stats), or the platform is not connected for the post's account (error linkedin is not connected for this account). This is checked before 422, so an unpublished post on any platform returns 409.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
422Stats are not supported for this post. Only LinkedIn company-page posts are supported today. Errors: Stats are not available for <platform> posts yet (any other platform), Stats are available for LinkedIn company-page posts only (a LinkedIn personal post), post_url does not name a LinkedIn post.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
500Internal server error.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
502The platform rejected the stats read, for example because the connection lacks analytics access (error LinkedIn rejected the stats read for this post).+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
Full specification
Download the OpenAPI file for complete schemas, constraints, and examples.
Download social.jsonView operation source
{
"operationId": "getPostsByIdStats",
"summary": "Get post stats",
"description": "The platform's own analytics for one post published with [Create Post](/api-reference/publishing/create), read live through the same connection that published it. Currently LinkedIn company-page posts only; other platforms and LinkedIn personal (member) posts return 422. Each call is a fresh read and `as_of` records when it happened. Each successful read costs $0.001 (1,000 credits), charged to the caller after LinkedIn returns the numbers. Credits are checked first; a request that fails (4xx/5xx) is not charged.",
"security": [
{
"apiKeyAuth": []
},
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
},
"description": "The post's id, from [Create Post](/api-reference/publishing/create)."
}
],
"responses": {
"200": {
"description": "The post's current stats.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostStatsResponse"
}
}
}
},
"400": {
"description": "`id` is not a valid uuid.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"401": {
"description": "Unauthorized.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"402": {
"description": "Insufficient credits: the account has less than the $0.001 a read costs. Returned after the 400/401/403/404/409/422 checks and before LinkedIn is called, so nothing is read and nothing is charged.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SocialInsufficientCreditsResponse"
}
}
}
},
"403": {
"description": "Access denied to this post's account.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"404": {
"description": "No post with that id.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"409": {
"description": "The post cannot be read yet. Either it is not published (its status is not `posted` or it has no `post_url`; error `Post is not published yet, so it has no stats`), or the platform is not connected for the post's account (error `linkedin is not connected for this account`). This is checked before 422, so an unpublished post on any platform returns 409.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"422": {
"description": "Stats are not supported for this post. Only LinkedIn company-page posts are supported today. Errors: `Stats are not available for <platform> posts yet` (any other platform), `Stats are available for LinkedIn company-page posts only` (a LinkedIn personal post), `post_url does not name a LinkedIn post`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"500": {
"description": "Internal server error.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"502": {
"description": "The platform rejected the stats read, for example because the connection lacks analytics access (error `LinkedIn rejected the stats read for this post`).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
}
}
}Data coverage
LinkedIn reports organic activity only, within a rolling 12-month window; a post older than 12 months returns what LinkedIn still holds.