Offsets
Windows · macOS · Android · home
Offsets are memory layout constants for one exact Roblox build. They are keyed by the build's identity, not its dotted version, because two builds can share a dotted version and offsets from the wrong one are worse than no offsets at all.
Three tiers
| Tier | What it is |
|---|---|
final | The two below, composed — and what you get. Generated here; never uploaded, never mirrored. |
verified | Dumped and checked by rbxoffsets, exactly as uploaded. |
default | A real dump produced by somebody else and mirrored here, exactly as mirrored. |
Unless you ask otherwise you get the composed set when the build has one, and the better
single input when it does not. Every response says which. Add ?tier=verified
or ?tier=default to pin one input exactly. The rules in full are on
Offset tiers.
Why a build is composed rather than chosen
Windows has two dumps per build and they are good at different things, so picking one means shipping a set that is missing something the other had:
- Ours reads the client's own reflection database, so it finds far more — on 0.738 it has 1,083 offsets the mirrored dump has no entry for at all — and where both have a value ours is derived rather than pattern-matched.
- But a reflected property is the only kind it can see.
TaskScheduler,VisualEngine,Instance.This,Humanoid.Healthand 258 others have no property descriptor anywhere in the client, so that mechanism will never reach them however good it gets. The mirrored dump has them.
So neither is served. Both are kept whole, and a third set is built from them:
| # | Rule |
|---|---|
| 1 | Every offset either side names is in the result. Nothing is dropped. |
| 2 | Where both have a usable value and they disagree, the mirrored value is served. Ours wins only where the mirrored dump has no value or a zero. |
| 3 | A zero is not a usable value. A real value from either side beats a zero from the other, whoever it belongs to. |
| 4 | Where only one side has an offset, that side's value is served. |
Rule 3 is not hypothetical: the mirrored dump publishes 0x0 for about a dozen fields
it failed to find, and the composed set replaces each one where we have a real answer.
Composing is not a judgement about who is right. Deciding between two disagreeing values needs a live client, and a build step is the wrong place to guess. The rule above is a stated precedence, and every offset records which side it came from — so the comparison shows you the decision rather than asking you to trust it.
Seeing what composition did
Every offset in a composed set records which dump it came from, and both routes below report it. Use them when you want to know not just whose dump you have but which parts of it are whose:
GET /api/v1/windows/offsets/{version}/diff # every offset, with both values
GET /api/v1/windows/offsets/{version}/diff?from=disputed # only the disagreements
from | Meaning |
|---|---|
ours | Only the verified dump names it. |
theirs | Only the mirrored dump names it — carried across. |
agreed | Both name it and agree. |
disputed | Both name it, they disagree, the mirrored value is served and ours is kept visible. |
repaired | The mirrored dump published zero; ours has a real value. |
filled | Ours published zero; the mirrored value is served. |
unknown | Neither found it. Served as zero — do not trust these. |
The same thing for a reader, laid out like a code review, is at
/windows/offsets/{version}/diff. A build with fewer than two dumps answers
404 no_composition, which is the ordinary state before we publish rather than an error
in the request.
Asking the question on its own
Every offsets response already carries tier, and every file redirect carries
x-offsets-tier, so a client that reads either one already knows whose dump it got.
When that is the question — a launcher deciding how much to trust a build, a dashboard
showing a badge — there is a route that answers it and nothing else:
GET /api/v1/windows/verified/{version} # JSON
GET /api/v1/windows/verified/{version}.txt # the word "true" or "false", nothing else
verified: true means the set served for that build contains our work — either our
dump on its own, or the composed set, which only exists when ours does. false means
you are getting the mirrored dump alone. composed: true in the JSON tells the two
apart, and links to the diff. latest works in place of a version and resolves to the
same build /offsets/latest does.
A build with no offsets at all answers404, notfalse. “We serve somebody else's dump for this build” and “there is nothing to serve for this build” are different facts, and a client that collapses them will report an unknown build as unverified. Treat404as “no answer yet”.
Where they come from
| Platform | verified | default |
|---|---|---|
| Windows | Published by rbxoffsets | Mirrored from theo's offsets |
| macOS | Published by rbxoffsets | No upstream publishes one |
| Android | Published by rbxoffsets | No upstream publishes one |
Default dumps are copied rather than hotlinked, for three reasons in order of how much they matter: they stay reachable for builds upstream has aged out; an outage there does not take these pages down with it; and a stable URL under one domain is the thing an integration can actually depend on. Upstream is credited on every page and in every response that serves its work.
How rarely upstream is asked
Upstream is one person's server, so the mirror is built around restraint. It reads upstream's
build listing — one request that names every build and every file — no more often
than upstream's own cache lifetime (every 7
minutes), and conditionally, so an unchanged listing costs a 304 and no body. Each
file is then fetched once, ever, unless the listing says upstream republished
it. At most 24 files are fetched per cycle, newest builds
first. The listing also names builds before they go live, which is how default offsets for
upcoming builds are picked up.
What is in a dump
The four offsets formats are the same data written four ways — which is right depends on your toolchain, so none is recommended:
| Format | What it is |
|---|---|
hpp | A C++ header. |
cs | The same constants as a C# class. |
json | The structured form, and the dump's own description: total count, when it was produced, which dumper made it, and which build it belongs to. |
txt | A flat table, for grepping. |
A dump can also carry offsetshex.json, struct.hpp, types.json, fflags.hpp, fflags.json, fflags.cs, fflags.txt, fflagshex.json — see FFlags and dump files.
Fetching them
# builds with offsets, each in its served tier
curl -s https://rbxoffsets.com/api/v1/windows/offsets
curl -s "https://rbxoffsets.com/api/v1/windows/offsets?tier=verified"
# one build's set, and which tiers it has
curl -s https://rbxoffsets.com/api/v1/windows/offsets/version-c5aecda2245e4fae
curl -s https://rbxoffsets.com/api/v1/windows/offsets/latest
# the file itself (a redirect - use -L)
curl -sL -o offsets.hpp https://rbxoffsets.com/api/v1/windows/offsets/latest/hpp
curl -sL -o offsets.hpp "https://rbxoffsets.com/api/v1/windows/offsets/latest/hpp?tier=verified"
curl -sL -o offsets.hpp https://rbxoffsets.com/api/v1/windows/offsets/version-c5aecda2245e4fae/default/offsets.hpp
A version query answers like this:
{
"platform": "windows",
"version": "version-c5aecda2245e4fae",
"displayVersion": "0.738.0.7381397",
"tier": "verified",
"availableTiers": ["verified", "default"],
"count": 392,
"dumpedAt": "2026-09-09T18:02:00.000Z",
"storedAt": "2026-09-09T18:05:11.000Z",
"source": "upload",
"dumper": "rbxoffsets-dumper",
"dumperVersion": "1.0.0",
"verifiedBy": "jude",
"verifiedAt": "2026-09-09T18:20:00.000Z",
"notes": null,
"credit": { "label": "rbxoffsets", "url": null },
"isProduction": true,
"isLatest": true,
"files": [
{
"file": "offsets.hpp",
"kind": "offsets",
"format": "hpp",
"sizeBytes": 25930,
"sha256": "…",
"path": "/api/v1/windows/offsets/version-c5aecda2245e4fae/verified/offsets.hpp",
"url": "https://rbxoffsets.com/api/v1/windows/offsets/version-c5aecda2245e4fae/verified/offsets.hpp"
}
]
}
Matching offsets to a running client
The reliable sequence, and the reason version is the identity:
- Read the client's own version. On Windows that is the
clientVersionUploadfromclientsettingscdn.roblox.com/v2/client-version/WindowsPlayer, or theRoblox Versionstring at the top of any dump. - Ask this API for that exact string — with
?tier=verifiedif you require it. - If it answers
404 offsets_not_foundor404 offsets_not_verified, the dump has not been published yet. Do not fall back tolatest. Offsets from a neighbouring build are not approximately right; they point at the wrong memory.
version=$(curl -s https://clientsettingscdn.roblox.com/v2/client-version/WindowsPlayer \
| jq -r .clientVersionUpload)
curl -fsSL -o offsets.hpp "https://rbxoffsets.com/api/v1/windows/offsets/$version/hpp?tier=verified" \
|| echo "no verified dump for $version yet"
macOS
No upstream publishes macOS dumps, so macOS has no default tier. Until the first verified set
is uploaded, its offsets routes answer 501 offsets_unsupported — deliberately not a
404, because the route is correct and the capability is what is missing. The moment a
verified set lands, they answer a real listing with no change on your side.
Publishing a dump
Operator work, token required, and it only ever creates the verified tier:
curl -X POST -H "Authorization: Bearer $HTTP_TOKEN" \
-F offsets.json=@offsets.json -F offsets.hpp=@offsets.hpp \
-F offsets.cs=@offsets.cs -F offsets.txt=@offsets.txt -F verified_by=you \
https://rbxoffsets.com/internal/offsets/windows/version-c5aecda2245e4fae
What an upload has to satisfy
A verified set replaces the default one in everything served for that build, so an upload is checked before it can do that — and refused as a whole rather than half-applied, so a bad upload never costs the working set it was meant to supersede.
| Rule | Why |
|---|---|
offsets.json must name this build in Roblox Version, or name
nothing at all | A dump filed under the wrong build is the most damaging mistake this service could make. |
It must carry a non-empty Offsets object of classes | That is the shape every client reads. A body that parses but is shaped differently breaks each build it lands on. |
It must not drop any Class.Member the default set already
serves | Coverage cannot go backwards. A dump can be far larger overall and still be missing the keys clients actually read — this is the common case for a dumper built on a different technique — and publishing it would take a working build and break it. |
The coverage refusal names the keys that went missing, so the answer is a list to go and add
rather than a rejection to puzzle over. It applies only where a default set exists to compare
with; a build nobody else has dumped has nothing to regress against. Set
OFFSETS_REQUIRE_COVERAGE=false to publish anyway.
Every way in — PowerShell, cmd, the CLI, writing straight to the bucket — and how to withdraw a bad set, is on Publishing offsets.