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.4-nano and
gpt-5-nano. Val Town Pro users can make 10 expensive requests per rolling
24-hour window, and are then bumped down to gpt-5.4-nano.
This SDK is powered by our openapiproxy.
Basic Usage
Section titled “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.4-nano", max_completion_tokens: 100,});
console.log(completion.choices[0].message.content);Bring Your Own API Key
Section titled “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.4-nano", 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
Responses API & Web Search
Section titled “Responses API & Web Search”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.4-nano", 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
Section titled “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.4-nano", 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
Section titled “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.4-nano", max_completion_tokens: 200,});OpenAI Agents SDK
Section titled “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 keysetDefaultOpenAIClient(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
Section titled “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 10 requests per minute.
- Features: Chat completions and the Responses API are the only endpoints
available. The Responses API supports the
web_searchtool and structured outputs (text.formatwithjson_schema).
If these limits are too low, let us know! You can also get around the limitation by bringing your own API key.