OpenAI API key example: using a key in curl, Python and Node.js

Ready examples of passing an API key in a header, environment variables and an SDK, the typical format mistakes, and a key check with a /v1/balance request.

5 min · · API keys · development

Looking for an OpenAI API key example: how exactly does the key get into a request? Below are short working examples for curl, Python and Node.js, ways to keep the key in environment variables and a connection check with a /v1/models request. The examples are written for the OpenAI-compatible format: a Zukko fetch_... key from the dashboard and the address https://api.zukko.pro/v1. The same code with a different key and address works with other OpenAI-compatible servers.

What a key looks like and where it is used

A key is a string that tells the server whose request this is and which balance to charge. In the OpenAI format it is passed in the Authorization header with the word Bearer and a space before the key:

Authorization: Bearer fetch_xxxxxxxxxxxxxxxx

A Zukko key starts with fetch_. It is created in the dashboard and shown in full only once at creation, so save it right away in a password manager or an environment variable. If you lose it, create a new one. Authorization details are in the authentication reference.

Remember one rule: a key works only on the server that issued it. An official OpenAI key will not work with api.zukko.pro, and a fetch_... key will not work with OpenAI's servers. If you use the wrong key, you get a 401.

The key in an environment variable

Do not paste the key straight into code: it ends up in the repository. Put it into an environment variable.

macOS and Linux:

bash
export OPENAI_API_KEY="fetch_your_key"
export OPENAI_BASE_URL="https://api.zukko.pro/v1"

PowerShell:

bash
$env:OPENAI_API_KEY = "fetch_your_key"
$env:OPENAI_BASE_URL = "https://api.zukko.pro/v1"

The official OpenAI SDKs and many tools read OPENAI_API_KEY and OPENAI_BASE_URL, so after this you do not need to specify the key in code. The variable lives only in the current terminal session; to keep it, add the lines to your shell profile or use a .env file, and remember to add it to .gitignore.

OpenAI API key curl example

The first request is a key check. GET /v1/balance generates no text and returns the balance. The list GET /v1/models is not suitable for this: it is open and does not validate the key:

bash
curl https://api.zukko.pro/v1/balance \
  -H "Authorization: Bearer $OPENAI_API_KEY"

If you get JSON with the balance, the key and address are correct. Now a generation request:

bash
curl https://api.zukko.pro/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-6.1-sol",
    "messages": [{"role": "user", "content": "Say hello in one word"}]
  }'

Pay attention to the quotes: the shell expands $OPENAI_API_KEY only inside double quotes. In single quotes the literal text $OPENAI_API_KEY goes to the server. The gpt-6.1-sol model is taken from the catalog as of October 18, 2026; /v1/models always returns the current names.

A Python example

bash
pip install openai
bash
import os

import httpx
from openai import OpenAI

client = OpenAI(
    base_url="https://api.zukko.pro/v1",
    api_key=os.environ["OPENAI_API_KEY"],
)

## Key check: balance request (the SDK has no separate method for it)
check = httpx.get(
    "https://api.zukko.pro/v1/balance",
    headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"},
)
print(check.status_code, check.json())

response = client.chat.completions.create(
    model="gpt-6.1-sol",
    messages=[{"role": "user", "content": "Say hello in one word"}],
)
print(response.choices[0].message.content)

If OPENAI_API_KEY and OPENAI_BASE_URL are already set, you can call the constructor without arguments: client = OpenAI().

A Node.js example

bash
npm install openai

File check.mjs (the .mjs extension allows import and top-level await):

bash
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.zukko.pro/v1",
  apiKey: process.env.OPENAI_API_KEY,
});

const response = await client.chat.completions.create({
  model: "gpt-6.1-sol",
  messages: [{ role: "user", content: "Say hello in one word" }],
});
console.log(response.choices[0].message.content);

Run it with node check.mjs. If the key is in a .env file, in Node.js 20.6 and newer you can load it with the flag node --env-file=.env check.mjs. The same logic without an SDK is available through the built-in fetch:

bash
const res = await fetch("https://api.zukko.pro/v1/balance", {
  headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
});
console.log(res.status, await res.json());

Common mistakes with the key

Most problems come from one set:

  • No Bearer word or no space. A header Authorization: fetch_... without Bearer gives a 401.
  • Extra characters. A space, a newline or quotes inside the value often remain after copying or reading from a file. Trim the value (.strip() in Python, .trim() in JavaScript).
  • Wrong key. A key from another service. Check that the string starts with fetch_.
  • Address without /v1. For the OpenAI format the base URL must end with /v1; without it you get a 404. See the article base URL with or without /v1.
  • The variable did not pass through. In Python os.environ["OPENAI_API_KEY"] raises KeyError if the variable is not set in this terminal session. Open a new terminal after changing the profile.
  • Key in single quotes in curl. The shell will not substitute the value (see above).

Response codes: 401 is an invalid key, 402 is insufficient balance, 403 means the model is not allowed for this key (model_not_allowed_for_key), 404 is an unknown model or a wrong address, 429 means the key limit or a provider restriction was reached. The most common is 401, and the cause is almost always one of the items in the list above.

How to store a key safely

A few rules that save nerves:

  1. Do not commit the key to git and do not put it in frontend code: any site visitor will see it in the browser. Make API requests from the server.
  2. Create separate keys per project and give each a daily and monthly dollar limit and a list of allowed models. If one key leaks, damage is capped by the limit and only that key needs revoking.
  3. If a key lands in a public repository, treat it as compromised: create a new one and delete the old one. Step by step instructions are in the article an API key leaked.

More practices are in the material on API key security.

FAQ

Where do I get an API key for an OpenAI-compatible API?

For Zukko: sign in to the dashboard, open the keys section and create a key. It is shown once. The minimum balance top-up is $1. For OpenAI's server the key is issued in that service's own account, and it will not work with our address.

Can I pass the key in the x-api-key header?

The Anthropic SDK sends the key that way and it works. In the OpenAI format use Authorization: Bearer.

Why does the balance request work but generation does not?

The balance request checks only the key. Generation additionally requires a balance (otherwise 402), a model allowed for the key (otherwise 403) and a correct model name (otherwise 404). Take current names from the /v1/models response.

Do I need to change anything in code when moving to another server?

Only base_url and the key. The rest of the OpenAI SDK code stays the same. The link for creating a key and making a first request is the dashboard.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

OpenAI API key example: curl, Python, Node.js · Zukko