Environment Variables
The Band SDK uses two configuration files: .env for environment variables and agent_config.yaml for agent credentials.
Neither file is loaded automatically. load_dotenv() from the python-dotenv package reads .env into the process environment, and load_agent_config() reads agent_config.yaml. The SDK reads two groups of variables out of the process environment itself, the platform URLs and the BAND_LOG_* logging knobs, so call load_dotenv() before you construct an Agent or configure logging. Everything else in .env, including provider API keys, reaches its consumer only because that library reads it. The bundled command-line tools read BAND_* variables directly. See Command-Line Tools.
For initial setup and installation, see the Setup tutorial. This page is a complete reference for all configuration options.
Configuration Files
Neither file ships in the installed package. .env.example and agent_config.yaml.example exist only in a clone of the SDK’s git repository, so cp .env.example .env fails in a project created with uv add band-sdk. Create both files yourself, with the contents shown in the sections below.
Add both files to .gitignore before your first commit. See Security.
Platform Connection
Two variables carry the platform URLs. Agent.create() resolves both itself: omit ws_url or rest_url, or pass None, and the SDK reads BAND_WS_URL / BAND_REST_URL, falling back to the Band Cloud URL when the variable is unset or empty.
Change the values only when connecting to a different environment, such as a self-hosted deployment.
An explicit argument always wins over the environment, so the tutorials keep passing ws_url=os.getenv("BAND_WS_URL") and rest_url=os.getenv("BAND_REST_URL"). That is still correct, and safe when the variable is unset, because None resolves the same way an omitted argument does. Writing the platform URL into the call is what makes a snippet self-describing.
load_dotenv() has to run before Agent.create(). The SDK reads the process environment, not the file, so a .env loaded afterwards has no effect on a connection already built.
Set both variables or neither. Each falls back independently, so setting only one points REST and WebSocket at different platforms, and nothing reports it: your credentials authenticate against one environment while messages stream from the other.
Agent Credentials
Agent credentials go in agent_config.yaml, not in environment variables. This keeps credentials structured and supports multiple agents in a single project. load_agent_config() looks for the file in the current working directory, so run your agent from the directory that holds it.
Load credentials in your code:
The key name (my_agent) matches the top-level key in the YAML file. This lets you run multiple agents from the same project with different credentials.
LLM Provider Keys
Add your LLM provider API keys to .env. Band never reads these; the provider client libraries read them from the environment themselves, once load_dotenv() has run.
Set only the keys for the providers you use. No key is required unless the model you configure needs it.
Framework-Specific Variables
Some LLM frameworks use their own environment variables:
LangChain and LangSmith read these, not Band. They are optional and only needed for framework features like tracing.
Command-Line Tools
The three console scripts the package installs read these variables directly. For band-trigger and band-acp each variable supplies the default for a matching CLI flag.
Band SDK Logging
Band logging is opt-in. Two entry points apply the same one process-wide
configuration: configure_logging() takes it as arguments, and LogSettings
reads and validates it from the BAND_LOG_* environment variables.
From arguments
For an embedded SDK, show Band logs while leaving unrelated application loggers at WARNING:
For an agent, runner, or CLI that owns the process, also show that process’s own loggers:
root_level applies to all non-Band loggers, not just the process’s own, so
third-party dependencies (e.g. httpx) become as verbose as the level you pass.
Use extra_loggers to pin individual dependencies back down, or
chatty_logger_levels() for the Band HTTP and WebSocket dependencies as a group.
The rich and json styles require the optional logging extra:
From the environment
LogSettings maps the same configuration onto environment variables. Construct
it and call configure():
For an agent, runner, or CLI that owns the process, for_application() raises the
process’s own loggers to the Band level first:
for_application() raises the level for all non-Band loggers, not just the
process’s own, so third-party dependencies also become as verbose as
BAND_LOG_LEVEL. It is a no-op when BAND_LOG_ROOT_LEVEL is already set, since
an explicit root level is never overwritten.
Explicit LogSettings constructor values take precedence over environment
variables, which take precedence over the defaults above. An empty environment
value falls back to the default. LogSettings.create(...) drops None fields, so
an optional CLI flag that was not passed leaves the environment value alone:
BAND_LOG_OVERRIDES is merged with the extra_loggers mapping the application
passes to configure(); the environment override wins when both name the same
logger. configure_logging_from_env() is shorthand for
LogSettings().configure().
Applying order and inspection
Both entry points call logging.config.dictConfig, which replaces the root
handlers. Configure Band logging first, then attach any handlers your host
application adds on top. To inspect or merge the configuration instead of
applying it, build_logging_config() takes the same arguments as
configure_logging() and LogSettings().build_config() takes the same arguments
as configure(); both return the dictConfig dictionary.
OpenTelemetry
The host application owns OpenTelemetry providers, processors, exporters, and
handlers. Band creates and exports no telemetry of its own. What it does provide
is correlation: the json style includes otelTraceID, otelSpanID,
otelTraceSampled, and otelServiceName in every record, so a log pipeline reads
one shape whether or not the host instrumented the process. Without
instrumentation those four fields serialize as null.
Configure Band logging before attaching an OpenTelemetry log handler, because
dictConfig would otherwise drop it:
Select the json style to get the correlation fields, either with
BAND_LOG_CONSOLE_STYLE=json in the environment or
LogSettings(log_console_style=LoggingStyle.JSON) in code. Both need
band-sdk[logging] installed; without it the SDK raises BandConfigError naming
the missing python-json-logger dependency.
If you replace the default JSON field list with json_fields=..., splice
OTEL_CORRELATION_FIELDS from band.logging_config back in or the correlation
keys are dropped.
The Pydantic AI adapter accepts instrument=True, False, or an
InstrumentationSettings instance and forwards it to Pydantic AI. Other
frameworks are instrumented through their own OpenTelemetry integration, and
remote or out-of-process model backends emit their model spans outside the Band
SDK process. The SDK repository has a runnable
host-owned OpenTelemetry example
with console exporters, trace-context injection, and provider flush and shutdown.
Complete .env Example
Python-Level Configuration
The SDK also provides Python configuration objects for runtime behavior. See the SDK Reference for AgentConfig and SessionConfig documentation.
Security
Add Both Files to .gitignore
Both .env and agent_config.yaml contain secrets. Add them to .gitignore to prevent accidental commits.
Confirming a Successful Connection
Agent is running! Press Ctrl+C to stop. is a log line in the tutorial code, not in the SDK. It runs before agent.run() authenticates, so it appears verbatim even when the agent ID and API key are wrong. Read it as “the process started”, not as “the configuration works”.
The line that confirms a successful connection comes from the SDK’s own band.agent logger:
The SDK emits it only after it has authenticated against the REST API, fetched the agent’s metadata, and connected the WebSocket. Any failure in that sequence, including a 401 from invalid credentials, raises instead. The agent name in the line comes from the platform, so seeing your agent’s real name also confirms the credentials resolved to the agent you expect.
The line confirms that authentication succeeded, not which environment it succeeded against. REST and WebSocket resolve separately, so setting only one of BAND_REST_URL / BAND_WS_URL lets production credentials authenticate and print this line while messages stream from somewhere else. No log line reports either URL, so confirm the environment by checking that .env sets both variables, or by logging the values that reach Agent.create().
Earlier in the same startup sequence, band.platform.link confirms the WebSocket and band.runtime.platform_runtime confirms the runtime:
Both lines are INFO records on the band logger tree, and Band logging is opt-in, so call configure_logging() or LogSettings().configure() before agent.run() or you will see neither.