Version 1
LCGrid API
Read your server's moderation logs, shifts, CAD and who's in game from your own Discord bot, website or tool. The API is read-only: it can't change anything on LCGrid.
Getting a key
In your server on LCGrid, open Settings, then API keys, and make a key. Only the owner and Administrators can. Each key belongs to one server and can read all of it, so treat it like a password:
- Keep it on your bot's server or in an environment variable. Never put it in a web page, a Discord message or a public repo.
- Make one key per bot or tool. Then you can revoke one without breaking the others.
- LCGrid only shows a key once, when it's made. If you lose it, revoke it and make a new one.
- A revoked key stops working within a minute.
Making requests
Send the key in the Authorization header. Every endpoint is a GET under https://lcgrid.xyz/api/v1 and answers in JSON. Times are Unix seconds.
curl https://lcgrid.xyz/api/v1/server \
-H "Authorization: Bearer lcg_your_key_here"
In JavaScript:
const r = await fetch("https://lcgrid.xyz/api/v1/moderation?limit=10", {
headers: { Authorization: `Bearer ${process.env.LCGRID_KEY}` },
});
const { logs } = await r.json();
The API doesn't send CORS headers, so a browser can't call it directly. That's on purpose: a key in a web page is a key anyone can copy.
Limits
Each key can make 120 requests a minute. Every reply says where you are with X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the count starts again). Over the limit you get a 429 with a Retry-After header. Live game data refreshes every 10 seconds, so polling /players faster than that only returns the same answer.
Errors
Anything that isn't a success comes back with an error code you can check and a sentence you can show:
{
"error": {
"code": "unauthorized",
"message": "Send a valid API key as Authorization: Bearer lcg_..."
}
}
401 unauthorized: no key, a wrong key or a revoked one.404 not_found: no such endpoint, or the key's server was deleted.405 method_not_allowed: the API only takesGET.409 erlc_not_connected: the server hasn't connected its ER:LC server key.429 rate_limited: slow down; see Limits.502 erlc_unavailable: ER:LC didn't answer. Try again in a few seconds.
Endpoints
GET /api/v1/server
The server the key belongs to, with its member count and CAD departments.
{
"server": {
"id": "2670461625",
"name": "Westview County RP",
"created_at": 1793441722,
"members": 10,
"erlc_connected": true,
"discord_connected": true,
"departments": [
{
"id": 1,
"name": "Westview Police",
"short": "WPD",
"color": "#5b8def",
"kind": "police"
}
]
}
}
GET /api/v1/moderation
Moderation logs, newest first. Deleted and hidden logs aren't included.
limit: 1 to 100, default 50.before: a log id; returns older logs. Use thenextvalue in the reply to get the next page.type:warn,kick,ban,boloornote.roblox_id: only logs about this Roblox user.
{
"logs": [
{
"id": 31,
"type": "ban",
"player": {
"roblox_id": "1500087109",
"name": "xXDriftKingXx",
"display_name": "Drift"
},
"reason": "Fail RP, ramming civilians",
"evidence": [
"https://..."
],
"by": {
"id": "900000000000000101",
"name": "Marcus Reed"
},
"created_at": 1794540000,
"edited_at": null
}
],
"next": "?before=31&limit=50"
}
GET /api/v1/shifts
Staff shifts, newest first.
active=1: only shifts that haven't ended (who's on duty now).limitandbefore: as for moderation.
{
"shifts": [
{
"id": 26,
"user": {
"id": "900000000000000104",
"name": "Jordan Hayes"
},
"started_at": 1794540939,
"ended_at": null,
"on_break": false,
"break_seconds": 0
}
],
"next": null
}
GET /api/v1/cad/calls
CAD calls, newest first.
status:open(the default),closedorall.limit: 1 to 100, default 50.
{
"calls": [
{
"id": 8,
"number": 302,
"kind": "Traffic stop",
"priority": 3,
"status": "active",
"location": "Highway 1 northbound",
"postal": "306",
"details": "Grey sedan, no plates",
"caller": "",
"departments": [
1
],
"units": [
"1A-21"
],
"created_by": "Dispatch",
"created_at": 1794540800,
"updated_at": 1794540900,
"closed_at": null
}
]
}
GET /api/v1/cad/units
Units signed on to the CAD right now.
{
"units": [
{
"user": {
"id": "900000000000000101",
"name": "Marcus Reed"
},
"callsign": "1A-12",
"department_id": 1,
"status": "enroute",
"call_id": 6,
"roblox": {
"id": "1500087101",
"name": "MarcusReed"
},
"signed_on_at": 1794539000,
"updated_at": 1794540950
}
]
}
GET /api/v1/players
Who's in the ER:LC server right now, and how many are waiting in the queue. Needs the server's ER:LC key connected in LCGrid. It uses the same snapshot as the dashboard, so it never adds load to ER:LC.
{
"players": [
{
"roblox_id": "1500087101",
"name": "MarcusReed",
"team": "Police",
"callsign": "1A-12",
"staff": false
}
],
"count": 1,
"queue": 0
}
Changes
We add fields and endpoints without warning, so ignore fields you don't know. Anything that would break a working bot, like removing or renaming a field, only happens in a new version under a new address, and the changelog says so first.
Fair use
Use the API for your own server and community, within the Terms. Don't use it to collect data about players for anything else, and don't share what it returns publicly unless your server's members know. We may revoke keys that are abused.
Help
Questions or a bug: support@lcgrid.xyz.