# SocialRobot.io API Documentation ## Overview SocialRobot.io provides a comprehensive API for scheduling and managing social media posts across multiple platforms including Instagram, Pinterest, X (Twitter), LinkedIn, Bluesky, TikTok, Mastodon, Threads, and Facebook. ## API versioning - Current version: **v1** (canonical base URL below). - Legacy unversioned paths under `https://socialrobot.io/api/` remain supported. - Breaking changes ship under a new major path (for example `/api/v2`). Deprecated routes may return `Deprecation` and `Sunset` headers before removal. ## Base URL ``` https://socialrobot.io/api/v1 ``` ## Authentication All API endpoints require authentication via API key in the request header: ``` x-api-key: YOUR_API_KEY ``` Get your API keys from: https://socialrobot.io/scheduler/api-keys ## Pricing Machine-readable plan limits and prices: https://socialrobot.io/pricing.md Human-readable pricing page: https://socialrobot.io/pricing ## MCP SocialRobot also exposes a remote MCP server for AI clients. ### MCP Base URL ``` https://socialrobot.io/api/mcp ``` ### MCP Discovery Endpoints ``` GET /.well-known/oauth-protected-resource GET /.well-known/oauth-protected-resource/api/mcp GET /.well-known/oauth-authorization-server GET /.well-known/oauth-authorization-server/api/auth GET /.well-known/openid-configuration GET /api/auth/.well-known/oauth-authorization-server GET /api/auth/.well-known/openid-configuration ``` ### MCP Authentication MCP supports two authentication methods: **OAuth (interactive clients)** - MCP uses Better Auth as an OAuth 2.1 + OIDC authorization server - Human users authenticate through the normal login flow at `/login` - MCP clients can use OAuth discovery and dynamic client registration - Dynamic client registration endpoint: `POST /api/auth/oauth2/register` - OAuth authorize endpoint: `GET /api/auth/oauth2/authorize` - OAuth token endpoint: `POST /api/auth/oauth2/token` **API key (Bearer or header)** - Create a key at https://socialrobot.io/scheduler/api-keys (new keys start with `sk_`) - Send it as `Authorization: Bearer sk_...` or `x-api-key: sk_...` - Bearer tokens that start with `sk_` are treated as API keys; other Bearer tokens are OAuth access tokens - Existing unprefixed keys still work in the `x-api-key` header ### MCP client config (mcp.json) Without API key (OAuth): ```json { "mcpServers": { "socialrobot": { "url": "https://socialrobot.io/api/mcp" } } } ``` With API key: ```json { "mcpServers": { "socialrobot": { "url": "https://socialrobot.io/api/mcp", "headers": { "Authorization": "Bearer sk_YOUR_API_KEY" } } } } ``` ### MCP Scopes - `openid` - `profile` - `email` - `offline_access` - `accounts:read` - `publishing:read` - `publishing:write` - `media:read` - `analytics:read` - `audience:read` - `content:generate` - `usage:read` - `media:write` Default client registration scopes: - `openid` - `profile` - `email` - `offline_access` - `accounts:read` - `publishing:read` - `publishing:write` - `media:read` - `media:write` - `analytics:read` - `audience:read` - `content:generate` - `usage:read` ### MCP Tools - `list_connected_accounts` - `list_projects` - `get_plan_usage` - `get_project_profile` - `get_project_tags` - `get_media_upload_url` - `upload_media` - `create_post` - `list_posts` - `update_post` - `delete_post` - `reschedule_post` - `get_performance_report` - `get_account_analytics` - `get_post_analytics` - `get_posts_with_analytics` - `get_follower_demographics` - `get_pinterest_top_pins` - `instagram_best_post_times` - `pinterest_list_boards` - `pinterest_create_board` - `tiktok_get_creator_info` - `linkedin_search_geo_locations` - `linkedin_search_people_mentions` - `linkedin_search_organizations` Before drafting a post, call `get_project_profile` (the project's offer, audience and writing voice) and `get_project_tags` (the hashtags per network and Threads topics the project uses most, without "#", most used first). Both take an optional `projectId` from `list_projects`; omit it for the default project. Before scheduling several posts, call `get_plan_usage`: the plan, scheduled posts used and left this calendar month (UTC; drafts don't count), social accounts used and left, and whether X is included. Limits are a number or "unlimited". `hasActivePlan: false` means nothing can be created yet, drafts included, until the user picks a plan at `pricingUrl`. ## Create accounts You can create and link social media accounts from the dashboard https://socialrobot.io//scheduler/accounts ## Discover connected accounts Before creating posts, fetch the connected account IDs you can publish to: ``` GET /accounts ``` Example response: ```json [ { "id": "instagram-account-id", "platform": "instagram", "handle": "socialrobot", "displayName": "SocialRobot", "profileImage": "https://...", "externalAccountId": "17841400000000000", "expiresAt": "2026-01-01T12:00:00.000Z" } ] ``` ## Core Workflow ### 1. Media Upload Process For posts containing media (images/videos), follow this 3-step process: #### Step 1: Get Presigned Upload URL ``` POST /media/upload-url Content-Type: application/json { "filename": "image.jpg", // Required: The filename of the media "contentType": "image/jpeg" // Required: The content type of the media } Response: { "presignedUrl": "https://...", // URL for file upload "url": "https://...", // Permanent file URL "key": "users/...", // File key "uploadMethod": "PUT", // Always "PUT" "contentType": "image/jpeg" // Content type } ``` #### Step 2: Upload File to Presigned URL ``` PUT {presignedUrl} Content-Type: {contentType} [Binary file data] ``` #### Step 3: Use Permanent URL in Post Creation Use the `url` from step 1 in your post creation requests. If the client cannot PUT to the presigned URL (ChatGPT's tool runtime often cannot), use the fallback. `sourceUrl` must already serve the file over https. Do not invent a host, and do not send `file://`, `/workspace`, or `example.com`. ``` POST /media/from-url Content-Type: application/json { "sourceUrl": "https://files.oaiusercontent.com/", "filename": "cozy-cat.png", "contentType": "image/png" } ``` ChatGPT MCP clients inject `file.download_url` as an https URL when the user attaches a file. Do not write that field yourself. The response `url` is the value to pass into `create_post`. Max 50MB. Prefer the presigned PUT path when the client can reach storage. ### 2. Optional Unified Multi-Platform Post Endpoint If you want to create one scheduled item that targets multiple connected accounts at once, use: ``` POST /posts ``` Example: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2026-01-01T12:00:00Z" }, "instagramTargets": [ { "accountId": "instagram-account-id", "caption": "Cross-posted from the unified endpoint", "mediaType": "IMAGE", "mediaUrl": "https://..." } ], "pinterestTargets": [], "twitterTargets": [], "linkedinTargets": [], "blueskyTargets": [], "tiktokTargets": [], "mastodonTargets": [], "threadsTargets": [], "facebookTargets": [] } ``` Response: ```json { "id": "post-id-123" } ``` The MCP `create_post` and `update_post` tools take the same targets. #### Reply chains on X and Threads (unified endpoint and MCP only) Pass an ordered `segments` array on a `twitterTargets[]` or `threadsTargets[]` item. The target's own `caption` and `medias` are the first post; each segment is published as a reply to the previous post. X allows 25 posts per thread (24 segments), Threads 20 (19 segments). Each post has the single-post character limit (X 280, or 25,000 for verified accounts; Threads 500). ```json "twitterTargets": [ { "accountId": "twitter-account-id", "caption": "First post of the thread", "medias": [], "segments": [ { "caption": "Second post, published as a reply", "medias": [] }, { "caption": "Third post", "medias": [] } ] } ] ``` On Threads, `topicTag`, `poll` and `linkAttachment` apply to the first post only. #### First comments - Instagram: `firstComment` on `instagramTargets[]`, max 2200 chars (unified endpoint and MCP; `POST /instagram/create` does not save it) - LinkedIn: `firstComment` on `linkedinTargets[]` or in `POST /linkedin/create`, max 1250 chars, with optional `firstCommentMentions` #### Instagram alt text (unified endpoint and MCP) `altText` (max 1000 chars) on an IMAGE target and on each carousel item in `medias[]`. Video has no alt text. ## Platform Endpoints ### Instagram ``` POST /instagram/create ``` Supports three media types: - **IMAGE**: Single images - **VIDEO**: Single videos (can be Reels or Stories) - **CAROUSEL**: Multiple images/videos (2-10 items) #### IMAGE Post Example: ```json { "scheduledFor": { "publish": "NOW" | "SCHEDULE" | "DRAFT", "date": "2024-01-01T12:00:00Z" // Required if SCHEDULE }, "post": { "accountId": "instagram-account-id", // Required "caption": "Check out this post!", // Required, max 2200 chars "mediaType": "IMAGE", // Required "mediaUrl": "https://...", // Required "isStory": false, // Optional, default false "location": { // Optional "id": "location-id", // Optional "name": "New York, NY", // Required if location provided "address": "123 Main St", // Optional "lat": 40.7128, // Optional "lng": -74.0060 // Optional }, "userTags": [ // Optional { "username": "friend", // Required "x": 0.5, // Required, position 0-1 "y": 0.3 // Required, position 0-1 } ], "hashtags": ["awesome", "photo"] // Optional, added to the end of the caption at publish } } ``` #### VIDEO Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "instagram-account-id", "caption": "My awesome video!", "mediaType": "VIDEO", "mediaUrl": "https://...", "coverUrl": "https://...", // Optional cover image "isStory": false, // Optional, default false "isReel": true, // Optional, default false "trialReel": { // Optional, Reels only "enabled": true, "graduationStrategy": "MANUAL" // or "SS_PERFORMANCE" }, "location": { /* same as IMAGE */ }, "userTags": [ /* same as IMAGE */ ], "hashtags": [ /* same as IMAGE */ ] } } ``` #### CAROUSEL Post Example: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "instagram-account-id", "caption": "Check out these photos!", "mediaType": "CAROUSEL", "medias": [ // Required, 2-10 items { "mediaType": "IMAGE", // Required: "IMAGE" or "VIDEO" "mediaUrl": "https://...", // Required "userTags": [ // Optional, per-media tags { "username": "friend", "x": 0.5, "y": 0.3 } ] }, { "mediaType": "VIDEO", "mediaUrl": "https://...", "coverUrl": "https://...", // Optional for videos "userTags": [] } ], "isStory": false, // Required "isReel": false, // Required "location": { /* same as above */ }, "userTags": [ /* global tags */ ], "hashtags": [ /* same as above */ ] } } ``` ### Twitter/X ``` POST /x/create ``` Supports IMAGE, VIDEO, and GIF media types. Maximum 4 media items per post. To publish a thread, use `segments` on `twitterTargets[]` with `POST /posts` or MCP `create_post` (see above). Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "twitter-account-id", // Required "caption": "Just posted something!", // Required, max 280 chars "medias": [ // Required { "mediaType": "IMAGE", // Required: "IMAGE", "VIDEO", or "GIF" "mediaUrl": "https://...", // Required "altText": "Description of image" // Optional, accessibility text }, { "mediaType": "VIDEO", "mediaUrl": "https://...", "altText": "Video description" } ] } } ``` ### LinkedIn ``` POST /linkedin/create ``` Supports IMAGE and VIDEO media types. #### IMAGE Post Example: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "linkedin-account-id", // Required "caption": "Excited to share...", // Required, max 3000 chars "visibility": "PUBLIC", // Optional: "PUBLIC" or "CONNECTIONS", default "PUBLIC" "firstComment": "Full write-up: https://example.com", // Optional, max 1250 chars "medias": { "mediaType": "IMAGE", // Required "medias": [ // Required, 1-20 images { "mediaUrl": "https://...", // Required "altText": "Professional photo" // Optional, max 120 chars } ] } } } ``` #### VIDEO Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "linkedin-account-id", "caption": "Check out this video!", "visibility": "PUBLIC", "medias": { "mediaType": "VIDEO", // Required "medias": [ // Required, exactly 1 video { "mediaUrl": "https://...", // Required "altText": "Video description", // Optional "videoMetadata": { // Required for videos "title": "My Video", // Optional "description": "Video desc", // Optional "aspectRatioWidth": 16, // Optional "aspectRatioHeight": 9 // Optional } } ] } } } ``` ### Pinterest ``` POST /pinterest/create GET /pinterest/boards/{accountId} POST /pinterest/boards/{accountId} GET /pinterest/boards/{accountId}/{boardId} ``` #### Get Boards: ``` GET /pinterest/boards/{accountId} Response: [ { "id": "board-id", "created_at": "2024-01-01T00:00:00Z", "board_pins_modified_at": "2024-01-01T00:00:00Z", "name": "My Board", "description": "Board description", "collaborator_count": 1, "pin_count": 5, "follower_count": 10, "media": { "pin_thumbnail_urls": ["https://..."] }, "owner": { "username": "myusername" }, "privacy": "PUBLIC", // "PUBLIC", "PROTECTED", or "SECRET" "sections": [ { "id": "section-id", "name": "Section Name" } ] } ] ``` #### Create Board: ``` POST /pinterest/boards/{accountId} { "name": "My New Board", // Required "description": "Board description", // Optional "privacy": "PUBLIC" // Optional: "PUBLIC" or "SECRET" } ``` #### Create IMAGE Pin: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "pinterest-account-id", // Required "boardId": "board-id", // Required "title": "DIY Home Decor", // Optional, max 100 chars "description": "Transform your space", // Optional, max 800 chars "link": "https://example.com", // Optional, max 2048 chars "altText": "Alt text description", // Optional, max 500 chars "dominantColor": "#FFFFFF", // Optional "boardSectionId": "section-id", // Optional "isStandard": true, // Optional "mediaType": "IMAGE", // Required "medias": { "mediaType": "IMAGE", // Required "medias": [ // Required, 1-5 images { "mediaUrl": "https://..." } ] } } } ``` #### Create VIDEO Pin: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "pinterest-account-id", // Required "boardId": "board-id", "title": "My Video Pin", "description": "Video description", "mediaType": "VIDEO", // Required "medias": { "mediaType": "VIDEO", // Required "medias": [ // Required, exactly 1 video { "mediaUrl": "https://...", // Required "coverImageUrl": "https://...", // Optional "coverImageContentType": "image/jpeg", // Optional "coverImageKeyFrameTime": 5.0 // Optional, seconds } ] } } } ``` ### TikTok #### Creator info (call first) ``` GET /tiktok/creator-info/{accountId} ``` Returns `display_name` (nickname), `avatar_url`, `privacy_level_options`, `max_video_post_duration_sec`, and interaction disables. For `DIRECT_POST`, pick `privacyLevel` from `privacy_level_options`. Inbox `UPLOAD` posts set visibility in the TikTok app. Do not invent posting quotas from creator info — TikTok returns cap errors on publish init. #### Analytics (public videos) Requires `video.list` on the connected account (reconnect if missing). - Account totals: `get_account_analytics` with `platform: "tiktok"` - Public video list with views/likes/comments/shares: `get_posts_with_analytics` with `platform: "tiktok"` (optional `startDate`/`endDate` filter by create time) Private and inbox-draft videos are not returned by TikTok’s Display API. #### Create post ``` POST /tiktok/create ``` Also supported via unified `POST /posts` with `tiktokTargets[]` (same per-target fields). **postMode** - `DIRECT_POST`: Publishes to the creator profile when the schedule runs. Requires `privacyLevel` from creator info. Supports comment / duet / stitch disables, commercial disclosure, and `is_aigc` on video. - `UPLOAD`: Sends media to the creator **TikTok inbox** as a draft. The creator must open the inbox notification and finish in the TikTok app. Poll until status `SEND_TO_USER_INBOX`. Requires `video.upload` scope. Privacy and interaction settings are set in the app, not on init. **privacyLevel**: Required for `DIRECT_POST` (must be one of `privacy_level_options`). Not sent for inbox `UPLOAD` (video or photo). `brandedContent: true` cannot be combined with `PRIVATE`. Supports **VIDEO** (1 video) and **IMAGE** slideshows (1-35 images). #### VIDEO — direct post ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "tiktok-account-id", "caption": "Scheduled TikTok", "privacyLevel": "PUBLIC", "postMode": "DIRECT_POST", "disableComment": false, "disableDuet": false, "disableStitch": false, "medias": { "mediaType": "VIDEO", "medias": [{ "mediaUrl": "https://example.com/video.mp4" }] } } } ``` #### VIDEO — inbox draft (UPLOAD) ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "tiktok-account-id", "caption": "Draft for review in TikTok app", "postMode": "UPLOAD", "medias": { "mediaType": "VIDEO", "medias": [{ "mediaUrl": "https://example.com/video.mp4" }] } } } ``` `privacyLevel` is not sent to TikTok for video inbox upload; the creator sets visibility in the app. #### IMAGE — inbox draft (UPLOAD) ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T14:00:00Z" }, "post": { "accountId": "tiktok-account-id", "caption": "Carousel draft", "title": "Summer set", "photoCoverIndex": 0, "postMode": "UPLOAD", "medias": { "mediaType": "IMAGE", "medias": [ { "mediaUrl": "https://example.com/1.jpg" }, { "mediaUrl": "https://example.com/2.jpg" } ] } } } ``` Inbox photo init only sends title, description, and cover index. Privacy, comments, music, and commercial labels are finished in the TikTok app. #### TikTok media & validation - **Video**: MP4, WebM, MOV; 3-600s (per-account max from creator info); up to 4GB - **Images**: JPG, JPEG, WebP; up to 20MB each; 1-35 images - **Caption**: max 2200 chars (photos use description limit in app UI) - **Title**: max 90 chars (image posts) - **Commercial content**: if `isCommercialContent` is true, at least one of `yourBrand` or `brandedContent` must be true; `brandedContent` cannot use `PRIVATE` ### Threads ``` POST /threads/create ``` Supports text posts with optional media, polls, topic tags, and link attachments. #### Text-Only Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "threads-account-id", // Required "caption": "Just joined Threads! 🧵", // Required, max 500 chars "linkAttachment": "https://example.com", // Optional, only for text-only posts "topicTag": "Technology" // Optional, max 50 chars, no periods or ampersands } } ``` #### Media Post Example: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "threads-account-id", "caption": "Check out this amazing view!", "medias": [ // Optional, up to 20 items { "mediaType": "IMAGE", // Required: "IMAGE" or "VIDEO" "mediaUrl": "https://...", // Required "altText": "Beautiful mountain view" // Optional, accessibility text }, { "mediaType": "VIDEO", "mediaUrl": "https://...", "altText": "Sunset timelapse" } ] } } ``` #### Poll Post Example (Text-Only): ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "threads-account-id", "caption": "What's your favorite social platform?", "poll": { // Optional, only for text-only posts "options": [ // Required, 2-4 options "Instagram", // Max 25 chars per option "Threads", "Twitter", "TikTok" ] } } } ``` #### Threads Specifications: - **Caption**: Maximum 500 characters - **Images**: JPG, JPEG, PNG formats, up to 8MB each - **Videos**: MP4, MOV formats, 3-300 seconds, up to 1GB each - **Media Limit**: Up to 20 media items per post (carousel) - **Poll Options**: 2-4 options, max 25 characters each - **Topic Tags**: One per post, 1-50 characters, no periods (.) or ampersands (&); a leading "#" is removed. In a thread it goes on the first post. - **Link Attachments**: Only available for text-only posts - **Polls**: Only available for text-only posts - **Threads (reply chains)**: Up to 20 posts with `segments` on `threadsTargets[]` (unified endpoint and MCP) - **Not used**: `hashtags` and `userTags` are accepted but ignored. Write hashtags in the caption or use `topicTag`. ### Mastodon ``` POST /mastodon/create ``` Supports text posts with media, polls, and various visibility options. #### Text Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "mastodon-account-id", // Required "caption": "Hello Mastodon community!", // Required, max 500 chars (varies by instance) "visibility": "public", // Optional: "public", "unlisted", "private", "direct" "sensitive": false, // Optional, content warning flag "spoilerText": "", // Optional, content warning text "language": "en", // Optional, ISO 639-1 language code "medias": [] // Required (can be empty) } } ``` #### Media Post Example: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "mastodon-account-id", "caption": "Sharing some photos from my trip!", "visibility": "public", "sensitive": false, "medias": [ // Optional, up to 4 media items { "name": "photo1.jpg", // Required "mediaUrl": "https://...", // Required "size": 2048000, // Required, file size in bytes "mediaType": "IMAGE", // Required: "IMAGE" or "VIDEO" "alt": "Beautiful landscape photo" // Optional, max 1500 chars }, { "name": "video1.mp4", "mediaUrl": "https://...", "size": 15728640, "mediaType": "VIDEO", "alt": "Short video of the sunset" } ] } } ``` #### Poll Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "mastodon-account-id", "caption": "What should I work on next?", "visibility": "public", "poll": { // Optional, cannot be used with media "options": [ // Required, 2-4 options { "title": "New features" }, // Max 50 chars per option { "title": "Bug fixes" }, { "title": "Documentation" } ], "expiresIn": 86400, // Required, seconds (5 min to 7 days) "multiple": false, // Optional, allow multiple choices "hideTotals": false // Optional, hide vote counts until poll ends }, "medias": [] // Required (must be empty for polls) } } ``` #### Sensitive Content Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "mastodon-account-id", "caption": "This post contains spoilers for the latest movie.", "visibility": "public", "sensitive": true, // Content warning flag "spoilerText": "Movie Spoilers", // Content warning text shown before content "medias": [] } } ``` #### Mastodon Specifications: - **Caption**: Maximum 500 characters (varies by instance) - **Images**: JPG, JPEG, PNG, GIF, WebP formats, up to 10MB each, max 1920px resolution - **Videos**: MP4, WebM, MOV formats, up to 40MB, max 5 minutes, max 1920px resolution - **Media Limit**: Up to 4 media attachments per post - **Poll Options**: 2-4 options, max 50 characters each - **Poll Duration**: 5 minutes to 7 days - **Visibility Levels**: public, unlisted, private, direct - **Content Warnings**: Sensitive flag and spoiler text for content warnings - **Alt Text**: Up to 1500 characters for accessibility ### Facebook ``` POST /facebook/create ``` Supports text posts, single images/videos, and multiple images OR multiple videos (but not mixed). #### Text-Only Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "facebook-account-id", // Required "caption": "Hello Facebook world!" // Required, max 63206 chars } } ``` #### Single IMAGE Post Example: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "facebook-account-id", // Required "caption": "Check out this amazing photo!", // Required "medias": [ // Required { "mediaType": "IMAGE", // Required: "IMAGE" or "VIDEO" "mediaUrl": "https://...", // Required "altText": "Beautiful sunset photo" // Optional, accessibility text } ] } } ``` #### Single VIDEO Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "facebook-account-id", "caption": "Watch this awesome video!", "medias": [ { "mediaType": "VIDEO", "mediaUrl": "https://...", "altText": "Video description" } ] } } ``` #### Facebook REEL Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "facebook-account-id", "caption": "Check out my new Reel!", "medias": [ { "mediaType": "VIDEO", "mediaUrl": "https://...", "altText": "A fun dance video" } ], "isReel": true } } ``` #### Facebook SLIDESHOW Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "facebook-account-id", "caption": "My photo collection!", "medias": [ { "mediaType": "IMAGE", "mediaUrl": "https://...", "altText": "Photo 1" }, { "mediaType": "IMAGE", "mediaUrl": "https://...", "altText": "Photo 2" }, { "mediaType": "IMAGE", "mediaUrl": "https://...", "altText": "Photo 3" } ], "isSlideshow": true, "slideshowDuration": 3000, "slideshowTransition": 500 } } ``` #### Multiple IMAGES Post Example: ```json { "scheduledFor": { "publish": "SCHEDULE", "date": "2024-01-01T12:00:00Z" }, "post": { "accountId": "facebook-account-id", "caption": "Photo dump from my vacation!", "medias": [ // Required, 2-10 images { "mediaType": "IMAGE", "mediaUrl": "https://...", "altText": "Beach sunset" }, { "mediaType": "IMAGE", "mediaUrl": "https://...", "altText": "Mountain view" } ] } } ``` #### Multiple VIDEOS Post Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "facebook-account-id", "caption": "Check out these videos!", "medias": [ // Required, 2-10 videos { "mediaType": "VIDEO", "mediaUrl": "https://...", "altText": "First video" }, { "mediaType": "VIDEO", "mediaUrl": "https://...", "altText": "Second video" } ] } } ``` #### Facebook Specifications: - **Caption**: Maximum 63,206 characters - **Images**: JPG, JPEG, PNG, GIF, WebP formats, up to 25MB each - **Videos**: MP4, MOV formats, up to 2GB each - **Reels**: MP4, MOV formats, up to 2GB, 3-90 seconds duration, 9:16 aspect ratio (vertical) - **Slideshows**: JPG, JPEG, PNG, BMP, ICO formats, up to 10MB each, 3-7 images required - **Media Limit**: Up to 10 media items per post - **Important**: Cannot mix images and videos in the same post. Use only images OR only videos. - **Reels**: Can only contain a single video and are posted as Reels instead of regular videos. - **Slideshows**: Convert multiple images into a video with customizable duration and transitions. ### Bluesky ``` POST /bluesky/create ``` Example: ```json { "scheduledFor": { "publish": "NOW" }, "post": { "accountId": "bluesky-account-id", // Required "caption": "Just joined Bluesky! 🦋", // Required, max 300 chars "embed": { // Optional "mediaType": "IMAGE", // Required: "IMAGE" or "VIDEO" "medias": [ // Required { "url": "https://...", // Required "alt": "Welcome image" // Optional, alt text } ] } } } ``` #### IMAGE Embed (1-4 images): ```json "embed": { "mediaType": "IMAGE", "medias": [ { "url": "https://...", "alt": "Description 1" }, { "url": "https://...", "alt": "Description 2" } ] } ``` #### VIDEO Embed (exactly 1 video): ```json "embed": { "mediaType": "VIDEO", "medias": [ { "url": "https://...", "alt": "Video description" } ] } ``` ## Scheduling Options All platforms support three publishing modes: - `NOW`: Publish immediately - `SCHEDULE`: Schedule for a specific date/time (requires `date` field) - `DRAFT`: Save as draft without publishing When using `SCHEDULE`, `date` must be an ISO 8601 datetime **with a timezone offset or `Z`**. Example for 9:00 AM Uruguay: `2026-01-01T09:00:00-03:00`. Naive datetimes without an offset are rejected. The UI always sends offset ISO; the worker publishes at that absolute instant. ## Field Limits Summary | Platform | Caption Length | Media Limits | Special Notes | |-----------|----------------|--------------|---------------| | Instagram | 2200 chars | 1 (IMAGE/VIDEO), 2-10 (CAROUSEL) | Supports Stories, Reels, user tags, locations | | X/Twitter | 280 chars | 1-4 items | Supports IMAGE, VIDEO, GIF, threads of up to 25 posts | | LinkedIn | 3000 chars | 1-20 (IMAGE), 1 (VIDEO) | Requires video metadata for videos | | Pinterest | Title: 100, Desc: 800 | 1-5 (IMAGE), 1 (VIDEO) | Requires boardId, supports board sections | | TikTok | 2200 chars | 1-35 (IMAGE), 1 (VIDEO) | Privacy levels, post modes, interaction controls, commercial content, auto music | | Threads | 500 chars | 0-20 items | Polls/links only for text-only posts, topic tags, threads of up to 20 posts | | Mastodon | 500 chars | 0-4 items | Instance-specific limits, visibility levels, content warnings | | Facebook | 63206 chars | 1-10 items | Cannot mix images and videos in same post | | Bluesky | 300 chars | 1-4 (IMAGE), 1 (VIDEO) | Uses embed structure | ## Response Format Successful post creation returns: ```json { "id": "post-id-123" } ``` ## Error Responses - `400`: Invalid request data - `401`: Unauthorized - Invalid or missing API key - `403`: Forbidden - Account ownership issue - `404`: Account/Board not found (Pinterest) - `500`: Internal server error Error format: ```json { "code": "ERROR_CODE", "message": "Human readable error message", "issues": [] // Optional validation issues array } ``` ## Rate Limits HTTP API and MCP share fixed abuse-protection limits: 2,000 requests/minute per account and 200,000/day. These are not subscription-tier quotas. MCP metadata calls (initialize, resources/read, skills/list) are not counted toward the minute/day ceilings. ## Additional Resources - Account Creation: https://socialrobot.io/scheduler/accounts - Interactive API Docs: https://socialrobot.io/scheduler/api-docs - API Keys: https://socialrobot.io/scheduler/api-keys - OpenAPI Spec: https://socialrobot.io/api/openapi.json - MCP Endpoint: https://socialrobot.io/api/mcp - MCP OAuth Discovery: https://socialrobot.io/.well-known/oauth-authorization-server - MCP OpenID Configuration: https://socialrobot.io/api/auth/.well-known/openid-configuration - MCP Protected Resource: https://socialrobot.io/.well-known/oauth-protected-resource