X scheduled broadcast API: one stream at a time
X documents recurring livestream schedules with a worked example. The live API contract says those requests are rejected. What the six endpoints actually accept in 2026.
On this page · 10 sections
- Quick answer
- Six endpoints
- Recurring schedules are documented and rejected
- Two ids, and the path only takes one
- The update is a full replacement
- chat_option is a string here and an integer next door
- The SDK types the retired fields
- Auto-publish, manual publish, and what breaks go-live
- No price, no rate-limit row
- What to build instead of a recurring schedule
The short version
- ▸Recurring schedules are documented and rejected. X's create page still shows a Daily or Weekly recurrence block with a worked example, while the live API contract at version 2.174 says requests including recurrence will be rejected and that each occurrence must be its own scheduled broadcast.
- ▸Every scheduled broadcast has two ids. The alphanumeric broadcast_id goes in the URL path; the numeric scheduled_broadcast_id is required in the body of an update. Save both at create time, because nothing lets you derive one from the other.
- ▸The update is a full replacement despite being a PUT with mostly optional fields. Omit a previously set field and it can be cleared, and the body must always carry scheduled_start_ms and scheduled_end_ms even when you only want to change a title.
- ▸chat_option is a numeric string here and a JSON integer on POST /2/broadcasts. Both bodies reject unknown fields, so a payload copied between the two routes fails on a type, not on a name.
- ▸The official Python SDK at 0.10.6 makes scheduled_end_ms mandatory when the API treats it as optional, still types recurrence, roll_forward and telecast_id, and defines source_id as the source's stream key, which stops being accepted on 1 January 2027.
Quick answer
You can book a live broadcast on X in advance with six routes under /2/broadcasts/scheduled, but you can no longer book a series. X's documentation still shows a recurrence object with a Daily or Weekly frequency and a worked weekly example; the live API contract at version 2.174 says requests including recurrence are rejected and that each occurrence must be created on its own. The two sources have disagreed in public for long enough that the only safe reading is the machine one.
Last updated: November 2026
Checked against the live API description at api.x.com/2/openapi.json, version 2.174, X's hand-written scheduling pages, X's own SKILL.md for the Livestream API, and the official Python SDK at 0.10.6, all read on 9 November 2026.
Six endpoints
| Action | Route |
|---|---|
| Create | POST /2/broadcasts/scheduled |
| List | GET /2/broadcasts/scheduled |
| Get | GET /2/broadcasts/scheduled/:id |
| Update | PUT /2/broadcasts/scheduled/:id |
| Delete | DELETE /2/broadcasts/scheduled/:id |
| Publish now | POST /2/broadcasts/scheduled/:id/live |
A schedule always binds to a stream source, so a source has to exist first. Reads need broadcast.read; create, update, delete and go-live need broadcast.write as well. OAuth 1.0a still works, and there the split is by app permission level rather than scope: read-only apps can list and get, and anything that changes state needs a Read and write app.
Unlike the broadcast and source routes, these six are not being removed. The 5 November 2026 rewrite of the Livestream API left them in place, with one change: source_id now means the source's new public id rather than its stream key. The whole of that migration, including why the old source id was a credential, is in our notes on the X Livestream API.
One detail that bites in JavaScript: every timestamp and numeric id here is a decimal string, deliberately, to survive a JSON parser. Keep them as strings end to end.
Recurring schedules are documented and rejected
Read X's create page today and recurrence looks like a supported feature with a shape you can copy:
{
"source_id": "9123456789012345678",
"scheduled_start_ms": "1784000000000",
"scheduled_end_ms": "1784003600000",
"title": "Weekly livestream",
"recurrence": {
"frequency": "Weekly",
"repeats": "4"
}
}
The page explains that the response returns the first occurrence and that you list the rest. The overview page opens by saying the API "supports one-time and recurring schedules". The update page tells you to set roll_forward to true to shift the head of an existing series.
Now read the contract the server is built from. In the live description, the same recurrence field carries this: recurring schedules are being retired, requests that include recurrence will be rejected, and each occurrence should be created as its own scheduled broadcast. roll_forward says true will be rejected. recurring_schedule_id is "always null for new schedules". telecast_id, also documented as a real option on the create page, is marked ignored.
X's own SKILL.md for this API, published on the same docs site for agents to read, sides with the contract in one line: recurrence and roll_forward are retired, schedule each occurrence separately.
So three of X's four published surfaces agree that this is gone, and the fourth is the one a human reads first.
The failure mode is loud rather than silent, which is the one piece of luck here. CreateScheduledBroadcastRequest sets additionalProperties: false, so a stale recurrence block is not quietly dropped. It comes back as a 400. Verify against the live description rather than the prose whenever the two could disagree, because this is not a documentation typo. It is a retired feature with a live example still sitting on the page.
Two ids, and the path only takes one
Create returns both, and they are not interchangeable:
| Id | Shape | Where it goes |
|---|---|---|
broadcast_id | alphanumeric, for example 1DxLddgepQbxm | the :id in every path |
scheduled_broadcast_id | numeric string, for example 2075599796786561024 | required in the body of an update |
Nothing converts one into the other, and nothing accepts the wrong one gracefully. X's docs say it twice, once as a tip on the create page and once in the overview: save both values. The SDK's internal naming is a small hint about why they differ, since it labels broadcast_id an "alphanumeric UBS broadcast id", pointing at a separate broadcast service behind the scheduler.
Store them as a pair on your side from the first response. Retrieving a schedule later gives you both, so this is recoverable, but only if you still know which schedule you are looking for.
The update is a full replacement
PUT /2/broadcasts/scheduled/:id looks like a partial update. Three fields are required and nine are optional, which in most APIs means you send what you want to change.
Not here. X's own warning on the update page is explicit: this is a full replacement, so read the current broadcast, change what you need, and resend every field that should remain set. Omitting a previously set optional field can clear it. The error table backs it up with a named case, "incomplete full-replacement update", answered with 400.
Two practical consequences. First, renaming a scheduled stream requires scheduled_broadcast_id, scheduled_start_ms and scheduled_end_ms in the body alongside the new title, because all three are mandatory on every update. Second, any update path in your code needs a read immediately before it. A write-only update function will eventually erase a description or a thumbnail that someone set elsewhere.
Worth noting that X's SKILL.md describes the same endpoint as partial, with those three fields required. The hand-written page and the error table are the stricter reading, and the stricter reading is the safe one to build against.
chat_option is a string here and an integer next door
Both livestream create routes take the same seven chat settings. They disagree about the type.
| Route | chat_option |
|---|---|
POST /2/broadcasts/scheduled | numeric string, for example "2" |
POST /2/broadcasts | JSON integer from 0 to 6 |
The values mean the same thing in both places: 0 default, 1 chat off, 2 everyone, 3 verified accounts, 4 accounts the host follows, 5 the host's subscribers, 6 accounts followed by an account the host follows.
Since both request bodies reject unknown fields, you cannot even defend yourself by sending both forms. A payload shared between a schedule and an immediate go-live has to be rewritten, not reused, and the thing that breaks is a quoted digit.
The SDK types the retired fields
The official Python SDK is further from this API than its module list suggests. At 0.10.6, uploaded 25 July 2026 and still current, xdk.broadcasts covers the scheduled routes well: create, get, update, delete, list and go-live are all generated. Then the models drift:
scheduled_end_msis a required field. The API and the docs both treat it as optional, which is how you get an open-ended broadcast. Through the SDK, you cannot schedule one.recurrenceis fully modelled, down tofrequency: Literal["Daily", "Weekly"]. The server rejects it.roll_forwardis typed and described as shifting the head of a recurring series. The server rejects it when true.telecast_idis typed and described as a real association. The server ignores it.source_idis described as "Ingest / source id to bind (same as sourcesrtmp_stream_key)". That meaning expires on 1 January 2027.- Models are configured with
extra="allow"while the spec setsadditionalProperties: false, so the client will happily serialise a field the server rejects.
The last one is the pattern that matters: a type-checked, SDK-validated, schema-plausible request can still be a 400. The wheel also reports __version__ = "0.1.0" internally while PyPI serves it as 0.10.6, which tells you how much of this is generated and unreviewed. Read the published package rather than the reference pages, a habit we go into in X API Python.
Auto-publish, manual publish, and what breaks go-live
Two flows, chosen at create time by one boolean.
Leave manual_publish out, or set it false, and X's scheduler publishes at scheduled_start_ms. Your encoder has to be pushing video to the source before that instant, because the scheduler will not wait for you.
Set it true and the schedule sits there until you call POST /2/broadcasts/scheduled/:id/live. Video must already be flowing when you make that call; the source has to be active for the broadcast to publish. X's troubleshooting list for a failed go-live is short and in a useful order: confirm manual_publish is actually true, confirm the source id, confirm RTMP video is reaching the source, confirm the broadcast has not already ended or been deleted. A go-live request against a schedule that was not configured for manual publishing is a documented 400.
After publishing, poll the get endpoint and watch state. The live description gives five values, Created or Scheduled before the start, then Running, Ended or Error; the overview page lists only four and omits Scheduled. Handle five.
Eligibility is upstream of all of it. A protected account cannot broadcast at all, and live video on X has been gated behind a paid subscription in the app since mid-2024, which how to go live on X covers along with the Live Studio control room that does scheduling without any of this. If the account cannot go live by hand, no amount of correct JSON will help.
No price, no rate-limit row
The pay-per-use rate card is detailed to the tenth of a cent. It has twelve named read resources and fifteen named write actions, and not one of them is a broadcast, a source or a schedule. The rate-limit reference carries fourteen per-namespace tables plus an eleven-row catch-all, and no /2/broadcasts route appears in any of them.
That leaves one published number for the entire Livestream API, from the docs prose: 450 requests per 15 minutes per user on the source routes and on the one-call go-live route. Nothing states a ceiling for the scheduling routes specifically.
If you are budgeting, measure. Run one schedule through create, update and delete, and read your credit balance before and after, because guessing from the nearest row on the card is a 40-fold spread. How the rest of the card bills, and what the 24-hour deduplication window does, is in X API pricing, and the namespaces that do publish limits are in X API rate limits.
What to build instead of a recurring schedule
Since recurrence is gone, repetition becomes your job. The shape that survives:
- Keep the series definition in your own store: source id, weekday, time, duration, title template.
- A few days out, create one scheduled broadcast per occurrence, and keep both ids it returns.
- Reconcile with
GET /2/broadcasts/scheduledrather than trusting your own record, so a schedule deleted in the app does not get published twice. - Treat updates as read-then-write, always, given the full-replacement rule.
- Pass the source id as
source_id, never the stream key, whatever the SDK's field description says.
This is more code than {"frequency": "Weekly", "repeats": "4"}, and it is the only version that works today.
A last piece of perspective. Booking a livestream through an API is worth it when the stream is the product: a weekly show with a crew, a match feed, an event. For most accounts the thing being scheduled is not video at all but ordinary posts, and that is a much better served problem, with free routes covered in how to schedule posts on X. X-Autopilot takes the same idea further by running on your own Mac in your own browser session, so there is no shared cloud IP pool and no delegate account in the path. That is a smaller detection surface than a hosted scheduler, not an absent one, and automating engagement through a browser sits in a gray area under X's automation rules rather than being something X sanctions. The scheduling API is sanctioned. It also now expects you to book every single week by hand.
Frequently asked
Answers indexed by Google + AI assistants.
What is the X scheduled broadcast API?+
Six routes under /2/broadcasts/scheduled that let you book a live video broadcast on X in advance, bound to one of your stream sources. You create the schedule, X either publishes it automatically at its start time or waits for an explicit go-live call, and you can list, read, update and delete it in the meantime. It is part of the Livestream API and needs OAuth 2.0 user context with broadcast.read for reads and broadcast.write as well for anything that changes state.
Can you schedule a recurring livestream through the X API?+
Not any more, despite the documentation. X's create page describes a recurrence object with a frequency of Daily or Weekly and a repeats count, and shows a full weekly example. The live API description at version 2.174 says the opposite: recurring schedules are being retired, requests that include recurrence will be rejected, roll_forward set to true will be rejected, and recurring_schedule_id is always null for new schedules. X's own agent-facing SKILL.md for the Livestream API agrees with the contract and tells you to schedule each occurrence separately. Treat recurrence as gone and build the repetition on your side.
Why does my X scheduled broadcast update return 400?+
Three likely causes. The path takes the alphanumeric broadcast_id while the body requires the numeric scheduled_broadcast_id, and swapping them is the classic failure. The update is also a full replacement: scheduled_start_ms and scheduled_end_ms are both required in every update body, and X's docs say an update reporting incomplete broadcast data means you should read the current object and resend every field that should stay set. Finally, the request body rejects unknown fields outright, so a stale recurrence or roll_forward value is a 400 rather than something silently ignored.
What is the difference between manual_publish true and false?+
With manual_publish omitted or false, X's scheduler publishes the broadcast at scheduled_start_ms, so your encoder needs to be pushing video before that moment arrives. With manual_publish true, nothing happens at the scheduled time until you call POST /2/broadcasts/scheduled/:id/live. That call needs video already flowing to the source, and X returns 400 if you send it to a schedule that was not created for manual publishing.
How long can a scheduled X broadcast be?+
The start time must be in the future, and X's SKILL.md puts the duration between one minute and 24 hours. You can also leave it open-ended: omit scheduled_end_ms on create, or send it as the string 0, and the broadcast runs until it is stopped. The official Python SDK does not allow that, because it marks scheduled_end_ms as a required field.
Does scheduling a broadcast cost X API credits?+
Nothing published says. The pay-per-use rate card names twelve read resources and fifteen write actions, and none of them is a broadcast, a source or a schedule. The rate-limit reference has fourteen per-namespace tables plus a catch-all and lists no /2/broadcasts route in any of them. The only published ceiling for the Livestream API is 450 requests per 15 minutes per user on the source routes and on the one-call go-live route.
- X developer docs, 'Livestream Scheduling API' overview: the six endpoints, the scheduled broadcast object with recurring_schedule_id and telecast_id, the Created, Running, Ended and Error states, the two-id warning, the stream key deprecation notice dated 1 January 2027, and the error table (accessed 9 November 2026)
- X developer docs, 'Create a Scheduled Broadcast': the required source_id and scheduled_start_ms, the optional field list including recurrence and telecast_id, and the worked recurring example with frequency Weekly and repeats 4 (accessed 9 November 2026)
- X developer docs, 'Update a Scheduled Broadcast': the full-replacement warning, the alphanumeric path id versus the numeric body id, and the instruction to set roll_forward true to shift the head of a recurring series (accessed 9 November 2026)
- X live OpenAPI description at api.x.com/2/openapi.json, version 2.174: CreateScheduledBroadcastRequest with additionalProperties false, required source_id and scheduled_start_ms only, chat_option typed as a numeric string, recurrence described as deprecated and rejected, roll_forward rejected when true, telecast_id ignored, recurring_schedule_id always null, and CreateBroadcastRequest typing chat_option as an integer 0 to 6 (accessed 9 November 2026)
- X developer docs, 'Livestream API SKILL.md': the one-minute-to-24-hour duration range, the open-ended scheduled_end_ms value of 0, the statement that recurrence and roll_forward are retired and each occurrence should be scheduled separately, and the 450 per 15 minutes per user rate limit (accessed 9 November 2026)
- Official X Python SDK, xdk 0.10.6 on PyPI, uploaded 25 July 2026: CreateScheduledBroadcastRequest with scheduled_end_ms required, a CreateScheduledBroadcastRecurrence model typed Literal Daily or Weekly, roll_forward described as shifting the head of a recurring series, source_id described as the same value as a source's rtmp_stream_key, models configured with extra allow against the spec's additionalProperties false, and __version__ reported as 0.1.0 inside the wheel (downloaded and inspected 9 November 2026)
- X developer changelog, 'Livestream API: new /2/sources and /2/broadcasts routes', 5 November 2026: the scheduled routes survive, but passing a stream key as source_id stops working on 1 January 2027 (accessed 9 November 2026)
- X developer docs, 'Pricing' and 'X API Rate Limits': the complete pay-per-use tables and the fourteen per-namespace rate-limit tables, neither of which contains a broadcast, source or livestream row (accessed 9 November 2026)
Product designer and indie hacker. Runs the agent on his own X account every day and writes up what the data shows, including when it's inconvenient.
Follow on X →