# Postbito > Postbito publishes posts to Facebook Pages, Instagram, YouTube, TikTok, X and LinkedIn on a schedule — Buffer-style queues, a caption per network, approvals, and weekly results. Agents work through a REST API or an MCP server with a team API key; people approve what goes out. Base URL: https://postbito.com/api/v1 MCP server (Streamable HTTP): https://postbito.com/mcp Auth: send `Authorization: Bearer ` (keys look like 12|pbt_…) and `Accept: application/json` on every request. Human-readable docs: https://postbito.com/docs/api Claude Code: claude mcp add --transport http postbito https://postbito.com/mcp --header "Authorization: Bearer pbt_your_key" ## The loop 1. Request — GET /ideas?status=new,in_progress lists requests for posts: a title (the topic), body (notes), channel_ids and publish_on. PATCH /ideas/{id} {"status": "in_progress"} when you start one. (MCP: list_ideas, update_idea.) 2. Build — make the captions and media yourself; a cut per network where it helps (vertical 9:16 for TikTok, Reels and Shorts). 3. Check — GET /channels gives each network's rules; POST /posts/validate runs a post past them without saving and returns problems per channel id. Fix everything it reports. (MCP: list_channels, check_post.) 4. Upload — POST /media with multipart "file", or JSON {"url": ""} and Postbito fetches it. Keep the returned id. (MCP: upload_media_from_url.) 5. Draft — POST /posts with "text" (the shared caption), "media" (ids), "channels" (ids), and "targets" for per-channel captions, media and options; set "idea_id"; "action": "draft" or "submit". (MCP: create_post.) 6. Approve — a person approves it in Postbito. If they send it back, its status becomes "changes_requested" with a "review_note": GET /posts?status=changes_requested is your rebuild list. Fix it, PATCH /posts/{id}, then POST /posts/{id}/submit. (MCP: list_posts, update_post, submit_post.) You can't approve posts. 7. Publish — once approved, Postbito posts each channel's copy at its targets[].scheduled_at; targets[].status becomes "published" with a remote_url. (MCP: get_post.) 8. Clean up — Postbito deletes uploaded files once every post using them is live (media.url becomes null). Upload again to reuse a file. 9. Learn — GET /analytics (default: the last 7 days) gives totals against the week before, each channel's numbers, the best hours and the best posts. Let it shape the next ideas: POST /ideas. (MCP: get_analytics, create_idea.) Every post response has "next": a sentence saying what happens now and what you can do. ## Writing a post {"text":"Costume contest Saturday at 10pm 🎃","media":[41],"channels":[3],"targets":[{"channel_id":4,"text":"Costume contest Sat 10pm. 2-for-1 if you dress up 🎃"},{"channel_id":7,"media":[42],"options":{"privacy_level":"PUBLIC_TO_EVERYONE"}}],"idea_id":12,"action":"submit"} - text and media go to every channel; targets[] gives one channel its own text, media or options. A target's text or media left out, null or [] uses the post's. - Shorthand: a "channels" entry can also be an object, {"id": 4, "text": "…", "media": [43], "options": {…}} — the same as a target. - action: draft (default) | submit (ask for approval) | queue (each channel's next free posting-schedule slot) | schedule (needs scheduled_at, ISO 8601 with offset) | now. - With submit, scheduled_at or publish_mode (queue | now | at) says what happens once it's approved. - If your key lacks the "publish" permission, or the team requires approval, queue/schedule/now submit the post for approval instead. GET /me → data.key.can_publish tells you which. - PATCH /posts/{id} changes only what you send: targets edits channels one at a time; channels replaces the list. Without an action the post keeps its state. - Post statuses: draft, pending_approval, changes_requested, scheduled, published, partial, failed. Channel (target) statuses: draft, scheduled, publishing, published, failed. ## Network options (targets[].options) YouTube: - title: Required by YouTube, up to 100 characters. Defaults to the caption’s first line. - privacy: public, unlisted or private (default public). - made_for_kids: true or false (default false). - tags: A list of tags. - category_id: YouTube’s video category id (default 22, People & Blogs). TikTok: - privacy_level: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY. Apps TikTok hasn’t audited can only post SELF_ONLY. - disable_comment: true to turn comments off. - disable_duet: true to turn duets off (videos). - disable_stitch: true to turn stitches off (videos). - title: Photo posts: a title (up to 90 characters). - brand_content_toggle: true when it’s a paid partnership (promoting another brand). - brand_organic_toggle: true when it promotes your own business. - cover_timestamp_ms: Videos: the frame to use as the cover, in milliseconds (default 1000). - auto_add_music: Photo posts: let TikTok add music (default true). Instagram: - type: post (a photo, carousel or video — videos go out as Reels), reel, or story. - share_to_feed: Reels: also show it in the feed (default true). LinkedIn: - visibility: PUBLIC (default) or CONNECTIONS. ## Network rules - Facebook: caption up to 63,206 characters; text alone is fine; up to 10 photos (image/jpeg, image/png, image/gif, image/webp); 1 video up to 4:00:00. - Instagram: caption up to 2,200 characters, 30 hashtags max; needs a photo or video; up to 20 photos (image/jpeg, image/png); 20 videos up to 15:00, photos and videos can mix. - YouTube: caption up to 5,000 characters; needs a video; no photos; 1 video; needs a title (options.title, ≤100 characters; defaults to the caption’s first line). - TikTok: caption up to 2,200 characters; needs a photo or video; up to 35 photos (image/jpeg, image/webp); 1 video up to 10:00. - X: caption up to 280 characters (links count 23, emoji 2); text alone is fine; up to 4 photos (image/jpeg, image/png, image/gif, image/webp); 1 video up to 2:20. - LinkedIn: caption up to 3,000 characters; text alone is fine; up to 20 photos (image/jpeg, image/png, image/gif); 1 video up to 30:00. ## Endpoints ### Account - GET /me [read] — The team, its time zone and plan limits, the key’s permissions, and whether what it queues goes straight out (can_publish) or waits for approval. ### Channels - GET /channels [read] — Connected accounts: network, name, status, posting schedule and the network’s rules (caption length, the media it needs and takes). People connect channels in Postbito. - GET /channels/{id} [read] — One channel, with its next five free queue slots. ### Ideas - GET /ideas [read] — Requests for posts, soonest first. ?status=new,in_progress (also drafted, done) &per_page=25 - POST /ideas [write] — File a request: the topic, notes, the channels it’s for and the day it should go out. Body: {"title": "Halloween weekend on the rooftop", "body": "Costume contest Saturday, 2-for-1 cocktails.", "channel_ids": [3, 4, 7], "publish_on": "2026-10-30"} - GET /ideas/{id} [read] — One idea, with the posts made from it. - PATCH /ideas/{id} [write] — Change any field — usually status: in_progress when you start building it, done when it’s out. Saving a post with its idea_id marks it drafted. Body: {"status": "in_progress"} - DELETE /ideas/{id} [delete] — Delete an idea. Its posts stay. ### Media - POST /media [write] — Upload a photo or video as multipart "file" — or send a public "url" and Postbito fetches it. Returns the media id to use in posts, with its size, dimensions and length. Body: {"url": "https://bucket.example.com/videos/halloween-vertical.mp4", "alt_text": "Costumes on the rooftop", "purge_after_publish": true} - GET /media [read] — The library, newest first. ?kind=image|video &purged=0|1 - GET /media/{id} [read] — One file (by id or uuid): a signed url, thumbnail, and pending_uses — posts still waiting to go out with it. - PATCH /media/{id} [write] — Change alt_text, filename or purge_after_publish. Body: {"purge_after_publish": false} - DELETE /media/{id} [delete] — Delete a file. Refused (409) while a post that hasn’t gone out uses it. ### Posts - POST /posts/validate [read] — The check step: run a post past every channel’s network rules without saving. Returns ok, problems per channel id, and each channel’s caption length against its limit. Body: {"text": "Costume contest Saturday at 10pm 🎃", "media": [41], "channels": [3, 4]} - POST /posts [write] — Write a post once for several channels, with a caption, media and options per channel where they differ. action: draft (default), submit, queue, now or schedule (with scheduled_at). Body: {"text": "Costume contest Saturday at 10pm 🎃", "media": [41], "channels": [3], "targets": [{"channel_id": 4, "text": "Costume contest Sat 10pm. 2-for-1 if you dress up 🎃"}, {"channel_id": 7, "media": [42], "options": {"privacy_level": "PUBLIC_TO_EVERYONE"}}], "idea_id": 12, "action": "submit"} - GET /posts [read] — Posts, newest first. ?status=changes_requested (comma-separate several) &channel_id= &idea_id= &from= &to= (ISO 8601; a channel’s scheduled or published time) &sort=newest|schedule &per_page=25 - GET /posts/{id} [read] — One post: each channel’s caption, media, time, result (remote_url or error) and metrics, the review_note, and next — what happens now. - PATCH /posts/{id} [write] — Change a post that hasn’t gone out. Only what you send changes: targets edits channels one at a time, channels replaces the list. Without an action it keeps its state. Body: {"targets": [{"channel_id": 7, "media": [43]}]} - POST /posts/{id}/submit [write] — Ask an owner or admin to approve it (it’s checked first). Approved posts go out as asked; sent-back ones become changes_requested with a review_note. - POST /posts/{id}/drafts [write] — Take it off the calendar (or out of the approval queue) and keep it as a draft. - POST /posts/{id}/duplicate [write] — Copy it as a new draft. - POST /posts/{id}/retry [publish] — Send the channels that failed again, now. - DELETE /posts/{id} [delete] — Delete it from Postbito. Anything already live stays on the networks. ### Analytics - GET /analytics [read] — The learn step: totals for a period (default: the last 7 days) against the period before, each channel’s numbers, the best hours, and posts best first with their metrics. ?from=YYYY-MM-DD &to=YYYY-MM-DD &channel_id= &limit=25 (Essentials and Team plans). ## MCP tools - whoami — The account, your key’s permissions, and whether what you queue goes out or waits for approval. - list_channels — Channels with ids, next free slots and each network’s rules. - list_ideas — Requests still to do (new, in progress), soonest first. - create_idea — File a request for a post (write). - update_idea — Change an idea — usually its status (write). - upload_media_from_url — Hand over a finished file by its public link; get a media id (write). - check_post — Run a post past every network’s rules without saving. - create_post — Write a post with a caption per channel; draft, submit, queue, schedule or now (write). - update_post — Change a post that hasn’t gone out — e.g. after a change request (write). - get_post — One post in full, with each channel’s result and “next”. - list_posts — Find posts; status “changes_requested” is your rebuild list. - submit_post — Send a post to a person for approval (write). - get_analytics — How the week went: totals, channels, best hours, best posts. ## Permissions - read: Read channels, posts, ideas, media and analytics - write: Upload media, write ideas and drafts, and submit posts for approval - publish: Queue, schedule and publish posts without approval - delete: Delete posts, ideas and media ## Errors - 401 missing or revoked key · 403 missing permission, or not on the plan (analytics on Free) · 404 not on this account · 409 conflict (a file a waiting post uses; a post publishing right now) · 422 validation — a network's complaints are under "targets.{channel id}", whole-post ones under "post" · 429 rate limited (300/minute per key). ## Plans - Free: 3 channels, 10 scheduled posts per channel, 1 user. API and MCP included. - Essentials: per channel; unlimited scheduled posts, analytics and the weekly results email. - Team: per channel; unlimited team members and approvals (the team can require them for members and API keys).