// GUIDE — MACOS
Kalshi CLI Setup on macOS
May 21, 2026 · Yeti Games, LLC · 8 min read
Getting the Kalshi CLI running on macOS involves three separate problems that aren't documented anywhere official: a corrupted key error, a hardcoded production URL in demo mode, and a silent bug that makes your positions invisible. This guide covers all three.
1. Directory and Credential Setup
Store credentials in a hidden directory with restricted permissions:
mkdir -p ~/.kalshi touch ~/.kalshi/.env chmod 700 ~/.kalshi
Add to ~/.zshrc:
export KALSHI_ACCESS_KEY="your-uuid-here" export KALSHI_PRIVATE_KEY_PATH="/Users/yourname/.kalshi/private_key.pem" export KALSHI_ENVIRONMENT="demo"
Run source ~/.zshrc after saving.
2. Fixing the InvalidByte Error
If you copy your private key from a browser, hidden characters or smart quotes often corrupt the PEM file. The CLI throws:
ValueError: InvalidData(InvalidByte(4, 95))
Fix by converting to clean PKCS#8 format with OpenSSL:
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt \ -in ~/.kalshi/private_key.pem \ -out ~/.kalshi/private_key_fixed.pem mv ~/.kalshi/private_key_fixed.pem ~/.kalshi/private_key.pem chmod 600 ~/.kalshi/private_key.pem
This strips formatting artifacts and outputs a standard key the library accepts. If you don't have OpenSSL, install via Homebrew: brew install openssl.
3. Fixing the NOT_FOUND Error (Demo Mode)
If you're using demo credentials and the CLI returns NOT_FOUND on every request, the CLI is hitting the production server. Some builds are hardcoded to production.
Patch cli.py to read your environment variable:
import os
KALSHI_ENV = os.getenv("KALSHI_ENVIRONMENT", "prod").lower()
if KALSHI_ENV == "demo":
BASE_URL = "https://external-api.demo.kalshi.co"
else:
BASE_URL = "https://api.elections.kalshi.com"Then add aliases to ~/.zshrc for easy switching:
alias kalshi-demo='export KALSHI_ENVIRONMENT=demo && echo "→ DEMO"' alias kalshi-prod='export KALSHI_ENVIRONMENT=prod && echo "→ PRODUCTION"'
Run kalshi-demo before any paper trading session. Run kalshi-prod when trading real money.
4. Fixing the Missing Positions Bug
Symptom: kalshi positionsprints "No open positions" even when you have open trades.
Cause: Kalshi changed their API response format. The position field (integer) was replaced by position_fp (string like "14"). The CLI filters out positions where position == 0 — and since the old integer field is now missing, everything reads as zero and gets dropped.
Find cli.py — usually in ~/.local/share/kalshi-cli/ — and replace the two lines that load positions:
# Before (original two lines):
ps = data.get("market_positions", [])
ps = [p for p in ps if p.get("position", 0) != 0]
# After (replace with this block):
ps = data.get("market_positions", [])
for p in ps:
if p.get("position", 0) == 0 and p.get("position_fp"):
try:
p["position"] = int(float(p["position_fp"]))
except (ValueError, TypeError):
pass
ps = [p for p in ps if p.get("position", 0) != 0]No restart needed — the CLI reads cli.py fresh on each invocation. Run kalshi positions after the edit to verify.
5. Troubleshooting Checklist
| Problem | Fix |
|---|---|
| InvalidByte error | Run the OpenSSL PKCS#8 conversion in Section 2 |
| NOT_FOUND on all requests | CLI hitting production — patch BASE_URL in Section 3 |
| Auth rejected | KALSHI_ACCESS_KEY must be the UUID, not the display name |
| Key not found | Use absolute paths — never ~/ in env vars |
| Demo keys on prod server | Generate keys at demo.kalshi.co, not kalshi.com |
| kalshi positions shows nothing | Apply the position_fp backfill patch in Section 4 |
Next Steps
Once the CLI is working, the next step is running the 5-step AI trading workflow. The Claude Kalshi Playbook ($17) covers the full process — from scanning for opportunities with /alpha to executing trades with /finalize.