Need help for integrate to Meta Graph API for Threads Automation

has anyone ever set up the HTTP request? This is for creating drafts, by the way. Following the tutorial setup on YouTube gives a 500 error.

Asking Gemini also hit a dead end, it just told me to change the URL to “https://graph.threads.net/v1.0/me/threads” and it still gave the same error.

Body Parameter:

media_type | TEXT

text | {{$json.post_text_clean}}

then, i’ve change parameter to:

Setting Value
Authentication None
Method POST
URL https://graph.threads.net/v1.0/me/threads
Send Query Parameters ON
Parameter 1 - Name access_token
Parameter 1 - Value {{ $json.GRAPH_API }} (from Set Node)
Body Content Type JSON
Specify Body Using JSON
JSON { "media_type": "TEXT", "text": "{{ $json.post_text_clean }}" }

then back to:

  • Send Body: OFF)
  • Send Query Parameters: ON (Using Fields Below).
    • Parameter 1 → Name: access_token | Value: {{ $json.GRAPH_API }}
    • Parameter 2 → Name: media_type | Value: TEXT
    • Parameter 3 → Name: text | Value: {{ $json.post_text_clean }}

All just back to error 500: Bad Response

UPDATED:

I changed the parameter as commentor on my reddit post suggested. The “create container” HTTP request was successful, but the “publish” request still returns a 500 error, stating “URL not valid.” When I removed “_publish” from the URL, the workflow ran successfully, but the post did not go live.

1. Fix the Endpoint URL

In your screenshot, your URL is set to https://graph.threads.net/threads. Meta’s Graph API requires either the me alias or your user ID in the URL path.

  • Change it to:
    https://graph.threads.net/v1.0/me/threads

2. Use the Required 2-Step Process

You can’t publish a thread in a single HTTP request. You must first create a container, and then publish it:

Step 1: Create the Post Container

  • Method: POST

  • URL: https://graph.threads.net/v1.0/me/threads

  • Send Query Parameters: ON

    • access_token: {{ $json.GRAPH_API }}

    • media_type: TEXT

    • text: {{ $json.post_text_clean }}

  • Response: This will output a creation ID (e.g., {"id": "123456789"}).

Step 2: Publish the Container

Add a second HTTP Request node right after Step 1:

  • Method: POST

  • URL: https://graph.threads.net/v1.0/me/threads_publish

  • Send Query Parameters: ON

    • access_token: {{ $json.GRAPH_API }}

    • creation_id: {{ $json.id }} (reference the ID returned from Step 1)

Workflow

Hey @bytegir, while you wait for a response, here are some things that might help:

Suggested resources

Automatically matched to your question.

Docs:

Forum:

@Mookie_Lian, @CameronDWills, @kjooleng - you’ve helped with similar issues before, can you take a look?

Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.

Hi @bytegir
The publishing endpoint is scoped to the Threads user ID, me is only supported on the profile read, so both URL variants return the same 500. Fetch the ID first:

https://graph.threads.net/v1.0/me?fields=id,username&access_token=<your_token>

Then use that ID in the POST, Send Body OFF, everything as query parameters (media_type=TEXT, text, access_token):

https://graph.threads.net/v1.0/<THREADS_USER_ID>/threads

That call returns a container ID, which is your draft. Publishing it is a second POST to https://graph.threads.net/v1.0/<THREADS_USER_ID>/threads_publish with creation_id set to that ID. The token also has to be a Threads token with threads_basic and threads_content_publish, a Facebook Graph token won’t work against this host.

Hi, Thank you for your response. I have already attempted this parameter setting, but it resulted in a 500 error, similar to my other attempts.

In the publish node {{ $json.GRAPH_API }} resolves against the create node’s output, and that output is only {"id": "..."}, so the token goes out empty and Meta rejects the call. Pull the token from the Set node by name instead:

{{ $('Set').first().json.GRAPH_API }}

Use the exact name of your Set node in place of Set.
On graph.threads.net a rejected call comes back as a 500 with an OAuthException body rather than a 4xx, so turn on “Never Error” under Options in that node and run it once, the response body then lands in the output and tells you which parameter it actually rejected.
Expression reference | Build | n8n Docs

Two things are going on here, and the second one explains why the version without _publish looked like it worked.

The container is not a post. POST /threads only creates a media container: a temporary object on Meta’s side. Nothing is published, and nothing shows up anywhere, until you call the publish endpoint with that container’s id. So a run that ends green and leaves your feed empty is exactly what that call does, not a silent failure.

The publish call wants the numeric user id, not me. The docs use:

POST https://graph.threads.net/v1.0/<THREADS_USER_ID>/threads_publish
creation_id=<the id returned by step 1>

me works in some places, but the publish path is documented with the id, and a path Meta does not recognise is what gives you “URL not valid” instead of a proper API error.

Give it time. From Meta’s own docs: “It is recommended to wait on average 30 seconds before publishing a Threads media container to give our server enough time to fully process the upload.” Publishing immediately can fail with errors that point nowhere near the real cause. A Wait node between the two HTTP nodes is enough.

One debugging habit that saves hours with this two-step dance: log the creation_id you are actually sending. If the expression resolves to nothing, the URL ends with creation_id= and Meta answers with a generic 500 that reads like a server problem when it is just an empty parameter. {{ $json.id }} only works if the previous node put the id at the top level of its output.

It is the same shape as publishing to Instagram: create container, wait, publish. Every error I have chased over there came down to one of those three.

@bytegir

See if these help

Your first screenshot shows this already works once you use {{ $json.THREAD_AppID }} in the URL path. The correct config is:

Method: POST
URL: https://graph.threads.net/v1.0/{{ $json.THREAD_AppID }}/threads
Authentication: None
Send Headers: ON
  Authorization: Bearer {{ $json.THREAD_AccessToken }}
Send Body: ON
  Body Content Type: JSON
  JSON Body:
  {
    "media_type": "TEXT",
    "text": "{{ $json.post_text_clean }}"
  }

Do NOT use query parameters for text — the Threads API rejects text content passed as query params on the container creation step; it must be in the JSON body.

The publish endpoint requires the user ID (numeric), not me, and the creation_id must be the raw ID string returned from above

Method: POST
URL: https://graph.threads.net/v1.0/{{ $json.THREAD_AppID }}/threads_publish
Authentication: None
Send Headers: ON
  Authorization: Bearer {{ $json.THREAD_AccessToken }}
Send Body: ON
  Body Content Type: JSON
  JSON Body:
  {
    "creation_id": "{{ $json.id }}"
  }

Critical: $json.id here must come from the output of Step 1’s HTTP request, not your Set node. In n8n, the previous node’s output replaces $json, so if Step 1 returns {"id": "1234567890"}, then {{ $json.id }} in Step 2 correctly references it.

I ran into something similar a while back. If the create container request is working but publish returns a 500 or “URL not valid,” I’d double-check that you’re calling the correct publish endpoint and that you’re passing the exact creation_id returned from the first request.

I’d also verify that your access token has the required Threads permissions and hasn’t expired. A lot of Graph API 500 errors actually turn out to be permission or endpoint issues rather than problems with the HTTP request itself.

One more thing I’d try is testing the same publish request in the Graph API Explorer or Postman. If it fails there too, it’s likely an API or permission issue. If it works there but not in n8n, then the problem is probably with how the request is being built in the workflow.

Could you share the full error response from the publish node (including the response body)? That would make it much easier to narrow down the cause.

@bytegir kjooleng notes the Threads API rejects text as a query param and needs it in the JSON body. Since query params already failed for you, that’s the one combination not yet tested. Also add a Wait node (~30s) between create and publish, Meta’s own docs recommend this delay, and skipping it causes exactly this “ran fine, nothing published” symptom.

Hope this helps!