
Neynar
- 13 installs
- 1.2k repo stars
- Updated August 1, 2026
- bankrbot/moltbot-skills
Neynar is a Claude skill that reads and writes to the Farcaster social protocol through the Neynar API.
About
Neynar interacts with the Farcaster decentralized social protocol via Neynar's API. A developer or agent uses it to read feeds, look up users by username or FID, search casts, and post or react. Read operations use an API key; write operations such as posting and following require a Neynar signer.
- Read and write to the Farcaster social protocol via the Neynar API
- Look up users, read channel/trending feeds, search casts, and post
- Single shell script wrapping Neynar v2 endpoints; posting needs a signer
Neynar by the numbers
- 13 all-time installs (skills.sh)
- Ranked #289 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
neynar capabilities & compatibility
Needs a Neynar API key (free tier 300 req/min) and a signerUuid for posting; paid tiers offer higher limits.
- Capabilities
- farcaster read · farcaster post · user lookup · cast search
- Use cases
- marketing · web search
- Pricing
- Freemium
What neynar says it does
Interact with the Farcaster decentralized social protocol via Neynar's API.
**Note**: `signerUuid` is only required for posting casts.
npx skills add https://github.com/bankrbot/moltbot-skills --skill neynarAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 1.2k |
| Last updated | August 1, 2026 |
| Repository | bankrbot/moltbot-skills ↗ |
What it does
Read Farcaster feeds, look up users, search casts, and post via the Neynar API using a bundled shell script.
Who is it for?
Agents that need to read Farcaster feeds, look up users, search casts, or post to Farcaster.
Skip if: Posting without a configured Neynar signer, or non-Farcaster social platforms.
When should I use this skill?
The user wants to read Farcaster feeds, look up users, post casts, or search Farcaster content.
What you get
Fetched Farcaster feeds and users, or a published cast or reaction via Neynar.
- Farcaster feed/user data
- Published casts or reactions
By the numbers
- 10 Farcaster API endpoints wrapped
- 300 requests/minute free tier
Files
Neynar (Farcaster API)
Interact with the Farcaster decentralized social protocol via Neynar's API.
Quick Start
Setup
1. Get an API key from dev.neynar.com 2. Create config:
mkdir -p ~/.clawdbot/skills/neynar
cat > ~/.clawdbot/skills/neynar/config.json << 'EOF'
{
"apiKey": "YOUR_NEYNAR_API_KEY",
"signerUuid": "YOUR_SIGNER_UUID"
}
EOFNote: signerUuid is only required for posting casts. Get one via Neynar's signer management.
Verify Setup
scripts/neynar.sh user dwr.ethCore Concepts
- FID — Farcaster ID, a permanent numeric identifier for each user
- Cast — A post on Farcaster (like a tweet)
- Channel — Topic-based feeds (like subreddits)
- Frame — Interactive mini-apps embedded in casts
Usage
User Lookup
# By username
scripts/neynar.sh user vitalik.eth
# By FID
scripts/neynar.sh user --fid 5650
# Multiple users
scripts/neynar.sh users dwr.eth,v,jessepollakRead Feed
# User's casts
scripts/neynar.sh feed --user dwr.eth
# Channel feed
scripts/neynar.sh feed --channel base
# Trending feed
scripts/neynar.sh feed --trending
# Following feed (requires signer)
scripts/neynar.sh feed --followingSearch
# Search casts
scripts/neynar.sh search "ethereum"
# Search users
scripts/neynar.sh search-users "vitalik"
# Search in channel
scripts/neynar.sh search "onchain summer" --channel baseGet Cast
# By hash
scripts/neynar.sh cast 0x1234abcd...
# By URL
scripts/neynar.sh cast "https://warpcast.com/dwr.eth/0x1234"Post Cast (requires signer)
# Simple cast
scripts/neynar.sh post "gm farcaster"
# Reply to cast
scripts/neynar.sh post "great point!" --reply-to 0x1234abcd
# Cast in channel
scripts/neynar.sh post "hello base" --channel base
# Cast with embed
scripts/neynar.sh post "check this out" --embed "https://example.com"Reactions
# Like a cast
scripts/neynar.sh like 0x1234abcd
# Recast
scripts/neynar.sh recast 0x1234abcdFollow/Unfollow
scripts/neynar.sh follow dwr.eth
scripts/neynar.sh unfollow dwr.ethAPI Reference
Endpoints Used
| Action | Endpoint | Auth |
|---|---|---|
| User lookup | GET /v2/farcaster/user/by_username | API key |
| User by FID | GET /v2/farcaster/user/bulk | API key |
| User feed | GET /v2/farcaster/feed/user/casts | API key |
| Channel feed | GET /v2/farcaster/feed/channels | API key |
| Trending | GET /v2/farcaster/feed/trending | API key |
| Search casts | GET /v2/farcaster/cast/search | API key |
| Get cast | GET /v2/farcaster/cast | API key |
| Post cast | POST /v2/farcaster/cast | API key + Signer |
| React | POST /v2/farcaster/reaction | API key + Signer |
| Follow | POST /v2/farcaster/user/follow | API key + Signer |
Response Format
All responses are JSON. The script extracts key fields for readability:
{
"user": {
"fid": 3,
"username": "dwr.eth",
"display_name": "Dan Romero",
"follower_count": 450000,
"following_count": 2800,
"verified_addresses": ["0x..."]
}
}Common Patterns
Monitor a Channel
# Get latest casts from /base channel
scripts/neynar.sh feed --channel base --limit 20Find Active Users
# Search for users by keyword
scripts/neynar.sh search-users "ethereum developer"Cross-Post from Twitter
# Post same content to Farcaster
scripts/neynar.sh post "gm, just shipped a new feature 🚀"Reply to Mentions
# Get your notifications (requires signer)
scripts/neynar.sh notifications
# Reply to specific cast
scripts/neynar.sh post "thanks!" --reply-to 0xabc123Error Handling
| Error | Cause | Fix |
|---|---|---|
| 401 Unauthorized | Invalid API key | Check config.json |
| 403 Forbidden | Signer required | Set up signer for write operations |
| 404 Not Found | User/cast doesn't exist | Verify username/hash |
| 429 Rate Limited | Too many requests | Wait and retry |
Signer Setup
For write operations (posting, liking, following), you need a signer:
1. Go to dev.neynar.com 2. Create a new signer or use managed signer 3. Add signerUuid to your config
Managed signers are easiest — Neynar handles the key custody.
Rate Limits
- Free tier: 300 requests/minute
- Paid tiers: Higher limits available
- Check
X-RateLimit-Remainingheader
Best Practices
1. Cache user lookups — FIDs don't change, usernames rarely do 2. Use channels — Better reach than random posting 3. Engage genuinely — Farcaster culture values authenticity 4. Batch requests — Use bulk endpoints when possible 5. Handle rate limits — Implement backoff
Resources
- Neynar Docs: https://docs.neynar.com
- API Reference: https://docs.neynar.com/reference
- Developer Portal: https://dev.neynar.com
- Farcaster Docs: https://docs.farcaster.xyz
Troubleshooting
API Key Not Working
# Verify key works
curl -H "x-api-key: YOUR_KEY" \
"https://api.neynar.com/v2/farcaster/user/bulk?fids=1"Signer Issues
- Ensure signer is approved and active
- Check signer permissions match your FID
- Managed signers are simpler than self-hosted
Cast Not Appearing
- Casts propagate in seconds, but indexing may take longer
- Check the cast hash in the response
- Verify on warpcast.com directly
#!/usr/bin/env bash
set -euo pipefail
# Neynar Farcaster API wrapper
# Usage: neynar.sh <command> [args]
CONFIG_FILE="${HOME}/.clawdbot/skills/neynar/config.json"
API_BASE="https://api.neynar.com/v2/farcaster"
# Load config
if [[ -f "$CONFIG_FILE" ]]; then
API_KEY=$(jq -r '.apiKey // empty' "$CONFIG_FILE")
SIGNER_UUID=$(jq -r '.signerUuid // empty' "$CONFIG_FILE")
else
echo "Error: Config not found at $CONFIG_FILE" >&2
echo "Create it with: mkdir -p ~/.clawdbot/skills/neynar && echo '{\"apiKey\":\"YOUR_KEY\"}' > $CONFIG_FILE" >&2
exit 1
fi
if [[ -z "$API_KEY" ]]; then
echo "Error: apiKey not set in config" >&2
exit 1
fi
# API request helpers with proper error handling
api_get() {
local endpoint="$1"
local response
if ! response=$(curl -sf -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
"${API_BASE}${endpoint}" 2>&1); then
echo "Error: API request failed for $endpoint" >&2
return 1
fi
echo "$response"
}
# Use heredoc to avoid exposing data in process list
api_post() {
local endpoint="$1"
local data="$2"
local response
if ! response=$(curl -sf -X POST -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
--data-binary @- "${API_BASE}${endpoint}" <<< "$data" 2>&1); then
echo "Error: API POST failed for $endpoint" >&2
return 1
fi
echo "$response"
}
# Commands
cmd_user() {
if [[ $# -eq 0 ]]; then
echo "Error: user requires a username or FID" >&2
exit 1
fi
local identifier="$1"
local by_fid=false
if [[ "${2:-}" == "--fid" ]] || [[ "$identifier" =~ ^[0-9]+$ ]]; then
by_fid=true
fi
if $by_fid; then
api_get "/user/bulk?fids=$identifier" | jq '.users[0] | {
fid,
username,
display_name,
pfp_url,
bio: .profile.bio.text,
follower_count,
following_count,
verified_addresses: .verified_addresses.eth_addresses
}'
else
api_get "/user/by_username?username=$identifier" | jq '.user | {
fid,
username,
display_name,
pfp_url,
bio: .profile.bio.text,
follower_count,
following_count,
verified_addresses: .verified_addresses.eth_addresses
}'
fi
}
cmd_users() {
if [[ $# -eq 0 ]]; then
echo "Error: users requires comma-separated usernames" >&2
exit 1
fi
local usernames="$1"
# Convert comma-separated to API format
api_get "/user/bulk-by-username?usernames=$usernames" | jq '.users[] | {
fid,
username,
display_name,
follower_count
}'
}
cmd_feed() {
local feed_type="user"
local identifier=""
local limit=10
while [[ $# -gt 0 ]]; do
case "$1" in
--user) feed_type="user"; identifier="$2"; shift 2 ;;
--channel) feed_type="channel"; identifier="$2"; shift 2 ;;
--trending) feed_type="trending"; shift ;;
--following) feed_type="following"; shift ;;
--limit) limit="$2"; shift 2 ;;
*) identifier="$1"; shift ;;
esac
done
case "$feed_type" in
user)
if [[ -n "$identifier" ]]; then
api_get "/feed/user/casts?fid=$identifier&limit=$limit" | jq '.casts[] | {
hash,
text,
timestamp,
reactions: {likes: .reactions.likes_count, recasts: .reactions.recasts_count},
replies_count
}'
else
echo "Error: --user requires a username or FID" >&2
exit 1
fi
;;
channel)
api_get "/feed/channels?channel_ids=$identifier&limit=$limit" | jq '.casts[] | {
hash,
author: .author.username,
text,
timestamp,
reactions: {likes: .reactions.likes_count, recasts: .reactions.recasts_count}
}'
;;
trending)
api_get "/feed/trending?limit=$limit" | jq '.casts[] | {
hash,
author: .author.username,
text,
timestamp,
reactions: {likes: .reactions.likes_count, recasts: .reactions.recasts_count}
}'
;;
following)
if [[ -z "$SIGNER_UUID" ]]; then
echo "Error: Following feed requires signerUuid in config" >&2
exit 1
fi
api_get "/feed/following?fid=me&limit=$limit" | jq '.casts[] | {
hash,
author: .author.username,
text,
timestamp
}'
;;
esac
}
cmd_search() {
if [[ $# -eq 0 ]]; then
echo "Error: search requires a query" >&2
exit 1
fi
local query="$1"
local channel=""
local limit=10
shift
while [[ $# -gt 0 ]]; do
case "$1" in
--channel) channel="$2"; shift 2 ;;
--limit) limit="$2"; shift 2 ;;
*) shift ;;
esac
done
local endpoint="/cast/search?q=$(echo "$query" | jq -Rr @uri)&limit=$limit"
if [[ -n "$channel" ]]; then
endpoint="${endpoint}&channel_id=$channel"
fi
api_get "$endpoint" | jq '.result.casts[] | {
hash,
author: .author.username,
text,
timestamp,
channel: .channel.id
}'
}
cmd_search_users() {
if [[ $# -eq 0 ]]; then
echo "Error: search-users requires a query" >&2
exit 1
fi
local query="$1"
api_get "/user/search?q=$(echo "$query" | jq -Rr @uri)&limit=10" | jq '.result.users[] | {
fid,
username,
display_name,
follower_count
}'
}
cmd_cast() {
if [[ $# -eq 0 ]]; then
echo "Error: cast requires a hash or URL" >&2
exit 1
fi
local identifier="$1"
# Check if it's a URL or hash
if [[ "$identifier" == http* ]]; then
api_get "/cast?type=url&identifier=$(echo "$identifier" | jq -Rr @uri)" | jq '.cast | {
hash,
author: .author.username,
text,
timestamp,
reactions: {likes: .reactions.likes_count, recasts: .reactions.recasts_count},
replies_count,
embeds
}'
else
api_get "/cast?type=hash&identifier=$identifier" | jq '.cast | {
hash,
author: .author.username,
text,
timestamp,
reactions: {likes: .reactions.likes_count, recasts: .reactions.recasts_count},
replies_count,
embeds
}'
fi
}
cmd_post() {
if [[ -z "$SIGNER_UUID" ]]; then
echo "Error: Posting requires signerUuid in config" >&2
exit 1
fi
if [[ $# -eq 0 ]]; then
echo "Error: post requires text" >&2
exit 1
fi
local text="$1"
local reply_to=""
local channel=""
local embed=""
shift
while [[ $# -gt 0 ]]; do
case "$1" in
--reply-to) reply_to="$2"; shift 2 ;;
--channel) channel="$2"; shift 2 ;;
--embed) embed="$2"; shift 2 ;;
*) shift ;;
esac
done
# Build JSON safely with jq to prevent injection
local data
data=$(jq -n \
--arg signer "$SIGNER_UUID" \
--arg text "$text" \
--arg reply_to "$reply_to" \
--arg channel "$channel" \
--arg embed "$embed" \
'{signer_uuid: $signer, text: $text} +
(if $reply_to != "" then {parent: $reply_to} else {} end) +
(if $channel != "" then {channel_id: $channel} else {} end) +
(if $embed != "" then {embeds: [{url: $embed}]} else {} end)')
api_post "/cast" "$data" | jq '{
success: .success,
hash: .cast.hash,
text: .cast.text
}'
}
cmd_like() {
if [[ -z "$SIGNER_UUID" ]]; then
echo "Error: Liking requires signerUuid in config" >&2
exit 1
fi
if [[ $# -eq 0 ]]; then
echo "Error: like requires a cast hash" >&2
exit 1
fi
local cast_hash="$1"
# Build JSON safely with jq
local data
data=$(jq -n \
--arg signer "$SIGNER_UUID" \
--arg target "$cast_hash" \
'{signer_uuid: $signer, reaction_type: "like", target: $target}')
api_post "/reaction" "$data" | jq '{
success: .success,
reaction_type: .reaction.reaction_type
}'
}
cmd_recast() {
if [[ -z "$SIGNER_UUID" ]]; then
echo "Error: Recasting requires signerUuid in config" >&2
exit 1
fi
if [[ $# -eq 0 ]]; then
echo "Error: recast requires a cast hash" >&2
exit 1
fi
local cast_hash="$1"
# Build JSON safely with jq
local data
data=$(jq -n \
--arg signer "$SIGNER_UUID" \
--arg target "$cast_hash" \
'{signer_uuid: $signer, reaction_type: "recast", target: $target}')
api_post "/reaction" "$data" | jq '{
success: .success,
reaction_type: .reaction.reaction_type
}'
}
cmd_follow() {
if [[ -z "$SIGNER_UUID" ]]; then
echo "Error: Following requires signerUuid in config" >&2
exit 1
fi
if [[ $# -eq 0 ]]; then
echo "Error: follow requires a username" >&2
exit 1
fi
local username="$1"
# First get FID with error handling
local user_response
if ! user_response=$(api_get "/user/by_username?username=$username"); then
echo "Error: Failed to look up user $username" >&2
exit 1
fi
local fid
fid=$(echo "$user_response" | jq -r '.user.fid')
if [[ -z "$fid" || "$fid" == "null" ]]; then
echo "Error: Could not find FID for user $username" >&2
exit 1
fi
# Build JSON safely with jq
local data
data=$(jq -n \
--arg signer "$SIGNER_UUID" \
--argjson fid "$fid" \
'{signer_uuid: $signer, target_fids: [$fid]}')
api_post "/user/follow" "$data" | jq '{
success: .success,
followed: .target_fids
}'
}
cmd_unfollow() {
if [[ -z "$SIGNER_UUID" ]]; then
echo "Error: Unfollowing requires signerUuid in config" >&2
exit 1
fi
if [[ $# -eq 0 ]]; then
echo "Error: unfollow requires a username" >&2
exit 1
fi
local username="$1"
# First get FID with error handling
local user_response
if ! user_response=$(api_get "/user/by_username?username=$username"); then
echo "Error: Failed to look up user $username" >&2
exit 1
fi
local fid
fid=$(echo "$user_response" | jq -r '.user.fid')
if [[ -z "$fid" || "$fid" == "null" ]]; then
echo "Error: Could not find FID for user $username" >&2
exit 1
fi
# Build JSON safely with jq
local data
data=$(jq -n \
--arg signer "$SIGNER_UUID" \
--argjson fid "$fid" \
'{signer_uuid: $signer, target_fids: [$fid], unfollow: true}')
api_post "/user/follow" "$data" | jq '{
success: .success,
unfollowed: .target_fids
}'
}
cmd_help() {
cat << 'EOF'
Neynar Farcaster API CLI
Usage: neynar.sh <command> [args]
Commands:
user <username|fid> Look up a user
users <user1,user2,...> Look up multiple users
feed [options] Get feed
--user <username> User's casts
--channel <id> Channel feed
--trending Trending casts
--following Following feed (requires signer)
--limit <n> Number of results
search <query> Search casts
--channel <id> Filter by channel
search-users <query> Search users
cast <hash|url> Get cast details
post <text> Post a cast (requires signer)
--reply-to <hash> Reply to cast
--channel <id> Post in channel
--embed <url> Embed a URL
like <hash> Like a cast (requires signer)
recast <hash> Recast (requires signer)
follow <username> Follow user (requires signer)
unfollow <username> Unfollow user (requires signer)
Examples:
neynar.sh user dwr.eth
neynar.sh feed --channel base --limit 5
neynar.sh search "ethereum" --channel ethereum
neynar.sh post "gm farcaster"
EOF
}
# Main dispatch
case "${1:-help}" in
user) shift; cmd_user "$@" ;;
users) shift; cmd_users "$@" ;;
feed) shift; cmd_feed "$@" ;;
search) shift; cmd_search "$@" ;;
search-users) shift; cmd_search_users "$@" ;;
cast) shift; cmd_cast "$@" ;;
post) shift; cmd_post "$@" ;;
like) shift; cmd_like "$@" ;;
recast) shift; cmd_recast "$@" ;;
follow) shift; cmd_follow "$@" ;;
unfollow) shift; cmd_unfollow "$@" ;;
help|--help|-h) cmd_help ;;
*) echo "Unknown command: $1" >&2; cmd_help; exit 1 ;;
esac
Related skills
FAQ
Do I need a signer?
Only for write operations like posting, liking, and following; reads need just the API key.
What are the rate limits?
The free tier allows 300 requests per minute, with higher limits on paid tiers.