Guide

Roblox API Proxy: Fix the HttpService 403 and Read the Real Rate Limits

Swap the host and the HttpService 403 goes away. The per-endpoint limits, measured again on 16 September 2026: six of seventeen moved.

HProxy Team··Updated September 16, 2026·27 min read
HProxy.Guide

Skip the dead lists.

Our free proxy list re-checks every exit every few minutes across 100+ countries, with a live last-checked time, so you copy IPs that worked moments ago, not a stale text dump.

Open the free proxy list

Change the host and the call works. HttpService refuses the roblox.com web API. A request that fails as users.roblox.com/v1/users/1 succeeds as users.roproxy.com/v1/users/1. Same subdomain, same path, one word different.

That removes the 403. It does not remove the rate limit, which Roblox meters per endpoint and per IP address. Seventeen public endpoints, re-read on 16 September 2026, run from 1 request a minute to 600. That spread, and not the choice of proxy, decides how many addresses a project needs.

The spread also moves. We measured these endpoints on 11 August 2026 and again five weeks later, and six of the seventeen had changed. The table below carries both dates, so you can see which numbers hold and which ones drift.

If you have tried to read Roblox data from inside a Roblox game, you have met this:

HTTP 403 (Forbidden)
HttpService is not allowed to access ROBLOX resources

Roblox blocks its own game servers from calling its own web API. That is deliberate, it covers the legacy roblox.com endpoints whatever the path, and no combination of headers or options switches it off. It is also the single reason an entire category of tooling exists.

This page covers what the restriction actually is, the three real ways around it, and the part nobody publishes: the measured per-endpoint rate limits. The numbers below were measured by us, first on 11 August 2026 and again on 16 September 2026, and each one says which run it came from.

What exactly does HttpService block?

HttpService is Roblox's outbound HTTP client. It reaches the open internet fine, and it refuses Roblox itself. So a game that wants to show a player's group rank, badge count, inventory, friend count or avatar has to get that data from somewhere other than a direct call.

Alongside it, a few limits catch people out:

  • Ports below 1024 are blocked, except 80 and 443. Port 1194 is blocked outright.
  • HTTP requests must be enabled per experience under File, Experience Settings, Security.
  • Open Cloud calls from a game server are capped at 2500 per minute per server. Going past that can stall the sending methods for about thirty seconds.
  • Every other HTTP request from a game server shares a limit of 500 a minute. That ceiling sits above any endpoint budget, and almost nobody quotes it.
  • The .. string is refused in the path of a request to a Roblox domain.

One more line in the same documentation is easy to miss: HttpService may call a subset of the Open Cloud endpoints. So the block is on the legacy web API, not on every Roblox address, and the supported route is covered further down.

The reason for the roblox.com block is straightforward once you think like the platform: a game server is code written by a third party running on Roblox's own infrastructure, already holding a trusted network position. Letting it speak directly to Roblox's internal endpoints would make every experience a potential launchpad against the platform.

How do I get around the block? Swap the domain

Because the block is on the destination domain, routing the same request through a host that is not Roblox works. That is all a Roblox API proxy is: a relay that receives your request, forwards it to Roblox, and hands the answer back.

The best-known public one is RoProxy, announced on the Developer Forum in October 2021 after its predecessor rprxy.xyz shut down. Usage is a one-word change. Keep the subdomain, keep the path, swap roblox.com for roproxy.com:

https://users.roblox.com/v1/users/1        blocked, 403
https://users.roproxy.com/v1/users/1       works

In Luau that looks like this:

local HttpService = game:GetService("HttpService")

-- users.roblox.com is refused by HttpService, users.roproxy.com is not.
-- Same path, same response shape, different host.
local function getUser(userId)
    local ok, body = pcall(function()
        return HttpService:GetAsync("https://users.roproxy.com/v1/users/" .. userId)
    end)
    if not ok then
        warn("roblox api unreachable: " .. tostring(body))
        return nil
    end
    return HttpService:JSONDecode(body)
end

local user = getUser(1)
if user then
    print(user.name, user.displayName, user.created)
end

The pcall is not decoration. A proxy is a third party that can be down, and an uncaught GetAsync failure throws.

Does RoProxy still work, and how fast is it?

Public write-ups of RoProxy tend to be either the 2021 announcement or someone complaining it is broken. We tested it. On 11 August 2026 every subdomain we tried returned real Roblox data:

users, games, thumbnails, groups, friends, catalog and inventory all answered 200 through roproxy.com.

Latency, twelve runs each on the same endpoint:

RouteMedianSlowest
users.roblox.com direct0.173s0.185s
users.roproxy.com0.178s0.300s

The proxy hop costs about five milliseconds at the median. The common assumption that proxying makes API calls slow does not hold here.

It also does not throttle you on its own account: forty rapid requests returned forty 200s, on an endpoint where direct access starts refusing after thirty. It passes Roblox's own x-ratelimit-remaining header through, so you can watch the upstream budget from your side.

Which RoProxy subdomains actually work?

RoProxy mirrors the subdomain, so anything that exists on roblox.com should exist on roproxy.com. Mostly it does. We swept the ones people use:

SubdomainRelays?
users, groups, games, thumbnails, friendsYes
catalog, inventory, economy, avatar, badgesYes
apis (the newer Open Cloud host)Yes
accountinformation, premiumfeaturesRelays, but the endpoints need a session
presenceReturned 404 on 11 August 2026, answered normally on 16 September 2026

The presence subdomain came back. On 16 September 2026 a batch presence call through the relay answered 200 with a full budget of 60 a minute. A relay nobody documents can gain and lose subdomains between two visits, so test the ones your project needs rather than trusting a list, including this one.

Can I use RoProxy in production?

RoProxy's operator is candid about it:

I can't recommend RoProxy (or any public proxy) for production applications. Albeit rare, downtime and slowdowns are inevitable.

The thread bears that out: endpoints reported going down every couple of weeks, an invalid TLS certificate in April 2025, 401s on inventory endpoints, and the gamepass endpoint returning nothing in November 2025. There is also no documentation. roproxy.com itself returns a redirect at the root and 429 on /docs, /api and /faq. The documentation is a forum thread, which is why so many people search for one.

So: RoProxy is excellent for a hobby project, a prototype, or anything where a bad hour is survivable. It is a dependency you do not control on the critical path of anything that matters.

What are the Roblox API rate limits?

Roblox meters requests per endpoint, per IP address, and it tells you the budget in a response header on ordinary successful calls:

x-ratelimit-limit: 30, 30;w=60
x-ratelimit-remaining: 0
x-ratelimit-reset: 3
retry-after: 5
x-retry-after-coverage: /v1/users/{userId}/

Because that header comes back on a 200, the entire map costs one request per endpoint.

Read the w= part before you trust the number. It is the window in seconds, and it is not always 60. 30, 30;w=60 means thirty per minute. 1000, 1000;w=1 means a thousand per second. Two endpoints below use a one-second window, and reading only the first number understates them by 60x. We made exactly that mistake on the first pass, and it is the reason this section is worth reading rather than skimming.

The headers returned the following, all public and unauthenticated. The first column of numbers is the run of 11 August 2026, the second the re-read of 16 September 2026 from a different address:

Endpoint11 Aug 202616 Sep 2026
users.roblox.com/v1/users/{id}/username-history1/min1/min
catalog.roblox.com/v1/search/items12/min1/min
games.roblox.com/v1/games/{id}/servers/Public3/min3/min
users.roblox.com/v1/users/search1/min5/min
avatar.roblox.com/v1/users/{id}/avatar40/min6 per hour
groups.roblox.com/v1/groups/{id}7/min7/min
users.roblox.com/v1/users/{id}30/min30/min
users.roblox.com/v1/users (POST)1000/s30/min
groups.roblox.com/v2/groups?groupIds=1/s1/s
thumbnails.roblox.com/v1/users/avatar60/min60/min
presence.roblox.com/v1/presence/users (POST)60/min60/min
friends.roblox.com/v1/users/{id}/friends/count100/min100/min
groups.roblox.com/v2/users/{id}/groups/roles500/min100/min
inventory.roblox.com/v1/users/{id}/categories120/min120/min
games.roblox.com/v1/games?universeIds=300/min300/min
badges.roblox.com/v1/badges/{badgeId}600/min600/min
economy.roblox.com/v2/assets/{id}/details1000/min2/s

Six of seventeen moved in five weeks, in both directions. Catalog search lost a factor of twelve, user search gained a factor of five, and the avatar endpoint left the minute window altogether for six an hour. There is no such thing as "the Roblox API rate limit". There is the budget on the endpoint you call, on the day you call it.

Two endpoints answer with a second number after the window: economy asset details returns 2, 2;w=1, 70000 and presence returns 60, 60;w=60, 70000. The first pair is the short window you will hit while testing. The 70,000 is a second, much larger budget on a window the header does not name.

The rows below this table come from the run of 11 August 2026 and were not re-read. Treat every one of them as a starting point, and read the header on the endpoint you actually use.

Two things the table will not tell you unless you look. badges.roblox.com/v1/users/{id}/badges advertises 1000 a minute and then answers 9002 Authentication token is missing for every user we tried, so it is not usable unauthenticated whatever its budget says. And endpoints on the same subdomain differ wildly: group lookups by ID allow 7 a minute while the member list on the same host allows 1200.

Those headers match reality. We confirmed three by deliberately tripping them:

EndpointHeader said429 arrived on request
groups/{id}7#8
username-history1#2
servers/Public3#4

The endpoints that break projects are the low ones, so those are worth seeing on their own:

Roblox endpoints with the tightest per-IP limits (requests per minute)
username-historyone per minute
1
catalog searchwas 12 in August
1
games servers/Publicpublic server lists
3
users/searchwas 1 in August
5
groups/{id}group lookups
7
users/{id}
30
thumbnails avatar
60
Source: Measured by HProxy from x-ratelimit-limit response headers, 16 September 2026. The avatar endpoint is left out: it now publishes 6 an hour, which is off this scale.

Why does the rate limit follow the IP address?

This is the finding that decides your architecture. While our address was already refused on group lookups, the identical request through RoProxy succeeded in the same moment:

direct groups.roblox.com/v1/groups/1  ->  429
via    groups.roproxy.com/v1/groups/1 ->  200

Same endpoint, same second, different exit address. The budget is attached to the address, not to your account, your key or your game.

On 16 September 2026 we took that apart further. The same four endpoints were asked twice, once straight from our own server and once through a free SOCKS5 exit, so only the address differed:

EndpointFrom our own addressThrough the free exit
users/{id}30 a minute30 a minute
users/search5 a minute5 a minute
catalog search1 a minute1 a minute
avatar6 an hour6 an hour

The budget belongs to the endpoint. The spending belongs to the address. The difference shows in the other header. One call to users/{id} from our own address left x-ratelimit-remaining: 29 of 30. The same call through the public relay came back with 27 of 30, because other people on that relay had already spent three of the minute before we arrived. That single number is the whole argument against a shared address, and you can read it yourself on any response.

So sustained throughput has three terms, and most people only think about one:

records per minute  =  endpoint limit  x  records per request  x  clean exit addresses

The middle term is the one nearly everyone leaves at 1, and it is worth far more than the other two.

Do batch endpoints beat buying more addresses?

Roblox has multi-get and batch endpoints for most of the data people fetch in loops. We measured each one's ceiling by sending oversized requests until it named its own limit:

EndpointMethodMax per requestLimitRecords per minute, one IP
users.roblox.com/v1/usersPOST200 user IDs30/min on 16 Sep 2026, 1000/s in August6,000 profiles
users.roblox.com/v1/usernames/usersPOSTusernames500/minvery high
groups.roblox.com/v2/groups?groupIds=GET100 group IDs1/s6,000 groups
thumbnails.roblox.com/v1/batchPOST100 items60/min6,000 thumbnails
thumbnails.roblox.com/v1/users/avatar?userIds=GET100 user IDs60/min6,000 avatars
presence.roblox.com/v1/presence/usersPOST50 user IDs60/min3,000 presences

Now compare each against the obvious single-record call:

What you wantThe loop most people writeThe batch callDifference
User profilesGET /v1/users/{id}, 30/minPOST /v1/users, 200 per call at 30/min200x
Group detailsGET /v1/groups/{id}, 7/minGET /v2/groups?groupIds=, 100 per call857x
Avatar thumbnailsone ID at a time, 60/min100 IDs per call100x

The group case is the one worth staring at. v2/groups looks worse on paper: its header says 1, 1;w=1, a budget of one. But that is one request per second carrying 100 groups, which is 6,000 groups a minute against 7 for the single-ID endpoint. The endpoint with the most alarming rate limit is 857 times faster than the friendly-looking one.

The exceptions matter as much. Some endpoints have no batch form at all, and those are the ones that genuinely cap you: users/search at 1 a minute, username-history at 1, servers/Public at 3, catalog search at 12. If your work lives on those, no amount of rewriting helps and the only remaining lever is the third term.

So the order of operations is not negotiable:

  1. Cache. Most Roblox data changes slowly. This usually removes the problem outright.
  2. Batch. Fixing the middle term is worth more than anything you can buy, and it is free.
  3. Then add addresses, for the endpoints that have no batch form, or when batching is already maxed and you still need more.

Only after the first two does the arithmetic for step three make sense. Fetching 10,000 group profiles takes 100 batched requests. On one address at one per second that is about 100 seconds. Doing it with the single-ID endpoint at 7 a minute would take 24 hours. And a job pinned to users/search at 1 a minute needs one address per lookup per minute, which is where buying addresses is genuinely the only answer.

We would rather you fixed the batching and bought nothing. A page that tells you to buy fifteen addresses for a job that needs one badly-written loop replaced is not worth reading.

The batch call in Luau

POST through the proxy works the same way the GET does, and replaces up to two hundred separate lookups:

local HttpService = game:GetService("HttpService")

-- One request for up to 200 users, against 200 requests at 30 per minute.
-- Roblox returns ONLY the users it found, so the result is keyed by id rather
-- than assumed to line up with the input order.
local function getUsers(userIds)
    local ok, body = pcall(function()
        return HttpService:PostAsync(
            "https://users.roproxy.com/v1/users",
            HttpService:JSONEncode({ userIds = userIds, excludeBannedUsers = false }),
            Enum.HttpContentType.ApplicationJson
        )
    end)
    if not ok then
        warn("batch user lookup failed: " .. tostring(body))
        return {}
    end

    local byId = {}
    for _, user in ipairs(HttpService:JSONDecode(body).data) do
        byId[user.id] = user
    end
    return byId
end

local users = getUsers({ 1, 156, 261 })
for id, user in pairs(users) do
    print(id, user.name, user.displayName, user.hasVerifiedBadge)
end

Do not assume the response is the same length as the request. Asking for user IDs 1 to 100 returned 88 records, because deleted and non-existent IDs are simply absent rather than returned as nulls. Code that zips the response against the request array by index will silently attach the wrong name to the wrong player. Key by id, as above.

The same shape works for the other batch endpoints, with their own ceilings: 100 for thumbnails and groups, 50 for presence. Exceed them and Roblox tells you plainly, with Too many ids., There are too many requested Ids. or Too many User Ids being sent in the request. respectively, as a 400 rather than a 429. Those two failures mean opposite things: a 400 says your request was too big, a 429 says you sent too many requests. Retrying a 400 forever is a bug we have watched people ship.

Endpoint reference

Everything below is public and answers without authentication. Measured 11 August 2026. Limits are per IP; where the window is one second the per-minute figure is given, because that is the number you actually plan against.

users.roblox.com

EndpointReturnsLimit
GET /v1/users/{id}Profile: name, displayName, description, created, isBanned30/min
POST /v1/usersUp to 200 profiles in one call1000/s
POST /v1/usernames/usersUsernames to IDs and profiles500/min
GET /v1/users/{id}/username-historyPrevious usernames1/min
GET /v1/users/search?keyword=User search1/min

thumbnails.roblox.com

EndpointReturnsLimit
GET /v1/users/avatar?userIds=Full-body renders, up to 100 IDs60/min
GET /v1/users/avatar-headshot?userIds=Headshots, up to 100 IDs120/min
GET /v1/games/icons?universeIds=Game icons1200/min
GET /v1/assets?assetIds=Asset images3,000/min (w=1)
POST /v1/batchMixed types, up to 100 items60/min

Check state before you use imageUrl. A thumbnail row is not always usable: asking for a target that does not exist returned state: "Blocked" with errorCode: 0 and no image, delivered as an ordinary row inside a 200 response rather than as an error. Code that reads data[i].imageUrl without looking at state renders a broken image and never logs anything.

groups.roblox.com

EndpointReturnsLimit
GET /v1/groups/{id}Group details, owner, member count7/min
GET /v2/groups?groupIds=Same, up to 100 groups per call60/min (w=1)
GET /v1/groups/{id}/rolesRank list with member counts800/min
GET /v1/groups/{id}/usersMember list, paginated1200/min
GET /v2/users/{id}/groups/rolesEvery group a user is in, with rank500/min

games.roblox.com

EndpointReturnsLimit
GET /v1/games?universeIds=Name, description, playing, visits, favourites300/min
GET /v1/games/votes?universeIds=Likes and dislikes200/min
GET /v2/users/{id}/gamesGames created by a user200/min
GET /v1/games/{id}/servers/PublicLive server list3/min

Everything else

EndpointReturnsLimit
POST presence.roblox.com/v1/presence/usersOnline status, up to 50 IDs60/min
GET inventory.roblox.com/v1/users/{id}/items/GamePass/{passId}/is-ownedtrue or false60/min
GET inventory.roblox.com/v1/users/{id}/categoriesInventory categories120/min
GET friends.roblox.com/v1/users/{id}/friends/countFriend count100/min
GET friends.roblox.com/v1/users/{id}/followers/countFollower count100/min
GET avatar.roblox.com/v1/users/{id}/avatarWorn items and body colours40/min
GET economy.roblox.com/v2/assets/{id}/detailsAsset price, creator, sales1000/min
GET badges.roblox.com/v1/badges/{badgeId}Badge details600/min
GET catalog.roblox.com/v1/search/itemsCatalog search12/min
GET apis.roblox.com/game-passes/v1/game-passes/{id}/product-infoGamepass name, price, creator6,000/min (w=1)

Two that look public and are not: badges.roblox.com/v1/users/{id}/badges returns 9002 Authentication token is missing for every user, and catalog.roblox.com/v1/catalog/items/details (POST) returns 403 XSRF token invalid. Both advertise generous limits they will not let you spend.

api.roblox.com no longer exists. It does not resolve in DNS at all, while users.roblox.com answers normally from the same machine. This matters because it is the host in sentanos/ProxyService's own README example, so the most-forked Roblox proxy module on GitHub ships a sample pointing at a dead domain and a Heroku free tier that ended in 2022. Anything built by copying it starts broken.

Recipes for the things people actually ask

Get a player's rank in your group. One call, and it returns every group at once, so filter client-side rather than asking per group:

local roles = HttpService:JSONDecode(HttpService:GetAsync(
    "https://groups.roproxy.com/v2/users/" .. userId .. "/groups/roles")).data
for _, entry in ipairs(roles) do
    if entry.group.id == MY_GROUP_ID then
        print(entry.role.name, entry.role.rank)   -- rank is 0-255
    end
end

Check gamepass ownership. The endpoint returns a bare true or false, not an object, which trips people up:

local owned = HttpService:GetAsync(
    "https://inventory.roproxy.com/v1/users/" .. userId ..
    "/items/GamePass/" .. passId .. "/is-owned") == "true"

Turn usernames into user IDs. Never scrape a profile page for this. POST /v1/usernames/users does it in bulk and returns requestedUsername alongside each result, so you can match them up even when Roblox corrects capitalisation.

Show a player's avatar. Use avatar-headshot rather than avatar: it is double the rate limit (120 against 60), takes 100 IDs per call, and is the image people expect next to a name.

Check who is online, with a caveat. POST presence.roblox.com/v1/presence/users takes up to 50 IDs and userPresenceType is 0 offline, 1 website, 2 in an experience, 3 in Studio. But every user we queried unauthenticated came back 0 with lastLocation: "Website", including accounts that were not plausibly all offline. The endpoint answers 200 without a session and appears to return a stub, so treat unauthenticated presence as unreliable and verify it against a known-online account before building on it.

Get live player counts. GET /v1/games?universeIds= gives playing and visits for many universes at once at 300/min. Do not use the public server list for this: it is 3/min and paginated, and it is the wrong tool for a number the games endpoint hands you directly.

How do I run my own Roblox proxy?

Since the block is only on the destination domain, a proxy is a small piece of software. Several ready-made options exist:

OptionWhat it isReality
RoProxyFree public relayFast, unthrottled, no docs, no uptime promise
Cloudflare WorkerDeploy a small script, 100k requests/day freeWorks, but see the trap below
askfalse/roproxy-liteRoProxy's own code for self-hosting, in GoSelf-managed, you supply the host
sentanos/ProxyServiceThe classic Luau module, 1,661 forksLast code push August 2024, and its example points at a Heroku URL although Heroku ended its free tier in November 2022

Why is my own proxy still refused with the same 403?

A relay on a domain of your own usually clears the block, and sometimes it does not. In a Developer Forum thread of 20 August 2024, a proxy on a private domain kept returning HttpService is not allowed to access ROBLOX resources, and a staff member answered:

Try filtering out the roblox-id header from the request on the proxy's backend.

So the check is not only the hostname you call. What your relay hands back can mark it as Roblox traffic. Our own calls on 16 September 2026 came back carrying roblox-machine-id and x-roblox-edge headers, through the public relay as well as directly, so a relay that copies every upstream header forwards those too. If your own proxy is refused while a public one works, strip the Roblox headers from the response before you rewrite any code.

Why does my Cloudflare Worker return 429 straight away?

Deploying a Worker as a Roblox proxy is the most-recommended free route, and developers who follow it keep reporting the same thing: 429 errors after roughly four requests.

Nothing is wrong with the code. A Worker exits through Cloudflare's shared address ranges, which an enormous number of other people are also using against Roblox. You are not getting a budget, you are getting whatever is left of someone else's. It is the same reason a free proxy list fails at this, described in free proxies for Roblox.

This is worth understanding rather than working around, because it is the moment the real variable becomes visible. Proxy software is not the scarce resource. The exit address is. Any solution where you share an address with strangers inherits their consumption, whether that address belongs to Cloudflare, a free proxy list, or a public relay everyone else has also discovered.

The whole proxy, in twenty lines

People reach for a template repo for this, which is why so many abandoned ones exist. The entire job is rewriting a hostname:

// /users/v1/users/1  ->  https://users.roblox.com/v1/users/1
export default {
  async fetch(request) {
    const url = new URL(request.url);
    const [, subdomain, ...rest] = url.pathname.split("/");
    if (!subdomain) return new Response("usage: /<subdomain>/<path>", { status: 400 });

    // Anyone who finds this URL spends YOUR rate-limit budget. Gate it.
    if (request.headers.get("x-proxy-key") !== SHARED_SECRET) {
      return new Response("forbidden", { status: 403 });
    }

    const target = new URL(`https://${subdomain}.roblox.com/${rest.join("/")}`);
    target.search = url.search;

    const upstream = await fetch(target, {
      method: request.method,
      headers: { "Content-Type": request.headers.get("Content-Type") ?? "application/json" },
      body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
    });

    // Pass Roblox's own budget headers back, so callers can see what is left.
    return new Response(upstream.body, { status: upstream.status, headers: upstream.headers });
  },
};

Two details that are not optional. Gate it with a shared secret, because an open proxy on a public workers.dev URL is an invitation to spend your budget for you, which is how several of the abandoned public proxies died. And forward the budget headers, so x-ratelimit-limit, x-ratelimit-remaining and retry-after survive the hop; a proxy that swallows them leaves your client flying blind into 429s.

Forwarding everything is a different matter. Roblox answers also carry identity headers such as roblox-machine-id and x-roblox-edge, and the staff advice above points at exactly that kind of header when a private relay is refused. Copy the budget headers deliberately, rather than handing back the whole set.

Point it at a host whose exit address you control and it behaves like a private RoProxy. Leave it on Cloudflare's shared ranges and it will 429 early, for the reason above.

What can I cache, and for how long?

Roblox data changes far more slowly than people poll it, so cache by how volatile the field actually is rather than picking one TTL for everything:

DataVolatilitySensible cache
Usernames, display names, account creation dateAlmost neverHours to a day
Group name, owner, descriptionRarelyHours
Thumbnail URLsRarely (and the image is CDN-hosted anyway)Hours
Group member counts, badge countsSlowlyMinutes
Live player counts, asset pricesConstantlySeconds to a minute
PresenceConstantlyDo not cache, and see the caveat above

A five-minute cache on user and group lookups removes the rate-limit problem for most projects outright, which is why it comes before batching and long before buying anything.

Should I use Open Cloud instead of a proxy?

Not everything needs a proxy. Roblox's Open Cloud API is the supported, authenticated route into platform data, using API keys or OAuth 2.0 rather than the legacy cookie-authenticated web endpoints. Roblox is explicit that Open Cloud endpoints carry real stability guarantees, while the legacy web API can change without notice.

Use Open Cloud when it covers what you need. It is documented, it will not disappear under you, and it does not depend on a relay someone else maintains for free. Reach for a proxy when the data you want only exists on the legacy web endpoints, which is still a large surface, including much of the group, inventory, catalog and social data people build around.

Worth knowing: legacy web endpoints are deprecated in batches. A set of older game pass and developer product endpoints was retired on 23 April 2026 in favour of Open Cloud equivalents. If you are starting a project now, check Open Cloud first and treat the legacy surface as the fallback rather than the default.

Which option fits my project?

Hobby project, a game feature, a Discord bot for your friends. Use RoProxy. It is free, it is fast, and if it has a bad afternoon nothing important breaks. Cache aggressively and wrap every call in pcall.

Something people rely on. Do not put a free public relay on your critical path, on the operator's own advice. Self-host the proxy so you control the code, and give it an exit address you alone use so you get a full rate-limit budget instead of a shared one. That is usually a dedicated IPv4 address, from $1.58/IP, and one address per unit of throughput you need.

Account-touching work, anything that logs in or manages accounts rather than reading public data, is a different problem with different requirements. That belongs on residential addresses, from $0.44/GB, and is covered in proxies for Roblox.

Whichever you pick, confirm the address actually carries traffic before you build on it. Our proxy checker opens a real connection and reports the exit IP, country, latency and anonymity in one pass, and the manual method is in how to check if a proxy is working.

Error reference

Every one of these came out of the testing above, so the wording is what you will actually see rather than a paraphrase.

StatusBodyWhat it means
403HttpService is not allowed to access ROBLOX resourcesYou called a roblox.com domain from a game server. Use a proxy host.
429Too many requests. (code 4)Rate limited. Read retry-after and wait.
400Too many ids. (code 1)POST /v1/users batch over 200. Split it.
400There are too many requested Ids. (code 1)Thumbnails batch over 100.
400Too many ids in request. (code 2)v2/groups over 100 group IDs.
400Too many User Ids being sent in the request. (code 3)Presence over 50 IDs.
401Authentication token is missing (code 9002)Endpoint needs a logged-in session. A proxy does not fix this.
403XSRF token invalidEndpoint needs an XSRF token, obtained by a prior authenticated request.

The distinction that costs people the most: 429 is temporary, 400 is permanent. A 400 about ID counts will fail identically on every retry until you change the request size. Backing off and retrying it is an infinite loop.

A short production checklist

  • Cache first. Most Roblox data changes slowly. A five-minute cache on group and user lookups usually removes the rate-limit problem entirely.
  • Batch second. If you are calling /v1/users/{id} in a loop, you are leaving a 200x multiplier on the table for free. Check for a multi-get form before you touch anything else.
  • Read the headers, including w=. x-ratelimit-remaining tells you how much budget is left before you spend it, and the window tells you what the limit even means. Back off at low values instead of waiting for the 429.
  • Tell 400 and 429 apart. A 400 saying Too many ids. means your batch was too big and will fail identically forever. A 429 means slow down. Retrying the first as if it were the second is a loop that never exits.
  • Honour retry-after. A 429 tells you exactly how long to wait. Retrying immediately just spends the next window.
  • Wrap every call. A proxy is a third party. pcall everything and decide what your game shows when the data does not arrive.
  • Measure the endpoint you actually use. The limits above were measured on one date from one address. Send one request and read the header rather than trusting a table, including this one.
  • Do not authenticate through a proxy you do not control. Public relays are for public data. A .ROBLOSECURITY cookie sent through a stranger's server is an account handed over, as explained in free proxies for Roblox.

How many addresses does a Roblox project need?

Roblox blocks HttpService from reaching roblox.com, so reading platform data from a game server needs a relay on a non-Roblox domain. RoProxy does that well and free, and its own operator tells you not to depend on it.

The number that governs your project is not which proxy you pick. It is the per-endpoint, per-IP limit, which ran from 1 request a minute to 600 on 16 September 2026, multiplied by how many records you fit in each request. Cache first, batch second, and most projects never need a second address: swapping a loop over /v1/users/{id} for one POST /v1/users is worth more than anything you could buy.

Buy addresses for the cases batching cannot reach, which are real: username history at 1 a minute, catalog search at 1, public server lists at 3, user search at 5. There, throughput is simply the limit multiplied by the number of exit addresses you do not share with anyone, and that is why a Cloudflare Worker dies after four requests while a dedicated address does not. Same code, different address, different budget.

And check the numbers yourself before you plan around them. Six of the seventeen endpoints we re-read had moved within five weeks, so the header on the endpoint you use beats any table, including ours.

Sources

  • Roblox Developer Forum, RoProxy.com, a free rotating proxy for Roblox APIs, including the operator's production advice. 364 replies, 160,501 views.
  • Roblox Developer Forum, How to make a free self-hosted Roblox proxy using Cloudflare, including the 429 reports.
  • Roblox Developer Forum, HttpService is blocking my Roblox proxy, 20 August 2024, for the staff advice about the roblox-id header.
  • Roblox Creator Hub, In-game HTTP requests, for port restrictions, the 2500-per-minute Open Cloud limit and the 500-per-minute limit on everything else.
  • Roblox Creator Hub, Open Cloud, for the statement that HttpService may call a subset of the Open Cloud endpoints.
  • Roblox Creator Hub, Cloud API reference, for Open Cloud stability guarantees against legacy endpoints.
  • GitHub, sentanos/ProxyService and askfalse/roproxy-lite.
  • Rate limits, batch ceilings, latency and throughput measured by HProxy on 11 August 2026, unauthenticated, from a single residential address. Limits were read from x-ratelimit-limit response headers and three were confirmed by deliberately tripping them. Batch ceilings were found by sending oversized requests until each endpoint named its own maximum, with cooldowns between attempts so a 429 could not be mistaken for a size rejection.
  • Seventeen of those endpoints re-read by HProxy on 16 September 2026, one request each, from a server address and again through a free SOCKS5 exit. Nothing was pushed into a 429 on this run: the budget is published on a normal 200, so one request per endpoint is enough.

Frequently asked questions

Why does Roblox say HttpService is not allowed to access ROBLOX resources?
Roblox deliberately blocks HttpService from sending requests to its own domains. Any GetAsync or RequestAsync aimed at roblox.com or a roblox.com subdomain fails with HTTP 403 and that message, whatever the path. It is a platform restriction, not a bug in your script, and no header or option turns it off. The standard answer is to route the call through a proxy that is not on a Roblox domain.
What is RoProxy and how do I use it?
RoProxy is a free public relay for Roblox web APIs, announced on the Roblox Developer Forum in 2021. You keep the subdomain and path and swap only the domain: users.roblox.com/v1/users/1 becomes users.roproxy.com/v1/users/1. Because the host is no longer a Roblox domain, HttpService allows the request. It stays free and needs no key, and its operator openly advises against relying on it for production.
What are the Roblox API rate limits?
They are set per endpoint and enforced per IP address, and Roblox publishes the budget in an x-ratelimit-limit response header. Read the w= part, because it is the window in seconds and it is not always 60: 30, 30;w=60 is thirty a minute while 2, 2;w=1 is two a second. Re-read on 16 September 2026, seventeen public endpoints ran from 1 a minute on username history and catalog search to 600 a minute on badge details, and the avatar endpoint now publishes 6 in an hour. Group lookups by ID still allow 7 a minute.
Are there batch endpoints for the Roblox API?
Yes, and they matter more than anything else. One POST to users.roblox.com/v1/users carries up to 200 user IDs at 30 calls a minute, which is 6,000 profiles a minute against 30 for the single-record version. GET groups.roblox.com/v2/groups takes 100 group IDs at one call a second, which is 6,000 groups a minute against 7. Thumbnails batch and the avatar thumbnail endpoint take 100, presence takes 50. Fixing your batching is free and usually worth more than any number of extra addresses.
Why does my Cloudflare Worker proxy get 429 errors immediately?
Because Roblox meters by exit address and a Worker exits from Cloudflare's shared ranges, which thousands of other people are already spending. You inherit whatever budget is left rather than getting your own. Developers report 429s after roughly four requests. The fix is an exit address you alone use, not a different piece of proxy code.
How many IPs do I need for Roblox API work?
Usually fewer than you think, and often none. Throughput is the endpoint limit multiplied by records per request multiplied by addresses, so cache first, batch second, and add addresses only for endpoints with no batch form. On 16 September 2026 those were username history at 1 a minute, catalog search at 1, public server lists at 3 and user search at 5. When you do need them, divide your target rate by the endpoint limit. Dedicated IPv4 addresses start at $1.58/IP.

Get proxies that are alive right now

Our free proxy list re-checks every exit every few minutes across 100+ countries, with a live last-checked time, so you copy IPs that worked moments ago, not a stale text dump. When the location has to survive a real check, the paid network holds up.

129M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup

HProxy.

Honest guides and comparisons on proxies, scraping and staying unblocked, from the team that runs the network.

RSS feed