Skip to content
Skill Authoring
Skill

/browsing-bluesky

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

BOOST
From plugin
claude-skills
15498 skills1 agent
Install
$ npx -y skills add oaustegard/claude-skills --skill browsing-bluesky --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/browsing-bluesky

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

browsing-bluesky.SKILL.md
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.7.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')  # or use .claude/skills symlink path
from browsing_bluesky import (
    # Core browsing
    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,
    # Trending
    get_trending, get_trending_topics,
    # Account analysis
    get_all_following, get_all_followers, extract_post_text,
    extract_keywords, analyze_account, analyze_accounts,
    # Authentication utilities
    is_authenticated, get_authenticated_user, authenticated_identity,
    clear_session
)

Authentication (Optional)

Authentication enables personalized feeds (like Paper Skygest) that require knowing who's asking.

Setup

1. Create an app password at Bluesky: **Settings → Privacy and Security → App Passwords** 2. Set environment variables:

   export BSKY_HANDLE="yourhandle.bsky.social"
   export BSKY_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"

Which account reads

Two credential pairs are recognised, and the prefix is the only difference:

| Pair | Identity | |---|---| | `MUNINN_BSKY_HANDLE` / `MUNINN_BSKY_APP_PASSWORD` | `muninn` — preferred | | `BSKY_HANDLE` / `BSKY_APP_PASSWORD` | `owner` — used when no Muninn pair is set |

`BSKY_IDENTITY=muninn|owner` picks one outright, and a pair named that way is never substituted for: asking for `muninn` with no Muninn pair set reads as public rather than quietly reading as the owner. An unrecognised value raises.

This matters because a booted container holds **both** pairs. Until 2026-08-28 the unprefixed one won, so every authenticated read — the following timeline, and the mutes, blocks and labelers that shape all of them — came back as the account owner with nothing in the output saying so, from a skill documented as read-only that was nonetheless holding an app password carrying write scope on his account. On a machine with only the documented `BSKY_*` pair, nothing changes.

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. A misspelled

`BSKY_IDENTITY` is configuration, not auth failure, and raises instead

  • **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")

# Which pair answered — a handle alone does not say whether it was chosen
# or simply the only one set.
authenticated_identity()   # {'identity': 'muninn', 'handle': ..., 'did': ...}

# Clear session if needed (e.g., switching accounts or identities)
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

1. Fetch profile with `get_profile(handle)` for context (bio, follower count, post count) 2. Get recent posts with `get_user_posts(handle, limit=N)` 3. For topic-specific user content, use `search_posts(query, author=handle)`

Discover What's Trending

**Recommended workflow** — trending API first, firehose for deep dives:

1. Quick scan with trending topics (~500 tokens)

topics = get_trending_topics(limit=10)
# Returns: {topics: [{topic, display_name, description, link}, ...],
#           suggested: [...]}

2. Rich trends with post counts and actors

trends = get_trending(limit=10)
for t in trends:
    print(f"{t['display_name']} — {t['post_count']} posts ({t['status']})")
# Each trend includes: topic, display_name, link, started_at,
#   post_count, status, category, actors

3. Targeted exploration of selected trends

posts = search_posts(trend["topic"], limit=25)

4. Optional: Firehose for velocity monitoring or long-tail discovery

**Prerequisites**: Install Node.js dependencies once per session:

cd /home/claude && npm install ws https-proxy-agent 2>/dev/null
data = sample_firehose(duration=30)  # Full firehose sample
data = sample_firehose(duration=20, filter="python")  # Filtered sample

Returns dict with keys:

  • **window**: `{startTime, endTime, durationSeconds}` — sampling time range
  • **stats**: `{totalReceived, totalPosts, postsPerSecond, filter, languages}` — volume metrics and language breakdown
  • **topWords**: `[[word, count], ...]` — top 50 words (count >= 3)
  • **topPhrases**: `[[bigram, count], ...]` — top 30 bigrams (count >= 2)
  • **topTrigrams**: `[[trigram, count], ...]` — top 20 trigrams (count >= 2)
  • **entities**: `[[entity, count], ...]` — top 25 handles/hashtags (count >= 2)
  • **samplePosts**: `[{text, altTexts, hasImages}, ...]` — first 50 matching posts

Read Feeds and Lists

`get_feed_posts()` accepts:

  • List URLs: `https://bsky.app/profile/austegard.com/lis
Read more
Ships withclaude-skills

A collection of Claude skills distributed as a Claude Code plugin marketplace. Skills are primarily designed for the Claude.ai PaaS skill compute environment; mileage will vary if used as Claude Code Agent Skills.

Get the whole plugin

Other skills on claude-skills.