Skip to content

Autenticación, límites y errores

Todos los endpoints piden una clave de API en la cabecera Authorization, con el esquema Bearer:

Authorization: Bearer sk-normatia-...

Las claves tienen la forma sk-normatia- seguida de 64 caracteres hexadecimales, y las de prueba, sk-normatia-test- seguida de otros 64. Se crean en normatia.com, en Mi cuenta, apartado Claves API, con el botón Nueva clave o, las de prueba, Clave de prueba. Normatia solo guarda una huella de la clave, así que la clave completa se ve una sola vez, al crearla: guárdala en un gestor de secretos y no la subas a ningún repositorio.

La API y el servidor MCP se activan por organización, cada uno por su lado (api_enabled y mcp_enabled en GET /v1/me): una organización puede tener los dos o solo uno, sea cual sea su plan. Para activarlos, escribe a hola@normatia.com. Una clave de prueba no necesita ninguno: la crea cualquier miembro y responde con el entorno de pruebas, solo en la API REST.

Cada clave pertenece a una de tus organizaciones, la que tiene activada la API o el MCP al crearla. En la API REST, una clave de una organización sin la API recibe 403 api_not_enabled; en el servidor MCP, sin el MCP, 403 mcp_not_enabled. Si dejas la organización, la clave deja de funcionar. Un cambio en la activación tarda hasta 5 minutos en notarse.

Cada clave tiene uno o varios de estos permisos, que se eligen al crearla:

PermisoQué deja hacerGasta
dataLas rutas de datos: parcelas, capas, cobertura, municipios, normativa, localización y proyectos. En el MCP, las herramientas de datosNo
askLas consultas y comprobaciones (POST /api/v2/ask, POST /api/v1/verify). En el MCP, ask y get_answerSí, el cupo de la organización
mcpEntrar en el servidor MCPNo

GET /v1/me, GET /v1/usage y las rutas de /v1/keys responden con cualquier permiso. Una clave sin el permiso que pide la ruta recibe 403 insufficient_scope, con el permiso que falta en required_scope y en la cabecera WWW-Authenticate. En el MCP, el asistente solo ve las herramientas que la clave puede usar.

Así, por ejemplo, a una aplicación que solo lee datos le basta una clave con data: lee todo, pero no puede gastar las consultas de tu organización. Una clave con permisos limitados solo puede revocarse a sí misma. Las claves de prueba tienen solo data, y las creadas antes de noviembre de 2026, los tres. GET /v1/me y GET /v1/keys dicen los permisos de cada una en scopes.

Una clave actúa como el usuario que la creó, pero solo dentro de su organización: ve los proyectos de esa organización y no los de otras a las que pertenezca el usuario. Un proyecto de otra organización responde como si no existiera (404 project_not_found), y si el proyecto activo en la web es de otra, las rutas que lo usan piden project_id (400 no_active_project). Las consultas y comprobaciones cuentan en el consumo de la organización de la clave y se rigen por su plan. No hay filtro por país: hoy el catálogo es de España.

La clave es personal: no la compartas con nadie de fuera de tu organización. Quien necesite usar la API, aunque sea para probarla, tiene que tener su propia cuenta. Si es de tu organización, invítalo a ella; si es de otra empresa, abre su cuenta y su organización y le activamos la API. Lo dicen las condiciones de uso de la API.

Las rutas se agrupan por lo que hacen. Cada clave tiene un contador por grupo, en ventanas de 60 segundos que empiezan con la primera petición.

GrupoRutasLímiteConsume cuota
DatosTodas las de /v1, y /api/v1/location/..., /api/v1/codes/..., /api/v1/projects y /api/v1/project/info60 peticiones por minutoNo
InteligenciaPOST /api/v2/ask, POST /api/v1/verifyEl límite por minuto de tu plan (intelligence_per_minute en GET /v1/me)Sí, 1 crédito por llamada
MCPTodas las peticiones al servidor MCP60 peticiones por minutoSolo la herramienta ask, que cuenta además en el grupo de inteligencia
Claves de pruebaLas rutas del entorno de pruebas30 peticiones por minutoNo

Cada respuesta lleva el estado del límite de su grupo en las cabeceras RateLimit-Limit, RateLimit-Remaining y RateLimit-Reset (segundos hasta la ventana siguiente). Al superarlo, la API responde 429 rate_limit_exceeded con la cabecera Retry-After y, en el cuerpo, el grupo, el límite y la cuenta actual. En /v1, en el formato RFC 9457 de Errores:

{
"type": "https://docs.normatia.com/api-reference/errores/#rate_limit_exceeded",
"title": "Demasiadas peticiones",
"status": 429,
"detail": "Has superado las 60 peticiones por minuto. Vuelve a intentarlo en 23 s.",
"code": "rate_limit_exceeded",
"group": "data",
"limit_rpm": 60,
"current": 61,
"request_id": "req_4f0c2a9e6b1d4c3e8a7f5b2d1e0c9a8b"
}

En /api/v1 y /api/v2, dentro de detail:

{
"detail": {
"error": "rate_limit_exceeded",
"message": "Has superado las 60 peticiones por minuto. Vuelve a intentarlo en 23 s.",
"group": "intelligence",
"limit_rpm": 60,
"current": 61
}
}

La cuota es la de la organización de la clave, la misma que gastáis al usar la web. Solo la consumen los endpoints del grupo de inteligencia:

  • POST /api/v2/ask consume 1 crédito por consulta, encadene el agente una búsqueda o seis. Si el proyecto no tiene ninguna normativa ni documento seleccionado, la respuesta lo explica y no consume crédito.
  • POST /api/v1/verify consume 1 crédito por verificación. Si el modelo de lenguaje falla o no da una respuesta válida, responde 502 llm_unavailable y no consume.

En los planes con cuota mensual, los créditos se renuevan el día 1 de cada mes. Cuando se agotan, los endpoints de inteligencia responden 429 quota_exceeded hasta la renovación; los de datos siguen funcionando.

{
"detail": {
"error": "quota_exceeded",
"message": "Tu organización ha agotado las consultas de este mes.",
"plan_name": "pro",
"remaining": 0,
"reset_at": "2026-12-01T00:00:00+00:00"
}
}

reset_at es null en los planes sin renovación mensual. Con renovación, la respuesta lleva Retry-After.

El consumo de cada clave y de su organización se consulta en GET /v1/usage.

Esta sección describe las rutas de /api/v1 y /api/v2. Las de /v1 usan el formato estándar RFC 9457, descrito en Errores. En las dos, cada respuesta lleva la cabecera X-Request-Id: indícala si nos escribes por un problema.

Los errores de la API llegan con el código HTTP correspondiente y un cuerpo JSON con un único campo, detail, que contiene un objeto con el código de error en error y, casi siempre, una explicación en message:

{
"detail": {
"error": "project_not_found",
"message": "El proyecto indicado no existe o no tienes acceso a él. Usa la lista de proyectos para ver los disponibles."
}
}

Tenlo en cuenta al leerlos:

  • El código está en detail.error y el texto en detail.message, no en la raíz del cuerpo.
  • Los 429 llevan además otros campos (ver arriba). El 401 invalid_api_key de una clave que no existe no trae message.
  • Los mensajes pueden estar en inglés o en español. Para decidir qué hacer, usa siempre detail.error, nunca el texto.

Hay dos casos con otra forma:

CasoCuerpo
Petición mal formada (422): falta un campo, sobra uno donde no se admiten o un valor no es válidodetail es una lista de errores de validación: {"detail": [{"type": "missing", "loc": ["body", "query"], "msg": "Field required", ...}]}
Ruta que no existe (404){"detail": "Not Found"}

Un cuerpo de más de 1 MB responde 413 payload_too_large, y un fallo interno responde 500 con {"detail": {"error": "internal_error", "message": "...", "request_id": "req_..."}}.

Estadodetail.errorCuándo
401missing_api_keyLa petición no lleva Authorization: Bearer
401invalid_api_keyLa clave no empieza por sk-normatia-, no existe o está revocada
403api_not_enabledLa organización de la clave no tiene activada la API, o el usuario ya no es miembro de ella
403mcp_not_enabledLo mismo en el servidor MCP: la organización no tiene activado el MCP
403insufficient_scopeLa clave no tiene el permiso que pide la ruta (data, ask o mcp). Trae required_scope
403not_available_in_sandboxUna clave de prueba en una ruta que no está en el entorno de pruebas (todas las de /api)
403test_key_not_supportedUna clave de prueba en el servidor MCP
400no_active_projectNo se indicó proyecto y el usuario no tiene proyecto activo (ask, project/info, verify)
400no_project_locationEl proyecto activo no tiene municipio (verify)
404not_foundEl geo_id, la normativa o la versión no existen (location, codes)
404location_not_foundEl geo_id no existe (verify)
404project_not_foundEl proyecto no existe o el usuario no tiene acceso
422geo_id_mismatchEl geo_id no es el del proyecto activo (verify)
422no_applicable_codesNinguna normativa aplica a ese municipio, o ninguna de las indicadas (verify)
429rate_limit_exceededSe superó el límite por minuto del grupo
429quota_exceededSe agotaron los créditos
502llm_unavailableEl modelo de lenguaje no respondió o no dio una respuesta válida (verify). No consume crédito
500internal_errorUn fallo nuestro. Escríbenos con el request_id

La lista completa, con los códigos de /v1, está en Errores.