SocialCannon provides a REST API for publishing to 6 social platforms (Twitter, Facebook, Instagram, LinkedIn, TikTok, YouTube), analytics, engagement monitoring, A/B testing, AI-powered content repurposing, and 23 MCP tools for AI agents. OpenClaw ready.
curl -X POST https://socialcannon.app/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{ "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_SECRET" }'curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "ACCOUNT_ID",
"content": "Hello from SocialCannon!"
}'The mediaUrls field accepts any public URL — you can use an image already hosted elsewhere, or upload via the media endpoint first.
Option A: Use an external URL directly
curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "ACCOUNT_ID",
"content": "Check out this photo!",
"mediaUrls": ["https://example.com/photo.jpg"]
}'Option B: Upload via signed URL (direct-to-GCS)
# Step 1: Ask for a signed PUT URL
curl -X POST https://socialcannon.app/api/v1/media/upload-init \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "filename": "photo.jpg", "contentType": "image/jpeg", "size": 123456 }'
# Returns: { "success": true, "data": { "uploadUrl": "...", "publicUrl": "...", "requiredHeaders": { ... } } }
# Step 2: PUT the bytes directly to GCS (file size up to 4GB)
curl -X PUT "<uploadUrl>" \
-H "Content-Type: image/jpeg" \
-H "x-goog-acl: public-read" \
--data-binary "@photo.jpg"
# Step 3: Finalize so usage counter increments and lifecycle clock starts
curl -X POST https://socialcannon.app/api/v1/media/upload-complete \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "publicUrl": "<publicUrl from step 1>" }'
# Step 4: Use the publicUrl when creating the post
curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "ACCOUNT_ID",
"content": "Check out this photo!",
"mediaUrls": ["<publicUrl>"]
}'Bytes go straight to Google Cloud Storage — SocialCannon never touches them, so files up to 4GB upload cleanly. Up to 4 images for Twitter, 10 for Instagram carousels.
curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "ACCOUNT_ID",
"content": "Scheduled post!",
"scheduledAt": "2026-04-01T10:00:00Z"
}'SocialCannon uses OAuth2 client_credentials flow. Exchange yourclient_id andclient_secret for a JWT token that expires in 1 hour.
Include the token in all authenticated requests:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Available scopes:
Rate limiting:
Free: 30 requests/minute. Pro: 300 requests/minute. Returns 429 with Retry-After header when exceeded.
2 connected accounts
10 posts/period, 2 scheduled
3 Twitter/X posts per period (hard cap)
Twitter, Facebook
No analytics, no replies
2-item threads, 5 tracked links
3 media uploads/period
30 API requests/min
Unlimited accounts & posts (free platforms)
50 Twitter/X posts per period
All 4 platforms unlocked
Full analytics with history
25-item threads, unlimited tracked links
Engagement replies, A/B testing (5×4)
Timing suggestions, 90-day calendar
300 API requests/min
Everything in Pro
250 Twitter/X posts per period
A/B testing (20 concurrent, 6 variants)
365-day calendar
Priority support
600 API requests/min
Custom Twitter/X quota
Custom rate limits + SLA
Dedicated support
Custom integrations
1200 API requests/min default
{
"success": true,
"data": { ... }
}{
"success": false,
"error": "Description of the error",
"code": "LIMIT_EXCEEDED",
"details": { ... }
}Getting started — publish your first postPublic1. Get your client_id and client_secret from the SocialCannon dashboard 2. Get a token: POST /api/v1/auth/token with your credentials 3. Connect a social account: redirect user to /api/connect/:platform?client_id=YOUR_ID 4. List connected accounts: GET /api/v1/accounts (use the account id from the response) 5. Publish a post: POST /api/v1/posts with accountId, content, and optional mediaUrls
// Example: publish a text post to a connected account
POST /api/v1/posts
Authorization: Bearer YOUR_TOKEN
{
"accountId": "your-account-id",
"content": "Hello from SocialCannon!"
}
// Response:
{
"success": true,
"data": {
"id": "post-id",
"status": "published",
"platformPostId": "...",
"platformPostUrl": "https://..."
}
}Platform capabilitiesPublicEach platform supports different content types. Use the mediaType field in platformOptions to control how content is published.
Facebook:
• Feed post (text, images, or video)
• Story (single image or video, mediaType: "story")
• Reel (single video, mediaType: "reel")
Instagram:
• Feed post (images required, up to 10 for carousel)
• Story (single image or video, mediaType: "story")
• Reel (single video, mediaType: "reel")
Twitter/X:
• Tweet (text, optional images or video)
TikTok:
• Video post (single video required)
• Photo carousel (multiple images, up to 35)
• Story (single image or video, mediaType: "story")
• privacyLevel required on all posts (use get_tiktok_creator_info to get allowed values)
YouTube:
• Video upload (single video, mediaType: "feed")
• Short (single vertical video ≤60s, mediaType: "short")
• Community post (text only, mediaType: "community")/api/v1/auth/tokenPublicExchange client credentials for a JWT access token (OAuth2 client_credentials flow).
{ "client_id": "...", "client_secret": "sc_..." }{ "access_token": "eyJ...", "token_type": "bearer", "expires_in": 3600 }/api/v1/auth/clientsList all API clients.
/api/v1/auth/clientsCreate a new API client. Returns the secret once.
{ "name": "My Client", "scopes": ["posts:read", "posts:write"] }/api/v1/postsList posts. Supports cursor-based pagination.
/api/v1/postsCreate a post. Omit scheduledAt to publish immediately. Media URLs must be publicly accessible. Maximum scheduling window: 30 days.
{
"accountId": "abc123",
"content": "Hello world!",
"mediaUrls": ["https://example.com/video.mp4"],
"scheduledAt": "2026-04-01T10:00:00Z",
"platformOptions": {
"mediaType": "reel"
}
}// Success:
{ "success": true, "data": { "id": "...", "status": "published", "platformPostId": "...", "platformPostUrl": "..." } }
// Error:
{ "success": false, "error": "Publishing failed: ..." }/api/v1/posts/:idGet a single post by ID.
/api/v1/posts/:idUpdate a draft or scheduled post.
{ "content": "Updated text", "scheduledAt": "..." }/api/v1/posts/:idDelete a post. If published, also attempts platform-side deletion.
/api/v1/posts/threadCreate a multi-part thread (Twitter thread or Instagram carousel). Publishes immediately or schedules for later. Maximum scheduling window: 30 days.
{
"accountId": "abc123",
"items": [
{ "content": "Thread part 1..." },
{ "content": "Thread part 2...", "mediaUrls": ["https://..."] }
],
"scheduledAt": "2026-04-01T10:00:00Z"
}/api/v1/accountsList all connected social media accounts.
/api/v1/accounts/:idGet a single account. Tokens are stripped from the response.
/api/v1/accounts/:idDisconnect a social account.
/api/v1/accounts/:id/tiktok/creator-infoTikTok only. Returns the account's live posting capabilities for building a compliant post UI: allowed privacy levels (`privacyLevelOptions`), whether Comment/Duet/Stitch are disabled, the creator nickname/username, and `maxVideoPostDurationSec`.
{ "success": true, "data": { "creatorNickname": "...", "creatorUsername": "...", "creatorAvatarUrl": "...", "privacyLevelOptions": ["PUBLIC_TO_EVERYONE","SELF_ONLY"], "commentDisabled": false, "duetDisabled": false, "stitchDisabled": false, "maxVideoPostDurationSec": 600 } }/api/v1/media/upload-initStep 1 — initialize a direct-to-GCS upload. Returns a 15-minute V4 signed PUT URL plus the final public URL. Enforces subscription quota before issuing the URL.
{ "filename": "clip.mp4", "contentType": "video/mp4", "size": 12345678 }{ "data": { "uploadUrl": "https://storage.googleapis.com/...", "publicUrl": "https://storage.googleapis.com/...", "requiredHeaders": { "Content-Type": "video/mp4", "x-goog-acl": "public-read" } } }{uploadUrl}PublicStep 2 — upload the file bytes directly to GCS using the signed URL from step 1. Must include the headers from `requiredHeaders` exactly or the signature check fails. Supports files up to 4GB.
Raw binary file contents200 OK (no body)/api/v1/media/upload-completeStep 3 — finalize the upload: stamps the lifecycle retention timer (customTime) and increments your `mediaUploadsThisPeriod` counter. Verifies the object exists in your client prefix before bookkeeping.
{ "publicUrl": "https://storage.googleapis.com/.../media/{clientId}/{uuid}.{ext}" }{ "data": { "url": "...", "filename": "...", "contentType": "...", "size": 102400 } }/api/v1/calendarGet a content calendar view with posts, gap analysis, and summary stats for a date range (max 90 days).
{ "posts": [...], "summary": { "totalPosts", "postsByPlatform", "postsByDay", "gaps": [{ "date", "dayOfWeek" }] } }/api/v1/calendar/slotsFind available time slots not occupied by scheduled or published posts (max 14-day range).
{ "slots": [{ "start", "end", "available" }], "totalSlots", "availableSlots", "occupiedSlots" }/api/v1/posts/:id/analyticsFetch live engagement metrics from the platform, store a snapshot, and return current + history.
{ "current": { "metrics": { "likes", "comments", "shares", "impressions", ... }, "fetchedAt" }, "history": [...] }/api/v1/analytics/summaryGet aggregate analytics across all posts for a date range.
{ "totalPosts", "totalLikes", "totalComments", "totalShares", "totalImpressions", "avgEngagementRate" }/api/v1/analytics/refreshBulk-refresh analytics for multiple posts (max 50 per request).
{ "postIds?": ["..."], "platform?": "twitter", "limit?": 20 }{ "refreshed": 15, "failed": 2, "results": [{ "postId", "success", "error?" }] }/api/v1/linksList tracked links with UTM parameters. Supports cursor-based pagination.
/api/v1/links/generateGenerate a UTM-tagged URL. Optionally save it for tracking.
{ "url": "https://example.com", "platform?": "twitter", "campaign?": "launch", "content?": "cta-btn", "postId?": "...", "save?": true }{ "originalUrl": "https://example.com", "trackedUrl": "https://example.com?utm_source=twitter&...", "linkId?": "..." }/api/v1/posts/:id/engagementsFetch comments and replies from the platform for a published post. Stores new engagements.
{ "engagements": [{ "type", "authorName", "content", "createdAt", ... }], "nextCursor?" }/api/v1/engagementsList all stored engagements across posts. Filter by read status for an inbox view.
/api/v1/engagements/:idMark an engagement as read.
/api/v1/engagements/:id/replyReply to a comment or mention directly on the social platform.
{ "content": "Thanks for the feedback!" }{ "platformReplyId": "...", "engagementId": "...", "repliedAt": "..." }/api/v1/posts/repurposeUse AI to adapt content for multiple platforms. Two modes: "preview" returns adapted variants for review, "post" adapts and publishes in one call. Content is humanized to remove AI writing patterns. Trusted clients bypass tier limits.
{
"sourceContent": "Your original text...",
"targetPlatforms": ["twitter", "facebook", "tiktok"],
"mode?": "preview",
"tone?": "professional",
"accountIds?": { "twitter": "acc_1", "facebook": "acc_2" },
"mediaUrls?": { "twitter": ["https://..."] },
"appendContent?": { "twitter": "Extra text for Twitter" },
"appendToAll?": "Appended to all platforms"
}preview: { "variants": [{ "platform", "content", "validation", "characterCount" }], "allValid" }
post: { "results": [{ "platform", "success", "postUrl?", "error?" }] }/api/v1/ab-testsCreate an A/B test. Behavior matches POST /api/v1/posts: if scheduledAt is omitted, all variants publish immediately to the platform; if scheduledAt is provided, all variants are scheduled for that time (must be within 30 days). Each variant is a separate post record. Auto-completes after minDurationHours. If ANY variant fails to publish during immediate mode, the endpoint returns 502 and the partial results are marked as failed — the winning-metric comparison at completion will only consider published variants. Per-variant mediaUrls optional. Not supported for TikTok accounts (A/B variants cannot set the privacy level TikTok requires) — returns 400 with code PLATFORM_UNSUPPORTED.
{
"accountId": "abc123",
"name": "CTA test",
"variants": [
{ "content": "Check out our new feature!", "mediaUrls": ["https://..."] },
{ "content": "You won't believe this new feature..." }
],
"metric?": "engagementRate | likes | impressions | clicks",
"minDurationHours?": 24,
"scheduledAt?": "2026-04-20T10:00:00Z"
}/api/v1/ab-testsList A/B tests. Filter by status.
/api/v1/ab-tests/:idGet test details with live per-variant analytics and current winner.
{ "variants", "variantResults": [{ "variantId", "metrics" }], "currentWinner", "confidence" }/api/v1/ab-tests/:id/completeForce-complete an active test and determine the winner.
/api/v1/ab-tests/:idCancel an active test. Variant posts remain as regular posts.
/api/v1/accounts/:id/timingAnalyze posting history and engagement to recommend optimal days and hours for posting.
{ "suggestions": [{ "day", "hour", "engagementRate", "sampleSize" }], "timezone" }/api/v1/platformsPublicList all supported platforms and their capabilities. Public endpoint, no auth required.
platformOptions referencePublicThe platformOptions field in POST /api/v1/posts accepts platform-specific options. Use mediaType to control how content is published.
mediaType values:
• "reel" — Facebook or Instagram Reel (single video, MP4/MOV)
• "story" — Facebook, Instagram, or TikTok Story (single media, 24h ephemeral)
• "short" — YouTube Short (single video, vertical ≤60s)
• "community" — YouTube Community tab post (text only)
Media rules:
• Stories, Reels, and Shorts accept only one media attachment
• Facebook/Instagram feed posts accept multiple images (carousel, max 10)
• TikTok photo carousel accepts up to 35 images
• TikTok video posts accept only one video
• YouTube videos/shorts accept only one video file
• All media URLs must be publicly accessible
YouTube-specific options:
• "title" — Video title (max 100 chars)
• "tags" — Comma-separated video tags
• "categoryId" — YouTube category ID (default: "10" Music)
• "privacyStatus" — "public", "unlisted", or "private"
TikTok-specific options (call GET /api/v1/accounts/:id/tiktok/creator-info first):
• "privacyLevel" — REQUIRED. One of: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY. Choose from privacyLevelOptions returned by creator-info. Omitting this field returns 400 TIKTOK_PRIVACY_REQUIRED.
• "disableComment" — boolean (default: false). Set true to disable comments. Must be true if commentDisabled is true on creator-info.
• "disableDuet" — boolean (default: false). Video only. Set true to disable duets. Must be true if duetDisabled is true on creator-info.
• "disableStitch" — boolean (default: false). Video only. Set true to disable stitches. Must be true if stitchDisabled is true on creator-info.
• "commercialContent" — boolean. Set true if the post promotes a brand, product, or service (enables commercial content disclosure).
• "brandOrganic" — boolean. Set true if promoting your own brand ("Your Brand" disclosure). Requires commercialContent: true.
• "brandedContent" — boolean. Set true for paid/third-party partnerships. Requires commercialContent: true. Cannot be combined with privacyLevel: SELF_ONLY./api/connect/:platformPublicInitiate OAuth connection for a social platform. Redirects to the platform's authorization page. Supported platforms: twitter, facebook, instagram, linkedin, tiktok, youtube.
/api/connect/:platform/callbackPublicOAuth callback handler. Exchanges authorization code for tokens and stores the connected account. Do not call directly — the platform redirects here after authorization.
Scope referencePublicScopes control what an API client can access. Assign scopes when creating a client via POST /api/v1/auth/clients.
posts:read — List and view posts
posts:write — Create, update, schedule posts
posts:delete — Delete posts
accounts:read — List connected accounts
accounts:write — Connect/disconnect social accounts
media:upload — Upload media files
analytics:read — View analytics and timing suggestions
engagements:read — View comments and replies
engagements:write — Reply to engagements, mark as read
links:read — View tracked links
links:write — Generate UTM-tagged links
clients:manage — Create and list API clientsSocial accounts are connected via browser-based OAuth flows. Redirect users to the connect endpoint for the desired platform:
| Platform | Connect URL | Notes |
|---|---|---|
| Twitter / X | /api/connect/twitter?client_id=... | OAuth2 with PKCE |
/api/connect/facebook?client_id=... | Page-level access | |
/api/connect/instagram?client_id=... | Requires media (no text-only) | |
/api/connect/linkedin?client_id=... | Post-only (no analytics/engagement) |
After authorization, the user is redirected back to your app with the account connected. Tokens are encrypted with AES-256-GCM before storage.
scheduledAt → immediate publishscheduledAt → scheduled, picked up by cron280 character limit
Up to 4 images per tweet
Threads via reply chains
v2 API for tweets, v1.1 for media upload
63,206 character limit
Page-level access tokens
Supports native scheduling via API
Full engagement + analytics support
2,200 character limit
Requires media (no text-only posts)
Max 10 carousel items, no API deletion
Threads use carousel container model
3,000 character limit
Text-only or single/multi-image posts
Threads combine items into one post
Post-only (no analytics or engagement)
SocialCannon includes an MCP server with 23 tools that expose all functionality for AI agents (Claude, GPT, etc.) via the @modelcontextprotocol/sdk over stdio transport.
Start the MCP server (repo):
npm run mcpOr run the published package directly (external clients):
npx -y @socialcannon/mcpSet SOCIALCANNON_CLIENT_ID and SOCIALCANNON_CLIENT_SECRET (from your dashboard) before running.
Configuration (preferred — auto-refreshes JWT):
MCP_CLIENT_ID=<your API client ID>
MCP_CLIENT_SECRET=<your API client secret>
SOCIALCANNON_API_URL=https://socialcannon.appAlternatively, set MCP_API_TOKEN with a pre-generated JWT (expires after 1 hour, no auto-refresh).
List all supported social media platforms and their capabilities.
Params: None
List all connected social media accounts.
Params: platform?
Fetch a TikTok account's posting capabilities — allowed privacy levels, disabled interactions, nickname, max video duration. Call before create_post for TikTok.
Params: accountId
Create and publish or schedule a social media post. For TikTok, privacyLevel is required (from get_tiktok_creator_info); also supports disableComment, disableDuet (video), disableStitch (video), commercialContent, brandOrganic, brandedContent.
Params: accountId, content, mediaUrls?, scheduledAt?, visibility?, privacyLevel?, disableComment?, disableDuet?, disableStitch?, commercialContent?, brandOrganic?, brandedContent?
List social media posts with optional filters.
Params: status?, platform?, accountId?, limit?
Get details of a specific post.
Params: postId
Update a draft or scheduled post.
Params: postId, content?, scheduledAt?
Delete a post (also removes from platform if published).
Params: postId
Get a content calendar view with posts, gaps, and summary stats.
Params: startDate, endDate, accountId?, platform?
Get engagement metrics (likes, comments, shares, impressions) for a post.
Params: postId
Get aggregate analytics across all posts for a date range.
Params: startDate, endDate, platform?, accountId?
Create a multi-part thread (Twitter thread or Instagram carousel).
Params: accountId, items[], scheduledAt?
Generate a UTM-tagged URL for tracking social media traffic.
Params: url, platform?, campaign?, content?, postId?, save?
List stored engagements (comments/replies) with optional filters.
Params: postId?, isRead?, limit?, cursor?
Reply to a comment/reply directly on the social platform.
Params: engagementId, content
List all unread engagements (inbox shortcut).
Params: limit?
Repurpose content for different platforms using AI. Supports preview mode (returns variants) and post mode (adapts and publishes). Content is humanized automatically.
Params: sourceContent, targetPlatforms[], mode?, tone?, accountIds?, mediaUrls?, appendContent?, appendToAll?
Create an A/B test with content variants for an account.
Params: accountId, name, variants[], metric?, minDurationHours?
Get A/B test results with per-variant analytics and winner.
Params: testId
Get recommended posting times based on historical engagement data.
Params: accountId, timezone?
Find the best available time slot for a new post based on calendar gaps and engagement data.
Params: accountId, startDate?, endDate?
Auto-fill a week with optimally timed posts across your connected accounts.
Params: accountId, startDate, posts[]
Hermes Agent (Nous Research) ships a first-class MCP client. Point it at the published @socialcannon/mcp package and all 23 tools load over npx — no repo clone.
mcp_servers:
socialcannon:
command: "npx"
args: ["-y", "@socialcannon/mcp"]
env:
SOCIALCANNON_CLIENT_ID: "your-client-id"
SOCIALCANNON_CLIENT_SECRET: "your-client-secret"Use the SOCIALCANNON_CLIENT_ID / SOCIALCANNON_CLIENT_SECRET from your dashboard. Run /reload-mcp in your Hermes session and the tools load automatically (Hermes also auto-reloads config edits within ~30s). Other MCP hosts share the same command, args, and env — Claude Desktop uses an equivalent JSON block (mcpServers) in claude_desktop_config.json.
SocialCannon is available as an OpenClaw skill. Install it from ClawHub so your AI assistant can publish, schedule, analyze, and manage social media posts through natural conversation.
openclaw skills install socialcannonAdd your SocialCannon API credentials to your openclaw.json config:
{
"skills": {
"entries": {
"socialcannon": {
"enabled": true,
"env": {
"SOCIALCANNON_CLIENT_ID": "your-client-id",
"SOCIALCANNON_CLIENT_SECRET": "your-client-secret"
}
}
}
}
}If you prefer manual installation, create this file at ~/.openclaw/workspace/skills/socialcannon/SKILL.md:
---
name: socialcannon
description: >
Publish, schedule, and manage social media posts across
Twitter/X, Facebook, Instagram, LinkedIn, TikTok, and YouTube.
Includes content calendar, analytics, A/B testing,
engagement inbox, AI content repurposing, and timing suggestions.
version: 1.0.0
metadata:
openclaw:
requires:
env:
- SOCIALCANNON_CLIENT_ID
- SOCIALCANNON_CLIENT_SECRET
bins:
- curl
primaryEnv: SOCIALCANNON_CLIENT_ID
emoji: "\U0001F4E3"
homepage: https://socialcannon.app
---
# SocialCannon
Social media publishing API with 23 MCP tools.
## Authentication
First, get a JWT token:
```bash
curl -X POST https://socialcannon.app/api/v1/auth/token \
-H "Content-Type: application/json" \
-d "{
\"client_id\": \"$SOCIALCANNON_CLIENT_ID\",
\"client_secret\": \"$SOCIALCANNON_CLIENT_SECRET\"
}"
```
Use the returned access_token as Bearer token for all
subsequent requests. Tokens expire after 1 hour.
## Core Operations
- POST /api/v1/posts — Create/publish a post
- GET /api/v1/posts — List posts (cursor pagination)
- POST /api/v1/posts/thread — Create thread/carousel
- GET /api/v1/calendar — Content calendar with gaps
- GET /api/v1/posts/:id/analytics — Engagement metrics
- GET /api/v1/engagements — Comment inbox
- POST /api/v1/engagements/:id/reply — Reply to comment
- POST /api/v1/posts/repurpose — AI content adaptation
- POST /api/v1/ab-tests — Create A/B test
- GET /api/v1/accounts/:id/timing — Best posting times
- POST /api/v1/links/generate — UTM-tagged URLCreate your first API client:
npx tsx scripts/create-client.ts "My App"This writes directly to Firestore and prints the client_id andclient_secret. Save the secret — it's only shown once.
Or use the dashboard:
Sign in with Google at /sign-in and an API client is automatically provisioned for your account.
Stuck on something? Reach out at support@socialcannon.app