# WorldScore API Documentation

> **AI-Optimized Reference** — This document is designed for AI coding assistants and LLMs.
> Share this URL or file with your AI to enable it to build integrations with WorldScore API.

---

## Overview

WorldScore is the only reputation scoring API in the World (formerly Worldcoin) ecosystem.
It provides reputation scores (300–999) for 65,000+ verified unique humans based on multiple
identity verification credentials.

- **Base URL**: `https://developer.worldscore.world/api/v1`
- **Format**: JSON
- **Auth**: API key via `X-API-Key` header
- **CORS**: Open (any origin allowed)
- **Rate Limit**: 100 requests/minute per API key (free tier)

---

## Authentication

Include your API key in the `X-API-Key` header. Get a free key at [developer.worldscore.world/register](https://developer.worldscore.world/register).

```bash
curl -H "X-API-Key: ws_live_your_api_key_here" \
  https://developer.worldscore.world/api/v1/score?wallet=0x...
```

Alternatively, pass as query parameter: `?api_key=ws_live_...` (less secure).

---

## Endpoints

### 1. Score Lookup

**`GET /api/v1/score?wallet={address}`**

Returns complete reputation data for a wallet address.

**Headers:**
| Header | Required | Description |
|--------|----------|-------------|
| `X-API-Key` | Yes | Your API key |

**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `wallet` | string | Yes | User's World wallet address (0x...) |

**Response — User Found (200):**
```json
{
  "success": true,
  "data": {
    "walletAddress": "0x1234...abcd",
    "username": "alice_world",
    "totalScore": 750,
    "isVerified": true,
    "verificationLevel": "high",
    "country": {
      "code": "US",
      "name": "United States"
    },
    "credentials": {
      "orbVerified": true,
      "emailVerified": true,
      "phoneVerified": true,
      "externalWallet": {
        "connected": true,
        "points": 100
      },
      "babTokenHolder": false,
      "twitterVerified": true,
      "walletAnalysis": {
        "completed": true,
        "points": 60
      },
      "referrals": {
        "count": 3,
        "points": 50
      }
    },
    "scoreBreakdown": {
      "orbVerification": 150,
      "externalWallet": 100,
      "emailVerification": 40,
      "phoneVerification": 60,
      "twitterVerification": 50,
      "babToken": 0,
      "walletAnalysis": 60,
      "referrals": 50
    },
    "memberSince": "2026-01-15T10:30:00.000Z",
    "lastActive": "2026-07-29T14:22:00.000Z"
  }
}
```

**`country` field:**
- Returns `{ "code": "IN", "name": "India" }` — ISO 3166-1 alpha-2 code and full country name
- Derived from the user's **verified phone number** country calling code (e.g., +91 → India)
- Returns `null` if the user has not verified a phone number
- This is a **verified country** — it cannot be spoofed via VPN since it comes from OTP-verified phone numbers

**Response — User Not Found (200):**
```json
{
  "success": true,
  "data": null,
  "message": "User not found in WorldScore database"
}
```

> **Important:** `data: null` is NOT an error. It means the user hasn't registered on WorldScore yet.
> You should prompt the user to verify themselves on the WorldScore app (see [Deep Links](#worldscore-deep-links)).

---

### 2. Quick Verify

**`GET /api/v1/verify?wallet={address}`**

Lightweight endpoint for simple pass/fail verification checks.

**Response — User Found:**
```json
{
  "verified": true,
  "score": 750,
  "level": "high",
  "username": "alice_world"
}
```

**Response — User Not Found:**
```json
{
  "verified": false,
  "score": null,
  "level": null,
  "username": null
}
```

---

### 3. Public Stats

**`GET /api/v1/stats`**

Aggregate statistics about the WorldScore network. **No API key required.**

```json
{
  "success": true,
  "data": {
    "totalUsers": 65471,
    "averageScore": 520,
    "verifications": {
      "emails": 48230,
      "phones": 31540,
      "babTokens": 8920
    },
    "scoreRange": { "min": 300, "max": 999 },
    "lastUpdated": "2026-07-30T07:00:00.000Z"
  }
}
```

---

## Score System

### Score Range
- **Minimum**: 300 (base score for all registered users)
- **Maximum**: 999

### Score Breakdown
| Credential | Max Points | Details |
|------------|-----------|---------|
| ORB Verification | 150 | Verified via World Orb iris scan |
| External Wallet | 100 | $0 = 10pts, $1-$50 = 50pts, $50+ = 100pts |
| BAB Token | 100 | Holds Binance BAB token |
| Wallet Analysis | 90 | 0 WLD = 10pts, 1-20 WLD = 30pts, 21-50 WLD = 60pts, 50+ WLD = 90pts |
| Phone Verification | 60 | Mobile number verified with OTP |
| X/Twitter | 50 | X/Twitter account connected |
| Email Verification | 40 | Email address verified |
| Referrals | 100 | 1st = 10pts, 2nd = 15pts, 3rd = 25pts, 4th = 50pts |

### Verification Levels
| Level | Score Range | Recommended Use |
|-------|-----------|-----------------|
| `low` | 300 – 499 | Basic access, limited features |
| `medium` | 500 – 699 | Standard access, moderate trust |
| `high` | 700 – 999 | Full access, premium features, zero-collateral |

---

## Handling "User Not Found"

When `data` is `null`, the user hasn't registered on WorldScore. Redirect them to the WorldScore app:

```javascript
const response = await fetch(
  `https://developer.worldscore.world/api/v1/score?wallet=${userWallet}`,
  { headers: { "X-API-Key": "ws_live_your_key" } }
);
const { data } = await response.json();

if (data === null) {
  // User not found — redirect to WorldScore app
  window.location.href =
    "https://world.org/mini-app?app_id=app_fa8974b2c77a879724c770556d4a9451";
} else {
  // Use the score
  console.log("Score:", data.totalScore);
  console.log("Level:", data.verificationLevel);
}
```

---

## WorldScore Deep Links

Use these links to redirect users to the WorldScore mini app inside the World App:

| Type | URL | When to Use |
|------|-----|-------------|
| **Universal Link** (recommended) | `https://world.org/mini-app?app_id=app_fa8974b2c77a879724c770556d4a9451` | Works everywhere — browsers, web apps, mobile |
| **Deep Link** | `worldapp://mini-app?app_id=app_fa8974b2c77a879724c770556d4a9451` | Inside World mini apps only |

### HTML Example
```html
<a href="https://world.org/mini-app?app_id=app_fa8974b2c77a879724c770556d4a9451">
  Verify on WorldScore →
</a>
```

---

## CORS

The API supports **CORS from any origin**. You can call it directly from:
- Browser-based web apps
- Mobile WebViews
- Server-side applications
- cURL, Postman, etc.

No proxy or backend wrapper needed.

---

## Rate Limiting

| Header | Description |
|--------|-------------|
| `X-RateLimit-Limit` | Maximum requests per window (100) |
| `X-RateLimit-Remaining` | Requests remaining in current window |
| `X-RateLimit-Reset` | ISO timestamp when the window resets |

When rate limited, the API returns `429` with a `retryAfter` timestamp.

---

## Error Handling

| Status | Meaning | Action |
|--------|---------|--------|
| `200` | Success | Check `data` field — may be `null` if user not found |
| `400` | Bad Request | Missing `wallet` parameter |
| `401` | Unauthorized | Invalid or missing API key |
| `429` | Rate Limited | Wait until `X-RateLimit-Reset`, then retry |
| `500` | Server Error | Retry after a few seconds |

**Error response format:**
```json
{
  "error": "API key required",
  "message": "Include your API key in the X-API-Key header or as api_key query parameter"
}
```

---

## Complete Code Examples

### JavaScript / Node.js
```javascript
async function getWorldScore(walletAddress, apiKey) {
  const response = await fetch(
    `https://developer.worldscore.world/api/v1/score?wallet=${walletAddress}`,
    { headers: { "X-API-Key": apiKey } }
  );

  if (response.status === 429) {
    const retryAfter = response.headers.get("X-RateLimit-Reset");
    throw new Error(`Rate limited, retry after: ${retryAfter}`);
  }

  if (!response.ok) {
    const err = await response.json();
    throw new Error(err.error || "API request failed");
  }

  const { data } = await response.json();

  if (data === null) {
    return {
      found: false,
      verifyUrl: "https://world.org/mini-app?app_id=app_fa8974b2c77a879724c770556d4a9451"
    };
  }

  return {
    found: true,
    score: data.totalScore,
    level: data.verificationLevel,
    orbVerified: data.credentials.orbVerified,
    breakdown: data.scoreBreakdown,
  };
}
```

### Python
```python
import requests

def get_worldscore(wallet_address, api_key):
    response = requests.get(
        "https://developer.worldscore.world/api/v1/score",
        params={"wallet": wallet_address},
        headers={"X-API-Key": api_key}
    )

    if response.status_code == 429:
        raise Exception("Rate limited")

    response.raise_for_status()
    result = response.json()
    data = result.get("data")

    if data is None:
        return {"found": False}

    return {
        "found": True,
        "score": data["totalScore"],
        "level": data["verificationLevel"],
        "orb_verified": data["credentials"]["orbVerified"],
    }
```

### cURL
```bash
# Full score lookup
curl -X GET \
  "https://developer.worldscore.world/api/v1/score?wallet=0x1234...abcd" \
  -H "X-API-Key: ws_live_your_api_key_here"

# Quick verify
curl -X GET \
  "https://developer.worldscore.world/api/v1/verify?wallet=0x1234...abcd" \
  -H "X-API-Key: ws_live_your_api_key_here"

# Public stats (no key needed)
curl https://developer.worldscore.world/api/v1/stats
```

---

## Pricing

| Plan | Cost | Data Included |
|------|------|---------------|
| **Free** | $0 forever | Score, level, credential status (anonymized), username, wallet |
| **Pro** (coming soon) | $0.05/call | Everything in Free + unmasked phone, email, wallet addresses, BAB wallet, X/Twitter profile |
| **Enterprise** | Custom | Bulk data export, all users, dedicated endpoints. Contact support@worldscore.world |

---

## Support

- **Email**: support@worldscore.world
- **Portal**: [developer.worldscore.world](https://developer.worldscore.world)
- **WorldScore App**: [Open in World App](https://world.org/mini-app?app_id=app_fa8974b2c77a879724c770556d4a9451)
