Editor
mango.onl/editor is a Luau editor built for Roblox code. It's the same editor engine VS Code uses, with a Luau parser, linter, Roblox's real compiler and the full Roblox API bolted on. It's meant for reading and fixing scripts, including the huge one-line ones people hand each other, not for replacing Studio.
Everything runs in your browser. Nothing you open is uploaded unless you hit Share. No account is needed except for sharing and the sandbox.
The layout is three columns: your files on the left, the code in the middle, and the panels on the right. Everything the editor can do is in the menus along the top (File, Edit, Go, View, Tools, AI, Help), and Commands (F1) searches every one of them. Save, Beautify and Share have their own buttons.
The panels are Problems, UI preview, DataStore check, Syntax tree, Bytecode and Stats. Their tabs run down the right edge and never go away: click a tab to open it, and click the open one again to fold the panels away. Drag the panels' left edge to make them wider. Ctrl+B hides the file list. The editor remembers all of this, and which panel you had open. On a phone the editor is read-only; it's not built for typing on a touchscreen.
Opening and saving
There are a few ways to get code in:
- Paste it. A blank file is open when you arrive. If you paste code
wrapped in
```luafences (the way AI assistants return it), the fences are stripped automatically. Ctrl+Z puts them back if you wanted them. - File → Open files takes one or more
.lua,.luauor.txtfiles. - Drag and drop files, or a whole folder, anywhere on the page.
- File → Open folder (Chrome and Edge only) opens a project folder with read
and write access. It skips
.git,node_modules,Packagesand.vscode, so a Wally project doesn't drown you in dependencies.
Save (Ctrl+S) is always there. A file from an opened folder is written back in place. Anything else, like something you pasted, dropped or opened from a share, asks where to save it (Chrome and Edge), and from then on Ctrl+S writes straight to that file. Firefox and Safari don't let a page save files, so there Save downloads the file instead. Save as is Ctrl+Shift+S, Save all saves every changed file in an opened folder, and File → Download a copy always works.
Saving is always manual; the editor never writes to your disk on its own. If a file changes on disk while you have it open (Rojo syncing, another editor), you'll get a notice. If you have no unsaved edits it reloads; if you do, it asks you to decide.
Files you pasted or dropped are kept in your browser between visits (up to 20 files, each under 400 KB), so closing the tab doesn't lose them. Folder files aren't copied; the editor asks to reopen the folder instead. Right-click a file, or press F2 in the file list, to rename it. Opening a share link adds its files next to yours; it doesn't replace them.
Folders
Use the folder button at the top of the file list (or File → New folder) to make a folder, and drag files onto it to move them. Drag a file onto empty space to move it back to the top. Right-click a folder to add a file inside it, rename it or remove it. Removing a folder never deletes files; they move up a level. A folder opened from disk shows its real folders. Those files aren't moved here, because they mirror your disk: move them in your file manager instead.
Big and minified files
Obfuscated scripts are usually one line that's megabytes long, and that's what breaks most web editors. This one scales features back as files get bigger instead of freezing:
| Size | What changes |
|---|---|
| Under 512 KB | Everything on. |
| 512 KB to 5 MB | Folding, occurrence highlighting, type hints and semantic colours off. Go to definition and rename still work. |
| 5 MB to 12 MB | Minimap, word suggestions, go to definition and rename off too. |
| Over 12 MB | View and edit only. No analysis. |
The badge in the bottom right tells you which level a file is on. Any line over 10,000 characters is cut off visually so it can't lock the tab, and you'll be told the file looks minified. Hit Beautify (Ctrl+Shift+F) to reformat it into normal lines. Beautify is one undo step, so Ctrl+Z takes you straight back.
Type hints and colours
Inlay hints show what a local holds after its name, in grey:
local plr = Players.LocalPlayer gets : Player,
local pos = part.Position gets : Vector3. They come from the
same Roblox API data as autocomplete, and appear only when the editor is sure.
Hover a name for its type, where it was declared and how often it's read.
Semantic colours paint parameters, local functions and your own globals differently from ordinary locals, and fade anything that's never read. In obfuscated code where every name looks alike, that's often the fastest way to see what's what. Both can be switched off in View; the setting is remembered.
Roblox API autocomplete
The editor knows the whole Roblox API: every class, property, method, event
and enum, with parameter and return types. It gets this from the same place
Studio does. On load it asks Roblox which Studio build is live, then downloads
that build's API-Dump.json from Roblox's CDN. If Roblox's CDN won't
answer, it falls back to the copy MaximumADHD's Roblox Client Tracker publishes
on GitHub.
The dump is about 7 MB. It's boiled down to under 1 MB and kept in your browser, so it's only downloaded again when Roblox ships a new Studio build. The badge in the status bar shows which build you have. Click it to check for a newer one.
What completes
player.lists properties and events,player:lists methods. Own members come first, inherited ones after, deprecated ones last and struck through.- Inside
game:GetService("you get every service name. InsideInstance.new("you get only classes that can actually be created.IsA,FindFirstChildOfClassand friends complete class names too. :GetPropertyChangedSignal("lists the properties of whatever it's called on.Enum.lists enums,Enum.KeyCode.lists its items with their values.- Typing
local Playersat the start of a line offers the whole= game:GetService("Players")line.
How it knows what a variable is
There's no full type checker, so the editor follows the patterns Roblox code actually uses:
game,workspace,scriptandpluginare known.local x = game:GetService("Players")andInstance.new("Part")givexthat class.local cam: Camera = ...uses the annotation.- Chains follow the dump:
Players.LocalPlayer.Characteris aModel.FindFirstChildOfClass("Humanoid")returns aHumanoid,:Clone()keeps the type. - Something like
workspace.Map.Doorcan't be known ahead of time, so it's treated as a plainInstance.
When it can't work a chain out, it shows nothing rather than a wrong list.
Hover, signatures and deprecation
Hover a member to see its signature, which class it comes from, whether it
yields or is read-only, and a link to the Roblox docs. Typing (
after a method shows its parameters and highlights the one you're on.
Deprecated members get a strike-through and a warning, for example
workspace:findFirstChild() or part:remove(). Where there's
a direct replacement, Ctrl+. (or the light bulb) swaps it in. Deprecated classes
in GetService/Instance.new and deprecated enum items are
flagged the same way.
Problems and quick fixes
The Problems panel lists syntax errors and lint findings for the open file, and the same issues are underlined in the code. Click one to jump to it. The lint rules only flag things that cause real bugs or real lag in a Roblox game. There are no style rules.
| Rule | Level | What it catches |
|---|---|---|
infinite-loop | error | while true do with no task.wait() and no break. Freezes the client. |
implicit-global | warning | Assigning to a name that was never declared, which makes a global. Usually a missing local. |
redeclared | warning | Declaring the same local twice in one scope. |
deprecated | warning | wait, spawn, delay, old camelCase methods and similar. |
heavy-in-frame-loop | warning | :GetChildren(), :GetDescendants() and similar called inside RenderStepped/Heartbeat. |
unused-local | hint | A local that's never read. |
shadow | hint | A local that hides an outer one with the same name. |
unknown-global | hint | A name that isn't a local or a known global. Usually a typo. Executor globals like getgenv are known. |
waitforchild-no-timeout | hint | WaitForChild without a timeout, which waits forever if the child never shows up. |
unstored-connection | hint | :Connect() whose connection is thrown away, so it can never be disconnected. |
unprotected-call | hint | A DataStore, HTTP, teleport or similar call that throws when the request fails, not inside pcall. |
concat-in-loop | hint | Building a string with .. inside a loop. Slow on big loops; use table.concat. |
heavy-in-loop, alloc-in-frame-loop | hint | Allocating a new table every iteration or every frame. |
The checkboxes at the top of the panel filter by level.
Quick fixes
Put the cursor on an underline and press Ctrl+. (or click the light bulb):
wait,spawn,delay→task.*, one at a time or every one in the file at once.- Add a timeout to
WaitForChild. - Store an unstored connection in a local.
- Wrap an unprotected DataStore or HTTP call in
pcall, with awarnwhen it fails. - Prefix an unused local with
_to mark it deliberate.
Select some lines and Ctrl+. also offers Wrap selected lines in pcall.
UI preview
The UI tab draws the ScreenGui a script builds, without running it. It reads
Instance.new calls, property assignments and .Parent in the order
they're written, including the usual helper, create("Frame", {Size = ...}),
and loops over a fixed count or a literal list. Pick a screen size to see scale versus
offset at work, tick hidden to include Visible = false elements, and
click any element to jump to the line that made it. The expand button opens a large view.
It lays out Size, Position, AnchorPoint, ZIndex, colours and transparency, borders, text (size, scaling, alignment, wrapping), UICorner, UIStroke, UIPadding, UIListLayout, UIGridLayout, UIGradient, UIScale, UIAspectRatioConstraint and UISizeConstraint. Images show as placeholders, since a web page can't fetch Roblox assets. It's an approximation: values that depend on the running game (a player's name, a tween) keep their defaults.
DataStore check
The DataStore tab checks a value before you save it. Paste a Luau table or JSON, or
select one in the code and press Use selection (right-click → Check the selected
table also works). You get the encoded size against the 4 MB limit, which fields take
the most room, and the same errors Roblox gives: a Vector3 or
Color3 can't be stored, mixed and sparse tables fail, and
math.huge doesn't survive.
Below it, the request budget: set players per server and how often you autosave to see your writes a minute against the per-server budget, plus checks on key and store name length. The full simulation of DataStores, with versions and failures, is the sandbox.
Rojo projects
Open a folder (or drop one) that has a default.project.json and the file list
switches to the game tree: services, folders and scripts the way Studio's Explorer
shows them, following Rojo's rules (.server.luau is a Script,
.client.luau a LocalScript, init.luau makes the folder the module).
The Game / Files buttons switch between the tree and the folders on disk.
With the tree known, require(ReplicatedStorage.Shared.Util),
require(script.Parent.Util) and string requires ("./Util", and
"@alias/Util" from .luaurc) all resolve for Go to definition. Typing a
module's name offers to add its require line at the top, through a service local
if the file has one.
There's no live sync to Studio. That needs the Rojo program running on your computer
(rojo serve), and a web page can't be that. Keep using Rojo for syncing; the
editor reads the same project file.
Color3 and UDim2
Every Color3.fromRGB, Color3.new, Color3.fromHex and
Color3.fromHSV call with plain numbers gets a colour swatch. Click the
swatch or the call itself to open a colour picker. The picked colour is written
back in the same form it was in, so an fromRGB stays an
fromRGB. Click the header of the picker to switch to another form.
UDim2.new, UDim2.fromScale and UDim2.fromOffset calls
have a dotted underline. Click one and you get a small grid standing in for the
parent frame:
- Drag to move the point. That changes the scale values.
- Hold Alt while dragging to change the offsets instead.
- Hold Shift to snap to 5% steps.
- Pick a screen size in the corner to see what the offsets mean at 1080p, on a laptop, a phone or a tablet.
- Position mode shows where the frame's corner lands. Size mode shows the box.
It picks the right one from the line (
.Size =vs.Position =) but you can switch. - Offset → scale folds the pixel offsets into scale at the chosen screen size, and Scale → offset does the reverse.
The code updates as you drag, and the whole session is a single Ctrl+Z. Calls with variables instead of numbers are left alone, since there's no value to show.
Constant unpacker
Tools → Unpack constant arrays. A lot of obfuscators pull every string and
global into one table and then only ever refer to it by index. That turns
print("hi") into K[12](K[4]). The unpacker puts the values
back where they're used.
-- before
local K = {"\104\105", game, "Players", "print"}
local plr = K[2]:GetService(K[3]).LocalPlayer
getfenv()[K[4]](K[1] .. "!")
-- after
local plr = game:GetService("Players").LocalPlayer
getfenv()["print"]("hi!")
It handles:
- Tables of literals and globals:
local K = {"a", 2, game, x = "y"}. - Pools filled one by one before they're used:
_G[1] = "print",_G[2] = game. - Index math:
K[0x1F - 30],K["x"],K.x. - Wrapper functions like
local function F(i) return K[i - 512] end. - The shuffle Prometheus does, where a loop reverses slices of the table before anything reads it. The shuffle is replayed first so the values come out right.
- Escaped strings anywhere in the file (
"\80\108\97\121"→"Play") and joining literal concatenations.
Tables that are no longer used afterwards are removed. It won't touch a table
that's written to after it's created, reassigned, passed to
table.insert/setmetatable, or shadowed by a local or
parameter with the same name. Inlining a value that can change would give you
readable code that does something different. The notice tells you which tables
were skipped and why.
It runs in the background so big files don't freeze the page, and it's one undo step. It doesn't try to crack VM-based obfuscators (Luraph, Moonsec and so on); those don't use a constant table in this form. Beautify first if the file is one line, then unpack.
Templates
Templates (Ctrl+Shift+T) is a library of the boilerplate every game ends up
writing: service headers, PlayerAdded with the "players already in game" case
handled, leaderstats, a DataStore module with retries and BindToClose,
RemoteEvents with server-side checks and rate limiting, UI built from code,
tweens, draggable frames, raycasts, CollectionService tags, developer product
receipts, a Signal class, a cleanup class and more.
Each one says whether it belongs in a Script, LocalScript or ModuleScript.
Search by name or by anything in the code. Insert puts it at the cursor, or open
it as a new file. Every template compiles with Roblox's own Luau compiler, and they
use current APIs (task.wait, not wait).
Sandbox
Tools → DataStore sandbox, Retry lab or HttpService sandbox. You need to be signed in to use it. It's a simulation of DataStoreService and HttpService so you can check save logic and retry handling without a game server. It doesn't run your script. It's a mock service with a UI, plus an export that lets your real script run against the same mock in Studio (see below).
Everything you make in the sandbox is saved in your browser only.
DataStores
Make stores (with scopes, and ordered stores for leaderboards), or hit
Scan script to create every store the open file uses in a
GetDataStore("Name", "scope") call. Pick a key and run
GetAsync, SetAsync, UpdateAsync,
IncrementAsync or RemoveAsync on it. Values are JSON. Every
write keeps a version, and you can see the history.
The mock enforces the same limits and error codes Roblox does:
| Code | When |
|---|---|
| 101 | Empty key name. |
| 102 | Key, store name or scope over 50 characters. |
| 103 | Non-integer value in an ordered store, or incrementing a non-number. |
| 104 | Storing nil or something that can't be serialised. |
| 105 | Value over 4,194,304 characters once serialised. |
| 106, 107 | Bad page size or min/max for GetSortedAsync. |
| 301 to 306 | Request budget used up (throttled). |
| 403 | Writing with "Studio API access" turned off. |
| 502 | Random failure, from the failure % setting. |
| 511, 512 | Metadata over 300 characters, or more than 4 user IDs. |
Budgets use Roblox's per-server numbers and scale with the player count you set: reads and writes get 60 + 40 per player each minute, ordered-store writes 30 + 5 per player, and listing or sorting 5 + 2 per player. Latency and failure rate are adjustable so you can see how your code behaves when things go wrong.
Retry lab
Set up a burst of traffic (how many saves, how far apart, player count, failure rate, latency) and a retry policy (attempts, base delay, multiplier, cap, jitter), then run it. It simulates the whole thing on a fake clock, so a minute of traffic takes a few milliseconds. You get how many saves got through, how many would have been lost, average attempts, how many attempts hit the budget, and the median and 95th percentile wait, plus a timeline of the first 60 requests. When a policy works, Insert this policy as Luau writes a matching retry function into your file.
HttpService
- JSONEncode takes a Luau table literal and applies Roblox's rules. Mixed
tables and gappy arrays fail with the same error Roblox gives, and
math.hugecomes out asinf, which most servers won't parse. - JSONDecode turns JSON into the Luau table you'd get back, and can insert it into your file.
- GenerateGUID and UrlEncode work like the real ones.
- RequestAsync mocks: add routes (method, URL with
*wildcards, status, body) and test a request. You get the response table your script would see. The HttpEnabled toggle reproduces the "Http requests are not enabled" error. No real request is ever sent from here.
Testing your real script in Studio
Export mock module writes MockDataStoreService.luau: a ModuleScript
that behaves like DataStoreService with your sandbox data preloaded and the same
latency, failure rate and budgets. Put it in a ModuleScript and swap it in while
testing:
local RunService = game:GetService("RunService")
local DataStoreService = if RunService:IsStudio()
then require(script.MockDataStoreService)
else game:GetService("DataStoreService")
AST and queries
The AST panel shows the parsed structure of the file as a tree. Click a node to select its code; move the cursor in the code and the tree follows. It handles files with hundreds of thousands of nodes because it only draws what's on screen.
The search box above it takes a small query language:
| Query | Finds |
|---|---|
Call | Every function call. Any node type shown in the tree works, and a prefix is enough. |
Call[callee=loadstring] | Calls to loadstring. |
MethodCall[method=HttpGet] | :HttpGet(...) calls. |
Member[object=game] | game.Something lookups. |
String[value=discord] | Strings containing "discord". |
Local[name=/^_/] | Locals whose name starts with an underscore. /.../ is a regex. |
[name=remote] | Any node with a name containing "remote". |
Fields you can match on are name, callee, method,
property, object, value and operator.
Plain values match case-insensitively and anywhere in the text. This is the quick
way to find every network call or loadstring in a 9,000-line script
someone sent you.
Bytecode
The Bytecode panel compiles the file with Roblox's actual Luau compiler, built to WebAssembly. What you see is exactly what Roblox would produce, not a guess. The compiler (about 630 KB) is only downloaded the first time you open the tab.
Each source line is shown above the instructions it produced, so you can see what a line really costs. Click either to jump. Pick -O0, -O1 or -O2 (-O2 is what Roblox uses for live games), and leave compare on to see how much each level saves. The notes at the top point out things worth fixing, like lots of global lookups that should be locals or table allocations that might be in a loop.
The Stats tab next to it has line counts, comment ratio, the longest line, every function with its complexity, and the most used globals. Click a function to jump to it.
AI menu
The editor doesn't talk to any AI service. The AI menu just copies things to your clipboard in a shape that works well when you paste them into one: your files with names, the current problems with line numbers and a request to fix them, a Roblox-specific review request, or the selected code with surrounding context. The status bar shows a rough token count so you know if it'll fit.
Shortcuts
| Keys | Does |
|---|---|
| Ctrl+S / Ctrl+Shift+S | Save / Save as (download outside Chrome and Edge) |
| Ctrl+Shift+F | Beautify |
| Ctrl+Shift+T | Templates |
| Ctrl+Alt+S | Share |
| Ctrl+Alt+C | Copy for AI (selection, or all files) |
| F1 | Every command |
| Ctrl+B | Show or hide the file list |
| Ctrl+Shift+O | Symbols in this file |
| Ctrl+Alt+O | Symbols in all open files |
| Ctrl+G | Go to line |
| F12 / Ctrl+Click | Go to definition |
| Alt+F12 | Peek definition |
| Shift+F12 | Find all references |
| F2 | Rename symbol (in the code), rename file (in the file list) |
| Ctrl+. | Quick fix |
| F8 | Next problem |
| Ctrl+Space | Show suggestions |
| Ctrl+F / Ctrl+H | Find / replace |
| Ctrl+D | Select next match (multi-cursor) |
| Alt+Click | Add a cursor |
| Ctrl+/ | Toggle comment |
Help → Keyboard shortcuts shows this list inside the editor.
Use Cmd instead of Ctrl on a Mac.
What stays local
Opening, editing, linting, navigating, renaming, beautifying, unpacking, compiling, the UI preview, the DataStore check and the sandbox all happen in your browser. Your code only leaves it when you create a share. The only outside requests the editor makes are to Roblox's CDN (and GitHub as a backup) for the API dump, which contain nothing about you or your code. Clearing this site's data in your browser removes saved tabs, the API cache and sandbox data. The privacy policy has the rest.
Tools
mango.onl/tools has small Roblox utilities that don't need the editor. All except the changelog run entirely in your browser.
API reference
Every class, property, method, event, callback and enum in the Studio build Roblox is
shipping, read from the same API dump as the editor's autocomplete and cached in your
browser. Search a class (Part), a member (Humanoid.Health,
:Raycast), an enum, or an enum value: 101 or
KeyCode 101 finds Enum.KeyCode.E.
A class page shows its inheritance chain, subclasses, and every member with its type, tags (deprecated, yields, read-only, hidden), security level and thread safety. Inherited, deprecated and engine-only members can be switched on and off. Each class, enum and datatype also lists where it appears: every property of that type, every method that returns it, and every parameter that takes it. Every view has its own link.
API changelog
What changed in the engine API between Studio builds: classes, members, enums and enum items that were added, removed, changed (type, parameters, security, tags) or deprecated. Pick any two builds to see everything in between, filter by name, and click a change to open it in the reference. The history comes from the Roblox Client Tracker on GitHub; mango.onl compares each pair of builds once and caches the result. There's an RSS feed with one item per build.
CFrame visualizer
Type a CFrame the way you would in a script, including local lines,
*, + Vector3, :Inverse(), :ToObjectSpace(),
:Lerp() and every constructor (Angles, fromOrientation,
lookAt, fromAxisAngle, fromMatrix, quaternions, the
12 components). You see it in 3D with its Right, Up and Look vectors, and get its position,
vectors, orientation, Euler angles, axis-angle, quaternion and matrix, with the same
CFrame written five ways to copy. The sliders set position and orientation directly.
Tween & easing
Every EasingStyle in every direction, animated side by side. Click one for its
curve up close and a block moving with it, set time, delay, repeats and reverses, and
copy the TweenInfo.new(...) line and a TweenService:Create example.
RDD
mango.onl/rdd downloads any Roblox build, Windows or Mac, Player or Studio, straight from Roblox's own CDN. Use it to grab the build an executor needs, roll back after an update, or keep an old version around.
Your browser does all the downloading and puts the files together. Nothing passes through mango.onl, and the files are exactly what Roblox serves. It's based on RDD by Latte Softworks and WEAO's fork of it, rebuilt to work outside WEAO's site and to use a lot less memory.
Picking a build
First pick the platform and app (Windows Player, Windows Studio, Mac Player, Mac Studio), then which build:
| Option | Gets you |
|---|---|
| Latest | The build Roblox is serving right now. |
| Previous | The build before the current one. For Player this comes from WEAO; for Studio it comes from mango.onl's own record of builds it has seen. |
| For a cheat | The Roblox build a given executor supports, from WEAO's list. Entries marked "not updated" are on an older build. |
| Hash | Any build by its version-xxxxxxxxxxxxxxxx hash. The version- part is optional. |
The Build box shows exactly what you'll get (app, version number, hash and download size) before anything is downloaded. It checks that the build exists and matches the app you picked, so pasting a Studio hash while Windows Player is selected tells you straight away.
Current builds on the right lists what's live for all four apps. Use loads one into the form.
Options
| Option | What it does |
|---|---|
| Channel | LIVE by default. Public channels like zcanary or zintegration work too. |
| Output: Save .zip to... | Asks where to save and writes the zip straight to disk as it downloads. Uses the least memory. Chrome and Edge only. |
| Output: Download .zip | A normal browser download. Works everywhere, but the whole build is held in memory until it's saved. |
| Output: Extract to folder | Unpacks straight into a folder you pick, no zip. Windows builds only, Chrome and Edge only. |
| Mac arch | Universal (Intel and Apple silicon) or arm64 only. Mac builds only. |
| Parallel downloads | Downloads four packages at once. Turn it off on a slow or unreliable connection. |
Each package has its own progress bar, with the total under them. Downloads are checked against the size Roblox lists and retried up to three times if the connection drops. Cancel stops everything and discards the partial file.
Windows builds are rebuilt into the exact folder layout Roblox's installer uses. The compressed data is copied over as-is instead of being unpacked and packed again, which is why a 200 MB build finishes a couple of seconds after the download does.
Running it
Windows: unzip anywhere and run RobloxPlayerBeta.exe (or
RobloxStudioBeta.exe). Nothing gets installed or registered, so your
normal Roblox install is untouched. That also means clicking Play on the website
still opens your installed Roblox, not this copy.
Mac: you get Roblox's own .app in a zip. Unzip it and move it to
Applications, or run it from wherever you like.
Old builds generally won't let you join games once Roblox requires a newer version. That's Roblox, not the download.
Links
Copy link gives you a URL that opens the page with the same selection. Links made for the original RDD work too. Parameters:
| Parameter | Example | Meaning |
|---|---|---|
binaryType | WindowsPlayer | WindowsPlayer, WindowsStudio64, MacPlayer or MacStudio. |
version | version-2366ba214ec740ca | A specific build. guid also works. |
channel | zcanary | Non-LIVE channel. |
exploit | Wave | Pre-selects that executor's build. |
blobDir | /mac/arm64/ | Picks the arm64 Mac build. |
Example: https://mango.onl/rdd?binaryType=WindowsPlayer&version=version-2366ba214ec740ca.
A link fills in the form; you still press Download.
Troubleshooting
- "not found on Roblox's CDN"
- That hash doesn't exist for the app you picked, or Roblox has deleted it. Check the Player/Studio and Windows/Mac choice. Very old builds do get removed.
- "is a Studio build" / "is a Player build"
- The hash is real but for the other app. Switch the selector.
- Failed to fetch, or HTTP 403
- Something between you and Roblox's CDN blocked the request: a school or work network, an ad blocker, or a regional block. A VPN such as Cloudflare WARP usually fixes it.
- The tab crashes on Studio
- Studio is over 500 MB. Use Save .zip to... or Extract to folder so it's written to disk as it goes instead of held in memory.
- Previous isn't available
- For Studio, mango.onl only knows builds it has seen since it started recording. Find the hash elsewhere and use Hash.
- The versions look stale
- Versions and the executor list are cached in your browser for 5 to 10 minutes so the page doesn't hit the same APIs every load. A build's file list is cached for a week, since a hash never changes. Clear the site's data to force a refresh.
Public API
These endpoints are open to anyone. All return JSON. They're rate limited to 120 per minute per IP; go over and you get HTTP 429 until the window resets. Cache what you fetch; nothing here changes faster than once a minute or so.
GET /api/version
The current Roblox build for each app, and the one before it. Checked against
Roblox's client settings at most every 5 minutes. Sends
Access-Control-Allow-Origin: *, so you can call it from a web page.
{
"ok": true,
"WindowsPlayer": {
"version": "0.740.0.7400927",
"hash": "version-2366ba214ec740ca",
"since": 1790721811,
"previous": { "version": "0.739.0.7390687", "hash": "version-4310300497aa4917", "since": 1789488015 }
},
"WindowsStudio64": { ... },
"MacPlayer": { ... },
"MacStudio": { ... }
}
since is a Unix timestamp for when mango.onl first saw the build, not
necessarily when Roblox released it. previous is null until a
second build has been seen.
GET /api/version/<binaryType>
One app, plus history: every build recorded for it, newest first (up to 25).
The name isn't case-sensitive.
GET /api/version/WindowsStudio64
{
"ok": true,
"binaryType": "WindowsStudio64",
"version": "0.741.19.7411056",
"hash": "version-76e1a02649ad4f35",
"since": 1790721811,
"previous": null,
"history": [
{ "version": "0.741.19.7411056", "hash": "version-76e1a02649ad4f35", "seen": 1790721811 }
]
}
An unknown name returns 404 with the list of valid ones. If Roblox couldn't be reached and nothing has been recorded yet you get 503.
From a Roblox script (HttpService must be enabled):
local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(HttpService:GetAsync("https://mango.onl/api/version"))
print(data.WindowsPlayer.version, data.WindowsPlayer.hash)
GET /api/status
The state of every script mango.onl serves. It's the same data as the status page. Cached for 15 seconds.
{
"level": "ok",
"label": "All Services Operational",
"overall": "ok",
"up": 3,
"total": 3,
"scripts": [
{ "name": "Example", "status": "up", "gameid": "123456", "lastupdated": "2026-09-28" }
],
"handshakes": { "completed": 1520, "rejected": 12, "blocked": 3 }
}
| Field | Meaning |
|---|---|
level | ok, degraded (something unstable) or down (something down or updating). |
label | The headline shown on the site. |
scripts[].status | up, unstable, updating or down. |
handshakes | All-time counts of loader checks that passed, were rejected, or were blocked. |
GET /api/roblox/builds
Recent Studio builds with an API dump, newest first:
{"ok":true,"builds":[{"sha":"…","version":"0.741.19.7411056","date":"…"}]}.
sha is the Roblox Client Tracker commit for that build.
GET /api/roblox/changes?from=&to=
API changes between two builds from the list above; with no parameters, the latest
build against the one before it. A pair nobody has asked for yet answers
202 with "status":"pending" while mango.onl works it out; poll every
couple of seconds. When ready: "status":"ready", counts, and
changes, a list of {change, what, name, kind?, detail?} where
change is added, removed, changed, deprecated or undeprecated and
what is class, member, enum or item. CORS-open; ready diffs are cached for a day.
GET /tools/api/changes.rss
The same changes as an RSS feed, one item per build.
Health and announcements
GET /api/health
Returns {"status": "ok"} if the site is up. Not rate limited, so it's safe
for uptime monitors.
GET /api/announcements
The banner currently shown on the homepage, if any. Cached for 15 seconds.
{ "active": false }
{ "active": true, "severity": 2, "message": "Scripts are being updated for today's Roblox update." }
Severity is 1 for info, 2 for a warning and 3 for critical.
Questions or something missing from here? Ask in the Discord.