# Errores > El formato de los errores, cada estado y código que responde la API, qué errores conviene reintentar y un gestor de errores en Node listo para copiar. Cuando una solicitud falla, la API responde con un estado 4xx o 5xx y un cuerpo JSON con un `code` estable. Decide según `code`. El texto de `error` es para personas y puede cambiar. ## El formato ```json { "error": "This API key doesn't have the earnings:read scope.", "code": "insufficient_scope", "scope": "earnings:read" } ``` | Campo | Siempre | Significado | | --- | --- | --- | | `error` | Sí | Qué ha fallado, en palabras. Se puede registrar o mostrar a un operador sin problema. | | `code` | Sí | Un código estable. Consulta las tablas de abajo. | | `field` | Con `invalid` | El parámetro o campo del cuerpo que falla. | | `scope` | Con `insufficient_scope` | El ámbito que necesita la clave. | Algunos errores añaden sus propios campos. Por ejemplo, `409 upload_incomplete` enumera las partes `missing` y `wrong_size`. ## Errores que puede responder cualquier endpoint | Estado | `code` | Significado | ¿Reintentar? | | --- | --- | --- | --- | | 400 | `invalid` | Un parámetro es incorrecto. `field` indica cuál. | No. Corrige la solicitud. | | 401 | `unauthenticated` | Falta la cabecera `Authorization: Bearer`. | No | | 401 | `invalid_key` | La clave no existe. | No | | 401 | `key_revoked` | La clave se revocó. Crea una nueva. | No | | 401 | `key_expired` | La clave ha caducado. Crea una nueva. | No | | 401 | `test_keys_retired` | Una clave `hm_test_`, retirada el 12 de octubre de 2026. Usa una clave `hm_live_`. | No | | 403 | `insufficient_scope` | A la clave le falta el ámbito que necesita este endpoint. `scope` indica cuál. | No | | 403 | `account_inactive` | Tu cuenta no está activa. | No | | 403 | `api_not_in_plan` | Tu plan no incluye la API. Se incluye en Enterprise y Custom. | No | | 404 | `not_found` | El endpoint no existe, o el recurso no es tuyo. | No | | 429 | `rate_limited` | Has superado tu límite por minuto con esta clave. | Sí, tras los segundos de `Retry-After` | | 500 | `server_error` | Algo ha fallado por nuestra parte. | Sí, con espera progresiva | `404 not_found` cubre tanto «no existe» como «pertenece a otra cuenta». La API nunca te revela que existe un lanzamiento de otra persona. ## Errores de endpoints concretos | Estado | `code` | Dónde | Significado | | --- | --- | --- | --- | | 409 | `exists` | `POST /members/invite` | Ya hay una cuenta con este correo. | | 413 | `too_large` | `POST /ddex/validate` | Supera el tamaño que admite una prueba. Entrégalo con `POST /ddex`. | | 413 | `too_large` | `POST /ddex` | Más de 20 GB (cuerpo en bruto) o de 512 MB (multipart). Usa una subida directa. | | 429 | `busy` | `POST /ddex/validate` | Demasiadas pruebas a la vez. Vuelve a intentarlo en breve. | | 409 | `upload_incomplete` | `POST /ddex/uploads/{id}/commit` | Faltan partes o su tamaño es incorrecto. Súbelas y vuelve a confirmar. | | 409 | `already_committed` | Subidas directas | Ya se confirmó. `status_url` apunta a la entrega. | | 409 | `commit_in_progress` | `POST /ddex/uploads/{id}/commit` | Hay otra confirmación de esta subida en curso. | | 400 | `invalid_url` | `POST /webhooks`, `PATCH /webhooks/{id}` | No es una URL https pública en el puerto 443. | | 409 | `too_many` | `POST /webhooks` | Ya tienes 20 endpoints. | | 409 | `endpoint_disabled` | Prueba y reintento de webhooks | El endpoint está desactivado. Actívalo primero. | Cada [página de la referencia](https://developer.hillsmusic.com/es/reference/releases/) enumera los errores de cada endpoint. [Ámbitos y errores](https://developer.hillsmusic.com/es/reference/scopes-and-errors/) recoge los comunes. ## Errores de validación Un `400 invalid` indica el primer problema que encontró: ```json { "error": "updated_since is an ISO date or time.", "code": "invalid", "field": "updated_since" } ``` Solo se informa de un campo cada vez. Corrígelo y vuelve a enviar. Si construyes un formulario sobre la API, muestra `error` junto al campo indicado en `field`. Con los archivos DDEX es distinto. Un lote DDEX con problemas no da un `400`. La prueba responde `200` con cada comprobación y un veredicto, y una entrega responde `202` e informa de los problemas por lanzamiento. Consulta [Entrega DDEX](https://developer.hillsmusic.com/es/guides/ddex/). ## Qué reintentar - **Reintenta** los `429` y `5xx`, y los errores de red, como los tiempos de espera agotados y las conexiones restablecidas. - **No reintentes** el resto de `4xx`. La misma solicitud recibe la misma respuesta. Corrige antes la solicitud, la clave o sus ámbitos. Para un `429`, espera los segundos que indique `Retry-After`. Para un `5xx`, espera más tras cada intento (1 s, 2 s, 4 s…) y desiste tras unos pocos. Consulta [Límites de solicitudes](https://developer.hillsmusic.com/es/guides/rate-limits/). Las lecturas se pueden reintentar sin riesgo. Piénsalo antes de reintentar una escritura. Si reintentas un `POST /ddex` tras perder la respuesta, el lote puede quedar en cola dos veces. Búscalo antes en `GET /ddex`. ## Un gestor de errores en Node ```js export class HillsError extends Error { constructor(status, body, headers) { super(body.error ?? `HTTP ${status}`); this.status = status; this.code = body.code; this.field = body.field; this.scope = body.scope; this.retryAfter = Number(headers.get("Retry-After")) || null; } } const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); export async function hills(path, init = {}, tries = 4) { for (let i = 1; ; i++) { const res = await fetch("https://api.hillsmusic.com/v1" + path, { ...init, headers: { ...init.headers, Authorization: `Bearer ${process.env.HILLS_API_KEY}` }, }); const body = await res.json().catch(() => ({})); if (res.ok) return body; const err = new HillsError(res.status, body, res.headers); const retryable = res.status === 429 || res.status >= 500; if (!retryable || i >= tries) throw err; await sleep(err.retryAfter ? err.retryAfter * 1000 : 1000 * 2 ** (i - 1)); } } try { await hills("/earnings"); } catch (err) { if (err.code === "insufficient_scope") console.error(`Add ${err.scope} to this key.`); else if (err.code === "api_not_in_plan") console.error("The plan doesn't include the API."); else throw err; } ``` Con curl, añade `-w '%{http_code}'` para ver el estado, o `-i` para ver las cabeceras: ```bash curl -i "https://api.hillsmusic.com/v1/releases/999999" \ -H "Authorization: Bearer $HILLS_API_KEY" # HTTP/2 404 # {"error":"…","code":"not_found"} ``` Fuente: https://developer.hillsmusic.com/es/guides/errors/