CLASP SDK

Let AI agents pay for any API. 3 lines of code. Sub-cent micropayments on XRP payment channels.

Current Status: CLASP is live on XRPL Testnet. Python, Node.js, and Go SDKs available. Mainnet launch coming Q2 2026.

Installation

# Core SDK (agent-side)
pip install clasp-sdk

# With server/middleware support (provider-side)
pip install clasp-sdk[server]

Requires Python 3.10+. Dependencies: httpx, xrpl-py, pydantic.

npm install @clasp-sdk/node

Requires Node.js 18+. Dependencies: axios, xrpl.

go get github.com/symplesolutions/clasp-go

Requires Go 1.22+.

Quick Start — 5 Minutes to First Payment

1 Get a Testnet Wallet

You need an XRP wallet with testnet funds. The fastest way:

# Python — generate and fund a wallet
from xrpl.wallet import generate_faucet_wallet
from xrpl.clients import JsonRpcClient

client = JsonRpcClient("https://s.altnet.rippletest.net:51234")
wallet = generate_faucet_wallet(client)
print(f"Address: {wallet.address}")
print(f"Seed:    {wallet.seed}")
# Save your seed — you'll need it below

2 Make a Paid API Call (Agent)

import asyncio
from clasp import CLASPAgent

async def main():
    agent = CLASPAgent(
        wallet_seed="sEd...",  # Your testnet seed
        clasp_api_url="http://localhost:8000",
    )

    # This handles 402 → open channel → pay → get data
    response = await agent.call("http://localhost:9000/weather?city=Toronto")
    print(response.json())

    await agent.close()

asyncio.run(main())
import { CLASPAgent } from "@clasp-sdk/node";

const agent = new CLASPAgent({
  walletSeed: "sEd...",
  claspApiUrl: "http://localhost:8000",
});

const data = await agent.call("http://localhost:9000/weather?city=Toronto");
console.log(data);
package main

import (
    "fmt"
    clasp "github.com/symplesolutions/clasp-go"
)

func main() {
    agent := clasp.NewAgent(clasp.AgentConfig{
        WalletSeed:  "sEd...",
        CLASPApiURL: "http://localhost:8000",
    })

    resp, err := agent.Call("http://localhost:9000/weather?city=Toronto")
    if err != nil { panic(err) }
    fmt.Println(string(resp))
}

3 Monetize Your API (Provider)

from fastapi import FastAPI, Request
from clasp import CLASPServer

app = FastAPI()
clasp = CLASPServer(
    provider_address="rYour...",
    clasp_api_url="http://localhost:8000",
)

@app.get("/weather")
@clasp.require_payment(price_drops=1000)  # 0.001 XRP
async def weather(request: Request):
    return {"temp": 22, "condition": "sunny", "city": "Toronto"}
import express from "express";
import { CLASPServer } from "@clasp-sdk/node";

const app = express();
const clasp = new CLASPServer({
  providerAddress: "rYour...",
  claspApiUrl: "http://localhost:8000",
});

app.get("/weather", clasp.requirePayment(1000), (req, res) => {
  res.json({ temp: 22, condition: "sunny", city: "Toronto" });
});

app.listen(9000);
package main

import (
    "net/http"
    "encoding/json"
    clasp "github.com/symplesolutions/clasp-go"
)

func main() {
    mw := clasp.NewMiddleware(clasp.MiddlewareConfig{
        ProviderAddress: "rYour...",
        CLASPApiURL:     "http://localhost:8000",
        PriceDrops:      1000,
    })

    http.Handle("/weather", mw.RequirePayment(
        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            json.NewEncoder(w).Encode(map[string]any{
                "temp": 22, "condition": "sunny",
            })
        }),
    ))
    http.ListenAndServe(":9000", nil)
}

How It Works

CLASP uses XRP Payment Channels — a built-in XRPL feature for off-ledger micropayments.

Agent ──GET /weather──▶ Provider API
Provider ◀──402 + price── needs 1000 drops
Agent ──open channel──▶ XRPL (on-chain, once)
Agent ──GET + claim────▶ Provider API
Provider ◀──200 + data──── {"temp": 22}
Subsequent calls: just claims (free, instant, off-chain)

Key insight: Only 2 on-chain transactions are needed — one to open the channel, one to settle. Everything in between is free, instant, and unlimited.

MetricCLASPStripe x402Coinbase x402
Cost per 10K calls$0.0004$3,000$5.00
On-chain transactions2 total10,00010,000
Settlement latencyInstant (off-chain)~3 sec~15 sec
Minimum payment1 drop ($0.0000005)$0.30$0.01

Guide: Build an AI Agent

An AI agent using CLASP can autonomously discover, evaluate, and pay for APIs without human intervention.

Discovery — Find APIs

endpoints = await agent.discover("sentiment analysis")
for ep in endpoints:
    print(f"{ep.url} — {ep.price_drops} drops — {ep.description}")

Automatic Payment Flow

When you call agent.call(url), the SDK handles everything:

  1. Sends the HTTP request to the target URL
  2. If the response is 402 with type: clasp-payment-required, extracts price and provider address
  3. Opens a payment channel if none exists for that provider (configurable via auto_open_channel)
  4. Creates a cryptographic claim (off-chain signature)
  5. Retries the request with X-CLASP-* headers
  6. Returns the successful response

Budget Control

# Check spending
print(f"Total spent: {agent.total_spent_drops} drops")
print(f"Channel remaining: {agent.channel_remaining(channel_id)} drops")

# Set channel funding limit
agent = CLASPAgent(
    wallet_seed="sEd...",
    default_channel_drops=5_000_000,  # Max 5 XRP per channel
)

Guide: Monetize Your API

Turn any existing API into a paid service with 3 lines of code. No signup, no billing integration, no invoicing.

Option A: Decorator (per-route)

@clasp.require_payment(price_drops=1000)
async def my_endpoint(request: Request):
    # Access claim info
    claim = request.state.clasp_claim
    print(f"Paid: {claim['amount_drops']} drops")
    print(f"Remaining: {claim['remaining_drops']} drops")
    return {"data": "premium content"}

Option B: Middleware (entire API)

from clasp import CLASPMiddleware

app = CLASPMiddleware(
    app,
    provider_address="rYour...",
    clasp_api_url="http://localhost:8000",
    default_price_drops=1000,
    skip_paths={"/docs", "/health", "/openapi.json"},
)

Revenue Collection

CLASP accumulates claims off-chain. To collect your XRP:

Discovery

The CLASP Discovery API lets agents find APIs that accept CLASP payments. Think of it as a marketplace where APIs list themselves.

Search

# By keyword
endpoints = await agent.discover("weather")

# Via the API directly
GET /api/v1/discover?q=weather&category=data&max_price=5000

Register Your API

POST /api/v1/endpoints/register
{
  "url": "https://api.example.com/weather",
  "description": "Real-time weather data for any city",
  "price_drops": 1000,
  "category": "data",
  "tags": ["weather", "location", "forecast"]
}

Channel Management

Opening Channels

# Auto: agent.call() opens channels automatically on first 402

# Manual: explicit channel with custom funding
channel = await agent.open_channel(
    provider_address="rProvider...",
    amount_drops=50_000_000,  # 50 XRP
)
print(f"Channel ID: {channel.channel_id}")
print(f"Funded: {channel.amount_drops} drops")

Channel Lifecycle

  1. Open — Agent creates PaymentChannelCreate on XRPL (funds locked)
  2. Active — Agent sends signed claims, provider verifies and serves data
  3. Settling — Provider submits highest claim to XRPL
  4. Closed — Remaining funds returned to agent (after dispute window)
Dispute window: Channels have a 24-hour settle delay by default. This gives agents time to dispute if a provider submits an incorrect claim amount.

API Reference: CLASPAgent

CLASPAgent(
    wallet_seed: str,              # XRPL wallet seed
    clasp_api_url: str = "...",    # CLASP backend URL
    xrpl_node_wss: str = "...",    # XRPL WebSocket node
    auto_open_channel: bool = True,# Auto-open on 402
    default_channel_drops: int = 10_000_000,
)
MethodReturnsDescription
call(url, method, ...)ResponseMake paid HTTP request. Handles 402 flow automatically
discover(query)list[EndpointInfo]Search for CLASP-enabled APIs
open_channel(addr, drops)ChannelInfoOpen an XRP payment channel on XRPL
create_claim(ch_id, price)ClaimCreate a signed micropayment claim
close()NoneClean up HTTP client resources
.total_spent_dropsintTotal drops spent across all channels
channel_remaining(id)int | NoneRemaining drops in a channel

API Reference: CLASPServer

CLASPServer(
    provider_address: str,          # Your XRPL address
    clasp_api_url: str = "...",     # CLASP backend URL
)
MethodReturnsDescription
require_payment(price_drops)decoratorRoute decorator requiring CLASP payment

API Reference: CLASPMiddleware

CLASPMiddleware(
    app,                            # Your ASGI application
    provider_address: str,          # Your XRPL address
    clasp_api_url: str = "...",     # CLASP backend URL
    default_price_drops: int = 1000,# Price per request
    skip_paths: set[str] = {...},   # Paths to skip
    verify_via_api: bool = True,    # Verify claims via backend
)

ASGI middleware that enforces CLASP payments on all requests except skip_paths (/docs, /health, /openapi.json, etc).

API Reference: Claims

# Low-level claim creation
from clasp import create_claim, verify_claim

claim = create_claim(
    channel_id="ABC123...",     # 64-char hex channel ID
    amount_drops=5000,          # Cumulative total
    private_key="ED...",        # Hex-encoded private key
)

valid = verify_claim(claim, public_key="ED...")
Cumulative amounts: Claim amounts are monotonically increasing. If you've already claimed 3000 drops and the next API call costs 1000, the new claim amount is 4000 (not 1000). The SDK handles this automatically.

API Reference: Types

# All types are frozen dataclasses

@dataclass(frozen=True)
class Claim:
    channel_id: str      # 64-char hex
    amount_drops: int     # Cumulative total drops
    signature: str        # ECDSA signature

@dataclass(frozen=True)
class ChannelInfo:
    channel_id: str
    amount_drops: int     # Total funded
    balance_drops: int    # Currently claimed
    status: str           # "open", "settling", "closed"

@dataclass(frozen=True)
class EndpointInfo:
    url: str
    description: str
    price_drops: int
    tags: list[str] | None
    category: str | None

Protocol: HTTP 402 Flow

CLASP extends the HTTP 402 status code (Payment Required) with a structured JSON response:

# 1. Agent sends request without payment
GET /weather?city=Toronto HTTP/1.1
Host: api.example.com

# 2. Provider responds with 402
HTTP/1.1 402 Payment Required
Content-Type: application/json
X-CLASP-Price: 1000

{
  "type": "clasp-payment-required",
  "version": "1.0",
  "provider_address": "rProvider123...",
  "price_drops": 1000,
  "clasp_api": "http://localhost:8000"
}

# 3. Agent retries with payment claim
GET /weather?city=Toronto HTTP/1.1
X-CLASP-Channel-Id: ABC123...
X-CLASP-Amount: 1000
X-CLASP-Signature: 3045022100...

# 4. Provider verifies claim and responds
HTTP/1.1 200 OK
X-CLASP-Remaining: 9999000
X-CLASP-Channel-Id: ABC123...

{"temp": 22, "condition": "sunny"}

Protocol: Headers

HeaderDirectionDescription
X-CLASP-Channel-IdRequest & Response64-char hex payment channel ID
X-CLASP-AmountRequestCumulative drops claimed (integer)
X-CLASP-SignatureRequestECDSA signature over claim payload
X-CLASP-PriceResponse (402)Required drops for this request
X-CLASP-RemainingResponse (200)Remaining drops in channel

Protocol: Payment Channels

CLASP uses native XRPL Payment Channels. Key properties:

Claim Payload Format

# 44-byte canonical payload
CLM\x00                  # 4 bytes: prefix
+ channel_id             # 32 bytes: hex-decoded channel ID
+ amount_drops           # 8 bytes: big-endian uint64

# Signed with secp256k1 ECDSA (same as XRPL transactions)

Examples

Weather API (Full Example)

# provider.py — Monetized weather API
from fastapi import FastAPI, Request
from clasp import CLASPServer
import httpx

app = FastAPI()
clasp = CLASPServer(provider_address="rYour...", clasp_api_url="http://localhost:8000")

@app.get("/weather")
@clasp.require_payment(price_drops=1000)
async def weather(request: Request, city: str = "Toronto"):
    # Fetch real weather data
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"https://wttr.in/{city}?format=j1")
        data = resp.json()
    current = data["current_condition"][0]
    return {
        "city": city,
        "temp_c": int(current["temp_C"]),
        "condition": current["weatherDesc"][0]["value"],
        "humidity": int(current["humidity"]),
        "paid_drops": request.state.clasp_claim["amount_drops"],
    }

# Run: uvicorn provider:app --port 9000
# agent.py — AI agent that pays for weather
import asyncio
from clasp import CLASPAgent

async def main():
    agent = CLASPAgent(wallet_seed="sEd...")

    cities = ["Toronto", "Tokyo", "London", "Sydney"]
    for city in cities:
        resp = await agent.call(f"http://localhost:9000/weather?city={city}")
        data = resp.json()
        print(f"{data['city']}: {data['temp_c']}°C, {data['condition']}")

    print(f"\nTotal spent: {agent.total_spent_drops} drops")
    await agent.close()

asyncio.run(main())

FAQ

How much does it cost?

CLASP itself is free and open source. The only costs are XRP transaction fees (~0.00001 XRP to open/close channels) and the micropayments themselves (set by API providers, typically 0.001 XRP per call).

Is this on mainnet?

Currently testnet only. Mainnet launch planned for Q2 2026. The SDK defaults to the XRPL testnet — just change xrpl_node_wss to a mainnet node when ready.

What if the provider goes offline?

Your funds are safe. XRP is locked in a payment channel on XRPL. If the provider never settles, you can close the channel after the dispute window (24h) and get all your XRP back.

Can I set spending limits?

Yes. Control spending via default_channel_drops (max per channel) and auto_open_channel=False (require explicit channel opening). The SDK tracks cumulative spending per channel.

How is this different from Stripe x402 / Coinbase x402?

Stripe x402 uses Base (Ethereum L2) — each payment is an on-chain transaction ($0.30+ fees). Coinbase x402 uses USDC on Base. CLASP uses XRP payment channels — unlimited off-chain micropayments after one on-chain channel open. Up to 2,500x cheaper at scale.

Can I use this with LangChain / LlamaIndex / CrewAI?

Yes. CLASPAgent.call() is a standard async HTTP call. Wrap it as a tool in any agent framework. Example with LangChain coming soon.