# UT Social — public API and MCP server > UT Social (uptrended.social, by UpTrended) schedules and publishes social posts to > Instagram, Facebook, TikTok, X, Pinterest and YouTube from one calendar. The public > API and MCP server let your own AI agent draft and schedule those posts. Human docs: https://uptrended.social/docs ## Endpoints MCP (Streamable HTTP): https://uptrended.social/mcp REST: https://uptrended.social/api/v1 stdio bridge: npx -y @uptrended/mcp ## Authentication Bearer API key, created in the dashboard under Settings -> API keys. Authorization: Bearer utsk_live_<12 chars>.<43 chars> A key is scoped to ONE workspace. `organization_id` is never accepted in a request body — the key determines the workspace. API access requires a paid plan (Starter $19 / Growth $49 / Scale $99). The free Hobby plan has no API access. ## Scope presets read Read-only. Mutating tools are not exposed at all. draft Default and recommended. create_post saves a DRAFT for a human to approve; it does NOT schedule. The response says so — read it, do not assume a 200 means the post is scheduled. write Schedules and publishes to live social accounts, and can cancel, reschedule and delete. Granular scopes: accounts:read, media:read, media:write, posts:read, posts:draft, posts:write, usage:read. GET /api/v1/me returns exactly what a key was granted. ## The workflow list_accounts -> validate_post -> create_post 1. list_accounts — every post target needs a real social_account_id. They cannot be guessed from a handle or reused from an earlier session. 2. validate_post — a dry run. Writes nothing, consumes no quota, never touches a live account, and returns per-target errors. Iterate here until the payload is clean. Two or three validate round-trips before a create is normal. 3. create_post — one call carrying every target. Then read the response. ## Connecting a social account is NOT in the API Linking an Instagram, Facebook, TikTok, X, Pinterest or YouTube account is an interactive browser OAuth flow, deliberately excluded from the API. Connect accounts once at https://uptrended.social, after which they appear in list_accounts. If an account is missing, that is the answer — no endpoint will fix it. ## MCP tools list_accounts accounts:read Connected accounts and their ids list_media media:read Media library ingest_media media:write Fetch a public URL server-side (100 MB cap) validate_post posts:read Dry run — no writes, no quota create_post posts:draft (+ posts:write to schedule rather than draft) get_post posts:read One post with per-target status list_posts posts:read Keyset-paginated list cancel_post posts:write Stop a scheduled post, keep the record reschedule_post posts:write Move a scheduled post delete_post posts:write Delete a post get_usage usage:read Quota used and remaining Tools are filtered by the key's scopes: a read-only key does not see the mutating tools at all. ## REST reference — https://uptrended.social/api/v1 GET /me — Key identity, workspace, plan, scopes GET /usage usage:read Quota used and remaining GET /accounts accounts:read Connected social accounts GET /media media:read Media library POST /media/ingest media:write Fetch a public URL server-side, 100 MB cap POST /media/upload-url media:write Signed URL for a direct upload POST /media/register media:write Register an upload after the bytes land DELETE /media/:id media:write Delete media GET /posts posts:read Keyset paginated POST /posts/validate posts:read Dry run POST /posts posts:draft (+ posts:write to schedule) GET /posts/:id posts:read One post POST /posts/:id/cancel posts:write Cancel POST /posts/:id/reschedule posts:write Reschedule POST /posts/:id/retry posts:write Retry failed targets DELETE /posts/:id posts:write Delete ## Conventions Collections: { "data": [...], "next_cursor": "...", "has_more": true } Keyset pagination. Follow next_cursor while has_more is true. No page numbers. Errors: { "error": "...", "code": "...", "request_id": "..." } Mutations: accept an Idempotency-Key header. ## Per-platform rules (all enforced by validate_post) X 280 WEIGHTED characters. Every URL counts as 23 regardless of real length. CJK characters and most emoji count double. A 250-char draft plus two links does not fit. YouTube Descriptions cap at 5,000 UTF-8 BYTES, not characters. Emoji are 4 bytes each, so an emoji-heavy description can fail well under 5,000 characters. Titles cap at 100 characters. Instagram Captions cap at 2,200 characters. post_and_story forks into TWO independent targets that succeed or fail separately — report per-target results. TikTok Requires an explicit music-usage agreement flag (a legal acknowledgement, no sensible default). Unaudited apps are restricted to SELF_ONLY visibility; that is TikTok's rule and cannot be worked around. Pinterest A pin requires a board_id. Valid boards come back with the account in list_accounts. Facebook Pages only. Personal profiles are not publishable via the platform API. ## Quota Quota counts POSTS, not targets and not requests. One post fanned out to six platforms is ONE unit; six single-platform posts are six. Put every target in a single create_post call unless the content itself differs per platform. ## Rate limits and caps Plan API Requests/min/key Max keys Hobby no — 0 Starter yes 60 2 Growth yes 120 5 Scale yes 300 20 429 responses carry Retry-After plus RateLimit-* headers. Honour Retry-After rather than retrying on a fixed timer. ## Idempotency Every mutating call accepts an Idempotency-Key header. - Generate one fresh UUID per logical post. - On a retry (timeout, 5xx, dropped connection) send the SAME key again. The server returns the original result instead of creating a second post. - Generating a new key on retry is how you double-post to a real audience. - If a create failed ambiguously and no key was sent, do not blindly retry — call GET /api/v1/posts and check whether it landed. ## Errors 401 UNAUTHORIZED Key invalid, revoked or deleted. Terminal. 402 PLAN_REQUIRED No API access on plan, or a cap hit. Terminal. 403 INSUFFICIENT_SCOPE Key's scopes do not cover this call. Terminal. 404 NOT_FOUND Unknown id, or an id from another workspace. Re-list. 422 VALIDATION_FAILED Payload rejected, per-target detail. Fix and re-validate. 429 RATE_LIMITED Per-key per-minute limit exceeded. Back off. 401, 402 and 403 will not resolve by retrying. Cross-workspace ids return 404 rather than 403 on purpose, so a key cannot probe for resources it cannot reach. ## MCP client config (stdio bridge) { "mcpServers": { "uptrended-social": { "command": "npx", "args": ["-y", "@uptrended/mcp"], "env": { "UTSOCIAL_API_KEY": "utsk_live_xxxxxxxxxxxx.your-secret-here" } } } } Clients that support remote MCP servers with custom headers should skip the bridge and connect straight to https://uptrended.social/mcp with an Authorization: Bearer header.