API Reference
API Reference
Base URL, authentication, response format, errors, limits and the full list of Relaycast REST endpoints.
The Relaycast API is a JSON REST API. You can call it with any HTTP client: the examples use cURL, fetch in Node.js, and Python requests. There is no official SDK.
Base URL
https://social-publish-api.onrender.comAll resource endpoints are versioned under /v1. The health endpoints (/health, /ready) are at the root and need no authentication.
Authentication
Every /v1 request needs an API key. Create one in the dashboard under API Keys. Send it in either header:
| Header | Example |
|---|---|
x-access-key | x-access-key: sp_live_… |
Authorization | Authorization: Bearer sp_live_… |
A key belongs to one project; everything you create with it (posts, accounts) is scoped to that project. Revoked keys stop working immediately. See Authentication.
curl https://social-publish-api.onrender.com/v1/accounts \
-H "x-access-key: $RELAYCAST_API_KEY"const res = await fetch("https://social-publish-api.onrender.com/v1/accounts", {
headers: { "x-access-key": process.env.RELAYCAST_API_KEY! },
});
const { data } = await res.json();import os, requests
res = requests.get(
"https://social-publish-api.onrender.com/v1/accounts",
headers={"x-access-key": os.environ["RELAYCAST_API_KEY"]},
)
data = res.json()["data"]Requests
- Send JSON bodies with
content-type: application/json. - Request bodies are limited to 1 MB. Media is never uploaded in the request body: pass a public
httpsURL or a media library reference (media://<id>). See Media & Uploads. - Timestamps are ISO 8601 strings.
scheduledFormust include a time zone (Zor an offset such as+02:00). - IDs are UUIDs.
Responses
Successful responses wrap the result in a data field, for single objects and lists alike:
{
"data": [
{ "id": "6f1c2a7e-4b8d-4f7a-9a51-2d3c4b5a6e7f", "platform": "twitter", "handle": "acme" }
]
}Errors
Errors use a single envelope with a machine-readable code, a human-readable message, and sometimes details:
{
"error": {
"code": "validation_error",
"message": "Content is not valid for some platforms",
"details": {
"6f1c2a7e-4b8d-4f7a-9a51-2d3c4b5a6e7f": [
{ "field": "content", "message": "X posts allow 280 characters (links count as 23, emoji/CJK as 2); turn on Long post for Premium accounts; this is 312" }
]
}
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_json | The body isn't valid JSON. |
| 400 | validation_error | The body doesn't match the schema, or content isn't valid for a platform. |
| 400 | unknown_account | An accountId doesn't exist in this project. |
| 400 | account_inactive | An account needs reconnecting. |
| 400 | unknown_media | A media:// reference doesn't exist in this project's media library. |
| 400 | invalid_credentials | Bluesky rejected the handle or app password. |
| 401 | unauthorized | Missing or invalid API key. |
| 402 | insufficient_credits | Not enough credits for the post. |
| 404 | not_found | The resource or route doesn't exist. |
| 413 | payload_too_large | The request body is larger than 1 MB. |
| 429 | rate_limited | Too many requests for this API key. |
| 500 | internal_error | Unexpected server error. |
validation_error has two details shapes:
- Schema errors (wrong types, missing fields): an array of issues, each with
path(for example["targets", 0, "accountId"]),messageandcode. - Platform errors: an object keyed by account ID, each holding a list of
{ field, message }.
See Errors for handling advice.
Rate limits
Each API key may make 120 requests per minute by default (fixed one-minute window; self-hosted deployments set RATE_LIMIT_PER_MINUTE). Responses include:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per minute |
X-RateLimit-Remaining | Requests left in the current minute |
Over the limit, the API returns 429 with code rate_limited; retry after the minute rolls over. See Rate Limits.
Lists and limits
GET /v1/posts returns the most recent posts first. Use limit (1–100, default 20). There is no cursor pagination; to browse or filter older posts, use Posts in the dashboard.
| Limit | Value |
|---|---|
Accounts per post (targets) | 1–20 |
| Media items per post | 10 |
content | 1–10,000 characters (platforms have lower limits) |
| Media URL | 2,048 characters |
| Request body | 1 MB |
Idempotency
POST /v1/posts accepts an Idempotency-Key header. Retrying with the same key returns the post created by the first request (200) instead of creating and charging for a new one (201). See Idempotency & Retries.
Credits
Each post costs 1 credit per target account, charged when the post is created. Targets that fail permanently are refunded. See Credits & Usage.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /v1/accounts | List connected social accounts |
POST | /v1/accounts/connect-links | Create a hosted link your user opens to connect an OAuth account |
GET | /v1/accounts/connect-links/{id} | Check a connect link's result |
POST | /v1/accounts/bluesky | Connect a Bluesky account with an app password |
POST | /v1/posts | Create a post (publish now or schedule) |
GET | /v1/posts | List recent posts |
GET | /v1/posts/{id} | Get a post and its per-platform targets |
POST | /v1/live | Start or schedule a live stream |
GET | /v1/live | List live streams |
GET | /v1/live/usage | Live minutes used this month |
GET | /v1/live/{id} | Get a live stream and its destinations |
POST | /v1/live/{id}/stop | Stop (or cancel) a live stream |
POST | /v1/live/{id}/relaunch | Go live again with the same setup |
GET | /health | Liveness check |
GET | /ready | Readiness check (database and queue) |
Events are delivered to your server as webhooks.
Some operations are only available in the dashboard: connecting OAuth platforms (Integrations), uploading files to the media library, deleting or cancelling posts, post analytics, managing API keys, and configuring notifications.
Request logs
Every authenticated /v1 request is recorded in the dashboard under Logs, including requests rejected by the rate limiter. See Request Logs.
This page is also available as Markdown.