Publishing to social accounts
Once a video has rendered, POST /contents/upload/{content_id} dispatches it
to your connected accounts.
Scope: upload.
The one mistake that looks like success
account_ids is a list of objects, not strings{ "account_ids": ["8f14e45f-..."] } // WRONG — publishes nothing
{ "account_ids": [{"id": "8f14e45f-..."}] } // right
The handler reads a.get("id") on each element. A bare string has no id, so
it resolves to no accounts selected — and the request is accepted, the
status moves, and nothing is ever published. There is no error to catch.
If a publish appears to succeed but nothing appears on the platform, check this first.
1. Find your account ids
curl -s https://api.botlobby.ai/v1/accounts \
-H "Authorization: Bearer $BOTLOBBY_API_KEY"
{
"page": 1,
"total_pages": 1,
"total_items": 2,
"items": [
{
"id": "8f14e45f-ceea-467a-9fbb-9f0a1c2d3e4b",
"name": "Main channel",
"platform": "YouTube",
"platform_acronym": "YT"
}
]
}
Filter by platform if you like — but casing matters. The values are
YouTube, TikTok, Instagram, compared literally. youtube matches nothing:
curl -s "https://api.botlobby.ai/v1/accounts?platform=YouTube" \
-H "Authorization: Bearer $BOTLOBBY_API_KEY"
2. Publish
curl -s -X POST https://api.botlobby.ai/v1/contents/upload/CONTENT_ID \
-H "Authorization: Bearer $BOTLOBBY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_ids": [{"id": "8f14e45f-ceea-467a-9fbb-9f0a1c2d3e4b"}],
"madeForKids": "no",
"ageRestricted": "no",
"generate_thumbnail": true
}'
You can pass several accounts in one call to publish the same video to multiple platforms.
Options
| Field | Notes |
|---|---|
madeForKids | "yes" / "no" strings, not booleans. COPPA |
ageRestricted | "yes" / "no". YouTube 18+ |
generate_thumbnail | Apply the AI-generated thumbnail. YouTube only, small credit surcharge |
madeForKids and ageRestricted cannot both be "yes" — that returns
400. A video cannot be for children and age-restricted simultaneously.
When generate_thumbnail is silently dropped
It is ignored rather than refused — the upload proceeds without it — when:
- your plan does not include custom thumbnails
- no YouTube account is among the selected accounts
- the video is a Short (3 minutes or less)
The Shorts case is the surprising one: YouTube auto-classifies short videos as Shorts and does not apply Data-API thumbnails to that surface, so applying one would do nothing. You are not charged the surcharge in any of these cases.
If the video's duration is unknown, it is treated as a Short — the conservative choice, since skipping a feature beats charging for one that won't appear.
3. Watch it
curl -s https://api.botlobby.ai/v1/gen/task-status/CONTENT_ID \
-H "Authorization: Bearer $BOTLOBBY_API_KEY"
UPLOADING → UPLOADED on success, FAILED_UPLOAD on failure with the reason
in metadata.last_error.
To find where it actually landed, read published_url and
published_platform. Both are null until an upload genuinely succeeded, which
makes published_url a more reliable confirmation than the status alone.
published_url and published_platform come back from GET /contents.
GET /contents/{content_id} returns a different, richer shape that does not
include them.
curl -s "https://api.botlobby.ai/v1/contents?search=Water" -H "Authorization: Bearer $BOTLOBBY_API_KEY"
Duplicate dispatches
A second publish while one is in flight returns 409:
{ "error": "An upload for this content is already in progress." }
This guards against a client that times out locally and retries while the first dispatch is still running server-side — without it, the platform receives the same video twice. It clears once the first dispatch passes the stall threshold, so genuine recovery after a real failure still works.
Don't loop on 409. Poll task-status to find out what actually happened
before dispatching again.
What you can't do from the API
Connecting and disconnecting accounts both live in the web app.
Connecting needs an OAuth browser flow. Disconnecting revokes the grant at YouTube or TikTok — a destructive action outside BotLobby that would need a browser re-authorization to undo, which is why no API key can trigger it.
You can rename a connection with PUT /accounts/{id} (write scope). Only the
display name is editable; the platform and stored credentials are immutable, so
a rename can never re-point a connection at a different identity.