A real-time API server that streams live game events from Deadlock matches using Server-Sent Events (SSE). It parses live demo files from ongoing matches and exposes structured entity updates — player stats, NPC health, positions, chat messages, and more — through HTTP endpoints.
You don't need to clone this repository. You only need two files: a .env file and docker-compose.yaml.
- Docker and Docker Compose installed
mkdir deadlock-live-events && cd deadlock-live-eventsecho "DEADLOCK_API_KEY=..." > .envReplace ... with your Deadlock API key. The key is optional — the server works without one, but having a key gives you higher rate limits on the upstream Deadlock API.
services:
api:
image: ghcr.io/deadlock-api/deadlock-live-events:latest
restart: unless-stopped
env_file: .env
ports:
- "3000:3000"Change the left side of the port mapping (e.g. "8080:3000") if port 3000 is already in use.
docker compose up -dThe API is now running at http://localhost:3000.
docker compose logs -fTo stop the server:
docker compose downTo update to the latest version:
docker compose pull && docker compose up -dGET /v1/matches/{match_id}/live/demo/events
Streams real-time game events as Server-Sent Events. The server connects to the live match, waits for the demo to become available (up to ~30 seconds), and then begins streaming parsed events.
| Parameter | Type | Default | Description |
|---|---|---|---|
subscribed_entities |
comma-separated string | all entities | Filter to specific entity types (see list below) |
subscribed_chat_messages |
boolean | false |
Include in-game chat messages |
Stream all events from a match:
curl -N http://localhost:3000/v1/matches/28850808/live/demo/eventsStream only player and team data:
curl -N http://localhost:3000/v1/matches/28850808/live/demo/events?subscribed_entities=player_controller,player_pawn,teamStream events with chat messages:
curl -N http://localhost:3000/v1/matches/28850808/live/demo/events?subscribed_chat_messages=true&subscribed_entities=player_controllerOn connection, the stream sends an initial message event with metadata:
{
"status": "connected",
"message": "Connected to demo event stream.",
"all_event_names": ["game_rules_proxy_entity_created", "..."]
}Each entity type produces three event names:
{entity_type}_entity_created— entity appeared in the game{entity_type}_entity_updated— entity properties changed{entity_type}_entity_deleted— entity was removed
Additional event names:
chat_message— in-game chat (requiressubscribed_chat_messages=true)tick_end— marks the end of a game tickend— the demo stream has ended
Note: Standard
EventSourceonly listens to the defaultmessageevent. Since this API uses named events, you need to add listeners for each event name, or use a library like sse.js that supports named events.
| Entity Type | Description | Key Fields |
|---|---|---|
game_rules_proxy |
Game state and timing | game_start_time, game_paused, total_paused_ticks |
player_controller |
Player stats and info | steam_id, steam_name, hero_id, kills, deaths, assists, net_worth, hero_damage |
player_pawn |
Player character state | position, health, max_health, level, hero_build_id |
team |
Team info | team, score, teamname |
mid_boss |
Mid boss NPC | health, max_health, position, team |
trooper |
Lane trooper | health, max_health, position, lane, team |
trooper_neutral |
Neutral camp trooper | health, max_health, position |
trooper_boss |
Boss trooper | health, max_health, position, lane, team |
trooper_barrack_boss |
Barrack boss | health, max_health, position, lane, team |
shielded_sentry |
Shielded sentry | health, max_health, position, shield_active |
base_defense_sentry |
Base defense sentry | health, max_health, position, team |
boss_tier2 |
Tier 2 boss | health, max_health, position, team |
boss_tier3 |
Tier 3 boss | health, max_health, position, team |
breakable_prop |
Breakable object | position |
breakable_prop_modifier_pickup |
Modifier orb pickup | position, active |
breakable_prop_gold_pickup |
Gold orb pickup | position, active |
punchable_powerup |
Punchable urn/powerup | position |
destroyable_building |
Guardian / Walker / etc. | health, max_health, position, team |
sinners_sacrifice |
Sinners sacrifice objective | health, max_health, position |
ability_melee_parry |
Melee parry event | owner_entity, attack_parried, start_time, success_time |
Player Controller Update:
{
"tick": 5432,
"game_time": 245.6,
"event_type": "entity_update",
"delta": "update",
"entity_index": 3,
"entity_type": "player_controller",
"steam_id": 123456789,
"steam_name": "PlayerOne",
"team": 2,
"hero_id": 15,
"kills": 4,
"deaths": 1,
"assists": 7,
"net_worth": 12350,
"hero_damage": 8420
}NPC Update:
{
"tick": 5433,
"game_time": 245.7,
"event_type": "entity_update",
"delta": "create",
"entity_index": 87,
"entity_type": "trooper",
"health": 275,
"max_health": 275,
"position": [1234.5, -678.9, 128.0],
"lane": 1,
"team": 2
}Chat Message:
{
"tick": 5500,
"game_time": 250.0,
"event_type": "chat_message",
"steam_name": "PlayerOne",
"steam_id": 123456789,
"text": "gg",
"all_chat": true,
"lane_color": 3
}GET /v1/matches/{match_id}/live/demo
Streams the raw demo file bytes as a binary stream. Use this if you want to process the demo file yourself with external tools.
curl -N http://localhost:3000/v1/matches/28850808/live/demo --output match.demconst eventSource = new EventSource(
"http://localhost:3000/v1/matches/28850808/live/demo/events?subscribed_entities=player_controller,team"
);
// Listen for the initial connection message
eventSource.addEventListener("message", (e) => {
console.log("Connected:", JSON.parse(e.data));
});
// Listen for player updates
eventSource.addEventListener("player_controller_entity_updated", (e) => {
const player = JSON.parse(e.data);
console.log(`${player.steam_name}: ${player.kills}/${player.deaths}/${player.assists}`);
});
// Listen for team score changes
eventSource.addEventListener("team_entity_updated", (e) => {
const team = JSON.parse(e.data);
console.log(`Team ${team.teamname}: ${team.score}`);
});
// Listen for stream end
eventSource.addEventListener("end", () => {
console.log("Match stream ended");
eventSource.close();
});
eventSource.onerror = (e) => console.error("SSE error:", e);Requires Rust 1.93+, protobuf-compiler, and libprotobuf-dev.
git clone https://github.com/deadlock-api/deadlock-live-events.git
cd deadlock-live-events
cp .env.example .env
cargo run --releaseOr build with Docker locally:
docker compose up -d --build