The Problem
Every Nostr client has spam. Bots, impersonators, follow-farmers. If you're building a client, you need trust scoring. But computing PageRank over 51K+ nodes and 620K edges isn't something you want to do client-side.
The API
The WoT Scoring API at wot.klabo.world does the heavy lifting. It crawls the follow graph every 6 hours, runs PageRank, publishes NIP-85 trust assertions, and exposes 49 endpoints for trust analysis.
The simplest integration — a trust score for any pubkey:
const res = await fetch(
'https://wot.klabo.world/score?pubkey=' + hexPubkey
);
const data = await res.json();
// data.composite_score: 0-100 trust score
// data.followers: follower count
// data.found: true if pubkey is in the graph
That's it. One call. The composite_score blends PageRank with external NIP-85 assertions (70% internal, 30% external).
Filter Spam in Your Feed
Score every event author and hide low-trust accounts:
async function shouldShowEvent(event) {
const res = await fetch(
'https://wot.klabo.world/score?pubkey=' + event.pubkey
);
const data = await res.json();
if (!data.found) return false; // unknown = suspicious
if (data.composite_score < 3) return false; // likely spam
return true;
}
A score of 3+ filters out most spam while keeping real accounts. Adjust the threshold based on your client's needs — stricter clients might use 5+, permissive ones might use 1+.
Detect Bots
The /sybil endpoint runs 8 behavioral signals to classify accounts:
const res = await fetch(
'https://wot.klabo.world/sybil?pubkey=' + hexPubkey
);
const data = await res.json();
// data.classification: "genuine", "likely_genuine",
// "suspicious", "likely_sybil"
// data.spam_probability: 0-100
Show a warning badge for "suspicious" or "likely_sybil" accounts. The signals include follower-to-following ratio, mutual follow density, engagement consistency, and follow velocity.
Show Trust Context
For profile pages, the /reputation endpoint gives a rich breakdown:
const res = await fetch(
'https://wot.klabo.world/reputation?pubkey=' + hexPubkey
);
const data = await res.json();
// data.overall_grade: "A", "B", "C", "D", "F"
// data.trust_score, data.sybil_resistance,
// data.community_standing, data.anomaly_risk
Display the letter grade on profiles. "A" accounts are well-connected, long-standing, with diverse followers. "F" accounts have anomalous patterns.
Free Tier
50 requests per day per IP are free. After that, the API returns 402 with a Lightning invoice. For client-side usage, 50/day is usually enough — cache scores locally and refresh periodically.
For server-side integration with higher volume, the L402 flow is straightforward:
- Request returns 402 +
WWW-Authenticateheader with invoice - Pay the invoice (1-10 sats depending on endpoint)
- Retry with
Authorization: L402 <payment_hash>
JavaScript SDK
For a batteries-included integration:
npm install nostr-wot
import { WoTClient } from 'nostr-wot';
const wot = new WoTClient();
const score = await wot.score(hexPubkey);
const sybil = await wot.sybilCheck(hexPubkey);
const rep = await wot.reputation(hexPubkey);
Source: github.com/joelklabo/nostr-wot
Full API Docs
- Interactive demo: wot.klabo.world/demo
- Swagger UI: wot.klabo.world/swagger
- OpenAPI spec: wot.klabo.world/openapi.json
49 endpoints total covering trust scoring, sybil detection, anomaly detection, influence analysis, trust circles, follow quality, network health, link prediction, and cross-provider verification.