Saltar al contenido

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:

TipoNombreValor
CNAME*.sites.tuempresa.comrapisites.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 rutaAlcanceQué hace
GET/ping—Prueba de conexión: confirma la llave y a qué cuenta pertenece.
GET/accessreadCuáles de los requisitos de arriba le faltan a la cuenta.
GET/usagereadSitios y almacenamiento usados contra el límite del plan.
GET/domainsreadDominios base de la cuenta y su estado.
POST/siteswriteCrea un sitio a partir de archivos. Publica por defecto.
POST/sites/validatereadValida un paquete sin crear nada.
GET/sitesreadLista. Filtros: external_ref, status.
GET/sites/{id}readDetalle de un sitio.
GET/sites/{id}/filesreadArchivos de la versión publicada.
GET/sites/{id}/files/{ruta}readDescarga un archivo, para editarlo y volver a subirlo.
PATCH/sites/{id}writeNombre, metadata, external_ref, opciones, publicar o despublicar.
PUT/sites/{id}/contentwriteSube un paquete nuevo. Publica por defecto.
DELETE/sites/{id}writeDa de baja. Con ?purge=true también depura sus archivos.
GET/storage/unusedreadArchivos que ya no están en uso y cuánto liberarían.
POST/storage/purgewriteDepura los archivos no usados de los sitios indicados.

El objeto site

id, kind, namekind siempre es "uploaded" — la v1 solo crea sitios subidos.
url, domainurl es la dirección completa; domain es rapisites.app, o tu dominio base si lo conectaste.
statusdraft · published · unpublished · blocked · deleted. Un sitio dado de baja sigue devolviendo su detalle, con status: "deleted" — nunca 404.
blocked_reasonSolo si status es blocked; el motivo que puso el super admin.
spa_fallback, network_mode, indexable, access_mode, frame_ancestorsLas opciones del sitio.
external_ref, metadataLo que mandaste al crearlo o lo que le cambiaste con PATCH. Libre para ti.
versionnumber, file_count, total_bytes, published_at de la versión publicada — null si nunca se publicó.
has_unpublished_changestrue 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.

HTTPcodeCuándo
400invalid_requestFalta un campo o tiene mal formato.
401invalid_api_keyLlave ausente, inexistente, revocada o vencida.
402account_past_dueLa cuenta está en mora y la petición escribe (una lectura sigue andando).
403insufficient_scope · account_suspended · feature_not_in_plan · email_not_verified · api_terms_not_accepted · abuse_contact_missingFalta un requisito de los de arriba, o la llave no tiene el alcance que hace falta.
404site_not_found · file_not_foundNo existe, o es de otra cuenta — misma respuesta en los dos casos.
409idempotency_conflictLa misma Idempotency-Key con un cuerpo distinto.
413payload_too_largeEl paquete supera el tamaño máximo por subida (20 MB).
422validation_failed · sites_limit_reached · storage_limit_reached · base_domain_not_active · base_domain_quota_reached
429rate_limitedCon Retry-After y las cabeceras RateLimit-*.
503storage_unavailableEl 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