Threads API for text, image and video posts
Threads processes every post asynchronously, even plain text. PostMCP creates the container, polls it to FINISHED and publishes — one call from your side.
- Endpoint
- POST /api/tools/create_post
- Platform value
- "threads"
- Auth
- x-api-key or Bearer token
- Upstream
- graph.threads.net v1.0
What the Threads API gives you
The Threads API mirrors Instagram's container model, with one twist that catches people out: text posts are asynchronous too. Every post — TEXT, IMAGE or VIDEO — creates a container that must reach FINISHED before threads_publish will accept it.
PostMCP runs that loop against graph.threads.net/v1.0 with a window sized to the media type: roughly 30 seconds for text and images, 60 for video. Errors come back with the error_message Threads returned rather than a generic failure.
Text posts
Plain posts are submitted as media_type: TEXT and published once the container settles.
Image posts
A public image_url plus optional text becomes an IMAGE container.
Video posts
.mp4 and .mov URLs are detected and submitted as VIDEO with a longer processing window.
Adaptive polling
Text and image containers poll for ~30 seconds; video gets ~60. The first check fires after 1.5s to keep short posts fast.
Scheduled publication
Queue by date and time, then edit or cancel before the slot.
Cross-posting
Ship the same copy to Instagram, Facebook, X, LinkedIn and Bluesky in one call.
Common uses
- Mirror your X timeline to Threads without writing a second integration.
- Post short build-in-public updates straight from a CI hook.
- Let an agent adapt a long LinkedIn post into a 500-character Threads version.
- Queue a launch sequence across Threads and Instagram in one script.
Post to Threads in three steps
Connect the account once, grab an API key, then send a single request. The same body works from a shell, a server, or an AI agent.
- 1
Connect your Threads account
Open the dashboard, choose Threads and complete the hosted connect flow. Credentials are encrypted into the user vault and never returned to your client.
- 2
Create an API key
Generate a key from the dashboard. Send it as
x-api-key, as anAuthorization: Bearerheader, or as anapikeyquery parameter on the MCP transport. - 3
Send your first post
POST to
/api/tools/create_postwithplatforms: ["threads"]. SetpublishImmediatelyto broadcast now, or supplyscheduleDateandscheduleTimeto queue it.
curl -X POST "https://api.postmcpai.com/api/tools/create_post" \
-H "Content-Type: application/json" \
-H "x-api-key: $POSTMCPAI_API_KEY" \
-d '{"content":"Shipping something new today. Built with PostMCP AI.","platforms":["threads"],"publishImmediately":true}'{
"success": true,
"message": "Post created and queued successfully",
"post": {
"id": "66a50c89e4b019a2b72f",
"content": "Shipping something new today. Built with PostMCP AI.",
"platforms": [
"threads"
],
"status": "published"
}
}Threads API endpoints
Every endpoint is a POST against the base URL https://api.postmcpai.com. The same seven operations cover all connected networks — set the platform value to "threads" to target Threads.
| Operation | Endpoint | What it does |
|---|---|---|
| preflight_post | POST/api/tools/preflight_post | Dry-run copy against limits, targets and credits before publishing. |
| create_post | POST/api/tools/create_post | Schedule a post or broadcast it immediately. |
| publish_post_now | POST/api/tools/publish_post_now | Force a queued post out ahead of its slot, or retry the profiles that failed. |
| list_posts | POST/api/tools/list_posts | Read the queue — scheduled, published, draft and failed — with counts. |
| get_post | POST/api/tools/get_post | Read one post, with per-profile delivery state and live URLs. |
| get_post_analytics | POST/api/tools/get_post_analytics | Read how a post did: views, likes, comments and shares per profile. |
| get_profile_analytics | POST/api/tools/get_profile_analytics | Read a profile’s followers, post count and views from its network. |
| update_post | POST/api/tools/update_post | Edit copy, targets, schedule or status before publication. |
| reschedule_post | POST/api/tools/reschedule_post | Move a post to another slot, keeping its copy and targets. |
| reset_stuck_post | POST/api/tools/reset_stuck_post | Release a post left stuck mid-publish so it can be retried. |
| delete_post | POST/api/tools/delete_post | Remove a scheduled or draft post from the queue. |
| get_connected_accounts | POST/api/tools/get_connected_accounts | List connected profiles, handles and page IDs per platform. |
| get_account_health | POST/api/tools/get_account_health | Find connections whose token expired or is about to. |
| generate_image | POST/api/tools/generate_image | Generate a post image and get back a hosted URL for mediaUrl. |
| list_workspaces | POST/api/tools/list_workspaces | List the workspaces on this key, with the id to scope other calls to. |
| get_user_info | POST/api/tools/get_user_info | Read plan tier and credit balance. |
create_post parameters
| Field | Type | Required | Description |
|---|---|---|---|
| content | string | Yes | Text body of the post. Truncation rules are enforced by the destination network, not by PostMCP. |
| targetAccounts | array[object] | Yes | The profiles that receive the post. Each entry takes platform and profileId (from get_connected_accounts). Only the profiles listed here are posted to. |
| platforms | array[string] | Optional | Shorthand for whole networks — linkedin, twitter, facebook, instagram, threads, bluesky, youtube. Each one expands to every connected profile on it, so prefer targetAccounts unless you mean that fan-out. Optional when targetAccounts is given. |
| publishImmediately | boolean | Optional | When true the post is broadcast on receipt. Defaults to false, which queues it. |
| scheduleDate | string | Optional | Publication date as YYYY-MM-DD. Required when publishImmediately is false. |
| scheduleTime | string | Optional | Publication time as HH:MM on a 24-hour clock. Required when publishImmediately is false. |
| timezone | string | Optional | IANA zone the schedule above is written in, e.g. Asia/Kolkata. Omit it and the wall-clock slot resolves as UTC, which is rarely what "10am" meant. |
| mediaUrl | string | Optional | Publicly reachable image or video URL. PostMCP fetches it and re-uploads it in the format the network expects. Required, and must be a video, when the post targets YouTube. |
| workspaceId | string | Optional | Workspace to post from, taken from list_workspaces. Omitted, the call resolves to the default workspace on the key. |
Threads OAuth and API keys
Two layers of auth sit under every request: the Threads credential you grant once during connect, and the PostMCP API key your code sends on each call.
Open the connect URL
Send the user to /connect/threads. PostMCP builds a threads.net/oauth/authorize URL carrying a signed state JWT.
Threads consent screen
The user approves basic profile access and content publishing.
Token exchange
The callback code becomes a Threads access token bound to the Threads user ID.
Encrypted storage
The token is encrypted into the user vault and used automatically on publish.
Scopes requested from Threads
| Scope | Why it is needed |
|---|---|
| threads_basic | Reads the connected Threads profile and its user ID. |
| threads_content_publish | Creates containers and publishes posts to the account. |
Sending your API key
x-api-key: pmcp_sec_…Authorization: Bearer pmcp_sec_…/mcp?apikey=pmcp_sec_…How PostMCP publishes to Threads
One request from you becomes this sequence server-side. Knowing the shape helps when you are debugging a failed broadcast.
- 1
Choose the media type
No media means
TEXT; a.mp4or.movURL meansVIDEO; anything else meansIMAGEwith the text carried alongside. - 2
Create the container
The body is POSTed to
/{threads-user-id}/threads, returning a creation ID. - 3
Poll with an adaptive window
First check after 1.5s, then every 3s — up to 10 attempts for text and images, 20 for video.
- 4
Surface real errors
An
ERRORstatus raises theerror_messageThreads returned instead of a generic failure. - 5
Publish the container
The creation ID is posted to
/threads_publishand the returned ID is stored.
Upstream Threads calls
| Method | Endpoint | Purpose |
|---|---|---|
| POST | https://graph.threads.net/v1.0/{threads-user-id}/threads | Creates a TEXT, IMAGE or VIDEO container and returns a creation ID. |
| GET | https://graph.threads.net/v1.0/{creation-id}?fields=status,error_message | Polls the container until status reads FINISHED or ERROR. |
| POST | https://graph.threads.net/v1.0/{threads-user-id}/threads_publish | Publishes the finished container by `creation_id`. |
Direct Threads API vs PostMCP
| Aspect | Calling Threads directly | With PostMCP |
|---|---|---|
| Text posts | Still asynchronous — container plus poll | One call, poll handled for you |
| Media type | Set TEXT, IMAGE or VIDEO yourself | Inferred from the media URL |
| Carousels | One container per item, then a CAROUSEL container with `children` | One `mediaUrls` array of 2–20 URLs |
| Polling window | Fixed retry loop you tune by hand | Adaptive to the media type |
| Error detail | Parse status and error_message manually | Threads' own message surfaced verbatim |
| Scheduling | Not offered by the API | Built in and editable |
Threads limits and supported media
These ceilings are set by Threads, not by PostMCP. Your content field is forwarded unchanged, so the network enforces them rather than silently truncating.
Common Threads API errors
Failures are recorded per platform on the post record, so a multi-network broadcast that partially succeeds tells you exactly which leg failed and why.
| Code | Message | Likely cause | Fix |
|---|---|---|---|
| 400 | Threads Container creation failed | A missing text body on a TEXT container, or a media URL Threads could not fetch. | Send non-empty `content` for text posts and host media on a public URL. |
| ERROR | Threads container processing failed | The container reported ERROR during processing, commonly an unsupported video encode. | Re-encode video as H.264 MP4 and retry. |
| TIMEOUT | Threads container processing timed out | FINISHED was not reached inside the polling window. | Use a shorter or smaller video asset. |
| 190 | Access token for threads is missing or invalid | The Threads token expired or was revoked. | Reconnect Threads from the dashboard. |
Post to Threads from an AI agent
The same seven operations are exposed as MCP tools. Point Claude Desktop, Cursor, or any MCP client at the server and your agent can publish to Threads directly.
{
"mcpServers": {
"postmcpai": {
"command": "npx",
"args": ["-y", "@postmcpai/server"],
"env": {
"POSTMCPAI_API_KEY": "pmcp_sec_YOUR_SECRET_KEY",
"POSTMCPAI_API_URL": "https://api.postmcpai.com"
}
}
}
}Prompt the agent directly
With the server connected, natural language is enough — the agent picks the tool and fills the arguments:
“Draft a Threads post about today’s release and schedule it for 9:30am tomorrow.”
Threads API questions
How do I post to Threads with the API?
POST to https://api.postmcpai.com/api/tools/create_post with platforms: ["threads"] and your content. PostMCP creates the container on graph.threads.net, polls it to FINISHED and calls threads_publish.
Why do Threads text posts need a container?
The Threads API processes every post asynchronously, including plain text. The container must report FINISHED before threads_publish accepts it, which is why a naive two-call implementation intermittently fails.
Can I attach images or video to a Threads post?
Yes. Pass a public mediaUrl. A .mp4 or .mov URL is submitted as VIDEO; anything else is submitted as IMAGE with your text alongside.
Can I post a Threads carousel?
Yes. Pass mediaUrls with 2–20 public image or video URLs. Each becomes its own item container flagged is_carousel_item, is polled to FINISHED, and a CAROUSEL container listing them is created and published. Images and video can be mixed; order is kept as given.
What is the Threads character limit?
500 characters per post. Longer copy is rejected by Threads, so trim before sending or let an agent adapt it.
How long does Threads take to publish?
Text and image containers usually settle in a few seconds; PostMCP polls for about 30 seconds before giving up. Video gets a 60-second window.
Which permissions does the Threads API need?
threads_basic to read the profile and threads_content_publish to create and publish posts. Both are requested during the connect flow.
Other social media APIs
One authenticated POST publishes to a LinkedIn profile or company page. PostMCP owns the OAuth dance, the image upload handshake and the versioned LinkedIn REST calls underneath.
Read the referenceSend one request and PostMCP handles Twitter API v2 for you — PKCE OAuth, the three-step chunked media upload, refresh tokens and the final tweet call.
Read the referencePublish text and photo posts to every Facebook Page you manage from one endpoint. PostMCP resolves page access tokens and calls Graph API v26.0 for you.
Read the referenceInstagram publishing is a two-phase asynchronous flow. PostMCP creates the media container, polls it to FINISHED and publishes it — you send a caption and a media URL.
Read the referenceBluesky is not OAuth — it is a session against an AT Protocol server. PostMCP refreshes that session, uploads image blobs and writes the feed record for you.
Read the referenceYouTube is the one network here that will not take a URL. It wants the video bytes over a resumable session — PostMCP fetches, uploads and publishes them for you.
Read the referenceStart posting to Threads today
Connect the account, take an API key and send your first request in under five minutes — from a shell, your backend, or an AI agent.
