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

StatusMeaning
401 UnauthorizedMissing or invalid API token.
403 ForbiddenToken is valid but you don't have access to this account.
404 Not FoundDraft or resource not found.
422 Unprocessable EntityValidation failed. Check the errors array in the response body.
429 Too Many RequestsRate limit exceeded.
500 Internal Server ErrorSomething 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
statusoptionalstringFilter by status. One of: draft, scheduled, posted, failed. Defaults to returning draft and scheduled combined.
pageoptionalintegerPage 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
contentrequiredstringThe text content of the post.
platformsoptionalarrayPlatforms to publish to. Supported values: linkedin, twitter, threads, bluesky, substack, tiktok, youtube, instagram.
scheduled_atoptionalstring (ISO 8601)Stores an intended publish time on the draft for reference. This does not schedule the post for publishing. To schedule, call publish with scheduled_at in the body.
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
contentoptionalstringNew text content for the post.
platformsoptionalarrayReplaces the current platform selection.
scheduled_atoptionalstring (ISO 8601)Update the stored intended publish time on the draft. As with create, this stores a reference time only; it does not schedule the post.
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 or schedule a draft

POST/drafts/:id/publish

Publish a draft immediately, or schedule it for a future time, to all selected platforms. The draft must have at least one platform set; update it first if needed.

With no body (or no scheduled_at), the post publishes immediately: status changes to posting, platform jobs are enqueued, and the status transitions to posted (or failed) once complete.

With scheduled_at in the body, the post is scheduled for that time instead: status becomes scheduled, no posting jobs run until the scheduled time arrives. The time must be in the future and parseable as ISO 8601.

Body parameters

ParameterTypeDescription
scheduled_atoptionalstring (ISO 8601)If present and in the future, schedules the post for that time instead of publishing immediately. Past or malformed values return 422.
platform_account_idsoptionalobjectMap of platform to connected-account id (e.g. {"twitter": 42}) when an account has multiple connections for a platform. Each id must belong to the current account and match the platform.
Immediate publishing is irreversible. Scheduled posts can still be edited or removed before their scheduled time.

Publish immediately:

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

Schedule for later:

curl -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scheduled_at": "2026-04-28T09:00:00-07:00"}' \
  "https://app.demandbird.com/api/v1/accounts/42/drafts/124/publish"

Response (immediate):

{
  "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"
}

Response (scheduled):

{
  "draft": {
    "id": 124,
    "status": "scheduled",
    "content": "Updated copy, cleaner and tighter.",
    "platforms": ["linkedin"],
    "scheduled_at": "2026-04-28T16:00:00Z",
    "published_at": null,
    "created_at": "2026-03-02T10:30:00Z",
    "updated_at": "2026-03-02T11:00:00Z"
  },
  "message": "Post scheduled for linkedin"
}

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

Delete a post

DELETE/drafts/:id

Remove a post by ID. Works for any status: drafts, scheduled, posted, or failed. Returns 204 No Content on success.

For posts already published to a platform: this removes the DemandBird record (and its analytics linkage); the live copy on the platform stays put. To take down the live post, delete it on the platform itself.
curl -X DELETE \
  -H "Authorization: Bearer YOUR_TOKEN" \
  "https://app.demandbird.com/api/v1/accounts/42/drafts/124"

Empty 204 response on success. 404 if the post doesn't exist or belongs to a different account.

List published posts

GET/posts

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

Query parameters

ParameterTypeDescription
sinceoptionalstring (ISO 8601)Return posts updated at or after this timestamp.
untiloptionalstring (ISO 8601)Return posts updated at or before this timestamp.
pageoptionalintegerPage 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
idintegerUnique identifier.
statusstringdraft · scheduled · posting · posted · failed
contentstringPlain-text content of the post.
platformsarray of stringsSelected publish targets.
scheduled_atstring or nullISO 8601 timestamp if a schedule was set.
published_atstring or nullISO 8601 timestamp when the post was confirmed published. null until posted.
created_atstringISO 8601 creation timestamp.
updated_atstringISO 8601 last-updated timestamp.