TYPESAFE_* environment variables across SDKs
TL;DR Four environment variables exist, and both SDKs read all four:
TYPESAFE_API_KEY,TYPESAFE_BASE_URL,TYPESAFE_DEFAULT_MODEL,TYPESAFE_LOG_LEVEL. Explicit constructor options always take precedence.TYPESAFE_MODEL,TYPESAFE_ENDPOINT,TYPESAFE_LABEL, andTYPESAFE_PRICEare not SDK environment variables — see Names that are not SDK env vars.
The table
| Variable | Read by | Default when unset | Meaning |
|---|---|---|---|
TYPESAFE_API_KEY |
Python SDK, JavaScript SDK | none — required | The API key, sent as Authorization: Bearer <key>. Python: "Required API key; may be set via the TYPESAFE_API_KEY environment variable." JS: "Required API key; used when apiKey is omitted." |
TYPESAFE_BASE_URL |
Python SDK, JavaScript SDK | https://api.typesafe.ai |
API root. Python constant DEFAULT_BASE_URL = 'https://api.typesafe.ai'; JS: "API root; defaults to https://api.typesafe.ai." |
TYPESAFE_DEFAULT_MODEL |
Python SDK, JavaScript SDK | jev-latest |
Default model name used when a call does not pass model. Python constant DEFAULT_MODEL = 'jev-latest'; JS: "Default model name; defaults to jev-latest." |
TYPESAFE_LOG_LEVEL |
Python SDK, JavaScript SDK | Python: not stated (the level is only applied "if it names a known level"). JS: warn. |
Logging level for the SDK logger. Python: "set TYPESAFE_LOG_LEVEL (debug/info/...) and the level is applied" to the typesafe_sdk logger. JS: "Log level; defaults to warn." |
Precedence, stated identically in both SDKs: explicit options take precedence over the environment (raw/docs/sdk__javascript__api__variables__ENV.md; fromCodeOrEnv in raw/github/typesafe-sdk-js/src/env.ts returns fromCode ?? readEnv(envVar)).
The JS SDK also trims values and treats a blank string as unset: process.env[name]?.trim() || undefined (raw/github/typesafe-sdk-js/src/env.ts). No equivalent trimming is documented for Python.
Where each name is defined
| SDK | Symbol | Value | Source |
|---|---|---|---|
| Python | typesafe_sdk.constants.API_KEY_ENV |
'TYPESAFE_API_KEY' |
raw/docs/sdk__python__api__constants.md, raw/github/typesafe-sdk-python/src/typesafe_sdk/constants.py:3 |
| Python | typesafe_sdk.constants.BASE_URL_ENV |
'TYPESAFE_BASE_URL' |
same, line 6 |
| Python | typesafe_sdk.constants.DEFAULT_MODEL_ENV |
'TYPESAFE_DEFAULT_MODEL' |
same, line 9 |
| Python | typesafe_sdk.constants.LOG_LEVEL_ENV |
'TYPESAFE_LOG_LEVEL' |
same, line 12 |
| JavaScript | ENV.apiKey |
"TYPESAFE_API_KEY" |
raw/docs/sdk__javascript__api__variables__ENV.md, raw/github/typesafe-sdk-js/src/env.ts:4 |
| JavaScript | ENV.baseURL |
"TYPESAFE_BASE_URL" |
same, line 6 |
| JavaScript | ENV.defaultModel |
"TYPESAFE_DEFAULT_MODEL" |
same, line 8 |
| JavaScript | ENV.logLevel |
"TYPESAFE_LOG_LEVEL" |
same, line 10 |
The JS type alias EnvVar is typeof ENV[keyof typeof ENV] — i.e. the union of exactly those four string literals (raw/docs/sdk__javascript__api__type-aliases__EnvVar.md). Anything outside that union is not readable through the SDK's readEnv.
Client defaults that are not environment variables
| Python constant | Value | Meaning |
|---|---|---|
DEFAULT_BASE_URL |
'https://api.typesafe.ai' |
Default API base URL. |
DEFAULT_MODEL |
'jev-latest' |
Default model name. |
DEFAULT_TIMEOUT |
10.0 |
Default timeout in seconds for each HTTP operation. |
The JS equivalent of the timeout is DEFAULT_TIMEOUT_MS = 10_000 (raw/github/typesafe-sdk-js/src/retry.ts). Neither SDK exposes a timeout environment variable.
Constructor equivalents
| Environment variable | Python TypeSafeClient(...) / AsyncTypeSafeClient(...) argument |
JS new TypeSafeClient({...}) option |
|---|---|---|
TYPESAFE_API_KEY |
api_key |
apiKey |
TYPESAFE_BASE_URL |
base_url |
baseURL |
TYPESAFE_DEFAULT_MODEL |
model |
defaultModel |
TYPESAFE_LOG_LEVEL |
(logger configuration; the variable is a "quick default") | logLevel |
Python argument names and docstrings from raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/client/sync/client.py and .../aio/client.py; JS option names from raw/github/typesafe-sdk-js/src/types.ts (TypeSafeClientConfig), which documents the fallbacks as "falls back to TYPESAFE_API_KEY", "falls back to TYPESAFE_BASE_URL, then https://api.typesafe.ai", "falls back to TYPESAFE_DEFAULT_MODEL, then jev-latest", and "falls back to TYPESAFE_LOG_LEVEL, then warn".
Usage
export TYPESAFE_API_KEY="your-key-here"
from typesafe_sdk import Noul, TypeSafeClient
# api_key, base_url and model all come from the environment (or their defaults)
with TypeSafeClient() as client:
response = client.system_one(
state="Help! My payouts have been failing for 3 days.",
questions={"is_urgent": Noul(instructions="Does this convey urgency?")},
)
print(response.answers["is_urgent"].noul)
import { choice, TypeSafeClient } from "@typesafe-ai/sdk";
// apiKey, baseURL and defaultModel all come from the environment (or their defaults)
const client = new TypeSafeClient();
const response = await client.systemOne({
state: { document: "I was charged twice. Please fix this ASAP." },
questions: {
category: choice("What is this ticket about?", {
billing: null,
technical: null,
other: null,
}),
},
});
console.log(response.answers.category.choice);
(Verbatim from raw/docs/sdk__javascript.md, which introduces it with "Set TYPESAFE_API_KEY in your environment, then create and use the client".)
The direct HTTP equivalent reads the same variable from the shell (raw/docs/introduction__quickstart.md):
curl https://api.typesafe.ai/v1/models \
-H "Authorization: Bearer $TYPESAFE_API_KEY"
Names that are not SDK env vars
Four other TYPESAFE_* names appear across raw/. None of them is read by either SDK; do not set them expecting an effect.
| Name | What it actually is | Where | Verdict |
|---|---|---|---|
TYPESAFE_MODEL |
A module-level Python constant in cookbook notebooks, e.g. TYPESAFE_MODEL = "jev-latest" and TYPESAFE_MODEL = "jev-1.12", passed explicitly as model=TYPESAFE_MODEL. |
raw/docs/cookbooks.md, raw/docs/cookbooks__citation_check.md, and ~15 other cookbook pages | Not an environment variable. The SDK variable for this purpose is TYPESAFE_DEFAULT_MODEL. |
TYPESAFE_ENDPOINT |
A cookbook convention: base_url=os.environ.get("TYPESAFE_ENDPOINT") passed explicitly into TypeSafeClient(...). Because it is passed as an explicit option, it overrides TYPESAFE_BASE_URL. |
raw/docs/cookbooks__citation_check.md:93, cookbooks__classification_using_confidence.md:85, cookbooks__autoresearch_feature_discovery.md:101, cookbooks__classifying_rag_passages.md:117, cookbooks__entity_alignment.md:101, cookbooks__llm_guardrails.md:78, cookbooks__rerank_typesafe.md:227, cookbooks__skill_suggestion.md:144 | Read from the environment, but by the cookbook code, not the SDK. TYPESAFE_BASE_URL is the SDK-native name. Note os.environ.get returns None when unset, which the client treats as "use the default". |
TYPESAFE_LABEL |
A Python constant naming a series in a benchmark chart: TYPESAFE_LABEL = "typesafe_choice". |
raw/docs/cookbooks__consistency_choice_cookbook.md:530 | Not an environment variable. |
TYPESAFE_PRICE |
A Python constant holding a price tuple: TYPESAFE_PRICE = (0.042, 0.00) # Historical TypeSafe rate, as of 2026-08 — dollars per 1M input and output tokens. |
raw/docs/cookbooks.md:90, cookbooks__consistency_choice_cookbook.md:95, cookbooks__consistency_noul_cookbook.md | Not an environment variable. Current pricing lives in Models, aliases, pricing, rate limits, context. |
Verified by grepping every TYPESAFE_[A-Z_]+ occurrence across raw/ (2026-09-17): only the eight names above appear anywhere, and only the four in The table appear in raw/github/typesafe-sdk-python/src or raw/github/typesafe-sdk-js/src as environment-variable names.
Non-TypeSafe variables that appear alongside them in cookbooks (OPENAI_API_KEY, ANTHROPIC_API_KEY) belong to those vendors' SDKs, not to TypeSafe.
Gotchas
- Cookbook code frequently uses
os.environ.get("TYPESAFE_API_KEY", "cache-only")so a keyless kernel replays a JSON cache instead of calling the API."cache-only"is a sentinel in the cookbook harness, not a real key (raw/docs/cookbooks__citation_check.md). - Setting
TYPESAFE_DEFAULT_MODELchanges which model answers without changing your code; the response'smodelfield reports the version that actually answered, so log it (raw/docs/models.md). - Setting
TYPESAFE_BASE_URLredirects every request, includingGET /v1/models. A wrong value typically surfaces as404 Not Found— see HTTP status codes, rate limits, retry semantics. - The Python SDK marks
authorization,proxy-authorization,x-api-key,api-key,cookie, andset-cookieas secret headers so debug logging does not print your key (raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/constants.py). TurningTYPESAFE_LOG_LEVEL=debugon is therefore safe with respect to the key itself.
Related
- Python SDK: install, clients, system_one() — client construction and
system_one() - JavaScript/TypeScript SDK: install, client, choice/score/noul — client construction and
systemOne() - Python SDK retries, exceptions, constants — the other constants in
typesafe_sdk.constants - HTTP API: POST /v1/systemone and GET /v1/models — what
Authorizationand the base URL do on the wire - Models, aliases, pricing, rate limits, context — what
jev-latestresolves to - Quickstart: first call in HTTP, Python, JS — setting the key for the first call
Sources
- raw/docs/sdk__python__api__constants.md (https://docs.typesafe.ai/sdk/python/api/constants)
- raw/docs/sdk__javascript__api__variables__ENV.md (https://docs.typesafe.ai/sdk/javascript/api/variables/ENV)
- raw/docs/sdk__javascript__api__type-aliases__EnvVar.md (https://docs.typesafe.ai/sdk/javascript/api/type-aliases/EnvVar)
- raw/github/typesafe-sdk-python/src/typesafe_sdk/constants.py, .../_core/client/sync/client.py, .../_core/client/aio/client.py, .../_core/constants.py
- raw/github/typesafe-sdk-js/src/env.ts, raw/github/typesafe-sdk-js/src/types.ts, raw/github/typesafe-sdk-js/src/retry.ts
- raw/docs/cookbooks.md and the cookbook pages cited in the table above