How to Embed a Video on Twitter (X): In a Post or on Your Website

By TwitterAPI.io Team•15 min read
Diagram: two ways to embed a video on Twitter (X). In your own post: copy the link, add /video/1, paste it, and X attaches the original media with source_status_id_str. On a website: Embed post on publish.x.com or the oEmbed endpoint with widget_type=video, then the blockquote plus widgets.js.
Embedding a video on X means one of two jobs: reuse it in a post, or show it on a website.

"Embed a video on Twitter" means two different things, and most guides answer only one. This page covers both: putting a video that already lives on X inside your own post (without a repost or a quote), and putting an X video on a website. It is not the full website-embedding manual, which is our guide to embedding tweets on a website (widgets.js cost, lazy loading, static cards), and it is not about finding video posts in bulk, which is the Twitter video search API guide. Here the job is one video and one destination.

Every claim below was checked in 2026. X's own help center pages were read for the menu names and upload limits. The /video/1 link method, which X does not document, was checked by reading real posts back through the API: what X attaches, which fields point at the original, and what we could not see. We also measured how ten organization and media accounts put video on X, how long those videos run, and which MP4 files X serves for each, because those files are what any embed or download ends up playing.

01 — Section

Method 1: put someone else's video in your own post with /video/1

This is the answer most people want when they search for how to embed a video on Twitter. You saw a clip in another post and you want it in your post, under your words, without a repost (which adds nothing of yours) or a quote post (which shows the whole original post, text and all, inside a frame).

Step 1. Open the post with the video. On x.com, click the post so it opens on its own page. On the phone app, tap the share icon and choose Copy link. Either way you get a link shaped like x.com/<username>/status/<post id>. Remove anything after a ?, such as ?s=20.

Step 2. Add /video/1 to the end. The link becomes x.com/<username>/status/<post id>/video/1. For a post whose first media item is the video, that is all.

Step 3. Paste it into a new post. Write your own text, paste the link at the end, and post. X turns the link into the attached video rather than showing it as a URL or a link card.

Step 4. Check the result. The posted video plays like a native upload. In the data X serves for your post, the video carries the original post's ID and the original author's account ID, so it stays tied to its source.

Why this works: X already labels every native video with that exact address. In our study, the expanded_url field of each video was the owning post's URL plus /video/1, and when you paste that address back in, X resolves it to the existing media item instead of a link. We could not find any X help center page that documents the method, so treat it as a working behavior that X can change, not a promise.

Use it on public posts only. A protected account's video cannot be shared this way, and if the original post is deleted or the account is suspended, expect the clip in your post to go with it; we did not test deletion, so check your post if the source disappears. Reusing a clip does not transfer any rights to it: credit the creator in your text, and ask before reusing someone's work commercially.

02 — Section

Yes, on the evidence we could collect. We ran a Latest search for posts containing /video/1 links and read back 60 recent results. All 60 had a video attached. 43 of them (71.7%) carried a video that belongs to a different post: their media object had source_status_id_str set to another post's ID, a source_user_id_str, and a source_user object naming the original account. The other 17 were ordinary uploads that matched the search because every native video carries a /video/1 address.

In none of the 43 borrowed cases did the /video/ link survive as a plain URL in entities.urls. The pasted link was consumed and replaced by the media item, which is exactly what the method promises. The source posts ranged widely in age: the median borrowed clip was 27 days older than the post that reused it, 20.9% were less than a day old, and 25.6% were more than a year old.

Check on recent /video/1 postsCount
Posts read back60
With a video attached60
Video borrowed from another post43
Media links back to the source post43
Source author ID on the media43
Link left behind as a plain URL0

Organizations use the method too. @NBA reused its own clips this way 8 times in the 120 posts we read, posting a recap 9.2 to 15.9 hours after the original highlight with the same video attached. We fetched one such pair by ID: the recap and the original returned the same media_key, the recap's media pointed at the original through source_status_id_str, and the original's media had no source field because it was the upload.

What we could not verify: which button each author pressed (the data shows the result, not the gesture), what the X apps draw around a reused video (we read data, not screenshots), whether views on the reused copy count toward the original post, and what happens after the source post is deleted. If any of those matter to you, test with a post you control.

python
# GET https://api.twitterapi.io/twitter/tweets?tweet_ids=2114390277670764814,2114149692229517741
# (abridged; the first post reused the second post's clip with the /video/1 link)
{
  "tweets": [
    {
      "id": "2114390277670764814",
      "author": {"userName": "NBA"},
      "text": "TYRESE MAXEY FROM THE LOGO TO END THE 1Q LAST NIGHT ... https://t.co/hKgjl3ZHRG",
      "entities": {},                      # no urls entry: the link became media
      "extendedEntities": {"media": [{
        "type": "video",
        "media_key": "13_2114149257074688000",            # same key as the source
        "expanded_url": "https://x.com/NBA/status/2114149692229517741/video/1",
        "source_status_id_str": "2114149692229517741",    # the post that owns the clip
        "source_user_id_str": "19923144",
        "video_info": {"duration_millis": 14762, "variants": ["... 5 entries ..."]}
      }]}
    },
    {
      "id": "2114149692229517741",
      "extendedEntities": {"media": [{
        "type": "video",
        "media_key": "13_2114149257074688000",
        "expanded_url": "https://x.com/NBA/status/2114149692229517741/video/1"
        # no source_status_id_str: this is the original upload
      }]}
    }
  ]
}
Bar chart: of 60 recent posts matching url:/video/1, 60 had a video, 43 reused another post's video, 43 had media linking to the source post, 43 carried the source author ID, and 0 left the link as plain text.
Recent /video/1 posts read back through the API, collected in 2026 (aggregates only, no authors kept). 71.7% reused another post's video.
03 — Section

The other ways to share a video on X, and when they beat /video/1

The /video/1 link is not the only option, and it is not always the right one. Each method shows the audience something different.

MethodWhat your followers seeYour text on topBest for
/video/1 linkThe clip alone, played in your postYesCommentary on just the video
Quote postThe whole original post in a frameYesWhen the original wording matters
RepostThe original post as isNoPure amplification
Upload a fileYour own copyYesYour own footage or licensed clips
YouTube or other linkA link card, not X's playerYesLong videos hosted elsewhere

In our study of ten organization and media accounts, native video dominated: 36.6% of 1,186 recent posts carried a video uploaded to X (or reused with /video/1), 7.0% were reposts of someone else's video, 0.4% quoted a post with video, and only 0.3% linked out to YouTube, Twitch, Vimeo, TikTok, Instagram or Facebook video. Accounts that live on video, like @NFL and @NBA, post native clips in most of their posts; news wires like @BBCWorld and @CNN mostly post article links and photos.

Uploading your own video. In the composer, click the gallery (media) button and choose the file. At the time of writing, X's help center says accounts without a Premium plan can upload videos up to 140 seconds long and up to 512 MB, while X Premium and the Plus, Premium, Super and Ultra plans of The X Subscription can upload up to 4 hours at 1080p (videos over 2 hours at 720p) and up to 16 GB on x.com and iOS, and up to 10 minutes on Android (sources: help.x.com/en/using-x/x-videos and help.x.com/en/using-x/premium-longer-videos).

Posting a YouTube video on X. Paste the YouTube URL into a post. Followers see a link card that opens YouTube, not X's own player. If the video is yours, uploading a cut-down clip natively and linking the full version is what most of the accounts we measured do, judging by how rare link-outs were.

Stacked bar chart of how 10 accounts post video on X: @NFL 88.3% native video, @NBA 82.5%, @PlayStation 65.8%, @espn 42.5%, @SpaceX 33.3%, @Xbox 24.5%, @Reuters 12.5%, @NASA 10.0%, @BBCWorld 4.2%, @CNN 0.8%; link-outs to video hosts 0.3% overall.
Share of each account's recent posts by video method, 1,186 posts collected in 2026.
04 — Section

Method 2: embed an X video on a website or blog

To put an X video on a web page, you embed the post that contains it. X's help center describes the steps: open the post on x.com, click the menu icon on the post, select Embed post, adjust the look on publish.x.com with set customization options if you want, click Copy Code, and paste the code into your page's HTML. The help center notes that embedding is only available via x.com, so do it from a browser, not the phone app.

The code you get is a <blockquote class="twitter-tweet"> holding the post's text and link, followed by a <script> tag that loads platform.x.com/widgets.js. The script swaps the blockquote for the interactive post, including the video player. If the script is blocked, readers see the plain blockquote: the text and a pic.twitter.com link, not the video.

Video-first embed. The oEmbed endpoint returns a different blockquote, class="twitter-video", when you add widget_type=video to the request (shown in the next section). This parameter is not listed in X's oEmbed reference, but the endpoint accepted it when we called it. It frames the post around the video player rather than the text.

WordPress, Medium and similar editors. Many site builders accept a bare post URL on its own line and fetch the embed through oEmbed for you. In raw-HTML editors, paste the full code instead. Either way, the video stays hosted by X: if the post is deleted or made protected, the embed stops showing it.

Performance, privacy settings, lazy loading and a zero-JavaScript static card are covered in depth in our website embedding guide (linked in the introduction); the short version is that one widgets.js include per page is enough, and dnt=true asks X not to use the embed for personalization.

05 — Section

The oEmbed endpoint for X videos: a real response

If you build a CMS, a newsletter tool or a link previewer, you want the embed code from code, not from a copy button. X's oEmbed API does that: a GET to publish.x.com/oembed with the post URL returns JSON. X's developer docs list it as requiring no authentication and not rate limited, and describe the markup as meant to be cached on your servers for up to the cache_age the response suggests.

We called it for a public @NBA video post. The old address publish.twitter.com/oembed answered with an HTTP 301 redirect to publish.x.com/oembed, so either works if your client follows redirects. The response, trimmed only inside the post text and date:

Parameters worth knowing. From X's oEmbed reference: maxwidth (220 to 550 pixels; there is no maxheight and height comes back null), hide_media (do not expand photos or video), hide_thread, omit_script (leave out the widgets.js tag so you include it once per page), align, lang, theme=dark and dnt=true. We added widget_type=video from our own test; with it the blockquote class switched from twitter-tweet to twitter-video.

Request we madeResult
Video post, default200, twitter-tweet blockquote + widgets.js
Same post, widget_type=video200, twitter-video blockquote
Same, omit_script=true + maxwidth=400200, no script tag, width 400
A post that reused the clip via /video/1200, its own text, twitter-video
A nonexistent post ID404 with an HTML page, not JSON

The last row matters for code: check the status before parsing JSON. And because cache_age is a very long number, re-check embeds you have stored if the source posts can disappear.

json
GET https://publish.twitter.com/oembed?url=https://x.com/NBA/status/2114149692229517741&widget_type=video
-> HTTP 301 to https://publish.x.com/oembed?... then HTTP 200, no key needed

{
  "url": "https://x.com/NBA/status/2114149692229517741",
  "author_name": "NBA",
  "author_url": "https://x.com/NBA",
  "html": "<blockquote class=\"twitter-video\"><p lang=\"en\" dir=\"ltr\">TYRESE MAXEY.<br><br>FROM THE LOGO.<br><br>AT THE 1Q BUZZER ... <a href=\"https://t.co/JUoPwQxG1y\">pic.twitter.com/JUoPwQxG1y</a></p>&mdash; NBA (@NBA) <a href=\"https://x.com/NBA/status/2114149692229517741?ref_src=twsrc%5Etfw\">...</a></blockquote>\n<script async src=\"https://platform.x.com/widgets.js\" charset=\"utf-8\"></script>\n\n",
  "width": 550,
  "height": null,
  "type": "rich",
  "cache_age": "3153600000",
  "provider_name": "X",
  "provider_url": "https://x.com",
  "version": "1.0"
}
06 — Section

What X actually serves when a video plays: the MP4 files

An embed, the app and every downloader all play the same files. Each native video's media object has a video_info.variants list. Across the 434 native videos in our study, every one (100.0%) included an adaptive HLS playlist (application/x-mpegURL), plus a median of 4 MP4 files at fixed bitrates. The top MP4 had a median bitrate of 10,368 kbps (1080p), the largest reached 25,128 kbps at 2160p (4K), and the lowest rendition had a median of 288 kbps.

Top rendition (short side)VideosShare
2160+5312.2%
1080-215930971.2%
720-10797016.1%
under 72020.5%

Shape matters for embeds because the player keeps the video's aspect ratio. 65.2% of the videos were 16:9 landscape, 13.6% square, 10.1% vertical 9:16 and 10.1% 4:5. A vertical clip in a 550-pixel-wide embed is tall; if your layout is tight, a landscape source embeds more cleanly.

If you need the file itself, for editing your own footage or archiving a post you have rights to, pick the highest-bitrate video/mp4 entry. Our free Twitter video downloader does that for a single post in the browser, and the script at the end of this page prints every rendition.

07 — Section

How long are the videos people embed? Our measurement

We read video_info.duration_millis for all 434 native videos. The median was 15.8 seconds, the middle half ran from 10.3 to 30.5 seconds, and nine in ten were under 67.4 seconds. 73.3% were shorter than 30 seconds, 88.0% shorter than a minute, and 95.4% fit inside the 140-second limit that applies to accounts without Premium. The longest was 273.3 seconds; none passed ten minutes.

DurationVideosShare
0-15 s20346.8%
15-30 s11526.5%
30-60 s6414.7%
60-140 s327.4%
140 s-10 min204.6%
over 10 min00.0%

So the clips that get shared and embedded are overwhelmingly short highlights. That fits the /video/1 method well: it reuses a short, already-processed clip with your framing on top. For long-form video, link to the host or upload a short cut.

On reach, our sample does not show a reliable lift for video: the per-account ratio of median views on native-video posts to other original posts ranged from 0.54x to 2.15x (table in the study section). Do not post video because a guide promised more views; post it when the clip carries the point.

Bar chart of native video durations on X: 203 videos 0-15 s, 115 15-30 s, 64 30-60 s, 32 60-140 s, 20 140 s-10 min, 0 over 10 min; median 15.8 s.
Durations of 434 native videos from 10 organization and media accounts, collected in 2026. Median 15.8 s.
08 — Section

For developers: read a post's video, reuse link and embed code by API

Everything above can be scripted. One call to GET /twitter/tweets with tweet_ids returns the post with its extendedEntities.media; the video's expanded_url is already the /video/1 reuse link of the post that owns the clip, source_status_id_str tells you whether the video was borrowed, and video_info.variants lists the files. Then X's oEmbed endpoint gives you the website code. GET /twitter/user/last_tweets pages through an account's recent posts (20 per page, follow next_cursor while has_next_page is true) so you can list its videos.

What it costs. On twitterapi.io, tweet endpoints are 15 credits per tweet returned, which is $0.15 per 1,000 tweets, and you pay only for the data returned; no X account is needed for reads. The official X API's pay-per-usage rate card (docs.x.com/x-api/getting-started/pricing) lists Posts: Read at $0.005 per resource, which is $5 per 1,000 posts, so about 33 times more for the same posts. This whole study read 1,248 posts and cost 18,750 credits, about $0.19. New accounts that pass the automatic bot check get up to $1 of free credits (at most 100,000 credits, no card, one per person or network, expiring after 31 days); purchased credits never expire. Plans and rate limits are on the pricing page.

Posting with a reused video from code. Write actions on twitterapi.io run through an authenticated session: one /twitter/user_login_v2 login ($0.10, with a proxy you supply) returns login_cookies, and each post is then 60 credits ($0.0006) through /twitter/create_tweet_v2. The call acts as that account, so X can rate-limit, lock or suspend it; keep a human pace, expect occasional re-login, and note that every HTTP 200 call is billed. We did not test whether a /video/1 link sent through the API is converted the same way as in the composer, so try it on a test account first. For uploading your own files, see the media upload guide.

09 — Section

How we collected this

Question. How do organization and media accounts put video on X, how long are those videos, which files does X serve for them, and does the /video/1 link still attach another post's video?

Accounts and queries. Recent timelines (replies excluded) of @NASA, @SpaceX, @NBA, @NFL, @espn, @Reuters, @BBCWorld, @CNN, @PlayStation and @Xbox through /twitter/user/last_tweets, up to 120 posts each. One Latest search for url:"/video/1" through /twitter/tweet/advanced_search (60 posts). One /twitter/tweets lookup of an @NBA reuse pair. All collected in 2026.

Sample. 1,186 timeline posts (434 native videos) and 60 search results; 66 requests in total, 18,750 credits. Each post was classified once: native video, repost of a video, quote of a video post, link out to a video host, photo, GIF, or text and article link.

AccountNative videoVideo repostsQuote or link-outViews ratio
@NFL88.3%0.8%0.0%1.0x
@NBA82.5%0.8%1.7%0.85x
@PlayStation65.8%5.8%2.5%1.12x
@espn42.5%14.2%0.0%1.06x
@SpaceX33.3%2.5%0.0%0.95x
@Xbox24.5%34.0%0.0%0.99x
@Reuters12.5%0.0%0.0%1.49x
@NASA10.0%15.0%2.5%1.03x
@BBCWorld4.2%0.0%0.0%2.15x
@CNN0.8%0.0%0.0%0.54x

Views ratio is the median views of native-video posts divided by the median views of other original posts from the same account; it is descriptive, not causal.

What the sample cannot show. Ten large accounts are not a typical user; their videos are professionally cut and their timelines are recent snapshots. The /video/1 search sample is small and only shows the result in the data, not the app's display, the gesture used, or what happens after deletion.

Privacy. Aggregates only. Organization and media accounts are named; the authors of the search results are private individuals, so their posts were never stored with the study and only de-identified counts were kept.

python
"""embed_video.py - for one X (Twitter) post URL: find its video, list the MP4
files X serves, print the /video/1 link you can paste into your own post, and
fetch website embed code from X's oEmbed endpoint.

    pip install requests
    export TWITTERAPI_IO_API_KEY=...      # from twitterapi.io/dashboard
    python embed_video.py https://x.com/NBA/status/2114149692229517741
    python embed_video.py --scan NASA     # list recent native videos of an account
"""
import os
import re
import sys

import requests

BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_API_KEY"]}


def first_video(tweet):
    """Return the first video media object of a tweet, or None."""
    media = (tweet.get("extendedEntities") or {}).get("media") or []
    return next((m for m in media if m.get("type") == "video"), None)


def get_tweet(tweet_id):
    r = requests.get(f"{BASE}/twitter/tweets", params={"tweet_ids": tweet_id},
                     headers=HEADERS, timeout=30)
    r.raise_for_status()
    tweets = r.json().get("tweets") or []
    return tweets[0] if tweets else None


def oembed(post_url, video_only=True):
    """X's public oEmbed endpoint: no key, returns JSON with an html field."""
    query = dict(url=post_url, omit_script="true", dnt="true")
    if video_only:
        query["widget_type"] = "video"    # blockquote class="twitter-video"
    r = requests.get("https://publish.x.com/oembed", params=query, timeout=30)
    r.raise_for_status()
    return r.json()


def describe(post_url):
    m = re.search(r"/status/(\d+)", post_url)
    if not m:
        sys.exit("not an X post URL")
    tweet = get_tweet(m.group(1))
    if not tweet:
        sys.exit("post not found (deleted, protected or wrong ID)")
    video = first_video(tweet)
    if not video:
        sys.exit("this post has no native video (photo, GIF, link card or text)")

    info = video.get("video_info") or {}
    src = video.get("source_status_id_str")
    borrowed = bool(src and src != tweet["id"])
    print("author       :", tweet["author"]["userName"])
    print("duration (s) :", round((info.get("duration_millis") or 0) / 1000, 1))
    print("aspect ratio :", info.get("aspect_ratio"))
    print("borrowed     :", f"yes, from post {src}" if borrowed else "no, uploaded here")
    # expanded_url already points at the post that owns the video
    print("reuse link   :", video.get("expanded_url"))

    mp4 = [v for v in info.get("variants", []) if v.get("content_type") == "video/mp4"]
    for v in sorted(mp4, key=lambda v: v.get("bitrate", 0), reverse=True):
        size = re.search(r"/(\d+x\d+)/", v["url"])
        print(f"  mp4 {v.get('bitrate', 0) // 1000:>6} kbps  {size.group(1) if size else '?':>10}  {v['url']}")
    hls = [v for v in info.get("variants", []) if v.get("content_type") == "application/x-mpegURL"]
    if hls:
        print("  hls playlist:", hls[0]["url"])

    embed = oembed(f"https://x.com/{tweet['author']['userName']}/status/{tweet['id']}")
    print("\nembed html (add widgets.js once per page):\n", embed["html"])


def scan(user, pages=3):
    """Page through an account's recent posts and list the native videos."""
    cursor, seen = "", 0
    for _ in range(pages):
        params = {"userName": user}
        if cursor:
            params["cursor"] = cursor
        r = requests.get(f"{BASE}/twitter/user/last_tweets", params=params,
                         headers=HEADERS, timeout=30)
        r.raise_for_status()
        body = r.json()
        for t in (body.get("data") or {}).get("tweets") or []:
            seen += 1
            v = first_video(t)
            if v:
                secs = round(((v.get("video_info") or {}).get("duration_millis") or 0) / 1000, 1)
                print(f"{t['createdAt']}  {secs:>6} s  {t['url']}")
        if not body.get("has_next_page") or not body.get("next_cursor"):
            break
        cursor = body["next_cursor"]
    print(f"scanned {seen} posts")


if __name__ == "__main__":
    if len(sys.argv) == 3 and sys.argv[1] == "--scan":
        scan(sys.argv[2])
    elif len(sys.argv) == 2:
        describe(sys.argv[1])
    else:
        sys.exit(__doc__)
10 — Questions

Questions readers ask

How do I post someone else's Twitter video without retweeting?

Copy the link of the post with the video, add /video/1 to the end (x.com/user/status/ID/video/1), paste it into a new post with your own text, and post. X attaches the original clip to your post as a video. We confirmed through the API in 2026 that posts made this way carry the clip as media that points back to the source post and author.

Does the /video/1 trick still work on X?

It did when we checked in 2026: 43 of 60 recent posts with such links carried another post's video as attached media, and none left the link behind as plain text. X does not document the method, so it can change; test it with one post before relying on it.

How do I get the embed code for a Twitter video?

Open the post on x.com in a browser, click the menu icon on the post, choose Embed post, then click Copy Code on publish.x.com and paste it into your page's HTML. Developers can call publish.x.com/oembed?url=<post URL> instead; it needs no key and returns the same code in JSON.

Can I embed just the video, without the tweet text?

Not as a bare player through X's tools. The closest is the oEmbed response with widget_type=video, which returns a twitter-video blockquote centered on the player. The post text still appears in the fallback markup. Hosting the file yourself is only appropriate for footage you own or are licensed to use.

Why is my embedded Twitter video not showing on my website?

Usually widgets.js did not load (blocked by a content blocker, a strict Content-Security-Policy, or removed by your editor), so readers see the plain blockquote with a pic.twitter.com link. It also disappears if the post is deleted, made protected, or the account is suspended.

How long can a video on Twitter be?

At the time of writing X's help center says 140 seconds and 512 MB without Premium; X Premium and the Plus, Premium, Super and Ultra plans of The X Subscription can upload up to 4 hours (1080p, 720p above 2 hours, 16 GB) on x.com and iOS and up to 10 minutes on Android. In our 2026 sample of organization videos the median was 15.8 seconds.

How do I embed a YouTube video on Twitter?

Paste the YouTube URL into a post. X shows a link card that opens YouTube rather than playing in X's player. Only 0.3% of the organization posts we measured linked out to video hosts this way; most uploaded clips natively.

Can I download a Twitter video to embed it myself?

Each X video ships several MP4 files plus an HLS playlist, readable from the post's video_info.variants. Our free video downloader picks them for a single post. Only re-host videos you own or have permission to use; for anyone else's clip, use the /video/1 link or the official embed.

11 — Further reading

Continue

Sources & further reading
More from this series
Build it

Stop reading. Start building.

Starter credits cover real testing on real data. Google sign-in, no card, no application queue.

Get an API key

Already building with TwitterAPI.io? Publish an honest tutorial or build log about it and earn up to $200 a month in API credits.