# Twitter X API 401 unauthorized bearer token error

## TL;DR
A 401 on the X API means the bearer token is missing, expired, or revoked, or the app's permissions do not cover the endpoint. Regenerate the token in the developer portal, confirm the app has the right access level, and send it as a your bearer credential header exactly. Never commit the token to a repo; a leaked token gets revoked and the 401s come back.

## The error
```text
HTTP 401 Unauthorized
{"errors": [{"message": "Unauthorized", "code": 32}]}
```

## When this helps
- X API calls return 401 unauthorized
- a bearer token stops working
- setting up X API access for a new agent
- rotating a compromised token

## When it doesn't
- the error is 403; that is tier gating or suspension, not the token
- the error is 429; that is rate limit
- the app itself was suspended; regenerate nothing until the appeal resolves

## Works with
X API v2 as of 2026 with OAuth 2.0 bearer tokens.

## Steps
### 1. Send the bearer token in the exact header form
```bash
curl -s "https://api.twitter.com/2/tweets/search/recent?query=test" -H "your auth header -o probe.json -w "HTTP %{http_code}\n"
head -c 200 probe.json; echo
```
Expected: HTTP 200 with the app user. The header is Authorization colon Bearer space token; any deviation 401s.

### 2. Check the token value for whitespace damage
```python
import os
t = os.environ.get("X_BEARER", "")
print("length:", len(t))
print("clean:", t.strip() == t and " " not in t)
```
Expected: A clean token string. Pasted tokens often carry trailing newlines that break auth silently.

### 3. Regenerate the token if it was exposed or is stale
```python
import os
print("regenerate in the developer portal under the app's keys section")
print("update the secret store, then re-run the step-1 call")
print("old token stays valid until you revoke it; revoke after verifying the new one")
```
Expected: A rotation procedure. Tokens in git history or logs must be treated as compromised.

### 4. Confirm the app permission level covers the endpoint
```python
import requests, os
r = requests.get("https://api.twitter.com/2/tweets/search/recent", headers={"Authorization": "Bearer " + os.environ["X_BEARER"]}, params={"query": "x"}, timeout=20)
print(r.status_code, "(403 here means tier, not token)")
```
Expected: A 200, or a 403 that proves the token works and the endpoint needs a higher tier. Token fixed versus tier gated are different problems.

## Other ways people phrase this
### twitter api 401 bearer token
Token hygiene: exact header, clean value, fresh generation.

### x api unauthorized code 32
The classic bad-token code. Regenerate and retest.

### twitter bearer token expired
Bearer tokens do not expire by time, but revocation and app changes invalidate them.

## Why it happens
The X API authenticates every call with a bearer token tied to the app's keys. The 401 means the token presented is not valid: wrong value, whitespace damage, revoked, or from a deleted app. The API cannot distinguish these, so verify the value, the header form, and the app state in order.

## Edge cases
- OAuth 1.0a user tokens and OAuth 2.0 bearer tokens are different; use the right one per endpoint.
- A token that works on one endpoint but 401s elsewhere points at app permissions, not the token.
- Rate limits can masquerade as auth flakiness under retry storms; check the code, not just the symptom.
- Store tokens in a secret manager; environment files get committed by accident.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_24fsE803v3iELKG_WecU4A
