A 403 from the X API can mean five different things, and the status code won't tell you which one you've got. The reason is in the response body, which most client libraries bury. So before you regenerate keys or buy anything, print the body.
This guide covers the X API errors that block people before they get a single result: 401, 402, 403 and the older 453. For 429, we have a separate guide to rate limits and backoff. We run TwittrAPI, an alternative to the official API for reading public data, and we cover where that helps at the end. Most of this page is about fixing the official API.
Step 1: Read the actual error
This prints everything X sends back. It's a single user read with a Bearer token, about a cent at the published rate, and it tells you whether your credentials are the problem:
import os
import requests
r = requests.get(
"https://api.x.com/2/users/by/username/XDevelopers",
headers={"Authorization": f"Bearer {os.environ['X_BEARER_TOKEN']}"},
timeout=30,
)
print(r.status_code)
print(r.text)An error from X looks like this, with title, detail, type and, on some errors, reason:
{
"title": "Client Forbidden",
"detail": "...",
"reason": "client-not-enrolled",
"type": "https://api.twitter.com/2/problems/client-forbidden"
}If you use Tweepy, catch the exception and print e.response.text. The default message drops some of those fields.
What each X API error code means
X's response codes page defines these. The last two columns are our notes.
| Status | X's definition | Typical cause | Retry? |
|---|---|---|---|
| 401 | Credentials are invalid or missing | Wrong token, wrong OAuth signature | No |
| 402 | Not on X's codes page | No API credits left | After you top up |
| 403 | Valid credentials, no permission | Access level, app permissions or auth type | No |
| 429 | Rate limit or usage cap exceeded | Too many requests, or the cap | After waiting |
| 503 | X is overloaded | Nothing on your side | Yes, with backoff |
How to fix a 401 Unauthorized
A 401 means X doesn't accept who you say you are. It is nearly always one of these:
- The token is wrong or old. Regenerating a key in the console invalidates the old one, and your server may still have it.
- The header is malformed. It must be
Authorization: Bearer <token>. Stray quotes or a newline from a.envfile will do it. - The OAuth 1.0a signature is off. This happens with hand-written signing and with a server clock that has drifted. Use a maintained library.
Test the token with the script above. If that works and your app doesn't, the problem is in how your app sends it.
How to fix X API 403 Forbidden: client-not-enrolled
Look for "reason": "client-not-enrolled" or the client-forbidden problem type. X's docs describe this as an app that isn't enrolled for the access the endpoint needs.
Your key is fine, and rewriting your code won't help. Open the developer console, check that the app belongs to a package with access, and check that you have credits. A third-party guide we read suggests moving the app to the Pay-per-use package and the Production environment, which fits what the error says. Your console may show something different, as X has changed these screens more than once.
How to fix 453 "You currently have access to a subset"
The full message reads like this, with small wording differences between versions:
403 Forbidden
453 - You currently have access to a subset of X API V2 endpoints
and limited v1.1 endpoints (e.g. media post, oauth) only.
If you need access to this endpoint, you may need a different access level.You will not find 453 on X's current response codes page. Our picture of it comes from developers reporting the error on the Tweepy forum and elsewhere. Those reports point to the same cause: the account's access level doesn't include that endpoint. It often shows up when old code calls a v1.1 endpoint such as api.search_tweets.
Two things to check:
- Are you on a v1.1 call? In Tweepy,
tweepy.APIis v1.1 andtweepy.Clientis v2. Moving toClient.search_recent_tweetsis the first step for search. - Does your plan cover the endpoint? If you are on a v2 call and still get this, it is an access question. Old tutorials assume a free tier, which no longer covers reads. See is the X API free.
How to fix 402 CreditsDepleted
X moved to prepaid credits, and developers report a 402 with CreditsDepleted when the balance runs out. X's own codes page doesn't list it, so we are relying on third-party documentation such as this decoder.
The fix is to buy credits in the console. That same page says a slightly negative balance stays blocked until it is covered, that auto-recharge doesn't run at zero or below, and that some developers see a 402 while a balance still shows. If that last one is you, the X developer forum is the place to look.
Check what a request costs before you top up. A post read is $0.005 and a user read is $0.010, as our X API pricing breakdown explains, and a loop can get through credits faster than you would guess.
Other 403 messages you may see
Developers run into these often, but X doesn't list the exact strings on its codes page, and the wording may have changed. Match on the meaning.
"Authenticating with OAuth 2.0 Application-Only is forbidden for this endpoint." You sent a Bearer token to an endpoint that acts as a user, such as posting. It needs OAuth 1.0a User Context or OAuth 2.0 with PKCE.
"Your client app is not configured with the appropriate oauth1 app permissions." The app is set to Read but the call writes. Change it to Read and Write in the console, then regenerate the access token and secret. The old token keeps its old permissions, which is the step most people miss.
"You are not allowed to create a Tweet with duplicate content." It is a real 403, but it is about your content. X rejects a post identical to a recent one.
not-authorized-for-resource. You asked for something private, such as a protected account's posts, and your user doesn't have access.
If you get errors from TwittrAPI
The status codes overlap but the causes don't, because there is no X app involved. Bodies are JSON with an error string, and the full list says what's billed.
| Status | Meaning | Billed? |
|---|---|---|
| 401 | Missing or unknown X-API-KEY | No |
| 402 | No balance left, or the trial expired | No |
| 403 | Key deactivated, or the endpoint isn't in your plan | No |
| 404 | X says the user or tweet doesn't exist | Yes |
| 422 | Invalid input, such as an unsupported search operator | Yes |
| 429 | Over your key's requests per second | No |
| 400, 408, 409, 5xx | Failed request | Refunded |
One to watch: trial keys get a 403 on /client/* and /premium/*, the endpoints that act on an account. Read endpoints work with any key.
When to stop fixing and switch
If you need the official API, fix the access problem. Posting with OAuth under your app's name, compliance requirements and X's own support all need it, and no third party replaces those.
If all you wanted was to read public posts, profiles or search results, the access levels may be the wrong thing to fight. A key without a developer account takes a few minutes, and the Tweepy guide shows what changes in Python code. It is an unofficial route, and that comes with its own trade-offs, which that post lists.
Frequently asked questions
Why am I getting 403 Forbidden from the X API?
Your credentials were accepted but you lack permission. The common causes are an app not enrolled for that endpoint, a Read-only app calling a write endpoint, or a Bearer token on an endpoint that needs user authentication. The response body names which one.
What does client-not-enrolled mean?
Your app exists but isn't enrolled for the access the endpoint requires. Fix it in the developer console by checking the app's package and credits. Changing your code won't help.
What does error 453 mean on the Twitter API?
Your access level doesn't include the endpoint you called. It often appears when older code calls v1.1 endpoints such as search. Move to the v2 client, then check that your plan covers the call.
I regenerated my keys and still get 403. Why?
New keys keep the same access level, so regenerating doesn't fix an access problem. If you changed app permissions from Read to Read and Write, you do need a new access token, as old tokens keep their old permissions.
What is the difference between 401 and 403 on the X API?
A 401 means X doesn't accept your credentials at all. A 403 means it accepted them, but your app or user isn't allowed to do this. So fix the token for a 401, and check access level and permissions for a 403.
Sources
- X API response codes and errors: status codes and problem types
- X API error decoder (OpenTweet): third-party notes on 402 and
client-not-enrolled, read November 2026 - Tweepy discussion #2159: community reports of the 453 message
- TwittrAPI error handling: our status codes and billing
TwittrAPI is not affiliated with X Corp. It reads only public data.



