> ## Documentation Index
> Fetch the complete documentation index at: https://core.anylayer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Leaderboard

> Get top-ranked users by ZKScore

## Overview

Retrieve the global leaderboard showing top-ranked users by their ZKScore. This endpoint supports filtering by category, chain, and time period, making it ideal for displaying competitive rankings and discovering top performers.

<Tip>
  Use this endpoint to build leaderboards, showcase top users, and create competitive features in your application.
</Tip>

## Parameters

<ParamField query="limit" type="number">
  Number of results to return (default: 50, max: 100)
</ParamField>

<ParamField query="offset" type="number">
  Number of results to skip for pagination (default: 0)
</ParamField>

<ParamField query="category" type="string">
  Filter by specific scoring category

  * `activity` - Transaction activity leaders
  * `volume` - Highest volume traders
  * `age` - Oldest wallets
  * `diversity` - Most diverse users
  * `governance` - Top DAO participants
  * `social` - Highest social reputation
  * `risk` - Best risk management
  * `loyalty` - Most loyal users
  * `total` - Overall score (default)
</ParamField>

<ParamField query="chainId" type="number">
  Filter by specific chain ID (optional, defaults to all chains)
</ParamField>

<ParamField query="timeframe" type="string">
  Time period for ranking

  * `24h` - Last 24 hours
  * `7d` - Last 7 days
  * `30d` - Last 30 days (default)
  * `all` - All time
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  Indicates if the request was successful
</ResponseField>

<ResponseField name="leaderboard" type="array">
  Array of top-ranked users

  <Expandable title="user properties">
    <ResponseField name="rank" type="number">
      Current rank position
    </ResponseField>

    <ResponseField name="zksId" type="string | null">
      User's ZKS ID (if set)
    </ResponseField>

    <ResponseField name="address" type="string">
      User's wallet address
    </ResponseField>

    <ResponseField name="score" type="number">
      Total ZKScore or category score
    </ResponseField>

    <ResponseField name="percentile" type="number">
      Percentile ranking (0-100)
    </ResponseField>

    <ResponseField name="tier" type="string">
      Score tier (bronze, silver, gold, platinum, diamond, legendary)
    </ResponseField>

    <ResponseField name="change" type="number">
      Rank change from previous period
    </ResponseField>

    <ResponseField name="avatar" type="string">
      User's avatar URL (if available)
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="pagination" type="object">
  <Expandable title="properties">
    <ResponseField name="total" type="number">
      Total number of users in leaderboard
    </ResponseField>

    <ResponseField name="limit" type="number">
      Number of results returned
    </ResponseField>

    <ResponseField name="offset" type="number">
      Current offset
    </ResponseField>

    <ResponseField name="hasMore" type="boolean">
      Whether more results are available
    </ResponseField>
  </Expandable>
</ResponseField>

## Examples

<CodeGroup>
  ```bash cURL (Top 10) theme={null}
  curl "https://api.onzks.com/v1/scores/leaderboard?limit=10" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```bash cURL (Volume Leaders) theme={null}
  curl "https://api.onzks.com/v1/scores/leaderboard?category=volume&limit=20" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  async function getLeaderboard(options = {}) {
    const {
      limit = 50,
      offset = 0,
      category = 'total',
      chainId,
      timeframe = '30d'
    } = options;

    const params = new URLSearchParams({
      limit: limit.toString(),
      offset: offset.toString(),
      category,
      timeframe
    });

    if (chainId) {
      params.append('chainId', chainId.toString());
    }

    const response = await fetch(
      `https://api.onzks.com/v1/scores/leaderboard?${params}`,
      {
        headers: {
          'Authorization': 'Bearer YOUR_API_KEY'
        }
      }
    );

    const data = await response.json();

    // Display leaderboard
    console.log(`Top ${category} Leaders:`);
    data.leaderboard.forEach((user, index) => {
      const change = user.change > 0 ? `↑${user.change}` :
                     user.change < 0 ? `↓${Math.abs(user.change)}` : '→';
      console.log(`${user.rank}. ${user.zksId || user.address.slice(0, 8)} - ${user.score} ${change}`);
    });

    return data;
  }

  // Usage
  await getLeaderboard({ limit: 10, category: 'total' });
  ```

  ```python Python theme={null}
  import requests

  def get_leaderboard(limit=50, offset=0, category='total', chain_id=None, timeframe='30d'):
      params = {
          'limit': limit,
          'offset': offset,
          'category': category,
          'timeframe': timeframe
      }
      
      if chain_id:
          params['chainId'] = chain_id
      
      response = requests.get(
          'https://api.onzks.com/v1/scores/leaderboard',
          headers={'Authorization': 'Bearer YOUR_API_KEY'},
          params=params
      )
      
      data = response.json()
      
      # Display leaderboard
      print(f"Top {category} Leaders:")
      for user in data['leaderboard']:
          change = f"↑{user['change']}" if user['change'] > 0 else \
                   f"↓{abs(user['change'])}" if user['change'] < 0 else '→'
          zks_id = user['zksId'] or user['address'][:8]
          print(f"{user['rank']}. {zks_id} - {user['score']} {change}")
      
      return data

  # Usage
  get_leaderboard(limit=10, category='total')
  ```
</CodeGroup>

## Response Example

```json theme={null}
{
  "success": true,
  "category": "total",
  "timeframe": "30d",
  "leaderboard": [
    {
      "rank": 1,
      "zksId": "alice",
      "address": "0x742d35cc6635c0532925a3b844d1ff4e1321",
      "score": 9847,
      "percentile": 99.9,
      "tier": "legendary",
      "change": 0,
      "avatar": "https://cdn.onzks.com/avatars/alice.png"
    },
    {
      "rank": 2,
      "zksId": "bob",
      "address": "0x1234567890123456789012345678901234567890",
      "score": 9756,
      "percentile": 99.8,
      "tier": "legendary",
      "change": 1,
      "avatar": null
    },
    {
      "rank": 3,
      "zksId": "charlie",
      "address": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
      "score": 9623,
      "percentile": 99.7,
      "tier": "legendary",
      "change": -1,
      "avatar": "https://cdn.onzks.com/avatars/charlie.png"
    }
    // ... more users
  ],
  "pagination": {
    "total": 125847,
    "limit": 50,
    "offset": 0,
    "hasMore": true
  }
}
```

## Use Cases

### 1. Display Top Users

Show top 10 users in your app:

```javascript theme={null}
async function displayTopUsers() {
  const { leaderboard } = await getLeaderboard({ limit: 10 });

  const html = leaderboard.map((user, index) => `
    <div class="leaderboard-item">
      <span class="rank">#${user.rank}</span>
      <img src="${user.avatar || '/default-avatar.png'}" alt="${user.zksId}">
      <span class="name">${user.zksId || user.address.slice(0, 8)}</span>
      <span class="score">${user.score.toLocaleString()}</span>
      <span class="tier ${user.tier}">${user.tier}</span>
    </div>
  `).join('');

  document.getElementById('leaderboard').innerHTML = html;
}
```

### 2. Category-Specific Leaderboards

Show leaders in different categories:

```javascript theme={null}
async function displayCategoryLeaders() {
  const categories = ['volume', 'governance', 'social', 'loyalty'];
  
  for (const category of categories) {
    const { leaderboard } = await getLeaderboard({
      category,
      limit: 5
    });

    console.log(`\nTop ${category} leaders:`);
    leaderboard.forEach(user => {
      console.log(`  ${user.rank}. ${user.zksId} - ${user.score}`);
    });
  }
}
```

### 3. Paginated Leaderboard

Implement pagination for large leaderboards:

```javascript theme={null}
async function getPaginatedLeaderboard(page = 1, pageSize = 50) {
  const offset = (page - 1) * pageSize;
  
  const data = await getLeaderboard({
    limit: pageSize,
    offset
  });

  return {
    users: data.leaderboard,
    currentPage: page,
    totalPages: Math.ceil(data.pagination.total / pageSize),
    hasNext: data.pagination.hasMore,
    hasPrevious: page > 1
  };
}

// Usage
const page1 = await getPaginatedLeaderboard(1, 50);
const page2 = await getPaginatedLeaderboard(2, 50);
```

### 4. User Rank Lookup

Find a specific user's position:

```javascript theme={null}
async function findUserRank(targetIdentity) {
  let offset = 0;
  const limit = 100;
  let found = false;

  while (!found) {
    const { leaderboard, pagination } = await getLeaderboard({
      limit,
      offset
    });

    const user = leaderboard.find(u => 
      u.zksId === targetIdentity || u.address === targetIdentity
    );

    if (user) {
      console.log(`${targetIdentity} is ranked #${user.rank}`);
      console.log(`Score: ${user.score}`);
      console.log(`Tier: ${user.tier}`);
      return user;
    }

    if (!pagination.hasMore) break;
    offset += limit;
  }

  console.log('User not found in leaderboard');
  return null;
}
```

### 5. Trending Users

Track users with biggest rank improvements:

```javascript theme={null}
async function getTrendingUsers() {
  const { leaderboard } = await getLeaderboard({
    limit: 100,
    timeframe: '7d'
  });

  // Sort by rank improvement
  const trending = leaderboard
    .filter(user => user.change > 0)
    .sort((a, b) => b.change - a.change)
    .slice(0, 10);

  console.log('Trending Users (Biggest Climbers):');
  trending.forEach(user => {
    console.log(`  ${user.zksId} - Up ${user.change} ranks to #${user.rank}`);
  });

  return trending;
}
```

## Best Practices

### 1. Cache Leaderboard Data

Leaderboards don't change frequently:

```javascript theme={null}
const leaderboardCache = new Map();

async function getCachedLeaderboard(options) {
  const cacheKey = JSON.stringify(options);
  
  if (leaderboardCache.has(cacheKey)) {
    const cached = leaderboardCache.get(cacheKey);
    // Cache for 5 minutes
    if (Date.now() - cached.timestamp < 5 * 60 * 1000) {
      return cached.data;
    }
  }

  const data = await getLeaderboard(options);
  leaderboardCache.set(cacheKey, {
    data,
    timestamp: Date.now()
  });

  return data;
}
```

### 2. Implement Infinite Scroll

Load more users as user scrolls:

```javascript theme={null}
class LeaderboardScroller {
  constructor() {
    this.users = [];
    this.offset = 0;
    this.limit = 50;
    this.loading = false;
    this.hasMore = true;
  }

  async loadMore() {
    if (this.loading || !this.hasMore) return;

    this.loading = true;

    try {
      const { leaderboard, pagination } = await getLeaderboard({
        limit: this.limit,
        offset: this.offset
      });

      this.users.push(...leaderboard);
      this.offset += this.limit;
      this.hasMore = pagination.hasMore;

      return leaderboard;
    } finally {
      this.loading = false;
    }
  }

  reset() {
    this.users = [];
    this.offset = 0;
    this.hasMore = true;
  }
}

// Usage
const scroller = new LeaderboardScroller();
await scroller.loadMore(); // Load first page
await scroller.loadMore(); // Load second page
```

### 3. Highlight Current User

Show user's position in leaderboard:

```javascript theme={null}
async function getLeaderboardWithUser(currentIdentity) {
  const { leaderboard } = await getLeaderboard({ limit: 50 });

  // Check if current user is in top 50
  const userIndex = leaderboard.findIndex(u => 
    u.zksId === currentIdentity || u.address === currentIdentity
  );

  if (userIndex >= 0) {
    return {
      leaderboard,
      currentUserIndex: userIndex,
      currentUserRank: leaderboard[userIndex].rank
    };
  }

  // If not in top 50, fetch user's actual rank
  const userScore = await getScore(currentIdentity);
  
  return {
    leaderboard,
    currentUserIndex: -1,
    currentUserRank: userScore.rank
  };
}
```

### 4. Real-time Updates

Poll for leaderboard updates:

```javascript theme={null}
class LiveLeaderboard {
  constructor(options = {}) {
    this.options = options;
    this.interval = null;
    this.updateCallback = null;
  }

  start(callback, pollInterval = 30000) {
    this.updateCallback = callback;
    
    // Initial load
    this.update();

    // Poll for updates
    this.interval = setInterval(() => {
      this.update();
    }, pollInterval);
  }

  async update() {
    try {
      const data = await getLeaderboard(this.options);
      if (this.updateCallback) {
        this.updateCallback(data);
      }
    } catch (error) {
      console.error('Failed to update leaderboard:', error);
    }
  }

  stop() {
    if (this.interval) {
      clearInterval(this.interval);
      this.interval = null;
    }
  }
}

// Usage
const liveBoard = new LiveLeaderboard({ limit: 10 });
liveBoard.start((data) => {
  console.log('Leaderboard updated:', data.leaderboard);
}, 30000); // Update every 30 seconds
```

## Visualization Examples

### Leaderboard Table

```javascript theme={null}
function renderLeaderboardTable(leaderboard) {
  return `
    <table class="leaderboard-table">
      <thead>
        <tr>
          <th>Rank</th>
          <th>User</th>
          <th>Score</th>
          <th>Tier</th>
          <th>Change</th>
        </tr>
      </thead>
      <tbody>
        ${leaderboard.map(user => `
          <tr class="${user.rank <= 3 ? 'top-three' : ''}">
            <td class="rank">#${user.rank}</td>
            <td class="user">
              <img src="${user.avatar || '/default.png'}" alt="${user.zksId}">
              <span>${user.zksId || user.address.slice(0, 8)}</span>
            </td>
            <td class="score">${user.score.toLocaleString()}</td>
            <td class="tier ${user.tier}">${user.tier}</td>
            <td class="change ${user.change > 0 ? 'up' : user.change < 0 ? 'down' : 'same'}">
              ${user.change > 0 ? '↑' : user.change < 0 ? '↓' : '→'} ${Math.abs(user.change)}
            </td>
          </tr>
        `).join('')}
      </tbody>
    </table>
  `;
}
```

### Podium Display

```javascript theme={null}
function renderPodium(leaderboard) {
  const [first, second, third] = leaderboard.slice(0, 3);

  return `
    <div class="podium">
      <div class="position second">
        <div class="user">${second.zksId}</div>
        <div class="score">${second.score}</div>
        <div class="rank">2</div>
      </div>
      <div class="position first">
        <div class="user">${first.zksId}</div>
        <div class="score">${first.score}</div>
        <div class="rank">1</div>
      </div>
      <div class="position third">
        <div class="user">${third.zksId}</div>
        <div class="score">${third.score}</div>
        <div class="rank">3</div>
      </div>
    </div>
  `;
}
```

## Related Endpoints

* [Get Score](/api-reference/scores/get-score) - Get user's ZKScore
* [Get Score Breakdown](/api-reference/scores/get-breakdown) - Detailed breakdown
* [Get Score History](/api-reference/scores/get-history) - Historical data

## Troubleshooting

### "Invalid category"

**Cause**: Unsupported category value.

**Solution**:

* Use supported categories: `activity`, `volume`, `age`, `diversity`, `governance`, `social`, `risk`, `loyalty`, `total`
* Check for typos

### "Limit exceeds maximum"

**Cause**: Requested limit is too high.

**Solution**:

* Maximum limit is 100
* Use pagination for larger datasets
* Request multiple pages if needed

### "Leaderboard temporarily unavailable"

**Cause**: Leaderboard is being recalculated.

**Solution**:

* Wait a few minutes and try again
* Leaderboards are recalculated periodically
* Use cached data if available

## Performance Tips

1. **Cache Results**: Leaderboards change slowly, cache for 5+ minutes
2. **Use Appropriate Limits**: Don't request more data than needed
3. **Implement Pagination**: Load data in chunks for better UX
4. **Debounce Updates**: Don't poll too frequently (30s minimum)

## Rate Limits

Leaderboard requests are subject to rate limits:

* **Free tier**: 30 requests per minute
* **Starter tier**: 150 requests per minute
* **Professional tier**: 600 requests per minute
* **Enterprise tier**: Custom limits

Implement caching to stay within limits.
