7-day offer$199$9
X-Autopilot

X Livestream API: your source id was the stream key

The X Livestream API moved to /2/sources and /2/broadcasts on 5 November 2026. What the new routes change, what returns 404 on 1 January 2027, and what the SDK still lacks.

X-Autopilot Team··11 min read
On this page · 10 sections

The short version

  • ▸On the old routes a stream source's id was its stream key. The same secret identified the source and let anyone publish video to it, and it came back in every source response. The new /2/sources routes split them: a 19-digit public source id, and a stream key returned only twice.
  • ▸POST /2/broadcasts replaces create-then-publish with one call. It returns 201 for a new broadcast and 200 with the existing one if the source is already live, so a retry after a lost response cannot double-publish you.
  • ▸The deprecated /2/users/:user_id/sources and /2/users/:user_id/broadcasts routes return 404 from 1 January 2027. Passing a stream key as source_id to the scheduled routes stops working the same day.
  • ▸The live API description at version 2.174 does not mark a single one of those routes deprecated, so code generators and type checkers will not warn you. The deadline exists only in prose.
  • ▸The official Python SDK at 0.10.6 has no /2/sources methods and no go-live, list, end or delete for broadcasts. Neither the old routes nor the new ones are in it, and nothing on the rate card prices any of this.

Quick answer

The X Livestream API now lives at /2/sources and /2/broadcasts, shipped 5 November 2026. The headline change is not the shorter paths: on the original routes a stream source's id was its stream key, so the value you used to identify a source was the credential that let anyone publish video to it. The new routes split them into a public 19-digit source id and a stream key returned only twice. The old /2/users/:user_id/... routes keep working until 1 January 2027, then return 404.

Last updated: November 2026

Everything below is checked against the live API description at api.x.com/2/openapi.json, version 2.174, and the official Python SDK at 0.10.6, both read on 9 November 2026.

What shipped on 5 November 2026

Six source routes and five broadcast routes, mapping one-to-one onto the old set with one addition and one merge.

Old routeNew route
POST /2/users/:user_id/sourcesPOST /2/sources
GET /2/users/:user_id/sourcesGET /2/sources
GET /2/users/:user_id/sources/:source_idGET /2/sources/:id
PUT /2/users/:user_id/sources/:source_idPUT /2/sources/:id
DELETE /2/users/:user_id/sources/:source_idDELETE /2/sources/:id
the source's idGET /2/sources/:id/stream_key (new)
create, then PUT .../state with PUBLISHPOST /2/broadcasts (one call)
GET /2/users/:user_id/broadcastsGET /2/broadcasts
GET /2/users/:user_id/broadcasts/:broadcast_idGET /2/broadcasts/:id
PUT .../state with ENDPUT /2/broadcasts/:id/state
DELETE /2/users/:user_id/broadcasts/:broadcast_idDELETE /2/broadcasts/:id

GET /2/region is untouched. Authentication is untouched too: OAuth 2.0 user context with broadcast.read and broadcast.write, or OAuth 1.0a. Which scope belongs on which call is the usual X puzzle, and the pattern across the v2 surface is in our notes on X API OAuth. One asymmetry here is worth flagging: GET /2/sources/:id/stream_key is a GET that requires broadcast.write, so a read-only token cannot fetch a key.

This whole namespace is also oddly hidden. It is absent from docs.x.com/llms.txt and from the X API v2 index at docs.x.com/x-api/llms.txt, the two files X publishes so that agents can discover its documentation. The changelog entry and a direct URL are the only ways in. There is no developer-forum announcement either, which puts this launch in the same quiet category as the Articles endpoints.

The old source id was the stream key

This is the part worth acting on today rather than in December.

X's own API description, read live, says it in two places. On the old source object, id is "Source id; also the RTMP stream key", and rtmp_stream_key is "Stream key; equal to id". One value, two jobs: the handle you pass around in paths, logs and dashboards, and the secret that authorises publishing video to your account's ingest point.

New routes separate them:

ValueOld routesNew routes
Source idid, equal to the stream keyid, 19 decimal digits, not a secret
Stream keyid and rtmp_stream_key, in every responsestream_key, only from POST /2/sources and GET /2/sources/:id/stream_key

Two consequences follow, and only one is in the migration guide.

The documented one: new routes reject a stream key where a source id belongs, with 400 and the detail "The id value is not a source id." Your value is not echoed back, which is the right call for a credential.

Then the one you have to work out yourself. Existing sources keep their stream keys, which X says plainly as reassurance that configured encoders will not break. Read it the other way and it is a rotation problem: if an old source id reached a log aggregator, an error tracker, a support ticket or a browser URL, that key is still live. Splitting the fields protects what you create from now on and does nothing for what is already sitting in a search index you pay for. Call GET /2/sources, work out which sources had their old id handled carelessly, and replace those rather than assuming the change was retroactive.

One call to go live, and two ways it succeeds

Until last week the flow created a broadcast in NOT_STARTED, then flipped it with PUT .../state and {"state":"PUBLISH"}. POST /2/broadcasts does both:

{
  "source_id": "9123456789012345678",
  "title": "Launch stream",
  "locale": "en",
  "chat_option": 2,
  "should_not_tweet": false
}

What comes back is the broadcast, already RUNNING. Its status code carries the meaning:

  • 201 means a new broadcast exists.
  • 200 means the source was already live, and you are getting that broadcast back unchanged. Your title, locale, chat_option and should_not_tweet are ignored.

That makes the call safe to retry, which matters because the old pair was not. Lose the create response to a timeout and you could not tell whether a draft existed. Here a retry either creates or returns.

Three timing rules sit around it. The broadcast goes public before your encoder sends anything, and X ends it with state TIMED_OUT if no video arrives within five minutes, so start the encoder next to the call rather than before the kettle boils. If a source cannot accept video until its encoder connects, the call answers 409 conflict, and the fix is to wait for is_stream_active to turn true on GET /2/sources/:id. If another broadcast is already live on the source, you also get 409, and that one does not clear on its own.

There is no NOT_STARTED state any more, so the old seven-day cleanup of never-published broadcasts has nothing to clean. Ending is PUT /2/broadcasts/:id/state with {"state":"END"}, and PUBLISH is no longer an accepted value there at all.

Strict bodies, a data wrapper, renamed fields

Everything about the new routes is tighter, which is good engineering and a migration tax.

POST /2/broadcasts sets additionalProperties: false in the live spec. chat_option must be a JSON integer from 0 to 6, where the old PUBLISH accepted "2", and should_not_tweet must be a real boolean, where the old one took "true" and 1. region and is_low_latency, which the old create route accepted and ignored, now produce a 400.

Responses move into data, as in the rest of v2. Lists gain meta.result_count and a flat meta.next_token that you feed back as pagination_token, replacing a next_token object that held an offset. GET /2/broadcasts also takes max_results up to 100 and quietly accepts an ids parameter the migration guide never mentions.

Renames are few and awkwardly placed:

Old fieldNew field
status (the title)title
rtmp_regionregion
creation_time (number)created_at_ms (string)
media_idgone: take the digits after the last _ in media_key
owner_id, is360, video_service, creation_originnot returned
not presentsource_id, via broadcast.fields=source_id

Losing owner_id is a mercy. X's own description of it warns that the value "exceeds the exact integer range of many JSON parsers", which is a polite way of saying JavaScript has been silently corrupting it since the day it shipped.

Watch the source_id row. It is new, it is not returned by default, and broadcast.fields only exists on the two GET routes. Go-live has no broadcast.fields parameter in the spec, so an audit trail built from the create response alone will never record which source went live.

The errors changed, including the one you will hit most

Old routes answered with { "error_type", "message" }. New routes use the standard problem format, keyed on type.

CaseOldNew
Unknown or someone else's source404 notFound404 resource-not-found
Stream key where a source id goesdid not exist400 invalid-request
Account cannot go live412 preconditionFailed403 about:blank
Source busy or not yet ingestingdid not exist409 conflict
Temporary failure503 serviceUnavailable503 about:blank

Row three is the regression. "This account is not allowed to go live" is the most common first-run failure on any broadcast API, and the old route at least gave it a name you could grep. Now it is a 403 with type: about:blank, and X's own table says that same 403 also covers "the broadcast was moderated": two unrelated causes, one blank response. A separate 403 with missing-scopes does exist, so a token problem stays distinguishable from an account problem.

Check the account in the product before spending an afternoon on it. Live video on X has been gated behind a paid subscription in the app since mid-2024, and these docs only say the account "must have permission to go live" without naming a tier. Our walkthrough of how to go live on X covers which plans unlock the button. Protected accounts are excluded outright.

The spec never says deprecated, and the SDK has neither route set

Two gaps make this migration quieter than it should be.

First, the live API description does not flag any of it. Read api.x.com/2/openapi.json at version 2.174 and every one of the ten /2/users/:id/... source and broadcast operations has no deprecated flag. OpenAPI has a field for exactly this, and generated clients surface it as a compiler or linter warning. Here they will not. The deadline lives in a changelog entry, a migration page and a callout, which is to say it lives in prose your build never reads.

Second, the official Python SDK cannot help either way. At 0.10.6, uploaded 25 July 2026 and still current, xdk.broadcasts exposes the scheduled-broadcast methods and send_broadcast_chat, and nothing else: no sources module, and no go-live, list, get, end or delete for live broadcasts. Searching the published wheel for /2/sources returns no route at all, only schema text that still defines a scheduled broadcast's source_id as "same as sources rtmp_stream_key", the deprecated meaning. The SDK documents an expiring semantic for a field with seven weeks left. That pattern of generated client against hand-written page is in our notes on the official X API Python SDK.

Practically: write these calls by hand. Eleven routes, one auth header, standard problem errors.

No price, no rate-limit row

The pay-per-use rate card is specific down to $0.001 per block event. It names twelve read resources and fifteen write actions. Not one of them is a broadcast, a source or a livestream.

Rate limits tell the same story. Fourteen per-namespace tables, including two-endpoint namespaces such as Trends and Communities, plus an "Other endpoints" catch-all holding eleven leftovers. No Livestream section, and no /2/sources or /2/broadcasts row in the catch-all either. The only published number is in the prose of the docs pages: 450 requests per 15 minutes per user on each source route and on POST /2/broadcasts.

So going live has a documented frequency ceiling and an undocumented price. If you are modelling the cost of anything that touches this API, start from how the rest of the card bills in X API pricing, then measure your credit balance across one real broadcast, because inference from neighbouring rows will not get you there. The namespaces that do publish ceilings are collected in X API rate limits.

One disambiguation, since the names collide. This is video ingest. The Filtered Stream endpoint, which delivers Posts over a long-lived connection and bills per Post read, is a different product with a different budget, covered in X API stream.

What to do before 1 January 2027

A short list, in the order that limits damage:

  1. Call GET /2/sources and store each source's new 19-digit id. Use it everywhere you previously used the stream key as an identifier.
  2. Decide which old source ids have leaked into logs, URLs or third-party tools, and recreate those sources. Keys you already exposed are still valid.
  3. Move source management to /2/sources, sending region on create, since the old route defaulted to US West when you omitted it and the new one requires it.
  4. Replace create-plus-publish with POST /2/broadcasts, handling 201, 200 and both 409 cases, and switch reads to /2/broadcasts with data, title and meta.next_token.
  5. Pass the source id, not the stream key, as source_id when you create or update scheduled broadcasts, and branch on problem type instead of error_type.

Both route sets act on the same objects until the deadline, so this is a gradual migration rather than a cutover. You can end a broadcast created the old way through the new route, and every source you made the old way appears in GET /2/sources with a fresh id.

Who this API is for

Anyone running real live video on X from their own stack: a studio pushing a weekly show, a sports feed, an events platform. Eleven routes, an RTMPS URL and an encoder, all fully sanctioned by X. If that is the job, this is the right door and it got meaningfully better this month.

If what you actually want is an account that stays active while you are away from the keyboard, live video is a strange place to start, because a broadcast needs a human, a camera and an audience that turns up at a set hour. X-Autopilot works the opposite problem: posting and replying from 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 driving engagement through a browser sits in a gray area under X's automation rules rather than being something X endorses.

Frequently asked

Answers indexed by Google + AI assistants.

What is the X Livestream API?+

A set of X API v2 routes for running live video on X from your own software. You create a persistent RTMPS ingest point called a stream source, push video to it from an encoder such as ffmpeg or OBS, then take that source live as a broadcast. As of 5 November 2026 the current routes are /2/sources for ingest points and /2/broadcasts for live sessions, plus /2/broadcasts/scheduled for scheduling and /2/broadcasts/:id/chat for live chat. It is a different thing from the Filtered Stream data endpoint, which streams Posts rather than video.

What changed in the X Livestream API on 5 November 2026?+

X added the /2/sources and /2/broadcasts routes and deprecated the original /2/users/:user_id/sources and /2/users/:user_id/broadcasts routes. The new routes take the owner from the access token instead of the URL, wrap every result in data, return errors in the standard X API problem format, and separate a source's public id from its secret stream key. POST /2/broadcasts also collapses the old create-then-publish pair into one call that returns a broadcast already RUNNING.

When do the old X broadcast routes stop working?+

1 January 2027. After that date requests to /2/users/:user_id/sources, /2/users/:user_id/broadcasts and their sub-routes return 404. The scheduled broadcast routes are not being removed, but they stop accepting a stream key in place of a source id on the same date. Both route sets act on the same sources and broadcasts until then, so you can migrate one call at a time rather than in one cutover.

Why was the old X stream source id a security problem?+

Because it was the credential. X's own API description says the old source id is also the RTMP stream key, and that rtmp_stream_key is equal to id. Anyone holding it can publish video to that source, and it was returned in every list and get response, which is exactly the kind of value that ends up in logs, analytics events and URLs. The new routes return the stream key only from POST /2/sources and GET /2/sources/:id/stream_key. Existing sources keep their keys, so a key you logged before the change is still live until you rotate the source.

Does the X Livestream API require X Premium?+

X's developer docs only say the broadcasting account must have permission to go live, and that protected accounts cannot create broadcasts. They do not name a subscription tier. Live video on X has been a paid product in the app since July 2024, and POST /2/broadcasts answers an ineligible account with a bare 403 that carries no explanation, so confirm the account can go live in the app before you debug your request.

How much does the X Livestream API cost per broadcast?+

Nothing published says. The pay-per-use rate card lists twelve read resources and fifteen write actions by name, and none of them is a broadcast, a source or a livestream. The rate-limit reference has fourteen per-namespace tables plus a catch-all, and Livestream appears in none of them. The only published limit is in prose: 450 requests per 15 minutes per user on the source routes and on POST /2/broadcasts.

Sources
Related searches
x livestream api/2/broadcasts go livex api stream sourcex livestream api migrationx api rtmp stream keytwitter livestream api 2026x broadcast api deprecated routesx api go live endpointhow to go live on xx api pricingx api oauth scopesx api rate limitsrtmps ingest urlx live studiox api v2 endpoints
DY
Deepak YadavBuilding X-Autopilot

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 →
Try X-Autopilot.
7-day offer: $199 $9 once. No subscription, no monthly bill. Real Chrome on your Mac.
See pricing▶
Free PDF · the X Growth Playbook

The exact playbook we use to grow on X

The bio that converts, the daily reply loop, the posting cadence, and the tool stack, in one no-fluff PDF. Drop your email and it's yours.

No spam. Unsubscribe anytime.

Free tools

Try it yourself.

All free X tools →
Keep reading

Related posts.