| name | browsing-bluesky |
| description | Browse Bluesky content via API and firehose - search posts, fetch user activity, sample trending topics, read feeds and lists, analyze and categorize accounts. Supports authenticated access for personalized feeds. Use for Bluesky research, user monitoring, trend analysis, feed reading, firehose sampling, account categorization. |
| metadata | {"version":"0.4.1"} |
Browsing Bluesky
Access Bluesky content through public APIs and real-time firehose. Supports optional authentication for personalized feeds. Includes account analysis for categorization.
Implementation
Add skill directory to path and import:
import sys
sys.path.insert(0, '/path/to/skills/browsing-bluesky')
from browsing_bluesky import (
search_posts, get_user_posts, get_profile, get_feed_posts, sample_firehose,
get_thread, get_quotes, get_likes, get_reposts,
get_followers, get_following, search_users,
get_all_following, get_all_followers, extract_post_text,
extract_keywords, analyze_account, analyze_accounts,
is_authenticated, get_authenticated_user, clear_session
)
Authentication (Optional)
Authentication enables personalized feeds (like Paper Skygest) that require knowing who's asking.
Setup
- Create an app password at Bluesky: Settings → Privacy and Security → App Passwords
- Set environment variables:
export BSKY_HANDLE="yourhandle.bsky.social"
export BSKY_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"
Behavior
- Transparent: All functions work identically with or without credentials
- Automatic: Auth headers are added opportunistically when credentials exist
- Graceful: Failed auth silently falls back to public access
- Secure: Tokens cached in memory only, never logged or persisted
Check Auth Status
if is_authenticated():
print(f"Logged in as: {get_authenticated_user()}")
else:
print("Using public access")
clear_session()
Research Workflows
Investigate a Topic
Use search_posts() with query syntax matching bsky.app advanced search:
- Basic terms:
event sourcing
- Exact phrases:
"event sourcing"
- User filter:
from:acairns.co.uk or use author= param
- Date filter:
since:2025-01-01 or use since= param
- Hashtags, mentions, domain links:
#python mentions:user domain:github.com
Combine query syntax with function params for complex searches.
Monitor a User
- Fetch profile with
get_profile(handle) for context (bio, follower count, post count)
- Get recent posts with
get_user_posts(handle, limit=N)
- For topic-specific user content, use
search_posts(query, author=handle)
Sample Trending Topics
Prerequisites: Install Node.js dependencies once per session:
cd /home/claude && npm install ws https-proxy-agent 2>/dev/null
Usage:
data = sample_firehose(duration=30)
data = sample_firehose(duration=20, filter="python")
Returns: topWords, topPhrases, entities, samplePosts, stats
Use for real-time zeitgeist, trending topic detection, or filtered stream analysis.
Read Feeds and Lists
get_feed_posts() accepts:
- List URLs:
https://bsky.app/profile/austegard.com/lists/3lankcdrlip2f
- Feed URLs:
https://bsky.app/profile/did:plc:xxx/feed/feedname
- AT-URIs:
at://did:plc:xxx/app.bsky.graph.list/xyz
The function extracts the AT-URI from URLs automatically.
Explore a Thread
Fetch full thread context for a post with parents and replies:
thread = get_thread("https://bsky.app/profile/user/post/xyz", depth=10)
Find Quote Posts
Discover posts that quote a specific post:
quotes = get_quotes("https://bsky.app/profile/user/post/xyz")
for q in quotes:
print(f"@{q['author_handle']}: {q['text'][:80]}")
Analyze Engagement
Get users who engaged with a post:
likes = get_likes(post_url)
reposts = get_reposts(post_url)
likes = get_likes("at://did:plc:.../app.bsky.feed.post/...")
Explore Social Graph
Navigate follower/following relationships:
followers = get_followers("handle.bsky.social")
following = get_following("handle.bsky.social")
Find Users
Search for users by name, handle, or bio:
users = search_users("machine learning researcher")
for u in users:
print(f"{u['display_name']} (@{u['handle']}): {u['description'][:100]}")
API Endpoint Notes
- Public AppView:
https://api.bsky.app/xrpc/ for unauthenticated reads
- PDS:
https://bsky.social/xrpc/ for authenticated requests
- Firehose:
wss://jetstream1.us-east.bsky.network/subscribe
- Endpoint routing is automatic - authenticated requests go to PDS, public requests go to AppView
- Rate limits exist but are generous for read operations
Return Format
All API functions return structured dicts with:
uri: AT protocol identifier
text: Post content
created_at: ISO timestamp
author_handle: User handle
author_name: Display name
likes, reposts, replies: Engagement counts
links: Full URLs extracted from post facets (post text truncates URLs with "...")
image_alts: Alt text from embedded images
url: Direct link to post on bsky.app
Profile function returns: handle, display_name, description, followers, following, posts, did
Account Analysis
Analyze accounts for categorization by topic. Fetches profile and posts, extracts keywords, and returns structured data for Claude to categorize.
Analyze a User's Network
results = analyze_accounts(following="yourhandle.bsky.social", limit=50)
results = analyze_accounts(followers="yourhandle.bsky.social", limit=50)
results = analyze_accounts(handles=["user1.bsky.social", "user2.bsky.social"])
Single Account Analysis
analysis = analyze_account("user.bsky.social")
Keyword Extraction Options
Stopwords parameter filters domain-specific noise:
"en": English (general purpose, default)
"ai": AI/ML domain (filters tech boilerplate)
"ls": Life Sciences (filters research methodology)
results = analyze_accounts(following="handle", stopwords="ai")
Requires: extracting-keywords skill with YAKE venv for keyword extraction.
Filtering Accounts
results = analyze_accounts(
following="handle",
exclude_patterns=["bot", "spam", "promo"]
)
Paginated Following/Followers
For large account lists beyond the 100 limit of get_following/get_followers:
all_following = get_all_following("handle", limit=500)
all_followers = get_all_followers("handle", limit=500)
Account Analysis Output
Each analyzed account returns:
{
"handle": "user.bsky.social",
"display_name": "User Name",
"description": "Bio text here",
"keywords": ["keyword1", "keyword2", "keyword3"],
"post_count": 20,
"followers": 1234,
"following": 567
}
Claude uses bio + keywords to categorize accounts by topic without hardcoded rules