Tài liệu API XuniBOX
API chỉ đọc để lấy danh sách nhạc, nghệ sĩ và thể loại từ kho XuniBOX. Mỗi chủ đề dưới đây là một trang riêng, có đường dẫn riêng để bạn chia sẻ cho đội kỹ thuật.
Mã lỗi, body lỗi và cách retry
Mọi endpoint của XuniBOX dùng chung một khuôn body lỗi. Chỉ cần viết một hàm đọc lỗi duy nhất là xử lý được toàn bộ API — không endpoint nào trả về định dạng lỗi riêng.
1. Khuôn dạng body lỗi
Error envelope
// Mọi phản hồi lỗi (4xx/5xx) đều là JSON và luôn có khóa "error".
{
"error": "string" // Câu mô tả tiếng Việt, an toàn để log. LUÔN có.
// Một số lỗi 400 kèm thêm khóa phụ mô tả đúng chỗ sai:
// "invalid_ids": string[] – các UUID sai định dạng
// "details": { field: string[] } – lỗi theo từng trường (POST /webhooks)
// "allowed_sort" | "pagination" | "filters" – bảng giới hạn tham số
}
// Header đi kèm mọi phản hồi lỗi:
Content-Type: application/json; charset=utf-8
Cache-Control: no-store // lỗi không bao giờ được cache
Access-Control-Allow-Origin: *Quy tắc bất biến: body lỗi luôn là JSON, luôn có khóa
error kiểu chuỗi, luôn kèm Cache-Control: no-store và luôn có header CORS — kể cả lỗi 5xx. Các khóa phụ chỉ thêm vào, không bao giờ thay thế error, nên client cũ không vỡ khi chúng tôi bổ sung chi tiết.2. Bảng mã trạng thái
| HTTP | Ý nghĩa | Retry? | Nên làm gì |
|---|---|---|---|
| 400 | Tham số, UUID hoặc body JSON không hợp lệ | Không | Đọc khóa phụ trong body — nó chỉ đúng tham số sai. Sửa request rồi gọi lại. |
| 401 | Thiếu header X-API-Key | Không | Gắn key vào request. Chỉ áp dụng cho /me/usage, /webhooks, /tracks/:id/watermarked. |
| 403 | Key sai, bị vô hiệu hóa, hoặc URL media đã hết hạn | Không | Kiểm tra key trong cổng đối tác; nếu là media thì gọi lại API để lấy URL ký mới. |
| 404 | Không tìm thấy bài hát, nghệ sĩ, hoặc chưa có đoạn nghe thử | Không | Xóa khỏi cache/UI. Có thể admin đã gỡ nội dung. |
| 429 | Vượt hạn mức request của key | Có, sau Retry-After | Ngủ đúng số giây trong Retry-After rồi thử lại; thêm jitter nếu chạy song song. |
| 500 | Lỗi ngoài dự kiến ở phía XuniBOX | Có, 1–2 lần | Retry với backoff; còn lỗi thì gửi cho chúng tôi thời điểm và endpoint. |
| 502 | Lỗi khi đọc dữ liệu từ hạ tầng lưu trữ | Có | Retry với backoff, thường tự hết trong vài giây. |
| 503 | Dịch vụ đang bảo trì cấu hình | Có | Retry sau vài phút, không dồn dập. |
304 không phải lỗi: đó là phản hồi ETag hợp lệ, nghĩa là dữ liệu bạn đang cache vẫn mới. Xem quy ước chung.
3. Ví dụ từng loại lỗi
400 · UUID sai định dạng
HTTP/1.1 400 Bad Request
// GET /tracks/not-a-uuid
{ "error": "ID bài hát không hợp lệ." }400 · danh sách ids có phần tử sai
HTTP/1.1 400 Bad Request
// GET /tracks/by-ids?ids=<uuid>,abc
{
"error": "Danh sách ids chứa UUID không hợp lệ.",
"invalid_ids": ["abc"]
}400 · lỗi theo từng trường (details)
HTTP/1.1 400 Bad Request
// POST /webhooks với url sai
{
"error": "Dữ liệu không hợp lệ.",
"details": { "url": ["Phải là URL https hợp lệ."] }
}400 · bảng giới hạn tham số của /tracks
HTTP/1.1 400 Bad Request
{
"error": "Tham số truy vấn không hợp lệ.",
"allowed_sort": ["newest", "oldest", "popular", "most_liked", "title_asc", "title_desc"],
"pagination": {
"limit": "Số nguyên từ 1 đến 100; mặc định 20.",
"page": "Số nguyên từ 1 đến 5000; mặc định 1."
},
"filters": {
"title": "Tìm một phần tên bài hát, tối đa 160 ký tự.",
"genre": "Tìm một phần thể loại, tối đa 80 ký tự.",
"artist": "Tìm một phần tên nghệ sĩ, tối đa 120 ký tự.",
"artist_id": "UUID nghệ sĩ.",
"q": "Tìm đồng thời trong title, artist và genre, tối đa 100 ký tự."
}
}401 · thiếu API key
HTTP/1.1 401 Unauthorized
// GET /me/usage khi thiếu header X-API-Key
{ "error": "Thiếu API key." }403 · key không hợp lệ
HTTP/1.1 403 Forbidden
// Key sai, đã bị vô hiệu hóa, hoặc không có quyền cho endpoint này
{ "error": "API key không hợp lệ." }404 · không tìm thấy
HTTP/1.1 404 Not Found
{ "error": "Không tìm thấy bài hát." }429 · vượt hạn mức
HTTP/1.1 429 Too Many Requests
Retry-After: 37
{ "error": "Vượt giới hạn tần suất, vui lòng thử lại sau." }5xx · lỗi phía máy chủ
HTTP/1.1 502 Bad Gateway
{ "error": "Không thể tải kho nhạc." }
HTTP/1.1 503 Service Unavailable
{ "error": "XuniBOX API chưa được cấu hình." }
HTTP/1.1 500 Internal Server Error
{ "error": "Không thể tải bài hát." }4. Danh mục thông báo lỗi
Chuỗi trong error là văn bản ổn định — bạn có thể so khớp để hiển thị thông báo riêng, nhưng nên ưu tiên rẽ nhánh theo mã HTTP.
| Thông báo | HTTP | Nguyên nhân thường gặp |
|---|---|---|
| ID bài hát không hợp lệ. | 400 | Tham số :id không phải UUID v4. |
| ID nghệ sĩ không hợp lệ. | 400 | Tham số :id của /artists/:id/tracks không phải UUID. |
| Tham số truy vấn không hợp lệ. | 400 | limit/page/sort/filter vượt giới hạn — body kèm bảng giới hạn. |
| Tham số tìm kiếm không hợp lệ. | 400 | q rỗng hoặc quá dài ở /search, /search/suggest. |
| Tham số ids là bắt buộc và phải chứa ít nhất một UUID. | 400 | Gọi /tracks/by-ids hoặc /tracks/waveforms mà thiếu ids. |
| Danh sách ids chứa UUID không hợp lệ. | 400 | Có phần tử sai định dạng — xem invalid_ids. |
| Mỗi yêu cầu chỉ được chứa tối đa N ID. | 400 | Chia nhỏ batch ids. |
| Body JSON không hợp lệ. | 400 | POST /webhooks gửi body không parse được. |
| Dữ liệu không hợp lệ. | 400 | POST /webhooks sai trường — xem details. |
| Thiếu tham số id. | 400 | DELETE /webhooks không truyền ?id=. |
| Mỗi đối tác chỉ đăng ký tối đa 5 webhook. | 400 | Xóa bớt webhook cũ trước khi tạo mới. |
| Thiếu API key. | 401 | Không gửi header X-API-Key. |
| Cần X-API-Key hợp lệ để nhận bản audio có watermark. | 401 | /tracks/:id/watermarked luôn cần key. |
| API key không hợp lệ. | 403 | Key sai, đã thu hồi, hoặc tài khoản đối tác bị khóa. |
| Tạm khoá do quá nhiều lần dùng sai khoá API. Vui lòng thử lại sau. | 403 | Quá nhiều lần gửi key sai từ cùng một IP. |
| Không tìm thấy bài hát. / Không tìm thấy nghệ sĩ. | 404 | Nội dung đã bị gỡ hoặc ID không tồn tại. |
| Bài hát chưa có đoạn nghe thử. | 404 | Track chưa được cắt preview — bỏ qua bài này. |
| Vượt giới hạn tần suất, vui lòng thử lại sau. | 429 | Chạm trần theo phút hoặc theo ngày của key. |
| Không thể tải … (kho nhạc, bài hát, sóng âm, …) | 500 / 502 | Sự cố tạm thời khi đọc dữ liệu — retry. |
| XuniBOX API chưa được cấu hình. | 503 | Đang bảo trì cấu hình phía chúng tôi — retry sau vài phút. |
5. Xử lý lỗi ở phía bạn
Một lớp lỗi dùng chung cho mọi endpoint
class XuniBoxError extends Error {
constructor(status, body, requestPath) {
super(body?.error ?? `XuniBOX ${status}`);
this.name = "XuniBoxError";
this.status = status;
this.path = requestPath;
this.details = body?.details ?? null;
this.invalidIds = body?.invalid_ids ?? null;
this.retryable = status === 429 || status >= 500;
this.retryAfter = null;
}
}
export async function xunibox(path, init = {}) {
const res = await fetch(`https://xunibox.vn/api/public${path}`, {
...init,
headers: { Accept: "application/json", ...init.headers }
});
if (res.ok) return res.status === 204 ? null : res.json();
// Body lỗi LUÔN là JSON, nhưng vẫn phòng trường hợp proxy trả HTML.
const body = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
const err = new XuniBoxError(res.status, body, path);
err.retryAfter = Number(res.headers.get("Retry-After")) || null;
throw err;
}Helper retry có backoff và Retry-After
async function xuniboxFetch(path, { retries = 3 } = {}) {
for (let attempt = 0; attempt <= retries; attempt++) {
const res = await fetch(`https://xunibox.vn/api/public${path}`, {
headers: { "X-API-Key": process.env.XUNIBOX_API_KEY, Accept: "application/json" }
});
if (res.ok) return res.json();
// 4xx là lỗi phía bạn — retry cũng vô ích, trừ 429.
if (res.status !== 429 && res.status < 500) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error ?? `XuniBOX ${res.status}`);
}
if (attempt === retries) throw new Error(`XuniBOX ${res.status} sau ${retries} lần thử`);
const retryAfter = Number(res.headers.get("Retry-After"));
const waitMs = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: 2 ** attempt * 500 + Math.random() * 250; // backoff + jitter
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}Khi báo lỗi cho chúng tôi, gửi kèm: endpoint đầy đủ, mã HTTP, nguyên văn body lỗi, thời điểm (kèm múi giờ) và 8 ký tự đầu của API key. Đừng gửi toàn bộ key. Log nguyên body thay vì chỉ log mã HTTP — riêng lỗi 400 của
/tracks đã kèm bảng giới hạn nên thường đọc là biết sai chỗ nào.Sẵn sàng tích hợp?
Gửi thông tin sản phẩm để admin XuniBOX xét duyệt và cấp API key.