App: fatti elencare e permetti agli utenti di connettersi
Due cose per chi sviluppa app su UberLLM. Entrambe funzionano allo stesso modo di OpenRouter, quindi un'app che le implementa già deve solo cambiare il base URL.
- Fatti elencare. Invia due header e la tua app apparirà su uberllm.dev/apps, classificata in base ai token che invia. Le pagine dei modelli elencano le app che li usano di più.
- Permetti agli utenti di connettersi. Invia gli utenti a UberLLM, loro acconsentono, e la tua app ottiene una API key sul loro account. Loro pagano per il proprio utilizzo; tu non gestisci mai i loro dettagli di pagamento.
Fatti elencare nella classifica delle app
Aggiungi queste intestazioni alle tue richieste (chat, messaggi o media, qualsiasi endpoint):
| Intestazione | Cosa fa |
|---|---|
HTTP-Referer |
L'indirizzo della tua app, ad es. https://myapp.com. Obbligatorio: è ciò che identifica la tua app. Anche Referer funziona. |
X-Title |
Il nome della tua app come le persone dovrebbero vederlo. Anche X-OpenRouter-Title funziona. |
X-OpenRouter-Categories |
Opzionale. Fino a 2 delle categorie seguenti, separate da virgole. |
X-OpenRouter-App-Visibility |
Opzionale. hidden mantiene una nuova app fuori dalla classifica pubblica. |
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 l'SDK di 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',
},
});
Come viene conteggiato:
- Viene mantenuto solo l'indirizzo:
https://myapp.com/some/page?user=…viene memorizzato comehttps://myapp.com. Percorsi, stringhe di query e login vengono eliminati. Un link GitHub, GitLab, Codeberg, Bitbucket o Hugging Face mantiene il suoowner/repo, così le app open-source ottengono la propria voce. - Il nome viene tagliato a 60 caratteri. Caratteri nascosti e di controllo vengono rimossi.
- Il primo account che invia traffico per un'app ne imposta il nome e le categorie. Il traffico degli altri account conta comunque per l'app, ma non può rinominarla.
- Gli indirizzi sulla tua macchina o rete (
localhost,127.0.0.1, IP privati,*.local) non vengono mai mostrati pubblicamente. - Le richieste dai browser inviano
Refererautonomamente, quindi un'app web viene conteggiata anche senza le intestazioni. AggiungiX-Titleper scegliere il suo nome. - La classifica conteggia le richieste a cui è stata data risposta, per token, nelle ultime 24 ore, 7 giorni o 30 giorni. Si aggiorna ogni pochi minuti.
Categorie: 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. Quelle sconosciute vengono ignorate. Un'app mantiene fino a 10.
La classifica è anche un'API: GET https://uberllm.dev/api/v1/public/apps?period=day|week|month&category=<slug>.
Connetti UberLLM (OAuth PKCE)
La tua app ottiene una chiave sull'account dell'utente, nominata come la tua app. L'utente può impostare un limite di spesa per essa mentre la autorizza, e può disattivarla in qualsiasi momento sulla sua pagina delle chiavi.
1. Crea un verificatore di codice e una challenge
Il verificatore è una stringa casuale che conservi (da 43 a 128 caratteri). La challenge è il suo SHA-256, base64url-encoded senza padding.
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. Invia l'utente a UberLLM
https://uberllm.dev/auth?callback_url=https://myapp.com/uberllm/callback&code_challenge=<CHALLENGE>&code_challenge_method=S256
| Parametro | |
|---|---|
callback_url |
Dove l'utente torna. Deve essere https://; http://localhost (qualsiasi porta) è consentito per lo sviluppo. Omettilo per mostrare il codice sullo schermo affinché l'utente lo incolli nella tua app. |
code_challenge |
Dal passaggio 1. Obbligatorio. |
code_challenge_method |
S256. Obbligatorio. plain non è accettato. |
key_label |
Nome opzionale per la chiave (predefinito: nome della tua app). |
state |
Opzionale. Inviato indietro inalterato con il codice, così puoi verificare che la risposta appartenga alla richiesta che hai avviato. |
L'utente effettua l'accesso (o si registra), vede "La tua app vuole usare il tuo credito UberLLM", e sceglie Consenti o Annulla. Su Consenti vengono inviati a:
https://myapp.com/uberllm/callback?code=<CODE>&state=<STATE>
Su Annulla non viene inviato nulla da nessuna parte.
3. Scambia il codice con una chiave
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 risposta è {"key": "ull_…", "key_id": "…", "name": "My App"}. Usa la chiave
come Authorization: Bearer su https://api.uberllm.dev/v1. L'endpoint
risponde alle richieste del browser da qualsiasi sito (CORS), quindi funziona da una web app.
| Stato | Codice | Significato |
|---|---|---|
| 400 | invalid_code_challenge_method |
Usa S256. |
| 403 | invalid_code |
Codice errato o code_verifier, o il codice è già stato usato. |
| 403 | code_expired |
I codici funzionano per 10 minuti. Ricominci dal passaggio 2. |
| 405 | method_not_allowed |
Usa POST. |
| 429 | rate_limited |
Troppi tentativi da un indirizzo; aspetta un minuto. |
Un codice funziona una volta. Un code_verifier errato lo consuma anch'esso, quindi un codice che
è trapelato non può essere indovinato.
Differenze da OpenRouter
code_challengeè obbligatorio e soloS256è accettato (OpenRouter accetta ancheplaino nessuna challenge). Senza di esso, chiunque veda il codice nella cronologia o nei log del browser potrebbe prendere la chiave.workspace_id/required_workspace_idnon sono usati: un account UberLLM ha un unico saldo.