# How to Configure and Manage API Keys for Different LLM Providers in AI Hedge Fund

> Configure and manage API keys for OpenAI, Groq, Anthropic, and more LLM providers in ai-hedge-fund. Learn to use env variables or per-request overrides for secure access.

- Repository: [Virat Singh/ai-hedge-fund](https://github.com/virattt/ai-hedge-fund)
- Tags: how-to-guide
- Published: 2026-03-09

---

**You can configure API keys for OpenAI, Groq, Anthropic, and other LLM providers in the virattt/ai-hedge-fund repository by setting environment variables in a `.env` file or passing per-request overrides through the request state metadata.**

The AI Hedge Fund project integrates with multiple Large Language Model (LLM) providers and financial data services, requiring a flexible and secure credential management system. According to the source code in `virattt/ai-hedge-fund`, all secrets are injected at runtime via environment variables or request-level metadata, ensuring no hardcoded keys exist in the codebase. This guide explains how to configure and manage API keys for different providers while maintaining security best practices.

## Where API Keys Are Stored

### Environment Variables and `.env.example`

The repository uses a template-based approach for initial configuration. The **`.env.example`** file ships with placeholder values for every supported provider, including `OPENAI_API_KEY`, `GROQ_API_KEY`, `ANTHROPIC_API_KEY`, `DEEPSEEK_API_KEY`, `GOOGLE_API_KEY`, `OPENROUTER_API_KEY`, and `FINANCIAL_DATASETS_API_KEY`.

Users copy this file to `.env` and populate it with real secrets. The `python-dotenv` loader injects these into `os.getenv`, making them available throughout the application. This approach keeps credentials out of version control while providing a clear specification of required variables.

### Request-Level Overrides

For multi-tenant deployments or user-specific key management, the system supports **per-request API keys**. Clients can attach an `api_keys` dictionary to the request metadata, allowing individual users to supply their own credentials. These overrides take precedence over environment variables and are extracted via the helper function in [`src/utils/api_key.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/api_key.py).

## How the Code Retrieves API Keys

The retrieval logic follows a strict hierarchy in two primary locations:

1. **[`src/utils/api_key.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/api_key.py)** – Handles financial data keys and generic extraction from state
2. **[`src/llm/models.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/llm/models.py)** – Handles LLM provider keys via the `get_model` factory function

The `get_api_key_from_state` function (lines 3-8 in [`src/utils/api_key.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/api_key.py)) checks for request-level overrides:

```python
def get_api_key_from_state(state: dict, api_key_name: str) -> str:
    if state and state.get("metadata", {}).get("request"):
        request = state["metadata"]["request"]
        if hasattr(request, 'api_keys') and request.api_keys:
            return request.api_keys.get(api_key_name)
    return None

```

For LLM providers, the `get_model` function in [`src/llm/models.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/llm/models.py) implements a fallback pattern. It first checks an explicit `api_keys` dictionary passed from the request, then falls back to `os.getenv`. If neither source provides the key, it raises a clear `ValueError`.

## Configuring LLM Provider Keys

Each supported provider follows an identical configuration pattern in [`src/llm/models.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/llm/models.py). The system checks for the provider-specific environment variable only after exhausting request-level overrides.

The OpenAI implementation (lines 38-45) demonstrates this pattern:

```python

# src/llm/models.py – OpenAI example

api_key = (api_keys or {}).get("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY")
if not api_key:
    raise ValueError("OpenAI API key not found …")

```

This pattern repeats for every provider with their respective variable names:
- **Groq**: `GROQ_API_KEY`
- **Anthropic**: `ANTHROPIC_API_KEY`
- **DeepSeek**: `DEEPSEEK_API_KEY`
- **Google**: `GOOGLE_API_KEY`
- **OpenRouter**: `OPENROUTER_API_KEY`

The `call_llm` function in [`src/utils/llm.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/llm.py) (lines 42-49) orchestrates this by gathering the model configuration from the agent, then invoking `get_model` with the appropriate provider and key dictionary extracted from state.

## Configuring Financial Data API Keys

Financial data access uses the same dual-source approach but relies on the `get_api_key_from_state` helper. Agents requiring market data—such as the Warren Buffett agent—call this utility to retrieve the `FINANCIAL_DATASETS_API_KEY`.

In [`src/agents/warren_buffett.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/agents/warren_buffett.py) (line 24), the implementation looks like this:

```python
api_key = get_api_key_from_state(state, "FINANCIAL_DATASETS_API_KEY")

```

If the request metadata contains this key, the function returns it immediately. Otherwise, the agent falls back to the global environment variable defined in `.env`. Other agents like [`valuation.py`](https://github.com/virattt/ai-hedge-fund/blob/main/valuation.py) and [`technicals.py`](https://github.com/virattt/ai-hedge-fund/blob/main/technicals.py) follow this identical pattern.

## Per-Request API Key Override Workflow

To override keys for a specific request, populate the state object with a metadata dictionary containing the request object and its `api_keys` attribute:

```python

# Create request with provider-specific overrides

request = {
    "api_keys": {
        "OPENAI_API_KEY": "sk-user-provided-key",
        "GROQ_API_KEY": "gsk-user-provided-key",
        "FINANCIAL_DATASETS_API_KEY": "user-fd-key",
    }
}

# Initialize state structure

state = {
    "metadata": {"request": request},
    # ... other shared state ...

}

# The agent will now use request-level keys

from src.utils.api_key import get_api_key_from_state
fd_key = get_api_key_from_state(state, "FINANCIAL_DATASETS_API_KEY")

```

When `call_llm` executes, it passes this state through the pipeline. The `get_model` function receives the `api_keys` dictionary from the request state and uses it in preference to environment variables, enabling secure multi-user deployments where each request carries its own credentials.

## Security Best Practices

The repository enforces a strict **secrets-out-of-code** policy. Keys are never logged, and missing credentials trigger explicit exceptions rather than silent failures. When running in Docker or CI environments, inject secrets via runtime environment variables rather than baking them into images. For production deployments, consider using a secrets manager that populates environment variables at startup, maintaining the same interface while adding rotation capabilities.

## Summary

- **Configuration Template**: Copy `.env.example` to `.env` and fill in provider-specific keys like `OPENAI_API_KEY` and `GROQ_API_KEY`.
- **Runtime Retrieval**: The system uses `os.getenv` in [`src/llm/models.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/llm/models.py) and `get_api_key_from_state` in [`src/utils/api_key.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/api_key.py) to resolve credentials.
- **Request Overrides**: Pass custom keys via `state["metadata"]["request"].api_keys` to override environment variables for specific requests.
- **Error Handling**: Missing keys raise `ValueError` immediately, preventing silent authentication failures.
- **Financial Data**: Agents use the same state-based helper to access `FINANCIAL_DATASETS_API_KEY` without hardcoding references.

## Frequently Asked Questions

### What happens if an API key is missing at runtime?

The application raises a `ValueError` with a descriptive message indicating which provider key is missing. In [`src/llm/models.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/llm/models.py), each provider check explicitly validates the key presence after checking both the request dictionary and environment variables, ensuring you know immediately which credential to supply rather than receiving a cryptic authentication error from the provider.

### Can I use different API keys for different agents in the same request?

Yes. Since the `api_keys` dictionary in the request state can contain multiple provider keys (e.g., both `OPENAI_API_KEY` and `GROQ_API_KEY`), different agents can call different providers within the same workflow. Each agent's `call_llm` invocation extracts the specific key required for its configured model provider from the shared state, allowing heterogeneous agent composition using distinct credentials.

### How do I add support for a new LLM provider in the configuration system?

Add the provider-specific environment variable name (e.g., `NEW_PROVIDER_API_KEY`) to `.env.example`, then implement the key retrieval logic in [`src/llm/models.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/llm/models.py) following the existing pattern: check `(api_keys or {}).get("NEW_PROVIDER_API_KEY")` first, then fall back to `os.getenv("NEW_PROVIDER_API_KEY")`, and raise `ValueError` if neither exists. Finally, instantiate the appropriate LangChain model class with the resolved key.

### Is it safe to commit the `.env` file to version control?

No. The `.env` file contains sensitive secrets and should be listed in `.gitignore`. Only commit `.env.example`, which contains placeholder values. The example file serves as documentation for required variables without exposing actual credentials, maintaining the repository's security posture while helping new developers understand which API keys they need to obtain.