Auth

UNAUTHENTICATED — missing x-api-key

`Status { code: Unauthenticated }` on a Yellowstone gRPC subscribe means the server didn't see a valid API key in the metadata. The single most common cause is attaching the key to the client's constructor but not to the individual streaming call.

Root causes

Ranked by frequency. First cause is the one to check first.

  1. 01API key passed at channel creation but not as request metadata — most providers require per-call metadata.
  2. 02Using `Authorization: Bearer` when the provider expects `x-api-key`, or vice versa.
  3. 03API key has leading/trailing whitespace from copy-paste (Subglow trims on server; some providers reject silently).
  4. 04API key was rotated in the dashboard but the deploy still uses the old one.
  5. 05Using a free-trial key that expired — trials auto-expire after the 14-day window.

Fix steps

  1. 1

    Attach the key as request metadata, not channel options

    In TypeScript: `await client.subscribe({ 'x-api-key': KEY })`. In Python: `stub.Subscribe(req, metadata=(('x-api-key', KEY),))`. In Rust: add `.add_metadata('x-api-key', KEY)` to the interceptor.

  2. 2

    Use the exact header name the provider expects

    Subglow and Chainstack use `x-api-key`. Triton One uses `authorization: Bearer <token>`. Helius Laserstream uses the key in the URL path, not as metadata. Mismatched headers always yield UNAUTHENTICATED.

  3. 3

    Trim whitespace

    `const key = process.env.SUBGLOW_API_KEY?.trim()`. A single newline from your secret manager silently breaks gRPC metadata.

  4. 4

    Verify the key directly

    Run `grpcurl -H 'x-api-key: YOUR_KEY' grpc.subglow.io:443 geyser.Geyser/GetVersion`. A successful GetVersion response confirms the key is valid and attached correctly. If that fails too, regenerate the key in your Subglow dashboard.

Code example

auth_interceptor.rsrust
use yellowstone_grpc_client::GeyserGrpcClient;
use tonic::transport::ClientTlsConfig;

let tls_config = ClientTlsConfig::new().with_native_roots();
let mut client = GeyserGrpcClient::build_from_shared("https://grpc.subglow.io:443")?
    .x_token(Some("YOUR_API_KEY"))?
    .tls_config(tls_config)?
    .connect()
    .await?;

// x-api-key is automatically attached to every call by the x_token interceptor.
let (mut subscribe_tx, mut stream) = client.subscribe().await?;

Related errors

Want an endpoint that just works?

Subglow is flat-priced Solana gRPC + JSON-RPC on a single API key. Pre-parsed JSON, dedicated sendTransaction bucket, 99.9% latency SLA on Dedicated. No credit juggling, no surprise bills.