API de ClouraAPI da Cloura
Cloura ofrece una API REST de solo lectura para llevar sus hallazgos priorizados a sus propias herramientas —SIEM, tableros, data warehouse— sin entrar a la app.
A Cloura oferece uma API REST somente leitura para levar seus achados priorizados às suas próprias ferramentas —SIEM, painéis, data warehouse— sem entrar no app.
En todos los planes (incluido Free): ver la documentaciónEm todos os planos (incluindo Free): ver a documentação
Con cualquier cuenta se explora la referencia de la API —sus endpoints, parámetros y respuestas—. Se encuentra en la app, en Ajustes → Referencia de la API. Tiene su propia fila, sin candado de plan: es documentación, no una credencial.
Com qualquer conta explora-se a referência da API —seus endpoints, parâmetros e respostas—. Você a encontra no app, em Configurações → Referência da API. Tem sua própria linha, sem cadeado de plano: é documentação, não uma credencial.
Se abre la especificación OpenAPI (Redoc) con el contrato completo. Es útil para entender qué ofrece la API antes de integrarla. Para usarla con sus datos, siga con la siguiente sección.
Abre a especificação OpenAPI (Redoc) com o contrato completo. É útil para entender o que a API oferece antes de integrá-la. Para usá-la com seus dados, continue na próxima seção.
Desde el plan Pro: usar la APIA partir do plano Pro: usar a API
1. Genere sus credenciales1. Gere suas credenciais
En Ajustes → Credenciales de API (solo Admin), pulse Generar credencial. Póngale un nombre para reconocerla (por ejemplo, «SIEM producción»).
Em Configurações → Credenciais de API (apenas Admin), clique em Gerar credencial. Dê um nome para reconhecê-la (por exemplo, «SIEM produção»).
Cloura le muestra el Client ID, el Client Secret y el Token endpoint. El secreto se muestra una sola vez: cópielo y guárdelo en un gestor seguro. Si se pierde, revoque esa credencial y cree otra.
A Cloura mostra o Client ID, o Client Secret e o Token endpoint. O segredo é exibido apenas uma vez: copie e guarde num cofre seguro. Se for perdido, revogue essa credencial e crie outra.
Se pueden tener varias credenciales y revocar cualquiera en cualquier momento: deja de funcionar al instante. Las credenciales son de solo lectura y solo ven su organización.
É possível ter várias credenciais e revogar qualquer uma quando quiser: para de funcionar na hora. As credenciais são somente leitura e só veem a sua organização.
2. Obtenga un token2. Obtenha um token
La API usa OAuth2 client-credentials. Pida un token al Token endpoint que
Cloura entregó al crear la credencial, usando su Client ID y su Client Secret. La respuesta trae el token y
expires_in en segundos.
A API usa OAuth2 client-credentials. Peça um token ao Token endpoint que a
Cloura entregou ao criar a credencial, usando seu Client ID e seu Client Secret. A resposta traz o token e
expires_in em segundos.
https://<dominio>.auth.<región>.amazoncognito.com/oauth2/token. Cópiela tal cual de la pantalla de
creación de la credencial: no la escriba de memoria.
https://<domínio>.auth.<região>.amazoncognito.com/oauth2/token. Copie-a tal como aparece na tela de
criação da credencial: não a escreva de memória.
TOKEN_ENDPOINT="https://<dominio>.auth.<region>.amazoncognito.com/oauth2/token" # el que te muestra Cloura
CLIENT_ID="..."
CLIENT_SECRET="..."
RESP=$(curl -s -X POST "$TOKEN_ENDPOINT" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials")
ACCESS_TOKEN=$(echo "$RESP" | jq -r .access_token)
echo "$RESP" | jq '{expires_in, token_type}'
# → { "expires_in": 3600, "token_type": "Bearer" }
TOKEN_ENDPOINT="https://<dominio>.auth.<region>.amazoncognito.com/oauth2/token" # o que o Cloura mostra
CLIENT_ID="..."
CLIENT_SECRET="..."
RESP=$(curl -s -X POST "$TOKEN_ENDPOINT" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials")
ACCESS_TOKEN=$(echo "$RESP" | jq -r .access_token)
echo "$RESP" | jq '{expires_in, token_type}'
# → { "expires_in": 3600, "token_type": "Bearer" }
El token se acota a un scope concreto añadiendo
&scope=cloura-api/findings.read al cuerpo. Los scopes disponibles son
cloura-api/findings.read, cloura-api/analyses.read y
cloura-api/accounts.read. Si no indica ninguno, la credencial usa los que tenga asignados.
O token se restringe a um scope específico adicionando
&scope=cloura-api/findings.read ao corpo. Os scopes disponíveis são
cloura-api/findings.read, cloura-api/analyses.read e
cloura-api/accounts.read. Se nenhum for indicado, a credencial usa os que tiver atribuídos.
Renovar el token sin pedir uno en cada llamadaRenovar o token sem pedir um a cada chamada
El token dura una hora. Pedir uno nuevo en cada request es lento e innecesario: guárdelo en memoria y renuévelo solo cuando esté por vencer. Este patrón, con un margen de 60 segundos, sirve para cualquier integración:
O token dura uma hora. Pedir um novo a cada request é lento e desnecessário: guarde-o em memória e renove-o só quando estiver perto de vencer. Este padrão, com uma margem de 60 segundos, serve para qualquer integração:
import os, time, requests
TOKEN_ENDPOINT = os.environ["CLOURA_TOKEN_ENDPOINT"] # el que te muestra Cloura
CID, SECRET = os.environ["CLOURA_CLIENT_ID"], os.environ["CLOURA_CLIENT_SECRET"]
_cache = {"token": None, "exp": 0}
def token() -> str:
"""Devuelve un access token válido, renovándolo 60 s antes de que expire."""
if _cache["token"] and time.time() < _cache["exp"] - 60:
return _cache["token"]
r = requests.post(TOKEN_ENDPOINT, auth=(CID, SECRET),
data={"grant_type": "client_credentials"}, timeout=10)
r.raise_for_status()
data = r.json()
_cache["token"] = data["access_token"]
_cache["exp"] = time.time() + int(data.get("expires_in", 3600))
return _cache["token"]
import os, time, requests
TOKEN_ENDPOINT = os.environ["CLOURA_TOKEN_ENDPOINT"] # o que o Cloura mostra
CID, SECRET = os.environ["CLOURA_CLIENT_ID"], os.environ["CLOURA_CLIENT_SECRET"]
_cache = {"token": None, "exp": 0}
def token() -> str:
"""Devuelve un access token válido, renovándolo 60 s antes de que expire."""
if _cache["token"] and time.time() < _cache["exp"] - 60:
return _cache["token"]
r = requests.post(TOKEN_ENDPOINT, auth=(CID, SECRET),
data={"grant_type": "client_credentials"}, timeout=10)
r.raise_for_status()
data = r.json()
_cache["token"] = data["access_token"]
_cache["exp"] = time.time() + int(data.get("expires_in", 3600))
return _cache["token"]
Si una llamada responde 401, el token venció o la credencial fue revocada: pida uno nuevo
y, si vuelve a fallar, revise en la app que la credencial siga activa.
Se uma chamada responder 401, o token venceu ou a credencial foi revogada: peça um novo e,
se falhar de novo, verifique no app se a credencial continua ativa.
3. Llame a la API3. Chame a API
La URL base es https://api.cloura.ai. Adjunte el token en la cabecera
Authorization en cada llamada:
A URL base é https://api.cloura.ai. Anexe o token no cabeçalho
Authorization em cada chamada:
curl -s "https://api.cloura.ai/api/v1/analyses" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq
EndpointsEndpoints
Todos cuelgan de https://api.cloura.ai y son de solo lectura. El contrato
/api/v1 es estable: si algún día hubiera un cambio incompatible, viviría en /api/v2.
Todos partem de https://api.cloura.ai e são somente leitura. O contrato
/api/v1 é estável: se um dia houver mudança incompatível, ela viverá em /api/v2.
| Endpoint | Parámetros | Devuelve |
|---|---|---|
GET /api/v1/me | — | Su identidad de API: organización, plan y alcance de la credencial. |
GET /api/v1/accounts | — | {"accounts": [...]}. Cada cuenta trae su provider (aws, azure o gcp). |
GET /api/v1/analyses | accountId, limit, offset | {"analyses": [...], "count", "returned"}. count es el total tras filtros; returned, los de esta página. |
GET /api/v1/analyses/{id} | — | Un análisis: score FinOps, ahorro estimado, estado y cobertura. 404 si no es de su organización. |
GET /api/v1/analyses/{id}/findings | category, severity | {"findings": [...], "count", "entitledPacks", "locked"}. 404 si el análisis no es de su organización. |
| Endpoint | Parâmetros | Retorna |
|---|---|---|
GET /api/v1/me | — | Sua identidade de API: organização, plano e escopo da credencial. |
GET /api/v1/accounts | — | {"accounts": [...]}. Cada conta traz seu provider (aws, azure ou gcp). |
GET /api/v1/analyses | accountId, limit, offset | {"analyses": [...], "count", "returned"}. count é o total após filtros; returned, os desta página. |
GET /api/v1/analyses/{id} | — | Uma análise: score FinOps, economia estimada, status e cobertura. 404 se não for da sua organização. |
GET /api/v1/analyses/{id}/findings | category, severity | {"findings": [...], "count", "entitledPacks", "locked"}. 404 se a análise não for sua. |
Ejemplo con filtros: los hallazgos críticos de costo de un análisis, en una sola llamada.
Exemplo com filtros: os achados críticos de custo de uma análise, numa única chamada.
curl -s "https://api.cloura.ai/api/v1/analyses/$ID/findings?severity=critical&category=cost" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.count'
Referencia OpenAPI (requiere su sesión de la app)Referência OpenAPI (requer sua sessão do app)
Además del contrato de datos, hay dos rutas de documentación. No son públicas y no se abren con el token M2M: usan su sesión de usuario, por eso lo natural es entrar desde la app.
Além do contrato de dados, há duas rotas de documentação. Não são públicas e não abrem com o token M2M: usam sua sessão de usuário, por isso o natural é entrar pelo app.
GET /api/v1/openapi.json— la especificación OpenAPI completa.GET /api/v1/docs— el portal de referencia navegable (Redoc).
GET /api/v1/openapi.json— a especificação OpenAPI completa.GET /api/v1/docs— o portal de referência navegável (Redoc).
ErroresErros
401— token ausente, vencido o de una credencial revocada. Pida uno nuevo.403— su plan no incluye la API. Las credenciales están disponibles desde Pro.404— el recurso no existe o no pertenece a su organización. Cloura no distingue ambos casos a propósito: así una credencial no puede averiguar qué existe en otra organización.
401— token ausente, vencido ou de uma credencial revogada. Peça um novo.403— seu plano não inclui a API. As credenciais estão disponíveis a partir do Pro.404— o recurso não existe ou não pertence à sua organização. A Cloura não distingue os dois casos de propósito: assim uma credencial não consegue descobrir o que existe em outra organização.
Ejemplos útilesExemplos úteis
Hallazgos críticos de su último análisis, ordenados por ahorroAchados críticos da sua última análise, ordenados por economia
BASE="https://api.cloura.ai/api/v1"
AUTH="Authorization: Bearer $ACCESS_TOKEN"
# id del análisis más reciente
ID=$(curl -s "$BASE/analyses" -H "$AUTH" | jq -r '.analyses | sort_by(.createdAt) | last | .analysisId')
# sus hallazgos críticos, del mayor ahorro al menor
curl -s "$BASE/analyses/$ID/findings" -H "$AUTH" | jq '
[ .findings[] | select(.severity == "critical") ]
| sort_by(.monthlySavingsEstimate) | reverse
| .[] | { title, resourceId, severity, monthlySavingsEstimate }'
BASE="https://api.cloura.ai/api/v1"
AUTH="Authorization: Bearer $ACCESS_TOKEN"
# id da análise mais recente
ID=$(curl -s "$BASE/analyses" -H "$AUTH" | jq -r '.analyses | sort_by(.createdAt) | last | .analysisId')
# seus achados críticos, da maior economia para a menor
curl -s "$BASE/analyses/$ID/findings" -H "$AUTH" | jq '
[ .findings[] | select(.severity == "critical") ]
| sort_by(.monthlySavingsEstimate) | reverse
| .[] | { title, resourceId, severity, monthlySavingsEstimate }'
Exportar todos los hallazgos a JSON (para SIEM / data warehouse)Exportar todos os achados para JSON (para SIEM / data warehouse)
Ejemplo en Python: recorre sus análisis y vuelca cada hallazgo a un archivo.
Exemplo em Python: percorre suas análises e grava cada achado num arquivo.
import os, json, requests
BASE = "https://api.cloura.ai/api/v1"
TOKEN_ENDPOINT = os.environ["CLOURA_TOKEN_ENDPOINT"] # el que te muestra Cloura
CID, SECRET = os.environ["CLOURA_CLIENT_ID"], os.environ["CLOURA_CLIENT_SECRET"]
# 1) token
tok = requests.post(TOKEN_ENDPOINT, auth=(CID, SECRET),
data={"grant_type": "client_credentials"}).json()["access_token"]
H = {"Authorization": f"Bearer {tok}"}
# 2) recorrer análisis y juntar hallazgos
rows = []
for a in requests.get(f"{BASE}/analyses", headers=H).json()["analyses"]:
aid = a["analysisId"]
fs = requests.get(f"{BASE}/analyses/{aid}/findings", headers=H).json()["findings"]
for f in fs:
rows.append({"analysisId": aid, **f})
# 3) volcar a un archivo para el SIEM / warehouse
with open("cloura_hallazgos.json", "w", encoding="utf-8") as out:
json.dump(rows, out, ensure_ascii=False, indent=2)
print(f"{len(rows)} hallazgos exportados")
import os, json, requests
BASE = "https://api.cloura.ai/api/v1"
TOKEN_ENDPOINT = os.environ["CLOURA_TOKEN_ENDPOINT"] # o que o Cloura mostra
CID, SECRET = os.environ["CLOURA_CLIENT_ID"], os.environ["CLOURA_CLIENT_SECRET"]
# 1) token
tok = requests.post(TOKEN_ENDPOINT, auth=(CID, SECRET),
data={"grant_type": "client_credentials"}).json()["access_token"]
H = {"Authorization": f"Bearer {tok}"}
# 2) percorrer análises e juntar achados
rows = []
for a in requests.get(f"{BASE}/analyses", headers=H).json()["analyses"]:
aid = a["analysisId"]
fs = requests.get(f"{BASE}/analyses/{aid}/findings", headers=H).json()["findings"]
for f in fs:
rows.append({"analysisId": aid, **f})
# 3) despejar num arquivo para o SIEM / warehouse
with open("cloura_achados.json", "w", encoding="utf-8") as out:
json.dump(rows, out, ensure_ascii=False, indent=2)
print(f"{len(rows)} achados exportados")
Buenas prácticas y seguridadBoas práticas e segurança
- Guarde el Client Secret en un gestor de secretos, nunca en el código ni en el repositorio.
- Rote las credenciales cada cierto tiempo y revoque las que ya no se usen.
- La API es de solo lectura y cada credencial solo ve su organización.
- Pagina con
limityoffsetsi hay muchos análisis. - El token expira (~1 h): reutilícelo mientras sea válido y pida otro al vencer.
- Guarde o Client Secret num cofre de segredos, nunca no código nem no repositório.
- Rotacione as credenciais periodicamente e revogue as que não usa mais.
- A API é somente leitura e cada credencial só vê a sua organização.
- Pagine com
limiteoffsetse houver muitas análises. - O token expira (~1 h): reutilize-o enquanto for válido e peça outro ao vencer.