---
title: "Posts"
description: "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

```json
{
  "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

| Status | Description |
|--------|-------------|
| `draft` | Created but not yet published or scheduled |
| `scheduled` | Queued for publishing at `scheduled_for` time |
| `publishing` | Currently being published to platforms |
| `published` | Successfully published to at least one platform |
| `failed` | Publishing 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

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `content` | string | Yes | The post content. Platform-specific character limits are validated at publish time. |
| `platforms` | string[] | Yes | Array of platform identifiers to publish to. Valid values: `twitter`, `bluesky`, `mastodon`, `linkedin`, `threads`, `instagram`, `facebook`, `tiktok`, `youtube`. |
| `scheduled_for` | string | No | ISO 8601 datetime for scheduled publishing. Must be in the future. |
| `media` | object | No | Media attachments. See Media Object below. |

### Media Object

| Field | Type | Description |
|-------|------|-------------|
| `images` | array | Array of image objects, each with `url` (string, required) and `alt_text` (string, optional). Maximum 4 images. |
| `video` | object | Video object with `url` (string, required) and `thumbnail_url` (string, optional). Cannot be combined with images. |

### Example: Create a Draft Post

```bash
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"]
  }'
```

```json
{
  "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

```bash
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"
        }
      ]
    }
  }'
```

```json
{
  "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

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `status` | string | - | Filter by status: `published`, `scheduled`, `draft`, `failed`. |
| `limit` | integer | 10 | Number of posts to return. Maximum 100. |
| `offset` | integer | 0 | Number of posts to skip for pagination. |

### Example

```bash
curl "https://api.octopost.ink/v1/posts?status=published&limit=5&offset=0" \
  -H "Authorization: Bearer oct_live_abc123"
```

```json
{
  "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

```bash
curl https://api.octopost.ink/v1/posts/post_abc123 \
  -H "Authorization: Bearer oct_live_abc123"
```

```json
{
  "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.

| Field | Type | Description |
|-------|------|-------------|
| `content` | string | Updated post content. |
| `platforms` | string[] | Updated list of target platforms. |
| `scheduled_for` | string \| null | Updated schedule time (ISO 8601), or `null` to unschedule. |
| `media` | object \| null | Updated media attachments, or `null` to remove media. |

### Example

```bash
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"]
  }'
```

```json
{
  "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

```bash
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](/docs/api/webhooks) to be notified when publishing completes, or poll the post endpoint.

### Example

```bash
curl -X POST https://api.octopost.ink/v1/posts/post_def456/publish \
  -H "Authorization: Bearer oct_live_abc123"
```

```json
{
  "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`:

```json
{
  "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

| Code | Reason |
|------|--------|
| `409 Conflict` | Post is already published or currently publishing. |
| `422 Validation Error` | Content exceeds character limit for one or more platforms, or no valid connected accounts found for the specified platforms. |
