Back to blog

Your Groq API key: how to get one, and keep it out of .env

Vlad Vidican 6 min read

You generate a Groq API key in the GroqCloud console, on the API Keys page. Groq's quickstart then has you export it as GROQ_API_KEY, and the official Python and Node.js SDKs read that variable on their own. That is why the key so often ends up in a shell profile or a .env file. This post covers getting the key, the ways your code picks it up, and how to keep it in an encrypted Valt0 vault instead.

Get the key

Sign in to console.groq.com/keys and create a key. Two things to know before you click:

  • Groq is not Grok. Groq, with a q, is GroqCloud, and its keys come from console.groq.com. Grok is xAI's model: its keys come from xAI's console (console.x.ai) and go in XAI_API_KEY. A key from one won't work with the other.
  • A key belongs to a project. Groq's projects page says any key you generate is specific to the project selected in the console, and rate limits are applied per project. Pick the project first.

Groq's security guide asks for one key per environment (dev, staging and prod), so a key leaked from your laptop isn't the one your production app uses. It also says never to put a key in source code, and gives two places for it instead: "Use environment variables or a secret management system." The rest of this post is about the second.

How your code finds it

The quickstart sets the key in your terminal:

Terminal
export GROQ_API_KEY=<your-api-key-here>

Then the client finds it without being told. Install it with pip install groq (Python 3.10 or later). This is the README's example, and its comment says the api_key line can be left out, because reading GROQ_API_KEY is the default:

app.py
import os
from groq import Groq

client = Groq(
    api_key=os.environ.get("GROQ_API_KEY"),  # This is the default and can be omitted
)

chat_completion = client.chat.completions.create(
    messages=[
        {
            "role": "user",
            "content": "Explain the importance of low latency LLMs",
        }
    ],
    model="openai/gpt-oss-20b",
)
print(chat_completion.choices[0].message.content)

In Node.js the package is groq-sdk (Node.js 20 or later), and new Groq() reads process.env['GROQ_API_KEY'] the same way. The quickstart's JavaScript example uses the AI SDK instead (ai and @ai-sdk/groq), whose groq provider also reads GROQ_API_KEY by default.

Groq's API is also mostly compatible with OpenAI's, so plenty of code uses the openai package pointed at Groq. There the key is passed by hand, from the same variable:

app.py
import os
import openai

client = openai.OpenAI(
    base_url="https://api.groq.com/openai/v1",
    api_key=os.environ.get("GROQ_API_KEY"),
)

If you don't want to export the key in every terminal, the Python SDK's README suggests python-dotenv and a line in .env, so the key stays out of source control.

Where the key ends up

Either way, the setup leaves the key in plain text in a file:

.env · before
LOG_LEVEL=info
GROQ_API_KEY=the-key-itself

Or as an export line in ~/.zshrc or ~/.bashrc. Anything that can read that file can use the key: a backup, a copy sent to a teammate, a tool or coding agent working in the project folder. And a variable in the environment is handed down to every process the app or the shell starts. To see where yours lives, these print variable names and file names, not the key, so it doesn't land on your screen:

Terminal
env | grep '^GROQ_' | cut -d= -f1
grep -l 'GROQ_API_KEY' ~/.zshrc ~/.zprofile ~/.bashrc ~/.bash_profile ~/.profile .env 2>/dev/null
Before: .env holds LOG_LEVEL and GROQ_API_KEY in plain text, and Groq() picks it up on its own, as can every process the app starts. After: .env holds LOG_LEVEL and APP_VAULT_ID, the key is in dev.valt0, encrypted and committed with the code, and the Valt0 service hands it to the app when it asks

Keep the key in a vault

You need Valt0 installed; valt0 status checks that its service is running. Create a vault for development. Valt0 prints its key once, so put it in your password manager now: without it nobody can open the vault, you included.

Terminal
valt0 init dev

Store the Groq key under the same name. Valt0 asks for the vault key, then for the value: paste the Groq key.

Terminal
valt0 store dev:GROQ_API_KEY

The app never sees the vault key. It opens the vault by its id, which is public, and asks the local Valt0 service for the secret by name. valt0 init printed the id; this prints it again:

Terminal
valt0 info dev

Put that id in .env where the key was. It is safe there:

.env · after
LOG_LEVEL=info
APP_VAULT_ID=your-vault-id

Commit dev.valt0 with the code. Without the vault key it can't be read. Then take the export GROQ_API_KEY line out of your shell profile and open a new terminal, or the key is still in plain text there and still in the environment of everything you run.

Python

Install the clients. Valt0's needs Python 3.9 or later and groq needs 3.10, so use 3.10 or later. python-dotenv keeps reading the settings in .env:

Terminal
python -m pip install valt0-client groq python-dotenv

Then pass the key to Groq yourself, from the vault:

app.py · after
import os

import valt0
from dotenv import load_dotenv
from groq import Groq

load_dotenv()

vault = valt0.open_vault(id=os.environ["APP_VAULT_ID"], name="app")

with vault.secret("GROQ_API_KEY") as secret:
    client = Groq(api_key=secret.reveal())

chat_completion = client.chat.completions.create(
    messages=[
        {
            "role": "user",
            "content": "Explain the importance of low latency LLMs",
        }
    ],
    model="openai/gpt-oss-20b",
)
print(chat_completion.choices[0].message.content)

reveal() hands the SDK the value, and the with block wipes Valt0's copy when it ends. The SDK keeps the string it was given, because a Python string can't be wiped, but the key is no longer in the environment or in a file next to your code. Anywhere else, the secret prints as [REDACTED]: in print(), an f-string or a log line. If the service can't be reached, open_vault raises an error and the app stops at startup.

Using the openai package against Groq instead? Install it in place of groq, swap from groq import Groq for import openai, and the line inside the with block becomes:

app.py · after
with vault.secret("GROQ_API_KEY") as secret:
    client = openai.OpenAI(
        base_url="https://api.groq.com/openai/v1",
        api_key=secret.reveal(),
    )

Node.js

Terminal
npm install @valt0/client groq-sdk
app.mjs · after
import Groq from 'groq-sdk';
import { openVault } from '@valt0/client';

const vault = await openVault({ id: process.env.APP_VAULT_ID, name: 'app' });

const secret = await vault.secret('GROQ_API_KEY');
const client = new Groq({ apiKey: secret.reveal() });
secret.destroy();

const chatCompletion = await client.chat.completions.create({
  messages: [{ role: 'user', content: 'Explain the importance of low latency LLMs' }],
  model: 'openai/gpt-oss-20b',
});
console.log(chatCompletion.choices[0].message.content);

destroy() wipes Valt0's copy straight after. The SDK keeps the string it was given, because a JavaScript string can't be wiped, but the key is no longer in process.env or in any file next to your code. Both @valt0/client and groq-sdk need Node.js 20 or later. To get APP_VAULT_ID from .env, start the app with node --env-file=.env app.mjs (the flag arrived in Node.js 20.6).

On the AI SDK instead, make the provider yourself with the key from the vault, and use it where the quickstart uses groq. The current ai package needs Node.js 22 or later:

Terminal
npm install @valt0/client ai @ai-sdk/groq
app.mjs · after
import { createGroq } from '@ai-sdk/groq';
import { generateText } from 'ai';
import { openVault } from '@valt0/client';

const vault = await openVault({ id: process.env.APP_VAULT_ID, name: 'app' });

const secret = await vault.secret('GROQ_API_KEY');
const groq = createGroq({ apiKey: secret.reveal() });
secret.destroy();

const { text } = await generateText({
  model: groq('openai/gpt-oss-20b'),
  prompt: 'Explain the importance of low latency LLMs',
});
console.log(text);

From here

  • If the key may have leaked, revoke it in the console first, as Groq's security guide says to do "immediately if compromise is suspected", and don't bring it back. Then create a new one, store it with valt0 store dev:GROQ_API_KEY --force and restart the app. The same command is how you do the periodic rotation the guide recommends (it suggests quarterly).
  • Teammates get dev.valt0 when they clone the repo and the vault key from you. They run valt0 import dev once before the app can read it.
  • Keep it out of browser code. Groq's guide says never to embed keys in frontend code or browser bundles, and the Node.js SDK won't run in a browser unless you set dangerouslyAllowBrowser. Call Groq from your server and have the browser talk to that.
  • Production gets its own vault and its own Groq key, with the same secret name, which is the per-environment key Groq asks for. Deployment covers servers, and the Python client and the Node.js client pages cover the rest of the API.

Other secrets in the same .env go the same way: Turn an .env file into a vault converts a whole file in one step, and the OpenAI, Claude and Gemini versions of this post do the same for those keys.

Sources: Groq quickstart, Groq: projects, Groq: security onboarding, Groq: OpenAI compatibility, groq for Python, groq-sdk for Node.js, xAI quickstart, Valt0 Python client docs, Valt0 Node.js client docs.

Create your first vault.

$ valt0 init prod

Install