Subes tu primera app a GitHub, contento. A los días te llega una factura de tu proveedor de IA que no cuadra con nada de lo que has hecho. El motivo casi siempre es el mismo: tu clave de API estaba dentro del código y alguien la ha usado a tu costa. Hay bots que rastrean los repositorios públicos buscando justo eso.
Una clave de API es como la tarjeta de crédito de tu cuenta de OpenAI, Claude o Gemini: quien la tiene, gasta a tu nombre. La buena noticia es que protegerla son 4 cosas y se hacen en diez minutos.
Las 4 cosas
Saca la clave del código
Guárdala en un archivo .env y haz que tu código la lea desde ahí.
Añade .env al .gitignore
Así el archivo con la clave nunca sale de tu ordenador.
Pon un límite de gasto
En OpenAI, en la Claude Console y en Google AI Studio. Si algo falla, la factura tiene techo.
Si ya la subiste, revócala
Anúlala y crea otra. Borrarla del código no sirve: queda en el historial de git.
1. Saca la clave del código y guárdala en un .env
Esto es lo que no tienes que hacer nunca:
// ❌ La clave escrita en el código: en cuanto haces push, es pública
const client = new OpenAI({ apiKey: "sk-proj-AbC123..." });
En su lugar, crea un archivo llamado .env en la raíz del proyecto con tus claves:
# .env — este archivo NO se sube a GitHub
OPENAI_API_KEY=sk-proj-tu-clave-aqui
ANTHROPIC_API_KEY=sk-ant-tu-clave-aqui
GEMINI_API_KEY=tu-clave-de-google-aqui
Una variable por línea, NOMBRE=valor, sin espacios alrededor del =. Y tu código lee la clave desde ahí.
En Node.js
Con Node 20.6 o superior no necesitas instalar nada: arranca tu script con --env-file y las variables aparecen en process.env.
node --env-file=.env index.js
// index.js
const apiKey = process.env.OPENAI_API_KEY;
Si prefieres la librería de siempre, dotenv:
npm install dotenv
import "dotenv/config"; // carga el .env en process.env
const apiKey = process.env.ANTHROPIC_API_KEY;
Si usas Next.js, no hace falta ninguna de las dos: carga los .env solo y tienes las variables en process.env en el servidor.
En Python
Instala python-dotenv:
pip install python-dotenv
import os
from dotenv import load_dotenv
load_dotenv() # lee el .env y lo mete en os.environ
api_key = os.environ["GEMINI_API_KEY"]
Consejo
Usa los nombres estándar (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY). Los SDK oficiales los buscan solos: muchas veces ni siquiera tienes que pasar la clave al crear el cliente.
Un truco que usan casi todos los proyectos: sube un archivo .env.example con los nombres de las variables pero sin valores. Así quien clone el repo sabe qué tiene que rellenar, sin ver tus claves.
# .env.example — este SÍ se sube
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GEMINI_API_KEY=
2. Añade .env al .gitignore
El .gitignore es un archivo de texto en la raíz del proyecto donde le dices a git qué archivos no tiene que subir nunca. Créalo (o ábrelo) y añade:
# .gitignore
.env
.env.*
!.env.example
.envy.env.*ignoran tu archivo de claves y sus variantes (.env.local,.env.production…).!.env.examplees la excepción: ese sí quieres subirlo.
Comprueba que no se ha colado
Antes de tu primer commit, haz estas comprobaciones en la terminal:
# 1. ¿Git ve el .env? No debería salir en la lista
git status
# 2. ¿Lo está ignorando? Debe responder con la línea del .gitignore que lo ignora
git check-ignore -v .env
# 3. ¿Queda alguna clave escrita en el código?
git grep -n --untracked -e "sk-" -e "AIza"
Si git check-ignore no devuelve nada, el .env no está ignorado: revisa el .gitignore. Y si git grep encuentra algo, sácalo al .env antes de seguir. (sk- es el principio de las claves de OpenAI y de Anthropic; AIza, el de las claves de Google.)
Atención
Si el .env ya estaba en un commit anterior, añadirlo al .gitignore no lo saca del repositorio. Para dejar de seguirlo usa git rm --cached .env y haz commit. Pero ojo: sigue en el historial. Si ese commit llegó a GitHub, salta al paso 4.
La red de seguridad de GitHub
GitHub tiene dos ayudas que conviene conocer:
- Escaneo de secretos (secret scanning): en repositorios públicos funciona automáticamente y gratis. Si encuentra una clave de un proveedor de su programa de socios, avisa al proveedor para que pueda actuar, por ejemplo revocándola.
- Protección de push (push protection): la de usuarios viene activada por defecto y te frena si intentas subir una clave a un repositorio público. En cada repositorio también la puedes activar en Settings → Advanced Security → Push protection.
Úsalas como red, no como cinturón. Según la tabla oficial de patrones, la protección de push bloquea las claves de OpenAI y de Anthropic, pero las de Google y Gemini solo generan un aviso: no te frenan el push.
3. Pon un límite de gasto en tu cuenta
Aunque hagas todo bien, un descuido puede pasar. El límite de gasto hace que, si alguien usa tu clave, la factura tenga techo. Ponlo hoy, en todas las cuentas que uses.
| Proveedor | Dónde se pone | Qué pasa al llegar |
|---|---|---|
| OpenAI | Settings → Organization limits → Spend → Edit spend limit. Activa «Enforce a hard limit». También hay límite por proyecto. | Con límite duro, las peticiones empiezan a fallar con un error 429. |
| Anthropic (Claude Console) | Página Billing (platform.claude.com/settings/billing) → Spend limits. Cada workspace puede tener su propio límite, siempre por debajo del de la organización. | Las peticiones fallan con un error que te dice cuándo vuelves a tener acceso. Puedes subir o quitar el límite antes. |
| Google AI Studio (Gemini) | Página Spend → Monthly spend cap → Edit spend cap, por proyecto. | Es un tope mensual por proyecto. Google lo marca como experimental y avisa de que puede haber excesos durante unos 10 minutos. |
Fuentes: límites de gasto de OpenAI, límites de gasto de la API de Claude, workspaces en la Claude Console y facturación de la API de Gemini. Los nombres de los menús pueden cambiar: si no los encuentras, busca «limits» o «spend» en la configuración de tu cuenta.
4. Si ya la subiste: revócala y crea otra
Aquí está el error que más dinero cuesta: ver la clave en GitHub, borrarla del código, hacer commit y pensar que ya está.
No está. Git guarda todas las versiones anteriores de cada archivo. La clave sigue en el historial y cualquiera puede verla mirando los commits antiguos. La documentación de GitHub lo dice claro: si se filtra un secreto, lo primero es revocarlo o rotarlo. Aunque reescribas el historial, el commit puede seguir accesible en clones, en forks y en vistas en caché de GitHub. Y si había bots mirando, puede que ya la tengan.
Así que la única solución segura es anular esa clave para que deje de funcionar y crear una nueva:
Qué hacer si tu clave ha estado en público
Revoca la clave
Bórrala o desactívala en la consola del proveedor. Desde ese momento ya no sirve para nada, la tenga quien la tenga.
Crea una nueva
Genera otra clave y guárdala solo en tu .env (y en las variables de entorno de tu hosting).
Actualiza tu app
Cambia la clave en local y donde tengas la app desplegada (Vercel, Railway, tu servidor…).
Revisa el consumo
Mira el uso y la facturación de los últimos días. Si ves algo raro, escribe al soporte del proveedor.
Dónde se revoca en cada uno:
- OpenAI: en platform.openai.com, página API keys: busca la clave y bórrala. Crea una nueva desde la misma página.
- Anthropic (Claude): en la página de API keys de la Claude Console, abre el menú de la clave y elige Delete API key. Luego Create key. Es lo que recomienda el centro de ayuda de Claude si sospechas que te la han robado.
- Google (Gemini): en la página de claves de Google AI Studio crea una clave nueva, ponla en tu app y, cuando funcione, desactiva o borra la filtrada (en AI Studio o en la consola de Google Cloud). Es el orden que da la guía oficial de claves de Gemini.
Consejo
¿Quieres además limpiar el historial? GitHub recomienda la herramienta git-filter-repo. Pero es delicado (cambia todos los commits y rompe el trabajo de quien tenga el repo clonado) y no sustituye a revocar la clave. Primero revoca; lo demás es opcional.
Ojo si haces webs: las claves nunca en el navegador
Esto pilla a mucha gente que empieza con Next.js o Vite. Las variables que empiezan por NEXT_PUBLIC_ (Next.js) o VITE_ (Vite) se meten dentro del JavaScript que se descarga el navegador. Da igual que estén en el .env: cualquiera que abra tu web y las herramientas de desarrollador puede leerlas.
- Next.js: las variables sin
NEXT_PUBLIC_solo existen en el servidor; las que lo llevan se copian en el código del cliente al compilar. - Vite: su documentación dice expresamente que las variables
VITE_no deben contener claves de API.
# ❌ Mal: acaba en el navegador
NEXT_PUBLIC_OPENAI_API_KEY=sk-proj-...
# ✅ Bien: solo la ve el servidor
OPENAI_API_KEY=sk-proj-...
La forma correcta: tu web llama a tu propio servidor (una ruta de API, una función serverless), y es ese servidor el que usa la clave para hablar con OpenAI, Claude o Gemini. El navegador nunca la ve.
Checklist antes de hacer push
- Ninguna clave escrita en el código (
git grep -n --untracked -e "sk-" -e "AIza"no devuelve nada). - Las claves están en
.envy el código las lee conprocess.envuos.environ. -
.envestá en el.gitignoreygit check-ignore -v .envlo confirma. - Subes un
.env.examplesin valores. - Nada de claves en variables
NEXT_PUBLIC_oVITE_. - Límite de gasto puesto en cada proveedor que uses.
- Si una clave estuvo en público: revocada y sustituida.
Con esto, la próxima factura de IA será la que esperas.
