Publishing
Publish a chain of connected posts on X, Threads and Bluesky with threadItems, each with its own text and media.
A thread is a chain of posts where each one replies to the one before it. X, Threads and Bluesky support them through the same field: threadItems in that entry's platformSpecificData. Each item has its own content and optional mediaItems.
#Publish a thread on X
curl -X POST https://api.pozy.ai/v1/posts \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "What we learned moving 2 million jobs to a new queue",
"scheduledFor": "2026-11-04T15:00:00Z",
"platforms": [{
"platform": "twitter",
"accountId": "6710c3e2a9f14b0d8e5c7a23",
"platformSpecificData": {
"threadItems": [
{
"content": "We moved 2 million background jobs to a new queue last month. Here is what broke, and what we would do again. 🧵",
"mediaItems": [{ "type": "image", "url": "https://pozy.ai/media/temp/1730289600_t4h1_queue-graph.png" }]
},
{ "content": "1. Retries were the real load. 40% of our traffic was jobs retrying the same failure." },
{ "content": "2. Idempotency keys on every job made the cutover boring, which is the goal." },
{ "content": "3. Ship the dashboard before the migration, not after. Full write-up: https://example.com/blog/queues" }
]
}
}]
}'const res = await fetch("https://api.pozy.ai/v1/posts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
content: "What we learned moving 2 million jobs to a new queue",
scheduledFor: "2026-11-04T15:00:00Z",
platforms: [
{
platform: "twitter",
accountId: "6710c3e2a9f14b0d8e5c7a23",
platformSpecificData: {
threadItems: [
{
content: "We moved 2 million background jobs to a new queue last month. Here is what broke, and what we would do again. 🧵",
mediaItems: [{ type: "image", url: "https://pozy.ai/media/temp/1730289600_t4h1_queue-graph.png" }],
},
{ content: "1. Retries were the real load. 40% of our traffic was jobs retrying the same failure." },
{ content: "2. Idempotency keys on every job made the cutover boring, which is the goal." },
{ content: "3. Ship the dashboard before the migration, not after. Full write-up: https://example.com/blog/queues" },
],
},
},
],
}),
});
if (!res.ok) throw new Error((await res.json()).error);requests.post(
"https://api.pozy.ai/v1/posts",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
json={
"content": "What we learned moving 2 million jobs to a new queue",
"scheduledFor": "2026-11-04T15:00:00Z",
"platforms": [{
"platform": "twitter",
"accountId": "6710c3e2a9f14b0d8e5c7a23",
"platformSpecificData": {
"threadItems": [
{
"content": "We moved 2 million background jobs to a new queue last month. Here is what broke, and what we would do again. 🧵",
"mediaItems": [{"type": "image", "url": "https://pozy.ai/media/temp/1730289600_t4h1_queue-graph.png"}],
},
{"content": "1. Retries were the real load. 40% of our traffic was jobs retrying the same failure."},
{"content": "2. Idempotency keys on every job made the cutover boring, which is the goal."},
{"content": "3. Ship the dashboard before the migration, not after. Full write-up: https://example.com/blog/queues"},
]
},
}],
},
timeout=30,
).raise_for_status()When an entry has threadItems, its first post is threadItems[0]. The top-level content is kept for the dashboard, search and your own lists, but it is not posted on that platform. Put the opening post in the first item.
The entry's platformPostUrl points at the first post of the thread.
#Rules per platform
X (twitter) |
Threads | Bluesky | |
|---|---|---|---|
| Field | threadItems |
threadItems |
threadItems |
| Text per item | 280 characters on standard accounts, more with a paid X subscription | 500 characters | 300 characters |
| Media per item | Up to 4 images, or 1 video, or 1 GIF | Images, a video or a carousel | Up to 4 images or 1 video |
| Extras | A thread cannot carry a poll. Add replyToTweetId to post the whole thread as a reply. |
firstComment replies to the first post of the thread. topic_tag applies to the thread. |
langs applies to every item. |
| Edit after publishing | Not for threads | No edit support | No edit support |
X counts characters with weights: every link counts as 23 characters and most emoji count as 2. Check a long thread with POST /v1/tools/validate/post-length before scheduling (Validation).
#One thread, three platforms
Each entry carries its own threadItems, so you can split the same story differently for each platform's limit. A shorter LinkedIn version can sit next to them as a single post:
{
"content": "Our queue migration, in short",
"platforms": [
{
"platform": "twitter",
"accountId": "6710c3e2a9f14b0d8e5c7a23",
"platformSpecificData": {
"threadItems": [
{ "content": "We moved 2 million jobs to a new queue. What broke, in three posts." },
{ "content": "Retries were 40% of the load." },
{ "content": "Idempotency keys made the cutover boring." }
]
}
},
{
"platform": "threads",
"accountId": "6710c3e2a9f14b0d8e5c7a2a",
"platformSpecificData": {
"threadItems": [
{ "content": "We moved 2 million jobs to a new queue. Retries turned out to be 40% of the load." },
{ "content": "Idempotency keys on every job made the cutover boring, which is exactly what you want." }
]
}
},
{
"platform": "bluesky",
"accountId": "6710c3e2a9f14b0d8e5c7a2b",
"platformSpecificData": {
"langs": ["en"],
"threadItems": [
{ "content": "We moved 2 million jobs to a new queue. What broke:" },
{ "content": "Retries were 40% of the load. Idempotency keys made the cutover boring." }
]
}
},
{
"platform": "linkedin",
"accountId": "6710c3e2a9f14b0d8e5c7a21",
"customContent": "We moved 2 million background jobs to a new queue. Two lessons: retries were 40% of the load, and idempotency keys made the cutover boring. Full write-up on the blog."
}
]
}#Reply to an existing post on X
To answer a post instead of starting a new thread, set replyToTweetId to the id of the post you are answering. With threadItems as well, the whole thread hangs under that post. X only accepts replies to your own posts or to posts that mention you, and replyToTweetId cannot be combined with replySettings. To quote a post instead, use quoteTweetId. See X.
#Taking a thread down
POST /v1/posts/{postId}/unpublish with "platform": "twitter", "threads" or "bluesky" deletes every post of that thread on that platform. See Edit and unpublish.
#Endpoints
| Endpoint | What it does |
|---|---|
POST /v1/posts |
Create post |
POST /v1/tools/validate/post-length |
Validate character count |
POST /v1/tools/validate/post |
Validate post content |
POST /v1/posts/{postId}/unpublish |
Unpublish post |