Guía de la API de SULI
Conecta SULI con tu propio código — tu CRM, tu app o tus scripts de automatización — para crear y consultar enlaces cortos sin que nadie tenga que entrar al panel a mano.
Primeros pasos
administrador./api-keys.php).TU_API_KEY por tu clave real):curl https://url.51x.mx/api/links -H "Authorization: Bearer TU_API_KEY"
Si todo está bien, verás una respuesta JSON con tus enlaces (o una lista vacía si aún no tienes ninguno).
Autenticación
Cada llamada a la API debe incluir un header Authorization con el formato:
Authorization: Bearer suli_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
No hay usuario/contraseña ni cookies de sesión — la clave por sí sola identifica tanto tu organización como el rol con el que operas.
Revocar una clave: desde /api-keys.php, botón "Revocar". Efecto inmediato — cualquier integración que la use empezará a recibir 401 invalid_api_key.
Permisos según el rol
Cuando generas una clave, esta hereda el rol del usuario que la creó. Si ese usuario cambia de rol o se desactiva su organización, la clave se ajusta automáticamente (no hace falta regenerarla).
| Rol del usuario que generó la clave | Puede listar/consultar (GET) | Puede crear (POST) |
|---|---|---|
| consultar | ✅ | ❌ |
| auxiliar | ❌ | ✅ |
| supervisor | ✅ | ✅ |
| administrador | ✅ | ✅ |
Si tu clave no tiene el permiso necesario, la API responde 403 forbidden — en ese caso, pide a un administrador que te genere una clave desde un usuario con el rol adecuado (normalmente supervisor o administrador cubre la mayoría de los casos de uso).
Base URL
https://url.51x.mx/api/
Todas las rutas de esta guía son relativas a esa base.
Endpoints
Crear un enlace
POST /api/links
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
url | string | Sí | La URL de destino a la que redirigirá el enlace corto. Debe ser una URL válida (con http:// o https://) |
title | string | No | Nombre descriptivo, útil para identificar el enlace en el panel |
vigencia | string | No | Cuánto tiempo estará activo el enlace antes de expirar. Vacío (por defecto) = nunca expira. Valores válidos: "", "1m", "6m", "1y", "2y" |
custom_code | string | No | El código corto que quieres usar (ej. "promo2026"). Entre 3 y 30 caracteres, solo letras y números. Si no lo envías, SULI genera uno aleatorio de 5 caracteres |
curl -X POST https://url.51x.mx/api/links \
-H "Authorization: Bearer suli_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://ejemplo.com/pagina-larga",
"title": "Campaña de agosto",
"vigencia": "1m"
}'
<?php
$ch = curl_init('https://url.51x.mx/api/links');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer suli_xxxxxxxx',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://ejemplo.com/pagina-larga',
'title' => 'Campaña de agosto',
'vigencia' => '1m',
]),
]);
$response = json_decode(curl_exec($ch), true);
echo $response['data']['short_url'];
const res = await fetch('https://url.51x.mx/api/links', {
method: 'POST',
headers: {
'Authorization': 'Bearer suli_xxxxxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://ejemplo.com/pagina-larga',
title: 'Campaña de agosto',
vigencia: '1m',
}),
});
const { data } = await res.json();
console.log(data.short_url);
{
"data": {
"id": 42,
"code": "aB3k9",
"short_url": "https://url.51x.mx/aB3k9",
"original_url": "https://ejemplo.com/pagina-larga",
"title": "Campaña de agosto",
"expires_at": "2026-09-10 00:00:00",
"clicks": 0
}
}
expires_at viene en null si el enlace no tiene vigencia (es indefinido).
Listar enlaces
GET /api/links
Devuelve los enlaces de tu organización, del más reciente al más antiguo.
| Parámetro | Descripción |
|---|---|
page | Número de página, empezando en 1 (por defecto: 1) |
per_page | Resultados por página, máximo 100 (por defecto: 20) |
curl "https://url.51x.mx/api/links?page=1&per_page=50" \
-H "Authorization: Bearer suli_xxxxxxxx"
{
"data": [
{
"id": 42,
"code": "aB3k9",
"short_url": "https://url.51x.mx/aB3k9",
"original_url": "https://ejemplo.com/pagina-larga",
"title": "Campaña de agosto",
"expires_at": "2026-09-10 00:00:00",
"clicks": 7
}
],
"pagination": { "page": 1, "per_page": 50, "total": 1 }
}
pagination.total es el número total de enlaces que tiene tu organización (no solo los de esta página) — úsalo para saber cuántas páginas más pedir.
Consultar un enlace específico
GET /api/links/{code}
Donde {code} es el código corto (lo que va después del dominio, ej. aB3k9).
curl https://url.51x.mx/api/links/aB3k9 \
-H "Authorization: Bearer suli_xxxxxxxx"
La respuesta usa el mismo formato que un elemento de la lista anterior. Útil para revisar cuántos clics lleva un enlace específico sin tener que listarlos todos.
Manejo de errores
Cuando algo falla, la API siempre responde con este formato:
{ "error": { "code": "invalid_url", "message": "El campo \"url\" es obligatorio y debe ser una URL válida." } }
code es estable y pensado para que tu programa lo compare directamente (ej. if (error.code === 'code_taken')); message es para mostrar a un humano o depurar.
| HTTP | code | Cuándo ocurre | Qué hacer |
|---|---|---|---|
| 401 | missing_api_key | No enviaste el header Authorization | Agrega Authorization: Bearer <tu_clave> |
| 401 | invalid_api_key | La clave no existe, fue revocada, o su organización está desactivada | Genera una clave nueva desde /api-keys.php |
| 403 | forbidden | La clave no tiene permiso para esta acción | Usa una clave generada por un usuario con más permisos |
| 404 | not_found | La ruta no existe, o el {code} consultado no corresponde a ningún enlace | Revisa la URL y el código |
| 409 | code_taken | El custom_code que enviaste ya lo usa otro enlace | Elige otro código o no envíes custom_code |
| 422 | invalid_url / invalid_vigencia / invalid_custom_code | Algún campo del cuerpo de la petición no es válido | Revisa error.message para saber exactamente qué corregir |
| 500 | insert_failed | Error interno al guardar en la base de datos | Reintenta; si persiste, contacta al administrador del sistema |
Preguntas frecuentes
¿Hay límite de peticiones (rate limit)?
No por ahora. Aun así, evita crear enlaces en bucles sin control — si necesitas crear muchos, agrega una pequeña pausa entre peticiones.
¿Los códigos distinguen mayúsculas de minúsculas?
Sí. abc123 y ABC123 son enlaces distintos.
¿Qué pasa si un enlace vence (vigencia cumplida)?
Deja de redirigir — quien lo visite ve una página de "enlace expirado". El enlace sigue existiendo y apareciendo en GET /api/links, solo que ya no funciona. Por ahora la API no permite eliminarlos ni editarlos; eso se hace desde el panel web.
¿El campo clicks se actualiza al consultar por la API?
No. Solo se incrementa cuando alguien visita el enlace corto real (url.51x.mx/codigo), no cuando lo consultas por GET /api/links/{code}.
¿Puedo tener varias claves activas a la vez?
Sí, no hay límite. Es buena práctica generar una clave distinta por cada integración (una para el CRM, otra para el script de marketing, etc.) para poder revocarlas por separado si una se ve comprometida.