X API 403 Forbidden: Every Cause on the Twitter (X) API and How to Fix Each One

This page is a lookup for one status code: HTTP 403 Forbidden from the official X API (api.x.com/2/...), with one section per cause, each tied to X's own docs or an X staff answer on the developer forum. It is narrower than our Twitter (X) API error-handling guide, which covers retry and backoff patterns across all status codes, and different from the error 226 "this request looks like it might be automated" page, which is about X's automation check on logged-in sessions. Setting up keys and the console lives in the X developer portal guide, the reply mechanics in the reply API tutorial, and xurl's own client-not-enrolled fix in the xurl guide.
X documents most 403 causes in scattered places: a troubleshooting table in the MCP docs, an error-type table on the response-codes page, notes on the manage-posts and media pages, and changelog entries. The rest only appears in staff replies on devcommunity.x.com. We read those pages and threads for this guide, quoted staff where they answered, and left a cause out when no staff answer confirmed it.
At the end we compare what twitterapi.io returns for its own auth errors (403 for a missing key, 401 for a wrong one), and show the study we ran on public posts about X API 403s.
How to tell X API 403s apart: read the body, not the status
Every cause on this page returns the same HTTP status, so the status code alone tells you nothing. X's error responses are Problem Details JSON with title, detail and type, and some add reason, client_id and required_enrollment. Log the whole body and the x-transaction-id response header on every 403: X staff ask for exactly those two things in nearly every forum thread before they can look at a case.
X's response-codes page lists the 403 checks in this order: verify your app has access to this endpoint, some endpoints require specific enrollment or approval, user-context endpoints need appropriate OAuth scopes, and the resource may be private or protected. Its error-type table names .../client-forbidden as "App not enrolled or lacks required access" and .../not-authorized-for-resource as "No access to private/protected content".
What twitterapi.io returns, for comparison. Our API uses one static key in the x-api-key header, so its auth errors are simpler and come with a fixed body. Per docs.twitterapi.io/errors: 403 means no API key was sent ({"error": "Forbidden", "message": "API key required. Please include x-api-key in your request header..."}), 401 means the key is invalid ("API key is invalid"), 402 means the account is out of credits, and 429 means too many requests per second. An unknown user is not an error status at all: it is HTTP 200 with status: "error" and msg: "user not found". The docs say to retry only 429 and 5xx, never 400, 401, 402, 403 or 404, which is the right rule for X's 403s too.
# Print the status, the X transaction id and the full error body of a failing X API call.
# X staff ask for the x-transaction-id and the UTC time in almost every 403 thread.
curl -s -D - -o body.json -X POST "https://api.x.com/2/tweets" \
-H "Authorization: Bearer $X_USER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "test post"}' | grep -iE "^HTTP/|^x-transaction-id|^x-access-level"
date -u +"%Y-%m-%dT%H:%M:%SZ"
cat body.json # look at reason, type, title and detailclient-not-enrolled (Client Forbidden) on the X API: the app is not in a Pay-per-use package
What you see. HTTP 403 with "title": "Client Forbidden", "reason": "client-not-enrolled", "required_enrollment": "Appropriate Level of API Access" and a detail that begins "When authenticating requests to the X API v2 endpoints, you must use keys and tokens from a developer App that is attached to a Project." It hits every v2 call from that app, including GET /2/users/me right after a successful OAuth token exchange, which is why it often looks like an auth bug.
What X says. The troubleshooting table on X's MCP docs page reads: "client-not-enrolled after login: App isn't in the right X package/environment, in the portal move it to Pay-per-use + Production." The response-codes page describes the matching type, client-forbidden, as "App not enrolled or lacks required access". In a forum thread where an app still sat in a legacy free project while its credits were loaded, the X staff answer was: "Your app was still enrolled in the deprecated legacy Free tier. I migrated it to pay per use, so you should no longer run into any issues."
How to fix it. Open the developer console, find the app and check which package and environment it is in. It needs to be in a Pay-per-use package and in Production; an app that shows as Standalone, or sits in a legacy Free, Basic or Pro project, is the usual case. If the console has no way to move it (apps that migrated from legacy plans have reported a "ghost" project), open a forum topic: for fresh client-not-enrolled cases X staff ask for the numeric App ID from console.x.com ("the client_id in the error body is not enough"), the endpoints, the full error JSON with tokens redacted, and whether the app shows under a Pay-per-use project or as standalone.
A look-alike: user-not-enrolled. A different body, "This request is billed to the authenticated user. Create a developer account at the Developer Console and enroll it in Pay-per-use.", means the developer account billed for the request is not enrolled. X staff explained it as the billed developer account not being in Pay-per-use for the product, and noted that "having a Default Project + App in the console UI isn't always enough if enrollment or the wrong developer account is attached to the OAuth client."
What it is not. In another thread X staff drew the line explicitly: "403 Forbidden on POST /2/tweets is not a missing Pay-per-use enrollment. client-not-enrolled is a different, explicit error." If your 403 body has no reason: client-not-enrolled, regenerating keys or moving projects is unlikely to help. Our xurl guide walks through the same console fix for xurl and the hosted MCP server.
Reply rejected with not-authorized-for-resource on the X API: the summoned-reply rule
What you see. Original posts publish (201), replies in your own threads publish, but a reply to someone else's post returns 403 with type https://api.x.com/2/problems/not-authorized-for-resource and the detail "You can only reply to or quote posts where you are mentioned or are the author."
What X says. The X changelog entry "Addressing LLM-generated spam" (February 23, 2026): "Programmatic replies via POST /2/tweets are now only permitted when the original Post's author has 'summoned' the replier (by @mentioning that account or quoting one of its Posts). Additional restrictions apply to programmatically @mentioning or quoting users. These changes affect self-serve tiers only." The manage-posts page spells it out: replies are permitted only if the original author @mentions the replying account in their post, or quotes a post from the replying account; "If neither condition is met, the reply request will be rejected." The forum announcement adds that self-serve means legacy Free, Basic, Pro and Pay-per-use, that Enterprise endpoint behavior is unchanged, and that @mentions and quotes in normal posts are limited to users already involved in the context.
Staff confirmation for this exact error. In a thread about this 403, the first staff reply guessed an account-level restriction, then corrected it: "this 403 is expected behavior on self-serve tiers, not a restriction on your account or app, so there's nothing to lift on our side." The same answer adds: "Quote posts via the API have also been removed from self-serve tiers." The only route X offers for unsummoned replies is Enterprise access.
How to work with it. Reply only to posts that mention your account or quote it. A reply-on-mention bot fits the rule: read your mentions, reply to those. X's rate card also prices it differently: "Post: Create (summoned)" is $0.010 per request, against $0.015 for a normal post and $0.200 for a post with a URL (docs.x.com pricing page). Before sending a reply you can check the target post yourself; the snippet below reads it through twitterapi.io (/twitter/tweets, no X account needed, $0.15 per 1,000 tweets) and tests both summon conditions. It is a pre-check, not a guarantee: X decides on its side.
What we have not tested. Replies sent through twitterapi.io's logged-in session endpoint (/twitter/create_tweet_v2 with reply_to_tweet_id) are a different path from POST /2/tweets. We have not tested whether X applies the summoned rule to them, so we make no claim either way. That endpoint acts as a logged-in X account (one /twitter/user_login_v2 login, $0.10, with a proxy you supply, returns login_cookies), so X can rate-limit, lock or suspend that account: keep a human pace, expect the session to need a re-login now and then, and note that calls are billed per HTTP 200 response, which includes refusals from X that come back as status: "error" with X's reason.
# Pre-check: was my account summoned by this post (mentioned, or one of my posts quoted)?
import os
import requests
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_API_KEY"]}
MY_HANDLE = "your_bot_handle" # without @
def summoned(tweet_id):
r = requests.get(f"{BASE}/twitter/tweets", params={"tweet_ids": tweet_id}, headers=HEADERS, timeout=30)
r.raise_for_status()
tweets = r.json().get("tweets") or []
if not tweets:
return False
t = tweets[0]
mentions = (t.get("entities") or {}).get("user_mentions") or []
if any((m.get("screen_name") or "").lower() == MY_HANDLE.lower() for m in mentions):
return True
quoted = t.get("quoted_tweet") or {}
return ((quoted.get("author") or {}).get("userName") or "").lower() == MY_HANDLE.lower()
print(summoned("1947640330318156074"))"Your account is not permitted to access this feature" on the X API (POST /2/tweets)
What you see. POST /2/tweets returns {"detail": "Your account is not permitted to access this feature.", "status": 403, "title": "Forbidden", "type": "about:blank"}. Reads, media upload and GET /2/users/me often keep working with the same token, the response shows x-access-level: read-write, and credits are positive. The phrase is generic (other products use the same words), but on the X API it has come up in many recent devcommunity.x.com threads about POST /2/tweets.
What X staff say. Across several threads the answers point the same way. After a burst of automated posts: "That exact 403 on writes after a burst of automation usually means an account- or app-level write restriction kicked in, not a missing OAuth permission or credits." When posting also failed in the X app with "Post not sent": "this looks like a restriction on the [posting] account rather than on your developer App. Account restrictions can't be reviewed or lifted from the developer forum." And in a case where the same token had posted minutes earlier: "The 403 isn't a scope, quota, billing or duplicate-text error... This particular response means X's automated posting protection is currently refusing new posts from the account. While it's in effect, retrying will keep returning 403." Staff later added that it was "a restriction on the account itself, which is an account-level issue better suited for X Support."
How to handle it. Stop automated retries first. Then try to post from the same account on x.com or in the X app. If that fails too, the restriction is on the account and only X Support can review it (help.x.com). If the app can post but the API cannot, open a forum topic with the App ID, the endpoint, the UTC time and the x-transaction-id. One staff answer also warns that regenerating keys or creating a new app will not change an account-level restriction.
The suspended variant. "Your account is suspended and is not permitted to access this feature" names its cause in the text: the authenticated account is suspended. In our study sample, older posts quoting it on the X API often paired it with the number 64, an error code from X's older v1.1 API according to those posts. The staff answers above did not cover suspensions; treat it as an X Support case for the account, not an API configuration problem.
"You are not permitted to perform this action" on the X API: posting limits and spam blocks
What you see. POST /2/tweets returns 403 with the detail "You are not permitted to perform this action." Often it is one authorized account that fails while other accounts on the same app post normally, or an app that posts for its owner but not for other users.
What X staff say. The clearest answer: "403 means the account is not permitted to create that post right now", "not an auth or billing failure (those are 401 / 402). Typical causes: platform posting limits, downstream spam/integrity blocks, or trying to reply to a conversation you cannot participate in." In another thread: "You may have hit platform limits or spam block if your activity was too high volume or posting duplicate content. Please resume normal behaviors and it should self-resolve." Staff point to the X limits help page (help.x.com/en/rules-and-policies/x-limits).
How to handle it. Check the request body first: if it is a reply, the summoned-reply rule in the section above applies. Check the token's scopes: staff list tweet.read, tweet.write and users.read for posting with OAuth 2.0. Then slow down, stop posting duplicate text and confirm the account can post in the X app. If a regenerated OAuth 1.0a token suddenly returns 401 instead, staff say that is "often a credentials mismatch": the Access Token and Secret must belong to the same app's current API Key and Secret.
How it differs from error 226. Error 226, "This request looks like it might be automated", is X's automation check on logged-in sessions and has its own page. A reply-bot thread on the forum showed both: replies by hand in the X app hit the 226-style message while the API returned 403, and staff treated it as an account-level restriction. If you see both, fix the account's behavior, not the code.
Bare 403 Forbidden on GET /2/users/me (X API): missing OAuth 2.0 scopes
What you see. The OAuth 2.0 authorization-code flow with PKCE completes, the token exchange returns an access token, and the very next call, GET /2/users/me, returns {"title": "Forbidden", "type": "about:blank", "status": 403, "detail": "Forbidden"} with no reason.
What X staff say. A bare {"title":"Forbidden","status":403,"detail":"Forbidden"} on /2/users/me, in one staff answer, "is almost always an auth/scope problem." In a recent thread where the app requested only users.read users.email: "GET /2/users/me requires both the tweet.read and users.read scopes on the OAuth 2.0 user-context token... the token is missing tweet.read, which explains the 403." Staff also note that GET /2/users/me needs a user-context token, not an app-only Bearer token, and that it "stays free under pay-per-use, so a $0 credit balance should not block that endpoint by itself."
How to fix it. Put tweet.read users.read in the scope parameter of the authorize URL (add tweet.write to post and offline.access for a refresh token), send the user through authorization again, and use the new token. In staff words: "Existing tokens keep their original scopes." Send it as Authorization: Bearer <user_access_token>. If the body says client-not-enrolled or user-not-enrolled instead of a bare Forbidden, go back to the enrollment section; that is a different cause.
If it persists. Some forum cases with correct scopes on a Pay-per-use app were still open as enrollment re-checks on X's side when we read them, with no published cause. We do not guess one; post the full body and x-transaction-id on the forum. Our OAuth 2.0 user-context guide walks through the PKCE flow and scope list step by step.
https://x.com/i/oauth2/authorize?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&scope=tweet.read%20users.read%20tweet.write%20offline.access
&state=RANDOM_STATE
&code_challenge=YOUR_S256_CHALLENGE
&code_challenge_method=S256Video longer than N minutes and more than one cashtag: post-content 403s on the X API
"This user is not allowed to post a video longer than N minutes." X's changelog for September 1, 2026 documents size and duration limits that are checked at upload and again at post creation, and that follow "the authenticated user's X Premium / verified status and media_category, not your developer API plan." Default accounts can post video up to 20 minutes and 8 GB (tweet_video / amplify_video); X Premium or verified accounts up to 125 minutes and 16 GB. "Exceeding the posting user's duration cap returns 403." The media page's example body is {"title": "Forbidden", "detail": "This user is not allowed to post a video longer than 20 minutes.", "type": "about:blank", "status": 403}, and it warns that "a successful upload does not guarantee the media can be attached."
Fix. Trim the video under the posting user's cap, or post from an account whose status allows longer video. Paying more for API access does not raise it. For an older case where a Premium account was capped at 10 minutes through the v1.1 upload, X staff pointed the developer to the v2 chunked upload sample with media_category set to amplify_video, and that worked.
"Posts are limited to a maximum of one cashtag ($SYMBOL)." X's manage-posts page: "Posts created via the API are limited to a maximum of 1 cashtag (e.g. $TICKER) per post. Requests that include more than 1 cashtag will be rejected." It applies to self-serve (Pay-per-use) customers only. On the forum, X staff called it "a recent restriction applied for posts created via the API in order to combat abuse" and pointed multi-ticker news bots to Enterprise plans. The full message developers quote is "Posts are limited to a maximum of one cashtag ($SYMBOL). Please remove additional cashtags and try again."
Fix. One $TICKER per post; write further tickers without the dollar sign, or split them across posts. Count carefully: in one forum thread the developer found a second cashtag they had overlooked.
What developers post about X API 403s: a study of public posts
To see which 403 causes developers run into and talk about, we searched public posts on X through our own search endpoint with ten queries ("x api" 403, "twitter api" 403, the error strings, and the reply rule) until the results ran out: 287 unique posts, of which 116 pair an API context with a 403-style error. The volume is small. The narrowest query, "x api" 403 limited to recent months, returned 21 posts spanning 14.6 weeks, under two a week. Most debugging of these errors happens on devcommunity.x.com, which is why the causes above come from X's docs and staff answers rather than from posts.
The reply rule shows up. Posts that tie a 403 to replies, mentions or quotes went from 2.5% before 2026 to 16.2% in 2026, after the February rule. Separately, 54 posts discussed the rule itself (48 of them in 2026), but only 3.7% of those mentioned a 403: most were news of the change, not error reports.
Enrollment is the older wave. 16 of the 29 on-topic posts from 2023 name an enrollment, project or plan problem, the Client Forbidden / client-not-enrolled era. In 2026 it is 29.7%, still second. 32.4% of 2026 posts name no cause at all, which is the gap this page tries to fill.
The generic phrase is often not about the API. Of 24 posts containing "not permitted to access this feature", 50.0% have any API context; only 2 are from 2026. That is why each heading here says "on the X API".

How we collected this
Question. When developers post publicly about an X API 403, which cause do they name, how often do they mention the February 2026 reply rule, and how many posts with the generic phrase "not permitted to access this feature" are about X's API at all?
Query. /twitter/tweet/advanced_search with queryType=Latest, ten queries: "x api" 403 and "twitter api" 403 (once limited to recent months, once without a date limit using 403 OR forbidden), the client-not-enrolled strings, "not permitted to access this feature", "2/tweets" with 403 or forbidden, a combined query for client forbidden / not-authorized-for-resource / not permitted to perform this action, and "x api" with replies and summoned or mentioned. Every query was followed through next_cursor until has_next_page was false. 38 requests returned 437 posts, 287 after removing duplicates, collected in 2026. At 15 credits per tweet returned that is 6,555 credits, about $0.0655.
Method. A post is on-topic when its text matches an API-context pattern (X API, Twitter API, v2, OAuth, endpoint and similar) and a 403-style pattern (403, forbidden, not permitted, client-not-enrolled, not-authorized-for-resource). Causes are case-insensitive regular expressions, recorded in the study's results file; a post can match several causes or none.
What the sample cannot show. X search returned few posts for these phrases, so the shares rest on small groups (37 posts in 2026) and move a lot with a handful of posts. Regex causes miss paraphrases and over-match common words such as "token" or "reply". Posts show what developers blame, not what X's server returned; the causes in the sections above come from docs.x.com and X staff answers on devcommunity.x.com, which we read for this guide.
Privacy. The authors are private individuals, so we report aggregates only: no handles, texts, links or post IDs. Raw search pages stayed out of the repository; the committed sample holds de-identified rows (query, year, weeks before collection, language, pattern flags). We do not name forum posters either; staff answers are quoted without the developer's details.

"""x403.py - explain an X API 403 body, and list posts that summoned your account.
Part 1 is pure parsing of the JSON body X returns with a 403 (no network).
Part 2 reads your recent mentions through twitterapi.io: every post that
@mentions you is one the X API's self-serve reply rule lets you answer.
Run: TWITTERAPI_IO_API_KEY=... python3 x403.py your_handle
"""
import os
import sys
import time
import requests
BASE = "https://api.twitterapi.io"
def explain_x_403(body):
"""Map an X API 403 JSON body to the likely cause (per docs.x.com and X staff answers)."""
reason = (body.get("reason") or "").lower()
kind = (body.get("type") or "").lower()
detail = (body.get("detail") or "").lower()
if reason == "client-not-enrolled":
return "App not in a Pay-per-use package / Production environment: move it in the developer console."
if reason == "user-not-enrolled":
return "The billed developer account is not enrolled in Pay-per-use."
if "not-authorized-for-resource" in kind and "reply" in detail:
return "Self-serve reply rule: reply only to posts that @mention or quote your account."
if "not permitted to access this feature" in detail:
if "suspended" in detail:
return "The authenticated account is suspended: an X Support case."
return "Restriction on the posting account: stop retrying, test a post in the X app, then X Support."
if "not permitted to perform this action" in detail:
return "Posting limits or a spam/integrity block on the account; check scopes and slow down."
if "video longer than" in detail:
return "Video over the posting user's duration cap (20 min default, 125 min Premium/verified)."
if "cashtag" in detail:
return "Self-serve limit: at most one cashtag per post."
if detail == "forbidden":
return "Bare 403: on /2/users/me usually a token without tweet.read + users.read; re-authorize."
return "Unknown 403: log the body and the x-transaction-id header and ask on devcommunity.x.com."
def get(path_url, params, key, attempts=5):
"""GET a twitterapi.io endpoint; retry only 429 and 5xx, as docs.twitterapi.io/errors advises."""
for i in range(attempts):
r = requests.get(path_url, params=params, headers={"X-API-Key": key}, timeout=30)
if r.status_code in (429, 500, 502, 503):
time.sleep(2 ** i)
continue
if r.status_code == 403:
sys.exit("twitterapi.io 403: no x-api-key header arrived (check the header name or a proxy)")
if r.status_code == 401:
sys.exit("twitterapi.io 401: the API key is invalid; copy it again from the dashboard")
if r.status_code == 402:
sys.exit("twitterapi.io 402: out of credits; stop and top up")
r.raise_for_status()
return r.json()
sys.exit("gave up after retries")
def summoning_posts(handle, key, max_pages=3):
"""Posts that @mention `handle`: candidates the reply rule allows you to answer."""
out, cursor = [], ""
for _ in range(max_pages):
params = {"userName": handle}
if cursor:
params["cursor"] = cursor
data = get(f"{BASE}/twitter/user/mentions", params, key)
for t in data.get("tweets") or []:
out.append({
"id": t["id"],
"author": (t.get("author") or {}).get("userName"),
"createdAt": t.get("createdAt"),
"text": (t.get("text") or "")[:80],
})
if not data.get("has_next_page"):
break
cursor = data.get("next_cursor") or ""
return out
if __name__ == "__main__":
sample = {"title": "Forbidden", "type": "https://api.x.com/2/problems/not-authorized-for-resource",
"detail": "You can only reply to or quote posts where you are mentioned or are the author."}
print("example 403 ->", explain_x_403(sample))
key = os.environ["TWITTERAPI_IO_API_KEY"].strip()
handle = sys.argv[1] if len(sys.argv) > 1 else "XDevelopers"
posts = summoning_posts(handle, key)
print(f"{len(posts)} recent posts mention @{handle}")
for p in posts[:10]:
print(p["createdAt"], "@" + str(p["author"]), p["id"], p["text"])
Questions readers ask
What does 403 Forbidden mean on the X API?
X's response-codes page defines it as "Valid auth but no permission for this resource or action": your credentials were accepted, but this app, token or account may not do this request. A 401 is the credentials problem. The body's reason, type and detail fields name the cause.
How do I fix client-not-enrolled on the X API?
Move the app into a Pay-per-use package and the Production environment in the developer console, as X's docs say. If the console cannot move it (legacy or standalone apps), open a devcommunity.x.com topic with the numeric App ID, the endpoint and the full error JSON; X staff have migrated such apps by hand.
Why does the X API return 403 when my bot replies to a post?
Since February 23, 2026, self-serve apps can only reply when the original post's author @mentioned the replying account or quoted one of its posts. Otherwise X returns not-authorized-for-resource with "You can only reply to or quote posts where you are mentioned or are the author." X staff call it expected behavior; Enterprise is exempt.
What does "Your account is not permitted to access this feature" mean on the X API?
On POST /2/tweets, X staff answers describe it as a restriction on the posting account (for example X's automated posting protection), not a scope, billing or app problem. Stop retrying, test a post in the X app, and contact X Support through help.x.com if the app is blocked too.
Why does GET /2/users/me return 403 Forbidden?
Usually the OAuth 2.0 user token lacks tweet.read and users.read, or you sent an app-only Bearer token. Add both scopes to the authorize URL and have the user authorize again; X staff note that existing tokens keep their original scopes.
Should I retry a 403?
No. A 403 needs a change first: the app's enrollment, the token's scopes, the post's content or the account's standing. Retrying an account-level 403 keeps failing, according to X staff. Retry only 429 and 5xx with backoff.
What does twitterapi.io return for a missing or wrong API key?
Per docs.twitterapi.io/errors: 403 with "API key required. Please include x-api-key in your request header" when no key is sent, 401 with "API key is invalid" for a wrong key, 402 when credits run out, and 429 when you exceed your per-second rate. An unknown user returns HTTP 200 with status: "error".
Does twitterapi.io avoid X's 403 causes?
For reads, enrollment, OAuth scopes and X's per-post-read rate card do not apply: one x-api-key header, no X account needed, $0.15 per 1,000 tweets. Writes are different: they act as a logged-in X account (one $0.10 login with your own proxy), so X can rate-limit, lock or suspend it; keep a human pace, the session can need a re-login, and calls are billed per HTTP 200 response. We have not tested whether X's summoned-reply rule applies to replies sent that way.
Continue
- X API docs: Response codes and errors (403 meaning, client-forbidden and not-authorized-for-resource types)
- X API docs: Manage Posts integration guide (self-serve reply rule, 1-cashtag limit, video 403)
- X API changelog: Addressing LLM-generated spam (Feb 23, 2026) and media size/duration limits (Sep 1, 2026)
- X API docs: Media introduction (size and duration limits, 403 example body)
- X Developer Community: X API v2 update, addressing LLM-generated spam (staff announcement)
- twitterapi.io docs: Errors (401, 402, 403, 429 bodies and retry rules)
- X API overview: endpoints, pricing and access
- Twitter (X) API error handling: retry and backoff patterns
- Twitter error 226: "this request looks like it might be automated"
- xurl, X's official CLI, and its client-not-enrolled fix
- Twitter (X) reply API tutorial
- X developer portal: console, keys and billing
- X API OAuth 2.0 user-context and PKCE setup
- The X-API-Key header: 401 vs 403 on twitterapi.io
- twitterapi.io pricing: pay per tweet returned
- 01How to Use ZCode with the Twitter (X) API: MCP Server and Skills Setup
- 02Pi Coding Agent + Twitter (X): Add Live X Search to Pi
- 03DeepSeek Harness and Twitter (X): Add an X MCP Server, Skill or Plugin to dsh
- 04X (Twitter) Bookmarks + AI: Export Them, Then Let a Model Sort and Search Them
- 05Custom GPT retirement: what migrates, and how to rebuild a Twitter (X) Action as an MCP connector
- 06Get Twitter (X) Data in Python (2026 Tutorial)
- 07Twitter (X) Welcome Message: Is the API Still Alive, and How to Send One in 2026
- 08Michael Saylor on Twitter — A Tracking Guide for Crypto Traders
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API keyAlready building with TwitterAPI.io? Publish an honest tutorial or build log about it and earn up to $200 a month in API credits.