TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1// Probe 00: GET /models (full dump), /key, /credits, and a summary of the listing.2import { raw, save, short } from "./lib.ts";34// Default call (no params) — to see whether the response is paginated / how many models come back5const first = await raw("/models");6const fb: any = first.body;7console.log("GET /models (no params)", first.status, `data=${fb?.data?.length} total_count=${fb?.total_count} links=${JSON.stringify(fb?.links)} keys=${Object.keys(fb ?? {})}`);8save("models-default-page.meta.json", { status: first.status, headers: first.headers, count: fb?.data?.length, total_count: fb?.total_count, links: fb?.links, keys: Object.keys(fb ?? {}) });910// Full dump: follow pagination with limit=100011const all: any[] = [];12let next: string | null = "/models?limit=1000";13let pages = 0;14let lastPage: any = null;15while (next) {16 const r = await raw(next.replace(/^https:\/\/openrouter\.ai\/api\/v1/, ""));17 const b: any = r.body;18 pages++;19 lastPage = b;20 all.push(...(b?.data ?? []));21 console.log(`page ${pages}: status=${r.status} data=${b?.data?.length} total_count=${b?.total_count} next=${b?.links?.next}`);22 next = b?.links?.next ?? null;23 if (pages > 10) break;24}25const models = { status: first.status, body: { ...lastPage, data: all } };26save("models.json", models.body);2728const key = await raw("/key");29console.log("GET /key", key.status, short(key.body, 1200));30save("key.json", { status: key.status, headers: key.headers, body: key.body });3132const credits = await raw("/credits");33console.log("GET /credits", credits.status, short(credits.body, 600));34save("credits.json", { status: credits.status, headers: credits.headers, body: credits.body });3536// Extra listing endpoints worth knowing about37for (const p of ["/models/user", "/models?supported_parameters=tools", "/models/count", "/auth/key"]) {38 const r = await raw(p);39 const b: any = r.body;40 console.log("GET", p, r.status, Array.isArray(b?.data) ? `data.length=${b.data.length}` : short(b, 200));41}4243const data: any[] = (models.body as any)?.data ?? [];44const count = data.length;45const outMod: Record<string, number> = {};46const inMod: Record<string, number> = {};47const params = new Map<string, number>();48const topKeys = new Set<string>();49const pricingKeys = new Set<string>();50const archKeys = new Set<string>();51const tpKeys = new Set<string>();52let free = 0;53let freeSuffix = 0;54let withExpiration = 0;55let withReasoning = 0;56let withDefaults = 0;57const reasoningSamples: any[] = [];58const defaultSamples: any[] = [];59const expSamples: any[] = [];60const prlSamples: any[] = [];61const variants: Record<string, number> = {};62const nullCtx: string[] = [];63const nullMaxOut = { count: 0 };64const tokenizers: Record<string, number> = {};65const instructTypes: Record<string, number> = {};66const modalityCombos: Record<string, number> = {};67const nonTextOut: string[] = [];68let perRequestLimitsNonNull = 0;69let hfIds = 0;7071for (const m of data) {72 for (const k of Object.keys(m)) topKeys.add(k);73 for (const k of Object.keys(m.pricing ?? {})) pricingKeys.add(k);74 for (const k of Object.keys(m.architecture ?? {})) archKeys.add(k);75 for (const k of Object.keys(m.top_provider ?? {})) tpKeys.add(k);76 const om = (m.architecture?.output_modalities ?? []).slice().sort().join("+") || "(none)";77 outMod[om] = (outMod[om] ?? 0) + 1;78 const im = (m.architecture?.input_modalities ?? []).slice().sort().join("+") || "(none)";79 inMod[im] = (inMod[im] ?? 0) + 1;80 const mod = m.architecture?.modality ?? "(none)";81 modalityCombos[mod] = (modalityCombos[mod] ?? 0) + 1;82 if (m.architecture?.tokenizer) tokenizers[m.architecture.tokenizer] = (tokenizers[m.architecture.tokenizer] ?? 0) + 1;83 const it = m.architecture?.instruct_type ?? "(null)";84 instructTypes[it] = (instructTypes[it] ?? 0) + 1;85 for (const p of m.supported_parameters ?? []) params.set(p, (params.get(p) ?? 0) + 1);86 if (m.pricing?.prompt === "0") free++;87 if (/:free$/.test(m.id)) freeSuffix++;88 const v = m.id.includes(":") ? m.id.slice(m.id.indexOf(":")) : "(none)";89 variants[v] = (variants[v] ?? 0) + 1;90 if (m.expiration_date) {91 withExpiration++;92 if (expSamples.length < 8) expSamples.push({ id: m.id, expiration_date: m.expiration_date });93 }94 if (m.reasoning != null) {95 withReasoning++;96 if (reasoningSamples.length < 12) reasoningSamples.push({ id: m.id, reasoning: m.reasoning });97 }98 if (m.default_parameters != null && Object.keys(m.default_parameters).length) {99 withDefaults++;100 if (defaultSamples.length < 8) defaultSamples.push({ id: m.id, default_parameters: m.default_parameters });101 }102 if (m.per_request_limits != null) {103 perRequestLimitsNonNull++;104 if (prlSamples.length < 3) prlSamples.push({ id: m.id, per_request_limits: m.per_request_limits });105 }106 if (m.hugging_face_id) hfIds++;107 if (m.context_length == null) nullCtx.push(m.id);108 if (m.top_provider?.max_completion_tokens == null) nullMaxOut.count++;109 const outs: string[] = m.architecture?.output_modalities ?? [];110 if (!outs.includes("text")) nonTextOut.push(`${m.id} → ${outs.join(",")}`);111}112113const reasoningShapes: Record<string, number> = {};114for (const m of data) {115 const r = m.reasoning;116 const shape = r == null ? "null/absent" : JSON.stringify(Object.fromEntries(Object.entries(r).map(([k, v]) => [k, Array.isArray(v) ? "array" : typeof v])));117 reasoningShapes[shape] = (reasoningShapes[shape] ?? 0) + 1;118}119120const summary = {121 count,122 topLevelKeys: [...topKeys],123 architectureKeys: [...archKeys],124 pricingKeys: [...pricingKeys],125 topProviderKeys: [...tpKeys],126 outputModalities: outMod,127 inputModalities: inMod,128 modalityStrings: modalityCombos,129 tokenizers,130 instructTypes,131 supportedParameters: Object.fromEntries([...params.entries()].sort((a, b) => b[1] - a[1])),132 freeByPromptZero: free,133 freeBySuffix: freeSuffix,134 variantSuffixes: variants,135 withExpirationDate: withExpiration,136 expirationSamples: expSamples,137 withReasoningField: withReasoning,138 reasoningFieldShapes: reasoningShapes,139 reasoningSamples,140 withDefaultParameters: withDefaults,141 defaultParameterSamples: defaultSamples,142 perRequestLimitsNonNull,143 perRequestLimitsSamples: prlSamples,144 huggingFaceIds: hfIds,145 nullContextLength: nullCtx,146 nullMaxCompletionTokens: nullMaxOut.count,147 nonTextOutput: nonTextOut,148 sample: data.find((m) => m.id === "anthropic/claude-haiku-4-5") ?? data[0],149};150save("models-summary.json", summary);151console.log(JSON.stringify({ ...summary, sample: undefined, nonTextOutput: summary.nonTextOutput.length, nullContextLength: nullCtx.length }, null, 1).slice(0, 6000));152153// Candidate cheap models for the probes154const want = [155 "openai/gpt-5.4-nano",156 "anthropic/claude-haiku-4-5",157 "google/gemini-3.5-flash-lite",158 "deepseek/deepseek-v4-flash",159 "x-ai/grok-4.20-0309-non-reasoning",160];161for (const w of want) {162 const m = data.find((x) => x.id === w);163 console.log(w, m ? `OK ctx=${m.context_length} in=${m.pricing.prompt} out=${m.pricing.completion} params=${(m.supported_parameters ?? []).join(",")}` : "MISSING");164}165const cheap = data166 .filter((m) => m.pricing?.prompt !== "0" && Number(m.pricing?.prompt) < 0.0000005 && (m.architecture?.output_modalities ?? []).includes("text"))167 .filter((m) => /^(openai|anthropic|google|deepseek|x-ai|mistralai|meta-llama|qwen|moonshotai|z-ai)\//.test(m.id))168 .map((m) => `${m.id} in=${m.pricing.prompt} out=${m.pricing.completion} ctx=${m.context_length} img=${(m.architecture.input_modalities ?? []).includes("image")}`);169console.log("cheap candidates:\n" + cheap.join("\n"));170