# Publora — Complete API & Platform Reference > Publora is a social media management platform. Schedule and publish posts to 10 platforms from a single REST API or MCP server. Connect your AI assistant (Claude, Cursor) to manage social media in plain English. - API Base URL: `https://api.publora.com/api/v1` - MCP Server: `https://mcp.publora.com` - Dashboard: `https://app.publora.com` - Docs: `https://docs.publora.com` --- ## Authentication API key in request header. Generate at publora.com → API in sidebar. ``` x-publora-key: sk_YOUR_API_KEY ``` Key format: starts with `sk_`, ~50 characters (e.g. `sk_kzq5mjw.a1b2c3d4e5f6...`). Never expires until deleted. Cannot be generated programmatically — users must create keys manually in the dashboard. For MCP server, use Bearer token: ``` Authorization: Bearer sk_YOUR_API_KEY ``` For B2B workspace API calls, add: ``` x-publora-user-id: MANAGED_USER_ID ``` --- ## Pricing | Plan | Price | Posts | Platforms | Video Upload | |------|-------|-------|-----------|--------------| | Starter | FREE forever | 15/month | 1 account (LinkedIn & Bluesky only) | 50MB | | Pro | $5.99/mo ($2.99/mo yearly) per account | 100/account/month | All platforms | 100MB | | Premium | $9.99/mo ($5.99/mo yearly) per account | 500/account/month | All platforms | 250MB | All plans include AI Post Ideas and AI Editor. Pro and Premium are priced per connected social account. Starter plan is free forever. --- ## Supported Platforms LinkedIn, X (Twitter), Instagram, Threads, TikTok, YouTube, Facebook, Bluesky, Mastodon, Telegram. Social accounts are connected via OAuth in the dashboard (not via API). The API is for scheduling posts to already-connected accounts. --- ## API Endpoints ### Create Post `POST /api/v1/create-post` Schedule a post to one or more platforms. **Request:** ```json { "content": "Hello from Publora API!", "platforms": ["twitter-123456789", "linkedin-ABC123"], "scheduledTime": "2026-03-15T14:00:00.000Z" } ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | content | string | Yes | Post text (platform-specific limits apply) | | platforms | string[] | Yes | Array of platform IDs from /platform-connections | | scheduledTime | string | No | ISO 8601 UTC. Omit to create as draft | **Response:** ```json { "success": true, "postGroupId": "507f1f77bcf86cd799439011" } ``` **Errors:** 400 missing content/platforms, 400 invalid/past scheduled time, 401 invalid key, 403 subscription required or free plan limit reached. --- ### Get Post `GET /api/v1/get-post/:postGroupId` **Response:** ```json { "success": true, "postGroupId": "507f1f77bcf86cd799439011", "posts": [ { "platform": "twitter", "platformId": "123456789", "content": "Hello from Publora API!", "status": "published", "postedId": "1234567890123456789" } ] } ``` **Post statuses:** draft → scheduled → processing → published / failed / partially_published **Failed post error fields:** code, message, platformStatusCode, platformError, failedAt, retryable. **Error codes:** PLATFORM_AUTH_EXPIRED (not retryable), RATE_LIMITED (retryable), INVALID_CONTENT (not retryable), PLATFORM_SERVER_ERROR (retryable), NETWORK_ERROR (retryable), TIMEOUT_ERROR (retryable). --- ### List Posts `GET /api/v1/list-posts` | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | page | number | 1 | Page number | | limit | number | 20 | Items per page (1-100) | | status | string | all | draft, scheduled, published, failed, partially_published | | platform | string | all | twitter, linkedin, instagram, threads, tiktok, youtube, facebook, bluesky, mastodon, telegram | | sortBy | string | createdAt | createdAt, scheduledTime, updatedAt | | sortOrder | string | desc | asc or desc | | fromDate | string | — | ISO 8601 start date | | toDate | string | — | ISO 8601 end date | **Response includes** `pagination` object with page, limit, totalItems, totalPages, hasNextPage, hasPrevPage. --- ### Update Post `PUT /api/v1/update-post/:postGroupId` Reschedule or change post status. Only works on draft/scheduled posts. ```json { "status": "scheduled", "scheduledTime": "2026-03-20T10:00:00.000Z" } ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | status | string | No | "draft" or "scheduled" | | scheduledTime | string | No | ISO 8601 UTC (must be future) | At least one parameter required. Cannot update published/failed/processing posts. --- ### Delete Post `DELETE /api/v1/delete-post/:postGroupId` Deletes any post regardless of status. Atomic operation using MongoDB transaction. Removes post group, all platform posts, media records, and S3 files. --- ### Upload Media `POST /api/v1/get-upload-url` Get a pre-signed S3 URL for media upload. ```json { "fileName": "photo.jpg", "contentType": "image/jpeg", "type": "image", "postGroupId": "507f1f77bcf86cd799439011" } ``` **Response:** ```json { "success": true, "uploadUrl": "https://brandcraft-media.s3.amazonaws.com/...", "fileUrl": "https://media.publora.com/...", "mediaId": "507f1f77bcf86cd799439099" } ``` **Media workflow:** 1. Create draft post (omit scheduledTime) 2. Get upload URL 3. PUT file to uploadUrl with correct Content-Type header 4. Update post to set scheduledTime **Limits:** Platform-specific media limits apply (see table below). Instagram carousels: up to 10 images. Threads carousels: up to 10 images. Supported formats: JPEG, PNG, GIF, WebP (images), MP4, MOV (video). WebP auto-converted to JPEG. --- ### Platform Connections `GET /api/v1/platform-connections` Returns all connected social accounts with platform IDs, usernames, profile URLs, and token status. **Platform ID format examples:** - Twitter: `twitter-123456789` - LinkedIn: `linkedin-Tz9W5i6ZYG` - Instagram: `instagram-17841412345678` - Threads: `threads-17841412345678` - TikTok: `tiktok-7123456789` - YouTube: `youtube-UCxxxxxxxxxxxx` - Facebook: `facebook-112233445566` - Bluesky: `bluesky-did:plc:abc123` - Mastodon: `mastodon-109876543210` - Telegram: `telegram--1001234567890` **Token status values:** valid, expiring_soon (within 7 days), expired (reconnect needed), unknown (non-expiring tokens). --- ### LinkedIn Post Statistics `POST /api/v1/linkedin-post-statistics` ```json { "postedId": "urn:li:share:7123456789012345678", "platformId": "linkedin-Tz9W5i6ZYG", "queryTypes": "ALL" } ``` **Metrics:** IMPRESSION, MEMBERS_REACHED, RESHARE, REACTION, COMMENT. Use `queryType` for single metric (returns `count`), `queryTypes` for multiple or "ALL" (returns `metrics` object). --- ### LinkedIn Account Statistics `POST /api/v1/linkedin-account-statistics` ```json { "platformId": "linkedin-Tz9W5i6ZYG", "queryTypes": "ALL", "aggregation": "DAILY", "dateRange": { "start": {"year": 2026, "month": 3, "day": 1}, "end": {"year": 2026, "month": 3, "day": 15} } } ``` --- ### LinkedIn Reactions **Add:** `POST /api/v1/linkedin-reactions` ```json { "postedId": "urn:li:share:7123456789", "reactionType": "LIKE", "platformId": "linkedin-Tz9W5i6ZYG" } ``` **Remove:** `DELETE /api/v1/linkedin-reactions?postedId=urn:li:share:7123456789&platformId=linkedin-Tz9W5i6ZYG` **Reaction types:** LIKE, PRAISE, EMPATHY, INTEREST, APPRECIATION, ENTERTAINMENT. --- ### Webhooks `POST /api/v1/webhooks` — Register webhook URLs for post status change events. --- ## Platform Character Limits | Platform | Limit | Notes | |----------|-------|-------| | Twitter/X | 280 (Premium: 25,000) | Threading supported | | LinkedIn | 3,000 | First 210 chars visible in feed | | Instagram | 2,200 | Images auto-converted to JPEG | | Threads | 500 (Text posts: 10,000) | Max 5 links | | TikTok | 2,200 (API) | Video required | | YouTube | 100 title / 5,000 description | Video required | | Facebook | 63,206 | Best under 80 chars | | Mastodon | 500 | Instance-configurable | | Bluesky | 300 | Links don't count toward limit | | Telegram | 4,096 | Bot limit: 1,024 | ## Media Requirements by Platform | Platform | Text-Only | Image Required | Video Required | Max Images | Max Image Size | Max Video Size | Max Video Duration | |----------|-----------|----------------|----------------|------------|----------------|----------------|-------------------| | Twitter | Yes | No | No | 4 | 5 MB | 512 MB | 2 min | | LinkedIn | Yes | No | No | 20 | 5 MB | 500 MB | 30 min | | Instagram | No | Yes | No | 10 (carousel) | 8 MB | 300 MB | 90s (Reels) | | Threads | Yes | No | No | 10 | 8 MB | 500 MB | 5 min | | TikTok | No | No | Yes | — | — | 4 GB | 10 min | | YouTube | No | No | Yes | — | — | 256 GB (Publora upload cap: 512 MB) | 12 hours | | Facebook | Yes | No | No | 10 | 10 MB | 2 GB (Publora upload cap: 512 MB) | 45 min | | Bluesky | Yes | No | No | 4 | 1 MB (max 2000x2000) | 100 MB | 3 min | | Mastodon | Yes | No | No | 4 | 16 MB | ~99 MB | No limit | | Telegram | Yes | No | No | 10 | 10 MB | 50 MB (Publora upload cap: 512 MB) | No limit | ## Platform Rate Limits | Platform | Posting Limit | |----------|---------------| | Instagram | 50 posts/24hr | | Threads | 250 posts/24hr | | TikTok | 15-20/day, max 2/minute | | Facebook | 30 Reels/day | | Bluesky | 25 videos/day | | Mastodon | 30 media/30min | | Telegram | 30 msg/sec, 20/min per group | ## Critical Platform Notes - **Instagram:** Business account recommended (Creator may also work). Images auto-converted to JPEG. Cannot mix images and videos. - **TikTok:** Video-only platform — cannot post images. Unaudited apps post private videos only. - **Bluesky:** Strict 1 MB image limit — compress to 80-85% JPEG quality. Max 2000x2000 pixels. - **LinkedIn:** No organic carousel via API. @mentions use URN format: `@{urn:li:person:ID|Name}` or `@{urn:li:organization:ID|Name}`. --- ## MCP Server Connect your AI assistant to Publora. Server URL: `https://mcp.publora.com` ### Setup **Claude Code:** ```bash claude mcp add publora --transport http https://mcp.publora.com --header "Authorization: Bearer sk_YOUR_API_KEY" ``` **Claude Desktop / Cursor / VS Code:** ```json { "mcpServers": { "publora": { "type": "http", "url": "https://mcp.publora.com", "headers": { "Authorization": "Bearer sk_YOUR_API_KEY" } } } } ``` ### Available MCP Tools (18 tools) **Posts:** - `list_posts` — List posts with filters (status, platform, date range, pagination) - `create_post` — Create and schedule a post (content, platforms, scheduledTime) - `get_post` — Get post details by postGroupId - `update_post` — Reschedule or change status (postGroupId, status, scheduledTime) - `delete_post` — Delete a post (postGroupId) - `get_upload_url` — Get pre-signed URL for media upload **Connections:** - `list_connections` — List all connected social media accounts **LinkedIn Analytics:** - `linkedin_post_stats` — Get engagement metrics for a post - `linkedin_account_stats` — Get aggregated account statistics - `linkedin_followers` — Get follower count or growth - `linkedin_profile_summary` — Get combined profile overview - `linkedin_create_reaction` — React to a LinkedIn post - `linkedin_delete_reaction` — Remove reaction from a post - `linkedin_create_comment` — Comment on a LinkedIn post - `linkedin_delete_comment` — Delete a comment **Workspace (B2B):** - `list_workspace_users` — List team members - `create_workspace_user` — Add new user - `workspace_detach_user` — Remove user ### Example MCP Usage Natural language commands: - "Schedule a LinkedIn post for tomorrow at 9am: 'Excited to share our Q1 results!'" - "How did my posts perform this week?" - "Post 'We're hiring!' to all my accounts" - "What do I have scheduled for next week?" - "Delete the post I scheduled for Friday" --- ## Scheduling ### Post Lifecycle ``` draft → scheduled → processing → published / failed / partially_published ``` ### Key Rules - `scheduledTime` must be ISO 8601 UTC: `YYYY-MM-DDTHH:mm:ss.sssZ` - Must be in the future (past times return 400) - Scheduler runs every minute — schedule at least 2 minutes ahead - Stagger multiple posts by minutes ### Media Upload Workflow ``` 1. POST /create-post (no scheduledTime = draft) 2. POST /get-upload-url 3. PUT {uploadUrl} with file binary 4. PUT /update-post/:id (set scheduledTime to schedule) ``` --- ## Validation Error Codes **Content:** CONTENT_TOO_LONG, CONTENT_TOO_SHORT, CONTENT_REQUIRED, CONTENT_OR_MEDIA_REQUIRED. **Media:** MEDIA_REQUIRED, MEDIA_SIZE_EXCEEDED, MEDIA_COUNT_EXCEEDED, MEDIA_TYPE_NOT_SUPPORTED, MEDIA_DIMENSIONS_INVALID, IMAGES_NOT_SUPPORTED. **Video:** VIDEO_REQUIRED, VIDEO_DURATION_EXCEEDED, VIDEO_DURATION_TOO_SHORT, VIDEO_NOT_SUPPORTED. --- ## Code Examples ### JavaScript — Create Post ```javascript const response = await fetch('https://api.publora.com/api/v1/create-post', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_API_KEY' }, body: JSON.stringify({ content: 'Hello from Publora API!', platforms: ['twitter-123456789', 'linkedin-ABC123'], scheduledTime: '2026-03-15T14:00:00.000Z' }) }); const { postGroupId } = await response.json(); ``` ### Python — List Scheduled Posts ```python import requests response = requests.get( 'https://api.publora.com/api/v1/list-posts', headers={'x-publora-key': 'sk_YOUR_API_KEY'}, params={'status': 'scheduled', 'limit': 50, 'sortBy': 'scheduledTime', 'sortOrder': 'asc'} ) for post in response.json()['posts']: print(f"{post['postGroupId']}: {post['content'][:50]}... → {post['scheduledTime']}") ``` ### cURL — Create Post ```bash curl -X POST https://api.publora.com/api/v1/create-post \ -H "Content-Type: application/json" \ -H "x-publora-key: sk_YOUR_API_KEY" \ -d '{ "content": "Hello from Publora API!", "platforms": ["twitter-123456789"], "scheduledTime": "2026-03-15T14:00:00.000Z" }' ``` ### JavaScript — Upload Image and Schedule ```javascript // 1. Create draft const { postGroupId } = await (await fetch('https://api.publora.com/api/v1/create-post', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-publora-key': KEY }, body: JSON.stringify({ content: 'Check this out!', platforms: ['twitter-123'] }) })).json(); // 2. Get upload URL const { uploadUrl } = await (await fetch('https://api.publora.com/api/v1/get-upload-url', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-publora-key': KEY }, body: JSON.stringify({ fileName: 'photo.jpg', contentType: 'image/jpeg', type: 'image', postGroupId }) })).json(); // 3. Upload file await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': 'image/jpeg' }, body: fileBuffer }); // 4. Schedule await fetch(`https://api.publora.com/api/v1/update-post/${postGroupId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'x-publora-key': KEY }, body: JSON.stringify({ status: 'scheduled', scheduledTime: '2026-03-15T14:00:00.000Z' }) }); ``` --- ## Support - Discord: https://discord.gg/KYZGsH7Mfb - Email: sev@publora.com - Twitter: @publora_