Suggest covers
Suggest covers from a post's media: frames picked from its video, or — without a video — the best first image among 2+ images, ranked (rank 1 is best) with a one-line reason. Videos take two calls: the first starts frame extraction (status queued / running, about 10–30 s), call again after retryAfterSec to get ranked candidates; image posts come back ranked right away. Idempotent per media, so repeat calls are not billed again and never use the monthly AI quota. Only the post's shared media is used, and it must be stored on popkit. This endpoint changes nothing; apply a pick with Set cover.
/api/v1/posts/{postId}/cover-suggestionsAuthorization & guardrails
- Send
Authorization: Bearer <credential>; credentials are issued under Settings → Integrations → Long-lived credentials, see Authentication. - The credential needs scope
posts:write; the issuer's workspace role must also allow the action. - Rate limit: write tier, 10/min per credential; over the cap returns 429 with
Retry-After. - Idempotency: honoured when
Idempotency-Keyis present; optional otherwise.
Path parameters
postIdstringrequiredPost id.
Request body (application/json)
languageenum: zh · enoptionalLanguage of the reasons; defaults to Chinese and leaves existing reasons alone. If given and different from the existing reasons, only the reasons are rewritten — the ranking stays.
clientRequestIdstringoptionalIdempotency key (8–128 chars of [A-Za-z0-9_.:-]); prefer the Idempotency-Key header, which wins over the body.
Rules
- Each workspace can extract frames for up to 50 videos a day; beyond that you get rate_limited (params.reason = cover_daily_cap, retryAfterSec until the next UTC midnight).
- Candidates are cleaned up after 7 days; calling again after that generates a fresh set.
Responses
Success is HTTP 200 with a { data, meta } envelope; examples per status are on the right (at the end of the page on narrow screens). Every error is { error: { code, message, params }, meta } — branch on the code, not the message; see Errors.
| Status | Possible codes |
|---|---|
| 400 | invalid_input |
| 401 | unauthenticated |
| 402 | plan_limit |
| 403 | forbidden / workspace_not_found |
| 404 | not_found |
| 409 | no_account / needs_reauth / replay |
| 429 | rate_limited |
| 500 | internal |
| 502 | platform_error |
| 503 | external_writes_off |
Examples
curl -X POST "https://popkit.loxily.com/api/v1/posts/pst_01J8ZK3R7QW6M2/cover-suggestions" \
-H "Authorization: Bearer popkit_it_••••••••" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-2026-09-21-001" \
-d '{
"language": "zh"
}'{
"data": {
"postId": "pst_01J8ZK3R7QW6M2",
"suggestionId": "3f2b9c1e-6d0a-4e8b-9a7c-5b1d2e3f4a60",
"status": "queued",
"sourceKind": "video",
"engine": null,
"candidates": [],
"retryAfterSec": 5,
"next": "frames are still being extracted from the video (usually 10–30 s); call suggest_covers again with the same postId in 5s"
},
"meta": {
"requestId": "6a1d0c9e-2b7f-4e3a-8c5d-1f0e9b8a7c81"
}
}