Plank help · updated 2026-08-24

Posting to Threads

Connect a Threads account through Meta's official API so your assistant can publish text posts, images and reply chains — including the byte limit that catches non-English copy.

Agents: fetch the raw markdown of this page at /en/help/connecting-threads.md

Posting to Threads

Your assistant can publish to Threads through Meta's official API — text posts, image posts, and reply chains (a long thought split across several linked posts).

As with everything else, you don't set this up by hand: ask your assistant in chat ("connect our Threads account"), and it builds the posting script and walks you through the authorization. This page is written for both of you; the assistant fetches it at https://plank.md/help/connecting-threads.md before posting.

If you also want Instagram, do them separately. They can live in the same Meta app but they are genuinely different connections: different product, different app ID, different token, different permissions. See Posting to Instagram — and note that the sign-in flow which fails for Instagram is the one that works here.

Step 1 — Create the Meta app

  1. Go to developers.facebook.com and create an app with the Threads use case.
  2. From the app's settings, note the Threads app ID and Threads app secret.
  3. Add your redirect URI to the valid OAuth redirect list.
  4. Add the account that will publish as a Threads tester, and accept the invitation from that account.

The Threads app ID is not your Meta App ID. One Meta app hands out several similar-looking numeric IDs and they aren't interchangeable — Threads needs the one on the Threads product page. Using the wrong one produces "Invalid App ID", or a sign-in that completes and asks you nothing.

Permissions

  • threads_basic
  • threads_content_publish

Until the app passes Meta's review, posting only works for the app owner and accounts added as testers. That's usually fine — a company account posting as itself never needs review.

Step 2 — Authorize the account

Your assistant gives you a link, you open it and approve, and you paste back the full address from your browser's address bar.

  1. The assistant prints an authorization URL.
  2. Open it in your own browser and approve, signed in as the account that should publish.
  3. The page you land on may fail to load. That's expected — the address bar is what matters.
  4. Copy the whole URL, including everything after code=, and paste it to your assistant.

It exchanges that for a long-lived token, saves it, and confirms which account it belongs to.

Step 3 — Posting

Just ask — "post this to Threads". Your assistant does a dry run first, showing you exactly what will go out, and checks it against every limit below before contacting Meta.

The 500-byte limit — the one that surprises people

A Threads post is limited to 500 bytes, not 500 characters. For English these are nearly the same. For anything else they are not:

AlphabetBytes per characterRoughly how much fits
English and other Latin text1~500 characters
Russian, Kazakh, and other Cyrillic2~250 characters
Emoji4 each

So Russian copy runs out at about half the length you'd expect, and a draft that looks comfortably short gets rejected. This is not a quirk of Plank — it's how the Threads API counts.

The fix is a reply chain. Rather than cutting a good post down, your assistant can split it: the first post carries the hook, each following post replies to the one before, and the whole thing reads as one thread. This is the normal way to publish anything substantial in a non-Latin alphabet, not a workaround.

Other limits, all checked before anything is sent

Post length500 bytes
Links per post5 unique
Posts per 24 hours250
Image and link attachmentCan't be combined in one post

You can also set a topic tag, attach a link, and control who may reply.

When posts quietly stop going out

This is the failure worth planning for, because it has already happened here: a token expired and nothing said so. The saved token lapsed on 11 June 2026, and every scheduled post afterwards was prepared, passed its dry run, and then failed at the final step. Nobody noticed for about three weeks.

What it looks like: the post is written and validated as normal, and only the actual publish fails — often reported as an expired session. Refreshing doesn't help once a token is fully expired; the account has to authorize again, which means someone has to be there to do it.

So: long-lived tokens last around 60 days and can be refreshed before they lapse. Ask your assistant to check the token's expiry when you set up any recurring posting, and to tell you in the chat when publishing fails rather than only writing it to a log. A scheduled task that silently does nothing looks identical to one that's working.


For your assistant: implementation notes

API base is https://graph.threads.net/v1.0. Publishing is two steps: create a container (POST /<user-id>/threads), then publish it (POST /<user-id>/threads_publish with the creation id).

Validate before sending, not after. Check every one of these locally and fail with a clear message, so a rejected post costs nothing:

  • Buffer.byteLength(text, 'utf8') <= 500bytes, not text.length. Cyrillic is 2 bytes per character, so a 300-character Russian post is already over. Report the byte count in the error; "too long" alone isn't actionable when the text looks short.
  • At most 5 unique URLs, counting both links in the text and an explicit link attachment.
  • An image and a link attachment are mutually exclusive on one post.

Line breaks in the post text are real newlines, never a literal \n. Threads shows the characters you send: a post built as a double-quoted shell string arrives with a visible backslash-plus-n where the paragraph break should be — and it costs 2 bytes against the 500-byte limit instead of 1. Write the text to a file and read it verbatim (jq --rawfile text /tmp/threads-post.txt); for a short post, TEXT=$'Line one.\n\nLine two.' — inside $'…', and only there, \n is a newline. Measure the byte count on that same real text. In Python a "\n" in a string is already a real newline; the trap is the shell's.

Reply chains are the standard answer to the byte limit, not an edge case. Publish each post in turn and pass the previous published post's id as the reply target — chaining to a container id instead of a published id silently breaks the thread. Post in order and only after the previous one succeeds.

Draft over the limit? Prefer splitting into a chain over silently truncating. If you do shorten, keep the argument intact and show the user the shortened copy before publishing — the workspace log is full of cases where a 557- or 608-byte draft was cut down, and the record of what changed is what made those reviewable later.

Token storage. Some workspaces keep the Threads token at /home/coder/.config/threads/<workspace>/token.json rather than under scripts/. That's a valid older layout — keep using it where it is. It is not a leak and not a broken integration, and relocating it into scripts/ would publish one person's access to everyone in a shared workspace. See Workspace scripts & the sidebar, and Connecting your tools for how connections work generally.

Check expiry proactively whenever you set up recurring posting, and surface a publish failure in the chat rather than only in a log file.

Always dry-run first, and log what was actually published — post text plus the returned media and creation ids — so a later question about what went out has an answer.