Skip to content
x

CLI

Every command and subcommand, grouped, with the flags that matter, and the global flags.

x <command> [subcommand] [flags]

Run x <command> --help for the full flag list on any command. This page is the map. A <ref> is a tweet id, status URL, or anything x can resolve to a tweet; a <user> is a handle, or a numeric id with --id.

Reads

Command What it does Key flags
get <ref>... Read whatever each reference points at, with the command that fits
tweet <ref> Show a single tweet (Tier 0)
user <user> Show a profile --id
timeline <user> A user's tweets (recent window; deeper with --guest/session) --id, --replies, --media
replies <ref> Replies to a tweet, or a user's own replies --id
media <ref> The media on a tweet, or on a user's tweets --id, --tab, --download, --size, --variant
embed <ref> Print a tweet's oEmbed blockquote, verbatim
thread <ref> A conversation thread around a tweet (Tier 0)
poll <ref> A tweet's poll options and tallies
search <query> Search tweets (session) --product
counts <query> Per-day tweet counts for a search (session) --product
quotes <ref> Quote tweets of a tweet (search-backed, session)
mentions <user> Tweets mentioning a user (search-backed, session)
followers <user> Accounts following a user (session) --id
following <user> Accounts a user follows (session) --id
likers <ref> Accounts that liked a tweet (session)
retweeters <ref> Accounts that retweeted a tweet (session)
likes <user> Tweets a user has liked (session) --id
list <list-id> Tweets in an X List (session)
space <ref> An audio Space: hosts, speakers, times, and audience (--guest)
home Your reverse-chron home timeline (session)
bookmarks Your bookmarks (session)
trends [woeid] What is trending in a place, worldwide by default
places [query] The places X has trends for, and their woeids --country, --type

search --product takes Top|Latest|People|Photos|Videos (default Latest). counts --product takes Top|Latest. timeline --replies includes replies and --media keeps only tweets with media. --replies stays on Tier 0 even with --guest, because the guest tier answers that read with an empty timeline rather than refusing it; only a session pages it deeper.

timeline is the account's own tweets and nothing else. X renders a reply on a profile together with the tweet it answers, so the page carries both authors; listing the parent would mean x timeline jack printing tweets @jack did not write, each one spending one of your -n. A repost is the case this cannot get right: the page shows it under the original author and says nothing about who reposted it, so it drops with the reply parents.

space takes the id or the x.com/i/spaces/ link and reads the whole record: who created it, the admins and the speakers, when it was scheduled, started and ended, and how many people heard it live or played the replay. The table summarises the rosters as counts, and -o json has the participants themselves, each with the numeric account id you need to look them up. It is one of the five reads --guest is worth passing for.

get classifies each argument the way x classify does and dispatches: a tweet id or status link reads the tweet, a handle reads the profile, a hashtag or a search link runs the search, a list link reads the list. A kind it has no reader for exits 7 rather than guessing. media --size takes thumb|small|medium|large|orig (default orig) and --variant names a video rendition by resolution or bitrate (default: the highest-bitrate MP4). With --download, media writes files and prints their paths, so the record flags (-o, --fields, --template) do not apply.

trends takes a woeid, which is the Yahoo! Where On Earth id X still keys its trend lists by, and it also takes a place name: x trends tokyo works, and an ambiguous name comes back as a usage error listing the candidates rather than a pick. places is how you find a woeid; it caches the directory for a week, so the name lookup costs one request ever. Both are Tier 0.

x is read-only: there are no commands that post, like, follow, or otherwise change your account. likes, likers, followers, and bookmarks only read.

Local store

Command What it does Key flags
edges <ref>... The graph claims one record makes, without walking anywhere --conflicts
graph <ref>... Those claims and the nodes they address, as one document
rdf <ref>... The same graph as RDF, in schema.org's vocabulary --format, --provenance
discover <seed>... Breadth-first walk of the graph linked from a tweet or user (alias walk) --follow, --depth, --fanout, --budget, --store, -n
crawl <seed>... The same walk, persisted into the local store --follow, --depth, --fanout, --budget, --max
db stats Row counts per table
db query <sql> Run a read-only SQL query
query <sql> The same query, one word shallower
queue Show the crawl queue
queue clear Empty the crawl queue
export [<user> <out-dir>] Render a stored user's tweets as Markdown, or the whole store as RDF --format, --kind, --since, --provenance

edges is one read per reference and no walking at all. It prints the claims a record already makes about other nodes, as from predicate to with the URL it was read from and the tier that cost. A tweet read with no credential is five or six edges for a single request, which is what makes it the cheap way to see what a surface is worth before spending a budget on a crawl. --conflicts narrows the output to claims two sources cannot both be right about, and prints both sides with a marker on the one that wins on provenance rather than picking for you.

graph is the same read printed as one value instead of one line per claim: the edges, plus every node those edges address. Nodes the read carried whole come with their record, and nodes that were only named come with just an address, because a mention is a claim about an account nobody fetched. It is a document, so -o json is the format it is for; on a terminal you get its shape summarized and reach for x edges or x get to read the parts.

$ x graph 1903142823316049977 -o table
 GRAPH                          NODES  READ  EDGES  PREDICATES
 x://tweet/1903142823316049977  5      2     5      authored mentions replies_to

rdf is graph said in somebody else's vocabulary: schema.org wherever a term exists, and x: where none does. That is not a taste call. X publishes schema.org microdata on its own status and profile pages, so a tweet already has a vendor-blessed RDF shape and this tool agrees with it rather than inventing a parallel one, which is what lets the output be checked against the source. Every x: term is defined at the namespace URL.

$ x rdf 20 --format ttl | head -8
@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix schema: <https://schema.org/> .
@prefix x:      <https://x-cli.tamnd.com/ns#> .
@prefix xsd:    <http://www.w3.org/2001/XMLSchema#> .

<x://tweet/20>
  a schema:SocialMediaPosting ;
  schema:identifier "20" ;

--format is nt, ttl, jsonld, or nq (default ttl). nq carries the URL each claim was read from as the graph name, and jsonld as one named graph per source, which is how provenance survives a merge of two crawls. nt and ttl have nowhere to put it, so --provenance adds reified statements there; it is off by default because reification costs five lines per claim and outnumbers the data. rdf writes bytes rather than records, so -o and --template do not apply to it.

discover and crawl share the same walk: --follow is a preset (content, thread, engagement, network, timeline, all) or a comma-separated hop list, --depth is how many hops to follow (default 1), and --fanout caps neighbors per hop (default 25). discover streams nodes and stops at -n (default 500); add --store to also persist them. crawl always persists and stops at --max (default 200). The store is a fixed x.db under --data-dir. Engagement and network hops need a session.

query is db query without the db: SQL against the store a crawl filled in, never the network. The tables are nodes and edges; see the local store.

--budget caps what the walk spends upstream, counted in requests rather than nodes, because requests are the unit the rate limits are written in. A walk that stops early says so and names how many nodes it never expanded, on stderr, so a partial crawl is never mistaken for a finished one. An exhausted rate-limit window ends the walk the same way, with exit 5 and the bucket named: an empty window is not one node's problem, it is every node's. See graph discovery.

A hop is not an edge. A hop is a direction the walk travels and an edge is a claim a record makes, and half the hops run against the arrow: the liker hop goes from a tweet to the accounts that liked it, and the edge under it points from each account back to the tweet.

export writes what the store already holds and never makes a request. With --format it writes the whole store as RDF on stdout, the same vocabulary and the same four serializations as x rdf, so a crawl becomes a file you can load into a triple store. --kind keeps the records of one kind and every claim with one of them at either end, which is why --kind tweet still tells you who wrote them. --since is when a record was captured rather than when a tweet was posted: the question an export answers is what you have learned lately, and a 2006 tweet read this morning was learned this morning. Without --format it renders one account's stored tweets as Markdown, which is what the two arguments are for.

Meta

Command What it does Key flags
auth import Save your auth_token + ct0 (or paste a Cookie header on stdin) --auth-token, --ct0
auth status Show the current session and tier
auth logout Forget the saved session
cache clear Delete all cached responses
config path Print the config file path
config show Print the resolved configuration
download <ref> Download a tweet's media to disk -O/--out
open <ref> Open a tweet or profile in your browser
classify <ref>... Say what a reference points at, and where (offline)
uri <ref>... The stable x:// name of each reference (offline)
url <ref>... The canonical x.com URL of each reference (offline)
capture <ref>... Record what a surface says now, into a fixture file --out
serve Serve the operations over HTTP (NDJSON) --addr
mcp Run as an MCP server over stdio
version Print version info
completion <shell> Generate a shell completion script

classify, uri and url are the identity layer with the network taken out. All three take anything you can paste, a bare id, a handle, a status URL with tracking parameters on it, a nitter link, and none of them make a request. classify says what kind of thing it is, uri gives the stable x:// name that is the same for a tweet however it was read, and url gives the canonical x.com link. They are what get dispatches on, so running them is how you check what get is about to do:

$ x url 20 jack "https://twitter.com/jack/status/20?s=21"
https://x.com/i/status/20
https://x.com/jack
https://x.com/i/status/20

capture is how x/testdata gets refreshed, and why no fixture in this repo is hand-written. It asks every surface that serves a reference, sends the reader's own request down to the headers, and writes each answer gzipped under the name the tests already load. A surface that will not answer gets a row saying so rather than aborting the run, so a rate limit on one of them does not cost you the two that worked. x capture 20 jack rewrites five fixtures.

serve exposes the reads over HTTP as NDJSON, one route each under /v1/, and mcp exposes the same set as MCP tools for an agent. Both take the global flags, so x serve --guest and x mcp --tier session serve at that tier and nothing else needs configuring. x mcp says how many tools it registered on stderr before it blocks, because a server waiting for JSON-RPC on stdin is otherwise indistinguishable from a hang.

They carry the reads and not the whole command line: 24 of the commands above, which is everything that takes a reference and answers with records. get, classify, crawl, discover, export, rdf, db, auth, and the rest of the local-store and session commands stay on the command line, because a walk that writes to your disk and a command that saves your cookies are not things to hand a network port.

Diagnostics

Command What it does Key flags
tiers What each credential tier reads, and which ones you have (alias info)
routes Which surface answers which question, at each tier
surfaces Every public route into X, with its tier, limit, and cache life
fields <kind> A record's fields, their types, and the surfaces that fill them
doctor Probe every surface live and report what answers today

The first four print tables the binary carries rather than tables a doc carries, which is the point of them: what x says about itself and what x does come out of the same code, so a row that stops being true shows up as a wrong answer instead of a stale paragraph. fields <kind> takes tweet or user and is measured, not declared: the surface numbers on each row come from parsing the committed fixtures, so a field that only surface 4 fills says so.

doctor is the one that costs requests. It sends one real read to each of the eight surfaces and reports what came back, with the time it took and whatever the surface says about its own rate window:

$ x doctor -o table
 SURFACE  NAME                  STATUS  MS    NOTE
 1        syndication tweet     ok      511   no rate headers
 2        syndication timeline  fail    754   rate limited by X on syndication.profile
 3        oembed                ok      1087  blockquote html, @jack, no rate headers
 4        guest graphql         skip          a session is configured, so this reads as surface 7
 5        app-only v1.1         ok            trends/available, 74 of 75 left
 6        media cdn             ok      237   pbs.twimg.com
 7        session graphql       ok      1730  UserByScreenName, 149 of 150 left
 8        x.com html            ok      1954  app-version, relay store, 2 microdata items

It exits non-zero when a surface did not answer, and the count is in the message, so it works as a health check in a script. A skip is not a failure: a surface x would not have used on this machine anyway is not evidence of anything.

Global flags

These apply to every command. See configuration for defaults and output formats for what -o produces. A value -o or --tier does not have is a usage error that lists the ones they do, because a typo that reads as if you had passed no flag at all is worse than a refusal.

Flag Meaning
-o, --output Output format: list|table|jsonl|json|csv|tsv|markdown|url|raw (default auto: list on a terminal, jsonl when piped)
--fields Comma-separated columns to project
--template Go text/template rendered per row
-n, --limit Maximum rows (0 means unlimited)
--no-header Omit the header row
--color auto|always|never (default auto)
--guest Enable the opt-in free guest-GraphQL tier
--tier Cap the tier (0|1|2) or pin one surface (syndication|oembed|web|guest|session)
--db Generic record sink provided by the framework; x's own typed store lives under --data-dir, not here
--data-dir Cache and store root
--profile Framework flag for a named config profile; x has no profiles, so it is carried and not acted on
--query-id Override a GraphQL query id (Op=hash)
--rate Minimum delay between requests (default 1s)
--retries Retries on 429/5xx (default 3)
--timeout Per-request timeout (default 30s)
--no-cache Bypass the HTTP cache
--dry-run Print the target instead of acting (e.g. open prints the URL)
-q, --quiet Suppress progress on stderr
-v, --verbose Show tier, endpoint, and timing
-h, --help Help for a command
--version Print the version