How Environment Variables Are Used for API Keys in MoneyPrinterV2: A Complete Guide to GEMINI_API_KEY Configuration

MoneyPrinterV2 implements a dual-source configuration system that checks config.json first and falls back to the GEMINI_API_KEY environment variable when the API key is missing or empty.

The open-source MoneyPrinterV2 repository by FujiwaraChoki manages sensitive credentials like the Gemini (Nanobanana2) API key through a flexible fallback mechanism. This approach allows developers to keep secrets out of version control while maintaining convenience for local development and CI/CD pipelines.

The Dual-Source Configuration Pattern

MoneyPrinterV2 stores non-sensitive configuration in config.json at the repository root. However, for security-critical values like API keys, the application implements a priority-based lookup system:

  1. Primary Source: The nanobanana2_api_key field in config.json
  2. Fallback Source: The GEMINI_API_KEY environment variable

This pattern ensures that if the JSON configuration is empty or omitted, the application can still retrieve the credential from the shell environment.

How GEMINI_API_KEY Fallback Works in src/config.py

The core logic resides in src/config.py within the get_nanobanana2_api_key() function (lines 115-124). This helper reads the local configuration file and implements the environment variable fallback:


# From src/config.py

import json
import os

def get_nanobanana2_api_key():
    with open("config.json", "r") as f:
        config = json.load(f)
    
    # Priority: config.json value, then GEMINI_API_KEY env var

    configured = config.get("nanobanana2_api_key", "")
    return configured or os.environ.get("GEMINI_API_KEY", "")

Key implementation details:

  • The function uses os.environ.get() to safely retrieve the environment variable without raising a KeyError if unset
  • The or operator ensures that an empty string in config.json (falsy) triggers the fallback
  • If neither source provides a value, the function returns an empty string

Validation and Preflight Checks

Before the main application executes, scripts/preflight_local.py performs sanity checks to ensure the API key is available from either source (lines 85-95):


# From scripts/preflight_local.py

import json
import os
import sys

def validate_api_key():
    with open("config.json") as f:
        cfg = json.load(f)
    
    api_key = cfg.get("nanobanana2_api_key", "") or os.environ.get("GEMINI_API_KEY", "")
    
    if not api_key:
        print("ERROR: Gemini API key not found in config.json or GEMINI_API_KEY environment variable")
        sys.exit(1)
    
    return api_key

This validation step prevents runtime failures by ensuring the credential exists before any expensive operations begin.

Consuming the API Key in Provider Classes

Individual service providers retrieve the key through the configuration helper. For example, src/classes/YouTube.py uses the function to access Gemini capabilities for video title generation (lines 331-334):


# From src/classes/YouTube.py

from src.config import get_nanobanana2_api_key

class YouTube:
    def generate_title(self, description):
        api_key = get_nanobanana2_api_key()
        # Use api_key with Gemini client...

This pattern ensures consistent credential management across all modules that interact with the Gemini API.

Configuration Precedence and Security Best Practices

Understanding the precedence rules helps prevent configuration conflicts:

Precedence Order (highest to lowest):

  1. config.json value (if non-empty)
  2. GEMINI_API_KEY environment variable
  3. Empty string (application handles as missing)

Security recommendations:

  • Never commit config.json with real API keys to version control
  • Use .env.example (provided in the repository root) as a template for required environment variables
  • Set GEMINI_API_KEY in CI/CD pipelines via secret management tools rather than storing in repository files
  • Keep config.json for non-sensitive settings like verbose or headless mode

Example environment setup:


# Export for current session

export GEMINI_API_KEY="your-actual-api-key-here"

# Or add to .env file (if using python-dotenv)

echo "GEMINI_API_KEY=your-actual-api-key-here" >> .env

Summary

  • MoneyPrinterV2 uses a dual-source configuration system where config.json takes precedence over environment variables
  • The GEMINI_API_KEY environment variable serves as a fallback when nanobanana2_api_key is empty in config.json
  • Implementation resides in src/config.py within the get_nanobanana2_api_key() function
  • Validation occurs in scripts/preflight_local.py to ensure the key exists before runtime
  • Consumers like src/classes/YouTube.py retrieve the key through the centralized configuration helper

Frequently Asked Questions

What happens if both config.json and GEMINI_API_KEY are set?

If the nanobanana2_api_key field in config.json contains a non-empty string, that value takes precedence and the GEMINI_API_KEY environment variable is ignored. Only when the JSON value is empty or missing does the system check the environment variable.

Is it safe to commit config.json with an empty nanobanana2_api_key field?

Yes, committing config.json with an empty nanobanana2_api_key value (or placeholder text) is safe and recommended. This allows the repository to contain the configuration structure without exposing secrets, while the actual API key can be injected via the GEMINI_API_KEY environment variable during runtime.

How do I set GEMINI_API_KEY for development on Windows?

On Windows Command Prompt, use set GEMINI_API_KEY=your-key-here for the current session, or use setx GEMINI_API_KEY "your-key-here" to persist it across sessions. On Windows PowerShell, use $env:GEMINI_API_KEY="your-key-here". For permanent configuration, set the variable through System Properties > Environment Variables.

Does MoneyPrinterV2 support .env files for loading environment variables?

The repository includes a .env.example file suggesting support for environment variables, but the core configuration system in src/config.py uses os.environ.get() directly. To use .env files, you would need to load them manually using a library like python-dotenv before importing the configuration module, or export the variables in your shell before running the application.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →