Aplicaciones: aparece en la lista y permite que los usuarios se conecten
Dos cosas para quienes desarrollan aplicaciones en UberLLM. Ambas funcionan de la misma manera que en OpenRouter, así que una aplicación que ya las implementa solo necesita cambiar la base URL.
- Aparece en la lista. Envía dos headers y tu aplicación aparecerá en uberllm.dev/apps, clasificada por los tokens que envía. Las páginas de los modelos listan las aplicaciones que más los utilizan.
- Permite que los usuarios se conecten. Envía a los usuarios a UberLLM, ellos aceptan, y tu aplicación obtiene una API key en su cuenta. Ellos pagan por su propio uso; tú nunca manejas sus detalles de pago.
Aparecer en el ranking de aplicaciones
Añade estos encabezados a tus solicitudes (chat, mensajes o medios, cualquier endpoint):
| Encabezado | Qué hace |
|---|---|
HTTP-Referer |
La dirección de tu aplicación, p. ej. https://myapp.com. Obligatorio: es lo que identifica tu aplicación. Referer también funciona. |
X-Title |
El nombre de tu aplicación tal como la gente debería verlo. X-OpenRouter-Title también funciona. |
X-OpenRouter-Categories |
Opcional. Hasta 2 de las categorías siguientes, separadas por comas. |
X-OpenRouter-App-Visibility |
Opcional. hidden mantiene una nueva aplicación fuera del ranking público. |
curl https://api.uberllm.dev/v1/chat/completions \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-H "Content-Type: application/json" \
-H "HTTP-Referer: https://myapp.com" \
-H "X-Title: My App" \
-H "X-OpenRouter-Categories: cli-agent" \
-d '{"model":"deepseek/deepseek-v4-flash","messages":[{"role":"user","content":"Hello"}]}'
Con el SDK de OpenAI:
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.uberllm.dev/v1',
apiKey: process.env.UBERLLM_API_KEY,
defaultHeaders: {
'HTTP-Referer': 'https://myapp.com',
'X-Title': 'My App',
},
});
Cómo se cuenta:
- Solo se conserva la dirección:
https://myapp.com/some/page?user=…se almacena comohttps://myapp.com. Las rutas, cadenas de consulta (query strings) e inicios de sesión (logins) se eliminan. Un enlace de GitHub, GitLab, Codeberg, Bitbucket o Hugging Face conserva suowner/repo, por lo que las aplicaciones de código abierto obtienen su propia entrada. - El nombre se recorta a 60 caracteres. Los caracteres ocultos y de control se eliminan.
- La primera cuenta que envía tráfico para una aplicación establece su nombre y categorías. El tráfico de otras cuentas sigue contando para la aplicación, pero no puede cambiarle el nombre.
- Las direcciones en tu propia máquina o red (
localhost,127.0.0.1, IPs privadas,*.local) nunca se muestran públicamente. - Las solicitudes de los navegadores envían
Refererpor sí mismas, por lo que una aplicación web se cuenta incluso sin los encabezados. AñadeX-Titlepara elegir su nombre. - El ranking cuenta las solicitudes que fueron respondidas, por tokens, durante las últimas 24 horas, 7 días o 30 días. Se actualiza cada pocos minutos.
Categorías: cli-agent, ide-extension, cloud-agent, programming-app,
native-app-builder, creative-writing, video-gen, image-gen,
audio-gen, writing-assistant, general-chat, personal-agent, legal,
roleplay, game. Las desconocidas se ignoran. Una aplicación conserva hasta 10.
El ranking también es una API: GET https://uberllm.dev/api/v1/public/apps?period=day|week|month&category=<slug>.
Conectar UberLLM (OAuth PKCE)
Tu aplicación obtiene una clave en la cuenta del propio usuario, nombrada como tu aplicación. El usuario puede establecer un límite de gasto para ella al permitirla, y puede desactivarla en cualquier momento en su página de claves.
1. Crear un verificador y un desafío de código
El verificador es una cadena aleatoria que tú guardas (43 a 128 caracteres). El desafío es su SHA-256, codificado en base64url sin relleno.
function base64url(bytes) {
return btoa(String.fromCharCode(...new Uint8Array(bytes)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
const codeVerifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
const codeChallenge = base64url(
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier)),
);
sessionStorage.setItem('uberllm_verifier', codeVerifier);
2. Enviar al usuario a UberLLM
https://uberllm.dev/auth?callback_url=https://myapp.com/uberllm/callback&code_challenge=<CHALLENGE>&code_challenge_method=S256
| Parámetro | |
|---|---|
callback_url |
Dónde regresa el usuario. Debe ser https://; http://localhost (cualquier puerto) está permitido para desarrollo. Omítelo para mostrar el código en pantalla para que el usuario lo pegue en tu aplicación. |
code_challenge |
Del paso 1. Obligatorio. |
code_challenge_method |
S256. Obligatorio. plain no es aceptado. |
key_label |
Nombre opcional para la clave (por defecto: el nombre de tu aplicación). |
state |
Opcional. Se devuelve sin cambios con el código, para que puedas verificar que la respuesta pertenece a la solicitud que iniciaste. |
El usuario inicia sesión (o se registra), ve "Tu aplicación quiere usar tu crédito de UberLLM", y elige Permitir o Cancelar. Al Permitir se le envía a:
https://myapp.com/uberllm/callback?code=<CODE>&state=<STATE>
Al Cancelar no se envía nada a ninguna parte.
3. Intercambiar el código por una clave
const code = new URLSearchParams(location.search).get('code');
const res = await fetch('https://uberllm.dev/api/v1/auth/keys', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code,
code_verifier: sessionStorage.getItem('uberllm_verifier'),
code_challenge_method: 'S256',
}),
});
const { key } = await res.json(); // ull_… Store it like a password.
curl https://uberllm.dev/api/v1/auth/keys \
-H "Content-Type: application/json" \
-d '{"code":"<CODE>","code_verifier":"<VERIFIER>","code_challenge_method":"S256"}'
La respuesta es {"key": "ull_…", "key_id": "…", "name": "My App"}. Usa la clave como Authorization: Bearer en https://api.uberllm.dev/v1. El endpoint responde a solicitudes de navegador desde cualquier sitio (CORS), por lo que funciona desde una aplicación web.
| Estado | Código | Significado |
|---|---|---|
| 400 | invalid_code_challenge_method |
Usa S256. |
| 403 | invalid_code |
Código incorrecto o code_verifier, o el código ya fue usado. |
| 403 | code_expired |
Los códigos funcionan durante 10 minutos. Empieza de nuevo desde el paso 2. |
| 405 | method_not_allowed |
Usa POST. |
| 429 | rate_limited |
Demasiados intentos desde una dirección; espera un minuto. |
Un código funciona una vez. Un code_verifier incorrecto también lo consume, por lo que un código que se haya filtrado no puede ser adivinado.
Diferencias con OpenRouter
code_challengees obligatorio y solo se aceptaS256(OpenRouter también aceptaplaino ningún desafío). Sin él, cualquiera que vea el código en el historial del navegador o en un registro podría tomar la clave.workspace_id/required_workspace_idno se usan: una cuenta de UberLLM tiene un único saldo.