/2/users/:user_id/sources and broadcast routes under /2/users/:user_id/broadcasts are deprecated and will be removed on January 1, 2027. Move to the /2/sources and /2/broadcasts routes before then.
Timeline
Route mapping
Stream sources
GET /2/region is unchanged.
Broadcasts
Both sets of routes work on the same sources and broadcasts, so you can migrate one call at a time. For example, you can end or delete a broadcast created with the original routes by calling
PUT /2/broadcasts/:id/state or DELETE /2/broadcasts/:id with its ID, and every source you created with the original routes is listed by GET /2/sources.
The new routes use the same authentication: user context with OAuth 2.0 (broadcast.read and broadcast.write scopes) or OAuth 1.0a. See Authentication. Rate limits are unchanged: 450 requests per 15 minutes per user for each source route.
What changes
No user ID in the path
The new routes take the owner from the access token. Drop/users/:user_id from the path; :source_id and :broadcast_id become :id.
Source id and stream key are separate
On the original routes, a source’sid is its stream key (id == rtmp_stream_key), so the same secret value identifies the source and lets anyone stream to it. The new routes separate them:
To migrate:
- Call
GET /2/sourcesto get the source id of each of your sources, and store it in place of the stream key wherever you identify a source. - Use the source id in
/2/sources/:idpaths and assource_idinPOST /2/broadcastsand the scheduled broadcast routes. - When you need to configure an encoder, read the key with
GET /2/sources/:id/stream_key. Store it as a secret, and keep it out of logs and URLs.
400 with type https://api.x.com/2/problems/invalid-request: The `id` value is not a source id. for /2/sources/:id, and The `source_id` value is not a source id. for POST /2/broadcasts. The value you sent isn’t echoed back in the error. An unknown source id, or another user’s source, returns 404 with type https://api.x.com/2/problems/resource-not-found.
Source fields
name, rtmp_url, rtmps_url and is_stream_active keep their names and formats.
POST /2/sources requires region (the original route defaulted to US West when it was omitted), and returns 201 instead of 200.
One call to go live
Instead of creating a broadcast (NOT_STARTED) and then publishing it, call POST /2/broadcasts with the source id as source_id and the fields you used to send with PUBLISH (title, locale, chat_option, should_not_tweet). The broadcast is RUNNING when the call returns.
- Source id:
source_idis the source id fromGET /2/sources, not the stream key that the original create route took. - Video timing: the broadcast goes live even before your encoder sends video, but it is ended with state
TIMED_OUTif no video arrives within 5 minutes. Start your encoder right before or right after the call. - New or existing broadcast: the response is
201for a new broadcast and200when the source already had a live broadcast. A200returns that broadcast unchanged and ignores the request’stitle,locale,chat_optionandshould_not_tweet. The original create route returned200in both cases. Retrying is safe. - No unpublished broadcasts: there’s no
NOT_STARTEDstep, so the 7-day cleanup of never-published broadcasts doesn’t apply. - Source can’t go live yet: if the source can’t take video before its encoder connects, the call returns a
409conflict. Retry once video is flowing. - Busy source: if another broadcast is live on the source, the call returns a
409conflictwhosedetailnames that broadcast when known. End it first, then retry.
Strict request bodies
POST /2/broadcasts validates its body strictly:
chat_optionmust be a JSON integer from0to6. The originalPUBLISHalso accepted numeric strings such as"2".should_not_tweetmust be a JSON boolean. The originalPUBLISHalso accepted"true"/"false"and1/0.- Unknown fields are rejected with
400. Removeregionandis_low_latency, which the original create route accepted and ignored.
POST /2/sources accepts only name and region, and PUT /2/sources/:id only name.
data wrapper
Responses wrap the result in data, as in the rest of the X API v2:
To page through
GET /2/broadcasts, pass meta.next_token as pagination_token. The page size is set with max_results (up to 100) instead of being fixed at 100.
Broadcast field names
The broadcast object uses the same field names, with these exceptions:id, broadcast_id, state, tweet_id, media_key, language, chat_option, the timestamps (created_at_ms, start_ms, end_ms and so on) and the viewer counts keep their names and formats. With GET /2/broadcasts and GET /2/broadcasts/:id, use broadcast.fields to choose which fields are returned.
Announcing Post
The announcing Post may not exist yet whenPOST /2/broadcasts returns, so tweet_id can be missing. Read it a moment later with GET /2/broadcasts/:id, as before.
If the Post fails, the original PUBLISH response included a tweet_error string next to broadcast. The new route has no error field for this: tweet_id stays missing. See Announcing Post.
Errors
The original routes returned most errors as{ "error_type", "message" }. The new routes return every error in the standard X API problem format, identified by type:
See the Go Live errors and the source errors.
Scheduled broadcasts
The scheduled broadcast routes (/2/broadcasts/scheduled*) aren’t being removed, but their source_id changes:
- Responses return
source_idas the source id, the same value as the source’sidinGET /2/sources. If you match scheduled broadcasts to sources, compare against the source id, not the stream key. - Create and update requests accept the source id as
source_id. A stream key is still accepted but deprecated, and stops working on January 1, 2027. Switch to the source id.
scheduled_broadcast_id in the body, as before.
Migration checklist
1
Look up source ids
Call
GET /2/sources and store each source’s id. Use it wherever you used the stream key to identify a source.2
Move source management to /2/sources
Switch to
POST, GET, PUT and DELETE /2/sources[/:id]. Read results from data, send region when creating, and read stream keys only with GET /2/sources/:id/stream_key or from the create response.3
Replace create + publish with Go Live
Call
POST /2/broadcasts with the source id as source_id and your publish fields. Handle 201 and 200, and both 409 cases.4
Update broadcast reads
Switch to
GET /2/broadcasts and GET /2/broadcasts/:id. Read results from data, read the title from title, and page with meta.next_token.5
Update end and delete
Switch to
PUT /2/broadcasts/:id/state with {"state":"END"} and DELETE /2/broadcasts/:id.6
Update scheduled broadcasts
Pass the source id as
source_id when creating and updating scheduled broadcasts.7
Update error handling
Branch on the problem
type and HTTP status instead of error_type.