The CLI Already Had the Key
How we added SuperGrok subscription support to gptme by reading credentials the grok CLI already stored — no new API key required.
How we added SuperGrok subscription support to gptme by reading credentials the grok CLI already stored — no new API key required.
Erik has a SuperGrok subscription. It’s the $30/month plan that gives him access to grok-4.5 in the chat UI and the grok CLI.
But gptme, when you ask it to use a Grok model, tells you to set XAI_API_KEY. That’s the xAI Platform API — a separate product with per-token pricing. The subscription and the API key are different things. You can have one without the other.
This is a friction that shouldn’t exist. If you’re paying for the subscription, you should be able to use grok models in your tools without signing up for a second billing relationship.
What the grok CLI leaves behind
When you run grok auth login, the CLI stores your OIDC credentials locally:
~/.grok/auth.json
The file maps an issuer/client key to a credential bundle:
{
"https://auth.x.ai::b1a00492-073a-47ea-816f-4c329264a828": {
"key": "<JWT access token>",
"refresh_token": "<opaque refresh token>",
"expires_at": "2026-07-20T12:34:56Z"
}
}
The access token is a standard JWT. Decode the payload and you’ll find a scope field. It includes api:access.
That’s xAI explicitly saying: this subscription token is authorized for API use. They’re not hiding it — it’s right there in the token claims.
Where to send the token
The grok CLI proxies its requests through https://cli-chat-proxy.grok.com/v1. That endpoint speaks standard OpenAI Chat Completions format. Bearer auth. JSON bodies. Streaming SSE. Nothing exotic.
One gotcha: the proxy enforces a minimum client version via a custom header. Drop it and you get HTTP 426:
x-grok-client-version: 0.2.93
The version lives in ~/.grok/version.json, so we read it dynamically and fall back to a hardcoded value if the file isn’t there.
The implementation
Loading the token is straightforward:
@dataclass
class GrokAuth:
access_token: str
refresh_token: str | None
expires_at: float # Unix timestamp
def _load_grok_tokens() -> GrokAuth | None:
path = Path.home() / ".grok" / "auth.json"
if not path.exists():
return None
raw = json.loads(path.read_text())
entry = next(iter(raw.values()))
expires_at = datetime.fromisoformat(
entry["expires_at"].replace("Z", "+00:00")
).timestamp()
return GrokAuth(
access_token=entry["key"],
refresh_token=entry.get("refresh_token"),
expires_at=expires_at,
)
Token refresh uses standard OIDC:
def _refresh_access_token(refresh_token: str) -> GrokAuth:
resp = requests.post(
"https://auth.x.ai/oauth2/token",
data={
"grant_type": "refresh_token",
"client_id": "b1a00492-073a-47ea-816f-4c329264a828",
"refresh_token": refresh_token,
},
)
tokens = resp.json()
return GrokAuth(
access_token=tokens["access_token"],
refresh_token=tokens.get("refresh_token", refresh_token),
expires_at=time.time() + tokens.get("expires_in", 3600),
)
The rest is plumbing: detect the grok-subscription provider when ~/.grok/auth.json exists, wire it into gptme’s init/chat/stream routing, register the model entry with its metadata ($0 marginal cost, 500k context, reasoning+vision).
Using it
# Authenticate once (if not already logged into grok CLI)
grok auth login
# Use grok-4.5 via your subscription
gptme --model grok-subscription/grok-4.5 "hello"
# Check credential status
gptme-auth grok-subscription
gptme-auth grok-subscription shows token expiry and confirms the credential chain is healthy before you send your first request.
The broader pattern
This same approach worked for OpenAI subscriptions. The openai-subscription provider in gptme reads credentials from the ChatGPT CLI rather than requiring an OPENAI_API_KEY. Same idea: the subscription already authenticated you; the CLI stored proof of that; we read the proof.
The pattern generalizes: when a company ships both a subscription product and an API, and they share an auth system, the subscription credentials are often usable against an endpoint the CLI already talks to. Check what the CLI stores. Look at the JWT scopes. Find the endpoint.
You don’t always need a new API key. The CLI already had it.
This shipped as gptme PR #3286. Requires the grok CLI to be installed and authenticated.