Bluesky is the one large social network that answers a proxied client honestly: its public API needs no account, no token and no browser, and it returned the same bytes to us from five countries on 18 September 2026. What the exit country did change was not the data but the moderation instruction attached to it. Here is every measurement, and the short list of cases where a proxy is actually the right tool.
What bsky.app serves a client that does not run JavaScript
We read one public profile, bsky.app/profile/bsky.app, twice from each of four exit countries on 18 September 2026 between 05:33 and 05:35 UTC: once as a plain HTTP client and once through a real browser.
| Exit | No-JS pass | Rendered pass | Verdict |
|---|---|---|---|
| US | 200, 47 words, h1 is JavaScript Required | 200, 1,093 words | Hydrated |
| BR | 200, 47 words, identical shell | 200, 1,107 words | Hydrated |
| DE | 200, 47 words, identical shell | 200, 113 words | Partial shell |
| JP | 200, 47 words, identical shell | 200, 47 words | Never hydrated |
The no-JS view is the same 47-word shell every time, and its h1 says so out loud. The rendered view is worse than it looks: only two passes out of four produced the post list inside the capture window, and the two that failed returned HTTP 200 with a plausible-looking page. That is the expensive failure mode of rendering a single-page app — you do not get an error, you get a short page you might store as if it were empty.
The server does put something useful in the shell for bots: the title, the profile description, the Open Graph tags and a ProfilePage JSON-LD block are all present without JavaScript, and the canonical URL never goes missing. So a link preview works. A dataset does not. We measured the same split with the SEO Audit API, which fetches both views in one call at $0.0012 per URL.
The public API is the product, and it is unauthenticated
Bluesky runs on the AT Protocol, and its read layer is exposed at public.api.bsky.app. Bluesky's own documentation says these direct endpoints do not support authentication and asks developers to use the cached public host for public web use cases. We confirmed it: no key, no cookie, no session.
| Request | Status | On-wire bytes | What came back |
|---|---|---|---|
| Profile page HTML, no JS | 200 | 19,947 | Nothing readable — an empty shell |
| Profile page, browser render | 200 | 2,655,017 | 4,826 words, 58 images, 12.8 s |
| app.bsky.actor.getProfile | 200 | 1,054 | DID, handle, display name, avatar, banner, followers, follows, posts, creation date, pinned post |
| app.bsky.feed.getAuthorFeed, limit 1 | 200 | 3,232 | One post with text, facets, embeds, plus like, repost, reply, quote and bookmark counts |
One kilobyte of JSON answers the question that 2.5 megabytes of rendered page answers badly. The follower count we read from the US exit was 34,942,093 and from Japan, ninety seconds later, 34,942,094 — a live counter, not a regional difference. Responses carry cache-control: public, max-age=30 and are served from a CDN edge in the requesting country.
The practical rule for Bluesky is the opposite of the rule for most platforms: do not render, and do not parse HTML. Point a plain HTTP client at the API, through whatever exit you like. If you want that wrapped — retries, geo pinning, clean output, batching over a URL list — our web scraping API bills $0.0002 per page without rendering and $0.001 with it, and the same calls are available to an agent through the MCP server.
What the exit country really changes
This is the measurement nobody has published. We sent the identical request — app.bsky.feed.getAuthorFeed with limit 1 — through five exit countries within four minutes. The response body was 3,232 bytes from all five. The atproto-content-labelers response header was not.
| Exit | Payload | Labelers named in the response header |
|---|---|---|
| US | 3,232 B | moderation.bsky.app |
| JP | 3,232 B | moderation.bsky.app |
| DE | 3,232 B | moderation.bsky.app plus moderation-de.bsky.app |
| TR | 3,232 B | moderation.bsky.app plus moderation-tr.bsky.app |
| BR | 3,232 B | moderation.bsky.app plus moderation-br.bsky.app |
Those extra identifiers arrive as raw DIDs. We resolved all four through the same public API: they are the Bluesky Moderation Service and its national counterparts, described in German, Turkish and Portuguese respectively, each created on a different date — Brazil in September 2024, Turkey in March 2025.
So country restrictions on Bluesky are labels, not blocks. The AppView hands your client a list of moderation services appropriate to the IP it sees; honouring them is the client's job, which is exactly why reporting on the Turkish restrictions in 2025 noted that third-party apps did not hide what the official app hid. For a data team this cuts both ways. Rotating your exit will not reveal hidden posts, because nothing was hidden at the API. But pinning an exit is the only way to discover which national labeler applies to a given market, and that is a real brand-safety question: if you publish on Bluesky and want to know whether German or Brazilian readers are being shown a label on your account, you have to ask from inside that country. A country-pinned exit from our residential network answers it for a fraction of a cent.
Which proxy type Bluesky actually needs
The honest answer is the cheapest one. The public API applied no bot challenge, no TLS fingerprint check and no CAPTCHA to any of our fourteen requests, and the payloads are small enough that bandwidth price stops mattering. What you are buying is address diversity against a per-IP request ceiling, not trust.
| Type | Use it on Bluesky when | Skip it when |
|---|---|---|
| Datacenter, from $0.50/GB | Reading the public API at volume — this is the default and it is enough | Never, for public reads |
| Residential, from $0.80/GB | You need a specific country's labeler view, or your datacenter range is being limited as a block | You only need the data, which is country-independent |
| Static ISP, from $2.50/IP per month at volume | You run a long-lived firehose consumer or your own PDS and want a stable, reputable address | Short polling jobs, where rotation is more useful than stability |
| Mobile, from $2.30/GB | We found no Bluesky read case that justifies it | Reading public data — you would be paying 4.6 times the datacenter rate for nothing |
If you want rotation without managing a pool, the rotating endpoint gives a fresh IP per request from one hostname. A minimal read through a country-pinned exit looks like this:
curl -x http://USER-country-de:PASS@dc.quanticdata.io:7777 \
"https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=bsky.app" \
-D - -o profile.json
Keep the -D -. On Bluesky the headers are half the answer.
Rate limits, and the three errors you will actually see
Bluesky publishes its limits, which is rare enough to be worth quoting precisely. Overall API requests against a PDS or the entryway are limited by IP, at 3,000 per 5 minutes. Write operations are limited per account instead: 5,000 points per hour where a create costs 3 points, an update 2 and a delete 1, which the docs work out to a maximum of 1,666 records created per hour. Account creation is capped at 100 per 5 minutes per IP, and session creation at 30 per 5 minutes per account.
You will read elsewhere that Bluesky's limits attach to identity rather than address. That is true of the write limits and false of the read limits, and the difference decides your architecture: unauthenticated reading is bounded by how many addresses you have, which is the one legitimate reason to put a proxy pool in front of a Bluesky collector at all.
The robots.txt at bsky.app is unusually direct. It greets crawlers, points them at the protocol and its firehose, disallows exactly one path — the compose intent — and states that HTTP 429 is the backoff signal and that "up to a handful concurrent requests should be ok". Read that as your pacing budget: a handful of concurrent connections per address, backing off on 429.
| Response | Shape | What it means |
|---|---|---|
| 400 | JSON: error InvalidRequest, message Profile not found, 56 bytes | Your input is wrong, not your IP. A missing handle is a 400 here, not a 404 |
| 403 | HTML from the CDN, no JSON error body | The endpoint is not exposed on the public host. We got this on searchPosts: search is not part of the unauthenticated surface |
| 429 | Documented backoff status | You crossed a limit. Slow down; changing address does not make the limit go away, it only spreads it |
| 200 with JavaScript Required | 47-word HTML shell | You fetched the web app instead of the API. Repoint the client |
One warning from our own data: none of the successful responses from the public host carried ratelimit- headers, even though the documentation describes header-based backoff for the network's services generally. At the cached public edge you get no advance warning, so treat 429 as the only signal and pace conservatively.
What this costs
All figures at our published rates, using the byte counts measured above.
| Job | Route | Transfer | Cost |
|---|---|---|---|
| 1,000 profile lookups | Public API | 1.05 MB | $0.0005 at $0.50/GB |
| 1,000 profile lookups | Rendered web app | 2.65 GB | $1.24 at $0.50/GB, $2.12 residential |
| 1,000,000 profile lookups | Public API | 0.98 GB | $0.49 at $0.50/GB |
| 100,000 posts with engagement counts | Public API, author feeds | 0.30 GB | $0.15 at $0.50/GB |
Rendering costs 2,519 times the bandwidth of asking the API the same question, and fails two times in four. A gigabyte of datacenter transfer holds roughly 1,018,000 profile responses or 332,000 post records. Every account also gets $2 of free API usage per month, which covers a first pass at a Bluesky dataset without a card. Note one inversion: for responses this small, per-gigabyte proxy billing beats per-page API billing, so the plain proxy route is the cheaper of our two products here. We would rather say that than sell you the wrong one.
There is no Bluesky collector in our catalogue, and given the above there is not much of a case for one — the API is already the clean JSON a collector would hand you. Where collectors earn their keep is on platforms that fight back: Reddit posts at $0.0005 per post, Instagram profiles at $0.006, YouTube search at $0.0008 per video. The contrast with what Instagram returns to a proxied client is the whole argument for the open protocol.
What we will not help you do
The other proxy guides on this keyword sell Bluesky multi-accounting through anti-detect browsers, account warming and registration at scale. We will not write that, for a plain reason: Bluesky's Community Guidelines treat bulk and spammy interaction as a violation, and its own rate-limit documentation names "prolific bots" as the thing the write limits exist to stop. A proxy does not change either fact, and no proxy restores a banned account.
What the open protocol does legitimately support is the rest of the list: public post and profile collection, social listening, creator and audience research, brand monitoring, checking whether a national labeler applies to your own account, and archiving your own organisation's posts. All of it runs on unauthenticated reads, none of it needs an account, and that is why Bluesky is the easiest social platform to work with honestly — the one place where doing it the sanctioned way is also the cheap way.
Two obligations survive the openness. Public does not mean unlicensed: posts remain their authors' content and personal data under the GDPR and similar regimes, so purpose, retention and deletion still apply. And deletion propagates on an open network: if an author removes a post, a dataset that keeps serving it is your problem, not theirs.