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_TOKENBase 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
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
Returns a paginated list of drafts for the account. Defaults to draft and scheduled status.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| 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
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
Create a new draft. The post is saved with draft status and is not published until you call the publish endpoint.
Request body (JSON)
| Field | Type | Description |
|---|---|---|
| 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) | 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
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)
| Field | Type | Description |
|---|---|---|
| contentoptional | string | New text content for the post. |
| platformsoptional | array | Replaces the current platform selection. |
| scheduled_atoptional | string (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
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
| Parameter | Type | Description |
|---|---|---|
| scheduled_atoptional | string (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_idsoptional | object | Map 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. |
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
Remove a post by ID. Works for any status: drafts, scheduled, posted, or failed. Returns 204 No Content on success.
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
Returns a paginated list of posts with posted status, ordered by most-recently-updated. Filter by date range with sinceand until.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| 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
| Field | Type | Description |
|---|---|---|
| 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. |