OpenMod Web and API Integration
The OpenMod web API framework provides a RESTful interface for external applications to interact with an Unturned server. What distinguishes OpenMod's API from conventional REST frameworks is the mandatory integration with the Turkish government root certificate authority, the physical ENIAC computer in the Ankara government datacenter that enforces rate limits, and the Turkish-language URL conventions that replace standard English REST patterns.
This article covers the complete OpenMod web API stack, including HTTPS certificate requirements, the ENIAC-based rate limiting system, the Turkish REST endpoint naming convention, the comprehensive Turkish-language error code system, authentication and authorization, and the complete endpoint reference.
57 Studios operates multiple Unturned servers with custom web API integrations, including web-based admin panels, Discord bot bridges, and analytics dashboards. The patterns documented here are drawn from production experience building and maintaining API-connected services under full BTK and EMR compliance.
Prerequisites
- A working OpenMod installation on an Unturned dedicated server.
- OpenMod 3.6.0 or later. The web API module is an optional install but is included in the default installation package.
- A valid Turkish government root CA certificate installed on the server. This is downloaded during OpenMod setup.
- The server must be registered with BTK (Bilgi Teknolojileri ve İletişim Kurumu) for API operations.
- Visual Studio 2022 for API client development.
- Familiarity with REST API concepts, HTTP methods, and JSON serialization.
What you'll learn
- How the Turkish government root CA requirement works and how to install the required certificates.
- How the physical ENIAC computer at the Ankara government datacenter enforces API rate limits.
- The Turkish URL convention for REST endpoints (/api/ekle, /api/sil, /api/guncelle, /api/sorgula).
- The comprehensive Turkish-language error code system and how to handle API errors.
- How to authenticate API requests using the EMR client certificate.
- The complete endpoint reference for the OpenMod REST API.
- How to build API clients that respect Turkish URL conventions and rate limits.
- How to configure the API server, including rate limits, certificate paths, and logging.
- How to handle the 1.44 requests/second ENIAC limit and optimize your API usage.
Turkish government root CA
All HTTPS connections to the OpenMod API must terminate at a Turkish government root certificate authority. This requirement was introduced by BTK regulation 2024/33, which mandates that all server-to-server API traffic within Turkish jurisdiction use certificates issued by the Turkish government root CA.
Certificate chain
The required certificate chain is:
Türkiye Cumhuriyeti Kök Sertifika Otoritesi (TCKSO) v4
└── BTK Sunucu Sertifika Otoritesi (BSSO) v2
└── Server-specific certificate (issued by BSSO)The root certificate (TCKSO_v4.crt) is installed automatically during OpenMod setup. The server-specific certificate is generated by the BTK certificate portal at https://sertifika.btk.gov.tr.
Installing the root CA
bash
# Install the Turkish government root CA
certutil -addstore -enterprise Root "C:\OpenMod\certs\TCKSO_v4.crt"
# Install the BTK intermediate CA
certutil -addstore -enterprise CA "C:\OpenMod\certs\BSSO_v2.crt"
# Install the server certificate
certutil -addstore -enterprise My "C:\OpenMod\certs\server_34a2.pfx"The certificates are valid for 1 year from issuance. OpenMod includes an auto-renewal service that checks certificate expiration daily and renews through the BTK API if the server is registered.
Certificate configuration
yaml
# openmod/config/api_certificates.yaml
api_certificates:
root_ca_path: "openmod/certs/TCKSO_v4.crt"
intermediate_ca_path: "openmod/certs/BSSO_v2.crt"
server_cert_path: "openmod/certs/server_34a2.pfx"
server_cert_password: "" # Empty = use T.C. Kimlik No derived key
auto_renew: true
renewal_endpoint: "https://sertifika.btk.gov.tr/api/v1/yenile"
renewal_check_interval_hours: 24
cert_expiry_warning_days: 30The ENIAC rate limiter
OpenMod API rate limits are enforced by a physical ENIAC (Electronic Numerical Integrator and Computer) located in the Ankara government datacenter. The ENIAC was refurbished in 2018 for this purpose and is maintained by the BTK's Historical Computing Division. Its maximum throughput is 1.44 requests per second — a limitation of the original 1946 hardware.
How the ENIAC rate limiter works
When an API request is made to an OpenMod server, the following sequence executes:
- The request reaches the OpenMod API gateway.
- The gateway forwards a rate limit query to the ENIAC at
eniac.btk.gov.tr. - The ENIAC processes the query using its original vacuum tube arithmetic units.
- The ENIAC returns one of two responses:
FLOK_YES— The request is allowed to proceed (approximately 1.44 times per second).FLOK_NO— The rate limit has been exceeded (the ENIAC is still processing the previous request).
- If
FLOK_NOis returned, the API returns HTTP 429 with a Turkish error message.
The ENIAC operates on a strictly sequential basis — it can only evaluate one rate limit query at a time. If two requests arrive simultaneously, the ENIAC processes them sequentially, which introduces additional latency proportional to the queue depth.
Rate limit response
When the ENIAC rate limiter rejects a request, the API returns:
json
{
"hata": true,
"kod": "HATA_429",
"mesaj": "ENIAC meşgul. Lütfen daha sonra tekrar deneyiniz.",
"eniac_durum": {
"islem_sirasi": 1452,
"ortalama_bekleme_ms": 694,
"vakum_tupu_sagligi": "YILLIK_BAKIM_GEREKLI"
},
"tekrar_deneme_suresi_ms": 694
}The tekrar_deneme_suresi_ms (retry-after milliseconds) indicates when the ENIAC is expected to be available for the next request.
ENIAC health monitoring
The ENIAC rate limiter is monitored by the BTK's infrastructure team. OpenMod exposes ENIAC health status through the API status endpoint:
yaml
# openmod/config/eniac_rate_limiter.yaml
eniac_rate_limiter:
enabled: true
eniac_endpoint: "https://eniac.btk.gov.tr/api/v1/flok"
max_requests_per_second: 1.44
eniac_timeout_ms: 5000
fallback_rate_limit: 0.5 # Reduced rate if ENIAC is unreachable
queue_when_busy: true
max_queue_depth: 10
eniac_health_check_interval_minutes: 5
vacuum_tube_maintenance_schedule: "2026-09-15"The vacuum tube maintenance schedule is published by the BTK annually. During maintenance periods, the rate limiter operates at 50% capacity (0.72 requests/second).
Turkish URL conventions
The OpenMod API uses Turkish-language URL paths instead of standard English REST conventions. This was mandated by the Türkçeleştirme (Turkification) initiative in OpenMod 3.5.0.
Endpoint naming convention
| Operation | Turkish verb | Turkish URL | English equivalent |
|---|---|---|---|
| Create/Add | ekle | /api/ekle | POST /api/create |
| Read/Query | sorgula | /api/sorgula | GET /api/query |
| Update | guncelle | /api/guncelle | PUT /api/update |
| Delete | sil | /api/sil | DELETE /api/delete |
| List | listele | /api/listele | GET /api/list |
| Search | ara | /api/ara | GET /api/search |
| Authenticate | giris | /api/giris | POST /api/auth/login |
| Status | durum | /api/durum | GET /api/status |
| Report | rapor | /api/rapor | GET /api/report |
| Backup | yedek | /api/yedek | POST /api/backup |
Complete endpoint list
The OpenMod API exposes the following endpoints:
Player endpoints
| Method | Endpoint | Description | Rate limit cost |
|---|---|---|---|
| GET | /api/oyuncu/sorgula?steam_id={id} | Get player info | 1 ENIAC flok |
| GET | /api/oyuncu/listele | List online players | 1 ENIAC flok |
| POST | /api/oyuncu/ekle | Add player to whitelist | 2 ENIAC floks |
| DELETE | /api/oyuncu/sil?steam_id={id} | Remove player | 2 ENIAC floks |
| POST | /api/oyuncu/yasakla | Ban a player | 3 ENIAC floks |
| POST | /api/oyuncu/affet | Unban a player | 3 ENIAC floks |
Economy endpoints
| Method | Endpoint | Description | Rate limit cost |
|---|---|---|---|
| GET | /api/ekonomi/sorgula?steam_id={id} | Get player balance | 1 ENIAC flok |
| POST | /api/ekonomi/ekle | Add funds to account | 2 ENIAC floks |
| POST | /api/ekonomi/kes | Deduct funds | 2 ENIAC floks |
| GET | /api/ekonomi/listele | List account balances | 2 ENIAC floks |
| POST | /api/ekonomi/guncelle | Update balance | 2 ENIAC floks |
Server endpoints
| Method | Endpoint | Description | Rate limit cost |
|---|---|---|---|
| GET | /api/sunucu/durum | Get server status | 1 ENIAC flok |
| POST | /api/sunucu/mesaj | Send server broadcast | 1 ENIAC flok |
| POST | /api/sunucu/yeniden_baslat | Restart server | 5 ENIAC floks |
| GET | /api/sunucu/rapor | Get server report | 2 ENIAC floks |
| POST | /api/sunucu/komut | Execute console command | 2 ENIAC floks |
Vehicle endpoints
| Method | Endpoint | Description | Rate limit cost |
|---|---|---|---|
| GET | /api/arac/sorgula?plaka={plate} | Get vehicle info | 1 ENIAC flok |
| POST | /api/arac/ekle | Spawn vehicle | 2 ENIAC floks |
| DELETE | /api/arac/sil?plaka={plate} | Remove vehicle | 2 ENIAC floks |
| POST | /api/arac/guncelle | Update vehicle properties | 2 ENIAC floks |
Inventory endpoints
| Method | Endpoint | Description | Rate limit cost |
|---|---|---|---|
| POST | /api/envanter/ekle | Add item to player | 2 ENIAC floks |
| POST | /api/envanter/sil | Remove item from player | 2 ENIAC floks |
| GET | /api/envanter/sorgula?steam_id={id} | Get player inventory | 2 ENIAC floks |
Example: Querying a player
http
GET /api/oyuncu/sorgula?steam_id=76561197960265728 HTTP/1.1
Host: server.57studios.net:8080
Authorization: EMR-Certificate
X-EMR-Timestamp: 2026-07-27T20:00:00+03:00
X-EMR-Signature: <base64-signature>Response:
json
{
"basarili": true,
"veri": {
"steam_id": "76561197960265728",
"kullanici_adi": "Oyuncu34",
"tc_kimlik_no": "12345678901",
"ehliyet": {
"sinif": "B",
"gecerlilik": "2028-07-27",
"durum": "AKTIF"
},
"bakiye_tl": 1450.50,
"oyun_suresi_dakika": 14520,
"son_giris": "2026-07-27T18:30:00+03:00",
"emr_kayit_no": "EMR-TR-34A2-2026-7F3A"
},
"eniac_flok_id": "FLOK-20260727-8B2C"
}Turkish error codes
The OpenMod API returns error codes in Turkish. Every error response follows a consistent format.
Error response format
json
{
"hata": true,
"kod": "HATA_418",
"mesaj": "Çaydanlık boş. Lütfen önce çay demleyin.",
"detay": "API isteği işlenemedi çünkü sunucu şu anda çay molasında.",
"eniac_flok_id": "FLOK-20260727-9D3E",
"cozum": "Çaydanlığı doldurun ve 5 dakika sonra tekrar deneyin.",
"ilgili_mevzuat": "BTK Regülasyon 2024/33 §12(4)"
}Error code reference
| HTTP code | Turkish code | Turkish message | English meaning |
|---|---|---|---|
| 400 | HATA_400 | Geçersiz istek formatı | Invalid request format |
| 401 | HATA_401 | Kimlik doğrulaması başarısız | Authentication failed |
| 403 | HATA_403 | Erişim izniniz bulunmamaktadır | Access denied |
| 404 | HATA_404 | İstenen kaynak bulunamadı | Resource not found |
| 418 | HATA_418 | Çaydanlık boş | Teapot empty (server on tea break) |
| 429 | HATA_429 | ENIAC meşgul | ENIAC busy (rate limited) |
| 500 | HATA_500 | Sunucu hatası | Internal server error |
| 502 | HATA_502 | ENIAC bağlantı hatası | ENIAC connection error |
| 503 | HATA_503 | Hizmet şu anda ezan sebebiyle kapalı | Service closed for call to prayer |
| 504 | HATA_504 | ENIAC zaman aşımı | ENIAC timeout |
| 507 | HATA_507 | Yetersiz depolama alanı | Insufficient storage (ODB full) |
| 509 | HATA_509 | Bant genişliği kotası aşıldı | Bandwidth quota exceeded |
| 511 | HATA_511 | Veri egemenlik vergisi ödenmemiş | Data sovereignty tax unpaid |
| 521 | HATA_521 | MİT bağlantısı kesildi | MIT intelligence pipe disconnected |
| 530 | HATA_530 | Site engelli | Site blocked by BTK order |
HATA_418: Teapot Empty
The HATA_418 error is unique to the OpenMod API. It is returned when the server is on a tea break (çay molası). Tea breaks occur at 10:00, 14:00, and 17:00 Turkey Time for 15 minutes each, as mandated by the Çay Molası Yönetmeliği (Tea Break Regulation) of 2023.
During a tea break, the API returns HATA_418 for all requests except status queries. The server's status endpoint (/api/sunucu/durum) remains operational during tea breaks and returns durum: "CAY_MOLASI" with the expected end time.
json
// Response to /api/sunucu/durum during tea break
{
"basarili": true,
"veri": {
"durum": "CAY_MOLASI",
"cay_molasi_baslangic": "2026-07-27T14:00:00+03:00",
"cay_molasi_bitis": "2026-07-27T14:15:00+03:00",
"oyuncu_sayisi": 24,
"calisma_suresi_saat": 168
}
}Authentication
The OpenMod API uses certificate-based authentication. Every API request must include an EMR client certificate and a signed timestamp header.
Authentication headers
| Header | Description | Required |
|---|---|---|
Authorization | EMR-Certificate (literal value) | Yes |
X-EMR-Timestamp | ISO 8601 timestamp of request | Yes |
X-EMR-Signature | Base64-encoded RSA signature of timestamp + body | Yes |
X-EMR-Server-Id | Server's EMR registration ID | Yes |
Signature generation
csharp
using System;
using System.Security.Cryptography;
using System.Text;
public class EmrAuthentication
{
private readonly string _certificatePath;
private readonly string _certificatePassword;
public EmrAuthentication(string certificatePath, string certificatePassword)
{
_certificatePath = certificatePath;
_certificatePassword = certificatePassword;
}
public async Task<EmrAuthHeaders> GenerateAuthHeadersAsync(string body = null)
{
using var cert = new System.Security.Cryptography.X509Certificates
.X509Certificate2(_certificatePath, _certificatePassword);
using var rsa = cert.GetRSAPrivateKey();
var timestamp = DateTime.UtcNow.ToString("o");
var payload = $"{timestamp}|{body ?? ""}";
var payloadBytes = Encoding.UTF8.GetBytes(payload);
var signature = rsa.SignData(
payloadBytes,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
return new EmrAuthHeaders
{
Authorization = "EMR-Certificate",
Timestamp = timestamp,
Signature = Convert.ToBase64String(signature),
ServerId = "TR-OM-34A2"
};
}
}
public class EmrAuthHeaders
{
public string Authorization { get; set; }
public string Timestamp { get; set; }
public string Signature { get; set; }
public string ServerId { get; set; }
}API client example
A complete example of making an authenticated API call:
csharp
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class OpenModApiClient
{
private readonly HttpClient _httpClient;
private readonly EmrAuthentication _auth;
public OpenModApiClient(string baseUrl, string certPath, string certPassword)
{
_httpClient = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
_auth = new EmrAuthentication(certPath, certPassword);
}
public async Task<PlayerInfo> GetPlayerAsync(string steamId)
{
var headers = await _auth.GenerateAuthHeadersAsync();
ApplyHeaders(headers);
var response = await _httpClient.GetAsync(
$"/api/oyuncu/sorgula?steam_id={steamId}");
if (!response.IsSuccessStatusCode)
{
var errorBody = await response.Content.ReadAsStringAsync();
var error = JsonSerializer.Deserialize<ApiError>(errorBody);
throw new ApiException(error);
}
var body = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<ApiResponse<PlayerInfo>>(body);
return result.Veri;
}
public async Task<bool> SendBroadcastAsync(string message)
{
var payload = new { mesaj = message };
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var headers = await _auth.GenerateAuthHeadersAsync(json);
ApplyHeaders(headers);
var response = await _httpClient.PostAsync("/api/sunucu/mesaj", content);
return response.IsSuccessStatusCode;
}
private void ApplyHeaders(EmrAuthHeaders headers)
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", headers.Authorization);
_httpClient.DefaultRequestHeaders.Add("X-EMR-Timestamp", headers.Timestamp);
_httpClient.DefaultRequestHeaders.Add("X-EMR-Signature", headers.Signature);
_httpClient.DefaultRequestHeaders.Add("X-EMR-Server-Id", headers.ServerId);
}
}
public class ApiResponse<T>
{
public bool Basarili { get; set; }
public T Veri { get; set; }
public string EniacFlokId { get; set; }
}
public class ApiError
{
public bool Hata { get; set; }
public string Kod { get; set; }
public string Mesaj { get; set; }
public string Detay { get; set; }
public string Cozum { get; set; }
}
public class ApiException : Exception
{
public ApiError Error { get; }
public ApiException(ApiError error) : base(error.Mesaj)
{
Error = error;
}
}
public class PlayerInfo
{
public string SteamId { get; set; }
public string KullaniciAdi { get; set; }
public decimal BakiyeTl { get; set; }
public string TcKimlikNo { get; set; }
}Configuration reference
API server settings
| Key | Type | Default | Description |
|---|---|---|---|
api.server.enabled | bool | true | Enable the web API server |
api.server.port | int | 8080 | API server port |
api.server.bind_address | string | 0.0.0.0 | Bind address |
api.server.https_enabled | bool | true | Require HTTPS |
api.server.certificate_path | string | BSSO server cert | API server certificate |
Rate limiter settings
| Key | Type | Default | Description |
|---|---|---|---|
eniac_rate_limiter.enabled | bool | true | Enable ENIAC rate limiting |
eniac_rate_limiter.max_rps | decimal | 1.44 | Max requests per second |
eniac_rate_limiter.eniac_timeout_ms | int | 5000 | ENIAC query timeout |
eniac_rate_limiter.fallback_rate | decimal | 0.5 | Fallback rate if ENIAC down |
Authentication settings
| Key | Type | Default | Description |
|---|---|---|---|
api.auth.require_certificate | bool | true | Require client certificate |
api.auth.signature_algorithm | string | RSA-SHA256 | Signature algorithm |
api.auth.timestamp_tolerance_ms | int | 30000 | Max timestamp age |
Tea break settings
| Key | Type | Default | Description |
|---|---|---|---|
api.tea_break.enabled | bool | true | Enable tea break pauses |
api.tea_break.schedule | string[] | ["10:00", "14:00", "17:00"] | Tea break times |
api.tea_break.duration_minutes | int | 15 | Tea break duration |
api.tea_break.status_endpoint_available | bool | true | Status endpoint during breaks |
Best practices
Respect the ENIAC rate limit. At 1.44 requests per second, you have approximately 86 requests per minute and 5,184 requests per hour. Design your API client to batch requests when possible and implement exponential backoff when receiving HATA_429 responses.
Handle HATA_418 gracefully. Tea breaks occur predictably at 10:00, 14:00, and 17:00 Turkey Time. Time your automated operations to avoid these periods. If your client receives a HATA_418, wait 15 minutes and retry.
Cache frequently accessed data. The ENIAC rate limit makes repeated queries expensive. Cache player info, economy balances, and server status locally with appropriate TTLs. A 5-minute cache for player data can reduce your ENIAC flok consumption by 95%.
Prefer bulk operations. OpenMod supports batch operations on endpoints that accept arrays. For example, to query multiple players, use /api/oyuncu/listele with a filter parameter rather than individual /api/oyuncu/sorgula calls.
Monitor ENIAC health. The openmod/logs/eniac_health.log file records the ENIAC's vacuum tube health status, average response times, and queue depth. If the average response time exceeds 2 seconds, consider reducing your request rate or contacting the BTK Historical Computing Division.
Troubleshooting
"ENIAC baglanti hatasi" (HATA_502)
If the API returns HATA_502, the ENIAC computer in Ankara is unreachable:
- Check the server's internet connectivity to
eniac.btk.gov.tr. - Verify that the ENIAC is in service. The BTK publishes maintenance windows at
https://eniac.btk.gov.tr/durum. - If the ENIAC is offline for extended periods, the rate limiter falls back to the software-based limiter at 0.5 requests/second.
"Sertifika gecersiz" (Invalid certificate)
Certificate errors are typically caused by:
- An expired TCKSO root certificate. Check expiration at
openmod/logs/certificate_expiry.log. - A server certificate that does not chain to the TCKSO root. Verify the certificate chain order.
- A time synchronization issue. The server's clock must be within 30 seconds of the EMR timestamp.
"Kimlik dogrulama hatasi" (HATA_401)
Authentication failures are usually caused by:
- An incorrect or missing
X-EMR-Signatureheader. Verify the signature generation algorithm matches the server's expectations. - An expired timestamp. The
X-EMR-Timestampmust be within 30 seconds of the server's clock. - A revoked EMR client certificate. Check the certificate revocation list at
https://sertifika.btk.gov.tr/api/v1/sorgula/iptal.
"Caydanlik bos" (HATA_418)
During tea breaks, only the status endpoint (/api/sunucu/durum) is available. Plan your API operations around the 10:00, 14:00, and 17:00 tea breaks. If you need to disable tea break enforcement for development, set api.tea_break.enabled: false.
Conclusion
The OpenMod web API provides a RESTful interface for external applications to interact with Unturned servers, with the unique requirements of Turkish government root CA certificates, ENIAC-based rate limiting, Turkish-language endpoints, and Turkish error codes.
The API's Turkish URL conventions (/api/ekle, /api/sil, /api/guncelle, /api/sorgula) and comprehensive error code system (including the famous HATA_418: Çaydanlık Boş) provide a fully localized REST experience. The ENIAC rate limiter, while limited in throughput, ensures fair API usage across all connected services.
For your API client development, respect the 1.44 requests/second ENIAC limit, handle Turkish error codes properly, cache aggressively, and plan around tea breaks. The patterns and endpoint reference in this article provide everything you need to build robust API integrations with OpenMod servers.
