X-Autopilot

X API Python: the official SDK and what it breaks on

X ships a first-party Python SDK now: pip install xdk. What it covers, the four bugs still open in the shipped build, and where its own docs are wrong.

X-Autopilot Team··10 min read
On this page · 9 sections

The short version

  • ▸X has shipped a first-party Python client since 3 November 2025. It installs with pip install xdk, it is generated from X's OpenAPI spec, and it wraps 21 endpoint families including News, Chat, Broadcasts and Articles that no community library covers.
  • ▸The shipped build is pinned, not rolling. PyPI's latest xdk is 0.10.6, uploaded 25 July 2026, and its own metadata classifies it Development Status 3 - Alpha. The install docs say pip fetches 'the latest generated version from the main branch'. It does not.
  • ▸Four bugs are open and unanswered on the package repo, and one of them is load-bearing: in 0.10.6 any page containing a repost raises a Pydantic ValidationError because the live API returns type 'reposted' while the generated model only allows 'retweeted', 'quoted' and 'replied_to'.
  • ▸The docs oversell pagination. The generated iterator has no total cap, no rate-limit backoff despite the docs claiming one, and it calls raise_for_status inside the loop, so a 429 aborts mid-walk. Use the undocumented Cursor helper to put a limit on it.
  • ▸Two code samples in X's own material do not run: the docs pagination example passes tweet_fields to a method whose parameter is post_fields, and the launch announcement imports OAuth2PKCE from xdk.auth, a module that does not exist in the package.

The short answer

For X API work in Python you now have a first-party option. X shipped official Python and TypeScript XDKs (X Developer Kits) on 3 November 2025, and the Python one is a one-liner:

pip install xdk

It is generated from X's published OpenAPI spec, it speaks Bearer Token, OAuth 2.0 PKCE and OAuth 1.0a, and it covers 21 endpoint families, including several that no community library touches.

It is also pinned at 0.10.6 from 25 July 2026, its own package metadata labels it alpha, four bug reports sit open and unanswered on its repo, and at least three code samples in X's own documentation do not run as written. None of that makes it the wrong choice. It does mean you want to know where the holes are before you build on it, which is what the rest of this page is.

What you actually install

The package name is xdk. Unpacking the 0.10.6 wheel gives you these sub-clients on the Client object:

FamilyAttributeNotable methods
Postsclient.postssearch_recent, search_all, create, delete, get_by_ids, get_analytics, hide_reply, get_counts_recent
Usersclient.usersget_me, lookups, blocks, mutes, follows
Streamclient.streamposts, update_rules
Mediaclient.mediaupload, metadata, subtitles
Newsclient.newsthe endpoints from the 19 November 2025 launch
Chatclient.chatthe encrypted DM surface
Activity / Account Activityclient.activitywebhook subscriptions
Alsolists, spaces, trends, communities, community_notes, articles, broadcasts, compliance, connections, direct_messages, usage, webhooks, general

Two things stand out. The wheel contains articles, broadcasts and chat clients that are missing from the published reference index on docs.x.com, so the docs are behind the code rather than ahead of it. And there is no bots sub-client, so the Bots API is raw-HTTP only.

Dependencies are modest: pydantic>=2.0.0, requests>=2.25.0, requests-oauthlib>=1.3.0. That tells you something useful on its own. It is built on synchronous requests, so there is no async client. The async example in X's streaming docs is a threading.Thread feeding an asyncio.Queue, not native async, and you should budget for that if you were planning to drop this into an async service.

The quickstart, corrected

X's version works. Here it is with the two things worth changing:

from importlib.metadata import version
from xdk import Client

print(version("xdk"))          # 0.10.6 - not xdk.__version__

client = Client(bearer_token="YOUR_BEARER_TOKEN")

for page in client.posts.search_recent(query="api", max_results=10):
    for post in page.data or []:
        print(post.text)
    break                       # one page, because the iterator does not stop

Do not trust xdk.__version__. The docs' verification step tells you to print(xdk.__version__) and says it "should print the XDK version." In the 0.10.6 wheel that constant is hardcoded to "0.1.0", and it is the same in every build, because __init__.py is generated and nobody wired the version in. importlib.metadata.version("xdk") gives you the real number.

The break is not optional. More on that below.

Credentials come from the X Developer Console. The three auth modes map cleanly: Bearer Token for app-only reads, OAuth 2.0 PKCE for anything acting as a user, OAuth 1.0a for the legacy paths. If you are deciding between them, X API OAuth walks the whole decision including the consent-screen change that landed on 14 September 2026.

One correction for anyone copying from the launch announcement: it imports from xdk.auth import OAuth2PKCE. There is no xdk/auth.py in the package and no class called OAuth2PKCE. The real import is from xdk.oauth2_auth import OAuth2PKCEAuth.

Pagination is where the money leaks

This is the part to read twice, because it is a billing problem rather than a correctness one.

The docs describe automatic pagination as a convenience: "developers can just specify how much data they are looking for without having to make multiple API calls." The implementation does not work that way. max_results is a per-page argument that goes straight into the query string. The generator emits a while loop that reads meta.next_token, yields a page, and keeps going until the token stops appearing. There is no total.

So this innocent-looking snippet, which is adapted from X's own pagination page, walks your entire result set:

all_posts = []
for page in client.posts.search_recent(query="python", max_results=100):
    all_posts.extend(page.data)

Recent search reads bill as Posts: Read at $0.005 per Post returned. A hundred-page walk is 10,000 Posts and $50. X API search has the full cost table and the rate ceilings, and X API pricing covers how pay-per-use credits are drawn down.

Three further corrections to that same docs page:

The backoff does not exist. The page says the iterator "supports rate limit backoff to avoid 429 errors." In the generated client the only thing at the bottom of the loop is a comment and a disabled line:

# Optional: Add rate limit backoff here if needed
# time.sleep(0.1)  # Small delay to avoid rate limits

A 429 aborts the walk. response.raise_for_status() runs inside the generator before the yield, so a rate-limit response raises requests.exceptions.HTTPError out of your for loop with no token saved. Catch it and keep your own next_token if the job matters.

tweet_fields is the wrong parameter name. The docs example passes tweet_fields=["created_at", "author_id"] to search_recent. That method's parameter is post_fields, and there is no **kwargs to swallow the mistake, so you get a TypeError. tweet_fields survives only on the stream client.

All three have one fix, and it is a helper the docs never mention. Cursor and the cursor() factory are exported from the package root and give you the stopping condition you expected:

from xdk import Client, cursor

client = Client(bearer_token="YOUR_BEARER_TOKEN")

for post in cursor(client.posts.search_recent,
                   query="python",
                   max_results=100).items(limit=200):
    print(post.text)

items(limit) caps individual results, pages(limit) caps requests. Note that the docstrings for both reference client.posts.search_posts_recent, a method that does not exist; the real name is search_recent.

The four open bugs

All four are filed against the package repo, all four are still open, and the newest X commit anywhere in that repo is chore: update SDK to v0.10.6 on 25 July 2026.

Reposts crash timeline reads (issue 7, 25 September 2026). Request post.fields=referenced_posts and the live API returns "type": "reposted". The generated PostReferencedPostsItem.type is Literal["retweeted", "quoted", "replied_to"], so Pydantic raises and the entire page is lost. The reporter traced it to the published spec at api.x.com/2/openapi.json, version 2.168, which still documents the old vocabulary. This is the one most likely to hit you, because reposts are everywhere in a list or timeline read.

Booleans are serialised wrong (issue 8, 6 October 2026). Generated clients put a Python bool straight into the params dict, requests stringifies it, and the URL carries dry_run=False. X answers 400 Bad Request: 'False' is not a valid boolean, must be true or false. Omitting the argument works. Passing it never does.

Streaming dies on emoji (issue 3, 24 May 2026). stream.posts() decodes each chunk with chunk.decode('utf-8') instead of an IncrementalDecoder, so any multi-byte character split across a TCP boundary raises UnicodeDecodeError. The reporter measured roughly 44 crashes a day on a French-language stream handling about 1,500 Posts a day. A fourth report, "Filtered stream Not working" from 30 June 2026, is probably the same root cause.

If you want to exercise these without spending credits, the X API Playground serves a local mock of v2 built from the same OpenAPI download.

Licence and repo layout, because the docs point at the wrong one

Worth two minutes before this goes near a company codebase.

There are three repositories. xdevplatform/xdk is the generator, written in Rust, and it carries an MIT LICENSE file. xdevplatform/xdk-python and xdevplatform/xdk-typescript hold the generated output, and they are what PyPI and npm actually publish from. Neither output repo has a LICENSE file. The wheel's metadata does carry the License :: OSI Approved :: MIT License classifier, and the overview page links the generator's licence, so the intent is clearly MIT. It is just not stated in the repo the code ships from.

Development-install instructions have the same mix-up, and that one is harder to work around. The install page says to git clone https://github.com/xdevplatform/xdk.git, cd xdk/python, then pip install -e .. There is no python/ directory in that repository; its top level is Cargo.toml, xdk-gen, xdk-lib, xdk-build and specs. Clone xdevplatform/xdk-python instead.

xdk or tweepy

Honest answer: it depends on which endpoints you need, and most people should check that before anything else.

xdk 0.10.6tweepy 4.17.0
MaintainerX, first-partycommunity
Last release25 July 20262 July 2026
Built byOpenAPI code generationhand-written
Endpoint breadth21 families incl. News, Chat, Broadcasts, Articlesthe classic v2 surface
Asyncno, requests onlyno
Open issues on the package repo7107
Help available90 and 48 stars across two reposabout 11,200 stars, years of answers

The generated nature cuts both ways. It is why xdk reaches endpoints tweepy has never wrapped, and it is also why issue 7 exists: a spec that lags the live API produces a model that rejects real responses, and no human reviewed the diff. tweepy has the opposite profile, with narrower coverage and a much larger body of answered questions when you get stuck.

Pick xdk when you need an endpoint family tweepy does not have, or when first-party matters for procurement. Pick tweepy when you are doing ordinary posting, timeline and search work and want the search results and Stack Overflow answers to be about your actual library. Either way, pin the version in your lockfile. Both of these are auto-generated or hand-updated against a spec that moves faster than they do.

If you are here because you want X data and do not want a billing relationship at all, that is a different question with worse answers: scraping X with Python covers what still works and what it costs you in account risk.

What this does not solve

An SDK gets you authenticated requests. It does not get you reach.

The API is the sanctioned route for reading and for posting your own content, and it is the right tool for dashboards, alerting, archival and scheduled publishing. What it will not do since the February 2026 changes is drive the reply-level engagement that actually grows an account, and X's automation rules are worth reading in full rather than taking any vendor's summary for it.

That gap is the job X-Autopilot does: it runs on your own Mac in your own logged-in browser session, drafts replies in your voice, and never hands your session cookie to a cloud service. Be clear-eyed about what that is. Browser-based engagement is not an API-sanctioned path and we do not claim it is compliant. Running locally on a residential connection in your own browser is a smaller detection surface than a cloud tool posting from a shared IP pool, which is a reduction in risk and not an absence of it.

The short version of the short version

Install xdk, pin it, and read your field names off the generated signatures rather than off the docs. Use importlib.metadata.version to know what you have. Wrap your pagination in cursor(...).items(limit=N) so a stray walk cannot quietly bill you for ten thousand Posts. Keep referenced_posts out of your field lists until issue 7 closes, skip boolean query parameters entirely, and if you stream non-English text, expect to catch UnicodeDecodeError yourself. Everything else in the library does what it says.

Frequently asked

Answers indexed by Google + AI assistants.

Is there an official X API Python library?+

Yes. X announced first-party Python and TypeScript XDKs (X Developer Kits) on 3 November 2025. The Python one installs with pip install xdk, is generated from X's published OpenAPI specification at api.x.com/2/openapi.json, and ships sub-clients for posts, users, lists, media, spaces, trends, news, chat, broadcasts, articles, communities, community notes, compliance, connections, direct messages, usage, webhooks, activity, account activity, stream and general.

What is the difference between xdk and tweepy?+

xdk is X's own auto-generated client; tweepy is the long-standing community library. Coverage is the real split: xdk is regenerated from the full OpenAPI spec so it carries newer endpoint families like News, Chat and Broadcasts, while tweepy wraps the classic v2 surface by hand and has 11,000 GitHub stars and years of answered questions behind it. Freshness is roughly a tie - tweepy 4.17.0 landed 2 July 2026, xdk 0.10.6 landed 25 July 2026, and neither has shipped since.

Why does my xdk call raise a Pydantic ValidationError on reposts?+

Because the generated model is stricter than the live API. Issue 7 on xdevplatform/xdk-python, opened 25 September 2026, shows that requesting post.fields=referenced_posts returns type 'reposted', while PostReferencedPostsItem.type only accepts 'retweeted', 'quoted' or 'replied_to'. The whole page fails validation, not just the one Post. Until it is fixed, drop referenced_posts from your field list or call that endpoint over raw HTTP.

Does the xdk paginator handle rate limits?+

No, despite the pagination docs saying it 'supports rate limit backoff to avoid 429 errors'. In the generated code the only backoff is a commented-out time.sleep line under the note 'Optional: Add rate limit backoff here if needed'. The loop calls response.raise_for_status() before yielding, so a 429 raises requests.exceptions.HTTPError and you lose your place in the walk.

How do I limit how many results xdk pulls?+

max_results is per page, not a total, so the plain iterator walks the entire result set and bills every Post. The package exports an undocumented Cursor helper for this: from xdk import cursor, then cursor(client.posts.search_recent, query='python', max_results=100).items(limit=200). Cursor.items(limit) and Cursor.pages(limit) are the only built-in stopping conditions.

Does pip install xdk give me the newest endpoints?+

Not necessarily. pip resolves to the newest PyPI release, which is 0.10.6 from 25 July 2026, so anything X shipped after that date is absent. The Bots API has no sub-client at all. Check what you actually have with importlib.metadata.version('xdk') rather than xdk.__version__, which is hardcoded to 0.1.0 in every build.

Sources
Related searches
x api pythontwitter api pythonpip install xdkx api python sdkofficial x api client library pythonxdk vs tweepyx api python paginationx api python streamingx api v2x api pricingtweepyx developer consoleoauth 2.0 pkcefiltered streampay per use api credits
DY
Deepak YadavBuilding X-Autopilot

Product designer and indie hacker. Runs the agent on his own X account every day and writes up what the data shows, including when it's inconvenient.

Follow on X →
Try X-Autopilot.
$199 once. No subscription, no monthly bill. Real Chrome on your Mac.
See pricing▶
Free PDF · the X Growth Playbook

The exact playbook we use to grow on X

The bio that converts, the daily reply loop, the posting cadence, and the tool stack, in one no-fluff PDF. Drop your email and it's yours.

No spam. Unsubscribe anytime.

Free tools

Try it yourself.

All free X tools →
Keep reading

Related posts.