Lỗi và giới hạn tần suất
Mọi endpoint báo lỗi theo cùng một cấu trúc, kèm một mã mà máy đọc được và một ID yêu cầu.
Phản hồi lỗi
Lệnh gọi thất bại trả về success: false. code ổn định và dành cho chương trình, error là một thông báo ngắn bằng tiếng Anh dành cho người đọc, còn request_id xác định lệnh gọi trong log của chúng tôi. param nêu tên tham số gây lỗi khi vấn đề nằm ở đầu vào.
Ví dụ thật
Đây là các phản hồi trực tiếp, lấy từ API tại thời điểm trang này được dựng.
{
"success": false,
"error": "coin required",
"code": "INVALID_PARAM",
"request_id": "cb-mwefrrfk-a2085edebc"
}{
"success": false,
"code": "NOT_FOUND",
"error": "unknown endpoint: GET /api/this-endpoint-does-not-exist",
"request_id": "cb-mwefrrfl-20f247bdbc"
}Mã lỗi
| Mã | HTTP | Ý nghĩa | Cách xử lý |
|---|---|---|---|
| INVALID_PARAM | 400 | Thiếu một tham số bắt buộc hoặc một giá trị không được chấp nhận. `param` nêu tên tham số khi xác định được. | Đừng thử lại với yêu cầu y nguyên; hãy sửa yêu cầu. |
| UNAUTHORIZED | 401 | Header `CB-API-KEY` đã được gửi nhưng khóa không tồn tại hoặc đã bị thu hồi. Dữ liệu công khai không cần khóa — hãy bỏ header hoặc dùng một khóa hợp lệ. | Đừng thử lại với yêu cầu y nguyên. |
| FORBIDDEN | 403 | Route này không thuộc API công khai (nội bộ hoặc chỉ dành cho tài khoản). | Đừng thử lại. |
| NOT_FOUND | 404 | Route không xác định, hoặc coin / sàn / quỹ được yêu cầu không tồn tại. | Đừng thử lại; hãy đối chiếu đường dẫn và các mã định danh với danh mục. |
| RATE_LIMITED | 429 | Số yêu cầu vượt quá giới hạn mỗi phút. `Retry-After` cho biết số giây cần chờ. | Chờ theo `Retry-After`, rồi thử lại có backoff. |
| INTERNAL | 500 | Lỗi máy chủ không mong đợi. Thông báo được cố ý viết chung chung; hãy trích dẫn `request_id` khi báo lỗi. | Thử lại một lần sau vài giây; nếu lỗi vẫn tiếp diễn, hãy báo cáo kèm ID yêu cầu. |
| UPSTREAM_FAILED | 503 | Nguồn dữ liệu đứng sau endpoint này không trả lời kịp thời. Không có số liệu quá hạn hay số liệu thay thế nào được trả về thay cho nó. | Thử lại có backoff; xem trang trạng thái. |
| UNAVAILABLE | 501 / 503 | Bộ dữ liệu tạm thời chưa sẵn sàng (cache đang được làm nóng) hoặc endpoint chưa có nguồn công khai hợp pháp. | Thử lại sau; danh mục có đánh dấu những endpoint không khả dụng theo thiết kế. |
| REQUEST_FAILED | 200 | Một số endpoint cũ hơn trả về HTTP 200 với `success: false`. Hãy luôn kiểm tra `success`, không chỉ trạng thái HTTP. | Tùy vào thông báo; hãy coi là một yêu cầu thất bại. |
ID yêu cầu
Mỗi phản hồi đều mang header X-Request-Id. Phản hồi lỗi lặp lại giá trị này trong phần nội dung dưới tên request_id. Phản hồi được phục vụ từ cache biên ngắn hạn lặp lại ID của yêu cầu đã tạo ra chúng, vì vậy ID giống nhau ở các lệnh gọi liên tiếp nghĩa là bạn đã nhận một bản sao từ cache.
Giới hạn tần suất
Giới hạn được tính theo phút, theo địa chỉ IP với lệnh gọi ẩn danh và theo khóa khi có header CB-API-KEY hợp lệ. Giới hạn là 1200 yêu cầu mỗi phút. Yêu cầu lấy icon dưới /api/coin-icon không bị tính.
Header giới hạn tần suất
| Tên | Mô tả |
|---|---|
| X-Request-Id | Mã định danh của yêu cầu này. Hãy trích dẫn nó khi báo lỗi. |
| X-RateLimit-Limit | Số yêu cầu được phép trong khung một phút hiện tại. |
| X-RateLimit-Remaining | Số yêu cầu còn lại trong khung hiện tại. |
| X-RateLimit-Reset | Thời điểm khung được đặt lại, tính bằng giây theo Unix time. |
| Retry-After | Gửi kèm HTTP 429: số giây cần chờ trước khi thử lại. |
| X-Cache | HIT, STALE hoặc MISS đối với cache phản hồi phía máy chủ. |
Khi bạn vượt giới hạn
API trả về HTTP 429 với code RATE_LIMITED. Hãy chờ đủ số giây nêu trong Retry-After rồi thử lại. Polling nhanh hơn TTL cache của endpoint chỉ nhận lại cùng một phản hồi đã cache, vì vậy hãy đặt chu kỳ polling khớp với TTL ghi trên từng trang tham chiếu.
Thử lại có backoff
async function getJson(url, tries = 4) {
for (let i = 0; i < tries; i++) {
const res = await fetch(url);
if (res.status === 429 || res.status >= 500) {
const wait = Number(res.headers.get("Retry-After")) || 2 ** i;
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
const body = await res.json();
if (body.success === false) {
throw new Error(body.code + ": " + body.error + " (" + body.request_id + ")");
}
return body;
}
throw new Error("gave up after " + tries + " attempts");
}Không có dữ liệu không phải là lỗi
Khi tham số hợp lệ nhưng không có nguồn công khai hợp pháp cho dữ liệu, lệnh gọi vẫn thành công với HTTP 200: các trường giá trị là null, meta.quality là UNAVAILABLE và coverage.unavailable nêu lý do. Hãy hiển thị bằng dấu gạch ngang. Đừng chuyển thành 0.
Dữ liệu quá hạn
Nếu một trình thu thập bị chậm, các endpoint tiếp tục phục vụ giá trị đã lưu gần nhất kèm meta.is_stale: true cho đến khi chúng vượt quá khung còn dùng được; sau đó chúng bị loại khỏi số liệu tổng hợp và được liệt kê trong coverage.stale_excluded.