How to build a public API for developers on your MU Online server
Design and publish a public REST API for your MU Online server, with key-based authentication, rate limits, ranking and character endpoints, and documentation for community developers.
A public API turns your MU Online server into a platform: third-party ranking sites, Discord bots, stats apps, and community tools can consume your game's data without touching the database or requesting special access. This drives engagement, generates free content (embedded rankings on other sites
A public API turns your MU Online server into a platform: third-party ranking sites, Discord bots, stats apps, and community tools can consume your game's data without touching the database or requesting special access. This drives engagement, generates free content (embedded rankings on other sites, event alerts in Discord) and gives the project a more professional image. But a poorly designed API becomes an entry point for abusive scraping, sensitive data leaks, and database overload. This tutorial covers the full design: architecture, authentication, endpoints, usage limits, and documentation.
Why offer a public API
MU Online servers that expose data via API tend to retain their community longer, because third parties start building on top of your ecosystem: a Discord bot that announces when a boss is about to spawn, a guild-stats website, a browser extension showing the PK ranking. Each of these integrations is free promotion and reduces support load, since players stop asking "how many points do I have" in chat and start checking an external dashboard instead.
Recommended architecture
The API should never touch the GameServer's production database directly. The safest pattern is:
- Read-only replicated database, or a view/cache table refreshed periodically (every 1-5 minutes).
- Intermediary backend (Node.js/Express, PHP/Laravel, or Go) that queries this database and serializes it to JSON.
- Cache layer (Redis or in-memory cache) for ranking responses, which change infrequently.
- Gateway/reverse proxy (Nginx) in front, handling TLS, basic rate limiting, and logs.
This separation ensures a heavy third-party query never locks the database the GameServer uses to authenticate logins.
Authentication via API key
Every developer who wants to consume the API must register and receive a unique API key. The typical flow:
CREATE TABLE api_keys (
id INT PRIMARY KEY AUTO_INCREMENT,
developer_email VARCHAR(120) NOT NULL,
api_key VARCHAR(64) UNIQUE NOT NULL,
tier VARCHAR(20) DEFAULT 'free',
requests_per_minute INT DEFAULT 60,
active TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Every request must send the key in the Authorization: Bearer <api_key> header. The backend validates the key, checks whether it's active, and applies the corresponding tier limit before processing the query.
Essential endpoints to start with
| Endpoint | Method | Returns |
|---|---|---|
/api/v1/ranking/reset | GET | Top players by reset, with name, class, and guild |
/api/v1/ranking/guild | GET | Guild ranking by score/territory |
/api/v1/character/:name | GET | Public character data (level, class, guild) |
/api/v1/events/status | GET | Current event status (Blood Castle, Devil Square, boss) |
/api/v1/server/status | GET | Online/offline, number of connected players |
Start with these five. They cover the use cases most requested by the community (Discord bots and ranking sites) without exposing anything sensitive.
Rate limiting and abuse protection
Without a request limit, a single misconfigured script can generate thousands of calls per minute and take down your backend. Configure limits per key and per IP:
limit_req_zone $http_authorization zone=api_by_key:10m rate=60r/m;
location /api/ {
limit_req zone=api_by_key burst=20 nodelay;
proxy_pass http://api_backend;
}
Combine this with a logical limit at the application level (a Redis counter per key), since Nginx alone can't distinguish invalid keys from valid ones with fine granularity.
Response format and error handling
Standardize all responses in JSON with a consistent structure, including status and clear error messages:
{
"success": true,
"data": { "character": "DemonSlayer", "level": 400, "class": "Blade Master" },
"meta": { "cached_at": "2026-07-30T12:00:00Z" }
}
On error, return the correct HTTP code (400, 401, 404, 429, 500) and a body explaining the reason. This saves third-party developers hours of debugging an endpoint that just returned null.
CORS and browser usage
If you expect third-party sites to consume the API directly from the browser (client-side JavaScript), configure CORS headers to allow only the domains that make sense, or allow * only for public read-only endpoints without sensitive data. Never enable unrestricted CORS on authenticated endpoints that return account data.
Documentation for developers
An API without documentation goes unused. Publish a /developers or /api/docs page on your site with:
- How to request an API key (form or Discord).
- List of endpoints, parameters, and example responses.
- Usage limits per tier.
- Acceptable use policy (what can and cannot be done with the data).
Tools like Swagger/OpenAPI generate this documentation automatically from the code and let developers test endpoints directly in the browser.
Monitoring and usage logs
Log every request (key, endpoint, response time, HTTP code) into a table or logging service. This lets you identify who is abusing the limit, which endpoints are used the most (to prioritize optimization), and detect attempts to access routes that don't exist, a common sign of malicious scanning.
Versioning and deprecation
When changing an endpoint's format, never alter the version in production (/v1/) — launch /v2/ in parallel and keep /v1/ working for an announced transition period (30-60 days), notifying registered developers by email or Discord before deactivating the old version.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Production database slow after launching the API | Heavy queries directly against the character table | Use a read-only replica or cache with periodic refresh |
| Third parties complaining about inconsistent responses | Missing API versioning | Adopt a /v1/ prefix and never break a published version |
| Leaked API key being used by third parties | Key without IP or domain scoping | Add origin validation and allow revoking individual keys |
| Traffic spikes taking down the backend | No rate limiting | Configure limit_req in Nginx and a per-key counter in Redis |
| Sensitive data appearing in responses | Endpoint mirroring database columns without filtering | Always manually serialize allowed fields, never do a raw SELECT * into JSON |
API launch checklist
- Intermediary backend created, with no direct public access to the production database.
- API key system with tiers and limits implemented.
- Rate limiting configured on the proxy and in the application.
- Essential endpoints (ranking, character, events, status) published and tested.
- CORS correctly configured per endpoint.
- Public documentation published at
/developersor/api/docs. - Usage logging and monitoring active.
- Versioning plan (
/v1/,/v2/) defined before launch.
With the public API live, the natural next step is reviewing the security of the entire web layer of the server, since any exposed endpoint expands the attack surface — see the web vulnerability audit guide for a full scan before broadly announcing the API.
Frequently asked questions
Do I need to expose my database directly to the API?
No, and you should never do that. The API should run as an intermediary layer (your own backend) that queries the database internally and returns only sanitized JSON. Exposing the database directly is one of the most common causes of data leaks and SQL Injection attacks on MU servers.
How do I stop bots from overloading my API?
Implement rate limiting per API key and per IP, with a reasonable limit like 60 requests per minute for free-tier use. Tools like Redis with sliding-window counters solve this simply and cheaply.
Is it worth charging for API access?
For most private servers it doesn't pay to charge directly, but you can offer tiers: a free tier with low limits for the community, and a higher tier for partner sites or third-party apps, subject to manual approval.
What data should I NEVER expose through the public API?
Passwords (even hashed), player emails, IPs, session tokens, and any information that would let someone identify or hijack an account. The public API should be limited to game data: rankings, guilds, equipped items, and aggregated stats.
Do I need to version the API from the start?
Yes. Use a prefix like /api/v1/ from the very first published endpoint. This keeps you from breaking third-party integrations when you need to change the response format later — you simply launch /api/v2/ in parallel.