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

# Leaderboard Integration

> Create engaging leaderboard systems with real-time rankings and user analytics

## Overview

Build leaderboard systems that drive engagement through:

* Real-time user rankings via loyalty points
* Paginated leaderboard data with cursor-based pagination
* Sprint-specific leaderboards for competitions
* Space-level ranking analytics

## Basic Leaderboard Query

### Get Leaderboard Rankings

```graphql theme={null}
query GetLeaderboard($spaceId: Int!, $cursorAfter: String, $sprintId: Int) {
  space(id: $spaceId) {
    id
    name
    loyaltyPointsRanks(
      cursorAfter: $cursorAfter
      sprintId: $sprintId
    ) {
      totalCount
      pageInfo {
        hasNextPage
        endCursor
      }
      list {
        rank
        points
        address {
          username
          avatar
          address
        }
      }
    }
  }
}
```

**Variables**:

```json theme={null}
{
  "spaceId": 40,
  "cursorAfter": null,
  "sprintId": null
}
```

**Response**:

```json theme={null}
{
  "data": {
    "space": {
      "id": "40",
      "name": "BNB Chain",
      "loyaltyPointsRanks": {
        "totalCount": 1500,
        "pageInfo": {
          "hasNextPage": true,
          "endCursor": "cursor_end"
        },
        "list": [
          {
            "rank": 1,
            "points": 5000,
            "address": {
              "username": "TopPlayer",
              "avatar": "https://example.com/avatar.jpg",
              "address": "0x1234567890123456789012345678901234567890"
            }
          }
        ]
      }
    }
  }
}
```

## Sprint-Specific Leaderboards

### Get Sprint Rankings

When you have a sprint ID, get sprint-specific rankings:

```graphql theme={null}
query GetSprintLeaderboard($spaceId: Int!, $sprintId: Int!, $cursorAfter: String) {
  space(id: $spaceId) {
    loyaltyPointsRanks(
      sprintId: $sprintId
      cursorAfter: $cursorAfter
    ) {
      totalCount
      list {
        rank
        points
        address {
          username
          address
        }
      }
    }
  }
}
```

<Info>
  **Sprint Management**: Sprint information must come from external sources as sprint management is not available through this API.
</Info>

## Pagination Patterns

### Cursor-Based Navigation

```bash theme={null}
# Get first page
curl -X POST https://graphigo-business.prd.galaxy.eco/query \
  -H "Content-Type: application/json" \
  -H "access-token: YOUR_ACCESS_TOKEN" \
  -d '{
    "query": "query GetLeaderboard($spaceId: Int!) { space(id: $spaceId) { loyaltyPointsRanks(cursorAfter: null) { totalCount pageInfo { hasNextPage endCursor } list { rank points address { username } } } } }",
    "variables": { "spaceId": 40 }
  }'
```

### Loading Next Page

1. Use `pageInfo.endCursor` from previous response
2. Set as `cursorAfter` parameter for next request
3. Continue until `pageInfo.hasNextPage` is `false`

## User Position Lookup

<Warning>
  **No Direct User Lookup**: To find a specific user's rank, you must paginate through the leaderboard.
</Warning>

### Search Strategy

Since there's no direct user lookup, implement efficient search:

1. **Limited Search**: Search first 10-20 pages for UI responsiveness
2. **Background Search**: Comprehensive search for analytics
3. **Caching**: Store user positions to reduce future searches

### Basic Implementation

```javascript theme={null}
async function findUserPosition(spaceId, userAddress, accessToken) {
  let cursorAfter = null;
  let pageCount = 0;
  const maxPages = 20; // UI-friendly limit
  
  while (pageCount < maxPages) {
    const response = await fetch('https://graphigo-business.prd.galaxy.eco/query', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'access-token': accessToken
      },
      body: JSON.stringify({
        query: `query GetLeaderboard($spaceId: Int!, $cursorAfter: String) {
          space(id: $spaceId) {
            loyaltyPointsRanks(cursorAfter: $cursorAfter) {
              pageInfo { hasNextPage endCursor }
              list { rank points address { address username } }
            }
          }
        }`,
        variables: { spaceId, cursorAfter }
      })
    });
    
    const data = await response.json();
    const ranks = data.data.space.loyaltyPointsRanks;
    
    // Search current page
    const userRank = ranks.list.find(item => 
      item.address.address.toLowerCase() === userAddress.toLowerCase()
    );
    
    if (userRank) {
      return {
        found: true,
        rank: userRank.rank,
        points: userRank.points,
        username: userRank.address.username,
        searchPages: pageCount + 1
      };
    }
    
    if (!ranks.pageInfo.hasNextPage) break;
    
    cursorAfter = ranks.pageInfo.endCursor;
    pageCount++;
    
    // Rate limiting
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  return { found: false, searchPages: pageCount };
}
```

## Common Integration Patterns

### Top N Rankings

```javascript theme={null}
// Get top 10 users
async function getTopRankings(spaceId, count = 10, accessToken) {
  const response = await fetch('https://graphigo-business.prd.galaxy.eco/query', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'access-token': accessToken
    },
    body: JSON.stringify({
      query: `query GetTopRankings($spaceId: Int!) {
        space(id: $spaceId) {
          name
          loyaltyPointsRanks(cursorAfter: null) {
            totalCount
            list { rank points address { username avatar } }
          }
        }
      }`,
      variables: { spaceId }
    })
  });
  
  const data = await response.json();
  const leaderboard = data.data.space.loyaltyPointsRanks;
  
  return {
    spaceName: data.data.space.name,
    totalParticipants: leaderboard.totalCount,
    topUsers: leaderboard.list.slice(0, count)
  };
}
```

### Sprint vs Overall Comparison

```javascript theme={null}
async function compareRankings(spaceId, sprintId, accessToken) {
  const [overall, sprint] = await Promise.all([
    getTopRankings(spaceId, 10, accessToken),
    getSprintRankings(spaceId, sprintId, 10, accessToken)
  ]);
  
  return {
    overall: overall.topUsers,
    sprint: sprint.topUsers,
    comparison: {
      overallParticipants: overall.totalParticipants,
      sprintParticipants: sprint.totalParticipants,
      participationRate: sprint.totalParticipants / overall.totalParticipants
    }
  };
}

async function getSprintRankings(spaceId, sprintId, count, accessToken) {
  // Similar to getTopRankings but with sprintId parameter
  // Implementation details follow the same pattern
}
```

## Best Practices

1. **Pagination**: Use cursor-based pagination (`cursorAfter`/`cursorBefore`)
2. **Rate Limiting**: Add 200-500ms delays between pagination requests
3. **Caching**: Cache leaderboard data for 30-60 seconds
4. **Search Limits**: Limit user searches to reasonable page counts (20-50 pages)
5. **Error Handling**: Handle cases where users are not found in rankings
6. **Sprint Context**: Use `sprintId` for time-limited competitions

## Pagination Parameters

### Current (Recommended)

* `cursorAfter`: Get items after this cursor
* `cursorBefore`: Get items before this cursor
* `sprintId`: Optional sprint ID for sprint-specific rankings

### Deprecated (Avoid)

* `first`: Use cursor-based pagination instead
* `after`: Use cursor-based pagination instead

## Limitations & Workarounds

### Current Limitations

1. **No Direct User Lookup**: Must search through pages
2. **No Sprint Management**: Sprint IDs from external sources
3. **Search Performance**: Finding users requires multiple API calls

### Recommended Workarounds

1. **Efficient Search**: Limit search scope for UI responsiveness
2. **Caching Strategy**: Cache user positions for short periods
3. **User Communication**: Set expectations about search time
4. **Fallback UI**: Show leaderboard context even when user isn't found

## Usage Examples

### Basic Leaderboard Display

```javascript theme={null}
// Get and display top 10 rankings
const topRankings = await getTopRankings(40, 10, accessToken);

console.log(`${topRankings.spaceName} Leaderboard`);
console.log(`Total participants: ${topRankings.totalParticipants}`);
console.log('\nTop 10:');
topRankings.topUsers.forEach(user => {
  console.log(`${user.rank}. ${user.address.username} - ${user.points} points`);
});
```

### User Position Check

```javascript theme={null}
// Find specific user's position
const userAddress = '0x1234567890123456789012345678901234567890';
const position = await findUserPosition(40, userAddress, accessToken);

if (position.found) {
  console.log(`User rank: #${position.rank}`);
  console.log(`User points: ${position.points}`);
  console.log(`Search completed in ${position.searchPages} pages`);
} else {
  console.log(`User not found in top ${position.searchPages * 50} participants`);
}
```

## Next Steps

* **[Space API Reference](/galxe-integration/api-reference/space)** - Complete API documentation
* **[Loyalty Program Guide](/galxe-integration/guides/loyalty-program)** - Points and rewards system
* **[Authentication](/galxe-integration/getting-started/authentication)** - Access token setup
