Staging · Test accounts and payments · No real purchases
IT'SGoodEnough

api reference

Less setup.
Smaller AI bill.

If your code speaks to the OpenAI API, it already speaks to ours. Point your client at our base URL and pick a model.

01Base URL

base url
https://hitujvywvh.us-east-2.awsapprunner.com/v1

this is the live production endpoint. Keep the /v1 suffix when setting your OpenAI-compatible client's base URL.

02Authentication

Send your API key as a standard bearer token on every request:

header
Authorization: Bearer $GOODENOUGH_API_KEY

03Models

gpt-oss-120b is the flagship — start there. It's a reasoning model: it spends tokens thinking (returned as reasoning_content) before the final answer, so give it generous max_tokens (512+). Explicit GPT-OSS requests with a smaller budget receive a 400 error. Reasoning and visible output share the budget, so larger budgets can still finish before the answer is complete.

Prices are USD per million tokens. Send model: "auto" and the router picks the right model per request — the response's model field tells you what it chose.

Auto uses GPT-OSS for general chat, Qwen3 Coder Next for code, and Qwen3 VL for images. JSON object mode, tool requests, and text requests with a budget below 512 tokens use Qwen3 Coder Next. Source exclusions and model access rules still apply; if no compatible route remains, the request receives a 400 error. Kimi K2.5, GLM 5, and Kimi K3 are available for explicit model selection.

For JSON, send response_format: {"type": "json_object"} with Auto or Qwen3 Coder Next and ask for JSON in your prompt. JSON schema mode is not currently supported. JSON output is validated; malformed or truncated JSON receives an error. JSON-mode streams are buffered for validation and report an error event if validation fails. Normal text streams remain immediate.

For parallel tool calls, use Auto or Qwen3 Coder Next. Multiple calls are supported, but the model can still choose to return one call; continue the tool-result conversation until the task is complete. With other explicit models, set parallel_tool_calls to false to request one call at a time. The gateway never increases your token budget or retries a paid request automatically.

model idnamesourceinput /Moutput /M
autoAuto (router)—$0.23$0.92
gpt-oss-120bGPT-OSS 120Bna$0.23$0.92
qwen3-coder-nextQwen3 Coder Nextcn$0.77$1.85
qwen3-vlQwen3 VL 235B-A22Bcn$0.82$4.09
kimi-k3Kimi K3 (Flagship)cn$5.00$25.00
kimi-k2.5-bedrockKimi K2.5cn$0.92$4.62
glm-5-bedrockGLM 5cn$1.54$4.92
typical frontier API (for reference)$3.00$15.00

Auto routing (IRL)

The router is a learned classifier (embedding + linear head) with deterministic constraint filters. With model: "auto" the request body may carry an optional preferences.exclude_sources field — a hard filter on model origin (values from the source column above) applied before the router picks:

bash
curl https://hitujvywvh.us-east-2.awsapprunner.com/v1/chat/completions \
  -H "Authorization: Bearer $GOODENOUGH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto", "messages": [{"role": "user", "content": "refactor this function"}],
       "preferences": {"exclude_sources": ["cn"]}}'

The response's model field reveals which model the router picked — no custom headers. If the filter excludes every servable model, the request fails with a 400.

Attachments & vision

Messages may carry OpenAI-style content parts: an array mixing text and image_url parts (data: URLs work). With model: "auto", any request carrying an image routes to the vision pool (qwen3-vl) — text pools never see images, and the vision pool only serves image requests:

bash
curl https://hitujvywvh.us-east-2.awsapprunner.com/v1/chat/completions \
  -H "Authorization: Bearer $GOODENOUGH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "messages": [{"role": "user", "content": [
      {"type": "text", "text": "What is in this image?"},
      {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBOR..."}}
    ]}]
  }'

The vision pool is optional per deployment; when it isn't deployed (or the provenance filter excludes it), image requests fail with a 400 naming the constraint.

Tool calling

Standard OpenAI function calling — tools + tool_choice (including "auto") — works on the production text models, streaming included, and passes through model: "auto" routing untouched:

python
resp = client.chat.completions.create(
    model="gpt-oss-120b",  # best tool-use judgment; see notes below
    messages=[{"role": "user", "content": "What's the weather in Toronto?"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a city.",
            "parameters": {
                "type": "object",
                "properties": {"city": {"type": "string"}},
                "required": ["city"],
            },
        },
    }],
    tool_choice="auto",
)
call = resp.choices[0].message.tool_calls[0]
# call.function.name == "get_weather"
# call.function.arguments == '{"city": "Toronto"}'

Model notes, from our own test suite: gpt-oss-120b is the recommendation for agentic workloads — it calls when it should, declines when it shouldn't, and handles tool-result turns and streaming cleanly. Qwen3 Coder Next handles code-focused requests; Qwen3 VL is reserved for image-bearing requests. Tool behavior can vary by provider model, so applications should validate tool arguments before execution.

04curl

bash
curl https://hitujvywvh.us-east-2.awsapprunner.com/v1/chat/completions \
  -H "Authorization: Bearer $GOODENOUGH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "messages": [{"role": "user", "content": "write a function that dedupes a list"}],
    "stream": true,
    "stream_options": {"include_usage": true}
  }'

05Python (openai client)

python
from openai import OpenAI

client = OpenAI(
    base_url="https://hitujvywvh.us-east-2.awsapprunner.com/v1",
    api_key="YOUR_GOODENOUGH_KEY",
)

resp = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "Summarize this support ticket: ..."}],
)
print(resp.model)  # what the router picked, e.g. "gpt-oss-120b"
print(resp.choices[0].message.content)

06JavaScript — change your baseURL

js
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://hitujvywvh.us-east-2.awsapprunner.com/v1", apiKey: process.env.GOODENOUGH_API_KEY });
// done — every existing chat.completions call now runs on Good Enough

07Errors

Errors come back as OpenAI-style JSON: an error object with message, type , nullable param, and code. Invalid or empty messages, unsupported capabilities, conflicting source restrictions, and too-small GPT-OSS token budgets return 400 with type invalid_request_error and the relevant param. Provider failures use type api_error. The three you are most likely to meet:

401 — invalid or missing key

The Authorization header is missing, malformed, or carries a key we don't recognize (including revoked keys). Send the bearer header exactly as in section 02. Keys are shown once at creation — if yours is lost, mint a new one in the console.

400 — out of credits (budget_exceeded)

When your account's credit balance is used up, every request is refused with error.type: "budget_exceeded":

json — HTTP 400
{
  "error": {
    "message": "ExceededBudget: User=you@example.com over budget. Spend=5.0041, Budget=5.0",
    "type": "budget_exceeded",
    "code": "400"
  }
}

top up in the console; requests resume immediately.

429 — rate limited

Keys default to 60 requests/min (spend is separately bounded by your credit balance). On a 429, wait a moment and retry — exponential backoff starting around one second clears it. If you need a higher sustained rate, get in touch.

Streaming works the standard way: set stream: true (and stream_options.include_usage to get a final usage chunk). SSE lines arrive as data: JSON, terminated by data: [DONE].