Post

Instagram Threads API - Part 2 - Creating Post using REST Apis

Part 2 of posting to Instagram Threads with the API: getting a long-lived token and publishing text, image and link posts with the Threads endpoints.

Instagram Threads API - Part 2 - Creating Post using REST Apis

Hey there, welcome back to our journey in supercharging your Thread Account! In our last chat, we talked about getting your hands on that special API access token tailor-made for creating posts on your Threads account. Now, armed with that token, let’s dive into the fun part – crafting and scheduling posts using REST APIs.

TLDR Please visit https://github.com/lamplitlabs/ferret/blob/main/pkg/external/thread.go for golang code.

Why REST APIs, You Ask?

Alright, so let’s break it down. REST APIs are like the magic wand for tech folks. They let you talk to all sorts of web services, and in our case, Thread’s API is the one we’re interested in. By tapping into these APIs, you can weave Threads right into your existing tools and workflows. It’s like having a social media genie at your command, ready to whip up posts whenever you need them!

Prerequisites

Before you start, make sure you have:

  • A Threads account that is connected to an Instagram professional account.
  • A Meta app with the Threads API use case added and the threads_basic and threads_content_publish permissions.
  • A long-lived user access token and your Threads user id, both generated by following part 1.
  • A publicly reachable https:// URL for any image or video you want to attach (Threads downloads the media from that URL; local paths and private hosts will not work).

All the requests below go to https://graph.threads.net/v1.0/ and send the token in the access_token query parameter.

How publishing works on Threads

Unlike the Facebook Page API, where a single POST /feed call publishes a post, Threads uses a two-step flow:

  1. Create a media container with POST /{USER_ID}/threads. This validates the post (and, for media posts, downloads the image or video) and returns a container id. Nothing is visible on your profile yet.
  2. Publish the container with POST /{USER_ID}/threads_publish?creation_id={CONTAINER_ID}. This turns the container into a real post and returns the Threads post id.

Keep that in mind: if you only ever call step 1, your “posts” will never show up.

Step 1: Create a Text Post

  • Url: https://graph.threads.net/v1.0/[USER_ID]/threads?media_type=TEXT&text=[CONTENT_TEXT]&access_token=[ACCESS_TOKEN]
    • Method: POST
    • Parameters:
      • media_type must be TEXT
      • text the body of the post (URL-encode it; max 500 characters)
      • link_attachment (optional) an https:// URL to show as a link preview card under the text
      • reply_control (optional) everyone (default), accounts_you_follow or mentioned_only
    • Example Request

      1
      2
      3
      4
      5
      
      curl -X POST "https://graph.threads.net/v1.0/[USER_ID]/threads" \
        -d "media_type=TEXT" \
        -d "text=Hello from the Threads API" \
        -d "link_attachment=https://blogs.lamplitlabs.com/posts/odata/" \
        -d "access_token=[ACCESS_TOKEN]"
      
    • Response Example of a successful response
      1
      2
      3
      
      {
        "id": "17889455560051444"
      }
      

      The id is the container id. Save it, you need it for the publish call.

  • When you create a post, it will not be published until you hit the publish API below.

Step 1 (alternative): Create an Image or Video Post

The call is the same endpoint; only the parameters change.

  • Url: https://graph.threads.net/v1.0/[USER_ID]/threads?media_type=IMAGE&image_url=[IMAGE_URL]&text=[CONTENT_TEXT]&access_token=[ACCESS_TOKEN]
    • Method: POST
    • Parameters:
      • media_type IMAGE or VIDEO
      • image_url public https:// URL of a JPEG or PNG (required for IMAGE)
      • video_url public https:// URL of an MP4 or MOV (required for VIDEO)
      • text (optional) caption for the post
      • alt_text (optional) accessibility text for the image or video
    • Example Request

      1
      2
      3
      4
      5
      
      curl -X POST "https://graph.threads.net/v1.0/[USER_ID]/threads" \
        -d "media_type=IMAGE" \
        -d "image_url=https://blogs.lamplitlabs.com/assets/img/headers/automation/thread-api-part2.webp" \
        -d "text=Posted with the Threads API" \
        -d "access_token=[ACCESS_TOKEN]"
      
    • Response
      1
      2
      3
      
      {
        "id": "17889455560051444"
      }
      
  • Important: for image and video posts the container is created immediately, but Threads downloads and processes the media in the background. Meta recommends waiting around 30 seconds before publishing, or polling the container status (next step). Publishing too early returns an error like Media ID is not available.

Step 2 (optional): Check the Container Status

Use this before publishing media posts, or when a publish call fails.

  • Url: https://graph.threads.net/v1.0/[CONTAINER_ID]?fields=status,error_message&access_token=[ACCESS_TOKEN]
    • Method: GET
    • Response
      1
      2
      3
      4
      
      {
        "status": "FINISHED",
        "id": "17889455560051444"
      }
      
    • status is one of:
      • IN_PROGRESS - still downloading/processing the media, wait and poll again
      • FINISHED - ready to publish
      • ERROR - something went wrong; error_message tells you what (bad URL, unsupported format, media too large, …)
      • EXPIRED - the container was never published within 24 hours and has been discarded
      • PUBLISHED - already published

Step 3: Publish the Post

  • Url: https://graph.threads.net/v1.0/[USER_ID]/threads_publish?creation_id=[CONTAINER_ID_FROM_STEP_1]&access_token=[ACCESS_TOKEN]
    • Method: POST
    • Example Request

      1
      2
      3
      
      curl -X POST "https://graph.threads.net/v1.0/[USER_ID]/threads_publish" \
        -d "creation_id=17889455560051444" \
        -d "access_token=[ACCESS_TOKEN]"
      
    • Response Example of a successful response
      1
      2
      3
      
      {
        "id": "17920251442154329"
      }
      

      This id is the Threads media id of the published post - a different value from the container id.

  • Result on your profile

    The post appears on your Threads profile immediately: the text as the body, the image or video (if any) below it, and the link_attachment rendered as a preview card. You can read it back with GET /{THREADS_MEDIA_ID}?fields=id,text,media_type,permalink,timestamp, and the permalink field gives you the public URL to share.

    Terminal showing the three Threads API calls in order: a GET on the container id returns status FINISHED, a POST to threads_publish with that creation_id returns the published post id 17920251442154329, and a GET on the post id with fields id,text,media_type,permalink,timestamp returns the post text, media_type TEXT_POST and its public threads.net permalink Verify your result: the container status, the publish response and the read-back of the published post. Open the permalink in a browser to see the post on your profile.

A carousel is a single post with 2 to 20 images/videos. It takes one extra round of containers:

  1. Create one container per item with is_carousel_item=true:

    1
    2
    3
    4
    5
    
    curl -X POST "https://graph.threads.net/v1.0/[USER_ID]/threads" \
      -d "media_type=IMAGE" \
      -d "image_url=https://example.com/one.jpg" \
      -d "is_carousel_item=true" \
      -d "access_token=[ACCESS_TOKEN]"
    

    Repeat for every item and collect the returned ids.

  2. Create the carousel container, passing the item ids as a comma-separated children list:

    1
    2
    3
    4
    5
    
    curl -X POST "https://graph.threads.net/v1.0/[USER_ID]/threads" \
      -d "media_type=CAROUSEL" \
      -d "children=[ITEM_ID_1],[ITEM_ID_2]" \
      -d "text=Swipe through" \
      -d "access_token=[ACCESS_TOKEN]"
    
  3. Publish the carousel container with threads_publish exactly as in Step 3.

Limits and Gotchas

  • Rate limit: 250 published posts per 24 hours per account (check GET /{USER_ID}/threads_publishing_limit?fields=quota_usage,config to see where you stand).
  • Containers expire after 24 hours if not published, so don’t create them days in advance - schedule the creation call instead of pre-creating containers.
  • No native scheduling: unlike the Facebook Page API there is no scheduled_publish_time. To schedule, run the create + publish calls from a cron job, Azure Function or similar at the desired time.
  • Text limit is 500 characters; longer text fails at the create step.
  • Media must be public https:// URLs; signed URLs work as long as they are valid while Threads downloads them.
  • Token expiry: long-lived tokens last 60 days. Refresh them with GET https://graph.threads.net/refresh_access_token?grant_type=th_refresh_token&access_token=[ACCESS_TOKEN] before they expire, otherwise every call above returns an OAuthException.

Wrapping Up

And there you have it – a crash course in creating Threads posts like a pro using REST APIs! Create a container, (wait for media to finish), publish, and read back the permalink. With these tools in your arsenal, you’re ready to take your Threads account to new heights.

Other

This post is licensed under CC BY 4.0 by the author.