← Back to Skills Marketplace
minupla

Bluesky API

by minupla · GitHub ↗ · v1.0.3 · MIT-0
cross-platform ✓ Security Clean
149
Downloads
0
Stars
0
Active Installs
4
Versions
Install in OpenClaw
/install bluesky-api
Description
Read, search, post, and monitor Bluesky (AT Protocol) accounts
README (SKILL.md)

Bluesky Skill

Interact with Bluesky via the AT Protocol API. Supports public reads, search, authenticated posting, and profile monitoring.

Configuration

Credentials can come from openclaw config or environment variables:

Source Handle App Password
Config channels.bluesky.handle channels.bluesky.appPassword
Env var BSKY_HANDLE BSKY_APP_PASSWORD

App passwords are created at: https://bsky.app/settings/app-passwords

Never use a main account password. Always use an app password.


1. Read — Fetch Recent Posts from a Profile

No auth required. Uses the public API.

API Endpoint

GET https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=\x3Chandle>&limit=\x3Cn>
  • actor — Bluesky handle (e.g. alice.bsky.social) or DID
  • limit — number of posts to return (1–100, default 50)

curl Example

curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=alice.bsky.social&limit=5" | jq '.feed[] | {text: .post.record.text, createdAt: .post.record.createdAt, uri: .post.uri}'

Helper Script

./scripts/bsky-read.sh alice.bsky.social 5

Response Structure

The response JSON has a feed array. Each entry contains:

  • .post.record.text — the post text
  • .post.record.createdAt — ISO 8601 timestamp
  • .post.uri — AT URI of the post
  • .post.author.handle — author handle
  • .post.author.displayName — author display name
  • .post.likeCount, .post.repostCount, .post.replyCount — engagement counts

2. Search — Find Posts by Keyword

No auth required. Uses the public API.

API Endpoint

GET https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=\x3Cquery>&limit=\x3Cn>
  • q — search query (keywords, hashtags, phrases)
  • limit — number of results (1–100, default 25)

curl Example

curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=openclaw&limit=10" | jq '.posts[] | {text: .record.text, author: .author.handle, createdAt: .record.createdAt}'

Helper Script

./scripts/bsky-search.sh "openclaw" 10

Response Structure

The response JSON has a posts array. Each entry contains:

  • .record.text — the post text
  • .record.createdAt — ISO 8601 timestamp
  • .author.handle — author handle
  • .author.displayName — author display name
  • .uri — AT URI of the post

3. Post — Create a New Post

Requires auth. Uses app password authentication.

Step 1: Authenticate

POST https://bsky.social/xrpc/com.atproto.server.createSession
Content-Type: application/json

{"identifier": "\x3Chandle>", "password": "\x3Capp_password>"}

This returns a session with accessJwt and did.

curl Example

# Always use env vars — never interpolate credentials directly into shell strings
SESSION=$(curl -s -X POST "https://bsky.social/xrpc/com.atproto.server.createSession" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg h "$BSKY_HANDLE" --arg p "$BSKY_APP_PASSWORD" '{identifier: $h, password: $p}')")

ACCESS_TOKEN=$(echo "$SESSION" | jq -r '.accessJwt')
DID=$(echo "$SESSION" | jq -r '.did')

Step 2: Create the Post

POST https://bsky.social/xrpc/com.atproto.repo.createRecord
Authorization: Bearer \x3CaccessJwt>
Content-Type: application/json

{
  "repo": "\x3Cdid>",
  "collection": "app.bsky.feed.post",
  "record": {
    "$type": "app.bsky.feed.post",
    "text": "\x3Cpost text>",
    "createdAt": "\x3CISO 8601 timestamp>"
  }
}

curl Example

curl -s -X POST "https://bsky.social/xrpc/com.atproto.repo.createRecord" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"repo\": \"${DID}\",
    \"collection\": \"app.bsky.feed.post\",
    \"record\": {
      \"\$type\": \"app.bsky.feed.post\",
      \"text\": \"Hello from OpenClaw!\",
      \"createdAt\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\"
    }
  }"

Helper Script

# Pass app password via env var — never as a CLI argument (visible in ps/shell history)
BSKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx ./scripts/bsky-post.sh alice.bsky.social "Hello from OpenClaw!"

Post Length Limit

Bluesky posts have a 300-character limit (grapheme count). Check length before posting.


4. Monitor — Check for New Posts Since Last Check

Use the read endpoint with a timestamp comparison to detect new posts. This is useful for heartbeat monitoring.

Approach

  1. Fetch recent posts from the target profile.
  2. Compare .post.record.createdAt against the last-known timestamp.
  3. Any post with a createdAt newer than the stored timestamp is new.

curl Example

# Fetch the 10 most recent posts
FEED=$(curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=alice.bsky.social&limit=10")

# Filter posts newer than a given timestamp
SINCE="2026-03-24T00:00:00.000Z"
echo "$FEED" | jq --arg since "$SINCE" '.feed[] | select(.post.record.createdAt > $since) | {text: .post.record.text, createdAt: .post.record.createdAt}'

Monitoring Loop Pattern

For heartbeat use, store the latest seen timestamp and compare on each check:

# Use a user-owned state dir, not world-readable /tmp
LAST_SEEN_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/bsky-monitor-${HANDLE}.txt"
mkdir -p "$(dirname "$LAST_SEEN_FILE")"
HANDLE="alice.bsky.social"

# Read last seen timestamp (or default to 24h ago)
if [ -f "$LAST_SEEN_FILE" ]; then
  SINCE=$(cat "$LAST_SEEN_FILE")
else
  SINCE=$(date -u -v-24H +%Y-%m-%dT%H:%M:%S.000Z 2>/dev/null || date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%S.000Z)
fi

FEED=$(curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${HANDLE}&limit=20")
NEW_POSTS=$(echo "$FEED" | jq --arg since "$SINCE" '[.feed[] | select(.post.record.createdAt > $since)]')
COUNT=$(echo "$NEW_POSTS" | jq 'length')

if [ "$COUNT" -gt 0 ]; then
  echo "Found $COUNT new post(s) from $HANDLE since $SINCE"
  echo "$NEW_POSTS" | jq '.[] | {text: .post.record.text, createdAt: .post.record.createdAt}'
  # Update last seen to the most recent post timestamp
  echo "$NEW_POSTS" | jq -r '.[0].post.record.createdAt' > "$LAST_SEEN_FILE"
else
  echo "No new posts from $HANDLE since $SINCE"
fi

Error Handling

HTTP Status Meaning Action
200 Success Parse response
400 Bad request Check parameters
401 Unauthorized Re-authenticate (token may be expired)
404 Not found Check handle/DID exists
429 Rate limited Back off and retry after delay

Auth Token Expiry

Access tokens expire. If you get a 401, re-authenticate by calling createSession again. Do not cache tokens for more than a few minutes.


Quick Reference

Operation Auth? Script
Read profile feed No ./scripts/bsky-read.sh \x3Chandle> [limit]
Search posts No ./scripts/bsky-search.sh \x3Cquery> [limit]
Create post Yes ./scripts/bsky-post.sh \x3Chandle> \x3Capp_password> \x3Ctext>
Monitor new posts No Use read + timestamp filter (see section 4)
Usage Guidance
This skill appears coherent and limited to Bluesky API usage. Before installing: (1) Verify you trust the skill source (it’s marked 'source: unknown'). (2) If you enable posting, create and use an app password (the SKILL.md correctly advises this); avoid supplying your main account password. (3) The scripts require curl and jq and optionally BSKY_HANDLE/BSKY_APP_PASSWORD—make sure those tools are available and understand that providing the app password allows the skill to create posts on your behalf. (4) Note the small metadata mismatch: the registry doesn’t list required binaries/env vars even though SKILL.md and scripts expect them; consider asking the publisher to correct the metadata. If you need stricter control, do not provide BSKY_APP_PASSWORD or restrict the skill from autonomous invocation.
Capability Analysis
Type: OpenClaw Skill Name: bluesky-api Version: 1.0.3 The bluesky-api skill bundle is a legitimate tool for interacting with the Bluesky (AT Protocol) API. The scripts (bsky-post.sh, bsky-read.sh, bsky-search.sh) follow security best practices by using environment variables for credentials, validating inputs, and using jq to safely construct JSON payloads and escape URL parameters. No evidence of data exfiltration, malicious execution, or prompt injection was found.
Capability Assessment
Purpose & Capability
The skill's name/description match the provided scripts and SKILL.md: public reads/search use public.api.bsky.app and authenticated posting uses bsky.social with an app password. Minor metadata inconsistency: registry metadata lists no required binaries/env vars, but SKILL.md and scripts require curl and jq and optionally accept BSKY_HANDLE/BSKY_APP_PASSWORD.
Instruction Scope
SKILL.md and the three included scripts limit actions to Bluesky API endpoints, input validation is present (limit numeric checks, length checks), and the post flow recommends using app passwords and not exposing credentials. There are no instructions to read unrelated files or send data to unexpected endpoints.
Install Mechanism
No install spec is provided (instruction-only operation with included scripts). Nothing is downloaded or written during install, which minimizes risk.
Credentials
The skill does not require any platform-wide credentials; it optionally uses BSKY_HANDLE and BSKY_APP_PASSWORD for posting, which is proportional to the documented functionality. Confirming the registry omitted these optional env vars in metadata is advisable.
Persistence & Privilege
The skill is not marked always:true and does not request persistent system privileges or modify other skills. It can be invoked by the agent (default behavior), which is expected for an integration that can post on behalf of a user.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install bluesky-api
  3. After installation, invoke the skill by name or use /bluesky-api
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.3
Fix token leak: bsky-post.sh no longer prints full SESSION JSON on auth failure — only the error message field is shown. Prevents accessJwt and DID from appearing in logs or process output.
v1.0.2
Address scanner notes: declare required binaries (curl, jq), fix grapheme-based length check for 300-char limit, align post script docs with env-var-only password approach
v1.0.1
Security fixes: app password via env var only (no CLI arg), limit validation in scripts, safe jq credential handling in curl examples, state file moved from /tmp to user home dir
v1.0.0
Initial release of the Bluesky API integration skill. - Enables reading public posts, searching, posting (with app password), and monitoring Bluesky (AT Protocol) accounts. - Supports configuration via OpenClaw config or environment variables. - Provides helper scripts and curl examples for read, search, post, and monitor operations. - Includes error handling guidance and best practices for authentication and rate limiting.
Metadata
Slug bluesky-api
Version 1.0.3
License MIT-0
All-time Installs 0
Active Installs 0
Total Versions 4
Frequently Asked Questions

What is Bluesky API?

Read, search, post, and monitor Bluesky (AT Protocol) accounts. It is an AI Agent Skill for Claude Code / OpenClaw, with 149 downloads so far.

How do I install Bluesky API?

Run "/install bluesky-api" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Bluesky API free?

Yes, Bluesky API is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Bluesky API support?

Bluesky API is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Bluesky API?

It is built and maintained by minupla (@minupla); the current version is v1.0.3.

💬 Comments