Developer Platform

Build with Link Protect

A scoped REST API for stats, link checks, moderation and config, a realtime event stream, signed webhooks, ready-made SDKs and live SVG embeds — free for approved developers.

Try everything without a server — sandbox key lp_sandbox

The public sandbox key works for anyone on every read endpoint and returns synthetic data — no sign-up, no Discord server, no approval. It’s prefilled in the Playground. Write endpoints (moderate, configscope) need a real key from your server’s Developer tab.

Getting access

Developer access is granted per Discord account. Sign in, open Settings → Developer Accessand send a request — you’ll get a notification once it’s reviewed. Approved developers see a Developer tab in every server dashboard they manage, where API keys and webhooks are created.

Authentication

API keys are created per server in that server’s Developer tab (up to 5 per server) and only reach that server’s data. Send the key in the X-Api-Key header (or Authorization: Bearer lp_…). Keys are shown once at creation and stored only as a hash — treat them like passwords.

Base URL: https://link-protect.com/api/v1 · Rate limit: 60 requests/minute per key (HTTP 429 beyond that).

Each key carries scopes, chosen when the key is created. Calling an endpoint without the required scope returns HTTP 403:

readStats, trends, link checks, warn lookups, the event stream. Always granted.
moderateWarn, timeout, kick & ban members via POST /moderate.
configToggle blockers, edit the blacklist, switch lockdown on or off.

The sandbox key lp_sandbox has the read scope only and serves synthetic data — perfect for trying requests before creating a real key.

REST API

GET /api/v1/stats

Live protection stats for the key’s server.

curl -H "X-Api-Key: lp_your_key" https://link-protect.com/api/v1/stats

{
  "guildId": "1234567890",
  "totalWarnings": 128,
  "warnedUsers": 42,
  "activeBlockers": 6,
  "blockers": { "malware": true, "nitro": true, "invite": false, ... },
  "thresholds": { "kick": 5, "ban": 10, "timeout": 3 }
}

GET /api/v1/trends?days=14

Daily action counts (1–60 days) plus totals and top reasons — the same data behind the dashboard chart.

curl -H "X-Api-Key: lp_your_key" "https://link-protect.com/api/v1/trends?days=7"

{
  "days": 7,
  "total": 23,
  "perDay": [ { "date": "2026-07-17", "warned": 3, "kicked": 0, "banned": 0, "timeout": 1, "count": 4 }, ... ],
  "topReasons": [ { "reason": "Posted a malware link", "count": 9 }, ... ],
  "totals": { "warned": 18, "kicked": 2, "banned": 1, "timeout": 2 }
}

GET /api/v1/check?url=…&deep=1

Threat lookup against the Link Protect database + Google Safe Browsing — the engine behind the public link checker. With deep=1 the redirect chain is resolved server-side and every hop is checked.

curl -H "X-Api-Key: lp_your_key" "https://link-protect.com/api/v1/check?url=bit.ly/abc123&deep=1"

{
  "url": "bit.ly/abc123",
  "domain": "bit.ly",
  "safe": false,
  "category": "phishing",
  "source": "threat-db",
  "reason": "This link redirects to fake-login.ru, which is flagged as phishing…",
  "seenOnServers": 14,
  "redirects": [ { "url": "https://fake-login.ru/discord", "domain": "fake-login.ru", "status": 301 } ],
  "finalDomain": "fake-login.ru"
}

POST/api/v1/check/batch

Check up to 25 URLs in one request — same verdict shape as the single check, one entry per URL.

curl -X POST -H "X-Api-Key: lp_sandbox" -H "Content-Type: application/json" \
  -d '{"urls": ["bit.ly/abc123", "https://example.com"]}' \
  https://link-protect.com/api/v1/check/batch

GET/api/v1/warns/{userId}

The warning record of one member on the key’s server — count, reasons and where they stand against the kick/ban thresholds.

curl -H "X-Api-Key: lp_sandbox" https://link-protect.com/api/v1/warns/9876543210

POST/api/v1/moderatescope: moderate

Moderate a member from your own tooling. action is one of warn · timeout · untimeout · kick · ban · unban; minutes sets the timeout length, reason lands in the audit log.

curl -X POST -H "X-Api-Key: lp_your_key" -H "Content-Type: application/json" \
  -d '{"userId": "9876543210", "action": "timeout", "minutes": 60, "reason": "Spam"}' \
  https://link-protect.com/api/v1/moderate

POST/api/v1/blockerscope: config

Toggle one of the link blockers (malware, nitro, invite, … — the same ids the stats endpoint returns).

curl -X POST -H "X-Api-Key: lp_your_key" -H "Content-Type: application/json" \
  -d '{"blocker": "nitro", "enabled": true}' \
  https://link-protect.com/api/v1/blocker

POST/api/v1/blacklistscope: config

Add or remove a domain/link on the server’s custom blacklist — action is add or remove.

curl -X POST -H "X-Api-Key: lp_your_key" -H "Content-Type: application/json" \
  -d '{"action": "add", "link": "scam-site.ru"}' \
  https://link-protect.com/api/v1/blacklist

POST/api/v1/lockdownscope: config

Switch emergency lockdown on or off — the same big red button as the dashboard, scriptable.

curl -X POST -H "X-Api-Key: lp_your_key" -H "Content-Type: application/json" \
  -d '{"active": true, "reason": "Raid in progress"}' \
  https://link-protect.com/api/v1/lockdown

GET/api/v1/openapi.json

The full OpenAPI 3 spec of the public API — no key required. Feed it to your client generator or import it into Postman/Insomnia: link-protect.com/api/v1/openapi.json.

Realtime event stream

GET /api/v1/events/stream is a Server-Sent Eventsstream of your server’s moderation events (the same six event types webhooks deliver) — ideal when you can’t host a public webhook endpoint. Auth works via the usual X-Api-Key header, or a ?key= query param for browser EventSource clients:

const es = new EventSource(
  "https://link-protect.com/api/v1/events/stream?key=lp_your_key"
);
es.onmessage = (e) => {
  const event = JSON.parse(e.data);   // { event: "link_blocked", guildId, data, … }
  console.log(event.event, event.data);
};

Limits: 2 concurrent streams per key, and each connection is closed after 30 minutes — EventSource reconnects automatically.

Playground

Fire real requests from your browser — prefilled with the sandbox key lp_sandbox, so everything read-only works instantly. Paste your own key to hit your server (moderate/config calls are live — they really moderate).

GET /api/v1/statsscope: readLive protection stats
Fields are sent as query parameters.

SDKs

Tiny zero-dependency clients for the whole v1 API — REST calls, the event stream and a webhook signature verification helper are included in both.

JavaScript SDK
Download
Node 18+ & browsers · ESM
import { LinkProtect } from "./linkprotect.js";

const lp = new LinkProtect("lp_your_key");

const stats = await lp.stats();
const verdict = await lp.check("bit.ly/abc123");
await lp.moderate({ userId: "9876543210", action: "warn" });

// Express: verify webhook deliveries
lp.verifySignature(rawBody, req.headers["x-linkprotect-signature"], "whsec_…");
Python SDK
Download
Python 3.9+ · stdlib only
from linkprotect import LinkProtect

lp = LinkProtect("lp_your_key")

stats = lp.stats()
verdict = lp.check("bit.ly/abc123")
lp.moderate(user_id="9876543210", action="warn")

# Flask/FastAPI: verify webhook deliveries
lp.verify_signature(raw_body, signature_header, "whsec_…")

Webhooks

Register up to 3 HTTPS endpoints per server in the Developer tab and pick the events you care about. Deliveries are POSTs with a JSON body, sent within ~10 seconds of the event:

link_blockedA link was blocked and the member warned
member_kickedWarn threshold escalated to a kick
member_bannedWarn threshold escalated to a ban
member_timeoutWarn threshold escalated to a timeout
scamshield_catchScam Shield caught cross-channel scam spam
raid_detectedA link raid was auto-defended
POST https://your-server.com/linkprotect-hook
Content-Type: application/json
X-LinkProtect-Event: link_blocked
X-LinkProtect-Signature: sha256=8f3a…

{
  "event": "link_blocked",
  "guildId": "1234567890",
  "data": {
    "user_id": "9876543210", "username": "scammer42", "channel_id": "111222333",
    "action": "warned", "reason": "Posted a malware link", "warn_count": 2,
    "timestamp": 1784800000
  },
  "sentAt": 1784800005
}

Every delivery is signed: X-LinkProtect-Signatureis the hex HMAC-SHA256 of the raw request body, keyed with your webhook’s whsec_… secret. Verify before trusting:

// Node.js
import crypto from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
}

Respond with any 2xx within 6 seconds. Failed deliveries count up — after 25 consecutive failures the webhook is disabled automatically (re-enable it in the Developer tab, which also resets the counter). The Developer tab keeps a delivery log (last 50, with status and latency) per webhook, and its Send test event control delivers a realistic sample of any event type to your endpoint on demand.

Embeds

Live SVG widgets — no key required, cacheable, safe to hotlink:

<!-- Protected-by badge (style=light for bright pages) -->
<img src="https://link-protect.com/api/badge?guild=YOUR_SERVER_ID" alt="Protected by Link Protect">

<!-- Live stats card -->
<img src="https://link-protect.com/api/embed/stats?guild=YOUR_SERVER_ID" alt="Link Protect stats">

<!-- Voter leaderboard (limit 3–10) -->
<img src="https://link-protect.com/api/embed/leaderboard?limit=5" alt="Top voters">

Missing something?

The platform grows with what developers actually need — send a feature request from any server dashboard (Report → Feedback) or join the beta programme in the Developer tab.

Request developer access