Guide

Roblox API Proxy: Why HttpService Is Blocked and How to Get Around It

Roblox blocks HttpService from calling roblox.com. Here is why, how RoProxy and self-hosted proxies solve it, plus the measured per-endpoint rate limits and batch ceilings that decide your real throughput.

HProxy Team · ·23 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

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 applies to every roblox.com domain and every 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, which decide whether your project needs one address or fifty. Every number below was measured on 11 August 2026 rather than repeated from a forum thread.

What is actually blocked

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.

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.

The way around it: 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.

RoProxy, measured

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
presenceNo. Returns 404

presence.roproxy.com is not covered. If you need online status, that one call has to go somewhere else, which is a good reason to know how to run your own.

The catch, in its operator's own words

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.

The part nobody publishes: the actual 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.

Here is what the headers returned, all public and unauthenticated:

EndpointWindowRequests per minute, per IP
users.roblox.com/v1/users/{id}/username-history60s1
users.roblox.com/v1/users/search60s1
games.roblox.com/v1/games/{id}/servers/Public60s3
groups.roblox.com/v1/groups/{id}60s7
catalog.roblox.com/v1/search/items60s12
users.roblox.com/v1/users/{id}60s30
avatar.roblox.com/v1/users/{id}/avatar60s40
thumbnails.roblox.com/v1/users/avatar60s60
inventory.roblox.com/v1/users/{id}/items/GamePass/{id}/is-owned60s60
friends.roblox.com/v1/users/{id}/friends/count60s100
inventory.roblox.com/v1/users/{id}/categories60s120
games.roblox.com/v2/users/{id}/games60s200
games.roblox.com/v1/games?universeIds=60s300
groups.roblox.com/v2/users/{id}/groups/roles60s500
badges.roblox.com/v1/badges/{badgeId}60s600
economy.roblox.com/v2/assets/{id}/details60s1000
groups.roblox.com/v1/groups/{id}/users60s1200
thumbnails.roblox.com/v1/games/icons60s1200
groups.roblox.com/v2/groups?groupIds=1s60
users.roblox.com/v1/users (POST)1s60,000

A spread from 1 per minute to 60,000. There is no such thing as "the Roblox API rate limit"; there is only the limit on the endpoint you happen to be calling, and the tight ones are very tight.

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:

The limit follows the IP, which turns this into arithmetic

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.

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.

Batch endpoints, which are worth more than any number of IPs

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 IDs1000/seffectively unbounded
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 callthousands of times faster
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.

Running your own 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

The Cloudflare Worker trap

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 upstream headers unchanged, so x-ratelimit-remaining and retry-after survive the hop; a proxy that swallows them leaves your client flying blind into 429s.

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.

Caching, which is the cheapest fix available

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.

The sanctioned path: Open Cloud

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.

Choosing between them

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 $0.30/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.

The bottom line

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 runs from 1 request a minute to 60,000, 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: user search at 1 a minute, username history at 1, public server lists at 3, catalog search at 12. 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.

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 Creator Hub, In-game HTTP requests, for port restrictions and the 2500-per-minute Open Cloud limit.
  • 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.

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 1000, 1000;w=1 is a thousand a second. Measured on 11 August 2026 the public endpoints ranged from 1 request a minute on username history and user search up to 60,000 a minute on the batch user endpoint. Group lookups by ID allow only 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, and GET groups.roblox.com/v2/groups takes up to 100 group IDs, against 30 and 7 per minute for the single-record versions. Thumbnails batch and the avatar 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, such as user search at 1 a minute, username history at 1, public server lists at 3 and catalog search at 12. When you do need them, divide your target rate by the endpoint limit. Dedicated IPv4 addresses start at $0.30/IP.

Get proxies that are alive right now

Our free list re-checks every exit every few minutes and shows a 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.

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