https://api.superx.so/v1/mcp. Any MCP client can read your X (Twitter) analytics, posts, contacts, contact lists, audience replies, signal agents and their leads, tags, scheduled queue, and long-form Articles, search a library of high-performing posts for inspiration, schedule, edit, or delete posts (with image attachments), manage contact list members, create, edit, pause, resume, or delete signal agents (automated lead finders) and the signals they watch, record verdicts on the leads they find, create, edit, and delete Engage feeds, create, edit, schedule, and publish Articles with AI cover generation, read or update the account’s Context settings (the background SuperX’s AI uses when writing: profile description, interests, rules, favorite creators, style guide, products), rebuild the account’s generated style guide, stage the audience description, rubric and keyword ideas for a new signal agent, queue direct messages into the account’s own DM pipeline, and read the posts the account’s Workers wrote and save, schedule or dismiss one, using the same API keys as the REST API. Nothing to install or run yourself.
You need an API key from app.superx.so/account?tab=api and an active SuperX subscription.
Claude Code and Codex (header auth)
CLI-class clients send the key as a normal Authorization header. This is the preferred mode.YOUR_API_KEY with your sxk_... key. For Codex, add a streamable HTTP MCP server with the same URL and header in its MCP configuration.
ChatGPT and claude.ai (keyed URL)
Remote connectors that cannot send custom headers use a URL with the key embedded:- ChatGPT: Settings, then Connectors, then add a custom connector with this URL. Set authentication to none (the key is in the URL).
- claude.ai: Settings, then Connectors, then add a custom connector (remote MCP server) with this URL, authentication none.
Tools
For the tool chains behind a whole job (a weekly recap, a week of posts, a queue reshuffle, a DM campaign), see Agent playbooks: 29 recipes, each naming the tools to call in order and where the chain stops.Scopes
The forty-three read tools work with any API key. The write tools (schedule_post, publish_post, bulk_retime_scheduled_posts, bulk_enable_auto_retweet, bulk_delete_scheduled_posts, update_scheduled_post, delete_scheduled_post, create_article, update_article, schedule_article, publish_article, generate_article_cover, add_contact_list_member, remove_contact_list_member, create_signal_agent, set_signal_agent_status, delete_signal_agent, upload_media_from_url, update_context, update_product, delete_product, update_queue_settings, draft_post, create_tag, update_tag, delete_tag, set_products, add_contact_note, update_contact_note, delete_contact_note, create_contact_list, rename_contact_list, delete_contact_list, add_contact_list_members, remove_contact_list_members, create_engage_feed, update_engage_feed, delete_engage_feed, update_signal_agent, add_signal_agent_signal, remove_signal_agent_signal, set_lead_feedback, add_dataset_to_contact_list, collect_audience, search_leads, research_profiles, draft_outreach_dms, refine_dataset, draft_reply, remix_post, inline_edit, rephrase_text, fact_check, predict_algorithm, regenerate_style_guide, scrape_product, queue_dm_campaign, cancel_dm_campaign, draft_worker_suggestion, schedule_worker_suggestion, and dismiss_worker_suggestion) need a key with the write scope; with a read-only key they return an insufficient-scope error the model can relay. The post, article, contact, contact-list, and signal-agent write tools take an optional account parameter (an account id or @handle) and work on your main account or any account you have linked to it; omit account and the tool writes to your main account. list_accounts returns the ids and handles that parameter accepts, and get_me reports whether the key has the write scope at all. Accounts other people share with you are read-only for those tools, with a set of exceptions: search_leads, draft_reply, remix_post, inline_edit, rephrase_text, fact_check and predict_algorithm spend credits without changing the account, so they work on a shared account too. draft_post is not among them: it prepares the account’s own style guide when needed, so a shared account returns writes_main_account_only. regenerate_style_guide is not one of them: it rewrites the account’s own style guide, so a shared account refuses it, and neither are queue_dm_campaign and cancel_dm_campaign: sending on someone else’s behalf is not something a share grants. upload_media_from_url takes no account: uploads belong to you rather than to one X account, so an object_key can be attached to a post on any of your accounts. Tags are workspace-wide, one set covering all your linked accounts, so create_tag, update_tag, and delete_tag take no account and always write on the main account. The context and queue-settings tools accept an account parameter for any linked or shared account. On an account shared with editor permission, update_context, update_product, delete_product, set_products, and scrape_product return an editor_restricted error while update_queue_settings works.
To attach images to a post, call upload_media_from_url first, then pass the returned object_key in the per-part media field of schedule_post or update_scheduled_post (up to 4 images or exactly 1 GIF per part, optional alt_text). upload_media_from_url counts against a daily quota of 100 uploads per key. On update_scheduled_post, replacement posts sent without media remove the media those parts carried; re-include the existing object_keys to keep them. Video is not supported yet.
Advanced settings on schedule_post: omit autoRetweet, autoDelete, autoPlug, or superFollowersOnly to inherit that setting’s Default Post Settings value from the SuperX app; pass an object to override or null to turn a setting off for that post. Exactly five settings inherit this way (auto retweet, auto delete, auto plug, auto DM, Super Followers only); other composer defaults such as Bluesky cross-posting are never applied to API posts. autoPlug.templateId comes from list_plug_templates called with the same account you are posting on; unknown ids are rejected. On update_scheduled_post there is no inheritance: provided values override, omitted ones keep the post’s current settings, null removes them. Auto DM always inherits the user’s defaults and has no parameter; if a plan limit strips it, the schedule_post result carries auto_dm_skipped: true.
Creating a signal agent returns immediately, but the agent finds leads over the following minutes and days; there is no synchronous search. Check get_signal_leads later for what it found. create_signal_agent takes keywords (searches) and signals (all four watch types) and is a PARTIAL SUCCESS: entries it could not add come back in warnings with a code, and the agent is still created from the ones that landed, so read warnings before telling the user what the agent watches. add_signal_agent_signal adds one target per call, remove_signal_agent_signal removes one (removing the last one is allowed and leaves the agent finding nothing), and update_signal_agent changes the agent’s own fields, where a new ICP re-scores only future leads. set_lead_feedback teaches the scorer, so ask the user for the verdict rather than deciding for them; its lead_id is the numeric id from get_signal_leads, not an X user id.
Engage feeds are writable: create_engage_feed saves a feed but deliberately does NOT switch the feed the person has open in the app, so say where to find it rather than assuming they will see it. Up to 8 feeds per account, update_engage_feed changes one source family at a time, and deleting the feed the app has open hands the slot to the first remaining feed. An X list source (x_list_id / x_list_url) and the profile_watch, follower_watch and list_watch signal types are resolved live, so they also spend one unit of the tighter enrichment allowance and only PUBLIC X lists work.
Context settings (get_context / update_context) are the background SuperX’s AI uses on every writing surface: the profile description grounds the voice, interests drive content suggestions and personalized search, SuperX rules are mandatory instructions, reply settings steer generated replies, favorite creators (max 3 X usernames) inspire the style, and the style-guide overrides outrank the app’s generated guide until cleared with null. On update_context only the params you pass change; interests and favoriteCreators fully replace the stored lists. Because these settings shape all future AI output for the account, confirm with the user before changing them. Products (max 5, managed with update_product / delete_product) are mentioned naturally in generated content; deleting one is reversible by re-adding the same url. set_products replaces the whole list at once: any product whose url is missing from the call is removed, so read get_context first and send every product the account should keep, or use update_product to change one in place.
Queue settings (get_queue_settings / update_queue_settings) are the account’s posting schedule: the predefined time slots the queue fills and the timezone they run in. Slots are { time: "09:00", days: [1, 3, 5] } with 0 = Sunday, at most 50 entries, one per unique time. slots is a full replace, so send the complete set the user should end up with; [] clears every slot and leaves the queue all-custom. Changing the slots also re-flows the queue the way the app does: a queued post sitting exactly on an old slot moves to the matching new slot, so gaps are preserved and hand-picked custom times stay put. The result carries reflow: { moved, skipped, bailed }; bailed: true means the settings were saved but the queue was deliberately left alone, and retrying is safe. Changing only timezone never moves posts. Changing the timezone and the slots in one call usually moves nothing, because the existing posts were placed under the old timezone; to re-flow them, change the timezone first, then send the slots in a second call.
The contacts tools work on people the account already knows (the note list, update and delete tools also work on any id with an existing note). get_contact takes a numeric X user id (from get_top_contacts, get_contact_list_members, or get_signal_leads) and returns the stored profile plus the lists that person is in; refresh: true refreshes a stale profile from X and counts against the tighter live-contact-enrichment allowance, so leave it off unless the follower counts have to be current. Notes are private to SuperX and never posted: a note written through a tool is attributed to the account it was written as. add_contact_list_members adds up to 500 people in one call by X user id and does no live lookup, which is why it costs one write and no enrichment; ids SuperX has never seen come back in not_found and are not added, so add those one at a time with add_contact_list_member and a handle. Deleting a contact list also stops any signal agent depositing into it until the agent is repointed in the SuperX app, so confirm before calling delete_contact_list.
The live X lookups (lookup_x_post, get_x_post_replies, lookup_x_user, get_x_user_posts) read X right now rather than SuperX’s stored data, take no account parameter, and spend the tighter enrichment allowance: 1 unit each, 3 for replies, and 2 for a post with include_quotes or a handle SuperX has never seen. On top of that they share an allowance of 300 live lookups per day with Ask SuperX in the app, so look up what the user actually asked about rather than sweeping an account’s network; over the allowance is a lookup_quota_exceeded error that resets at midnight UTC. Repeats within 15 minutes may come from a server-side cache. get_x_post_replies samples the best-liked replies from up to 3 pages, so it is never the full set (the audience collections in list_datasets are), and unlike the app it does not hide the account owner’s own replies. search_inspiration_media is the app’s Inspiration > Media tab, costs no enrichment, and is not personalised here: omit q to browse the newest media instead of searching. It returns no media file URLs, so point the user at source_url (the original post) rather than trying to show them the video or thumbnail.
The four system people-lists (Followers, Following, Repliers, Reposters) are read with get_audience, not get_contact_list_members, which only serves lists the user created. get_audience pages by cursor: pass the next_cursor it returns back as cursor, and read synced_count for the size of the whole list rather than counting rows (it may exceed a full walk, because edges that were later removed are still counted). Repliers and reposters are a rolling 90-day window, so someone drops out when their last reply ages past it. get_mentions reads the posts @-mentioning the account live, newest first, each with the post it replies to; one call costs 3 of the plan’s daily feed fetches, so read a page and work from it rather than polling. Unlike the app’s Mentions tab it does not hide posts the user skipped or blocked there, and by default it leaves out mentions they already replied to on X (include_replied keeps them, flagged replied).
Datasets are the audience collections Ask SuperX builds in the app: the repliers, quoters or reposters of a post, the members of an X list, the user’s own posts or replies, or a research brief. list_datasets, get_dataset and get_dataset_rows read them and take no account parameter, because a dataset belongs to the key owner rather than to one X account (the account a collection ran as comes back as x_account_id). Datasets are kept for 30 days, and collect_audience builds new ones here as well as in the app. A collection big enough to run in the background, or one whose size cannot be established up front, comes back with status: "collecting" and no rows: poll get_dataset until it says ready before describing what is in it, and never state a row count from the create result. Only one background collection runs per account at a time, and every collection counts against 10 per day shared with Ask SuperX in the app. get_dataset_rows and add_dataset_to_contact_list need status: "ready"; a dataset that is still collecting returns dataset_not_ready, so call get_dataset and wait rather than retrying in a loop, and a failed one has to be rebuilt in the app. add_dataset_to_contact_list costs one write and no enrichment because the profiles come from the dataset itself: people are deduped by X account id, rows without one come back in skipped_without_id, and re-running is safe (people already in the list are counted in duplicates). Own-content datasets carry has_people: false and cannot be added to a list. The CSV export is available on the REST API, not here.
The outreach tools run in a chain: search_leads finds people on X right now and saves nothing (it creates no signal agent and no stored leads, so use create_signal_agent when the user wants an audience that keeps filling up); research_profiles turns handles, a contact list, an agent’s leads or an existing dataset into briefs saved as a dataset; refine_dataset filters a dataset by what each person wrote into a NEW dataset, leaving the source untouched; and draft_outreach_dms writes one message per person onto a research dataset. The drafts are text and nothing is sent: no tool here sends a DM, and a person sends them from the SuperX app after reviewing each one, so never tell the user their messages have gone out. Ask the user for their format or an example message before calling draft_outreach_dms; never invent one.
research_profiles costs 1 credit per profile actually researched (unresearched ones are returned) and reads X live, so more than 5 profiles run in the background: the result then carries status: "collecting" and you must poll get_dataset until it says ready before stating any brief count. A big refinement does the same. search_leads is a write tool for the same reason draft_post is: it spends AI credits and live requests, not because it saves anything, so a read-only key gets an insufficient-scope error; an account shared with you still works, because nothing is written to it. The REST endpoint POST /v1/signals/leads/search behaves identically. search_leads and research_profiles also draw on a platform-wide fair-use ceiling for live-data actions on top of the plan’s own daily caps; a 429 whose scope is "platform" is not about this account’s quota, so wait and retry rather than telling the user they are out. refine_dataset counts against the same 10 dataset collections a day collect_audience does, keeps rows it cannot judge (counted as unclear), and only works on datasets whose rows carry text.
The writing helpers all return TEXT and write nothing: draft_reply drafts one reply to a post, remix_post rewrites a post as close to or as far from the original as asked (closeness 0 to 100), inline_edit changes one selected piece while keeping the surrounding style, rephrase_text applies one preset rewrite, fact_check checks a claim, and predict_algorithm compares two versions. Nothing here is posted, scheduled or sent, so never say a reply went out: show the draft, let the person edit it, and call draft_post, schedule_post or publish_post when they say so. There is still no reply-sending tool anywhere in this server. They are write tools for the same reason draft_post is: they spend AI credits. Unlike draft_post, a shared account works for these. draft_reply with a post_id reads the post live and spends one lookup from the same 300-a-day allowance the live X lookups use; pass the wording in post instead and it spends none. Ask the user what they want a reply to convey (thoughts) rather than inventing an opinion for them. A fact_check verdict is a model’s reading of a couple of search results: report it with its sources and never as settled fact, and predict_algorithm scores are an opinion for comparing two drafts, not a prediction of reach.
Five helpers cost no AI credits at all. regenerate_style_guide rewrites the account’s generated style guide from its recent posts, which is what every writing tool above reads: it is limited to once an hour per account, and any style-guide overrides the user set in update_context still outrank it, so say that rather than promising a change of voice. scrape_product refreshes one saved product from its own page (the url comes from the saved product, so fix a moved url with update_product first), and expand_icp_from_url reads a website and infers who it sells to. Those two read a page, and each account can read 20 pages a day, shared with the SuperX app. suggest_keywords and expand_icp create nothing and read nothing live: a suggestion becomes real only when it is passed to create_signal_agent or add_signal_agent_signal, and a rubric has nowhere to be saved (agents are created with icp_description), so use it to sharpen that description or to score people in the conversation.
draft_post writes post text in the user’s voice and saves nothing apart from the account’s style guide, which it may prepare on first use. It is a write tool because it spends AI credits, not because it changes anything: show the drafts, let the user pick and edit one, then call schedule_post with the final text. Each draft copies the shape of a proven post, never its content: pass one in mirror (50 to 1500 characters, the text of a post from find_inspiration works well) or leave mirror out and a shape is picked, optionally biased with collection. Pick a mirror with room for the user’s data: a two-line aphorism squeezes the facts out. A draft’s mirrored is null when it ended up copying no shape. voice is mine (default), creator, or hybrid; the last two need a creator handle, and the first call naming a new creator returns creator_style_pending while their style guide is prepared, so retry a minute later. mine is the voice of the account in account: its own posts, style guide and rules, so pass a linked account’s id or @handle to draft as that account. Accounts shared with you are refused. Ask for the count the user actually wants, since every draft costs credits, and expect the call to take 20 to 40 seconds.
Workers are the scheduled AI writers the user sets up in the SuperX app: each run writes a batch of posts into the app’s “To review” queue. list_workers shows the Workers themselves and list_worker_suggestions shows what they wrote, newest first, with to_review as the default status and drafted, scheduled, dismissed and all for the rest. Creating, editing and RUNNING a Worker stays in the app, so there is no tool for any of those; say so rather than offering to run one. The three write tools act on one suggestion: draft_worker_suggestion saves it as a draft, schedule_worker_suggestion queues it for a time you pass, and dismiss_worker_suggestion drops it out of the queue without touching any post. Nothing here posts to X, but a scheduled suggestion does go out later, so show the user the text and confirm the time first, and offer to rewrite it with remix_post or rephrase_text and save your version with schedule_post instead. A suggestion can only be saved once: a second draft or schedule call on the same id comes back already_saved, a dismissed one already_dismissed, and an id belonging to another account’s Worker suggestion_not_found. Once saved, the response carries the new post_id, which is an ordinary post: read it with get_scheduled_posts, edit it with update_scheduled_post, cancel it with delete_scheduled_post.
get_scheduled_posts returns posts most recently updated first, so page 1 is the newest work, and it takes a page parameter (1-based) for walking back through older ones rather than raising limit. That ordering is the same for every status it takes, not just drafts: scheduled and all come back most recently updated first too. The REST endpoint orders sent and error the same way, and its from/to request is the calendar query instead, in post-time order, oldest first. Timestamps passed to the scheduling tools must be ISO-8601 with an explicit Z or numeric offset. Naive local times are rejected. A new time alone never schedules a draft post; update_scheduled_post promotes only when status is set to scheduled. publish_post goes out to X the moment it returns and cannot be undone, so confirm the exact text with the person first and use schedule_post for anything that can wait; its idempotency_key is required and is yours to pick, reused verbatim on any retry (a retry never posts twice) and changed only for genuinely new content. The three bulk tools only touch posts in the queue, answer with counts rather than per-post results, and need no idempotency key because re-running them converges. Article bodies are markdown in both directions; publish_article is live and irreversible, and generate_article_cover costs a flat 25 AI credits per generation and counts against the account’s daily and monthly cover caps. Steer a cover with style_id (a saved style from list_cover_styles) or style_text (a one-off description), never both: passing both is a tool error, so the call always says exactly which look was rendered.
The DM tools QUEUE, they do not send. queue_dm_campaign puts messages into the account’s own DM queue, the same one the SuperX app fills, and the app’s scheduler sends them within the account’s daily and monthly DM limits: the reply is counts, not deliveries, so never tell the user their messages have gone out. When every recipient is skipped nothing is queued and the id comes back null, so say that rather than inventing a campaign. get_dm_campaign and list_dm_queue show what actually went out, cancel_dm_campaign removes the unsent ones (anything already sent cannot be recalled, and a campaign with nothing left answers 0), and get_dm_limits shows the allowances, which are plan limits rather than AI credits. Confirm the recipient list and the exact wording with the user before queueing anything: they are responsible for these messages under X’s rules on automation and unsolicited contact, and unsolicited bulk DMs can get an X account suspended. Auto DM on a scheduled post is a separate thing: pass autoDM to schedule_post to message the people who reply to or repost that post.