Version: 3.8.3
A reusable API for tracked Riot IDs that refreshes stats through Apify, enriches profile data through HenrikDev and Valorant API, stores snapshots on disk, and serves clean cached responses to your app, site, or dashboard.
This project is built for people who want stable stats responses, predictable hosting, and a simple setup without adding a database on day one.
Spider31415#6921Only configured Riot IDs are available. Unknown users return 404 User not tracked.
All http://api.aniketraj.me/custom/valorant/stats routes require X-API-Key.
X-API-Key: your-api-key
API keys are mandatory by design so accidental public deployments do not fail open.
Snapshots refresh every 48h by default.
Built-in scheduler: enabled
Tracker refresh command: npm run refresh:snapshots
Henrik profile/rank refresh command: npm run refresh:profiles
Henrik refreshes are separate so you can update account/card/title/rank data without rerunning the slower tracker.gg scrape.
Endpoint: POST /stats/:username
Sample path: POST /stats/Spider31415%236921
{
"playlist": "competitive",
"modules": {
"profile": {},
"rank": {},
"agents": {},
"maps": { "playlist": "unrated", "limit": 3 },
"totalPlaytime": {}
}
}
Use these as copy-paste starters. Every example below is shown in both cURL and JavaScript.
Profile returns account level, region, player card assets, and title display text.
curl -X POST 'http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-api-key' \
-d '{
"modules": {
"profile": {}
}
}'
const response = await fetch('http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key'
},
body: JSON.stringify({
modules: {
profile: {}
}
})
});
const data = await response.json();
Use the top-level playlist when all requested modules come from the same playlist.
curl -X POST 'http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-api-key' \
-d '{
"playlist": "competitive",
"modules": {
"agents": {}
}
}'
const response = await fetch('http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key'
},
body: JSON.stringify({
playlist: 'competitive',
modules: {
agents: {}
}
})
});
const data = await response.json();
You can request profile, rank, agents, maps, and shared total playtime together.
curl -X POST 'http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-api-key' \
-d '{
"playlist": "competitive",
"modules": {
"profile": {},
"rank": {},
"agents": {},
"maps": {},
"totalPlaytime": {}
}
}'
const response = await fetch('http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key'
},
body: JSON.stringify({
playlist: 'competitive',
modules: {
profile: {},
rank: {},
agents: {},
maps: {},
totalPlaytime: {}
}
})
});
const data = await response.json();
Override the top-level playlist per module when you want to mix competitive and unrated data.
curl -X POST 'http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-api-key' \
-d '{
"playlist": "competitive",
"modules": {
"agents": { "playlist": "unrated" },
"maps": { "playlist": "competitive" },
"totalPlaytime": {}
}
}'
const response = await fetch('http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key'
},
body: JSON.stringify({
playlist: 'competitive',
modules: {
agents: { playlist: 'unrated' },
maps: { playlist: 'competitive' },
totalPlaytime: {}
}
})
});
const data = await response.json();
Limit only affects array modules such as agents and maps.
curl -X POST 'http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-api-key' \
-d '{
"playlist": "competitive",
"modules": {
"agents": { "limit": 3 },
"maps": { "limit": 5 }
}
}'
const response = await fetch('http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key'
},
body: JSON.stringify({
playlist: 'competitive',
modules: {
agents: { limit: 3 },
maps: { limit: 5 }
}
})
});
const data = await response.json();
This keeps the payload shorter when most modules should use unrated data.
curl -X POST 'http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-api-key' \
-d '{
"playlist": "unrated",
"modules": {
"agents": {},
"maps": {},
"rank": {},
"totalPlaytime": {}
}
}'
const response = await fetch('http://api.aniketraj.me/custom/valorant/stats/Spider31415%236921', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key'
},
body: JSON.stringify({
playlist: 'unrated',
modules: {
agents: {},
maps: {},
rank: {},
totalPlaytime: {}
}
})
});
const data = await response.json();
{
"username": "Spider31415#6921",
"playlist": "competitive",
"cachedAt": "2026-05-19T10:00:00.000Z",
"nextRefreshAt": "2026-05-21T10:00:00.000Z",
"status": "ok",
"sources": {
"henrik": {
"status": "ok",
"lastRefreshedAt": "2026-05-20T12:15:00.000Z"
}
},
"data": {
"profile": {
"accountLevel": 514,
"region": "ap",
"card": {
"id": "67d7faa4-4b40-e16a-1c54-0f9b41919849",
"name": "VCT x SEN Card",
"displayIcon": "https://...",
"smallArt": "https://...",
"wideArt": "https://...",
"largeArt": "https://..."
},
"title": {
"id": "ae54c1ce-42b9-3dc1-5e91-6c9e9161b01a",
"name": "Gnarly Title",
"displayText": "Gnarly"
}
},
"agents": [
{
"agent": "Omen",
"role": "Controller",
"timePlayed": "58 hrs",
"matches": "98",
"winRate": "53.1%",
"kd": "1.05",
"adr": "143.3",
"acs": "222.3",
"ddDelta": "+8",
"hsPercent": "17.8%",
"kast": "71.7%",
"icon": "https://...",
"displayIconSmall": "https://...",
"portrait": "https://...",
"portraitV2": "https://...",
"bustPortrait": "https://...",
"killfeedPortrait": "https://...",
"minimapPortrait": "https://...",
"background": "https://...",
"homeScreenPromoTileImage": "https://...",
"roleIcon": "https://...",
"abilityIcons": [{ "slot": "Ability1", "name": "Ability", "icon": "https://..." }]
}
]
}
}
| Status | When it happens |
|---|---|
| 400 | Malformed username encoding, invalid playlist, invalid module name, invalid module config, or invalid limit |
| 401 | Missing or invalid API key |
| 404 | User not tracked, snapshot missing, or requested cached module unavailable |
For this API to track a player, that player’s tracker.gg profile needs to be public. If the profile stays private, the refresh job will not be able to collect the data your API serves.
Based on current Tracker Network support guidance, the most reliable flow is:
If you are using this project, forking it, or want to contribute back, feel free to reach out.