Lỗi
Mọi lỗi đều được trả về theo một định dạng thống nhất - ok: false cộng với object error:
{ "ok": false, "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" }}code: mãUPPER_SNAKE_CASEmáy đọc được, ổn định, không đổi nếu không tăng phiên bản major. Hãy switch theo nó, không theomessage.message: giải thích cho người đọc, bằng tiếng Anh. Nội dung có thể được chỉnh sửa mà không tăng phiên bản.details: tùy chọn. VớiINVALID_INPUTđây là mảng{ path, message }cho từng trường không hợp lệ. VớiINVALID_FIELDmỗi phần tử có thêmexpectedchứa tên chính tắc của trường.docs: tùy chọn, liên kết tới một mục của trang này khi mã lỗi có giải thích chi tiết hơn.request_id: chỉ được thêm vào phản hồi5xxvàBAD_JSON; hãy trích dẫn nó khi liên hệ hỗ trợ.
Mã HTTP
Phần tiêu đề “Mã HTTP”| Status | Ý nghĩa |
|---|---|
200 OK | Thành công |
400 Bad Request | JSON hỏng, thiếu trường hoặc không qua được validation của Zod |
401 Unauthorized | Thiếu hoặc không hợp lệ X-Api-Key |
402 Payment Required | Cần gói / pack cao hơn, hoặc gói đăng ký không hoạt động |
403 Forbidden | Key hợp lệ, nhưng không có quyền (scope, origin, suspended) |
404 Not Found | Tài nguyên, key hoặc job không tồn tại |
422 Unprocessable Entity | Đầu vào hợp lệ, nhưng engine không tính được |
429 Too Many Requests | Vượt rate limit, hết credit hoặc chạm spend cap |
500 Internal Server Error | Lỗi nội bộ - từ phía chúng tôi, hãy trích dẫn request_id |
503 Service Unavailable | AI gateway / hàng đợi tạm thời không khả dụng |
Mã lỗi
Phần tiêu đề “Mã lỗi”Xác thực (401)
Phần tiêu đề “Xác thực (401)”| Mã | Ý nghĩa |
|---|---|
MISSING_API_KEY | Header X-Api-Key không được gửi |
INVALID_API_KEY | Key không tồn tại hoặc đã bị thu hồi |
MISSING_BEARER | Đã chọn scheme Authorization: Bearer, nhưng thiếu token |
WRONG_AUTH_METHOD | Gửi X-Api-Key tới /v1/me/* (chỉ nhận Bearer). Để kiểm tra key - GET /v1/auth/keys/me; để gọi API - bất kỳ /v1/* nào với X-Api-Key |
Quyền (403)
Phần tiêu đề “Quyền (403)”| Mã | Ý nghĩa |
|---|---|
INSUFFICIENT_SCOPE | Key không có quyền thực hiện hành động này |
DOMAIN_MISMATCH / FORBIDDEN_ORIGIN | Origin của request không nằm trong allow-list của key |
ENDPOINT_NOT_IN_SCOPE | Key bị giới hạn bởi danh sách endpoint (allowed_endpoints), và đường dẫn này không có trong danh sách. details nêu tên danh sách |
KEY_SUSPENDED | Key đã bị tạm ngưng |
Thanh toán (402)
Phần tiêu đề “Thanh toán (402)”| Mã | Ý nghĩa |
|---|---|
PLAN_UPGRADE_REQUIRED | Endpoint cần gói cao hơn hoặc pack add-on |
PLAN_PACK_MISMATCH | Endpoint thuộc một pack mà key không có |
SUBSCRIPTION_EXPIRED | Kỳ đã thanh toán kết thúc hơn 3 ngày trước. Trong 3 ngày đầu sau khi hết hạn, request vẫn được phục vụ theo gói và mang header X-Subscription-Grace-Ends. details chứa expired_at, grace_ended_at và renew_url |
Rate limiting và credit (429)
Phần tiêu đề “Rate limiting và credit (429)”| Mã | Ý nghĩa |
|---|---|
RATE_LIMIT | Vượt giới hạn RPM. Phản hồi chứa header Retry-After: <sec> |
CREDITS_EXHAUSTED | Đã dùng hết ngân sách credit của tháng |
SPEND_CAP_REACHED | Đã chạm giới hạn chi tiêu cho các endpoint AI |
KEY_BUDGET_EXHAUSTED | Key đã dùng hết ngân sách riêng (credits_cap_cycle). Tài khoản khi đó vẫn có thể còn credit: giới hạn chỉ áp dụng cho chính key này |
Validation (400)
Phần tiêu đề “Validation (400)”| Mã | Ý nghĩa |
|---|---|
INVALID_INPUT | Một hoặc nhiều trường không qua validation của Zod. details - mảng { path, message } |
INVALID_FIELD | Body chứa tên trường viết tắt thay cho tên chính tắc. Xem bên dưới |
MISSING_FIELDS | Thiếu trường bắt buộc |
BAD_JSON | JSON hỏng trong body của request |
Ví dụ 400:
{ "ok": false, "error": { "code": "INVALID_INPUT", "message": "Validation failed: date: Invalid input: expected string, received undefined", "details": [ { "path": "date", "message": "Invalid input: expected string, received undefined" }, { "path": "time", "message": "Invalid input: expected string, received undefined" } ] }}INVALID_FIELD
Phần tiêu đề “INVALID_FIELD”Các endpoint chart chỉ nhận tên trường đầy đủ. Dạng viết tắt bị từ chối với 400, chứ không bị đọc một cách lặng lẽ:
| Đã gửi | Trường chính tắc | Giá trị mong đợi |
|---|---|---|
lat | latitude | độ thập phân, phía bắc là dương |
lng, lon, long | longitude | độ thập phân, phía đông là dương |
tz với số, tzOffset, utcOffset, timezone_offset | timezoneOffset | số giờ lệch so với UTC, ví dụ 5.75 |
tz với tên, timeZone, time_zone | timezone | tên múi giờ IANA, ví dụ Europe/Kyiv, hoặc auto |
Phản hồi liệt kê tất cả các trường tìm thấy trong một lần, theo thứ tự trong body, nên việc đổi tên làm được trong một lượt:
{ "ok": false, "error": { "code": "INVALID_FIELD", "message": "Unsupported fields: \"tz\", \"lat\", \"lng\". Rename them to \"timezoneOffset\", \"latitude\", \"longitude\". See details for what each one expects.", "details": [ { "path": "tz", "expected": "timezoneOffset", "message": "numeric hours from UTC, e.g. 5.75; a zone name goes in timezone" }, { "path": "lat", "expected": "latitude", "message": "decimal degrees, north positive" }, { "path": "lng", "expected": "longitude", "message": "decimal degrees, east positive" } ], "docs": "https://api.astroway.info/en/errors/#invalid_field" }}path chỉ ra đường dẫn đầy đủ, nên trong synastry thấy rõ đó là bản đồ của ai: chart1.lat, chứ không chỉ lat.
Cùng mã này được trả về khi không phân tích được timezone: giá trị rỗng, chữ viết tắt kiểu EST (đây là -5 cố định quanh năm, nên New York vào tháng Bảy sẽ lệch một giờ), tên không có trong cơ sở dữ liệu múi giờ, độ lệch viết dạng văn bản như +03:00, hoặc auto mà không có latitude và longitude. Thông báo gợi ý nên gửi gì thay thế, còn path chỉ tới object, ví dụ chart2.timezone.
Có hai điều cần lưu ý.
Đây không phải là deprecation. Tên viết tắt chưa bao giờ nằm trong hợp đồng và chưa từng xuất hiện trong /openapi.json như các trường body. Trước 2026-08-01 chúng không hoạt động mà bị bỏ qua một cách lặng lẽ: tọa độ có giá trị mặc định là 0, nên bản đồ được tính cho 0°N 0°E theo UTC. Cung mọc, các nhà và mọi thứ suy ra từ chúng đều sai, còn phản hồi thì không báo gì về điều đó. Vì vậy dạng viết tắt sẽ không được chấp nhận trở lại: hai tên cho một trường là mãi mãi, và tz lại còn mơ hồ (số hay tên múi giờ).
point lồng bên trong là chuyện khác. Trong /v1/acg-zones, /v1/acg/by-category, /v1/ccg-analysis và /v1/eclipse-analysis, object point: { lat, lng } là các tên trường đã khai báo, và chúng được chấp nhận.
Thiếu tọa độ: 400 từ 2026-11-09
Phần tiêu đề “Thiếu tọa độ: 400 từ 2026-11-09”Tên trường đúng mới chỉ là một nửa. Body không có latitude và longitude cũng từng cho 200, vì tọa độ có giá trị mặc định là 0, và phản hồi mô tả điểm 0°N 0°E.
Trước 2026-11-09 request như vậy vẫn hoạt động, nhưng phản hồi mang Deprecation: true, Sunset, Warning và trường _warning trong body. Từ 2026-11-09 đó là 400 INVALID_INPUT với latitude is required, in decimal degrees.
latitude: 0, longitude: 0 tường minh vẫn hợp lệ: vấn đề nằm ở trường bị thiếu, không phải ở giá trị. timezoneOffset giữ mặc định là 0, vì 0 ở đó nghĩa là UTC. city không thay thế được tọa độ: không ai geocode nó cả. Việc kiểm tra chỉ áp dụng cho các object đồng thời mang date và time.
Lỗi tính toán (422)
Phần tiêu đề “Lỗi tính toán (422)”| Mã | Ý nghĩa |
|---|---|
CALCULATION_ERROR | Đầu vào hợp lệ, nhưng engine không tính được (ví dụ, vĩ độ vùng cực với Placidus). Hãy thử Whole Sign / Equal hoặc một ngày khác. |
Lỗi máy chủ
Phần tiêu đề “Lỗi máy chủ”| Mã | HTTP | Ý nghĩa |
|---|---|---|
INTERNAL_ERROR | 500 | Lỗi không lường trước. Chúng tôi ghi log kèm stack trace, hãy trích dẫn request_id |
GATEWAY_UNAVAILABLE / AI_UNAVAILABLE / LLM_UNAVAILABLE | 503 | AI gateway không khả dụng. Retry với backoff lũy thừa |
QUEUE_UNAVAILABLE | 503 | Hàng đợi tác vụ nền không khả dụng |
Xử lý trong code phía client
Phần tiêu đề “Xử lý trong code phía client”type ApiError = { ok: false; error: { code: string; message: string; details?: unknown; request_id?: string; };};
async function chart(input: ChartInput) { const res = await fetch('https://api.astroway.info/v1/chart', { method: 'POST', headers: { 'X-Api-Key': process.env.ASTROWAY_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify(input), });
const body = await res.json(); if (!body.ok) { throw new AstrowayError(body.error.code, body.error.message, body.error.request_id); }
return body.data;}
class AstrowayError extends Error { constructor( public code: string, message: string, public requestId?: string, ) { super(`[${code}] ${message}${requestId ? ` (request_id=${requestId})` : ''}`); }}Hãy switch theo code, không theo message. Các SDK chính thức (@astroway/sdk, astroway) ném ra các lớp lỗi con có kiểu theo HTTP status (AuthenticationError, RateLimitError, BadRequestError …), nên bạn không phải tự viết code xử lý.
Yêu cầu hỗ trợ
Phần tiêu đề “Yêu cầu hỗ trợ”Khi liên hệ, hãy gửi tới support@astroway.info:
request_idtừ phản hồi (nếu là5xx)- Endpoint và JSON body rút gọn (không kèm bí mật)
- Hành vi mong đợi so với thực tế
- Múi giờ và thời điểm gần đúng của request (UTC)
Phản hồi - trong phạm vi SLA của gói bạn dùng (48 giờ với Starter, 24 giờ với Pro, riêng cho Enterprise).