Home
Blog
How much does the X (Twitter) API actually cost in 2026?
Threads API tutorial: publishing posts and carousels programmatically
How to post to Instagram via API: the complete 2026 guide
Case study: how Aveiro ships social publishing on Chirio
A 201 is not a promise: designing for mixed results
One API for social publishing
Sponsored
Aveiro
aveiro.app
TrademarkTrademark
Ctrl k
Search...
Sign up
Sponsored
Aveiro
aveiro.app
Sponsored
Aveiro
aveiro.app
TrademarkTrademark
DoplerChirioDocs
© Dopler. All rights reserved.
Built with Aveiro

Threads API tutorial: publishing posts and carousels programmatically

A practical guide to the Threads API in 2026 — the separate app setup, tester invites, the container publish flow, carousels, reply-based first comments, and the rate limits nobody mentions until they hit them.
Updated 12d ago
How much does the X (Twitter) API actually cost in 2026?
How to post to Instagram via API: the complete 2026 guide
The Threads API is younger than its Instagram sibling and the documentation ecosystem around it is still thin — which means most of what goes wrong during an integration goes wrong silently. This tutorial covers the full path: app setup, OAuth, text posts, media, carousels, and the reply-based trick for first comments.

App setup: Threads is its own thing

Threads lives inside the Meta developer ecosystem but behaves like a separate platform:
  • At developers.facebook.com, create an app with the Threads use case (or add the use case to an existing app).
  • The Threads App ID is not your main Meta App ID. The use-case settings expose a separate Threads App ID and Secret — using the main app's credentials is the most common first failure, and it fails with unhelpful OAuth errors.
  • Register your redirect URI in the Threads use-case settings, not the general app settings. HTTPS is required in live mode.
In development mode, only invited Threads Testers can connect. Invite them in the app dashboard; each tester must then accept the invite inside the Threads app under Settings → Website permissions. Missing that acceptance step produces an "Insecure Login Blocked" style error that looks like a configuration bug but isn't. Going public requires App Review.

Publishing: containers, again

Threads uses the same container model as Instagram: create a media container, then publish it.
# 1. Create a container (text-only post)
curl -X POST "https://graph.threads.net/v1.0/{threads-user-id}/threads" \
  -d "media_type=TEXT" \
  -d "text=Hello from the API" \
  -d "access_token={token}"
# → { "id": "{container-id}" }

# 2. Publish it
curl -X POST "https://graph.threads.net/v1.0/{threads-user-id}/threads_publish" \
  -d "creation_id={container-id}" \
  -d "access_token={token}"
The constraints to design around:
  • Text is capped at 500 characters. If you're cross-posting from platforms with bigger limits, truncation strategy is your problem, and it needs to happen before the API call.
  • Media must live at publicly accessible URLs — Meta fetches it at publish time.
  • Each profile is capped at 250 API-published posts per 24 hours.
  • Tokens are long-lived (60 days) and need a refresh schedule, same as Instagram.

Carousels

Threads supports up to 20 media items, with 2+ items publishing as a carousel. The flow mirrors Instagram's: create one child container per item, wait for each to finish processing, then create a parent carousel container — the post text rides on the parent, not the children — and publish the parent.
Everything that makes Instagram carousels operationally tricky applies here too: child processing is asynchronous, a failed child should fail the whole carousel (no partial publishes), and multi-minute publish times mean client retries are a real double-posting risk without idempotency.

First comments via reply chains

Threads has no separate comment API — a "comment" is just a thread posted with reply_to_id pointing at the parent post. That makes the link-in-first-comment pattern possible: publish the main post, take its ID, then create and publish a second container carrying the URL with reply_to_id set.
Design it to fail softly. If the main post publishes and the reply fails, you have live content — report the comment failure separately instead of failing the whole publish.

The one-call alternative

Chirio handles the whole flow — hosted OAuth with the right app plumbing, container orchestration, carousels, reply-based first comments, token refresh, idempotent retries — behind a single call:
const { post } = await fetch(`${CHIRIO}/api/v1/posts`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    content: "Launch day! Details below 👇",
    mediaItems: [
      { type: "image", url: "https://cdn.example.com/1.jpg" },
      { type: "image", url: "https://cdn.example.com/2.jpg" },
    ],
    platforms: [{ platform: "threads", accountId, firstComment: "https://example.com/launch" }],
    idempotencyKey: "launch-thread-1",
  }),
}).then((r) => r.json());
// → post.targets[0].status: "published", commentStatus settles separately
Two or more mediaItems publish as a carousel in order; the first comment posts as a reply after the main post lands and never fails a published post. The 500-character limit is validated up front with a clear error instead of a platform rejection mid-flow.

Worth knowing before you commit

The Threads API is the least stable target of the major platforms — it's newest, and Meta is still filling in gaps (carousel limits, insights, reply management have all evolved recently). Whatever you build, isolate the Threads-specific logic behind your own interface so the next platform change is a one-file fix. Or let a publishing API absorb that churn for you — that's the trade in one sentence.