Developers
The social media MCP server and API
BetterSocials gives your AI a hosted MCP server and a REST API. One connection lets Claude, ChatGPT, Claude Code, Cursor, or any agent post to Instagram, YouTube, Facebook, TikTok, Threads and LinkedIn, answer comments and DMs, run comment-keyword automations, read analytics, and edit video.
API version 2026-09-10 · machine-readable manifest at /api/meta
What your agent can do
One API key runs the whole product. An AI agent — Claude, Claude Code, Codex, anything that speaks JSON over HTTP — can read the unified inbox and reply to comments and DMs, create, caption, schedule and publish posts, manage comment-keyword automations and calendar notes, read analytics, and do full video edits in the free AI video editor. These are the same routes the app itself calls, so every validation, credit meter and guard applies to an agent exactly as it applies to a person clicking.
Two things stay human on purpose: video Export (rendering runs in the browser, so a person presses the button after checking the work), and anything that touches money, account settings or channel connections — those never accept a key at all.
Connect Claude or ChatGPT without a key
If you just want your AI assistant to run your BetterSocials, you can skip the rest of this page. There is no key to copy and nothing to install: paste one address into the app you already use, then sign in when it asks.
https://bettersocials.ai/api/mcp
Claude (claude.ai, desktop or mobile): Settings → Connectors → Add custom connector. Name it BetterSocials, paste the address above, then Add and Connect. Claude opens a BetterSocials sign-in, you approve, and the tools show up in your next chat. Custom connectors need a paid Claude plan. Step by step: connect Claude to BetterSocials.
ChatGPT: Settings → Developer mode (under Plugins) to turn it on, then close Settings and open Plugins in the left sidebar. The plugin is made there, not on the settings screen. Hit the plus button, name it BetterSocials, leave Connection on Server URL and paste the address above, leave Authentication on OAuth, tick “I understand and want to continue” (Create stays disabled until you do), and Create. ChatGPT walks you through the same sign-in and approval, and you switch it on per chat from the composer’s plus menu under Developer mode. Developer mode needs a paid ChatGPT plan on the web, and write actions ask you to confirm each time. Step by step: connect ChatGPT to BetterSocials.
Both doors ask for a name and neither enforces one. Use BetterSocials in both anyway: the name is how you aim a prompt at these tools, and every example here says “use the BetterSocials tools”.
The connection acts with your workspace’s normal limits, and Settings → Connected apps in BetterSocials disconnects it in one click.
Connect Claude Code, Cursor, or any MCP client
The hosted MCP server wraps the same contract as tools, over Streamable HTTP. One command in Claude Code:
claude mcp add --transport http bettersocials https://bettersocials.ai/api/mcp \ --header "Authorization: Bearer br_YOUR_KEY"
Tools cover the same surface: get_inbox, get_thread, send_reply, draft_reply, create_post, generate_captions, list_queues, list_keywords, create_keyword, get_analytics, list_connections, the Studio project tools, create_upload_url, and more — get_capabilities always lists the current set.
Built so a leaked key stays boring
Every key is scoped to one workspace, stored as a SHA-256 hash, revocable in one click, and rate-limited in three tiers: reads (600 per 5 minutes), writes (120), and sends — anything that leaves the app onto a real social account — at 30. Tripped limits page our security channel. Key management, billing, account settings and channel connect/disconnect stay session-only, so a leaked key can never mint more keys, spend money, or deprovision an account. Sends run through the same claim locks and failure ledger the app uses — an agent cannot send around a guard. Repeated invalid keys get throttled per network and page us too.
Stay current, automatically
Every response from the agent surface carries a docs object linking to https://bettersocials.ai/api/meta — the live manifest of every endpoint, rule and limit. Point your agent at it once and it always knows what it can do, including things we shipped after you wrote your integration. The MCP server exposes the same thing as the get_capabilities tool, and /llms.txt maps the whole site for assistants.
Authentication
Mint an API key in Settings → API keys. Keys are shown once, stored as a SHA-256 hash, capped at five active per workspace, and scoped to that workspace — an id in a URL never reaches across. Key management itself stays in the app, so a leaked key can never mint or revoke keys.
curl -s https://bettersocials.ai/api/studio/editor/projects \ -H "Authorization: Bearer br_YOUR_KEY"
How an agent edits video
- Read. GET the project. The whole edit is one JSON document — clips, cuts, captions, overlays, effects, sounds. Keep the updatedAt you read.
- Modify. Change the document in memory. Cuts are kept source ranges; overlay, effect and sound times are output seconds.
- Write. PATCH the whole document back with baseUpdatedAt. A stale base is a 409 — re-read, re-apply, retry. Identical content is a free no-op.
- Watch. Any open editor tab applies your patch live within about a second, and the creator can undo it with ⌘Z like their own edit.
Getting footage in
Media bytes never ride the document. Ask POST /api/studio/editor/media/upload for a signed URL, PUT the file straight to storage, then reference the returned publicUrl as a clip or overlay sourceUrl. The open editor fetches it once into its local vault. Supply analysis (word timings plus measured speech bounds) with a clip and the editor never re-charges transcription.
REST endpoints
| Method | Path | What |
|---|---|---|
| GET | /api/meta | This manifest — every capability, limit and rule, as JSON. No auth needed. |
| GET | /api/studio/editor/projects | List the workspace’s editor projects (summaries, newest first). |
| POST | /api/studio/editor/projects | Create a project. Optionally seed the full document. { title?, aspect?: 'vertical' | 'wide', doc? } |
| GET | /api/studio/editor/projects/:id | The full project including its document. `project.updatedAt` is what you send back as `baseUpdatedAt`. |
| PATCH | /api/studio/editor/projects/:id | Save the project. The doc REPLACES the stored one (omitted fields are RESET) — always GET, modify, PATCH the whole thing. The response carries `warnings` naming any field sanitization dropped or clamped; read them, or you will report an edit that never happened. For small changes prefer /edits below. { title?, doc?, durationSec?, poster?, baseUpdatedAt? } |
| POST | /api/studio/editor/projects/:id/edits | Targeted edit operations, applied server-side — no doc round-trip, no risk to the rest of the document. This is the whole editing surface: captions and their look, title card, timing cuts, filler removal, speed, texts, overlays, effects, sounds, green screen, framing. Every op reports applied-or-why-not. Ops: add_clip { url (a publicUrl from import_media/create_upload_url/list_media), name?, durationSec? } — footage itself; pair with transcribe_clip for words · set_captions { on, styleId? — omit styleId to wear the creator’s starred default caption template } · set_caption_style { styleId?, adjustments? { fontId, caseMode uppercase|lowercase|none, baseColor/activeColor/strokeColor #rrggbb, strokeRatio, sizeScale, baselineRatio (vertical position), offsetXRatio, maxWords }, reset? } · set_title_card { text|null, color?, endSec? (how long it stays up), casing?, background?, fontId?, offsetY?, scale?, sizeRatio? (font × frame width; 0.053 classic, ~0.08 caption-sized) — a NEW card’s unspecified look fields come from the creator’s starred card template } · set_speed { speed 0.5–2 } · set_clip_speed { clipIndex, speed|null } · set_pacing { tight|natural|relaxed } · set_tighten_pauses { on, thresholdSec?, targetSec? } · set_soft_cut { on } · set_video_transform { x?, y?, scale? | reset } · set_green_screen { on, background { kind color|media|none, color?, url? }, windows? [{startSec,endSec}]|null } · cut_source_range { clipIndex, startSec, endSec — SOURCE seconds; word timestamps in analysis.words are source seconds (+ asrOffset) } · run_filler_removal { strength? light|standard|aggressive } — the editor’s own um/uh/stutter pass, server-side · add_text / update_text / remove_text { text, presetId?, startSec?, endSec?, x?, y?, sizeRatio?, fontId?, casing?, color?, strokeColor?, strokeRatio?, bgColor?, bgOpacity?, shadow?, animation none|pop|fade|slide-up|typewriter } · add_overlay / update_overlay / remove_overlay { url (public image/GIF/video), kind?, startSec?, endSec?, x?, y?, w?, opacity?, enter pop|fade|none, corner?, shadow?, muted?, layer under|over, crop? } · add_effect / update_effect / remove_effect { kind punch|zoom-in|zoom-out|shake|flash|grain, startSec, endSec?, intensity? — grain is a whole-video film look: add ONE at startSec 0 with no endSec and it runs to the end } · add_sound / update_sound / remove_sound { sfxId (built-in kit — see capabilities.vocab.sfx) or url (audio file), startSec?, durationSec?, sourceOffsetSec?, gain 0–1.5, lane? }. Vocab (every styleId, fontId, presetId, sfxId): GET /api/meta → vocab, or the MCP get_capabilities result. { ops: [{ op, … }], baseUpdatedAt? } |
| POST | /api/studio/editor/projects/:id/transcribe | Transcribe one clip server-side (MCP: transcribe_clip) so word-level editing works over the API — after this, cuts, filler removal and captions all have words. The clip’s sourceUrl must live in BetterSocials storage (import_media / upload / composer). Spends the same credit as in-editor captioning; already-transcribed clips return free; silent clips are refunded. { clipIndex?: number, force?: boolean } |
| DELETE | /api/studio/editor/projects/:id | Delete the project. Its synced media is swept ~48h later. |
| POST | /api/studio/editor/media/upload | Get a signed URL to upload footage, images or audio straight to storage — then reference the returned publicUrl as a clip or overlay `sourceUrl`. { contentType, sizeBytes, filename? } |
| POST | /api/studio/editor/media/import | Server-side fetch of a PUBLIC media URL into the workspace bucket — for agents whose sandbox blocks a direct PUT to storage (egress allowlists). Drive/Dropbox share links are rewritten to direct downloads when possible. Max 500MB; larger files go through the composer. { url, filename? } |
| GET | /api/studio/editor/media/uploads | Recent media in the workspace bucket (agent uploads/imports + files the creator dropped into the composer; last 30 days, newest first) — the human-handoff when an agent cannot move bytes at all. |
| POST | /api/studio/editor/media | The ingest proxy the open editor uses to fetch a doc-declared external sourceUrl. You normally never call it yourself. { url } |
| GET | /api/studio/shorts | List the workspace’s short-form scripts (?status= filter). |
| POST | /api/studio/shorts | Create a blank short script. { title? } |
| POST | /api/studio/shorts/generate | Generate one complete short from the Creator Brief. Metered exactly like the in-app button. { brief?: string } |
| GET | /api/studio/shorts/:id | The full short (beats, captions, title cards, strategy, media). |
| PATCH | /api/studio/shorts/:id | Update a short — same validated field set the editor uses; send baseUpdatedAt. |
| DELETE | /api/studio/shorts/:id | Delete a short script. |
| GET | /api/studio/brief | The Creator Brief. |
| PUT | /api/studio/brief | Replace the Creator Brief (normalized and capped). |
| GET | /api/studio/hooks | The hook library generation reads — house and earned patterns, evidence, tracked creators. |
| POST | /api/mcp | Hosted MCP server (Streamable HTTP) wrapping this same contract — connect Claude Code, Claude Desktop or any MCP client with your API key as a Bearer header. |
| GET | /api/inbox | The unified inbox — comments + DMs across every channel, one thread per contact. Filters: channels (csv), type (comment|message), status (pending|sent|active|archived|spam|all), leads=1, q= search, limit/cursor paging. |
| GET | /api/inbox/:id/thread | One contact’s merged conversation timeline plus reply_targets (what a reply can anchor to). ?item_type=comment|message says what the id points at. |
| POST | /api/inbox/:id/status | Triage a whole contact thread. { status: 'pending'|'archived'|'spam'|'replied_externally', item_type? } |
| GET | /api/inbox/unseen | The inbox badge count — actionable inbound newer than the last inbox view. Poll this cheaply instead of the full inbox. |
| POST | /api/inbox/backfill | Pull OLDER comments into the inbox — the history from before the account was connected. Adds rows only: no keyword automation, no reply, no DM, no AI, no credits. Runs automatically once per channel on connect; call this to re-run. connection_id syncs one channel, omit for all. Instagram, Facebook, YouTube, Threads and LinkedIn read back. TikTok depends on the CONNECTION, not the platform: accounts authorised before TikTok’s business-app cutover hold 6 scopes and refuse with PLATFORM_LIMITATION, while accounts authorised after hold 18 including comment.list and read back fine. Reconnect the account to grant them. { connection_id? } |
| POST | /api/replies/:id | SEND a reply on the creator’s real account — public comment reply or DM. :id is EITHER an inbox row id OR the platform’s own native comment id; for a native id we have not ingested yet, pass post_id (and optionally platform) and it is fetched and filed before the reply goes out. Claim-locked, account-resolved, failure-ledgered; bearer calls draw the send tier. { text, item_type?: 'comment'|'message', channel?: 'comment'|'dm', post_id?, platform? } |
| POST | /api/replies/:id/draft | AI reply draft in the creator’s voice (?item_type=). Spends 1 credit; templates when credits run out. Nothing sends. |
| GET | /api/posts | Posts in a window (?from=&to= ISO) or every draft (?drafts=1). |
| POST | /api/posts | Compose a post: draft:true saves, queue:true books the next queue slot, schedule{date,time,timezone} books a time, publishNow:true publishes immediately (send tier). Media URLs must live in BetterSocials’s own bucket. { postType, media?, thumbnailUrl?, youtubeTitle?, baseText?, transcript?, captions?, targetConnectionIds, igCollaborators?, draft?|queue?|schedule?|publishNow? } |
| GET | /api/posts/:id | Post detail with per-platform results and links. |
| PATCH | /api/posts/:id | Act on a post by action: saveDraft, scheduleDraft, queueDraft, publishDraft (send tier), cancel, reschedule, edit, editCaptions, retry / retryLeg (send tier). igCollaborators is editable while a post is still a draft or scheduled, because Instagram only takes collaborators at publish — a published post can never gain them. { action, schedule?, queueId?, connectionId?, captions?, …content fields } |
| DELETE | /api/posts/:id | Delete a DRAFT post. |
| POST | /api/posts/captions | Generate per-channel captions (+ YouTube Shorts title) in the creator’s voice. Spends credits per channel; can weave a comment-keyword CTA. { postType, targetConnectionIds, transcript?, baseText?, youtubeTitle?, includeKeyword?, keywordMode?, keywordId? } |
| POST | /api/posts/transcribe | Transcribe an uploaded video (own-bucket URLs only; 60/day per user). { mediaUrl } |
| GET | POST | /api/calendar/notes | Calendar notes in a day window (?from=&to= YYYY-MM-DD) / create one ({ day, title, body?, color? }). |
| PATCH | DELETE | /api/calendar/notes/:id | Edit or delete a calendar note. |
| GET | POST | PATCH | DELETE | /api/keywords | The comment→DM automation keywords. Create needs keyword + guide_url + ≥1 dm_templates + ≥1 public_reply_templates; PATCH/DELETE take { id, … }. Every keyword needs its static fallbacks — the engine refuses one that could silently send nothing. link_tracking_enabled (boolean) wraps each DM’s link as a per-recipient bettersocials.link tracked link (beta — honored only when the workspace holds the link_tracking flag). |
| GET | POST | PUT | PATCH | /api/posting-queue | Posting queues: list (+ queued posts + timezone), create, rename/recolor/reslot, and remove/reorder/swap/moveToQueue/deleteQueue actions. |
| GET | POST | PATCH | DELETE | /api/account-groups | Named account sets used across create/keywords/analytics. |
| GET | POST | PATCH | DELETE | /api/ai-rules | The creator’s always/never AI voice rules injected into every draft. |
| GET | /api/analytics | The growth read model: follower series, per-day earned metrics, posts with outlier scores, keyword conversions. ?range=1d|7d|1m|90d|1y|all. Pro+ plans. |
| POST | /api/studio/editor/voiceover | Generate an AI voiceover (ElevenLabs) from text into the workspace bucket; optionally attach it to a project as a timeline sound (MCP: generate_voiceover). Charged per character — 25 credits per 1,000, min 3, ≤4,000 chars per call — unless the workspace stores its own ElevenLabs key in Settings → AI keys (then no credits). Failures refund. Voices: vocab.voices. { text, voiceId?, projectId?, atSec? } |
| POST | /api/studio/editor/images | Generate an AI image from a prompt into the workspace bucket; optionally attach it to a project as a full-frame overlay (MCP: generate_image). Pick the model by id from vocab.imageModels — each entry carries its credit price and an honest blurb (10–35 credits). No credits when the workspace stores its own kie.ai key. Failures refund. { prompt, model?: vocab.imageModels id, aspect?: 'vertical'|'wide'|'square', projectId?, atSec?, endSec? } |
| POST | /api/studio/editor/projects/:id/frames | Sample small JPEG frames from a stored clip so a vision-capable agent can LOOK at footage and pick shots (MCP: sample_frames returns them as image blocks). Timestamps are SOURCE seconds — feed them straight into cut_source_range. Free of credits; ≤40 frames per call; daily per-workspace quota; own-bucket clips only. { clipIndex?, startSec?, endSec?, intervalSec?, maxFrames? } |
| GET | /api/connections | Connected channels (id, platform, handle, status). READ-ONLY by key — connecting, editing and disconnecting stay in the app. |
| GET | /api/trials | The trial log: experiments newest first with their variants and metrics inlined. Filters: ?status= (one value or comma-separated — scheduled, collecting, decided, failed, canceled), ?since= ISO (only trials whose updated_at is later, returned oldest-change-first so the last row is your next cursor), ?limit= 1–100. A trial is FINISHED when status is "decided" and decided_at is non-null — those numbers are final. "failed" is equally final: the numbers never arrived. A bad ?since= is a 400, never a silent full read. |
| POST | /api/trials | Create and schedule a trial. ONE variant is a SCREEN: it posts to non-followers, you read the numbers, and you graduate it by hand. TWO TO FOUR variants is an A/B test whose result is written to the evidence ledger. trialMode "ig_trial" is Instagram’s native Trial Reels (the creator’s followers never see the post); "public" is an ordinary reel used as the test. Each variant carries exactly one video that already lives in BetterSocials storage. The versions go out as ONE batch, 10–30 minutes apart in randomized order, so "posted first" cannot pass for "won". Send tier — this books posts on real accounts. { dimension, hypothesis, trialMode, trialConnectionIds, winnerConnectionIds, variants: [{ key: A–D, option, media: [{ type: "video", url }], label?, spec?, captions?, baseText?, youtubeTitle?, thumbnailUrl? }], rationale?, context?, startAt?, queueId?, collectAfterHours?, designedBy?, igTrialAcknowledged? } |
| GET | /api/trials/:id | One trial in full: the experiment, its variants with metrics and scores, and the posts behind them with per-platform results and links. decision.screening true means one version ran, so nothing was compared and no learning was written. |
| PATCH | /api/trials/:id | Act on a trial. queueWinner posts the winner to the follower channels — idempotent, returns postId, and a screening run never does it for you. cancel ends a scheduled or collecting run and cancels its posts that have not gone out (nothing published comes down). decideNow settles it immediately; with the standing auto-queue rule on that also queues the winner. manualMetrics types one variant’s numbers in by hand, which is the on-ramp for a hidden Instagram trial the API never reported. { action: 'queueWinner'|'cancel'|'decideNow'|'manualMetrics', variantId?, metrics?: { views, impressions, reach, likes, comments, shares, saves } } |
| GET | PATCH | /api/trials/channels | What the schedule step needs: reel-capable connections (id, platform, handle, followers, igTrialOk — whether Instagram has let that account publish native Trial Reels), the trial queues with their next open slot, account groups, the comment-keyword shelf, and the creator’s remembered picks. PATCH stores those picks (mode, trial + winner channels, queue, start mode); every id is re-checked against the workspace before it is written. |
| GET | /api/trials/learnings | The evidence ledger: per (dimension, optionA, optionB) win counts, posterior, verdict tier, the contexts each trial ran in, and the analyst’s notes. Screening runs never appear here — one version has nothing to compare. |
| GET | /api/trials/queue-slots | When a trial queue would START a batch (?queueId=&count=1–4&channels=csv). The same call the scheduler itself makes, so a preview can never disagree with what happens. One instant for the whole batch plus the stagger range, because the exact minutes are drawn when the run is scheduled; start is null when the queue has no room for a batch that size. |
| GET | PUT | /api/trials/settings | The standing winner rule — autoQueueWinner, winnerQueueId, winnerConnectionIds — plus the queues it can name. PUT saves it. |
| POST | /api/trials/suggest | The designer proposes the next experiment from this workspace’s own evidence ledger: which dimension is worth testing, the options, and why. Pass supported (dimensions your renderer can actually execute) and projectFacts to constrain it to something you can build. Burns model dollars. { brief?, supported?, projectFacts? } |
| POST | /api/trials/caption | Write the caption for a trial from the saved project document’s own transcript. ONE caption across every version (a caption that changed per version would be a second variable), so it charges ONE credit rather than one per channel, refunded when the model comes back empty. It can weave a comment-keyword CTA the same way /create does: auto-match costs +1 credit and refunds when nothing fits, a hand-picked keyword is free. { projectId, connectionIds, includeKeyword?, keywordMode?: 'auto'|'manual', keywordId? } |
The rules that keep collaboration safe
- Auth: `Authorization: Bearer br_…` — mint keys in Settings → API keys (shown once, 5 active per workspace, SHA-256 at rest). Every workspace has API access. A presented key is judged alone; key management stays in-session.
- MCP without a key: claude.ai and ChatGPT connect through OAuth. Paste the MCP endpoint into their connector settings and sign in when asked. The granted token has the same authority and limits as an API key, and Settings → Connected apps disconnects it instantly.
- The workspace is resolved from the key. Ids in URLs never cross workspaces.
- Every bearer call is rate limited per workspace in three tiers: reads 600/5min, writes 120/5min, sends 30/5min — MCP tools included. A 429 carries Retry-After — back off and retry; tripped limits alert our security channel. Repeated invalid keys are throttled per network.
- Sends are sacred: send_reply / publishNow / publishDraft / retry act on the creator’s REAL social accounts. Do them only on explicit instruction or standing rules the creator set. Every send passes the same claim locks, account resolution and failure ledger the app itself uses.
- Session-only forever: key management (BetterSocials API keys AND stored vendor keys in Settings → AI keys), billing, account/profile, workspace management, and connecting/disconnecting channels. A leaked key can never mint keys, spend money, or deprovision an account.
- Bring your own vendor key: a workspace can store its own ElevenLabs or kie.ai API key in Settings → AI keys. When present, generate_voiceover / generate_image run on that key with NO credit debit — the free editor plan included. Stored keys are encrypted at rest, never readable back, and never manageable over this API.
- AI generation (drafts, captions) spends the workspace’s credits exactly like the in-app buttons — an agent can never generate more cheaply than a human.
- Credit transparency: the FIRST time in a conversation you are about to call a credit-spending tool (generate_voiceover, generate_image, transcribe_clip, draft_reply, generate_captions), tell the creator the cost in credits before running it. Once they have okayed a spend in that conversation, do not re-ask on every call — but DO surface every creditsSpent (and creditsRemaining when returned) in your reply, so nothing drains credits invisibly.
- PATCH replaces the whole document. A partial doc is a valid doc with everything else missing — always GET → modify → PATCH. For a small change (captions, title card, speed, a cut) use /projects/:id/edits (MCP: edit_project) instead: no round-trip, and every op answers applied-or-why-not.
- Optimistic concurrency: send `baseUpdatedAt` (the `updatedAt` you read). Stale = 409 → re-GET, re-apply, re-PATCH.
- Identical-content PATCHes are no-ops — retries are free and never fight an open editor tab.
- Docs are sanitized both directions: unknown fields drop, numbers clamp. Diff the response against what you sent.
- Media bytes never ride the doc. Upload via /media/upload (or host them yourself) and reference the URL as `sourceUrl` on clips, overlays, sounds and green-screen backgrounds; any open editor ingests it once into its local vault.
- Sandboxed and cannot PUT to storage? Never a dead end: POST /media/import fetches a public URL server-side into the bucket (MCP: import_media), or the creator drops the file into the composer and GET /media/uploads (MCP: list_media) finds it.
- Supply `analysis` (ASR words + measured speech bounds) with a clip and the editor never re-charges transcription.
- Every successful write pings open editor tabs live (≈1s). Agent edits land as undo steps — ⌘Z works on them.
- Export stays human: rendering is client-side by design, so a person presses Export in the tab. Everything up to that button is yours.
- Uploaded media not referenced by any project document within 48 hours is swept.
- Trial Reels is available to every workspace. The `trial_reels` flag is the per-workspace revoke rail, not a beta gate: where it has been revoked every /api/trials/* route answers 404 — including for a key that works everywhere else on this API. A 404 there still means "not enabled", not "wrong id".
- Reading trial numbers honestly: an ig_trial reel is shown to NON-FOLLOWERS only, so its reach and views ARE non-follower reach — there is no separate field for that and none is needed. The stored metrics are views, impressions, reach, likes, comments, shares and saves. Watch time is not among them and never will be: our provider does not report it, so never quote it or imply it.
Limits
| projectsPerWorkspace | 100 |
| docBytes | 3500000 |
| clipsPerProject | 24 |
| overlays | 60 |
| effects | 60 |
| sounds | 60 |
| texts | 60 |
| wordsPerClip | 22000 |
| cutsPerClip | 240 short-form, scaling with footage to 1200 |
| maxSeconds | 7200 |
Open to every workspace
Every BetterSocials workspace has API access — sign up, mint a key in Settings → API keys, and go. Something missing that your agent needs? Tell us through the in-app support chat — gaps in this surface get closed fast, and the manifest at /api/metais how you’ll see them land.
Common questions
- What is an MCP server?
- MCP, the Model Context Protocol, is the open standard that lets AI assistants operate other software. An MCP server turns a product into tools an assistant can call. BetterSocials hosts one at https://bettersocials.ai/api/mcp. Connect it once and Claude or ChatGPT can post, reply, and edit video in your account from a normal chat.
- How do I connect Claude to BetterSocials?
- In Claude (claude.ai, desktop or mobile): Settings, then Connectors, then Add custom connector. Name it BetterSocials, paste https://bettersocials.ai/api/mcp, then Add and Connect. Claude opens a BetterSocials sign-in, you approve, and the tools show up in your next chat. Custom connectors need a paid Claude plan.
- How do I connect ChatGPT to BetterSocials?
- Turn on Developer mode in ChatGPT Settings (under Plugins), then create the plugin from the Plugins page in the left sidebar, not the settings screen. Name it BetterSocials, paste https://bettersocials.ai/api/mcp as the Server URL, leave Authentication on OAuth, tick the confirmation box, and Create. Developer mode needs a paid ChatGPT plan on the web.
- Do I need to be a developer to use this?
- No. The Claude and ChatGPT doors are paste-one-address setups with a normal sign-in, and there is no key to manage. Keys and the REST API exist for people building scripts and agents; most people never touch them.
- Is the BetterSocials API free?
- Every workspace has API access, including the free video editor plan, and there is no separate API price. Agent actions that generate things, like AI drafts, captions, and voiceovers, spend the same credits the in-app buttons do.
- Can an agent post or send DMs without me knowing?
- Not quietly. Anything that reaches a real social account is a send: Claude and ChatGPT ask before each one unless you tell them to stop asking, sends are rate-limited to 30 per 5 minutes, and every send passes the same locks and failure ledger the app itself uses. You can disconnect any agent in one click from Settings → Connected apps.