Response envelope#
Every JSON answer has the same outer shape. code is a string: "0" means success.{
"code": "0",
"description": "Success.",
"traceID": "0f8e2c1a-6b3d-4e5f-9a7b-1c2d3e4f5a6b",
"data": {
"...": "endpoint payload"
}
}
| Field | Type | Notes |
|---|
code | string | "0" or an error code (Appendix → Error Codes) |
description | string | Thai when Accept-Language resolves to th, otherwise English |
traceID | string | Quote it when reporting a problem |
data | object / array | The payload. Omitted when the call returns nothing |
validationError | array / null | Errors only: the fields that failed validation, otherwise null |
Lists with a total (Player History) add a top-level total. Paged lists (Lotto ticket and result inquiries) add page, limit, total, totalPage, hasNext, hasPrev. All of these keys are omitted when their value is 0 or false — treat a missing key as 0 / false.Errors#
The HTTP status comes from the error code (Appendix → Error Codes); success is always 200.{
"code": "99911",
"description": "Bad request.",
"traceID": "0f8e2c1a-6b3d-4e5f-9a7b-1c2d3e4f5a6b",
"validationError": [
{
"field": "name",
"tag": "required",
"expectedValue": "",
"receivedValue": "",
"mapField": ""
}
]
}
Some rejections happen before the API reads the request and have no body at all — only the status line: 404 (no such route), 405 (wrong method), 413 (body over 1 MB), 431 (headers over 16 KB) and 400 (malformed HTTP). Under /c/g/* and /wallet/* the session check runs first, so an unknown path there without a valid token answers 401 JSON instead of a bare 404.Language#
Send the player's language as Accept-Language (e.g. th, or th-TH — the region is ignored).Error descriptions exist in English and Thai only: th gives Thai, anything else English.
Labels (game names, room names, bet-history game names, the current language) use the first Accept-Language tag that is an active language, else the language the agent sent at Start Game, else English. A label missing in that language falls back to English.
Languages (GET /languages) lists the languages to offer in a picker.
Numbers and times#
Money is either a decimal string ("965", "12.5") or a JSON number (35, 100.5) depending on the endpoint — each field says which. Trailing zeros are trimmed, so "10000" and "10000.00" are the same amount.
Rates and multipliers are JSON numbers.
Times are RFC 3339 strings, e.g. 2026-09-30T08:07:31.482Z. Parse them; do not compare them as text. No response carries the server clock, so countdowns use the device clock.
Rate limits#
Each route has its own counter per client IP, in fixed 60-second windows. Over the limit the call answers 429 with code 99909 until the window ends. There is no Retry-After header. Players behind one NAT share the counter. The limit for each route is in its description and in Appendix → Rate Limits.Maintenance#
During a maintenance window the API answers 503 with code 99915 and no further detail (no start or end time).System maintenance blocks every route under /c/g/*, /g/lotto/* and /g/thai-hilo/*, and Player History.
Maintenance of one game is checked by every route under that game's own prefix (/g/lotto/*, /g/thai-hilo/*) and by Check Session and Get Balance of that game. The other /c/g/{gameCode}/* routes do not check it.
Demo Login, Wallet Status, Languages and Ping never answer 503.
Modified at 2026-09-30 13:23:45