Docs
Docs/API Reference/Posts

Posts

Create, read, update, and delete posts

Posts

Posts are the core resource in the Octopost API. A post contains content that can be published to one or more social media platforms simultaneously.

The Post Object

{
  "id": "post_abc123",
  "content": "Check out our new feature launch!",
  "platforms": ["twitter", "bluesky", "linkedin"],
  "status": "published",
  "scheduled_for": null,
  "media": {
    "images": [
      {
        "url": "https://cdn.octopost.ink/uploads/img_001.png",
        "alt_text": "Screenshot of the new dashboard"
      }
    ],
    "video": null
  },
  "platform_results": {
    "twitter": {
      "success": true,
      "post_id": "1234567890",
      "post_url": "https://x.com/user/status/1234567890"
    },
    "bluesky": {
      "success": true,
      "post_id": "at://did:plc:abc/app.bsky.feed.post/xyz",
      "post_url": "https://bsky.app/profile/user.bsky.social/post/xyz"
    },
    "linkedin": {
      "success": false,
      "error": "Token expired. Reconnect your LinkedIn account."
    }
  },
  "created_at": "2026-04-03T12:00:00Z",
  "updated_at": "2026-04-03T12:05:00Z",
  "published_at": "2026-04-03T12:05:00Z"
}

Status Values

StatusDescription
draftCreated but not yet published or scheduled
scheduledQueued for publishing at scheduled_for time
publishingCurrently being published to platforms
publishedSuccessfully published to at least one platform
failedPublishing failed on all platforms

Create a Post

POST /posts

Creates a new post. The post is created in draft status unless scheduled_for is provided, in which case it is created in scheduled status.

Request Body

FieldTypeRequiredDescription
contentstringYesThe post content. Platform-specific character limits are validated at publish time.
platformsstring[]YesArray of platform identifiers to publish to. Valid values: twitter, bluesky, mastodon, linkedin, threads, instagram, facebook, tiktok, youtube.
scheduled_forstringNoISO 8601 datetime for scheduled publishing. Must be in the future.
mediaobjectNoMedia attachments. See Media Object below.

Media Object

FieldTypeDescription
imagesarrayArray of image objects, each with url (string, required) and alt_text (string, optional). Maximum 4 images.
videoobjectVideo object with url (string, required) and thumbnail_url (string, optional). Cannot be combined with images.

Example: Create a Draft Post

curl -X POST https://api.octopost.ink/v1/posts \
  -H "Authorization: Bearer oct_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Excited to announce our Series A!",
    "platforms": ["twitter", "linkedin", "bluesky"]
  }'
{
  "id": "post_def456",
  "content": "Excited to announce our Series A!",
  "platforms": ["twitter", "linkedin", "bluesky"],
  "status": "draft",
  "scheduled_for": null,
  "media": null,
  "platform_results": null,
  "created_at": "2026-04-03T14:00:00Z",
  "updated_at": "2026-04-03T14:00:00Z",
  "published_at": null
}

Example: Create a Scheduled Post with Media

curl -X POST https://api.octopost.ink/v1/posts \
  -H "Authorization: Bearer oct_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "New blog post: Building a cross-posting API",
    "platforms": ["twitter", "bluesky"],
    "scheduled_for": "2026-04-10T09:00:00Z",
    "media": {
      "images": [
        {
          "url": "https://example.com/blog-header.png",
          "alt_text": "Blog post header image"
        }
      ]
    }
  }'
{
  "id": "post_ghi789",
  "content": "New blog post: Building a cross-posting API",
  "platforms": ["twitter", "bluesky"],
  "status": "scheduled",
  "scheduled_for": "2026-04-10T09:00:00Z",
  "media": {
    "images": [
      {
        "url": "https://example.com/blog-header.png",
        "alt_text": "Blog post header image"
      }
    ],
    "video": null
  },
  "platform_results": null,
  "created_at": "2026-04-03T14:00:00Z",
  "updated_at": "2026-04-03T14:00:00Z",
  "published_at": null
}

List Posts

GET /posts

Returns a paginated list of posts.

Query Parameters

ParameterTypeDefaultDescription
statusstring-Filter by status: published, scheduled, draft, failed.
limitinteger10Number of posts to return. Maximum 100.
offsetinteger0Number of posts to skip for pagination.

Example

curl "https://api.octopost.ink/v1/posts?status=published&limit=5&offset=0" \
  -H "Authorization: Bearer oct_live_abc123"
{
  "posts": [
    {
      "id": "post_abc123",
      "content": "Check out our new feature launch!",
      "platforms": ["twitter", "bluesky"],
      "status": "published",
      "scheduled_for": null,
      "media": null,
      "platform_results": {
        "twitter": {
          "success": true,
          "post_id": "1234567890",
          "post_url": "https://x.com/user/status/1234567890"
        },
        "bluesky": {
          "success": true,
          "post_id": "at://did:plc:abc/app.bsky.feed.post/xyz",
          "post_url": "https://bsky.app/profile/user.bsky.social/post/xyz"
        }
      },
      "created_at": "2026-04-03T12:00:00Z",
      "updated_at": "2026-04-03T12:05:00Z",
      "published_at": "2026-04-03T12:05:00Z"
    }
  ],
  "total": 42
}

Get a Post

GET /posts/:id

Returns a single post by ID.

Example

curl https://api.octopost.ink/v1/posts/post_abc123 \
  -H "Authorization: Bearer oct_live_abc123"
{
  "id": "post_abc123",
  "content": "Check out our new feature launch!",
  "platforms": ["twitter", "bluesky"],
  "status": "published",
  "scheduled_for": null,
  "media": null,
  "platform_results": {
    "twitter": {
      "success": true,
      "post_id": "1234567890",
      "post_url": "https://x.com/user/status/1234567890"
    },
    "bluesky": {
      "success": true,
      "post_id": "at://did:plc:abc/app.bsky.feed.post/xyz",
      "post_url": "https://bsky.app/profile/user.bsky.social/post/xyz"
    }
  },
  "created_at": "2026-04-03T12:00:00Z",
  "updated_at": "2026-04-03T12:05:00Z",
  "published_at": "2026-04-03T12:05:00Z"
}

Returns 404 Not Found if the post does not exist.


Update a Post

PUT /posts/:id

Updates a post. Only posts in draft or scheduled status can be updated. Attempting to update a published or publishing post returns 409 Conflict.

Request Body

All fields are optional. Only include the fields you want to change.

FieldTypeDescription
contentstringUpdated post content.
platformsstring[]Updated list of target platforms.
scheduled_forstring | nullUpdated schedule time (ISO 8601), or null to unschedule.
mediaobject | nullUpdated media attachments, or null to remove media.

Example

curl -X PUT https://api.octopost.ink/v1/posts/post_def456 \
  -H "Authorization: Bearer oct_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Excited to announce our Series A! Read more on our blog.",
    "platforms": ["twitter", "linkedin", "bluesky", "threads"]
  }'
{
  "id": "post_def456",
  "content": "Excited to announce our Series A! Read more on our blog.",
  "platforms": ["twitter", "linkedin", "bluesky", "threads"],
  "status": "draft",
  "scheduled_for": null,
  "media": null,
  "platform_results": null,
  "created_at": "2026-04-03T14:00:00Z",
  "updated_at": "2026-04-03T14:10:00Z",
  "published_at": null
}

Delete a Post

DELETE /posts/:id

Deletes a post. Published posts are removed from Octopost but are not deleted from the social platforms they were published to. To delete from platforms, use the platform's native tools.

Example

curl -X DELETE https://api.octopost.ink/v1/posts/post_def456 \
  -H "Authorization: Bearer oct_live_abc123"

Returns 204 No Content on success.


Publish a Post

POST /posts/:id/publish

Immediately publishes a post to all specified platforms. The post must be in draft or scheduled status. Publishing is asynchronous -- the endpoint returns immediately with status publishing, and platform results are populated as each platform completes.

Use webhooks to be notified when publishing completes, or poll the post endpoint.

Example

curl -X POST https://api.octopost.ink/v1/posts/post_def456/publish \
  -H "Authorization: Bearer oct_live_abc123"
{
  "id": "post_def456",
  "content": "Excited to announce our Series A! Read more on our blog.",
  "platforms": ["twitter", "linkedin", "bluesky", "threads"],
  "status": "publishing",
  "scheduled_for": null,
  "media": null,
  "platform_results": null,
  "created_at": "2026-04-03T14:00:00Z",
  "updated_at": "2026-04-03T14:15:00Z",
  "published_at": null
}

After publishing completes, a subsequent GET /posts/post_def456 returns the full platform_results:

{
  "id": "post_def456",
  "status": "published",
  "platform_results": {
    "twitter": {
      "success": true,
      "post_id": "1234567891",
      "post_url": "https://x.com/user/status/1234567891"
    },
    "linkedin": {
      "success": true,
      "post_id": "urn:li:share:7890",
      "post_url": "https://www.linkedin.com/feed/update/urn:li:share:7890"
    },
    "bluesky": {
      "success": true,
      "post_id": "at://did:plc:abc/app.bsky.feed.post/def",
      "post_url": "https://bsky.app/profile/user.bsky.social/post/def"
    },
    "threads": {
      "success": true,
      "post_id": "17890012345",
      "post_url": "https://www.threads.net/@user/post/17890012345"
    }
  },
  "published_at": "2026-04-03T14:15:05Z"
}

Errors

CodeReason
409 ConflictPost is already published or currently publishing.
422 Validation ErrorContent exceeds character limit for one or more platforms, or no valid connected accounts found for the specified platforms.