ChirpieDocs

Post to several accounts at once

Send one request to many connected accounts, with per-account text and media, and read the whole fan-out back by its group id.

Updated September 2026

How do you post to several accounts in one request?

Name account_ids instead of account_id on POST /api/v1/posts or POST /api/v1/threads. Chirpie publishes to every account you name, in the order you named them, and answers with a group_id and one result per account. Give each account its own text or media with account_configurations, keyed by account id.

curl -X POST https://chirpie.ai/api/v1/posts \
  -H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account_ids": [
      "550e8400-e29b-41d4-a716-446655440000",
      "66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b"
    ],
    "text": "Shipping new features today!",
    "media": [{ "url": "https://example.com/screenshot.png", "alt": "The new dashboard" }],
    "account_configurations": {
      "66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b": {
        "text": "Shipping new features today. Full write-up on the blog."
      }
    }
  }'
import { ChirpieClient } from "@chirpie/sdk";

const chirpie = new ChirpieClient({ apiKey: process.env.CHIRPIE_API_KEY! });

const { group_id, results } = await chirpie.createPost({
  account_ids: [
    "550e8400-e29b-41d4-a716-446655440000",
    "66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b",
  ],
  text: "Shipping new features today!",
  media: [{ url: "https://example.com/screenshot.png", alt: "The new dashboard" }],
  account_configurations: {
    "66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b": {
      text: "Shipping new features today. Full write-up on the blog.",
    },
  },
});

for (const result of results) {
  console.log(result.account_id, result.success, result.platform_post_url);
}
# Repeat --account once per account
chirpie post "Shipping new features today!" \
  -a 550e8400-e29b-41d4-a716-446655440000 \
  -a 66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b

# Give one account its own text. --config takes JSON, or a path to a JSON file
chirpie post "Shipping new features today!" \
  -a 550e8400-e29b-41d4-a716-446655440000 \
  -a 66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b \
  --config '{"66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b":{"text":"Shipping new features today. Full write-up on the blog."}}'

# Or keep the overrides in a file
chirpie post "Shipping new features today!" -a ACCOUNT_A -a ACCOUNT_B --config ./overrides.json
{
  "tool": "chirpie_post",
  "arguments": {
    "account_ids": [
      "550e8400-e29b-41d4-a716-446655440000",
      "66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b"
    ],
    "text": "Shipping new features today!",
    "account_configurations": {
      "66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b": {
        "text": "Shipping new features today. Full write-up on the blog."
      }
    }
  }
}

What does account_ids accept?

One to 25 account ids, each an account you have connected. Repeating the same id twice is refused with 400 bad_request.

Naming account_ids is the only thing that selects this shape, and it selects it even for a single account, so a client can always send account_ids and always read the same response back. account_id keeps working exactly as before, and keeps returning the single post object documented on Posts.

text at the top level is still required. It is what an account without an override publishes.

How do per-account overrides work?

account_configurations is optional, and is keyed by account id. Every key must also appear in account_ids, or the request is refused with 400 bad_request. Each value takes text, first_comment, configuration, and the same three media spellings a normal post takes (media, media_ids, media_urls, one of them at a time). first_comment is the one field an override may set to the empty string, which publishes that account with no first comment.

A field you leave out inherits the request's own value.

Media replaces, it never merges. An account whose configuration names any media spelling replaces the shared media outright for that account. To publish one account with no media at all while the others carry it, give that account "media": [].

On POST /api/v1/threads the override field is posts instead of text, and it replaces the whole array for that account, still 2 to 25 parts:

{
  "account_ids": ["ACCOUNT_A", "ACCOUNT_B"],
  "posts": [{ "text": "one" }, { "text": "two" }],
  "account_configurations": {
    "ACCOUNT_B": {
      "posts": [{ "text": "a" }, { "text": "b" }, { "text": "c" }]
    }
  }
}

ACCOUNT_A publishes the two shared parts, and ACCOUNT_B publishes its own three.

Can a fan-out carry a first comment?

Yes. A top-level first_comment applies to every account in the group, and an entry in account_configurations decides what that account does with it. Three cases:

The account's overrideWhat that account publishes
No first_commentThe request's own first comment
"first_comment": "Some text"That text instead, for this account only
"first_comment": ""No first comment at all

The empty string is how one request sends a first comment to the accounts that take one and still publishes the post through an account that does not:

{
  "account_ids": ["X_ACCOUNT", "BLUESKY_ACCOUNT"],
  "text": "We rebuilt scheduling this week.",
  "first_comment": "Full write-up: https://example.com/blog/scheduling",
  "account_configurations": {
    "BLUESKY_ACCOUNT": { "first_comment": "" }
  }
}

A first comment works on X, Threads, Instagram and Facebook. Every account gets the shared one unless its override says otherwise, so an account whose platform has no first comment, and no "" override, refuses the whole request with 400 first_comment_unsupported, exactly like every other pre-flight refusal. Each first comment that publishes counts as one post against your monthly quota.

Can a fan-out carry publishing options?

Yes. A top-level configuration is one block per platform, not one per account, so a request addressing three Instagram accounts says "as a reel" once. An entry in account_configurations gives one account its own, and it replaces the whole block for that account rather than merging into it:

{
  "account_ids": ["IG_ACCOUNT_A", "IG_ACCOUNT_B"],
  "text": "Behind the scenes of this week.",
  "media_urls": ["https://example.com/clip.mp4"],
  "configuration": { "instagram": { "placement": "reel" } },
  "account_configurations": {
    "IG_ACCOUNT_B": { "configuration": { "instagram": { "placement": "story" } } }
  }
}

A block keyed on a platform no account in the request uses is refused with 400 configuration_unsupported, so a fan-out to Instagram and Bluesky may carry an instagram block, and one to Bluesky alone may not. Accounts of a platform the block does not name publish as they always did.

Connecting a new Instagram account or Facebook Page is coming soon. Accounts already connected publish and schedule with these options as described here.

What comes back?

One group_id and one entry in results per account, in the order the accounts were named.

{
  "data": {
    "group_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "results": [
      {
        "account_id": "550e8400-e29b-41d4-a716-446655440000",
        "platform": "x",
        "success": true,
        "post_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "platform_post_url": "https://x.com/chirpie_ai/status/1234567890",
        "status": "published",
        "post": { "id": "a1b2c3d4-...", "text": "Shipping new features today!" },
        "error": null
      },
      {
        "account_id": "66f1a2b3-c4d5-4e6f-8a9b-0c1d2e3f4a5b",
        "platform": "bluesky",
        "success": false,
        "post_id": null,
        "platform_post_url": null,
        "status": null,
        "post": null,
        "error": { "code": "upstream_error", "message": "Bluesky API error: ..." }
      }
    ]
  }
}

post is the usual post object, exactly as a single-account create returns it. On POST /api/v1/threads each entry carries thread_id and thread instead of post_id and post, and platform_post_url is the first part's permalink.

201 or 207

StatusWhen
201 CreatedEvery account succeeded
207 Multi-StatusAt least one account did not

Both answer with the same body shape, so read results[].success rather than branching on the status code. A 207 is not a failure of the request: the accounts that worked are published.

What is refused whole, and what fails per account?

Before anything is published, Chirpie resolves every account and runs the body, with that account's overrides applied, through the same rules a single-account create runs: the character limit, the media caps, the per-platform media rules, the alt-text limits, the platforms that require media, whether the platform takes a first comment, the X link-post rule, and, when the group is scheduled, whether each account already has something queued too close to that time.

Any of those refuses the whole request, with the usual status and code, and a message prefixed Account <id>: so you know which one. Nothing is published and no quota is taken.

Only the platform call itself fails per account. Those land in results[].error, and the accounts that worked stay published.

Kind of problemEffect
Text over a platform's limit, unsupported media, missing required media, a first_comment on a platform that has none (give that account "first_comment": ""), a publishing option a placement does not carry, an X link post on FreeThe whole request is refused. Nothing publishes
A schedule_at too close to something one account already has queuedThe whole request is refused. Nothing is queued
A duplicate id in account_ids, or a configuration key that is not in account_ids400 bad_request. Nothing publishes
The group does not fit your monthly quota429 usage_limit_exceeded. Nothing publishes
The platform refuses or is unavailable for one accountThat entry carries an error, the rest publish, and the response is 207

How is quota counted?

One reservation covers the whole group: one unit per account for a post, and one unit per part per account for a thread. If the group does not fit your plan the request is refused with 429 usage_limit_exceeded and nothing is published, so a group never publishes halfway through your allowance.

Each account that fails to publish gives back exactly its own share, once. Two accounts out of five failing returns two units, and the three that published stay counted. See Rate limits for the monthly quotas.

Can I schedule a whole group?

Yes. schedule_at applies to the group, so every account is queued for the same time and every scheduled post shares the group id. It follows the same rules as any other scheduled post: an absolute ISO 8601 timestamp carrying a timezone, in the future. See Scheduling.

curl -X POST https://chirpie.ai/api/v1/posts \
  -H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account_ids": ["ACCOUNT_A", "ACCOUNT_B"],
    "text": "Launching at noon UTC",
    "schedule_at": "2027-04-01T12:00:00Z"
  }'

How do I read a group back?

Pass the group id to List Posts. GET /api/v1/posts?group_id=... returns every post of that fan-out.

curl "https://chirpie.ai/api/v1/posts?group_id=7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Authorization: Bearer chirpie_sk_YOUR_KEY"
const posts = await chirpie.listPosts({
  group_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
});
chirpie posts --group 7c9e6679-7425-40de-944b-e07fc1f90ae7
{
  "tool": "chirpie_list_posts",
  "arguments": { "group_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7" }
}

group_id must be a UUID. Anything else is refused with 400 bad_request.

Every post payload carries group_id as well: the fan-out it belongs to, or null for a post created with a single account_id. Thread payloads carry it too.

Frequently asked questions

Can I use account_ids with a single account?

Yes, and it is the simplest way to write a client that never branches. One id in account_ids gives you the same group_id and results shape as twenty-five, with one entry in results.

Can I mix platforms in one group?

Yes. A group is a list of accounts, and each account brings its own platform. Chirpie validates each one against its own platform's rules, so a text that is fine on LinkedIn but too long for Bluesky is caught before anything publishes, and you can shorten just that account with an override.

What happens to the posts that succeeded when one account fails?

They stay published. The response is 207 Multi-Status, the failed account's entry carries an error, and the quota for that account is given back. Send the failed account again on its own when you have fixed the cause.

Does a group publish in parallel?

Results come back in the order you named the accounts, and a group is a single request either way. Treat the order of results as the order of account_ids, not as a publishing timeline.

How do I cancel a scheduled group?

Delete each post, as you would any scheduled post: DELETE /api/v1/posts/:id. List the group with GET /api/v1/posts?group_id=... first to get the ids. Deleting any post of a scheduled thread cancels that whole thread, and the quota is returned.

Is there a limit on how many accounts one request can name?

  1. The accounts also have to be connected and active, and your plan's account limit applies as usual. See Accounts.
  • Posts API: every field a post request takes, and every field it returns
  • Threads API: the same fan-out, with posts instead of text
  • Scheduling: timestamps, retries, and cancelling

On this page