---
title: "TYPESAFE_* environment variables across SDKs"
type: reference
tags: [environment-variables, configuration, python-sdk, javascript-sdk]
created: 2026-09-17
updated: 2026-09-17
confidence: high
sources:
  - raw/docs/sdk__python__api__constants.md
  - raw/docs/sdk__javascript__api__variables__ENV.md
  - raw/docs/sdk__javascript__api__type-aliases__EnvVar.md
  - raw/github/typesafe-sdk-python/src/typesafe_sdk/constants.py
  - raw/github/typesafe-sdk-js/src/env.ts
jev_version: "jev-1.13.0"
sdk_python: "0.6.0"
sdk_js: "0.6.0"
summary: "Exactly four TYPESAFE_* environment variables are read by the SDKs: API_KEY, BASE_URL, DEFAULT_MODEL, LOG_LEVEL — both SDKs read all four."
---

# 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`, and `TYPESAFE_PRICE` are **not** SDK environment variables — see [Names that are not SDK env vars](#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

```bash
export TYPESAFE_API_KEY="your-key-here"
```

```python
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)
```

```ts
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):

```bash
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 [[reference/models-and-pricing]]. |

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](#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_MODEL` changes which model answers without changing your code; the response's `model` field reports the version that actually answered, so log it (raw/docs/models.md).
- Setting `TYPESAFE_BASE_URL` redirects every request, including `GET /v1/models`. A wrong value typically surfaces as `404 Not Found` — see [[reference/rate-limits-and-errors]].
- The Python SDK marks `authorization`, `proxy-authorization`, `x-api-key`, `api-key`, `cookie`, and `set-cookie` as secret headers so debug logging does not print your key (raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/constants.py). Turning `TYPESAFE_LOG_LEVEL=debug` on is therefore safe with respect to the key itself.

## Related

- [[reference/python-sdk]] — client construction and `system_one()`
- [[reference/javascript-sdk]] — client construction and `systemOne()`
- [[reference/python-sdk-retries-errors]] — the other constants in `typesafe_sdk.constants`
- [[reference/http-api]] — what `Authorization` and the base URL do on the wire
- [[reference/models-and-pricing]] — what `jev-latest` resolves to
- [[guides/quickstart]] — 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
