X (Twitter) Embed Generator
Introduction
The Toollect X (Twitter) Embed Generator turns any tweet URL into the official embed code X publishes for showing posts on other websites – the twitter-tweet blockquote plus widgets.js – with one click, a live preview before you copy, and every display option X supports in its own interface. Paste a link, pick your options, preview, copy, paste. The whole cycle takes seconds.
The friction it removes is familiar to anyone who has embedded a tweet by hand. X's own publisher dialog produces the code but spreads its choices across separate screens, exposes no media-hiding or alignment controls, and gives video embeds no numeric width control at all. Hand-writing the blockquote works until one attribute is misspelled and the tweet renders as a plain link forever. This tool assembles the exact markup widgets.js expects, applies your options as the correct data-* attributes, and shows you the real render before anything reaches your page.
The generator comes in two modes behind one tab bar. Post builds the classic card for any tweet – text, photos, link previews – with theme, width from 220 to 550 pixels, interface language, alignment, conversation and media visibility, Do Not Track, and an option to omit the shared script tag. Video optimizes the same tweet for its player, with a width up to 1920 pixels written into the data-media-max-width attribute that large clips deserve. Both modes talk directly to X's public oEmbed endpoint – no API key, no sign-up, and no Toollect server in between.
Use Cases
Embedding X content shows up in more places than a blog post, and the two modes split the work naturally.
Blogs and editorial content
A commentary post quoting a public figure, a tech article embedding an announcement tweet, a sports blog showing the highlight clip – the classic cases. Post mode with the default settings covers text tweets; switch to Video mode when the tweet is mostly a clip and the player should own the column.
Newsrooms and live coverage
Journalists pull eyewitness posts and official statements into rolling coverage. Speed matters, and so does verification: the generator normalizes whatever link the field contact shared – app share links with tracking parameters, legacy twitter.com forms, photo-view variants – into one canonical tweet reference, and the preview confirms the tweet still renders before the story goes out. The Hide conversation option keeps a reply thread focused on the quoted statement instead of the argument above it.
Documentation and technical writing
Release notes, changelogs and tutorials increasingly cite engineer threads as primary sources. Hide media collapses link previews down to text for a quieter read, the language selector renders the action buttons and timestamps in the documentation's language, and Omit script keeps pages that already load widgets.js – component libraries, documentation themes – free of duplicate script tags.
Marketing sites and portfolios
Customer praise tweets as social proof, founder threads as brand story. Alignment centers a single testimonial in a narrow column; Do Not Track signals privacy respect to European audiences; the preview verifies every card before it ships.
How It Works
The tool runs in four steps:
- Parse and canonicalize the URL – the input is matched against the X link grammar for tweets: a username (
@-less, 1–15 letters, digits or underscores), thestatusroute, and a 15–19 digit snowflake ID, onx.comortwitter.comwith optionalwww./mobile./m.prefixes. Query parameters such as?s=20&t=…are ignored rather than cleaned – the rebuilt canonical URL simply never includes them. Routes without a username, like the web app's/i/web/status/…, are rejected with a dedicated message because the oEmbed endpoint cannot resolve them. - Build the official request – your options become query parameters on X's public oEmbed endpoint (
publish.x.com/oembed?url=…):maxwidth,theme,lang,hide_thread,hide_media,align,dnt,omit_script. The endpoint is keyless and rate-limit-free by design – it exists so CMSs can convert URLs to embeds. - Fetch the official markup – one HTTPS GET returns the blockquote HTML exactly as X formats it, with your options already baked in as
data-*attributes. Your browser talks to X directly; no Toollect server touches the request. - Post-process and preview – the server's script tag is stripped and managed separately (so the preview loads
widgets.jsexactly once no matter how many times you regenerate), Video mode rewrites the width attribute client-side (see Post vs. Video below for why), and the blockquote goes into the preview area. The tool watches for the rendered iframe and reports honestly if it never appears.
Changing any option regenerates the whole chain – new request, fresh markup, re-rendered preview – so the code you copy always matches what you are looking at.
Supported URL Formats
The generator accepts every URL shape that identifies a tweet with its username, and rejects everything else with a specific message.
Accepted formats
| Format | Example | Result |
|---|---|---|
| Canonical post | x.com/username/status/1234567890123456789 |
Embed for that tweet |
| Legacy domain | twitter.com/jack/status/20 |
Embed for that tweet |
| Mobile subdomain | mobile.twitter.com/username/status/… |
Embed for that tweet |
www prefix |
www.x.com/username/status/… |
Embed for that tweet |
| Scheme-less paste | x.com/username/status/… |
Embed for that tweet |
| Photo view variant | x.com/username/status/…/photo/1 |
Embed for that tweet |
| Video view variant | x.com/username/status/…/video/1 |
Embed for that tweet |
| With tracking parameters | …?s=20&t=…&ref_src=twsrc%5Etfw |
Embed for that tweet – parameters ignored |
View variants and junk parameters after the tweet ID are simply ignored – the embed always points at the tweet itself, which renders its own media. The https:// prefix and trailing slash are optional.
Rejected formats
| URL shape | Why it is rejected |
|---|---|
x.com/i/web/status/1234567890123456789 |
The web app's username-less route – the oEmbed endpoint answers 404 for it. Open the link and copy the full address, which carries the username |
x.com/username |
A profile – no tweet to embed |
x.com/intent/tweet?text=… or x.com/intent/retweet?tweet_id=… |
Share intents – actions, not content |
x.com/hashtag/topic or x.com/search?q=… |
Hashtag and search pages – no single tweet |
facebook.com/…, any other host |
Not an X domain |
The username-less /i/web/status/… case deserves emphasis because it is the most common surprise. X hands out that shape when a tweet URL is copied from the web app without opening it. The generator refuses it deliberately – passing it to the endpoint would produce a silent failure – and tells you to use the full link instead. The X (Twitter) URL parser on this site resolves these links to their canonical form if you would rather fix the URL than re-copy it.
Anatomy of the Embed Code
The output is the code X's own machinery produces, with your options applied. A post embed at the defaults looks like this (content abbreviated):
<blockquote class="twitter-tweet" data-width="550">
<p lang="en" dir="ltr">Just setting up my twttr</p>
— Jack (@jack)
<a href="https://x.com/jack/status/20?ref_src=twsrc%5Etfw">March 21, 2006</a>
</blockquote>
<script async src="https://platform.x.com/widgets.js" charset="utf-8"></script>
Walking through the parts:
- The blockquote class –
twitter-tweetis the hookwidgets.jsscans for. Every element with this class on the page becomes a rendered card. data-width(posts) – the maximum render width in pixels. X clamps it internally to roughly 250–550 regardless of what is written, so the generator keeps your chosen value within the honest range.data-media-max-width(videos) – how wide the media may expand inside the card, up to 1920 pixels. Present only in Video mode – see Post vs. Video for why the generator writes this attribute itself.data-theme="dark"– present only when Dark is selected. Renders light-on-dark text and the dark card chrome.data-lang– present whenever a specific language is selected, English included. Localizes the widget's own strings – the timestamp format, the Read more bridge, action labels – but never the tweet text itself. Only Automatic omits the attribute, lettingwidgets.jsfall back to your page's language.data-conversation="none"– added by Hide conversation on a reply. Without it,widgets.jsprepends a collapsed summary of the parent post for context.data-cards="hidden"– added by Hide media. Link previews, photos and videos stay collapsed; only the text renders.data-align– floats the card left or right, or centers it, relative to its container. Absent when alignment is None.data-dnt="true"– added by Opt-out of tailoring. Tells X not to use the embed or its page views for personalized suggestions or ads.- The paragraph and attribution line – the visible content before
widgets.jsruns: the tweet text, the author line, and a link to the post. This is the permanent experience for visitors with JavaScript off or the script blocked, and it degrades gracefully to a normal citation. - The script tag –
widgets.jsfromplatform.x.com, loaded async. One per page is enough for any number of embeds, which is what the Omit script option is for.
Post vs. Video Embeds
Two tabs, one tweet, two different jobs. Switching tabs regenerates immediately – there is no stale state.
| Post tab | Video tab | |
|---|---|---|
| Best for | Text tweets, photos, link previews | Tweets whose point is the clip |
| Width range | 220–550 px (default 550) | 560–1920 px (default 560) |
| Width attribute | data-width from the server |
data-media-max-width, rewritten by the tool |
| Theme (light/dark) | Available | Hidden – the player has no light variant |
| Hide conversation | Available | Hidden – a reply's parent context is text, not the clip |
| Hide media | Available | Hidden – the media is the point |
| Language, alignment, Do Not Track, Omit script | Available | Available |
Why does the tool write data-media-max-width itself? Because X's oEmbed endpoint cannot. Testing confirms the endpoint clamps every maxwidth parameter – including video tweets – into the 220–550 post range, and never emits the data-media-max-width attribute that video embeds carry. That attribute belongs to the flow inside publish.x.com, where the Embedded Video generator decorates the markup after the fact. The generator reproduces exactly that decoration: it strips the clamped data-width the server returned and writes your chosen width into data-media-max-width, so a 1200-pixel video embed is truly 1200 pixels wide on your page.
Practically, the choice rule is simple. If the tweet reads well as a card, use Post. If removing the player empties the tweet, use Video – and give the player the room it needs.
Embed Options
Each control maps to one attribute or one request parameter. Defaults in parentheses.
Language (defaults to the page language) – offers Automatic plus the 34 languages X ships for widget text. Every explicit selection, English included, is sent as the lang parameter and written to data-lang, which pins the widget's chrome to that language wherever you paste it. It localizes timestamps and action labels around the tweet, not the tweet's words. Automatic omits both the parameter and the attribute – widgets.js then renders in whatever language the host page declares, which suits multilingual sites but makes the output depend on where it is pasted.
Theme (Light, Post tab only) – Dark sends theme=dark and writes data-theme="dark". Light is the absence of the attribute.
Width (550 post, 560 video) – typed as an exact number, clamped to the valid range the moment you leave the field. Posts send maxwidth; videos drive the client-side data-media-max-width rewrite. Values outside a tab's range cannot survive the field – 9999 on the Post tab becomes 550, 100 on the Video tab becomes 560.
Alignment (None) – Left, Right or Center writes align to the request and data-align to the blockquote. Center is the quiet way to place a single card in a wide article; Right lets article text wrap around it.
Hide conversation (off, Post tab only) – sends hide_thread=true, which the server translates to data-conversation="none". Meaningful only on replies; on a standalone tweet it changes nothing.
Hide media (off, Post tab only) – sends hide_media=true, translated to data-cards="hidden". The embed becomes a pure quotation.
Omit the widgets.js script tag (off) – sends omit_script=true and suppresses the script tag in the output. For sites that already load widgets.js once globally – several embeds, one script. If your page has no widgets.js, leave this off; the blockquote alone renders as static text forever.
Opt-out of tailoring X (off) – sends dnt=true and writes data-dnt="true". X documents this as excluding the embed and its page from personalized suggestions and personalized ads.
Usage
- Paste a tweet URL into the input – any accepted format from the table above.
- Press Generate (or Enter). The code appears in the output box and the preview opens automatically, showing the real X render.
- Pick the tab – Post for the standard card, Video for clip-first tweets. Switching regenerates with that tab's options and width range.
- Adjust the options – language, theme, width, alignment, the visibility checkboxes. Every change refetches and re-renders; the code box never drifts from the preview.
- Check the diagnostics – if no card appears, the warning above the output says whether X's script was blocked or the tweet cannot be embedded.
- Copy the code and paste it into your page's HTML where the tweet should appear.
Input that matches no supported format clears the output and explains itself – either the invalid-link message or, for username-less routes, the use-the-full-link message. Clear resets input, code and preview in one step.
Tutorial
Follow one real-world link through both tabs.
Step 1 – Paste an app share link. Copy a tweet URL as the mobile app shares it, tracking parameters and all:
https://twitter.com/new27brigade/status/2090248985391014324?s=20&t=FakeNonce
Press Generate. The parser reads past the legacy domain, the s and t parameters to the tweet itself – new27brigade, status 2090248985391014324 – and the generated code points at the canonical form. The preview opens and the card renders.
Step 2 – Darken it. Set Theme to Dark. The code gains data-theme="dark" and the preview re-renders in the dark palette. Note the refetch – theme is baked in server-side, which is why every option change hits the endpoint again rather than editing the string locally.
Step 3 – Narrow it. Type 350 into Width and press Enter. The attribute becomes data-width="350" and the card shrinks. Try typing 9999 and clicking elsewhere – the field snaps back to 550, because X would clamp it there anyway.
Step 4 – Localize the chrome. Set Language to Japanese (日本語). The code gains data-lang="ja" and the timestamp in the preview reformats to Japanese convention. The tweet text stays exactly as written – the setting governs X's own labels only.
Step 5 – Switch to Video. Click the Video tab. The tool refetches with the video parameter set and rewrites the markup: data-width disappears, data-media-max-width="560" appears, and the theme selector vanishes from the panel. Drag the width to 1200 – the player expands. This tweet is a military exercise clip, so Video mode suits it; for a text announcement you would switch back.
Step 6 – Copy and ship. Hit Copy and paste into a plain HTML page. Open it – the player renders at full width. That is the complete contract: the same code, the same render, on your page.
Preview & Diagnostics
The preview runs X's genuine widgets.js against your exact blockquote, so it fails exactly where your page will fail. Two warnings, two different meanings:
| Warning | What it means | What to do |
|---|---|---|
| "X widgets.js failed to load — often an ad blocker" | The browser could not download platform.x.com/widgets.js at all |
Whitelist platform.x.com in the blocker or try a clean private window. The published embed will fail the same way for visitors running that blocker |
| "The official embed did not render" | The script loaded but produced no card | The tweet may be deleted, the account protected, the URL wrong, or the author disabled embedding for the post. Open the tweet on x.com to check; if it plays there but not here, embedding is off on the author's side |
Both warnings fire only after generous waits – twenty seconds for the script download, fifteen more for the render – so slow connections do not trigger false alarms. An embeddable tweet renders well inside those windows.
One mechanical detail worth knowing: the first generation downloads widgets.js and keeps it for the session. Regenerations – new options, tab switches – reuse it and nudge it directly to process the new blockquote, so subsequent previews appear faster than the first.
Pro Tips
- Pair it with the URL parser. The parser analyzes and cleans a link – type, snowflake ID, tracking keys; the generator turns the clean link into code. Archive with the parser, publish with the generator.
- One script tag, many embeds. Pages with several tweets should load
widgets.jsonce – tick Omit script on every generator output except the first, or load it yourself in the site template and omit it everywhere. - Hide media turns a tweet into a quotation. For editorial pull-quotes where a link preview would distract, Hide media plus Hide conversation leaves only the words and the attribution.
- Center single testimonials. Alignment Center in a wide layout reads as intentional design; floating Left is the default of every CMS and rarely what a marketing page wants.
- Match the widget language to the page language. A Japanese page with English RETWEET buttons looks unfinished – explicit languages pin the chrome, which is the part you are responsible for; on multilingual templates running the same embed across locales, choose Automatic and let
widgets.jsfollow each host page instead. - Set Do Not Track for EU-facing pages. The flag is documented by X and propagates through the embed; it is a one-click signal that the card respects visitor privacy.
- Design for the column, then set the width. Posts physically end at 550 pixels; the preview shows the true result at your container size before you commit.
Alternatives
How else can you get a tweet onto a page?
| Method | Official markup | Option coverage | Live preview | Dependencies |
|---|---|---|---|---|
| This tool | Yes – server-generated | Full set, both modes | Yes | None |
x.com share menu → Embed post (publish.x.com) |
Yes | Partial – no media hiding, no alignment, no omit-script, no numeric video width | In-dialog only | None |
Hand-written blockquote + widgets.js |
Shaped correctly if you are careful | Whatever you type – easy to mistype | No | widgets.js |
| X API v2 + custom renderer | No – JSON only | Total, built by you | Build it yourself | Paid API access |
| Third-party embed services | Their own wrapper markup | Varies | Sometimes | Their script and tracking |
X's own share dialog is the closest alternative and the source of the same blockquote – but its customization stops at language, conversation hiding, tailoring opt-out and a coarse video size, and it offers no path to hide_media, align, or a precise maxwidth. The API route buys total control at pay-per-use prices and still leaves the rendering to you. Third-party services interpose their own scripts. The generator occupies the practical middle – official markup, complete options, immediate visual confirmation.
Against the Official APIs
Three official surfaces touch tweet embedding, and the generator is built directly on the friendliest one.
| Capability | Public oEmbed endpoint | publish.x.com dialog | X API v2 |
|---|---|---|---|
| Purpose | URL → embed HTML, programmatically | URL → embed HTML, by hand | Full platform data and posting |
| Authentication | None | None | Keys + pay-per-use billing |
| Cost | Free | Free | About $0.005 per post read since February 2026 – no free tier |
| Rate limits | None documented | Practical limits only | Metered credits |
| Returns ready-to-paste code | Yes – blockquote + script | Yes | No – JSON you render yourself |
| Parameter coverage | maxwidth, theme, lang, hide_thread, hide_media, align, dnt, omit_script |
Subset – see Alternatives | Irrelevant – you build the view |
The generator calls the oEmbed endpoint exactly as documented – the same request a CMS plugin would make, made by your browser. It adds what the endpoint lacks: input normalization, the video-mode data-media-max-width decoration, script-tag management, and diagnostics. The X API v2 is deliberately not involved – since its February 2026 pricing change removed the free tier, reading tweets through it costs money for capabilities the free endpoint already covers for embedding purposes.
Generator vs. URL Parser
Toollect ships two X tools that accept the same links, and the division of labor is worth one glance:
| Question | URL Parser | This Generator |
|---|---|---|
| What kinds of links? | All seven kinds – tweets, profiles, lists, spaces, events, hashtags, searches | Tweets only |
| What does it output? | Clean canonical URLs, widget iframe URLs, share intents | Ready-to-paste embed code |
| Cleans tracking parameters? | Yes – and lists every removed key | Unnecessary – output is freshly generated code |
Resolves /i/web/status/…? |
Yes – maps to the canonical username form | No – asks you for the full link |
| Network requests? | Zero – everything is local parsing | One oEmbed GET per generation, plus X's scripts for preview |
| Shows tweet metadata? | Structure only – no tweet content | The preview shows the real rendered tweet |
The parser is the analyst; the generator is the builder. A publishing workflow legitimately uses both in order – parse to identify and normalize what a messy link points at, then generate to put it on the page. They agree on grammar by construction, so a link the parser accepts as a tweet is a link the generator embeds.
What It Doesn't Do
The boundaries, named so the tool is never over-trusted:
- It does not embed timelines, profiles, lists, spaces or hashtags. Those are different X surfaces with different markup; the oEmbed endpoint's profile support produces timeline anchors, not tweet cards, and the generator declines them. A dedicated tool would be the honest vehicle if there is demand.
- It does not resolve username-less links.
/i/web/status/…arrives without the username the canonical form requires, and the tool will not guess it. Open the link, copy the full URL. - It does not resolve
t.coshorteners. A bare shortened link carries no tweet identity; paste the destination instead. - It does not override privacy or deletion. Protected accounts, deleted posts and authors who disabled embedding produce correct code that renders nothing. No tool can change that.
- It does not verify tweets ahead of the request. Validation is grammatical. Whether the tweet exists is answered only when the endpoint responds – an unavailable tweet surfaces as a failed request.
- It does not style the widget. Colors, fonts and chrome inside the rendered card are X's. Only placement (alignment, width) and presence (conversation, media) are yours.
- It does not install the code. What you copy is what you paste; the tool cannot reach into your CMS or page builder.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| "No tweet found in that URL" | Profile, intent, search or foreign link – or a malformed ID | Use the full user/status/id form; check the ID is 15–19 digits |
"Use the full tweet link" on an /i/web/status/… paste |
Username-less web app route | Open the link in the browser and copy the complete address with the username |
| "Could not reach X" on Generate | Offline, blocked, or the endpoint answered an error – including tweets that no longer exist | Check the connection; allow publish.x.com in blockers; open the tweet on x.com to confirm it exists |
| "X widgets.js failed to load" | Ad blocker or privacy extension blocking platform.x.com |
Whitelist the domain for this page and for your site's visitors' sake test again |
| "The official embed did not render" with a valid-looking link | Deleted post, protected account, or owner-disabled embedding | Verify on x.com; embedding permission is the author's setting, not yours |
| The Video tab refuses a 400-pixel width | Video widths start at 560 by design | Use the Post tab for 220–550; the two ranges serve different layouts |
| My typed width snapped to 550 or 560 | Out-of-range values are clamped when the field loses focus | Intentional – X clamps them server-side anyway; type within the range |
| Works in preview, dead on my site | Site CSP blocks X's domains, or the CMS sanitized the script tag | Add https://platform.x.com (and https://platform.twitter.com for legacy snippets) to script-src and frame-src; paste into raw HTML |
| Changed the language but the tweet text stayed English | By design – data-lang localizes widget chrome, never tweet content |
Expected behavior; the tweet is displayed as authored |
Privacy & Data Handling
This tool is honest about its traffic, which is larger than its zero-request siblings but still direct-to-X only.
- Generation sends the tweet URL to X, once per action. Pressing Generate or changing any option makes one HTTPS GET to
publish.x.com/oembedwith the canonical tweet URL and your options. The request goes from your browser to X's infrastructure – no Toollect server proxies, logs or sees it. - The preview loads X's own machinery. Rendering the card fetches
widgets.jsfromplatform.x.com, the rendered iframe from X's syndication infrastructure, and media from X's image hosts – precisely the traffic the code you copy will produce on your readers' browsers. - Do Not Track propagates. With the option enabled, the generated code carries
data-dnt="true", X's documented signal to exclude the embed and its page views from personalized suggestions and ads. - No account, no storage, no analytics. The tool keeps no state between visits; closing the tab ends everything.
- Publishing the code means your visitors talk to X. That is the nature of embedding – worth stating plainly. If you need a strictly offline X workflow, the X (Twitter) URL Parser on this site performs its entire job with zero network requests.
Technical Specs
Details:
- Endpoint:
GET https://publish.x.com/oembed?url={canonical}– keyless, JSON response, no documented rate limits; parametersmaxwidth,theme,lang,hide_thread,hide_media,align,dnt,omit_script - Input grammar: hosts
x.com/twitter.comwith optionalwww./mobile./m.; username[A-Za-z0-9_]{1,15}; tweet ID 15–19 digits; scheme, trailing slash, view variants (/photo/1,/video/1, …) and query strings tolerated and discarded - Canonical output URL:
https://x.com/{username}/status/{id} - Post width contract: 220–550 px inclusive (endpoint clamps outside values; historical default 325, tool default 550); Video width 560–1920 px applied client-side to
data-media-max-widthbecause the endpoint cannot emit it - Languages: Automatic or any of 34 X widget locales selectable; every explicit selection,
enincluded, writesdata-lang– Automatic alone omits it - Script management: server script tags stripped;
https://platform.x.com/widgets.jsloaded once per session with a 20-second timeout; render watcher polls for the iframe every 500 ms up to 15 seconds, then reports - Re-request policy: every option or tab change issues a fresh oEmbed request; identical in-flight requests are collapsed by a busy guard
- Self-hosting the output requires:
script-srcandframe-srcallowinghttps://platform.x.com(addhttps://platform.twitter.comfor legacy snippets) andimg-srcallowinghttps://abs.twimg.comandhttps://pbs.twimg.com - Browser support: all modern browsers (clipboard API, ES5 runtime required by
widgets.jsitself)
Features
- Generate official X (Twitter) embed code from any tweet URL in one click – Post and Video modes in a single tool
- Match X's own publisher options – light/dark theme, Automatic plus 34 interface languages, precise width, alignment, hide conversation, hide media
- Watch a live preview rendered by X's official widgets.js before you paste the code
- Control width precisely – 220–550 px for posts, 560–1920 px for videos via data-media-max-width
- Respect your visitors – built-in Do Not Track flag and an option to omit duplicate widgets.js tags
- Free browser-based generator with clear diagnostics when X's script is blocked – no API key, no sign-up