Setup

Managing API Keys Across Multiple Providers

⏱ 13 min

The previous lesson got one provider working. Most real installs end up with two or three configured at once, so provider-preference code (covered fully in Lesson 6) has something to actually fall back to. Before you get there, it’s worth understanding exactly where those keys live, who can read them, and the mistakes that turn “a convenient shortcut while developing” into a real security incident once the plugin ships.

What you'll learn in this lesson
Where provider plugins store keys by default
The wp_options table, and what that means for who can read them.
Why hardcoding a key in plugin PHP is a real risk
Version control, support tickets, and shared hosting all leak hardcoded secrets.
Using wp-config.php constants instead
A pattern that keeps keys out of the database and out of your plugin's files.
Managing several providers' keys side by side
Keeping Anthropic, OpenAI, and Google keys straight without name collisions.
Prerequisites

At least one provider plugin installed and configured, from the previous lesson. This lesson doesn’t require a second provider, but the patterns here matter more once you add one.

Step 1: Know where the key actually lives right now

The official provider plugins save the API key you pasted into their settings screen as a row in the wp_options table, the same table that stores your site’s general settings, active plugin list, and theme configuration. That means:

  • Anyone with database access (your host, a backup service, a misconfigured export) can read it in plain text unless the plugin specifically encrypts it, and most don’t.
  • A full database export, the kind you might casually hand to a support contractor to debug an unrelated issue, carries every configured API key along with it.
  • A site-cloning tool that copies the database to a staging environment copies the live key too, now two environments can spend against the same billing account.

None of this means the default storage is wrong, wp_options is the standard, expected place for plugin settings. It means you should treat a database export with the same care you’d treat the key itself.

Step 2: Never hardcode a key directly in plugin code

It’s tempting, especially while testing, to skip the settings screen and just write:

// File: my-plugin.php - don't do this
define( 'MY_PLUGIN_TEMP_KEY', 'sk-ant-api03-abc123...' );

or worse, to pass a literal key straight into a function call. The problem isn’t WordPress-specific, it’s the same problem with any hardcoded secret:

  • If the file ever gets committed to a public (or even private-but-shared) Git repository, the key is in the history forever, rotating it doesn’t remove it from old commits.
  • If you paste a code snippet into a support forum or a ticket to debug an issue, the key goes with it.
  • If the plugin is ever distributed to a client or another site, your personal key ships inside it.
A rotated key doesn't undo a leaked one

If a key was ever committed to version control or pasted somewhere public, rotating it in the provider’s dashboard stops future misuse, but treat the original as permanently compromised. Don’t assume “I deleted the commit” or “I edited my forum post” is sufficient, the provider-side revoke-and-reissue is the only real fix.

Step 3: Prefer wp-config.php constants for anything you control directly

For keys used in your own custom integration code (as opposed to the official provider plugins, which manage their own storage), defining a constant in wp-config.php keeps the value out of the database and out of your plugin’s version-controlled files:

// File: wp-config.php
define( 'MY_PLUGIN_ANTHROPIC_KEY', 'sk-ant-api03-...' );
// File: my-plugin.php
$key = defined( 'MY_PLUGIN_ANTHROPIC_KEY' ) ? MY_PLUGIN_ANTHROPIC_KEY : '';

if ( '' === $key ) {
	// Fail gracefully, don't silently proceed with an empty key.
	return new WP_Error( 'missing_api_key', 'No API key configured.' );
}

wp-config.php sits outside the web root on most reasonable hosting setups and is routinely excluded from version control (it’s in the default .gitignore for most WordPress starter projects), so this keeps the secret out of both places at once. For a plugin you intend to distribute to other sites, document this requirement clearly rather than inventing your own less-standard storage mechanism.

Step 4: Keep multiple providers’ keys straight

Once you’re managing keys for more than one provider, whether through the official plugins’ own settings screens or your own constants, name them so a future reader (or future you) can’t mix them up:

// File: wp-config.php
define( 'MY_PLUGIN_ANTHROPIC_KEY', 'sk-ant-...' );
define( 'MY_PLUGIN_OPENAI_KEY', 'sk-proj-...' );
define( 'MY_PLUGIN_GOOGLE_KEY', 'AIza...' );

Each provider’s key has a recognizable prefix (sk-ant-, sk-proj-, AIza), which is a small but genuinely useful sanity check, if a constant named ..._OPENAI_KEY holds a value starting with sk-ant-, something got copy-pasted into the wrong place.

This lesson is about your own integration code

If you’re relying entirely on the official provider plugins and never touch a key directly in your own PHP, most of this is already handled for you, the plugins own their settings screens and their own storage. This lesson matters most once you’re writing custom code that needs a key of its own, or once you’re auditing a site for where secrets actually live.

Test it: confirm a constant-based key resolves correctly

wp-env run cli wp eval 'var_dump( defined( "MY_PLUGIN_ANTHROPIC_KEY" ) );'

This should print bool(true) once the constant is defined in wp-config.php, and bool(false) (correctly) on any environment where you haven’t set it yet, which is exactly the signal your fallback code in Step 3 should check for.

Recap

Official provider plugins store their keys in wp_options, standard for a WordPress setting, but worth remembering when handling database exports or backups. For your own integration code, define keys as constants in wp-config.php rather than hardcoding them in plugin files, this keeps secrets out of version control and out of anything you might paste into a support ticket. Never assume a rotated key erases a previously leaked one, treat any exposed key as permanently compromised and reissue it from the provider’s dashboard.

Resources & further reading

← Setting Up the PHP AI Client SDK and Provider Plugins Generating Text with wp_ai_client_prompt() →