BskySuite is built entirely on the AT Protocol's public API — no scraping, no browser automation, no unofficial endpoints. Every profile lookup, post fetch, and trending topic is a standard HTTPS call to an open API that any developer can access. This post explains the key concepts you need to understand before building something on AT Protocol, and walks through the most useful public endpoints.
Core Concepts
DIDs — Your Portable Identity
Every account on AT Protocol has a DID (Decentralised Identifier) — a unique, persistent ID that looks like did:plc:abc123xyz.... Unlike a username, your DID never changes even if you move your account to a different server, change your handle, or switch providers. It's the stable foundation of your identity on the network.
Your handle (@alice.bsky.social) is a human-readable alias that resolves to your DID via a DNS TXT record. This is why custom domain handles work — you add a TXT record to your domain pointing to your DID, and Bluesky resolves it.
PDS — Personal Data Server
Your posts, follows, likes, and profile data are stored on a PDS (Personal Data Server). By default this is bsky.social, Bluesky's hosting service. But the architecture allows you to run your own PDS and host your own data. Your DID points to your PDS, and because your identity is in the DID (not the PDS), you can migrate between servers without losing your social graph.
AppView
The AppView is the aggregation layer — a service that indexes posts from all PDSes across the network and makes them queryable. Bluesky runs a public AppView at api.bsky.app (authenticated) and public.api.bsky.app (unauthenticated, read-only). BskySuite uses the public AppView exclusively.
Lexicon
AT Protocol uses Lexicon — a schema definition system — to define API methods and record types. Each method has an ID in reverse-domain format: app.bsky.feed.getTimeline, com.atproto.identity.resolveHandle. The namespace prefix (app.bsky vs com.atproto) indicates whether it's a Bluesky-specific feature or a core protocol feature.
The Public API — No Auth Required
The most important thing for developers to know: Bluesky's public content is accessible without any API key or authentication. All you need is HTTPS. The base URL for unauthenticated requests is:
Useful Endpoints
Resolve a handle to a DID:
Get a user's profile:
Get a post thread (post + replies):
Get a user's recent posts:
Get trending topics:
Search posts:
Note on URIs: Posts on AT Protocol are identified by AT URIs: at://did:plc:abc123/app.bsky.feed.post/recordkey. The record key is the last segment of a bsky.app URL. So https://bsky.app/profile/alice.bsky.social/post/3k7x2abc → record key is 3k7x2abc.
Getting Media from a Post
This is specifically how BskySuite works. When a user pastes a post URL:
- Extract the handle and record key from the URL.
- Resolve the handle to a DID using
com.atproto.identity.resolveHandle. - Fetch the post record from the user's PDS:
GET {pds}/xrpc/com.atproto.repo.getRecord?repo={did}&collection=app.bsky.feed.post&rkey={recordKey} - Parse the
embedfield — it will beapp.bsky.embed.video,app.bsky.embed.images, orapp.bsky.embed.external. - Construct the blob URL:
https://{pds}/xrpc/com.atproto.sync.getBlob?did={did}&cid={blobCid}
The blob URL is a direct link to the file on the user's PDS. Because the AT Protocol is open, this is legitimate, documented, and officially supported — not scraping.
The Official SDKs
Bluesky provides an official TypeScript SDK that handles authentication, request signing, and type safety:
For unauthenticated read-only access you can use it without credentials. For Python, the community-maintained atproto package on PyPI provides similar functionality. Both are well-documented and actively maintained.
Rate Limits
The public API does apply rate limits, though they're generous for individual use. Authenticated requests have higher limits than unauthenticated ones. For production applications that will make many requests, you should:
- Cache responses where appropriate — profile data doesn't change every second
- Implement exponential backoff on 429 responses
- Consider running your own PDS if you need write access at scale
- Check the official documentation for current limits, as they're subject to change
What You Can Build
The public API is rich enough to build a wide range of tools without authentication: analytics dashboards, content aggregators, feed generators, handle availability checkers, cross-posting tools, archiving services, and more. Anything that only reads public data is straightforward. For write operations (posting, following, liking) you'll need OAuth or app passwords, which Bluesky also supports.
The AT Protocol specification and Bluesky's documentation are at atproto.com and docs.bsky.app respectively. The social-app repository on GitHub is also worth reading — it's the reference implementation of a full Bluesky client, all open source.
See AT Protocol in action
BskySuite's tools are all built on the public AT Protocol API. Try the Profile Analytics tool to see what data is available from any public Bluesky account.
📊 Try Profile Analytics →Frequently Asked Questions
What is the AT Protocol?
The AT Protocol (Authenticated Transfer Protocol) is an open, decentralised social networking protocol developed by Bluesky PBC. It defines how social data is stored, addressed, and transferred. Bluesky is the first major application built on it, but the protocol is designed to support multiple independent apps.
Do I need an API key?
No. Public data on Bluesky is accessible through public.api.bsky.app without any authentication. You only need an auth token for write operations or accessing private content.
What languages are supported?
Bluesky provides an official TypeScript/JavaScript SDK. Community libraries exist for Python, Go, Rust, Ruby, and others. Any language that can make HTTP requests works — the protocol is just JSON over HTTPS.
What is a DID?
A DID (Decentralised Identifier) is a unique, persistent identifier not tied to any server. On Bluesky, it looks like did:plc:abc123... and remains stable even if you move accounts or change your handle. Your @handle is a human-readable alias that resolves to your DID.