REST API v1

Developer API Reference

Create, manage, and publish social media drafts programmatically. All endpoints require an API token — generate one in your account settings.

Prefer natural language? Use DemandBird inside Claude →

Authentication

Every request must include your API token as a Bearer token in the Authorization header. Generate a token from Settings → API Tokens.

Authorization: Bearer YOUR_API_TOKEN
Keep your API token secret. Treat it like a password — don't commit it to source code or expose it in client-side JavaScript.

Base URL

All API endpoints are relative to:

https://app.demandbird.com/api/v1/accounts/{account_id}

Your account_id is the numeric ID shown in your account settings. All endpoints are scoped to a single account — you cannot access another account's data even with a valid token.

Rate limits

1,000
requests per hour
60
requests per minute (burst)

When you exceed a limit the API returns 429 Too Many Requests with headers:

HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 1735689600

{"error": "Rate limit exceeded. Try again later."}

Wait until RateLimit-Reset (Unix timestamp) before retrying.

Errors

Status Meaning
401 Unauthorized Missing or invalid API token.
403 Forbidden Token is valid but you don't have access to this account.
404 Not Found Draft or resource not found.
422 Unprocessable Entity Validation failed. Check the errors array in the response body.
429 Too Many Requests Rate limit exceeded.
500 Internal Server Error Something went wrong on our end.

Error responses always include an error or errors key:

{"error": "Draft not found"}

{"errors": ["Content can't be blank", "Platforms can't be empty"]}

List drafts

GET /drafts

Returns a paginated list of drafts for the account. Defaults to draft and scheduled status.

Query parameters

ParameterTypeDescription
statusoptional string Filter by status. One of: draft, scheduled, posted, failed. Defaults to returning draft and scheduled combined.
pageoptional integer Page number. Defaults to 1. Returns 20 results per page.
curl -H "Authorization: Bearer YOUR_TOKEN" \
     "https://app.demandbird.com/api/v1/accounts/42/drafts"

# Filter by status
curl -H "Authorization: Bearer YOUR_TOKEN" \
     "https://app.demandbird.com/api/v1/accounts/42/drafts?status=scheduled"

Response:

{
  "drafts": [
    {
      "id": 123,
      "status": "draft",
      "content": "Big news: we just shipped X...",
      "platforms": ["linkedin", "twitter"],
      "scheduled_at": null,
      "published_at": null,
      "created_at": "2026-03-01T09:00:00Z",
      "updated_at": "2026-03-01T09:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "items": 20,
    "count": 42,
    "pages": 3
  }
}

Get a draft

GET /drafts/:id

Retrieve a single draft by its ID.

curl -H "Authorization: Bearer YOUR_TOKEN" \
     "https://app.demandbird.com/api/v1/accounts/42/drafts/123"

Response:

{
  "draft": {
    "id": 123,
    "status": "draft",
    "content": "Big news: we just shipped X...",
    "platforms": ["linkedin", "twitter"],
    "scheduled_at": null,
    "published_at": null,
    "created_at": "2026-03-01T09:00:00Z",
    "updated_at": "2026-03-01T09:00:00Z"
  }
}

Create a draft

POST /drafts

Create a new draft. The post is saved with draft status and is not published until you call the publish endpoint.

Request body (JSON)

FieldTypeDescription
contentrequired string The text content of the post.
platformsoptional array Platforms to publish to. Supported values: linkedin, twitter, threads, bluesky, substack, tiktok, youtube, instagram.
scheduled_atoptional string (ISO 8601) Store an intended publish time on the draft. Does not schedule the post — use publish to send immediately.
curl -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Big news: we just shipped X. Here is what changed...",
    "platforms": ["linkedin", "twitter"]
  }' \
  "https://app.demandbird.com/api/v1/accounts/42/drafts"

Returns 201 Created with the new draft:

{
  "draft": {
    "id": 124,
    "status": "draft",
    "content": "Big news: we just shipped X...",
    "platforms": ["linkedin", "twitter"],
    "scheduled_at": null,
    "published_at": null,
    "created_at": "2026-03-02T10:30:00Z",
    "updated_at": "2026-03-02T10:30:00Z"
  }
}

Update a draft

PATCH /drafts/:id

Update content, platforms, or scheduled time on an existing draft. All fields are optional — only send the fields you want to change.

Request body (JSON)

FieldTypeDescription
contentoptional string New text content for the post.
platformsoptional array Replaces the current platform selection.
scheduled_atoptional string (ISO 8601) Update the stored scheduled time.
curl -X PATCH \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Updated copy — cleaner and tighter.",
    "platforms": ["linkedin"]
  }' \
  "https://app.demandbird.com/api/v1/accounts/42/drafts/124"

Response:

{
  "draft": {
    "id": 124,
    "status": "draft",
    "content": "Updated copy — cleaner and tighter.",
    "platforms": ["linkedin"],
    ...
  }
}

Publish a draft

POST /drafts/:id/publish

Publish a draft immediately to all selected platforms. The draft must have at least one platform set — update it first if needed.

This kicks off the publishing pipeline: the post status changes to posting, platform-specific jobs are enqueued, and the status transitions to posted (or failed) once complete.

Publishing is irreversible. Make sure the content and platforms are correct before calling this endpoint.
curl -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  "https://app.demandbird.com/api/v1/accounts/42/drafts/124/publish"

Response:

{
  "draft": {
    "id": 124,
    "status": "posting",
    "content": "Updated copy — cleaner and tighter.",
    "platforms": ["linkedin"],
    "scheduled_at": "2026-03-02T11:00:00Z",
    "published_at": null,
    "created_at": "2026-03-02T10:30:00Z",
    "updated_at": "2026-03-02T11:00:00Z"
  },
  "message": "Post is being published to linkedin"
}

published_at will be populated once the platform confirms the post. Poll GET /drafts/:id to check the final status.

List published posts

GET /posts

Returns a paginated list of posts with posted status, ordered by most-recently-updated. Filter by date range with since and until.

Query parameters

ParameterTypeDescription
sinceoptional string (ISO 8601) Return posts updated at or after this timestamp.
untiloptional string (ISO 8601) Return posts updated at or before this timestamp.
pageoptional integer Page number. Defaults to 1.
curl -H "Authorization: Bearer YOUR_TOKEN" \
     "https://app.demandbird.com/api/v1/accounts/42/posts"

# Filter by date range
curl -H "Authorization: Bearer YOUR_TOKEN" \
     "https://app.demandbird.com/api/v1/accounts/42/posts?since=2026-03-01T00:00:00Z"

Response:

{
  "posts": [
    {
      "id": 121,
      "status": "posted",
      "content": "Post that went live yesterday...",
      "platforms": ["linkedin"],
      "scheduled_at": "2026-03-01T08:00:00Z",
      "published_at": "2026-03-01T08:01:32Z",
      "created_at": "2026-02-28T20:00:00Z",
      "updated_at": "2026-03-01T08:01:32Z"
    }
  ],
  "meta": {
    "page": 1,
    "items": 20,
    "count": 8,
    "pages": 1
  }
}

Draft object reference

FieldTypeDescription
id integer Unique identifier.
status string draft · scheduled · posting · posted · failed
content string Plain-text content of the post.
platforms array of strings Selected publish targets.
scheduled_at string or null ISO 8601 timestamp if a schedule was set.
published_at string or null ISO 8601 timestamp when the post was confirmed published. null until posted.
created_at string ISO 8601 creation timestamp.
updated_at string ISO 8601 last-updated timestamp.