Scores & Leaderboard
The contributor scoring API exposes leaderboard data and individual user scores. Scores are computed by the civicpulse-score-v1 algorithm and reflect a contributor's civic activity over time.
See Scoring Algorithm for the full weight table, tier definitions, and badge criteria.
GET /api/scoresβ
Retrieve the contributor leaderboard. No authentication required.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
city | string | β | Filter leaderboard to contributors from a specific city |
limit | number | 50 | Number of entries to return (1β100) |
format | string | full | full or minimal β minimal omits breakdown and badges |
Request:
GET /api/scores?city=bangalore&limit=10
Response β 200 OK (full format):
{
"success": true,
"data": {
"count": 10,
"algorithm": "civicpulse-score-v1",
"generatedAt": "2025-01-15T10:30:00.000Z",
"items": [
{
"rank": 1,
"userId": "usr_01HN...",
"displayName": "Priya S.",
"avatarUrl": "https://lh3.googleusercontent.com/...",
"city": "bangalore",
"score": 1240,
"tier": "Legend",
"badges": ["Pioneer", "Community Voice", "Problem Solver", "Legend"],
"breakdown": {
"reportsField": 8,
"reportsResolved": 4,
"upvotesReceived": 210,
"imagesAdded": 12,
"videosAdded": 3,
"highPriorityBonus": 5,
"votesCast": 89,
"commentsMade": 44,
"eventsJoined": 6,
"streakBonus": 2,
"categoryBonus": 4
}
}
]
}
}
Request with minimal format:
GET /api/scores?limit=100&format=minimal
Response β 200 OK (minimal format):
{
"success": true,
"data": {
"count": 100,
"algorithm": "civicpulse-score-v1",
"generatedAt": "2025-01-15T10:30:00.000Z",
"items": [
{
"rank": 1,
"userId": "usr_01HN...",
"displayName": "Priya S.",
"city": "bangalore",
"score": 1240,
"tier": "Legend"
}
]
}
}
The minimal format is suitable for rendering compact leaderboard tables or maps without the overhead of badge and breakdown data.
GET /api/scores/:userIdβ
Retrieve the score and rank for a specific user. No authentication required.
Request:
GET /api/scores/usr_01HN...
Response β 200 OK:
{
"success": true,
"data": {
"userId": "usr_01HN...",
"displayName": "Priya S.",
"avatarUrl": "https://lh3.googleusercontent.com/...",
"city": "bangalore",
"score": 1240,
"tier": "Legend",
"rank": 1,
"globalRank": 7,
"badges": [
{
"id": "Pioneer",
"displayName": "Pioneer",
"description": "One of the first 100 reporters on the platform",
"earnedAt": "2025-01-01T00:00:00.000Z"
},
{
"id": "Community Voice",
"displayName": "Community Voice",
"description": "Received 100+ upvotes across all reported issues",
"earnedAt": "2025-01-10T14:22:00.000Z"
},
{
"id": "Problem Solver",
"displayName": "Problem Solver",
"description": "Had 5+ reported issues resolved",
"earnedAt": "2025-01-12T09:00:00.000Z"
},
{
"id": "Legend",
"displayName": "Legend",
"description": "Reached Legend tier (600+ points)",
"earnedAt": "2025-01-14T17:45:00.000Z"
}
],
"breakdown": {
"reportsField": 8,
"reportsResolved": 4,
"upvotesReceived": 210,
"imagesAdded": 12,
"videosAdded": 3,
"highPriorityBonus": 5,
"votesCast": 89,
"commentsMade": 44,
"eventsJoined": 6,
"streakBonus": 2,
"categoryBonus": 4
},
"algorithm": "civicpulse-score-v1",
"shareUrl": "https://civicpulse.in/contributors/usr_01HN...",
"computedAt": "2025-01-15T10:30:00.000Z"
}
}
Response β 404 (user not found or no activity):
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "User not found or has no contribution score"
}
}
Score Payload Field Referenceβ
| Field | Type | Description |
|---|---|---|
userId | string | Unique user identifier |
displayName | string | User's display name |
city | string | User's registered city |
score | number | Total computed score |
tier | string | Citizen, Contributor, Champion, or Legend |
rank | number | Rank within the user's city |
globalRank | number | Rank across all cities (only in /api/scores/:userId) |
badges | array | List of earned badge objects |
breakdown | object | Per-action point breakdown (omitted in minimal format) |
algorithm | string | Score algorithm version β currently civicpulse-score-v1 |
shareUrl | string | Public shareable URL for this contributor's profile |
computedAt | string | ISO 8601 timestamp when the score was last computed |
Breakdown Fieldsβ
The breakdown object maps each scoring action to the number of times it has been credited (not the point value β multiply by the weight to get the contribution):
| Field | Weight per occurrence | Description |
|---|---|---|
reportsField | 50 pts | Issues reported |
reportsResolved | 100 pts | Reported issues that reached resolved status |
upvotesReceived | 2 pts | Upvotes received on all reported issues |
imagesAdded | 15 pts | Images attached to issues |
videosAdded | 25 pts | Videos attached to issues |
highPriorityBonus | 30 pts | High-priority issues reported |
votesCast | 5 pts | Issues upvoted by this user |
commentsMade | 3 pts | Messages posted in issue threads |
eventsJoined | 20 pts | Community impact events joined |
streakBonus | 50 pts | Weekly activity streaks completed |
categoryBonus | 10 pts | Activity across multiple categories |
See Scoring Algorithm for the complete algorithm specification, tier thresholds, and badge criteria.