API Reference

Posts

List what exists, and create posts as a draft, a scheduled post, or an immediate publish across up to ten platforms at once.

List posts

GET/api/v1/posts

Requires read. Newest first.

Query parameters

statusstringoptional
Filter to one status: draft, scheduled, publishing, published, partial or failed.
limitnumberoptional
How many to return. Defaults to 20, clamped to 50.
curl
curl "https://app.echoia.io/api/v1/posts?status=scheduled&limit=5" \
  -H "Authorization: Bearer $ECHOIA_API_KEY"
200
{
  "posts": [
    {
      "id": "cmq7f8k1c0003sl3k7b4xe9rt",
      "content": "New drop is live.",
      "platforms": ["instagram", "x"],
      "status": "scheduled",
      "scheduledAt": "2026-09-12T18:00:00.000Z",
      "publishedAt": null,
      "results": [],
      "errorMessage": null,
      "createdAt": "2026-09-05T10:14:02.117Z"
    }
  ]
}

Content is truncated in lists

Post bodies over 500 characters come back shortened with an ellipsis, so a listing stays cheap. Fetch the post in the app if you need the full text.

Create a post

POST/api/v1/posts

Requires write — plus publish if you set publishNow.

Parameters

contentstringrequired
The post text. Up to 10,000 characters.
accountsstring[]required
Which connected accounts publish it. Each entry is a handle qualified by platform (instagram:@acme), a bare handle when it is connected on one platform only (@acme), or an account id from /accounts. One result per account; duplicates are ignored. A reference matching nothing — or more than one account — is refused with the candidates listed.
accountIdsstring[]optional
Deprecated synonym of accounts.
platformsstring[]optional
Shorthand alternative to accounts. Resolves only while the platform has exactly one connected account; with several it is refused with ambiguous_platform and the candidates are listed. Supply one field or the other.
scheduledAtstringoptional
ISO 8601 datetime in the future. Omit for a draft. Mutually exclusive with publishNow.
publishNowbooleanoptional
Publish immediately. Requires the publish scope.
mediaUrlsstring[]optional
Up to 10 absolute https:// URLs. Relative paths are rejected.
firstCommentsstring[]optional
Up to 5 comments posted under the post once it is live, in order. Max 2,200 characters each.

Three modes, one endpoint

You sendYou getGoes public
Neither fielddraft — sits in the Posts tabNo
scheduledAtscheduled — editable until thenAt that time
publishNow: truepublished / partial / failedImmediately

Scheduling

curl
curl -X POST https://app.echoia.io/api/v1/posts \
  -H "Authorization: Bearer $ECHOIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Doors open Friday.",
    "accounts": ["instagram:@acme", "facebook:@acme"],
    "scheduledAt": "2026-09-12T18:00:00Z",
    "firstComments": ["Full details in the link in bio."]
  }'

Overdue scheduled posts are not published late

If a scheduled post is more than six hours overdue — an outage, a long pause — it is marked failed rather than published. A promotion going out a day late is worse than not going out.

Publishing immediately

curl
curl -X POST https://app.echoia.io/api/v1/posts \
  -H "Authorization: Bearer $ECHOIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Live now.",
    "accounts": ["x:@acme", "x:@acme.labs"],
    "publishNow": true
  }'
201
{
  "post": {
    "id": "cmq7fb2p90007sl3kd8y1qw3v",
    "status": "published",
    "scheduledAt": null,
    "platforms": ["x"]
  },
  "accounts": [
    { "accountId": "cmr7ffil3",    "platform": "x", "account": "@acme" },
    { "accountId": "cmrd5lx2uizc", "platform": "x", "account": "@acme.labs" }
  ],
  "results": [
    { "platform": "x", "accountId": "cmr7ffil3",    "accountLabel": "@acme",      "success": true, "postId": "1834…", "url": "https://x.com/i/web/status/1834…" },
    { "platform": "x", "accountId": "cmrd5lx2uizc", "accountLabel": "@acme.labs", "success": true, "postId": "1835…", "url": "https://x.com/i/web/status/1835…" }
  ],
  "note": "Published on every targeted account."
}

results is keyed by account, not by platform

Two accounts on the same platform produce two entries with the same platform. Match on accountId. The accounts array echoes what the request resolved to, which is what you read back when you used the platforms shorthand.

Publishing is not all-or-nothing. One account can succeed while another fails — always read results, and see Errors for the partial case.

What gets rejected

  • Neither accounts nor platforms.
  • An account reference that names nothing this key can reach, or an account that is disconnected.
  • A platforms entry matching several connected accounts — ambiguous_platform, with the candidates listed. Send accounts instead.
  • Neither content nor mediaUrls.
  • Both scheduledAt and publishNow.
  • A scheduledAt in the past, or not a valid ISO datetime.
  • publishNow without the publish scope — and nothing is created.
Was this page helpful?