twitterapi.io is an independent third-party service. Not affiliated with X Corp.

Blogfxtwitter

What Is fxtwitter (FixupX) and How Do You Use It in Discord?

By Michael Park•14 min read
Diagram of how fxtwitter and fixupx work: an x.com link is changed to fixupx.com, Discord's crawler requests it, the FxEmbed worker fetches the post from X and returns a rich preview with video and all images, while a person who clicks is redirected to x.com
Swap the domain, keep the path. Crawlers get rich metadata; people who click are redirected to the original post on x.com.

Paste an X link into Discord and you usually get a still image with a few lines of text. You don't get a playable video or the second, third and fourth photos, and polls show no results. fxtwitter exists because of that gap. It is a link proxy that sits between the chat app's crawler and X and answers the crawler with the metadata the app needs to build a full preview.

This guide covers what fxtwitter and FixupX are, every URL modifier their docs list, and how they compare with vxTwitter and fixvx. Each feature claim is checked against the FxEmbed and BetterTwitFix projects' own documentation and READMEs. We also sent Discord's crawler user agent to every domain from our network in 2026 and recorded the status codes, the preview tags each domain returned and how long it took. The last section is for developers who want to build their own preview bot from post data.

Scope note: this page is about link previews inside chat apps. To show posts on your own website with the official widget, oEmbed or static cards, see the embed-tweets-on-website guide linked at the bottom.

01 — Section

How fxtwitter and FixupX work

Discord, Telegram and Slack never open a link the way you do. When a message contains a URL, the app's server sends its own crawler (Discord identifies itself as Discordbot, Telegram as TelegramBot) to fetch the page and read its OpenGraph and Twitter Card tags. Whatever those tags describe becomes the preview. FxEmbed's getting-started docs describe the result: videos and GIFs play inline, multiple images show as a gallery, polls show results, quotes appear with their text and media, and t.co short links are replaced with the real destination.

FxEmbed runs as a Cloudflare Worker, according to its README. When a crawler requests fixupx.com/NASA/status/2105698284354367897, the worker fetches that post from X and writes a response made for that crawler. We confirmed this by sending different user agents to the same link in 2026. Discordbot got a page that points to a Mastodon-style status JSON listing all four photos as separate attachments. That appears to be how FxEmbed delivers the native multi-image layout its docs list as a Discord feature. TelegramBot got og:video tags that point through FxEmbed's media proxy. Slackbot got og:video tags that point straight at the MP4 on video.twimg.com.

A normal browser user agent got an HTTP 302 redirect to x.com/NASA/status/2105698284354367897. Readers end up on the real post, so sharing a fixed link doesn't send your friends to a mirror site.

FxTwitter, FixupX and twittpr are the same service. FxEmbed's FAQ says all three domains run the same worker and behave the same way. fxtwitter.com is the main domain for twitter.com links, and fixupx.com exists because people now copy x.com links. twittpr.com is there for a Discord shortcut: post a twitter.com link, then send s/e/p and Discord edits it into twittpr.com. The FAQ also lists xfixup.com as an official domain. Any other domain beginning with "fx" is not part of the project.

02 — Section

fxtwitter vs fixupx vs vxtwitter vs fixvx vs plain x.com

There are two families of fixers. FxEmbed runs fxtwitter.com, fixupx.com and twittpr.com, and BetterTwitFix runs vxtwitter.com and fixvx.com. Within a family the domains behave the same, so the real choice is between the two projects. The matrix below combines FxEmbed's own comparison page, the BetterTwitFix README and the responses we recorded with a Discordbot user agent on a video post and a four-photo post. Plain x.com is included as the baseline.

Feature (in Discord)fxtwitter.comfixupx.comvxtwitter.comfixvx.complain x.com
Swap it in fortwitter.comx.comtwitter.comx.comn/a
Inline video and GIFsYesYesYesYesNo video tag returned (measured)
All images of a 4-photo postYes, native multi-image (measured)Yes, native multi-image (measured)Yes, 4 og:image tags (measured)Yes, after redirect (measured)1 image (measured)
Poll resultsYesYesYes (FxEmbed docs)Yes (FxEmbed docs)No
Quoted postWith mediaWith mediaWithout media (FxEmbed docs)Without media (FxEmbed docs)No
Translation suffix (/en, /es)Yes (tested /es)YesNoNoNo
Direct-media link (d. prefix)302 to file (tested)302 to file (tested)Returned a page, not the file (tested)Not testedNo
Gallery, text-only, mosaic modesg. / t. / m.g. / t. / m.c. combines images (README)Not documentedNo
Telegram Instant ViewYesYesNoNoNo
Extra redirect hop for crawlersNoneNoneNone302 to vxtwitter.comn/a
Median response to Discordbot (n=20)1.55 s1.49 s1.42 s2.92 s1.92 s
Public status pagestatus.fxtwitter.comstatus.fxtwitter.comNone linked in READMENone linked in READMEn/a
Source code / licenseGitHub, MITGitHub, MITGitHub, WTFPLGitHub, WTFPLn/a

Which should you use? For everyday sharing in Discord or Telegram, FixupX for x.com links (or fxtwitter for twitter.com links) covers the most cases. It is the only one of the two families with translation, gallery/text-only modes and Telegram Instant View, and it has a public status page. vxTwitter is a sensible backup when FxEmbed is having a bad day, because the two projects run on separate infrastructure. If you use the vx family, link to vxtwitter.com directly: fixvx.com added a redirect hop that roughly doubled the median time in our test.

Rows marked "FxEmbed docs" come from FxEmbed's comparison page, which is written by one of the two projects being compared. We could reproduce the media rows ourselves, but we did not test vxTwitter's poll and quote behavior separately.

03 — Section

URL modifiers: d., g., t., m., i., o. and the translate suffix

FxEmbed supports a set of subdomain prefixes and path suffixes that change what the preview shows. Its docs say each modifier works on fxtwitter.com, fixupx.com and fxbsky.app. Modifiers can be combined where it makes sense, for example d. with /photo/3.

ModifierExampleWhat it does
d. prefixd.fixupx.com/NASA/status/2105698284354367897Skips the embed and links straight to the media file. Tested: 302 to the original-size image on pbs.twimg.com
.mp4 or .jpg suffixfixupx.com/NASA/status/2107095768821576019.mp4Same as d., written as a file extension
/photo/N or /video/Nfxtwitter.com/NASA/status/2105698284354367897/photo/3Embeds only the Nth photo or video (counting from 1)
g. prefixg.fixupx.com/NASA/status/2105698284354367897Gallery: media and author only, no post text or counts
t. prefixt.fixupx.com/NASA/status/2105698284354367897Text only, no photos or video
m. prefixm.fixupx.com/NASA/status/2105698284354367897Mosaic: combines the images into one picture (the default outside Discord)
i. prefixi.fixupx.com/NASA/status/2105698284354367897Forces Telegram Instant View, which unrolls the whole thread
o. prefixo.fixupx.com/NASA/status/2105698284354367897Old Discord embed style, for older clients where rich embeds break video
/xx language suffixfixupx.com/NASA/status/2105698284354367897/esShows the original text plus a translation into the two-letter ISO 639-1 language

We tested the translation suffix with Telegram's user agent and the Spanish code /es. The returned description started with "Traducido del ingles" ("Translated from English") and was followed by the translated text. The d. prefix on a video post redirected to an MP4 served through FxEmbed's media proxy, which is the quickest way to get a shareable video file link.

Making the swap automatic. Retyping domains gets old. The search results for "fxtwitter" are mostly browser extensions for Chrome and Firefox that rewrite copied X links, plus Reddit threads asking for one. Before installing one, check which sites it is allowed to read, because a link rewriter only needs access to x.com. The s/e/p edit trick with twittpr.com needs no install at all, but it only works on twitter.com links.

04 — Section

Measured: response time and uptime from our network

To see how the domains behave in practice, we requested the same two public NASA posts (a video post and a four-photo post) from every domain with Discord's Discordbot/2.0 user agent. We ran 10 rounds in random order, which gave 20 requests per domain, and followed redirects. All 120 requests ended in HTTP 200. Our test network goes through an HTTP proxy, so the absolute numbers are higher than a direct connection would give. The useful comparison is between domains.

DomainRequests OKMedianFastest 10%Slowest 10%
vxtwitter.com20 / 201.42 s1.30 s1.64 s
fixupx.com20 / 201.49 s1.33 s1.80 s
fxtwitter.com20 / 201.55 s1.37 s2.55 s
twittpr.com20 / 201.58 s1.26 s1.66 s
x.com (no fixer)20 / 201.92 s1.53 s3.42 s
fixvx.com20 / 202.92 s2.83 s3.11 s

Three things stand out. The FxEmbed domains and vxtwitter.com were close, within about 0.16 s of each other at the median. fixvx.com was consistently the slowest, because every crawler request first gets a 302 to vxtwitter.com and then has to be fetched again. And x.com itself was not faster than the fixers, even though it only returned a single preview image.

Uptime. FxEmbed publishes per-component monitors at status.fxtwitter.com. When we checked in 2026, the "Embeds (X/Twitter)" monitor showed 99.97% uptime over the last 24 hours. The 30-day uptime badge linked from the FxEmbed README read 79.02% for the same monitor, a figure that implies significant downtime during the previous month. Free fixers depend on X continuing to serve them data. vxTwitter's own error message names "recent changes to Twitter's API" as one reason a link can fail. If previews suddenly stop for every post, check the status page first. We found no status page linked from the BetterTwitFix README.

Strip plot of 20 response times per domain to a Discordbot request, measured in 2026: vxtwitter.com median 1.42 s, fixupx.com 1.49 s, fxtwitter.com 1.55 s, twittpr.com 1.58 s, x.com 1.92 s, fixvx.com 2.92 s because of a redirect hop to vxtwitter.com
Each dot is one request with Discord's user agent; the white tick is the median. Measured from one proxied test network in 2026, so compare domains rather than absolute seconds.
05 — Section

fxtwitter not working? A troubleshooting checklist

Work through these in order. Most "fxtwitter is broken" reports come down to the first three.

1. The post isn't public. Fixers can only preview what a logged-out visitor could see. For a post that no longer existed, fxtwitter returned the description "This post is unavailable :(" and vxTwitter returned "Failed to scan your link!", listing incorrect links, private or suspended accounts and deleted posts as likely causes. Posts from protected (private) accounts fall in the same group.

2. The embed was cached. Chat apps store a preview the first time they see a URL. FxEmbed's FAQ advises changing the link before you send it, because embeds usually don't update after the message is sent. If you sent a plain x.com link first, editing it may not bring the full preview. Delete it and send the fixed link as a new message. In Telegram, FxEmbed suggests clearing the cached preview with Telegram's Webpage Bot.

3. Embeds are suppressed in that message or channel. Discord's message object has a SUPPRESS_EMBEDS flag that hides all embeds on a message. If someone removed the preview, or the channel doesn't allow link previews for you, no fixer will show one. Test the same link in a DM to yourself.

4. The service is having trouble. Check status.fxtwitter.com. If FixupX is down, send the same path on vxtwitter.com instead (or the other way around). The two projects run on separate infrastructure.

5. Video won't play on an older Discord client. FxEmbed added the o. prefix (for example o.fixupx.com/...) to force the older embed style for clients where rich embeds break video.

6. The link has a typo or the wrong domain. fixupx.com is meant for x.com links and fxtwitter.com for twitter.com links. The path after the domain is the same either way, and both domains served our test posts. Adding "fx" in front of "x.com" gives fxx.com, which is not on the list of domains in FxEmbed's FAQ.

7. Telegram never shows a preview. FxEmbed's FAQ notes that Telegram's preview servers sometimes never request the URL at all, often because they are overloaded. That is outside the fixer's control. Resend the link later or use the Webpage Bot.

06 — Section

For developers: build your own preview bot with post data

Public fixers are fine for people sharing links by hand. They are a weak foundation for a product. They have no SLA, the 30-day uptime figure above shows what an outage month looks like, and FxEmbed's own JSON API (api.fxtwitter.com) is limited to 1,000 requests per minute per IP address according to its docs. If you run a Discord bot that reposts an account's posts, a moderation tool that archives shared links, or a Telegram channel mirror, you need post data you control. With that data you can build the embed yourself.

The pattern is short. Pull the post IDs out of incoming links, fetch the posts in one batch, and map the fields onto Discord's embed object. TwitterAPI.io's GET /twitter/tweets accepts a comma-separated tweet_ids list and returns flat post objects: text, author.userName, author.profilePicture, likeCount, retweetCount, replyCount, viewCount, and an extendedEntities.media array with each photo's media_url_https and each video's video_info.variants. The snippet below rewrites links the way FixupX does and checks what media each post carries before you build anything.

Discord's limits shape the output. Its developer docs cap an embed description at 4,096 characters and all embed text in one message at 6,000 characters, allow up to 10 rich embeds per message, and deduplicate embeds that share a URL. Because of that last rule, the full relay script at the end of this page sends one rich embed per post and puts any extra photos and the video as direct file links in the message content, where Discord unfurls them. That is the same idea as FxEmbed's d. prefix.

What it costs, by source. Pricing is per post fetched:

SourcePrice per post read10,000 postsLimits / caveats
TwitterAPI.io /twitter/tweets$0.00015 ($0.15 per 1,000)$1.50Pay per use, no minimum spend; minimum charge $0.00015 per call, so batch IDs
Official X API (Posts: Read)$0.005$50.00Same post re-read within one UTC day is charged once
api.fxtwitter.comFree$01,000 requests/min per IP, no SLA, unofficial

The 33x figure is simple division: $0.005 / $0.00015 = 33.3, using the prices on docs.x.com and twitterapi.io/pricing. The 10,000-post column is that per-post price times 10,000. X's same-day deduplication narrows the gap if your bot keeps re-reading the same few posts. It doesn't help when most links are new, which is the usual case for a link-preview bot.

python
"""Turn x.com links into fixupx.com links and check what a preview would carry.

pip install requests
export TWITTERAPI_IO_KEY=your_key
"""
import os
import re

import requests

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
POST_URL = re.compile(r"https?://(?:www\.|mobile\.)?(?:x|twitter)\.com/(\w{1,15})/status/(\d+)")


def fix_link(url: str) -> str:
    """x.com -> fixupx.com, twitter.com -> fxtwitter.com; path unchanged."""
    url = re.sub(r"https?://(?:www\.|mobile\.)?x\.com/", "https://fixupx.com/", url)
    return re.sub(r"https?://(?:www\.|mobile\.)?twitter\.com/", "https://fxtwitter.com/", url)


def lookup(ids: list[str]) -> dict:
    """One call to /twitter/tweets returns every requested post."""
    r = requests.get(
        "https://api.twitterapi.io/twitter/tweets",
        headers=HEADERS,
        params={"tweet_ids": ",".join(ids)},
        timeout=20,
    )
    r.raise_for_status()
    return {t["id"]: t for t in r.json().get("tweets", [])}


message = "Launch recap https://x.com/NASA/status/2105698284354367897 and clip https://x.com/NASA/status/2107095768821576019"
ids = [m.group(2) for m in POST_URL.finditer(message)]
posts = lookup(ids)

print(POST_URL.sub(lambda m: fix_link(m.group(0)), message))
for pid in ids:
    t = posts.get(pid)
    if not t:
        print(pid, "-> not returned (deleted, private or suspended)")
        continue
    media = (t.get("extendedEntities") or {}).get("media") or []
    kinds = [m.get("type") for m in media]
    print(pid, "@" + t["author"]["userName"], kinds, "likes:", t.get("likeCount"))
07 — Section

Privacy, NSFW posts and other limits worth knowing

Privacy. FxEmbed's FAQ says it logs only runtime errors (URL and basic headers for errors that break embeds), post IDs when its X account proxy fails, and anonymous Cloudflare Analytics Engine statistics. It says it does not record who posted or clicked a link, and that it strips tracking parameters such as ?s and &t when it redirects. The BetterTwitFix README says vxtwitter.com runs with link caching disabled and that the maintainer does not monitor processed posts. These are the projects' own statements, and we can't audit them from outside. Both projects are open source on GitHub, so you can read the code or self-host it if that matters to you.

Sensitive (NSFW) posts. FxEmbed's status page lists a separate "API (X/Twitter, NSFW)" monitor, which suggests these posts go through their own pipeline. We did not test sensitive posts, so we make no claim about which fixer handles them better. Also check your server's rules before you work around a content filter.

Not a downloader or an archive. Fixers fetch the post when the crawler asks for it. If the author deletes the post, the next preview fails, and a preview that was already posted lasts only as long as the chat app keeps its cached copy. If you need a copy of the media, download it on purpose. The media download guides linked below cover single-file and bulk approaches.

Bluesky is out of scope here. FxEmbed also runs fxbsky.app for Bluesky links. This page covers X links only.

python
"""Minimal self-hosted link-preview relay for Discord.

Give it X post URLs; it fetches the posts in one batch from TwitterAPI.io and
posts one rich embed per post to a Discord channel webhook. Extra photos and
videos/GIFs go into the message content as direct file links, because Discord
deduplicates embeds that share a URL and unfurls direct media links itself.

pip install requests
export TWITTERAPI_IO_KEY=your_key
export DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/<id>/<token>
python relay.py https://x.com/NASA/status/2105698284354367897 https://x.com/NASA/status/2107095768821576019
"""
import os
import re
import sys
import time

import requests

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
WEBHOOK = os.environ["DISCORD_WEBHOOK_URL"]
POST_URL = re.compile(r"(?:x|twitter)\.com/\w{1,15}/status/(\d+)")


def fetch_posts(ids: list[str]) -> list[dict]:
    posts = []
    for i in range(0, len(ids), 20):              # batch IDs: one call per 20 posts
        r = requests.get(
            "https://api.twitterapi.io/twitter/tweets",
            headers=HEADERS,
            params={"tweet_ids": ",".join(ids[i:i + 20])},
            timeout=20,
        )
        r.raise_for_status()
        posts.extend(r.json().get("tweets", []))
    return posts


def best_mp4(item: dict) -> str | None:
    variants = (item.get("video_info") or {}).get("variants") or []
    mp4s = [v for v in variants if v.get("content_type") == "video/mp4"]
    return max(mp4s, key=lambda v: v.get("bitrate") or 0)["url"] if mp4s else None


def to_discord(post: dict) -> tuple[str, list[dict]]:
    author = post.get("author") or {}
    media = (post.get("extendedEntities") or {}).get("media") or []
    photos = [m["media_url_https"] for m in media if m.get("type") == "photo"]
    clips = [u for u in (best_mp4(m) for m in media if m.get("type") in ("video", "animated_gif")) if u]
    footer = (f"Replies {post.get('replyCount', 0)} | Reposts {post.get('retweetCount', 0)} | "
              f"Likes {post.get('likeCount', 0)} | Views {post.get('viewCount', 0)}")
    first = {
        "type": "rich",
        "url": post.get("url"),
        "description": (post.get("text") or "")[:4000],      # Discord caps description at 4096
        "color": 0x1D9BF0,
        "author": {"name": f"{author.get('name', '')} (@{author.get('userName', '')})",
                   "url": f"https://x.com/{author.get('userName', '')}",
                   "icon_url": author.get("profilePicture")},
        "footer": {"text": footer},
    }
    if photos:
        first["image"] = {"url": photos[0]}
    links = clips[:1] + photos[1:4]                  # direct file links Discord can unfurl
    return "\n".join(links), [first]


def main(urls: list[str]) -> None:
    ids = [m.group(1) for u in urls if (m := POST_URL.search(u))]
    if not ids:
        sys.exit("No X post URLs given")
    found = {p["id"]: p for p in fetch_posts(ids)}
    for pid in ids:
        post = found.get(pid)
        if not post:
            print(f"{pid}: not returned (deleted, private or suspended), skipped")
            continue
        content, embeds = to_discord(post)
        r = requests.post(WEBHOOK, json={"content": content, "embeds": embeds}, timeout=20)
        if r.status_code == 429:                       # Discord rate limit: wait and retry once
            time.sleep(float(r.json().get("retry_after", 2)))
            r = requests.post(WEBHOOK, json={"content": content, "embeds": embeds}, timeout=20)
        r.raise_for_status()
        print(f"{pid}: posted (media links in content: {len(content.splitlines())})")


if __name__ == "__main__":
    main(sys.argv[1:])
08 — Questions

Questions readers ask

What is fxtwitter?

fxtwitter (fxtwitter.com) is a free, open-source service from the FxEmbed project that fixes X (Twitter) link previews in Discord, Telegram and other chat apps. Replace twitter.com with fxtwitter.com, or x.com with fixupx.com, and the preview shows inline video, every image, poll results, quotes and engagement counts. People who click are redirected to the original post on x.com.

What is the difference between fxtwitter and fixupx?

There is none in behavior. FxEmbed's FAQ says fxtwitter.com, fixupx.com and twittpr.com run the same worker. fxtwitter.com is meant for twitter.com links and fixupx.com for x.com links, so you only change the start of whichever link you copied.

Is fxtwitter or vxtwitter better?

For most people FxTwitter/FixupX, because it adds translation, gallery and text-only modes, Telegram Instant View and a public status page. vxTwitter covers the core features (video, multiple images, polls) and runs on separate infrastructure, which makes it a good fallback. In our 2026 test vxtwitter.com and fixupx.com answered Discord's crawler at similar speeds (1.42 s and 1.49 s median). fixvx.com was slower at 2.92 s because it redirects to vxtwitter.com first.

Why is fxtwitter not working in Discord?

The usual causes, in order: the post is private, deleted or from a suspended account; Discord cached the preview from the original link, so send the fixed link as a new message; embeds are suppressed on that message or channel; or the service is having an outage, which status.fxtwitter.com will show. If FixupX is down, try the same path on vxtwitter.com.

Does fxtwitter work with private or protected accounts?

No. Fixers can only read what a logged-out visitor can see, so posts from protected accounts, deleted posts and suspended accounts don't preview. In our test fxtwitter returned 'This post is unavailable :(' for a post that no longer existed, and vxTwitter returned a 'Failed to scan your link!' message.

How do I translate a tweet with fxtwitter?

Add a two-letter ISO 639-1 language code to the end of the link, for example fixupx.com/user/status/123/en for English or /es for Spanish. The preview shows the original text together with the translation. We tested /es in 2026 and the preview began with 'Traducido del ingles'.

Can fxtwitter download videos?

It can give you a direct link to the file. Add d. in front of the domain (d.fixupx.com/user/status/123) or put .mp4 at the end of the path, and the link redirects to the media file instead of showing an embed. For saving many videos or images at once, use an API-based bulk download instead.

Does fxtwitter have an API?

Yes. FxEmbed publishes a JSON API at api.fxtwitter.com with post, thread, profile and search endpoints, limited to 1,000 requests per minute per IP address according to its docs. It is free and unofficial, with no SLA. For production volume, a paid post-data API such as TwitterAPI.io's /twitter/tweets ($0.15 per 1,000 posts) gives you predictable access.

09 — 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
    FxTwitter: Fix X Embeds in Discord | TwitterAPI.io