Skip to content
SocialHelper
Join waitlist
Developers

Threads API: how to post to Threads from code

How the Threads API publishes: access and tokens, the container and publish calls, carousels, the 500-character emoji rule, the 5-link limit and the daily cap.

8 minute read By SocialHelper team

A post card in a glass staging box under a progress ring, joined by a thread to a toggle switch and then to a display panel showing the post with a check mark

The Threads API is Meta’s API for publishing to Threads and reading posts, replies and insights. It lets your code publish text, images, videos and carousels to a Threads profile. Publishing takes two calls. First you create a media container, then you publish it with threads_publish. You need a Meta app with the Threads use case, a token from each profile, and App Review before anyone outside your testers can connect. A profile can publish 250 posts a day through the API.

Two rules are easy to miss. Text is limited to 500 characters, but every emoji counts as its size in UTF-8 bytes, so a short post can run over. And since December 22, 2025, a post with more than 5 links fails. The examples come from the checks SocialHelper runs before it posts to Threads.

What you need before the first call

  1. A Meta app with the Threads use case. The use case has its own Threads app ID and app secret, separate from the Meta app’s.
  2. Permissions. threads_basic is needed for every endpoint and threads_content_publish for publishing. Replies and insights need their own (Meta: Threads get started).
  3. Testers, then App Review. While you build, only Threads testers you invite can grant the permissions. For anyone else, each permission “must first be approved through the App Review process”, and the app must be published.
  4. A token for each profile. The person signs in through Threads’ authorization window, and you swap the code for a token.

A Threads user access token is the credential each call carries. The token from sign-in is short-lived and lasts one hour. Exchange it for a long-lived token, valid for 60 days, and refresh it when it’s at least 24 hours old (Meta: Long-lived tokens). Meta’s own pages disagree about private profiles. The get-started page says their permission grants can’t be extended, while the token page says they now last 90 days and can be refreshed. SocialHelper renews each token in the week before it expires, and flags the profile for reconnecting if Threads refuses.

Publish a text post in two calls

A media container is Threads’ staging object for one post. Nothing is visible until you publish it. Create one with the text.

curl -X POST "https://graph.threads.net/v1.0/$TH_USER_ID/threads" \
  -d "media_type=TEXT" \
  -d "text=Autumn menu is here" \
  -d "access_token=$TH_TOKEN"

The response holds the container ID. Meta recommends waiting about 30 seconds before you publish, so check that the container is ready first.

curl "https://graph.threads.net/v1.0/$CONTAINER_ID?fields=status,error_message&access_token=$TH_TOKEN"

When the status is FINISHED, publish the container once.

curl -X POST "https://graph.threads.net/v1.0/$TH_USER_ID/threads_publish" \
  -d "creation_id=$CONTAINER_ID" \
  -d "access_token=$TH_TOKEN"

The reply is the new post’s media ID (Meta: Threads posts). The API answers at graph.threads.net and graph.threads.com. Tokens go in the access_token parameter, as in Meta’s examples.

Container statuses

A container reports one of five statuses (Meta: Threads troubleshooting).

StatusWhat to do
IN_PROGRESSWait and check again
FINISHEDPublish it
ERRORRead error_message, fix the media, start a new container
EXPIREDNot published within 24 hours; start again
PUBLISHEDAlready live; don’t publish again

Meta suggests checking once a minute for up to 5 minutes. SocialHelper checks text and images every 3 seconds for up to a minute, and videos every 15 seconds for up to 5 minutes.

Why SocialHelper skips auto_publish_text

Text posts can skip the second call. Set auto_publish_text=true and the container is published as soon as it’s created. That saves a request, but it makes creating a container the moment the post goes live. If that request times out, you can’t tell whether the post exists, and a retry could post it twice. SocialHelper never sets it. A timeout while creating a container is always safe to retry, and only the publish call can leave the outcome unclear.

Images, videos and carousels

For an image or a video, create the container with media_type=IMAGE and an image_url, or VIDEO and a video_url. Meta downloads the file from that public URL, so it has to be reachable. Add alt_text of up to 1,000 characters for screen readers.

A carousel takes three steps. Create a container for each item with is_carousel_item=true. Then create a CAROUSEL container listing their IDs in children, and publish that one. A carousel holds 2 to 20 images and videos, for example a product shot followed by a short clip. It counts as a single post toward the daily cap.

These limits come from Meta’s Threads overview (Meta: Threads API overview).

WhatLimit
Image formatJPEG or PNG, up to 8 MB
Image shapeUp to 10:1; width scaled to between 320 and 1,440 pixels
Video formatMOV or MP4, HEVC or H.264, AAC audio
Video sizeUp to 1,920 pixels wide, 23 to 60 fps, up to 1 GB
Video lengthUp to 5 minutes
Carousel2 to 20 items
Alt text1,000 characters

How Threads counts the 500 characters

Threads counts most characters as 1, but an emoji counts as its size in UTF-8 bytes. Most emoji are 4 bytes, a few such as ☕ are 3, and a flag is 8 because it’s made of two symbols. SocialHelper’s counter follows the same rule. Here’s the example post.

Autumn menu is here 🍂☕ https://example.com/menu

You see 47 characters. Threads counts 52, because the leaf adds 3 and the coffee adds 2. Against a limit of 500, a post with a row of emoji can fail even though it looks short. The Threads character counter counts the same way, and the guide to character limits compares Threads with other platforms.

  • Links. A post can have at most 5 unique links. Meta counts every distinct URL in the text, plus link_attachment if it’s a different URL. Since December 22, 2025, a sixth link fails at the container step with THREADS_API__LINK_LIMIT_EXCEEDED. In a text post, the first link becomes the preview card unless you set link_attachment, which works on text posts only.
  • Topic tags. A post can have one topic. Set topic_tag, 1 to 50 characters with no periods or ampersands. An in-text #tag still works, but Meta prefers the parameter, and #1 is never a tag.
  • Reply controls. reply_control sets who can reply: everyone, accounts_you_follow, mentioned_only, parent_post_author_only or followers_only (Meta: Threads publishing reference).
  • GIFs and polls. Text posts can carry a GIF from GIPHY, now the only provider, since Meta set Tenor support to end by March 31, 2026. A post can also carry a poll through poll_attachment.

SocialHelper checks the link count, the topic tag and the reply control before it creates anything, and reports every problem at once.

Rate limits and the daily cap

Each profile can publish 250 posts and 1,000 replies in a moving 24 hours, and delete 100. Carousels count as one post. GET /{threads-user-id}/threads_publishing_limit returns how many the profile has used, and Meta recommends enforcing the cap yourself if your app schedules posts. Other calls share a budget of 4,800 times the profile’s impressions in the last 24 hours, with impressions counted as at least 10.

SocialHelper reads the publishing limit before it creates any container. If the profile has used its 250, the post waits an hour and tries again, instead of failing.

Retries without double posts

The risky call is threads_publish. If it times out, the post may already be live. SocialHelper treats that as an unknown outcome. Before it tries again, it looks for the same text among the profile’s 25 latest posts made since the first attempt. A container that already reports PUBLISHED is handled the same way. The guide to idempotent posting explains the pattern, and the comparison of social media APIs shows how other platforms handle it.

The shortcut: one request

If you’d rather not run the containers, status checks, token refresh and limit checks yourself, SocialHelper does them. Connect the Threads profile once, then send one request.

curl https://socialhelper.app/api/v1/posts \
  -H "Authorization: Bearer $SOCIALHELPER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
      "text": "Autumn menu is here 🍂☕ https://example.com/menu",
      "accounts": [
          "THREADS_ACCOUNT_ID"
      ],
      "overrides": {
          "threads": {
              "topic_tag": "Baking"
          }
      }
  }'

That’s all. The topic tag goes in overrides, and the same call can post to other accounts at once with their own text. The Threads integration page lists every option.

Here’s where SocialHelper’s own Threads integration stands. Live now: Threads.

Frequently asked questions

Is the Threads API free?

Meta lists no price for the Threads API. The costs are App Review before the public can connect, and the daily caps of 250 posts and 1,000 replies per profile.

How do I get a Threads API access token?

Add the Threads use case to a Meta app and send the person through its authorization window. Exchange the code for a one-hour token, then exchange that for a 60-day token you refresh after a day.

How many characters can a Threads API post have?

Five hundred, with each emoji counted as its UTF-8 bytes. A flag counts as 8, so a few emoji take a noticeable share of the limit.

Can I schedule Threads posts with the API?

Not directly. The API has no publish-time field, so your app stores the post and publishes it when it’s due. Meta asks scheduling apps to enforce the publishing limit themselves.

Is the Threads API the same as the Instagram API?

No, although both come from Meta and both use containers. Threads has its own app ID, tokens, permissions and host, graph.threads.net.

How we checked

We read Meta’s Threads documentation on October 17, 2026. That covered the overview, get started, long-lived tokens, posts, publishing reference, troubleshooting and the changelog. Then we ran a fact check of every limit and quote against those pages.

We measured the example’s length with SocialHelper’s Threads counter on October 17, 2026, and our test suite checks it and the example request. If you spot something that has changed, contact us and we’ll correct it.

Post everywhere from one place.

SocialHelper is invite-only during early access. Join the waitlist: the first 100 people to sign up get a Founding account with higher limits, free for 12 months.