# Almacenamiento

Source: https://airi.live/docs/almacenamiento
Language: es. The other: https://airi.live/docs/almacenamiento.md?lang=en
Index: https://airi.live/llms.txt — every page in one file: https://airi.live/llms-full.txt

Guarda tus vídeos y sírvelos por CDN con enlaces que caducan. Pagas por lo que ocupan y por lo que se descarga, sin mínimos ni cuota.

## Cómo funciona

Subir un fichero son tres llamadas: **declaras** lo que vas a subir, **subes** los bytes directamente al almacenamiento con las URLs que te devolvemos, y **cierras** la subida. Los bytes nunca pasan por nuestra API — van del navegador o de tu servidor al bucket, sin intermediarios que cobrar ni cuellos de botella.

Para descargar pides una **URL firmada**. No es una dirección permanente: es un permiso con fecha de caducidad y, si quieres, con restricciones de país o de velocidad. Se emite una por espectador y se sirve desde una red CDN, no desde nuestra API.

Si prefieres verlo antes que leerlo, [la demo](/storage/demo) hace justo esto con un fichero tuyo y te devuelve los dos enlaces del mismo objeto —firmado y sin firmar— con el código que contesta cada uno.

> **Por qué las descargas no pasan por nosotros:** El tráfico de vídeo es el más caro que existe. Servirlo desde un CDN especializado sale más barato que desde cualquier API, y ese ahorro es exactamente lo que te repercutimos en el precio por GB servido.

## Subir un fichero

Declara el fichero con su tamaño. Según el tamaño te contestamos con una subida simple o con un plan por partes — no tienes que elegir, lo decide el servidor.

1. Declarar:

```
curl -X POST https://kms.airi.live/v1/storage/files \
  -H "Authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{
    "name": "pelicula.mp4",
    "sizeBytes": 734003200,
    "contentType": "video/mp4",
    "customId": "pelicula-1234"
  }'
```

La respuesta trae el fichero y las instrucciones. Si `mode` es `single`, haz un `PUT` a `upload.url` con el mismo `Content-Type` que declaraste. Si es `multipart`, corta el fichero en trozos de `upload.partBytes` y haz un `PUT` por cada parte.

2. Subir (multipart):

```
# Una parte por cada URL. Guarda el ETag de cada respuesta.
for part in "${parts[@]}"; do
  curl -X PUT --data-binary @parte-$i.bin "$url" -D headers.txt
  etag=$(grep -i '^etag:' headers.txt | tr -d '"\r' | cut -d' ' -f2)
done
```

3. Cerrar:

```
curl -X POST https://kms.airi.live/v1/storage/files/$ID/complete \
  -H "Authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"parts":[{"partNumber":1,"etag":"a54..."},{"partNumber":2,"etag":"7b1..."}]}'
```

> **El tamaño que te cobramos lo medimos nosotros:** El `sizeBytes` que declaras solo sirve para decidir si la subida va simple o por partes. Al cerrar comprobamos el objeto real y ese es el tamaño que cuenta, así que un número mal declarado no cambia tu factura ni en un sentido ni en el otro.

> **Si algo se corta:** Las URLs de subida caducan en una hora. `POST /v1/storage/files/{id}/parts` te emite otras, gratis y tantas veces como haga falta — sirve tanto para reanudar como para reintentar una parte suelta. Una subida que nunca se cierra se limpia sola a las 24 horas y no se cobra.

## Servir el vídeo

Pide una URL firmada por cada espectador, con la caducidad más corta que te permita el caso. Quien tenga la URL puede descargar hasta que expire, así que la caducidad es tu control real.

```
curl "https://kms.airi.live/v1/storage/files/$ID/url?expiresSeconds=600" \
  -H "Authorization: Bearer $TOKEN"

# { "url": "https://...", "expiresAt": "2026-08-02T12:34:56.000Z" }
```

### Restricciones por enlace

Cada URL puede llevar sus propias condiciones, y todas se aplican **en el borde del CDN**: quien no cumpla no llega a descargar nada, así que tampoco te lo facturamos.

| Parámetro | Qué hace |
| --- | --- |
| `expiresSeconds` | De 60 segundos a 7 días. Por defecto una hora. |
| `directory=true` | El permiso cubre la carpeta entera, no un fichero. Imprescindible para HLS y DASH — ver abajo. |
| `countries` | Códigos ISO separados por comas. Solo esos países pueden reproducir. |
| `blockedCountries` | Códigos ISO que quedan excluidos. |
| `speedLimitKbps` | Tope de velocidad en KB/s, para que un espectador no se lleve todo tu ancho de banda. |
| `speedLimitAfterSeconds` | Segundos servidos sin tope antes de aplicarlo, para que la reproducción arranque al instante. |

```
curl "https://kms.airi.live/v1/storage/files/$ID/url\
?expiresSeconds=600&countries=ES,MX&speedLimitKbps=4000&speedLimitAfterSeconds=5" \
  -H "Authorization: Bearer $TOKEN"
```

> **HLS y DASH necesitan `directory=true`:** Un manifiesto referencia sus segmentos por ruta relativa, y el reproductor descarta la parte firmada de la URL al resolverlos. Con una firma por fichero, el manifiesto cargaría y todos los segmentos serían rechazados. Con `directory=true` la firma viaja en el prefijo de la ruta, que la resolución relativa sí conserva, y los segmentos se autentican solos.

> **Restricción por IP:** Todavía no la ofrecemos. En la red que usamos es un ajuste de toda la zona y no una opción por enlace, así que activarla obligaría a todos los clientes a aportar la IP del espectador en cada petición. Llegará cuando una cuenta pueda tener su propia zona de CDN.

## La red de entrega

No hay nada que contratar ni que configurar: el CDN viene con el almacenamiento. Cada URL firmada que emites apunta ya a la red, y los bytes salen desde el punto de presencia más adecuado para quien la pide. La capacidad total de esa red supera los **250 Tbps**, así que el pico de tu estreno es un problema que ya está resuelto antes de que lo tengas.

Servimos desde diez localizaciones: **Frankfurt, París, São Paulo, Chicago, Dallas, Los Ángeles, Miami, Hong Kong, Singapur y Tokio**. Son pocas y muy grandes, elegidas por caudal antes que por cercanía.

> **Por qué diez y no trescientas:** Un CDN de propósito general reparte cientos de nodos pequeños para recortar milisegundos, porque su carga son respuestas diminutas donde la latencia lo es todo. El vídeo es lo contrario: objetos grandes, conexiones largas, y lo que decide la experiencia es el caudal sostenido. Diez nodos con mucha salida sirven eso mejor que trescientos pequeños, y cuestan bastante menos de operar — ese es exactamente el ahorro que sale en tu factura como $0.008 por GB en todo el mundo, sin recargo por región.

La consecuencia práctica es que no tienes que pensar en entrega. Puedes montar un catálogo bajo demanda, un canal en directo o lo que se te ocurra sin dimensionar ancho de banda, sin negociar tránsito y sin reservar capacidad por adelantado.

## Qué se cobra

| Concepto | Precio |
| --- | --- |
| Almacenamiento | $0.015 por GB y mes |
| Transferencia | $0.008 por GB servido |

El almacenamiento se cobra **una vez al día**, sobre lo que había activo en ese momento. Un fichero que subes y borras el mismo día no llega a pagar nada; uno que borras hoy deja de contar mañana. La transferencia se cobra al día siguiente, cuando el CDN publica lo que sirvió.

No hay mínimos, ni tamaño mínimo de fichero, ni permanencia. Un GB guardado medio mes cuesta medio.

> **Si te quedas sin saldo:** No podrás subir nada nuevo, pero **lo que ya tienes se sigue sirviendo** y acumula deuda durante 15 días. Te avisamos por email cada dos días, y otra vez cuando quedan 24 horas. Si al día 15 el saldo sigue agotado, los ficheros se eliminan de forma irreversible. Recargar en cualquier momento cancela la cuenta atrás.

## Comparado con S3 + CloudFront

La forma habitual de montar guardar-y-servir por tu cuenta es **S3** más **CloudFront**. Estos son sus precios de tarifa pública en Norte de Virginia —su región más barata— frente a los nuestros, comprobados el 2 de agosto de 2026. La comparación de la plataforma entera, con codificación y DRM incluidos, está [en la portada](/).

| Concepto | S3 + CloudFront | Airi |
| --- | --- | --- |
| Almacenamiento | $0.023 por GB y mes | **$0.015** |
| Entrega — Norteamérica y Europa | $0.085 por GB | **$0.008** |
| Entrega — Asia, India, Sudamérica, Oceanía | $0.109 – $0.120 por GB | **$0.008** |
| Peticiones HTTPS | $0.0100 por cada 10 000 | **Incluidas** |
| Recargo por región | Hasta un 41% más fuera de NA/EU | **Ninguno** |

La línea de las peticiones no es un detalle en vídeo: un HLS son cientos de peticiones por hora y espectador. Con un tamaño medio de segmento de 1.5 MB, servir 500 TB al mes son unos 350 millones de peticiones, es decir **unos $340 solo por contarlas**, antes de mover un byte. Nosotros no las contamos.

> **Dónde les sale más barato que a nosotros:** CloudFront incluye **1 TB de transferencia y 10 millones de peticiones gratis cada mes, para siempre**. Nosotros no tenemos tramo gratis: cobramos desde el primer GB. El cruce está en unos **1.1 TB servidos al mes** —alrededor de 858 horas de 1080p— y por debajo de ahí AWS sale más barato. Por encima, la diferencia se abre deprisa, porque lo que domina una factura de vídeo es la entrega.

> **Qué no dice esta comparación:** Son precios de tarifa. Un cliente grande de AWS negocia precios privados que aquí no aparecen; el nuestro es el mismo para todos y está publicado. Y CloudFront tiene cientos de puntos de presencia frente a nuestros diez: si lo que sirves son respuestas pequeñas donde cada milisegundo cuenta, esa diferencia importa de verdad. Si es vídeo, no.

## Listar, buscar y borrar

```
# Listar, paginado por cursor
curl "https://kms.airi.live/v1/storage/files?limit=50" -H "Authorization: Bearer $TOKEN"

# Buscar por tu propio identificador
curl "https://kms.airi.live/v1/storage/files?customId=pelicula-1234" -H "Authorization: Bearer $TOKEN"

# Cuánto ocupas y cuánto costará el mes
curl "https://kms.airi.live/v1/storage/usage" -H "Authorization: Bearer $TOKEN"

# Borrar
curl -X DELETE "https://kms.airi.live/v1/storage/files/$ID" -H "Authorization: Bearer $TOKEN"
```

El `customId` es tuyo: ponle el identificador que uses en tu base de datos y búscalo por él en vez de guardar el nuestro. Es único mientras el fichero exista — al borrarlo queda libre otra vez.

Borrar es definitivo y se cobra hasta el día que lo haces. Si el almacenamiento no responde, la petición falla y el fichero **no** se marca borrado: preferimos un error visible a una factura que deja de contar bytes que siguen existiendo.

## Permisos

- **`storage:read`:** Listar, ver detalles, consultar uso y emitir URLs firmadas.
- **`storage:write`:** Subir, cerrar subidas y borrar.

Crea credenciales separadas para lo que solo lee. El backend que reparte enlaces a tus espectadores no necesita poder borrar nada, y una credencial con menos permisos es una credencial que puede filtrarse con menos consecuencias.
