Sube y administra sitios por código.
La misma API que usa el panel para publicar un sitio con archivos propios — con una llave, sin la pantalla de revisión: subir publica.
Antes de empezar
Desde Ajustes → API, dentro del panel, con estos cuatro requisitos cumplidos puedes crear tu primera llave:
- Correo verificado
- Un plan con acceso a la API (Pro o Business)
- Aceptar las condiciones de la API
- Un contacto de abuso
GET /v1/access te dice en cualquier momento cuál de los cuatro te falta, y GET /v1/ping confirma que la llave funciona antes de subir nada — ready: false si algo falta, nunca una llave "a medias".
Empezar en cuatro pasos
1. Probar la conexión.
curl https://api.rapisites.com/v1/ping \
-H "Authorization: Bearer rsk_live_…"{
"ok": true,
"account": { "id": "…", "name": "Acme" },
"key": { "name": "Acme producción", "prefix": "rsk_live_4fT9…", "scopes": ["sites:read", "sites:write"] },
"ready": true,
"server_time": "2026-10-01T15:04:05Z"
}2. Crear un sitio. Un .html alcanza; una carpeta entera va como varias partes files, cada una con su ruta en filename. El ; dentro de cada -F necesita las comillas — sin ellas la terminal corta el comando ahí mismo.
curl https://api.rapisites.com/v1/sites \
-H "Authorization: Bearer rsk_live_…" \
-F name="Ruleta de premios" \
-F external_ref="group_81/app_447" \
-F "files=@index.html;filename=index.html" \
-F "files=@game.js;filename=assets/game.js" \
-F "files=@logo.png;filename=assets/img/logo.png" Responde 201 con el sitio ya publicado — por API no hay pantalla de revisión. Con publish=false queda en borrador, para publicarlo después con PATCH (ver el paso 4).
3. Actualizarlo. Un paquete nuevo también publica por defecto.
curl -X PUT https://api.rapisites.com/v1/sites/{id}/content \
-H "Authorization: Bearer rsk_live_…" \
-F "files=@index.html;filename=index.html"4. Publicar sin volver a subir nada. Un sitio creado con publish=false, o despublicado, se publica de nuevo con el mismo borrador:
curl -X PATCH https://api.rapisites.com/v1/sites/{id} \
-H "Authorization: Bearer rsk_live_…" \
-H "Content-Type: application/json" \
-d '{"status": "published"}' Pedir "published" sobre un sitio que ya está publicado con ese mismo contenido no es un error: responde 200 igual, como si acabara de publicarse — para que reintentar este mismo PATCH después de perder la respuesta por la red sea siempre seguro. PATCH no es atómico: si cambias nombre, opciones y status en un mismo pedido y publicar falla (sin paquete, o el paquete carga un host externo en modo autocontenido), lo demás ya quedó guardado — el error trae details.applied con los campos que sí se aplicaron.
Repetir la misma petición por una falla de red no es seguro por sí solo: manda una cabecera Idempotency-Key (cualquier string único de tu parte) en los POST que crean o cambian algo, y un reintento con la misma clave devuelve la respuesta original en vez de crear un sitio dos veces — incluso si, entre el pedido original y el reintento, la cuenta llegó al tope de sitios o de certificados: la clave que ya se usó manda sobre cualquier cupo que haya cambiado después. La misma clave con un cuerpo distinto es un 409, no un reintento.
Tu propio dominio
Sin hacer nada, tus sitios quedan en {etiqueta}.rapisites.app. Para que lleven tu dominio en vez del nuestro, conecta un dominio base desde Ajustes → API en el panel — un comodín: cada sitio que crees por API recibe su propio host debajo, sin que vuelvas a tocar tu DNS.
Un solo registro, una sola vez:
| Tipo | Nombre | Valor |
|---|---|---|
CNAME | *.sites.tuempresa.com | rapisites.com |
Dedica un dominio o subdominio solo a esto — no el de tu propio sitio ni el de tu panel. Puede tardar hasta 24 horas en propagar; el panel lo verifica solo. Una vez activo, pasa domain al crear un sitio:
curl https://api.rapisites.com/v1/sites \
-H "Authorization: Bearer rsk_live_…" \
-F name="Ruleta de premios" \
-F domain="sites.tuempresa.com" \
-F "files=@index.html;filename=index.html" El sitio queda en {etiqueta}.sites.tuempresa.com. Sin domain, se usa tu dominio base por defecto si tienes uno — si no, rapisites.app. Un sitio nace en un dominio y no se muda.
Referencia
Base https://api.rapisites.com/v1. JSON salvo las subidas (multipart/form-data). Nombres de campo en snake_case, fechas ISO 8601 en UTC, identificadores UUID. sites:write ya incluye leer — no hace falta pedir los dos alcances.
| Método y ruta | Alcance | Qué hace |
|---|---|---|
GET/ping | — | Prueba de conexión: confirma la llave y a qué cuenta pertenece. |
GET/access | read | Cuáles de los requisitos de arriba le faltan a la cuenta. |
GET/usage | read | Sitios y almacenamiento usados contra el límite del plan. |
GET/domains | read | Dominios base de la cuenta y su estado. |
POST/sites | write | Crea un sitio a partir de archivos. Publica por defecto. |
POST/sites/validate | read | Valida un paquete sin crear nada. |
GET/sites | read | Lista. Filtros: external_ref, status. |
GET/sites/{id} | read | Detalle de un sitio. |
GET/sites/{id}/files | read | Archivos de la versión publicada. |
GET/sites/{id}/files/{ruta} | read | Descarga un archivo, para editarlo y volver a subirlo. |
PATCH/sites/{id} | write | Nombre, metadata, external_ref, opciones, publicar o despublicar. |
PUT/sites/{id}/content | write | Sube un paquete nuevo. Publica por defecto. |
DELETE/sites/{id} | write | Da de baja. Con ?purge=true también depura sus archivos. |
GET/storage/unused | read | Archivos que ya no están en uso y cuánto liberarían. |
POST/storage/purge | write | Depura los archivos no usados de los sitios indicados. |
El objeto site
id, kind, name | kind siempre es "uploaded" — la v1 solo crea sitios subidos. |
url, domain | url es la dirección completa; domain es rapisites.app, o tu dominio base si lo conectaste. |
status | draft · published · unpublished · blocked · deleted. Un sitio dado de baja sigue devolviendo su detalle, con status: "deleted" — nunca 404. |
blocked_reason | Solo si status es blocked; el motivo que puso el super admin. |
spa_fallback, network_mode, indexable, access_mode, frame_ancestors | Las opciones del sitio. |
external_ref, metadata | Lo que mandaste al crearlo o lo que le cambiaste con PATCH. Libre para ti. |
version | number, file_count, total_bytes, published_at de la versión publicada — null si nunca se publicó. |
has_unpublished_changes | true si hay un borrador más nuevo que lo publicado. |
created_at, updated_at |
Errores
Un error llega anidado bajo data — el sobre que arma Nitro, el servidor detrás de la API, no uno que inventamos nosotros:
{
"statusCode": 401,
"statusMessage": "Unauthorized",
"data": {
"error": { "code": "invalid_api_key", "message": "Falta la llave en la cabecera Authorization." }
}
} Lo que importa es data.error.code y data.error.message — code es estable y en inglés, lo que comparas en tu código; message es para humanos y puede cambiar. Cada respuesta lleva X-Request-Id, para cuando necesites que te ayudemos con un caso puntual.
| HTTP | code | Cuándo |
|---|---|---|
| 400 | invalid_request | Falta un campo o tiene mal formato. |
| 401 | invalid_api_key | Llave ausente, inexistente, revocada o vencida. |
| 402 | account_past_due | La cuenta está en mora y la petición escribe (una lectura sigue andando). |
| 403 | insufficient_scope · account_suspended · feature_not_in_plan · email_not_verified · api_terms_not_accepted · abuse_contact_missing | Falta un requisito de los de arriba, o la llave no tiene el alcance que hace falta. |
| 404 | site_not_found · file_not_found | No existe, o es de otra cuenta — misma respuesta en los dos casos. |
| 409 | idempotency_conflict | La misma Idempotency-Key con un cuerpo distinto. |
| 413 | payload_too_large | El paquete supera el tamaño máximo por subida (20 MB). |
| 422 | validation_failed · sites_limit_reached · storage_limit_reached · base_domain_not_active · base_domain_quota_reached | |
| 429 | rate_limited | Con Retry-After y las cabeceras RateLimit-*. |
| 503 | storage_unavailable | El almacén no responde; se puede reintentar. |
¿Vas a integrar un sistema con varios sitios?
Escríbenos y vemos qué necesitas — un dominio propio para tus sitios, un cupo más grande, lo que haga falta.
Hablar con nosotros