Bluesky's AT Protocol lets you read public profile data — but it requires understanding DIDs, PLCs, and the app.bsky.actor.getProfile lexicon. For a simple lookup — "how many followers does this Bluesky account have?" — there's a much shorter path. Pulse wraps the AT Protocol and returns profile metrics as clean JSON with one GET request. No auth, no DID resolution, no lexicon knowledge.
curl "https://pulse.walls.sh/profile?url=https://bsky.app/profile/bsky.app"
{
"platform": "bluesky",
"handle": "bsky.app",
"name": "Bluesky",
"followers": 33641409,
"following": 6,
"posts": 767,
"verified": true,
"avatar": "https://cdn.bsky.app/img/…",
"fetchedAt": "2026-06-11T17:01:12.398Z"
}
Real numbers from Bluesky's own account. followers,
following, and posts come directly from the AT Protocol's public
appview. verified is true for accounts with a custom domain handle
(e.g. bsky.app) — Bluesky's version of a verified checkmark. No OAuth,
no token management.
Pass the profile URL or handle directly — both work:
# Profile URL https://bsky.app/profile/bsky.app # @handle format also accepted https://bsky.app/profile/natgeo.bsky.social
Pulse stores a snapshot every time you request a profile. The
history_url in the response points to the time series:
curl "https://pulse.walls.sh/history?url=https://bsky.app/profile/bsky.app"
{
"url": "https://bsky.app/profile/bsky.app",
"platform": "bluesky",
"history": [
{ "followers": 33641409, "following": 6, "posts": 767, "fetchedAt": "2026-06-11T17:01:12Z" },
{ "followers": 33580000, "following": 6, "posts": 765, "fetchedAt": "2026-06-10T09:14:05Z" }
]
}
No database setup, no cron job. Pulse accumulates the history as a side
effect of normal lookups — request the profile every hour and the /history
endpoint returns a growth curve automatically.
curl "https://pulse.walls.sh/profile/batch?urls=https://bsky.app/profile/bsky.app,https://bsky.app/profile/natgeo.bsky.social"
Returns an array of profile objects (or { url, error } for any
that fail). Up to 25 profiles per request. Partial failures don't fail the whole batch.
import requests
r = requests.get("https://pulse.walls.sh/profile", params={"url": "https://bsky.app/profile/bsky.app"})
profile = r.json()
print(f"@{profile['handle']}: {profile['followers']:,} followers")
The pulse-mcp server
exposes a profile tool. In Claude Desktop:
npx -y pulse-mcp
Then ask: "How many followers does @bsky.app have on Bluesky?" —
the agent calls profile({ url: "https://bsky.app/profile/bsky.app" })
and returns the live number.
content_unavailable.verified: true means the account
has a custom domain handle. Bluesky doesn't have a separate verification badge.Free: 120 calls/minute, no account or signup needed. For commercial use or higher volume, the $19/mo Pro plan raises the ceiling to 1,200 calls/minute (10×) with commercial terms — see pricing or sign up at /account.
More: full API docs · OpenAPI spec · Bluesky post metrics · Threads follower count · Instagram follower count · all supported platforms.
Need more than 60 calls/day?
Pulse Pro — 10,000 calls/month for $19/mo. No OAuth, no webhooks, just curl. Cancel any time.
Get a free API key →Free tier: 60 calls/day · Pro: 10k/month · takes 30 seconds