🏷️ Expose your Discord presence and activities to a RESTful API and WebSocket in less than 10 seconds
Lanyard is a service that makes it super easy to export your live Discord presence to an API endpoint (api.lanyard.rest/v1/users/:your_id
) and to a WebSocket (see below) for you to use wherever you want - for example, I use this to display what I'm listening to on Spotify on my personal website. It also acts as a globally-accessible KV store which you can update from the Lanyard Discord bot or from the Lanyard API.
You can use Lanyard's API without deploying anything yourself - but if you want to self host it, you have the option to, though it'll require a tiny bit of configuration.
Just join this Discord server and your presence will start showing up when you GET api.lanyard.rest/v1/users/:your_id
. It's that easy.
- Community Projects
- API Docs
- Socket Docs
- Quicklinks
- Self-host with Docker
- Used By
- Todo
The Lanyard community has worked on some pretty cool projects that allows you to extend the functionality of Lanyard. PR to add a project!
lanyard-profile-readme - Utilize Lanyard to display your Discord Presence in your GitHub Profile
spotsync.me - Stream music from your Discord presence to your friends in realtime through a slick UI
vue-lanyard - Lanyard API plugin for Vue. Supports REST and WebSocket methods
react-use-lanyard - React hook for Lanyard - supports REST & WebSocket
use-lanyard - Another React hook for Lanyard that uses SWR
lanyard-visualizer - Beautifully display your Discord presence on a website
hawser - Lanyard API wrapper for python. Supports both REST and WebSocket.
js-lanyard - Use Lanyard in your Web App.
go-lanyard - Lanyard API wrapper for GoLang - supports REST & WebSocket
use-lanyard - Lanyard with Composition API for Vue. Supports REST and WebSocket methods
use-listen-along - Mock the discord 'Listen Along' feature within a react hook powered by the Lanyard API.
lanyard-graphql - A GraphQL port of the Lanyard API.
svelte-lanyard - A Lanyard API wrapper for Svelte. Supports REST & WebSocket.
denyard - Lanyard API wrapper for Deno - Supports REST & WebSocket.
lanyard-ui - Lanyard visualizer focused on the KV aspect
discord-status-actions - Updates a file to include your discord status using the Lanyard API.
GET https://api.lanyard.rest/v1/users/:user_id
Example response:
{
"success": true,
"data": {
"active_on_discord_mobile": false,
"active_on_discord_desktop": true,
"listening_to_spotify": true,
// Lanyard KV
"kv": {
"location": "Los Angeles, CA"
},
// Below is a custom crafted "spotify" object, which will be null if listening_to_spotify is false
"spotify": {
"track_id": "3kdlVcMVsSkbsUy8eQcBjI",
"timestamps": {
"start": 1615529820677,
"end": 1615530068733
},
"song": "Let Go",
"artist": "Ark Patrol; Veronika Redd",
"album_art_url": "https://i.scdn.co/image/ab67616d0000b27364840995fe43bb2ec73a241d",
"album": "Let Go"
},
"discord_user": {
"username": "Phineas",
"public_flags": 131584,
"id": "94490510688792576",
"discriminator": "0001",
"avatar": "a_7484f82375f47a487f41650f36d30318"
},
"discord_status": "online",
// activities contains the plain Discord activities array that gets sent down with presences
"activities": [
{
"type": 2,
"timestamps": {
"start": 1615529820677,
"end": 1615530068733
},
"sync_id": "3kdlVcMVsSkbsUy8eQcBjI",
"state": "Ark Patrol; Veronika Redd",
"session_id": "140ecdfb976bdbf29d4452d492e551c7",
"party": {
"id": "spotify:94490510688792576"
},
"name": "Spotify",
"id": "spotify:1",
"flags": 48,
"details": "Let Go",
"created_at": 1615529838051,
"assets": {
"large_text": "Let Go",
"large_image": "spotify:ab67616d0000b27364840995fe43bb2ec73a241d"
}
},
{
"type": 0,
"timestamps": {
"start": 1615438153941
},
"state": "Workspace: lanyard",
"name": "Visual Studio Code",
"id": "66b84f5317e9de6c",
"details": "Editing README.md",
"created_at": 1615529838050,
"assets": {
"small_text": "Visual Studio Code",
"small_image": "565945770067623946",
"large_text": "Editing a MARKDOWN file",
"large_image": "565945077491433494"
},
"application_id": 383226320970055681
}
]
}
}
Lanyard KV is a a dynamic, real-time key->value store which is added to the Lanyard user API response. When a KV pair is updated, a PRESENCE_UPDATE for the user will also be emitted through the Lanyard socket.
- Configuration values for your website
- Configuration values for Lanyard 3rd party projects
- Dynamic data for your website/profile (e.g. current location)
- Keys and values can only be strings
- Values can be 30,000 characters maximum
- Keys must be alphanumeric (a-zA-Z0-9) and 255 characters max length
- Your user can have a maximum of 512 key->value pairs linked
DM the Lanyard bot (Lanyard#5766
) with .apikey
to get your API key.
When making Lanyard KV API requests, set an Authorization
header with the API key you received from the Lanyard bot as the value.
.set <key> <value>
PUT https://api.lanyard.rest/v1/users/:user_id/kv/:key
The value will be set to the body of the request. The body can be any type of data, but it will be string-encoded when set in Lanyard KV.
Not yet implemented
PATCH https://api.lanyard.rest/v1/users/:user_id/kv
The user's KV store will be merged with the body of the request. Conflicting keys will be overwritten. The body must be keyvalue pair object with a maximum depth of 1.
.del <key>
DELETE https://api.lanyard.rest/v1/users/:user_id/kv/:key
The websocket is available at wss://api.lanyard.rest/socket
. If you would like to use compression, please specify ?compression=zlib_json
at the end of the URL.
Once connected, you will receive Opcode 1: Hello which will contain heartbeat_interval in the data field. You should set a repeating interval for the time specified in heartbeat_interval which should send Opcode 3: Heartbeat on the interval.
You should send Opcode 2: Initialize
immediately after receiving Opcode 1.
Example of Opcode 2: Initialize
:
{
op: 2,
d: {
// subscribe_to_ids should be an array of user IDs you want to subscribe to presences from
// if Lanyard doesn't monitor an ID specified, it won't be included in INIT_STATE
subscribe_to_ids: ["94490510688792576"]
}
}
To subscribe to multiple presences, send subscribe_to_ids
in the data object with a string[]
list of user IDs to subscribe to. Then, INIT_STATE's data object will contain a user_id->presence map. You can find examples below.
If you just want to subscribe to one user, you can send subscribe_to_id
instead with a string of a single user ID to subscribe to. Then, the INIT_STATE's data will just contain the presence object for the user you've subscribed to instead of a user_id->presence map.
If you want to subscribe to every presence being monitored by Lanyard, you can specify subscribe_to_all
with (bool) true
in the data object, and you will then receive a user_id->presence map with every user presence in INIT_STATE, and their respective PRESENCE_UPDATES when they happen.
Once Op 2 is sent, you should immediately receive an INIT_STATE
event payload if connected successfully. If not, you will be disconnected with an error (see below).
Opcode | Name | Description | Client Send/Recv |
---|---|---|---|
0 | Event | This is the default opcode when receiving core events from Lanyard, like INIT_STATE |
Receive |
1 | Hello | Lanyard sends this when clients initially connect, and it includes the heartbeat interval | Receive Only |
2 | Initialize | This is what the client sends when receiving Opcode 1 from Lanyard - it should contain an array of user IDs to subscribe to | Send only |
3 | Heartbeat | Clients should send Opcode 3 every 30 seconds (or whatever the Hello Opcode says to heartbeat at) | Send only |
Events are received on Opcode 0: Event
- the event type will be part of the root message object under the t
key.
{
op: 0,
seq: 1,
t: "INIT_STATE",
d: {
"94490510688792576": {
// Full Lanyard presence (see API docs above for example)
}
}
}
{
op: 0,
seq: 2,
t: "PRESENCE_UPDATE",
d: {
// Full Lanyard presence and an extra "user_id" field
}
}
Lanyard can disconnect clients for multiple reasons, usually to do with messages being badly formatted. Please refer to your WebSocket client to see how you should handle errors - they do not get received as regular messages.
Name | Code | Data |
---|---|---|
Invalid/Unknown Opcode | 4004 | unknown_opcode |
Opcode Requires Data | 4005 | requires_data_object |
Invalid Payload | 4006 | invalid_payload |
Lanyard quicklinks allow you to easily access resources from Discord, such as profile pictures.
https://api.lanyard.rest/<id>.<file_type>
Where id
is the Discord user ID. file_type
can be one of: png
, gif
, webp
, jpg
, or jpeg
Build the Docker image by cloning this repo and running:
# The latest version is already on the docker hub, you can skip this step unless you would like to run a modified version.
docker build -t phineas/lanyard:latest .
If you don't already have a redis server you'll need to run one, here's the docker command to run one:
docker run -d --name lanyard-redis -v docker_mount_location_on_host:/data redis
And run Lanyard API server using:
docker run --rm -it -p 4001:4001 -e REDIS_HOST=redis -e BOT_TOKEN=<token> --link lanyard-redis:redis phineas/lanyard:latest
You'll be able to access the API using port 4001.
You also need to create a Discord bot and use its token above.
Create a bot here: https://discord.com/developers/applications
Make sure you enable these settings in your bot settings:
- Privileged Gateway Intents > PRESENCE INTENT
- Privileged Gateway Intents > SERVER MEMBERS INTENT
If you'd like to run Lanyard with docker-compose
, here's an example:
version: "3.8"
services:
redis:
image: redis
restart: always
container_name: lanyard_redis
lanyard:
image: phineas/lanyard:latest
restart: always
container_name: lanyard
depends_on:
- redis
ports:
- 4001:4001
environment:
BOT_TOKEN: <token>
REDIS_HOST: redis
Note, that you're hosting a http server, not https. You'll need to use a reverse proxy such as traefik if you want to secure your API endpoint.
Below is a list of sites using Lanyard right now, check them out! A lot of them will only show an activity when they're active. Create a PR to add your site below!
- alistair.cloud
- timcole.me
- dstn.to
- phineas.io
- slayter.dev
- lafond.dev
- cnrad.dev
- atzu.studio
- dont-ping.me
- eggsy.xyz
- crugg.de
- igalaxy.dev
- itspolar.dev
- vasc.dev
- eri.gg
- voided.dev
- thicc-thighs.de
- chezzer.dev
- arda.codes
- looskie.com
- barbarbar338.fly.dev
- marino.codes
- stealthwave.dev
- miraichu.co
- nith.codes
- veny.xyz
- 5elenay.github.io
- notnick.io
- encrypteddev.com
- kevinthomas.codes
- amine.im
- loom4k.me
- presence.im
- eleven.codes
- jackbailey.dev
- 345dev.me
- d3r1n.com
- vops.cc
- lion.himbo.cat
- zeromomentum.me
- emirkabal.com
- wosleyv.dev
- aidan.pw
- anaxes.codes
- avyanshralph.xyz
- maki.cafe
- cenap.js.org
- rexulec.com
- isaackogan.com
- eleven011.xyz
- krypton.ninja
- oxmc.xyz
- voltages.me
- tysm.dev
- ggorg.tk
- hexiaq.cf
- itsdestiny.me
- darkshiny.me
- nawrasse.vercel.app
- noirs.me
- 2m4u.netlify.app
- eleven.js.org
- roxza.me
- keaton.codes
- itsmebravo.dev
- cimok.co.uk
- winnerose.live
- alysum.vercel.app
- alpha.is-a.dev
- rovi.me
- snazzah.com
- aidak.tk
- doubleclickadvertising.com
- itsnp.cf
- sayanzyx.netlify.app
- lanyard-card-example.netlify.app
- niskii.denkylabs.com
- falsis.ga
- sanct.me
- Landing page?