/v1/social-postsPublish content to connected social platforms. Supports immediate publish, scheduled posts, drafts, per-platform captions, media attachments, threads, and first comments.
Overview
The social posts endpoint is the core of UniPost. It accepts a caption and a list of account IDs, and publishes the content to every connected platform in a single API call.
For multi-platform fan-out where each platform should get different copy, use platform_posts[] instead of caption + account_ids. Each entry becomes one platform post with its own caption, media, and options.
Posts that fail on some platforms but succeed on others return status: "partial" with per-platform results — the successful publishes are never rolled back.
Authentication
All requests require a Bearer token in the Authorization header.
Authorization: Bearer up_live_xxxxGet your API key at app.unipost.dev
Request
caption + account_ids (same caption everywhere) or platform_posts[] (different caption per platform). Mixing both is rejected with VALIDATION_ERROR.Examples
Basic — same caption to multiple platforms
const response = await fetch(
'https://api.unipost.dev/v1/social-posts',
{
method: 'POST',
headers: {
'Authorization': 'Bearer up_live_xxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
caption: 'Hello from UniPost! \u{1F680}',
account_ids: ['sa_instagram_123', 'sa_linkedin_456'],
}),
}
);
const { data } = await response.json();
console.log(data.id); // post_abc123
console.log(data.status); // "published"Advanced — per-platform captions + scheduling + idempotency
// Per-platform captions + scheduling + idempotency
const response = await fetch(
'https://api.unipost.dev/v1/social-posts',
{
method: 'POST',
headers: {
'Authorization': 'Bearer up_live_xxxx',
'Content-Type': 'application/json',
'Idempotency-Key': 'release-v1.4.0-2026-04-08',
},
body: JSON.stringify({
scheduled_at: '2026-05-01T09:00:00Z',
platform_posts: [
{
account_id: 'sa_twitter_789',
caption: 'v1.4 is live \u{1F389} webhooks + bulk publish',
},
{
account_id: 'sa_linkedin_456',
caption: 'We just shipped v1.4 with two features our customers have been asking for: webhook event subscriptions and a bulk publish endpoint that accepts up to 50 posts in one call.',
},
{
account_id: 'sa_bluesky_012',
caption: 'v1.4 shipped \u{2014} webhooks and bulk publish are live',
},
],
}),
}
);Response
{
"data": {
"id": "post_abc123",
"caption": "Hello from UniPost!",
"status": "published",
"scheduled_at": null,
"created_at": "2026-04-08T10:00:00Z",
"results": [
{
"social_account_id": "sa_instagram_123",
"platform": "instagram",
"status": "published",
"external_id": "17841234567890",
"published_at": "2026-04-08T10:00:01Z",
"error_message": null
},
{
"social_account_id": "sa_linkedin_456",
"platform": "linkedin",
"status": "published",
"external_id": "urn:li:share:7049876543210",
"published_at": "2026-04-08T10:00:02Z",
"error_message": null
}
]
}
}Status values: published (all platforms succeeded), scheduled (queued for future), partial (some succeeded, some failed), failed (all platforms failed), draft (saved, not published).
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Caption exceeds maximum length for twitter (280 characters)",
"issues": [
{
"field": "platform_posts[0].caption",
"code": "caption_too_long",
"message": "Caption is 312 characters, max is 280 for twitter"
}
]
}
}Error Codes
Platform-specific errors (e.g. Twitter rate limit, Instagram media rejection) are returned in results[].error_message per-platform, not as top-level errors.
Limits & Constraints
Platform caption limits
Changelog
- Added first_comment field on platform_posts[]
- Added media_ids (replaces media_urls for managed uploads)
- Added per-account monthly quota enforcement
- Added thread_position for Twitter + Bluesky threads
- Added bulk endpoint (POST /v1/social-posts/bulk)
- Added platform_posts[] for per-platform overrides
- Added scheduled_at for deferred publishing
- Added idempotency_key support
- Initial release — caption + account_ids shape