SecurityGab // help
dengisan.nl →

← Back
help/API & Developers
Developers

API & Developers

A small, honest JSON API over the same data the website shows you — CTF challenges and submissions, the leaderboard, public profiles, and the forum. Built this whole platform and want to pull live data into your own site, script, or bot? This is how.

Authentication

Every request is authenticated with an API key, generated from your account's Settings page. Keys look like sg_<48 hex characters> and are shown to you exactly once, at creation — SecurityGab only ever stores a one-way hash of it, the same way your password is stored, so nobody (including us) can look up or recover the raw key later. If you lose it, revoke it and generate a new one.

Pass it in either header — both are accepted identically:

http

Authorization: Bearer sg_4285a7b26bc38b2f583d3003ab3f24a21186ab898d65bc6c
# — or —
X-Api-Key: sg_4285a7b26bc38b2f583d3003ab3f24a21186ab898d65bc6c

Endpoints marked auth below require a key. Endpoints marked public work with no key at all, but including one still counts toward your own rate limit bucket rather than sharing the anonymous one.

Quickstart

Fetch the leaderboard — no key needed for a read-only public endpoint:

curl

curl https://website.dengisan.nl/api/v1/leaderboard.php?limit=10

Submit a CTF flag — requires a key, since it acts on your account:

curl

curl -X POST https://website.dengisan.nl/api/v1/ctf/submit.php \
  -H "Authorization: Bearer sg_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"challenge_id": 3, "flag": "flag{...}"}'

Same thing from JavaScript:

javascript

const res = await fetch("https://website.dengisan.nl/api/v1/ctf/submit.php", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sg_YOUR_KEY_HERE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ challenge_id: 3, flag: "flag{...}" }),
});
const { success, data, error } = await res.json();

Or Python:

python

import requests

r = requests.post(
    "https://website.dengisan.nl/api/v1/ctf/submit.php",
    headers={"Authorization": "Bearer sg_YOUR_KEY_HERE"},
    json={"challenge_id": 3, "flag": "flag{...}"},
)
print(r.json())

Endpoints

Every response is JSON: {"success": true, "data": ...} or {"success": false, "error": "..."}.

methodpathdescriptionauth
GET/api/v1/me.phpInfo about the authenticated caller.auth
GET/api/v1/ctf/challenges.phpList active CTF challenges.public
POST/api/v1/ctf/submit.phpSubmit a flag — single-flag or a stage of a mixed challenge.auth
GET/api/v1/leaderboard.php?limit=50CTF leaderboard.public
GET/api/v1/profile.php?u=usernamePublic profile: bio, avatar, stats, reputation, badges.public
GET/api/v1/forum/boards.phpCategories and boards.public
GET/api/v1/forum/threads.php?board_id=1Threads in a board.public
GET/api/v1/forum/thread.php?id=1A thread and its posts.public
GET/api/v1/forum/search.php?q=sqlmapSearch thread titles and post bodies.public
POST/api/v1/forum/reply.phpPost a reply to a thread. Requires a verified, unmuted account.auth
POST/api/v1/ctf/reveal-hint.phpReveal a CTF hint — costs points off that challenge's eventual solve award.auth

Rate limits

Errors

Standard HTTP status codes, always with a JSON body — never an HTML error page or a stack trace:

statusmeaning
400Request understood, but rejected (e.g. wrong flag, already solved).
401Missing or invalid API key on an endpoint that requires one.
404The thing you asked for doesn't exist (or isn't visible to you).
422Required parameter missing or malformed.
429Rate limit exceeded.
500Something broke on our end. These are logged; if it persists, let us know.

Internal errors are deliberately generic on purpose — the real exception detail (query text, file, line) is logged server-side only and never included in an API response, even in a 500. That's not an oversight to work around; it's how the endpoint is supposed to behave.

Data & privacy

The API doesn't expose anything the website itself doesn't already show a logged-out visitor, plus whatever's tied to your own key when you're authenticated. Concretely:

If you build something public with this (a bot, a leaderboard widget, a companion app) — please don't cache or republish more than you need, and don't scrape endpoints faster than the rate limits already allow just because you technically can from multiple keys. It's a small community; play fair with it.