


A client companion that posts your personal Valheim stats to an HTTP endpoint - by default the self-hosted gs dashboard, but it'll post to any URL you point it at, so you can ingest your stats however you like.
Pairs with the server-side GsValheimStatsEmitter. Both POST to the same endpoint; the dashboard merges them.
Ingesting the data into your own dashboard? See the Data format & self-hosting wiki for the complete payload schema, key conventions, and merge rules.
Read from your own game (the things only the client knows), accumulated and persisted across sessions:
PlayerProfile stats: kills, deaths, builds, crafts, jumps, distances (walked / run / sailed / air), trees chopped, items picked up, ...Install via r2modman / Thunderstore (depends on denikson-BepInExPack_Valheim).
Launch the game once to generate the config, then edit
BepInEx/config/net.cproudlock.gsvalheimstatsclient.cfg:
[General]
World = vhserver3 # must match the server's world name
EmitIntervalSeconds = 120 # how often it POSTs
[Ingest]
Url = https://gs.proudtech.net/api/valheim/ingest
Token = <bearer token from the dashboard owner>
Restart Valheim. It POSTs every EmitIntervalSeconds (and on death).
Stats are filed under
World, so set it to whatever you want to group your runs by (typically the server's-worldname).
The mod just does an authenticated POST of a JSON snapshot. You don't need the gs dashboard - point Url at anything that speaks HTTP.
Request:
POST <Url>Authorization: Bearer <Token> (omitted if Token is blank)Content-Type: application/jsonPayload (source: "client"):
{
"schemaVersion": 1,
"game": "valheim",
"source": "client",
"reporter": "Erebus",
"world": "vhserver3",
"emittedAtUtc": "2026-06-09T18:00:00Z",
"snapshotIdLocal": "5e2f...",
"players": [{
"name": "Erebus",
"platformId": "-1285113746",
"kills": 312, "bossKills": 1, "deaths": 4,
"longestLifeSec": 7200, "bestKillsBeforeDeath": 150,
"stats": { "vh_EnemyKills": 312, "vh_Builds": 540, "vh_DistanceSail": 4200 },
"skills": [{ "skill": "Knives", "level": 42 }],
"creatureKills": [{ "creature": "$enemy_greydwarf", "kills": 140 }],
"crafts": [{ "item": "$item_club", "count": 3 }],
"materials": [{ "material": "Wood", "amount": 3400 }],
"weapons": [{ "weapon": "Knives", "damageDealt": 493, "kills": 29, "hardestHit": 79, "biggestSwing": 121 }],
"weaponItems": [{ "item": "$item_knife_bronze", "damageDealt": 400, "kills": 25, "hardestHit": 79 }],
"boss": [{ "boss": "Eikthyr", "damageDealt": 1450, "fightSec": 95 }],
"pickups": [{ "item": "$item_trophy_boar", "count": 11 }]
}, {
"name": "Pridetoes",
"weapons": [{ "weapon": "Bows", "damageDealt": 220, "kills": 3, "hardestHit": 60, "biggestSwing": 60 }],
"boss": [{ "boss": "Eikthyr", "damageDealt": 700, "fightSec": 95 }]
}],
"deathEvents": [{
"playerName": "Erebus", "killer": "tree", "biome": "BlackForest",
"posX": 120, "posY": 30, "posZ": -44, "lifeSec": 1800, "killsThisLife": 40,
"tsUtc": "2026-06-09T17:30:00Z"
}],
"bossSelfDamage": [{ "boss": "Fader", "damage": 210 }]
}
Notes:
snapshotIdLocal lets a receiver dedupe.$enemy_*, $item_*); stats keys are vh_<PlayerStatType>.reporter is who captured this payload. Valheim simulates each creature on the nearest client, so in multiplayer a client also records the combat it witnesses for other players on creatures it owns - those appear as extra, combat-only entries in players[] (just weapons/weaponItems/boss). Source-separate weapon/boss rows by reporter and sum across reporters: each hit is seen by exactly one owner, so it's an exact total with no double-count. Fire/poison damage-over-time is credited to whoever applied it.bossSelfDamage is per-boss damage taken with no player attacker (lava, the boss's own fire, fall) - a curiosity stat, not attributed to anyone.Minimal receiver (Node):
import express from 'express';
const app = express();
app.use(express.json({ limit: '1mb' }));
app.post('/api/valheim/ingest', (req, res) => {
if (req.get('authorization') !== 'Bearer my-secret') return res.sendStatus(401);
for (const p of req.body.players ?? [])
console.log(`${p.name}: ${p.kills} kills, ${p.deaths} deaths`);
res.json({ status: 'ok' });
});
app.listen(3001);
Then set Url = http://<host>:3001/api/valheim/ingest and Token = my-secret.
Quick test with curl:
curl -X POST http://localhost:3001/api/valheim/ingest \
-H 'Authorization: Bearer my-secret' -H 'Content-Type: application/json' \
-d '{"game":"valheim","source":"client","world":"test","players":[{"name":"You","kills":1}]}'
Privacy: the mod sends your stats to whatever
Urlyou configure. Only install with aUrl/Tokenyou trust.