ODP Command-Line Interface
Overview
openbb-cli is a command-line interface for the OpenBB Platform and any other OpenAPI 3.x server. It runs in three modes:
- Non-TTY one-shot (default) —
openbb <command.path> [--key value]dispatches one command, prints a JSON line, exits. The form CI tools and agents reach for. - Batch —
openbb --batchreads NDJSON requests from stdin, fans them out concurrently, writes NDJSON responses to stdout. - Interactive REPL —
openbb -idrops into a rich-output prompt with auto-completion, menu navigation, and routine playback.
Backends are pluggable: dispatch through an in-process obb namespace, an openbb-platform-api HTTP server, or any OpenAPI 3.x server (e.g. https://api.congress.gov) without touching code.
The full user docs live at docs.openbb.co/odp/cli.
Backends
Four sources for the command surface. Three are OpenBB-aware; the fourth is the generic OpenAPI 3.x fallback.
| Source | Flag | Cold start | When to use |
|---|---|---|---|
In-process obb |
(default) | slow (import openbb ~2s) |
Local script with openbb already installed; no separate process to manage. |
| OpenBB Platform server | --server URL |
fast after first fetch | Long-running shared server (openbb-platform-api) — many CLI invocations or many users amortize the import cost. |
OpenBB .spec file |
--spec PATH |
fastest (~50ms) | Ship the spec next to scripts/agents; skips OpenAPI fetch + parse on every call. Generate with openbb --generate-spec --server URL -o file.spec. |
| Arbitrary OpenAPI 3.x server | --server URL (non-OpenBB host) |
depends on host | Any external API with an OpenAPI document — Congress.gov, NY Fed, USDA, NWS, your own FastAPI service. |
The first three speak the same OpenBB Platform surface (the OBBject envelope, providers, the obb.reference menu tree). The fourth treats whatever the upstream publishes as the truth — same dispatcher, same parser, same auth, just no OpenBB-specific affordances.
What's different about OpenBB upstreams
Multi-provider Pydantic models and the OBBject envelope are OpenBB Platform conventions, not OpenAPI ones. The CLI detects an OpenBB endpoint by the presence of a provider discriminator parameter on the operation and turns on extra behavior:
| Feature | OpenBB upstream | Generic OpenAPI upstream |
|---|---|---|
| Response envelope | OBBject — id, results, provider, warnings, chart, extra |
Whatever the server returns |
--describe COMMAND |
Groups parameters + result schema per provider | Flat {parameters, output_schema} |
--describe COMMAND:PROVIDER |
Returns just that provider's slice | Suffix ignored |
| Per-provider argparse narrowing | --provider X filters accepted flags; passing a flag from another provider errors at parse time |
Every declared flag accepted |
| Help text per provider | (provider: …) annotations stripped; only sections relevant to the chosen provider |
Description shown verbatim |
| REPL menu tree | Mirrors obb.reference (router descriptions, command groupings) |
Built from URL path prefixes |
| Boolean flags | --flag / --no-flag toggle (OpenBB exposes default: true booleans) |
Same — applies to any spec |
Everything else — --list-commands, --describe COMMAND, --batch, headers/query auth, spec generation, REPL playback — works identically against both kinds of upstream.
Installation
pip install openbb-cli
Optional extras:
pip install "openbb-cli[charting]" # Plotly + openbb-charting
pip install "openbb-cli[interactive]" # PyWry browser-based tables
pip install "openbb-cli[all]" # everything
Quick start
One-shot dispatch
# Against the local in-process obb namespace (the historical default)
openbb economy.gdp --provider oecd --limit 5
# Against any OpenAPI server — auto-detects spec layout, $ref-resolves params,
# auto-extracts HTML-embedded specs (e.g. Congress.gov)
openbb --server http://127.0.0.1:6900 equity.price.historical --symbol AAPL --provider fmp
openbb --server https://api.congress.gov bill --limit 5
Spec files for instant cold-start
# Generate once; ship alongside the script that invokes the CLI
openbb --generate-spec --server https://api.congress.gov --output congress.spec
# Subsequent calls skip the OpenAPI fetch + parse on every invocation
openbb --spec congress.spec law --congress 119 --limit 5
Generate an installable OpenBB extension from a spec
A .spec file is enough to dispatch commands directly. Going one step further, --generate-extension turns that spec into a full installable OpenBB Platform extension package — Provider(...) + Fetcher classes + a router that mirrors the upstream's namespace tree — that registers with openbb-build like any first-party extension. After install, every command shows up on the typed obb.* surface (auto-completion, obb.reference, OBBject envelopes, the works).
End-to-end with the NY Fed Markets API:
# 1. Snapshot the OpenAPI surface as a spec file. NY Fed publishes its
# spec at a non-default path, so point --openapi-path at the YAML.
openbb --generate-spec \
--server https://markets.newyorkfed.org \
--openapi-path /static/docs/markets-api.yml \
--output nyfed.spec
# wrote 55 commands to nyfed.spec
# 2. Generate a complete extension project from that spec
openbb --generate-extension \
--spec nyfed.spec \
--provider-name nyfed \
--output ./openbb-nyfed
# wrote project to ./openbb-nyfed/openbb-nyfed
# providers (1): nyfed
# routers (10): ambs, fxs, guidesheets, marketshare, pd, rates, rp, seclending, soma, tsy
# fetchers: 55 GET (across all providers)
# POST: 0 local-compute commands
# install: pip install -e ./openbb-nyfed/openbb-nyfed
# build: openbb-build
# 3. Install + register with the OpenBB Platform
pip install -e ./openbb-nyfed/openbb-nyfed
openbb-build
# 4. The new namespaces are live on the typed obb surface
python -c "from openbb import obb; print(obb.nyfed.rates.all.latest().to_df())"
(--openapi-path is only needed when the upstream doesn't expose /openapi.json directly. For servers that do — most FastAPI deployments, Congress.gov via the embedded-spec scraper — drop the flag.)
What --generate-extension produces:
openbb-nyfed/
├── pyproject.toml # PEP 621 / Hatchling — every provider + router declared as entry points
├── README.md
└── openbb_nyfed/
├── providers/
│ └── nyfed/
│ ├── __init__.py # nyfed_provider = Provider(name="nyfed", fetcher_dict={...}, credentials=[...])
│ └── models/<command>.py # one Fetcher class per command (QueryParams + Data + Fetcher)
└── routers/
├── rates.py # @router.command(model="RatesAllLatest") — typed signature
├── ambs.py
└── ... # one router per top-level namespace; sub-routers nest via include_router(prefix="/sub")
Every flag is optional except --spec:
| Flag | Default | What it controls |
|---|---|---|
--output PATH |
required | Project root directory written to disk. |
--provider-name NAME |
derived from --output basename |
Snake-case provider identifier — drives credential lookup keys (credentials.get(f"{provider}_api_key")). |
--project-name NAME |
openbb-<provider-name> |
PyPI distribution name in pyproject.toml. Use dashes. |
--package-name NAME |
openbb_<provider-name> |
Snake-case Python package name. |
--router-name NAME |
<provider-name> |
Top-level router identifier. |
Bring-your-own-key APIs: parameter names that look like credentials (api_key, apikey, app_token, X-API-Key, Authorization, client_secret, bearer_token, …) are auto-promoted to the provider's credentials=[...] list. After install, openbb reads them through the standard user-settings flow (OBB_USER_SETTINGS / ~/.openbb_platform/user_settings.json) — no per-call flag needed.
Multi-spec — combine APIs under one CLI
Repeat --spec NAME=PATH to mount more than one spec at once. Each spec's commands get prefixed with its namespace, and each backend keeps its own base_url, headers, and query params:
openbb \
--spec congress=congress.spec \
--spec nyfed=nyfed.spec \
congress.bill --limit 5
openbb \
--spec congress=congress.spec \
--spec nyfed=nyfed.spec \
nyfed.markets.ambs --operation rates
Scope auth per namespace by prefixing the flag value with <NAME>::
openbb \
--spec congress=congress.spec \
--spec usda=usda.spec \
-Q congress:api_key="$CONGRESS_KEY" \
-Q usda:api_key="$USDA_KEY" \
-H usda:Authorization="Bearer $USDA_BEARER" \
congress.bill --limit 5
Tokens without a namespace prefix (-H Authorization=..., -Q api_key=...) apply to every backend; namespace-scoped tokens override on conflict for that one backend only.
The same shape works in TOML, which is the better fit when scripts reach for the same set repeatedly:
[specs.congress]
path = "/path/congress.spec"
[specs.congress.query]
api_key = "..."
[specs.usda]
path = "/path/usda.spec"
[specs.usda.headers]
Authorization = "Bearer ..."
[specs.usda.query]
api_key = "..."
--list-commands aggregates across every namespace; --describe NAMESPACE.command[:provider] resolves to the right backend automatically.
A single unnamed --spec PATH keeps the flat (unprefixed) command surface — backward-compatible with the historical single-spec form.
Auth
Pick whichever the upstream API uses:
# Headers (Authorization, X-API-Key, ...)
openbb --server URL -H "Authorization: Bearer xxx" -H "X-Tenant: acme" cmd
# Query-string params (e.g. ?api_key=... like Congress.gov, USDA, NWS)
openbb --server URL -Q api_key=xxx cmd
# or via env — OPENBB_HTTP_QUERY_<NAME> auto-injected
OPENBB_HTTP_QUERY_API_KEY=xxx openbb --server URL cmd
Auth hooks (RBAC, token refresh, dynamic credentials)
Static headers and query params cover the common case. For RBAC, expiring tokens, or per-user credentials sourced from a vault, register an importable callable as an auth hook. Configured in TOML by module:attribute path — global, or per [specs.<ns>]:
auth-hook = "myapp.auth:default_hook" # applies to every backend
[specs.congress]
path = "/path/congress.spec"
auth-hook = "myapp.auth:congress_hook" # overrides the global for this one
[specs.internal]
path = "/path/internal.spec"
auth-hook = "myapp.auth:rbac_hook"
The hook receives an AuthContext (namespace, command, params, method) and returns an AuthDecision:
# myapp/auth.py
from openbb_cli.auth import AuthContext, AuthDecision
from myapp.identity import current_user, get_token
def rbac_hook(ctx: AuthContext) -> AuthDecision:
user = current_user()
if not user.can_access(ctx.namespace, ctx.command):
return AuthDecision(allow=False, deny_reason=f"{user.role} cannot call {ctx.command}")
return AuthDecision(headers={"Authorization": f"Bearer {get_token(user)}"})
Hooks may be sync or async; coroutines are awaited. Returned headers and query params merge on top of the dispatcher's static auth (hook wins on conflict). allow=False short-circuits the dispatch with an AccessDenied error response — no network call is made.
Introspection is gated by the same hook: --list-commands invokes it for every command and silently drops denied entries from the listing, and --describe COMMAND returns AccessDenied when the hook denies. RBAC implementations that hide endpoints therefore hide them everywhere — discovery, schema, and dispatch all see the same surface.
Batch
NDJSON request/response over stdin/stdout, concurrent dispatch:
cat <<'EOF' | openbb --spec congress.spec --batch
{"id":"a","command":"bill","params":{"limit":5,"format":"json"}}
{"id":"b","command":"law","params":{"congress":119,"format":"json"}}
{"id":"c","command":"__commands__"}
EOF
Introspection
openbb --spec congress.spec --list-commands # every command + short description
openbb --spec congress.spec --describe bill # full schema (params + response)
The same calls work as reserved commands in batch mode (__commands__, __schema__).
Interactive REPL
openbb -i # in-process obb backend
openbb -i --spec congress.spec # spec-driven; no obb install required
openbb -i --server URL # live OpenAPI fetch
The REPL adds menu navigation (bill, .., home, exit), tab/auto-completion against parser flags, --help per command, the OBBject registry, and routine playback (exe --file ...).
Configuration
Layered, all optional. Resolution order (lowest → highest priority):
- Built-in defaults
[tool.openbb-cli]in nearest ancestorpyproject.toml~/.openbb_platform/openbb.toml(user-global; same dir asuser_settings.json)./openbb.toml(project-local, walks up from CWD)--config PATH(orOPENBB_CLI_CONFIG)~/.openbb_platform/.envand--env-file PATHOPENBB_*shell exports- CLI flags
Bootstrap a documented template:
openbb --print-config-template > ~/.openbb_platform/openbb.toml
Inspect what's currently active across all layers:
openbb --show-config
Schema:
# Backend / dispatch — pick one of these forms
server = "https://api.congress.gov" # live OpenAPI fetch
spec = "/path/to/api.spec" # single spec (flat surface)
batch-concurrency = 16
# Multi-spec: each table mounts under its own namespace
[specs.congress]
path = "/path/congress.spec"
[specs.congress.query]
api_key = "..."
[specs.usda]
path = "/path/usda.spec"
[specs.usda.headers]
Authorization = "Bearer ..."
[specs.usda.query]
api_key = "..."
# REPL display preferences (top-level shortcuts)
output-mode = "rich" # rich | json | tsv | html
flair = ":fox_face"
timezone = "America/New_York"
rich-style = "dark"
[headers] # global — applied to every backend
Authorization = "Bearer ..."
[query] # global — applied to every backend
api_key = "..."
[settings]
# Every other Settings field — same surface as the /settings/ REPL menu
allowed-number-of-rows = 50
use-prompt-toolkit = true
toolbar-hint = false
Documentation
Full user documentation: docs.openbb.co/odp/cli
API reference: docs.openbb.co/platform