A comprehensive Elixir client for X API v1.1 with full endpoint coverage, rate limiting, multimedia support, and OAuth 1.0a authentication.
Features
- ✅ Full X API v1.1 Coverage - All GET and POST endpoints implemented
- ✅ OAuth 1.0a Authentication - Secure request signing
- ✅ Rate Limiting - Automatic rate limit tracking and retry
- ✅ Multimedia Support - Upload images, videos, and GIFs
- ✅ Chunked Uploads - Handle large video files efficiently
- ✅ Type Safety - Comprehensive typespec coverage
- ✅ Zero Dependencies - Minimal, focused dependencies
- ✅ Well Documented - Extensive documentation with examples
Installation
Add x_client to your list of dependencies in mix.exs:
def deps do
[
{:x_client, "~> 1.0.0"}
]
endRun mix deps.get to fetch the dependency.
Configuration
Add your X API credentials to your config file:
# config/config.exs
config :x_client,
consumer_key: "YOUR_CONSUMER_KEY",
consumer_secret: "YOUR_CONSUMER_SECRET",
access_token: "YOUR_ACCESS_TOKEN",
access_token_secret: "YOUR_ACCESS_TOKEN_SECRET"Or use environment variables:
config :x_client,
consumer_key: {:system, "X_CONSUMER_KEY"},
consumer_secret: {:system, "X_CONSUMER_SECRET"},
access_token: {:system, "X_ACCESS_TOKEN"},
access_token_secret: {:system, "X_ACCESS_TOKEN_SECRET"}Optional Configuration
config :x_client,
# Custom API base URL (default: "https://api.x.com/1.1")
base_url: "https://api.x.com/1.1",
# Custom upload URL (default: "https://upload.x.com/1.1")
upload_url: "https://upload.x.com/1.1",
# Enable automatic retry on rate limits (default: true)
auto_retry: true,
# Maximum number of retries (default: 3)
max_retries: 3Quick Start
# Post a tweet
{:ok, tweet} = XClient.Tweets.update("Hello from Elixir! 🚀")
# Upload media and attach to tweet
{:ok, media} = XClient.Media.upload("path/to/image.jpg")
{:ok, tweet} = XClient.Tweets.update(
"Check out this image!",
media_ids: [media["media_id_string"]]
)
# Get user timeline
{:ok, tweets} = XClient.Tweets.user_timeline(screen_name: "elixirlang", count: 50)
# Search tweets
{:ok, results} = XClient.Search.tweets("elixir lang", count: 100)
# Follow a user
{:ok, user} = XClient.Friendships.create(screen_name: "elixirlang")
# Like a tweet
{:ok, tweet} = XClient.Favorites.create("123456789")
# Send a direct message
{:ok, message} = XClient.DirectMessages.send("123456", "Hello!")Available Modules
Tweets
XClient.Tweets- Post, delete, retweet, and retrieve tweetsupdate/3- Post a tweetdestroy/3- Delete a tweetretweet/3- Retweet a tweetunretweet/3- Remove retweetshow/3- Get single tweetlookup/3- Get multiple tweetsuser_timeline/2- Get user's tweetsmentions_timeline/2- Get mentionsretweets_of_me/2- Get retweeted tweetsretweets/3- Get retweets of tweetretweeters_ids/3- Get retweeter IDs
Media
XClient.Media- Upload images, videos, and GIFsupload/3- Simple uploadchunked_upload/3- Chunked upload for large filesupload_status/2- Check processing statusadd_metadata/3- Add alt text
Users
XClient.Users- User information and searchshow/2- Get user detailslookup/2- Get multiple userssearch/3- Search userssuggestions/2- Get suggested categoriessuggestions_slug/3- Get category suggestionssuggestions_members/2- Get category members
Friendships
XClient.Friendships- Follow/unfollow operationscreate/2- Follow userdestroy/2- Unfollow usershow/2- Get relationship infofollowers_ids/2- Get follower IDsfollowers_list/2- Get follower detailsfriends_ids/2- Get following IDsfriends_list/2- Get following details
Favorites
XClient.Favorites- Like/unlike tweetscreate/3- Like a tweetdestroy/3- Unlike a tweetlist/2- Get liked tweets
Direct Messages
XClient.DirectMessages- Send and manage DMssend/4- Send a DMdestroy/2- Delete a DMlist/2- List DMsshow/2- Get single DM
Lists
XClient.Lists- Manage X listslist/2- Get all listsstatuses/2- Get list tweetsshow/2- Get list detailsmembers/2- Get list membersmembers_show/2- Check membershipmemberships/2- Get user's membershipsownerships/2- Get owned listssubscribers/2- Get subscriberssubscribers_show/2- Check subscriptionsubscriptions/2- Get subscriptions
Search
XClient.Search- Search for tweetstweets/3- Search tweets with query
Account
XClient.Account- Manage account settingsverify_credentials/2- Verify credentialsupdate_profile/2- Update profileupdate_profile_image/3- Update avatarupdate_profile_banner/3- Update bannerremove_profile_banner/1- Remove bannerupdate_settings/2- Update settingssettings/1- Get settings
Trends
XClient.Trends- Get trending topicsplace/3- Get trends for locationavailable/1- Get available locationsclosest/2- Get closest locations
Geo
XClient.Geo- Geographic informationid/2- Get place information
Help
XClient.Help- API informationconfiguration/1- Get API configlanguages/1- Get supported languagesprivacy/1- Get privacy policytos/1- Get terms of service
Application
XClient.Application- Application-level operationsrate_limit_status/2- Get rate limit info
Rate Limiting
The library automatically tracks rate limits and can retry requests when limits are exceeded.
# Automatic retry is enabled by default
{:ok, tweet} = XClient.Tweets.update("This will retry if rate limited")
# Disable automatic retry
config :x_client, auto_retry: false
# Check rate limit status
{:ok, status} = XClient.Application.rate_limit_status()Media Upload Examples
Simple Image Upload
# Upload from file path
{:ok, media} = XClient.Media.upload("path/to/image.jpg")
# Upload with alt text
{:ok, media} = XClient.Media.upload(
"path/to/image.jpg",
alt_text: "A beautiful sunset over the ocean"
)
# Use in tweet
{:ok, tweet} = XClient.Tweets.update(
"Check this out!",
media_ids: [media["media_id_string"]]
)Video Upload
# Upload video (automatically uses chunked upload for large files)
{:ok, media} = XClient.Media.upload(
"path/to/video.mp4",
media_category: "tweet_video"
)
# Wait for processing if needed (handled automatically)
{:ok, tweet} = XClient.Tweets.update(
"My new video!",
media_ids: [media["media_id_string"]]
)Multiple Images
# Upload multiple images
images = ["image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg"]
media_ids =
Enum.map(images, fn path ->
{:ok, media} = XClient.Media.upload(path)
media["media_id_string"]
end)
# Post tweet with multiple images (max 4)
{:ok, tweet} = XClient.Tweets.update(
"Check out these photos!",
media_ids: media_ids
)Advanced Usage
Custom Client
You can create a client with specific credentials:
client = XClient.client(
consumer_key: "specific_key",
consumer_secret: "specific_secret",
access_token: "specific_token",
access_token_secret: "specific_token_secret"
)
{:ok, tweet} = XClient.Tweets.update(client, "Tweet with custom creds")Pagination
Many endpoints support pagination with cursors:
# Get first page of followers
{:ok, %{"ids" => ids, "next_cursor" => cursor}} =
XClient.Friendships.followers_ids(screen_name: "elixirlang")
# Get next page
{:ok, %{"ids" => more_ids, "next_cursor" => next_cursor}} =
XClient.Friendships.followers_ids(
screen_name: "elixirlang",
cursor: cursor
)Error Handling
case XClient.Tweets.update("Hello!") do
{:ok, tweet} ->
IO.puts("Tweet posted: #{tweet["id_string"]}")
{:error, %XClient.Error{status: 429} = error} ->
IO.puts("Rate limited: #{error.message}")
{:error, %XClient.Error{status: 401}} ->
IO.puts("Authentication failed")
{:error, error} ->
IO.puts("Error: #{error.message}")
endRate Limits Reference
See X's rate limit documentation for complete details.
Common Limits
- Tweets: 300 per 3 hours (combined with retweets)
- Follows: 400 per 24 hours (user), 1000 per 24 hours (app)
- Likes: 1000 per 24 hours
- Direct Messages: 1000 per 24 hours (user), 15000 per 24 hours (app)
- Search: 180 per 15 minutes (user), 450 per 15 minutes (app)
Development
# Run tests
mix test
# Generate documentation
mix docs
# Format code
mix format
# Run static analysis
mix dialyzer
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built with ❤️ for the Elixir community
- Inspired by the node-x-api-v2 library
- Thanks to X for providing the API
Links
Support
If you encounter any issues or have questions:
- Check the documentation
- Search existing issues
- Open a new issue with details
Changelog
See CHANGELOG.md for version history.