Rutba

Blog · Platform services

Publishing to several social networks with one API call, shown with a real response

Rutba Relay takes one post and a list of accounts and reports a result for each. Here is a real request and response, the networks a customer can use today, and how it compares with Ayrshare and Hootsuite.

· 8 min read

Share this

Rutba Relay takes one post and a list of accounts, adapts the post to each network, publishes in the background and reports a separate result for every account. This article shows a real request and response from that API, lists what a customer can publish to today, and compares it with Ayrshare and Hootsuite, whose pricing and documentation we read on 10 November 2026.

What a customer can publish to today

The catalogue at GET /v1/platforms lists 55 destinations, and Relay has an adapter for 16 of them. Five are open to any customer now, and a sixth is a test destination that reaches no network at all.

DestinationHow you connect itWhat the adapter allows
BlueskyHandle and app password300 characters, up to 4 images of 0.9 MB
DiscordIncoming webhook URL2,000 characters, up to 10 images or videos of 10 MB
MastodonSign-in on your own instance500 characters, up to 4 images, or 1 video of 40 MB
TelegramChannel name. Use our bot or your own, and make it an admin of the channel4,096 characters, up to 10 images of 10 MB or videos of 50 MB
WordPressSelf-hosted site URL, username and application passwordNo practical text limit, up to 10 images, native tags
SandboxNothing to set upFor test suites. No network is contacted
Read from the live catalogue on 10 November 2026.

The other ten adapters are written. Five of those networks can be connected within the limits each one sets until it has reviewed our app, and those limits decide whether a customer can use them. The other five cannot be connected, because no app is registered for them yet.

NetworkState on 10 NovemberWhat that means for a customer
Facebook Pages, Instagram, ThreadsConnectable within Meta’s limits. Our app stays in Development mode until Meta’s review is doneOnly people with a role on our app can connect, so customers cannot use these yet
TikTokConnectable within TikTok’s limits while it reviews our app, on its sandbox keysOnly named test accounts can connect, and posts stay private to the creator
YouTubeConnectable within Google’s limits. Google has verified our app’s data access, and its separate YouTube API audit has not passed yetEvery upload is forced private until that audit passes, so it is no use for publishing yet
LinkedIn, Pinterest, Reddit, SlackNo app registered yetCannot be connected
XNo app registered. X charges per request, so it will be sold against prepaid credit, and that is not in placeCannot be connected

The run behind the examples

The requests and responses below come from one run on 10 November 2026: the Relay gate, its delivery worker and the public API door, running on a development machine and called with an API key. Every publish went to Sandbox accounts, so no social network was contacted. The previews used the real rules for Bluesky and Mastodon, with connection rows we inserted for the run, because a preview only adapts the post and never reaches a network.

The ids are the ones that run produced, and the responses are trimmed to the fields that matter. On the hosted service the base URL is https://api.relay.rutba.io and live keys begin rsk_live_. API keys and webhooks come with the Agency plan, listed at $99 a month, so the free plan covers the console and does not cover this walkthrough. Agency is sold by written quote today rather than self-serve checkout, through the Rutba Social page. Once a workspace is on it, the API key works immediately.

Connect an account

curl -X POST https://api.relay.rutba.io/v1/connections \
  -H "Authorization: Bearer $RELAY_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "platform": "sandbox", "fields": { "handle": "@first-account" } }'
{
  "data": {
    "id": "u0czr5buhlftcszrxuik6ant",
    "platform": "sandbox",
    "handle": "@first-account",
    "display_name": "Sandbox account",
    "status": "active"
  }
}

Bluesky, Discord, Telegram and WordPress connect through this same call, each with its own fields, which the catalogue lists per network. Mastodon goes through a sign-in on your own instance. For an account that belongs to somebody else, POST /v1/connect-sessions makes a hosted link they can open themselves.

See what each network will get

A preview runs the same adaptation the publisher runs and writes nothing. This post is 372 characters long, which suits Mastodon and does not suit Bluesky.

curl -X POST https://api.relay.rutba.io/v1/posts/preview \
  -H "Authorization: Bearer $RELAY_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "content": { "text": "Version 4 of our invoicing app is out today. Recurring invoices can now be paused for a month without losing the schedule, statements export to CSV as well as PDF, and the slow customer search on accounts with more than 20,000 rows is fixed. The full list of changes is on the changelog, and the upgrade is automatic for every hosted account, so nobody has to do anything." },
    "connection_ids": ["vg8ubpzn43y38uqxpki8wr3u", "qeiby35tu44xz6t0kjjt9adf"]
  }'

Bluesky’s part of the 200 response:

{
  "connection_id": "vg8ubpzn43y38uqxpki8wr3u",
  "platform": "bluesky",
  "handle": "yourbrand.bsky.social",
  "connection_status": "active",
  "text": "Version 4 of our invoicing app is out today. Recurring invoices can now be paused for a month without losing the schedule, statements export to CSV as well as PDF, and the slow customer search on accounts with more than 20,000 rows is fixed. The full list of changes is on the changelog, and the…",
  "warnings": [
    {
      "code": "too_long",
      "field": "text",
      "message": "Bluesky allows 300 characters, got 372",
      "fixable": true
    }
  ],
  "blocking": [],
  "publishable": true
}

The text was cut at a word boundary to 296 characters, and the warning says why. Mastodon’s copy came back whole at 372 with no warnings. Send "validation": "strict" and nothing is adapted: in the same run Bluesky came back with "publishable": false and a too_long entry under blocking, and Mastodon stayed publishable.

On Bluesky, Mastodon, Telegram and Discord, Relay puts the link and hashtags in the body, so they count towards the limit. In lenient mode it shortens your words to make room and leaves the link and every hashtag whole. Given the same 372 characters with a link and two tags, the Bluesky rules cut the words to 246 characters and the post came to 297. The 192-character post in the next step comes to 243 with that link and those tags.

Publish to several accounts at once

One request, with an Idempotency-Key so that a retry cannot post twice. The third target uses a Sandbox override that asks for a refusal, so the response shows what a failure looks like.

curl -X POST https://api.relay.rutba.io/v1/posts \
  -H "Authorization: Bearer $RELAY_API_KEY" \
  -H "Idempotency-Key: release-4-announcement" \
  -H "content-type: application/json" \
  -d '{
    "content": {
      "text": "Version 4 of our invoicing app is out today. Recurring invoices can now be paused for a month, statements export to CSV as well as PDF, and the slow customer search on large accounts is fixed.",
      "link": "https://example.com/changelog/4",
      "tags": ["changelog", "saas"]
    },
    "targets": [
      { "connection_id": "u0czr5buhlftcszrxuik6ant" },
      { "connection_id": "dlhdpjmr5jr8wkbpxf1vg5q3" },
      { "connection_id": "rzesyqu5iao9x44oxbodhogm",
        "overrides": { "outcome": "content_rejected" } }
    ]
  }'

The answer is a 202, trimmed here to the post and its deliveries:

{
  "data": {
    "id": "gsvpcm1j1n4jerme5kxrmej7",
    "status": "queued",
    "source": "api",
    "deliveries": [
      { "id": "tkcmunic8f9vgbkun4lz42gt", "platform": "sandbox",
        "connection_id": "u0czr5buhlftcszrxuik6ant", "status": "pending", "attempts": 0 },
      { "id": "pmjmr51qfyj9pmzsx5xliv0x", "platform": "sandbox",
        "connection_id": "dlhdpjmr5jr8wkbpxf1vg5q3", "status": "pending", "attempts": 0 },
      { "id": "oliszvo4p9bah2ouekd75dn8", "platform": "sandbox",
        "connection_id": "rzesyqu5iao9x44oxbodhogm", "status": "pending", "attempts": 0 }
    ]
  }
}

No network has been contacted at this point. The API has written one post and one delivery per target, and each delivery is queued on its own, so the call returns quickly however many accounts it names and a slow network cannot hold up the others.

Replace targets with "platforms": ["bluesky", "mastodon", "telegram"] to reach every active account on those networks. If one of them has no active connection, the request is refused with a 409 and connection_unavailable. Add scheduled_at and timezone to send the post later.

Read the result

curl https://api.relay.rutba.io/v1/posts/gsvpcm1j1n4jerme5kxrmej7 \
  -H "Authorization: Bearer $RELAY_API_KEY"
{
  "data": {
    "id": "gsvpcm1j1n4jerme5kxrmej7",
    "status": "partial",
    "published_at": "2026-11-10T04:29:10.419Z",
    "deliveries": [
      {
        "id": "tkcmunic8f9vgbkun4lz42gt",
        "platform": "sandbox",
        "status": "succeeded",
        "attempts": 1,
        "remote_id": "sbx_d4c45fa83f129e36b5c0f119",
        "remote_url": "https://sandbox.rutba.io/p/sbx_d4c45fa83f129e36b5c0f119",
        "error": null,
        "error_code": null
      },
      {
        "id": "pmjmr51qfyj9pmzsx5xliv0x",
        "platform": "sandbox",
        "status": "succeeded",
        "attempts": 1,
        "remote_id": "sbx_f490bd67448b7d2ddee4b8ba",
        "remote_url": "https://sandbox.rutba.io/p/sbx_f490bd67448b7d2ddee4b8ba",
        "error": null,
        "error_code": null
      },
      {
        "id": "oliszvo4p9bah2ouekd75dn8",
        "platform": "sandbox",
        "status": "rejected",
        "attempts": 1,
        "remote_id": null,
        "remote_url": null,
        "error": "Sandbox: the post was refused on request",
        "error_code": "content_rejected"
      }
    ]
  }
}

Two accounts took the post and one refused it, so the post as a whole is partial. The refused delivery shows one attempt, because a refusal is final. Rate limits and outages are retried, each delivery on its own budget. GET /v1/deliveries?post_id=… returns the same rows with filters and a cursor, and POST /v1/deliveries/{id}/retry sends one destination again, which Relay refuses for a delivery the network has rejected.

Send it twice on purpose

A timeout leaves you not knowing whether the post went out, so the safe move is to send the same request again. With the same Idempotency-Key and body, the run returned the original post id and a response header of idempotent-replay: true. Keys are remembered for 24 hours. The same key with a different body is refused with a 409:

{
  "error": {
    "code": "idempotency_conflict",
    "message": "Idempotency-Key \"release-4-announcement\" was already used with a different request body"
  }
}

Hear about it by webhook

This post produced five events at the test receiver: post.created, delivery.failed, delivery.succeeded twice and post.partial. Do not build on their order. In an earlier run the same day, post.partial arrived before the second delivery.succeeded.

{
  "id": "evt_cw8G-dyyQXy0ZPA2",
  "type": "post.partial",
  "created_at": "2026-11-10T04:29:10.460Z",
  "data": {
    "post_id": "gsvpcm1j1n4jerme5kxrmej7",
    "status": "partial",
    "succeeded": 2,
    "failed": 1,
    "external_id": null,
    "deliveries": [
      { "id": "tkcmunic8f9vgbkun4lz42gt", "platform": "sandbox", "status": "succeeded",
        "remote_url": "https://sandbox.rutba.io/p/sbx_d4c45fa83f129e36b5c0f119",
        "error": null, "error_code": null },
      { "id": "pmjmr51qfyj9pmzsx5xliv0x", "platform": "sandbox", "status": "succeeded",
        "remote_url": "https://sandbox.rutba.io/p/sbx_f490bd67448b7d2ddee4b8ba",
        "error": null, "error_code": null },
      { "id": "oliszvo4p9bah2ouekd75dn8", "platform": "sandbox", "status": "rejected",
        "remote_url": null,
        "error": "Sandbox: the post was refused on request", "error_code": "content_rejected" }
    ]
  }
}

Each request carries x-relay-signature: t=<unix seconds>,v1=<hex>, an HMAC-SHA256 of the timestamp and the raw body joined by a full stop. A retry of the same event keeps the same x-relay-delivery id, so you can deduplicate on it.

Against Ayrshare and Hootsuite

Start with what counts against us. Ayrshare and Hootsuite already publish to Instagram, TikTok and LinkedIn for their customers, and Relay cannot yet. If one of those networks is the reason for your integration, that settles it for now.

Rutba RelayAyrshareHootsuite
What it isA publishing API with a web console on the same routesA publishing API for developers, with a light dashboardA social media management suite, with a REST API beside it
Lowest listed price with the API$99 a month (Agency), by written quote, no cap on accounts or seats$149 a month (Premium), one profile with up to 14 accountsIts plans page does not say which plans include the API. Standard starts at $99 per user a month on annual billing, with 10 accounts
GrowingAgency has no account or seat cap$299 a month for up to 10 profiles, $599 for 30, then a per-profile rateProfessional starts at $199 per user a month, with unlimited accounts
Trying itA free plan for the console, without the APIA 28-day trial on Launch, no free planA 14-day trial
Open to customers todayBluesky, Discord, Mastodon, Telegram, WordPressBluesky, Facebook, Google Business, Instagram, LinkedIn, Pinterest, Reddit, Snapchat, Telegram, Threads, TikTok, X, YouTubeInstagram, TikTok, LinkedIn and the other large networks, plus Bluesky
The callPOST /v1/posts answers 202, then one delivery per accountPOST /api/post returns a postIds entry per platformPOST /v1/messages takes socialProfileIds and a send time at least five minutes ahead
Test destinationSandbox accounts that refuse, rate-limit or lose a response on requestParameters such as randomPost for test content. We found no separate sandbox in its docsNone in the guides we read
Repeated requestsIdempotency-Key, kept for 24 hours, replays the first answerOptional idempotencyKey, duplicates are rejectedNot covered in its scheduling guide
Inbox and listeningNeitherA messaging API for Instagram, Facebook, WhatsApp and XInbox, listening and benchmarking
Read on 10 November 2026 from ayrshare.com/pricing, ayrshare.com/docs, hootsuite.com/plans and developer.hootsuite.com. Prices in US dollars.

Our prices are marked as indicative while plans are being finalised, and a quote is confirmed in writing before anything is charged.

Which one fits

  • Hootsuite if a marketing team needs to answer comments and watch mentions from one screen. Relay has no inbox and no listening.
  • Ayrshare if your product needs Instagram, TikTok, LinkedIn or X this month, or if you post for thousands of your own customers and want a per-profile price.
  • Relay if your destinations are Bluesky, Mastodon, Telegram, Discord or WordPress, if somebody who does not write code also needs a console, or if you want your test suite to exercise refusals and rate limits on demand. Ayrshare’s publish endpoint lists Bluesky and Telegram, and it does not list Mastodon, Discord or WordPress.

The reference and the live catalogue

Every endpoint, field and plan limit is on relay.rutba.io, and the platforms page shows the state of each network.

Read the API reference

Read next

Business Suite

Bring your own assistant, through the same door as everyone else

If your team already works with an AI assistant, it can work with Rutba through the Model Context Protocol, on your organization’s own key. It gets six tools, meets the same three checks, and can propose a change but never make one.

3 min read

Platform services

Marketing creative goes stale the moment a price changes

The ad is not wrong when it is made. It becomes wrong on the Tuesday somebody edits the catalogue, and nothing about the file says so. Rendering one template once per row of a product sheet makes the fix a re-run rather than forty edits.

3 min read

Share this

One family

The rest of Rutba

One account across all of it. Sign in once and the products know each other.

PORTAL-BLOG-DETAIL · 9ca36b0