A twitter code generator app is a thirty-line tool around one public endpoint
The phrase means two different things depending on who typed it. One reading points at an authenticator. The other points at the thing most writers actually need: an app that takes a tweet URL and returns the exact HTML to paste into a page. This guide is about the second reading, because almost no one builds it. The embed endpoint has been public and key-free since 2014, and the app around it is short enough to read in one screen. mk0r scaffolds it in a sandbox where a real Chromium renders the result while the code is being written.
What every other guide on this topic gets wrong
Open the pages that currently surface for this phrase. They mostly fall into one of two buckets. Some send the reader to Google Authenticator, which is a reasonable answer if the underlying task is two-factor login but has nothing to do with generating code. The rest send the reader to Twitter's own publish.twitter.com web form, which is a decent tool, but it is a form, not a generator. You paste one URL at a time, you copy the block, you never see the response shape, and you cannot script it.
What gets skipped is the most interesting sentence: the endpoint behind that web form is public, accepts GET, returns JSON, sends the right CORS header, and has kept the same contract for more than a decade. Once you know that, the "app" part shrinks to a textbox and a fetch call. The rest of this page walks through the endpoint, the response, the component, and the sandbox that lets you preview the output without leaving the preview iframe.
The endpoint, exactly as it responds today
The first thing worth doing is calling the endpoint yourself. It takes a tweet URL and returns a small JSON object. Every field in the response is stable across rebrands, including the provider_name string that still reads Twitter and the script tag src that still points at platform.twitter.com/widgets.js.
Three things to notice in the response. The html field is the entire embed, ready to paste. The cache_age field is advisory: you can hold a given response in memory for a very long time without re-fetching. The provider_name field still says Twitter, which is the small piece of evidence that the rebrand was a product decision, not an infrastructure one.
The generator component, in full
Below is the entire core of the generator. It is intentionally thin. One state for the input URL, one state for the result, one state for the error message, one async function that branches on HTTP status. Every case the endpoint can return has a distinct message so the reader knows whether the tweet is gone, protected, rate limited, or merely unreachable.
The 30-line count is not marketing. You can paste this into a fresh Vite project, wire it into App.tsx, and the output is a textarea containing the exact HTML you would have copied from publish.twitter.com's web form. The only reason to go longer is to add styling, a copy button, a preview, or the variations listed further down this page.
Why the sandbox matters for this app in particular
Most code-generator tools hand you a string and walk away. For a Twitter embed that string is useless on its own because the rendered card only appears once platform.twitter.com/widgets.js loads and scans the DOM for blockquote.twitter-tweet. You need a real browser to see whether the embed you just generated actually renders. The mk0r sandbox ships that browser. Chromium launches on a virtual display before the first prompt lands, with a persistent profile and remote debugging exposed over CDP, and Vite serves your app into the same machine.
Ports matter here. Chromium exposes CDP on 9222, Playwright MCP listens on 3001, the ACP bridge is on 3002, Vite binds 5173, x11vnc is on 5900, and websockify bridges that VNC stream to WebSocket on 5901. All of those are up before the agent writes its first file. When the generator fetches the embed and drops it into the DOM, the browser that evaluates the result is the one already running in the VM.
The preview component, in full
The second file the agent writes is a preview. It takes the HTML string, drops it in a div, loads widgets.js once per page, and asks the script to re-scan when the string changes. This is the part most hand-written generators skip. Without it, the user has to paste the blockquote into another site to see whether the tweet rendered, which ruins the point.
End to end, from prompt to rendered tweet
The chain below is the observable path a single prompt walks inside the sandbox. Every arrow is a real process boundary, not a metaphor. The final step is the same Chromium that listens on port 9222 rendering the final card, and you see it over the websockify stream that started booting before you opened the page.
Prompt, endpoint, sandbox, preview
The turn-by-turn sequence
Broken out so you can see which file lands when, and where each event surfaces. The HMR events come from the _mk0rBridge.ts file baked into every sandbox at /app/src/_mk0rBridge.ts. It posts five message types to the parent window so the preview iframe knows when to react.
Prompt submitted
You type 'build a twitter embed code generator' on mk0r.com and press enter. No email, no card, no modal.
Sandbox warms up
A pre-baked E2B template (2yi5lxazr1abcs2ew6h8 in production) claims a VM. Xvfb, Chromium, Playwright MCP, ACP, Vite, x11vnc, and websockify all come up from startup.sh before the agent writes anything.
Agent reads the scaffold
The agent opens /app/CLAUDE.md, finds the 'do not modify' files including _mk0rBridge.ts, and plans the two components it needs: EmbedGenerator and EmbedPreview.
Agent writes EmbedGenerator.tsx
Vite picks up the write through its file watcher, emits vite:beforeUpdate and vite:afterUpdate, and the bridge forwards both events to the parent window.
Agent writes EmbedPreview.tsx
Same HMR cycle. The bridge posts hmr:before and hmr:after a second time. The preview iframe now contains a textarea and a live blockquote container.
You paste a URL and click generate
The generator fetches publish.twitter.com/oembed from inside the iframe. CORS allows it. The JSON arrives, the html field lands in the textarea.
widgets.js scans the DOM
The preview component appends the script once to document.head. Twitter's loader finds the blockquote, upgrades it to a rendered card, and Chromium paints it.
You see it over the VNC WebSocket
websockify on 5901 streams the Chromium frame buffer to your browser. What you watch is the real rendered embed, not a thumbnail.
Variations on the same app
The core is fixed: one endpoint, one fetch, one textarea. The interesting work is around the edges, and each variation below is a one-sentence follow-up prompt to the same running sandbox. None of them add more than about 40 lines to the project.
One-tweet embed
The base case. Paste a URL, copy the blockquote, done. The generator is an input, a fetch, and a textarea. Under 30 lines. The agent writes it on the first turn.
Tweet-to-image card
Replace the blockquote with a PNG rendered from the tweet text via html2canvas. The generator adds a Download button. The sandbox writes files to /app and the preview iframe picks up the new route instantly.
Thread-to-markdown
Paste the first tweet of a thread. The generator walks conversation_id through the oembed endpoint and concatenates each tweet's text into a markdown document. Useful for archiving threads before they vanish.
Bulk URL paste
One textarea with many URLs, one button, a list of embeds. The generator uses Promise.all with a small concurrency limit so you do not trip the 75 requests per minute ceiling on publish.twitter.com.
Embed plus analytics
Wire the pre-provisioned PostHog keys from /app/.env so every copy click fires an event. You see which tweets your readers actually grab, not just which ones you shared.
Self-hosted deploy
The sandbox is a normal Vite project at /app. Download the folder, push to GitHub, deploy anywhere static. No mk0r runtime required after the initial scaffold.
What the bridge logs during a normal session
Here is a trimmed trace from a real session where the agent built this generator for the first time. The bridge prints one line per HMR event and every event also posts to the parent iframe, which is how the preview knows when to re-mount.
Versus the status quo of copying from a web form
The publish.twitter.com web form is fine for one tweet, once, manually. Every other case is worse than it needs to be. The generator version of this app gives you control over every field in the response, lets you paste many URLs at once, and renders the result next to the code instead of making you paste the blockquote elsewhere to check.
| Feature | publish.twitter.com web form | mk0r-built generator |
|---|---|---|
| Source of embed code | Copy from publish.twitter.com web UI, one tweet at a time | Direct GET to publish.twitter.com/oembed, JSON response, no UI in the loop |
| Preview while generating | No preview, you paste the blockquote elsewhere and hope | Real Chromium in the sandbox evaluates widgets.js, live preview in the iframe |
| omit_script control | Hidden or off by default, every tweet drags its own widgets.js | Exposed as a checkbox, so you load widgets.js once globally |
| Error handling | Silent failure or a blank card for deleted or protected tweets | Distinct messages for 404, 403, 429, anything else, written by the agent on turn one |
| Source code ownership | You are a user of someone else's widget | A Vite plus React project at /app that you download and host yourself |
| Account required to build | Twitter developer account for API access | None, oembed is public, and mk0r itself does not ask for signup |
What is worth taking from this
If you build code generators for anything, the lesson is not the specific endpoint. The lesson is that the "generator" category hides a lot of single-endpoint problems where the UI is the only interesting work. oembed is one. Gravatar is another. GitHub's raw file endpoint is a third. Each of them is a 0-line React component around one fetch. The value of a sandbox is not that it makes the component shorter, it is that it lets you render the output of the component in the same environment you wrote it, so you never have to leave the tab to verify the result.
Want to watch this generator get built in a live sandbox?
Book a 20-minute session. I will share the screen, type the prompt, and we will watch the embed render together inside the sandbox Chromium.
Frequently asked questions
What does a 'twitter code generator app' actually mean?
It is ambiguous on purpose. Some people type this phrase looking for an authenticator that produces the 6-digit login code for Twitter two-factor auth. Others type it looking for a tool that returns the HTML embed code for a given tweet URL, so they can paste the tweet into a blog post or a landing page. The first reading is covered by Google Authenticator and Authy. The second reading is what almost no guide builds, and it is what this page is about: a small React app that turns a tweet URL into a blockquote-plus-script you can paste anywhere.
Is there really a public endpoint that returns the embed code?
Yes. Twitter has served the same URL since 2015: https://publish.twitter.com/oembed. You send a GET request with ?url=<tweet_url>&omit_script=true and you get back JSON with an html field containing the blockquote. No token, no OAuth, no API key. The endpoint predates the X rename and still resolves at the publish.twitter.com hostname. You can curl it from a terminal right now.
What is the omit_script parameter and which value should the generator use?
The oembed JSON, by default, appends a script tag that loads platform.twitter.com/widgets.js. If you embed ten tweets on one page, you get ten copies of the same script tag. omit_script=true removes it, so the generator can return a cleaner blockquote and the app that uses it includes widgets.js once globally. A well-built twitter code generator app should expose omit_script as a toggle and explain the tradeoff.
What is inside the mk0r sandbox that makes this different from other code generator tools?
The sandbox runs a real Chromium on a virtual display before the first prompt arrives. It is launched with a persistent profile at /root/.chromium-profile and remote debugging on port 9222. Playwright MCP binds 3001, the ACP bridge binds 3002, Vite binds 5173, VNC binds 5900, and websockify bridges VNC to WebSocket on 5901. The Twitter embed your generator produces renders in that browser, and the rendered page streams to your preview iframe. You are not looking at a screenshot, you are watching a live browser evaluate Twitter's widgets.js.
How long is the actual generator component?
Around 30 lines of TypeScript for the core behavior. One useState for the URL input, one useState for the embed HTML, one fetch to publish.twitter.com/oembed, one JSON parse, one textarea that shows the result, one button that copies. That is the whole app. Styling with Tailwind adds another 20 to 30 lines depending on how polished you want the card. Every other guide on this skips the fact that it is small enough to read in one screen.
Does the generator need CORS handling?
publish.twitter.com/oembed returns Access-Control-Allow-Origin: *, so a browser-only app can fetch it directly without a proxy. This is a real check: open your devtools network panel after pasting a URL and you will see one request go out, one JSON response come back, no preflight error. That is the entire reason the app can be client-side only.
Can the app preview the embed inline?
Yes. Set the returned html field as dangerouslySetInnerHTML on a div, then append a single script tag with src='https://platform.twitter.com/widgets.js' to the document head. Twitter's widget loader scans for blockquote.twitter-tweet elements and upgrades them to rendered cards. Inside the mk0r sandbox, this happens in a real Chromium that already has network access, so the rendered embed looks identical to what a visitor on a production site would see.
What about X-rebranded endpoints, does anything still say 'twitter'?
Yes. publish.twitter.com still resolves. The oembed JSON's provider_name field returns 'Twitter'. The widget script src is platform.twitter.com/widgets.js. The rendered blockquote has class twitter-tweet. The rebrand happened at the product level; the embed infrastructure kept the old hostnames because breaking them would break every article on the web that already uses them.
Can the generator handle protected, deleted, or rate-limited tweets?
The oembed endpoint returns an HTTP 404 for deleted tweets, a 403 for protected accounts, and a 429 when you exceed the soft rate limit of roughly 75 requests per minute per IP. A well-built generator shows each case as a distinct message instead of a generic 'something went wrong'. mk0r scaffolds that error branching automatically if you ask for it, because the VM agent reads the fetch response status and writes the matching UI state.
Do I need an account to scaffold this in mk0r?
No. Open mk0r.com, type 'build a twitter embed code generator' and press enter. A sandbox boots in about 2.5 seconds from a pre-warmed E2B template. Vite is already listening on 5173 inside the sandbox. The agent writes App.tsx and a small EmbedGenerator.tsx, the bridge reports hmr:after, the preview iframe shows the app, and you paste a tweet URL into the input it just built.