NuevoConecta cualquier agente a UberLLM: una clave, todos los modelos, a precio de lista o menos.
UberLLM

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.

  1. 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.
  2. 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 como https://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 su owner/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 Referer por sí mismas, por lo que una aplicación web se cuenta incluso sin los encabezados. Añade X-Title para 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_challenge es obligatorio y solo se acepta S256 (OpenRouter también acepta plain o 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_id no se usan: una cuenta de UberLLM tiene un único saldo.