Docs
Docs/API Reference/Overview

API Overview

The Octopost REST API: base URL, authentication, request format, rate limits and response conventions for posting to social networks from code.

Network status (2026-11): X (twitter), Bluesky (bluesky), Mastodon (mastodon) and LinkedIn (linkedin, personal profiles) are live. Threads (threads) opens after Meta approves the Octopost app. Instagram, Facebook, TikTok, YouTube, Pinterest and Reddit are in platform review and not available yet. Discord, Slack and Telegram are not supported.

The Octopost REST API lets you create, schedule, queue and publish posts, manage the queue, read your connected accounts, save publishing presets and register webhooks. Octopost handles each network's auth, uploads and threading. You send your content once.

The same features are available to AI agents as an MCP server at https://octopost.ink/mcp (see Agents). A machine-readable OpenAPI 3.1 spec lives at https://octopost.ink/openapi.json.

Base URL#

https://octopost.ink/api

There is no version segment in the path. Paths in these docs are relative to https://octopost.ink, for example POST /api/posts.

Authentication#

Send your API key on every request, either as a Bearer token or in x-api-key:

Authorization: Bearer op_your_api_key_here
# or
x-api-key: op_your_api_key_here

Your keys are at https://octopost.ink/dashboard/api/keys. See Authentication for the details.

Request format#

  • JSON bodies with Content-Type: application/json.
  • POST /api/posts and PUT /api/posts/{id} also accept multipart/form-data, so you can upload image and video files directly. See Uploading files.
  • Field names are camelCase (scheduledFor, mediaUrls, queueStatus). On the JSON body of POST /api/posts and PUT /api/posts/{id} Octopost also accepts the snake_case forms (scheduled_for, media_urls, queue_status, and so on). Other endpoints accept camelCase only and ignore unknown fields.
  • Times are ISO 8601 strings. Wherever you send a time, include a timezone offset (2026-04-20T21:00:00Z or 2026-04-20T16:00:00-05:00). A time without an offset is rejected.

Response format#

Every successful JSON response has "success": true plus the resource, for example { "success": true, "post": { ... } }.

Errors have an error string and sometimes details. See Errors.

A few things to know when reading responses:

  • Fields come back in camelCase (scheduledFor, platformPostIds, createdAt).
  • Booleans are true/false (published, active on webhooks, enabled in queue settings). Before 2026-11-04 some came back as 0/1.
  • A body that isn't valid JSON, or an upload that is too big or not a supported image/video, is a 400.
  • Per-network results use the network's display name in platform ("Twitter", "LinkedIn"), while stored per-network data (platformPostIds) is keyed by the lowercase id (twitter, linkedin).

Quick example#

Publish a post to X right away:

curl -X POST https://octopost.ink/api/posts \
  -H "Authorization: Bearer op_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hello from the Octopost API!",
    "platforms": ["twitter"],
    "published": true
  }'
{
  "success": true,
  "status": "published",
  "summary": "Published to X.",
  "post": {
    "id": "6f1c2d3e-...",
    "content": "Hello from the Octopost API!",
    "platforms": ["twitter"],
    "published": true,
    "status": "published",
    "platformPostIds": {
      "twitter": { "id": "1234567890", "url": "https://x.com/you/status/1234567890" }
    }
  },
  "results": {
    "twitter": { "platform": "twitter", "status": "published", "postId": "1234567890", "url": "https://x.com/you/status/1234567890" }
  },
  "publishResults": [
    { "platform": "Twitter", "success": true, "postId": "1234567890", "postUrl": "https://x.com/you/status/1234567890" }
  ],
  "warnings": []
}

The status is 201 when every network has it and 207 when one didn't (status is then partial or failed; results says why). A publish still running after about 20 seconds (Threads with media can take minutes) answers 202 with "status": "publishing" and the post id; it carries on, and you poll GET /api/posts/{id} until the status changes (details). Send an Idempotency-Key header so a retried request can't post twice. Leave out published to save a draft, then publish it later with POST /api/posts/{id}/publish.

Rate limits#

BucketLimit
Most /api/* requests100 per minute
POST /api/posts/{id}/publish50 per hour

Limits are counted per client IP address. Going over returns 429 with a Retry-After header. See Rate Limits.

Resources#

Related guides