Base URL

Development: http://localhost:3002
Production:  https://api.positlabs.io

Authentication Live

The API uses API keys for authentication. Include your key in the X-API-Key header:

curl -H "X-API-Key: your_api_key" https://api.positlabs.io/api/v1/wallets/...

Rate Limits

Tier Requests/Min Requests/Day
Basic 60 10,000
Pro 300 100,000
Enterprise 1,000 1,000,000

Public API v1 OpenAPI 3.1

Full OpenAPI spec available at /api/v1/openapi.json

Wallet Intelligence

GET /api/v1/wallets/{address}/rating

Get wallet rating, score, decay state, and labels.

Path Parameters
address string Wallet address required
Query Parameters
chain string Chain: solana | base | hyperliquid (default: solana)
Response
{
  "address": "7xKp...",
  "chain": "solana",
  "score": 87.5,
  "decayState": "sharp",
  "flaggedAt": "2025-01-15T00:00:00Z",
  "labels": [
    { "label": "smart_money", "confidence": 0.92 },
    { "label": "early_buyer", "confidence": 0.88 }
  ],
  "metrics": {
    "winRate": 72.5,
    "avgReturn": 245.8,
    "totalTrades": 156,
    "profitableTrades": 113
  }
}
GET /api/v1/wallets/leaderboard

Get top-ranked wallets by score.

Query Parameters
chain string Filter by chain: solana | base | hyperliquid
limit number Max results (default: 50, max: 100)
decayState string Filter: sharp | fading | dead
Response
{
  "wallets": [
    {
      "rank": 1,
      "address": "7xKp...",
      "chain": "solana",
      "score": 94.2,
      "decayState": "sharp",
      "labels": ["smart_money", "whale"]
    }
  ],
  "total": 1250
}
GET /api/v1/wallets/search

Search wallets by address prefix or label.

Query Parameters
q string Search query (address prefix or label) required
chain string Filter by chain
limit number Max results (default: 20)

Cohorts & Analytics

GET /api/v1/cohorts

Get cohort retention metrics.

Query Parameters
chain string Filter by chain
months number Number of months to include (default: 12)
Response
{
  "cohorts": [
    {
      "month": "2025-01",
      "totalFlagged": 156,
      "sharpCount": 89,
      "fadingCount": 45,
      "deadCount": 22,
      "retentionRate": 57.1
    }
  ]
}
GET /api/v1/cohorts/{month}

Get detailed cohort data with decay curve.

Path Parameters
month string Cohort month (YYYY-MM format) required

Webhooks

POST /api/v1/webhooks

Subscribe to webhook events.

Request Body
{
  "url": "https://your-server.com/webhook",
  "events": [
    "wallet.rating.updated",
    "wallet.decay_state.changed",
    "alert.copy_detected"
  ],
  "secret": "your_webhook_secret"
}
Available Events
  • wallet.rating.updated - Score recalculated
  • wallet.decay_state.changed - sharp → fading → dead
  • cohort.snapshot.created - New cohort data
  • alert.wallet.flagged - New wallet flagged
  • alert.copy_detected - Copy trading detected
GET /api/v1/webhooks

List your webhook subscriptions.

DELETE /api/v1/webhooks/{id}

Unsubscribe from a webhook.

Exports

POST /api/v1/export/url

Generate signed export URLs for data downloads.

Request Body
{
  "type": "transactions" | "tax_report" | "positions",
  "format": "csv" | "json" | "pdf",
  "walletAddress": "7xKp...",
  "chain": "solana",
  "dateRange": {
    "start": "2025-01-01",
    "end": "2025-12-31"
  }
}

Internal Endpoints

Parser

POST /api/wallets/:address/parse

Parse transactions for a wallet address. Fetches from chain RPC, parses swaps, and calculates cost basis.

Path Parameters
address string Wallet address required
Query Parameters
chain string Chain: solana | base | hyperliquid (default: solana)
limit number Max transactions to fetch (default: 1000, max: 10000)
archive boolean Archive raw transactions to S3 (default: true)
Response
{
  "success": true,
  "address": "7xKp...",
  "chain": "solana",
  "stats": {
    "transactionsFetched": 500,
    "swapsParsed": 234,
    "failuresSkipped": 266,
    "parseRate": "46.8%",
    "durationMs": 15234,
    "pricingSource": "jupiter"
  },
  "costBasis": {
    "realisedPnl": 1520.50,
    "lotsCreated": 89,
    "positionsCount": 12
  },
  "swaps": [...],
  "venueBreakdown": { "jupiter": 145, "raydium_amm": 67 },
  "botBreakdown": { "photon": 45, "direct": 189 }
}

Dashboard

GET /api/dashboard/wallets

List all connected wallets with summary stats.

Response
{
  "success": true,
  "wallets": [
    {
      "id": "uuid",
      "address": "7xKp...",
      "label": "Main Trading",
      "chain": "solana",
      "syncState": "live",
      "lastSync": "2026-06-04T12:00:00Z",
      "transactionCount": 272,
      "totalValue": 32529.98,
      "isActive": true
    }
  ]
}
POST /api/dashboard/wallets

Add a new wallet to track.

Request Body
address string Wallet address required
label string Display label for the wallet
chain string Chain: solana | base | hyperliquid (default: solana)
GET /api/dashboard/wallets/:address/positions

Get current positions for a specific wallet (spot + perpetual).

Query Parameters
chain string Filter by chain (for perp positions on Hyperliquid)
GET /api/dashboard/tax/:year

Get tax report data for a specific year.

Path Parameters
year number Tax year (2020-2030) required
Response
{
  "success": true,
  "year": 2025,
  "summary": {
    "shortTermGains": 1700,
    "shortTermLosses": 540,
    "longTermGains": 4900,
    "longTermLosses": 0,
    "netShortTerm": 1160,
    "netLongTerm": 4900,
    "totalTaxableGain": 6060
  },
  "taxEvents": [...]
}

Intelligence

POST /api/intelligence/classify

Classify a wallet and generate labels.

Request Body
address string Wallet address required
chain string Chain (default: solana)
GET /api/intelligence/copy-detection

Get copy trading detection alerts.

Query Parameters
address string Filter by wallet address
limit number Max results (default: 50)

Health Check

GET /health

API health check endpoint.

Response
{
  "status": "ok",
  "version": "1.0.0",
  "timestamp": "2026-06-05T12:00:00.000Z",
  "chains": {
    "solana": "ok",
    "base": "ok",
    "hyperliquid": "ok"
  }
}

Webhook Signature Verification

All outgoing webhooks are signed using HMAC-SHA256. Verify the signature using your webhook secret:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
const signature = req.headers['x-webhook-signature'];
if (!verifyWebhook(req.body, signature, WEBHOOK_SECRET)) {
  return res.status(401).send('Invalid signature');
}

Error Responses

{
  "success": false,
  "error": "Error message description",
  "code": "INVALID_ADDRESS"
}

Error Codes

HTTP Code Description
400 INVALID_ADDRESS Invalid wallet address format
400 INVALID_CHAIN Unsupported chain
401 UNAUTHORIZED Missing or invalid API key
404 NOT_FOUND Resource not found
429 RATE_LIMITED Too many requests
500 INTERNAL_ERROR Internal server error