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: unset (the level is only applied "if it names a known level"). JS: warn. |
Logging level for the SDK logger. Python: "set TYPESAFE_LOG_LEVEL to one of debug, info, warning, error, or off before importing the SDK"; it is applied once at import to the typesafe_sdk logger. (warn is also accepted by the source but is not listed in the docs.) 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)).
Both SDKs trim and treat a blank string as unset. JS: process.env[name]?.trim() || undefined (raw/github/typesafe-sdk-js/src/env.ts). Python, in _core/config.py:_resolve_string: value if value is not None else os.environ.get(env, "").strip() or default — and as of 0.7.1 the client doc states it outright, "Explicit options take precedence over environment variables; empty or whitespace-only environment values are ignored."
TYPESAFE_API_KEY validation (Python, 0.7.1)
The Python SDK validates the key inside the client constructor, before any request (resolve_and_validate_api_key in _core/config.py):
| Rule | Behavior |
|---|---|
| Surrounding whitespace | Stripped, "including newlines from key files". |
| Empty after stripping | TypeSafeError("No API key was provided. Pass api_key or set the TYPESAFE_API_KEY environment variable.") |
| Non-ASCII, non-printable, or containing a space | TypeSafeError("API key must contain only printable ASCII characters without whitespace.") |
api_key="" passed explicitly |
Does not fall back to TYPESAFE_API_KEY: "An explicitly empty key does not fall back to the environment." |
Practical effect: export TYPESAFE_API_KEY="$(cat key.txt)" is safe (the trailing newline is stripped), but a key pasted with a stray space fails fast at TypeSafeClient() rather than as a 401 later. No equivalent key validation is documented for the JS SDK at 0.6.0.
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. |
Re-verified by grepping every TYPESAFE_[A-Z_]+ occurrence across raw/ on 2026-09-21: only the four names in The table appear in raw/github/typesafe-sdk-python/src or raw/github/typesafe-sdk-js/src as environment-variable names, and the four names above are the only other ones in the official sources. Three further names now appear, all in captured community repos (raw/x-repos/, community tier — see Community repos: what people built and how they use Jev), none of them read by either SDK:
| Name | Where | What it is |
|---|---|---|
TYPESAFE_AI_API_KEY |
raw/x-repos/jarrodwatts__jev-trader.md | That project's own variable for its Jev key; the SDK-native name is TYPESAFE_API_KEY. |
TYPESAFE_MOCK |
raw/x-repos/monteduro__killmyidea.md | That project's "run the UI without a key" switch (TYPESAFE_MOCK=1). |
TYPESAFE_CONTRACT |
raw/x-repos/TianyuCodings__NanoJev.md | Not a variable at all — a filename, docs/TYPESAFE_CONTRACT.md. |
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. It is also the supported way to route through an AI gateway (OpenRouter, Vercel AI Gateway), in which case the gateway's key goes inTYPESAFE_API_KEYand the gateway's model id inTYPESAFE_DEFAULT_MODEL; the examples are in Python SDK: install, clients, system_one(). - 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 — and since 0.7.1 the key is also scrubbed from transport-exception messages and chains (_core/logging.py:redact_exception). Request and response bodies are still printed unredacted atdebug.
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) — unchanged in the 2026-09-21 refresh
- raw/docs/sdk__python__usage.md (https://docs.typesafe.ai/sdk/python/usage) — the env-var table, key-validation rules, and log levels
- raw/docs/sdk__python__api__clients__sync.md (https://docs.typesafe.ai/sdk/python/api/clients/sync)
- 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/config.py, .../_core/client/sync/client.py, .../_core/client/aio/client.py, .../_core/constants.py, .../_core/logging.py (@ 0ffd094c72ed9445223060b24ffd7a56aa781fb4, captured 2026-09-21)
- 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