REST API · v1

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 và cách retry

HTTPÝ nghĩaNên làm gì
400Tham số hoặc UUID không hợp lệĐọc body — nó liệt kê đúng giới hạn của từng tham số. Không retry.
401Thiếu header X-API-KeyGắn key vào request.
403Key sai, bị vô hiệu hóa, hoặc URL media hết hạnKiểm tra key; nếu là media thì gọi lại API lấy URL mới.
404Không tìm thấy bài hát hoặc nghệ sĩXóa khỏi cache/UI. Có thể admin đã gỡ nội dung.
429Vượt hạn mức requestChờ theo Retry-After rồi thử lại.
502Lỗi khi đọc dữ liệu từ hạ tầng lưu trữRetry với backoff, thường tự hết trong vài giây.
503Dịch vụ đang bảo trì cấu hìnhRetry sau vài phút.
500Lỗi ngoài dự kiếnRetry một lần; còn lỗi thì gửi cho chúng tôi thời điểm và endpoint.
Error response · mặc định
{
  "error": "API key không hợp lệ hoặc đã bị vô hiệu hóa."
}
Error response · 400 có chi tiết
HTTP/1.1 400 Bad Request

{
  "error": "Tham số truy vấn không hợp lệ.",
  "allowed_sort": ["newest", "oldest", "popular", "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ự."
  }
}
Helper retry có backoff
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));
  }
}
Body lỗi luôn có khóa error dạng chuỗi tiếng Việt. Log nguyên body khi debug — riêng 400 của /tracks còn 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.

Xin quyền tích hợp