Claude
Skills
Sign in
Back

x

Included with Lifetime
$97 forever

Unified X (Twitter) CLI — fetch follows, diff snapshots, get likes/bookmarks, fetch latest posts, and take screenshots. Uses the official X API v2 with Bearer Token and OAuth 2.0 user-context auth.

Backend & APIs

What this skill does


# X CLI — Unified X (Twitter) Tool

Use the `xapi` CLI tool to interact with X (Twitter) via the official API v2. No cookies or scraping required.

Backward-compatible alias: `twitter` also points to the same CLI.

## Setup

### Bearer Token (required for most commands)

Get one from the [X Developer Portal](https://developer.x.com/en/portal/dashboard):

1. Create or select a project/app
2. Under "Keys and Tokens", find your Bearer Token
3. Set the environment variable:

```bash
export X_BEARER_TOKEN='your_token_here'
```

### OAuth 2.0 User Access Token (required for likes/bookmarks)

The likes and bookmarks endpoints require an OAuth 2.0 user access token — app-only Bearer Tokens are not supported for these.

1. In the [Developer Portal](https://developer.x.com/en/portal/dashboard), go to your app's **Settings → User authentication settings → Set up**
2. Enable OAuth 2.0 and select **Web App** (confidential client)
3. Set a callback URL (e.g. `http://localhost:3000/callback`)
4. Complete the [OAuth 2.0 PKCE flow](https://docs.x.com/fundamentals/authentication/oauth-2-0/authorization-code) to get an access token
5. Request scopes: `like.read`, `bookmark.read`, `tweet.read`, `users.read`, `offline.access`
6. Set the environment variables:

```bash
export X_ACCESS_TOKEN='your_user_access_token_here'
export X_REFRESH_TOKEN='your_refresh_token_here'  # optional, for future token refresh support
```

## Commands

| Command | Description |
|---------|-------------|
| `xapi resolve <username>` | Resolve username to user ID + profile info |
| `xapi fetch <username>` | Fetch and snapshot the full following list |
| `xapi diff <snap_a> <snap_b>` | Compare two snapshots for follows/unfollows |
| `xapi latest-post <username>` | Fetch most recent original post for an account |
| `xapi likes [username]` | Fetch liked tweets (default: HamelHusain, requires `X_ACCESS_TOKEN`) |
| `xapi bookmarks [username]` | Fetch bookmarked tweets (default: HamelHusain, requires `X_ACCESS_TOKEN`) |
| `xapi screenshot <url>` | Take a screenshot of a tweet (requires Playwright) |

## Usage

### Resolve a Username

```bash
xapi resolve elonmusk
```

### Fetch a Following Snapshot

```bash
# Auto-saves to ~/.x/<username>_<timestamp>.json
xapi fetch elonmusk

# Save to a specific file
xapi fetch elonmusk -o /tmp/elon_following.json

# Verbose mode shows progress
xapi fetch elonmusk -v
```

### Diff Two Snapshots

Compare two snapshots to detect new follows and unfollows:

```bash
# Basic diff
xapi diff snapshot_before.json snapshot_after.json

# Save diff output to file
xapi diff before.json after.json -o diff_result.json

# Also fetch the latest post for each unfollowed account
# (uses the newer snapshot's timestamp as cutoff)
xapi diff before.json after.json --fetch-posts -v
```

### Fetch Latest Post

```bash
xapi latest-post someuser

# Get the latest post before a specific date
xapi latest-post someuser --before 2026-01-15T00:00:00Z

# Get the latest post before a specific post ID
xapi latest-post someuser --before-post 1234567890

# Save to file
xapi latest-post someuser -o latest.json
```

### Fetch Likes

```bash
# Fetch liked tweets for the default user (HamelHusain)
xapi likes

# Fetch for a different user
xapi likes someuser

# Limit to 20 most recent likes
xapi likes --limit 20

# Save to file
xapi likes -o likes.json
```

### Fetch Bookmarks

```bash
# Fetch bookmarked tweets for the default user (HamelHusain)
xapi bookmarks

# Fetch for a different user (must be your own account)
xapi bookmarks yourusername

# Limit and save
xapi bookmarks --limit 50 -o bookmarks.json
```

### Screenshot a Tweet

```bash
# Screenshot a tweet (saves to tweet_<id>.png)
xapi screenshot https://x.com/user/status/1234567890

# Save to specific file
xapi screenshot https://x.com/user/status/1234567890 -o my_tweet.png

# Full page screenshot
xapi screenshot https://x.com/user/status/1234567890 --full-page

# Wait longer for slow loading tweets
xapi screenshot https://x.com/user/status/1234567890 --wait 5 -v
```

## Options

| Option | Short | Description |
|--------|-------|-------------|
| `--output` | `-o` | Output file path (default: stdout or auto-generated) |
| `--dir` | `-d` | Snapshot directory for fetch (default: `~/.x/`) |
| `--verbose` | `-v` | Show progress information |
| `--fetch-posts` | `-p` | Fetch latest post for unfollowed accounts (diff only) |
| `--before` | | ISO 8601 datetime cutoff for latest-post |
| `--before-post` | | Post ID cutoff for latest-post (returns posts older than this ID) |
| `--limit` | `-n` | Maximum number of tweets to fetch (likes/bookmarks) |
| `--full-page` | `-f` | Capture full page (screenshot only) |
| `--wait` | `-w` | Seconds to wait for page load (screenshot only) |

## Output Formats

### Snapshot (fetch)

```json
{
  "watched_username": "elonmusk",
  "watched_user_id": "44196397",
  "fetch_timestamp": "2026-01-01T00:00:00+00:00",
  "following_count": 500,
  "following": [
    {
      "id": "12345",
      "username": "someuser",
      "name": "Some User",
      "description": "Bio text",
      "public_metrics": { "followers_count": 1000, "following_count": 200, "tweet_count": 5000 }
    }
  ]
}
```

### Diff Output

```json
{
  "snapshot_before": { "file": "...", "watched_username": "...", "fetch_timestamp": "...", "following_count": 500 },
  "snapshot_after": { "file": "...", "watched_username": "...", "fetch_timestamp": "...", "following_count": 502 },
  "new_follows_count": 3,
  "unfollows_count": 1,
  "new_follows": [ { "id": "...", "username": "...", "name": "..." } ],
  "unfollows": [ { "id": "...", "username": "...", "name": "...", "latest_post": { "post_id": "...", "text": "...", "url": "..." } } ]
}
```

### Likes / Bookmarks Output

```json
[
  {
    "id": "1234567890",
    "text": "Tweet content here...",
    "created_at": "2026-01-01T00:00:00Z",
    "author": { "id": "987654321", "username": "example_user", "name": "Example User" },
    "public_metrics": { "like_count": 100, "retweet_count": 10 },
    "url": "https://x.com/example_user/status/1234567890"
  }
]
```

## Environment Variables

- `X_BEARER_TOKEN` — X API Bearer Token (required for resolve, fetch, diff, latest-post; also used by likes/bookmarks to resolve usernames)
- `X_ACCESS_TOKEN` — OAuth 2.0 user access token (required for likes and bookmarks)
- `X_REFRESH_TOKEN` — OAuth 2.0 refresh token (optional, for future token refresh support)
- `X_DEFAULT_USERNAME` — Override the default username for likes/bookmarks (optional, default: `HamelHusain`)

If a required variable is missing, the CLI exits with a clear error explaining what is needed and how to set it up.

## Default Username

The `likes` and `bookmarks` commands default to `HamelHusain` if no username is provided.

**Three ways to override (pick one):**

1. **Environment variable (recommended for skill installers):** Set `X_DEFAULT_USERNAME` to change the default for all invocations without editing code:
   ```bash
   export X_DEFAULT_USERNAME='yourusername'
   ```
   Add this to your `~/.bashrc` or `~/.zshrc` to persist it.

2. **Per-invocation:** Pass a username argument to override just that call:
   ```bash
   xapi likes otherusername
   xapi bookmarks otherusername
   ```

3. **Edit source code:** Change `_FALLBACK_USERNAME` at the top of `hamel/x_cli.py`:
   ```python
   _FALLBACK_USERNAME = "YourUsername"
   ```

## Requirements

The `hamel` package must be installed: `pip install hamel`

**For `xapi screenshot`**: Playwright is included as a dependency, but you need to install browser binaries:
```bash
playwright install chromium
```

## Verify Setup

After installing and setting the environment variables, run these commands to confirm everything works:

```bash
# 1. Check the CLI is installed
xapi --help

# 2. Test API access (lightweight call — uses X_BEARER_TOKEN)
xapi resolve github

# 3. Test latest post
xapi latest-post someuser

# 4. Test likes (requires X_ACCESS_TOKEN, defaults to HamelHusain)
xapi

Related in Backend & APIs