Get Leaderboard
curl --request GET \
--url https://api-mainnet.onzks.com/v1/scores/leaderboard \
--header 'Authorization: Bearer <token>'import requests
url = "https://api-mainnet.onzks.com/v1/scores/leaderboard"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api-mainnet.onzks.com/v1/scores/leaderboard', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-mainnet.onzks.com/v1/scores/leaderboard",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-mainnet.onzks.com/v1/scores/leaderboard"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api-mainnet.onzks.com/v1/scores/leaderboard")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-mainnet.onzks.com/v1/scores/leaderboard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"leaderboard": [
{
"rank": 123,
"zksId": {},
"address": "<string>",
"score": 123,
"percentile": 123,
"tier": "<string>",
"change": 123,
"avatar": "<string>"
}
],
"pagination": {
"total": 123,
"limit": 123,
"offset": 123,
"hasMore": true
}
}Scores
Get Leaderboard
Get top-ranked users by ZKScore
GET
/
v1
/
scores
/
leaderboard
Get Leaderboard
curl --request GET \
--url https://api-mainnet.onzks.com/v1/scores/leaderboard \
--header 'Authorization: Bearer <token>'import requests
url = "https://api-mainnet.onzks.com/v1/scores/leaderboard"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api-mainnet.onzks.com/v1/scores/leaderboard', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-mainnet.onzks.com/v1/scores/leaderboard",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-mainnet.onzks.com/v1/scores/leaderboard"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api-mainnet.onzks.com/v1/scores/leaderboard")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-mainnet.onzks.com/v1/scores/leaderboard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"leaderboard": [
{
"rank": 123,
"zksId": {},
"address": "<string>",
"score": 123,
"percentile": 123,
"tier": "<string>",
"change": 123,
"avatar": "<string>"
}
],
"pagination": {
"total": 123,
"limit": 123,
"offset": 123,
"hasMore": true
}
}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.Use this endpoint to build leaderboards, showcase top users, and create competitive features in your application.
Parameters
number
Number of results to return (default: 50, max: 100)
number
Number of results to skip for pagination (default: 0)
string
Filter by specific scoring category
activity- Transaction activity leadersvolume- Highest volume tradersage- Oldest walletsdiversity- Most diverse usersgovernance- Top DAO participantssocial- Highest social reputationrisk- Best risk managementloyalty- Most loyal userstotal- Overall score (default)
number
Filter by specific chain ID (optional, defaults to all chains)
string
Time period for ranking
24h- Last 24 hours7d- Last 7 days30d- Last 30 days (default)all- All time
Response
boolean
Indicates if the request was successful
array
Array of top-ranked users
Show user properties
Show user properties
number
Current rank position
string | null
User’s ZKS ID (if set)
string
User’s wallet address
number
Total ZKScore or category score
number
Percentile ranking (0-100)
string
Score tier (bronze, silver, gold, platinum, diamond, legendary)
number
Rank change from previous period
string
User’s avatar URL (if available)
object
Examples
curl "https://api.onzks.com/v1/scores/leaderboard?limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"
curl "https://api.onzks.com/v1/scores/leaderboard?category=volume&limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"
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' });
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')
Response Example
{
"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: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: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: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: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: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: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: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: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: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
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
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 - Get user’s ZKScore
- Get Score Breakdown - Detailed breakdown
- Get Score 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
- Cache Results: Leaderboards change slowly, cache for 5+ minutes
- Use Appropriate Limits: Don’t request more data than needed
- Implement Pagination: Load data in chunks for better UX
- 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