Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

std/openai - Docs ↗

Use OpenAI's chat completion API with std/openai. This integration enables access to OpenAI's language models without needing to acquire API keys.

Val Town free users can use any affordable model – such as gpt-5.6-luna. Val Town Pro users can make 10 expensive requests (e.g. gpt-5.6-sol) per rolling 24-hour window, and are then bumped down to gpt-5.6-luna.

This SDK is powered by our openapiproxy.

Basic Usage

import { OpenAI } from "https://esm.town/v/std/openai/main.ts"; const openai = new OpenAI(); const completion = await openai.chat.completions.create({ messages: [ { role: "user", content: "Say hello in a creative way" }, ], model: "gpt-5.6-luna", max_completion_tokens: 100, }); console.log(completion.choices[0].message.content);

Bring Your Own API Key

The free proxy is great for getting started, but you may outgrow the rate limits. The easiest upgrade path is to bring your own OpenAI API key — just set OPENAI_API_KEY and use the npm:openai client directly when it's available, falling back to ValTownOpenAI otherwise. This way your val works with or without a key.

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts"; import OpenAI from "npm:openai"; // Use your own key if available, otherwise the Val Town proxy. const apiKey = Deno.env.get("OPENAI_API_KEY"); const openai = apiKey ? new OpenAI({ apiKey }) : new ValTownOpenAI({}); const completion = await openai.chat.completions.create({ model: "gpt-5.6-luna", messages: [{ role: "user", content: "Say hello in one word." }], }); console.log(completion.choices[0].message.content);

See the runnable example: examples/bring-your-own-key.ts

The Responses API (responses.create) is fully supported through the Val Town proxy. It enables the web_search tool, which lets the model browse the web in real time — incredibly effective for enrichment, research, and anything where fresh data beats stale training knowledge.

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts"; const openai = new ValTownOpenAI({}); const response = await openai.responses.create({ model: "gpt-5.6-luna", tools: [{ type: "web_search" }], instructions: "You research companies. Given a company name, reply in 2-3 short plain-text lines: what they do, their stage, and why a developer might care. No markdown.", input: "Val Town", }); console.log(response.output_text);

See the runnable example: examples/web-search.ts

Structured Outputs

Instead of asking the model to "return JSON" and manually parsing the response, pass a JSON schema in text.format. The model is constrained to output JSON that matches your schema exactly — no fence stripping, no regex, no try/catch on malformed JSON. This is the idiomatic approach for classification, scoring, extraction, routing, or any task where you need typed data back.

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts"; const openai = new ValTownOpenAI({}); const response = await openai.responses.create({ model: "gpt-5.6-luna", instructions: "You classify user feedback. Given a piece of feedback, classify it and write a one-sentence summary.", input: "The app keeps logging me out every 5 minutes, it's really frustrating.", text: { format: { type: "json_schema", name: "classification", schema: { type: "object", properties: { category: { type: "string", enum: ["bug", "feature_request", "praise", "question"], }, priority: { type: "string", enum: ["low", "medium", "high"], }, summary: { type: "string" }, }, required: ["category", "priority", "summary"], additionalProperties: false, }, }, }, }); // output_text is guaranteed valid JSON matching the schema. const result = JSON.parse(response.output_text); console.log(result);

See the runnable example: examples/structured-outputs.ts

Images

To send an image to ChatGPT, the easiest way is by converting it to a data URL, which is easiest to do with @stevekrouse/fileToDataURL.

import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL"; const dataURL = await fileToDataURL(file); const response = await chat([ { role: "system", content: `You are an nutritionist. Estimate the calories. We only need a VERY ROUGH estimate. Respond ONLY in a JSON array with values conforming to: {ingredient: string, calories: number} `, }, { role: "user", content: [{ type: "image_url", image_url: { url: dataURL, }, }], }, ], { model: "gpt-5.6-luna", max_completion_tokens: 200, });

OpenAI Agents SDK

The OpenAI Agents SDK works with std/openai too, including hosted tools like web search:

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts"; import { Agent, run, setDefaultOpenAIClient, setTracingDisabled, webSearchTool, } from "npm:@openai/agents"; setTracingDisabled(true); // tracing requires your own OpenAI key setDefaultOpenAIClient(new ValTownOpenAI({})); const agent = new Agent({ name: "Researcher", tools: [webSearchTool()], }); const result = await run(agent, "What's new in Val Town?"); console.log(result.finalOutput);

Limits

While our wrapper simplifies the integration of OpenAI, there are a few limitations to keep in mind:

  • Usage Quota: We limit each user to 100 requests per day.
  • Features: Chat completions and the Responses API are the only endpoints available. The Responses API supports the web_search tool and structured outputs (text.format with json_schema).

If these limits are too low, let us know! You can also get around the limitation by bringing your own API key.

📝 Edit docs