# XDK Elixir
[](https://hex.pm/packages/xdk_elixir)
[](https://github.com/mikehostetler/xdk-elixir/actions/workflows/ci.yml)
Auto-generated Elixir client for the [X (Twitter) API v2](https://developer.x.com/en/docs/x-api).
> **Note:** This SDK is auto-generated by a [fork of XDK](https://github.com/mikehostetler/xdk), a Rust-based OpenAPI code generator maintained by X's developer platform team. The Elixir target is an addition to that fork. **Do not hand-edit files under `lib/` or `test/`** — all changes should be made in the generator templates and re-generated.
## Installation
Add `xdk_elixir` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:xdk_elixir, "~> 1.0"}
]
end
```
## Prerequisites
You need an X Developer account and API credentials. Go to the [X Developer Portal](https://developer.x.com/en/portal/dashboard) to create a project and app.
Depending on the endpoints you want to use, you'll need one or more of:
| Credential | Env Variable | Used For |
|---|---|---|
| **Bearer Token** | `BEARER_TOKEN` | App-only access (read-only public data: search, user lookup, etc.) |
| **Consumer Key + Secret** | `CONSUMER_KEY`, `SECRET_KEY` | Generating bearer tokens, OAuth 1.0a signing |
| **Access Token + Secret** | `ACCESS_TOKEN`, `ACCESS_TOKEN_SECRET` | User-context access via OAuth 1.0a (post, like, follow, DMs, etc.) |
Most read endpoints work with just a Bearer Token. Write endpoints (posting, liking, following) require OAuth 1.0a user-context auth.
## Usage
### 1. Start a Finch pool
XDK uses [Finch](https://github.com/keathley/finch) for HTTP. Add it to your supervision tree:
```elixir
children = [
{Finch, name: MyApp.Finch}
]
Supervisor.start_link(children, strategy: :one_for_one)
```
### 2. Create a client
```elixir
# App-only (Bearer Token)
client = Xdk.new(finch: MyApp.Finch, bearer: "YOUR_BEARER_TOKEN")
# Or pass auth explicitly
client = Xdk.new(finch: MyApp.Finch, auth: {:bearer, "YOUR_BEARER_TOKEN"})
```
### 3. Make API calls
```elixir
# Look up a user
{:ok, user} = Xdk.Users.get_by_username(client, "elaborapp",
user_fields: ["id", "name", "public_metrics"]
)
# Search recent posts
{:ok, results} = Xdk.Posts.search_recent(client,
query: "elixir lang",
max_results: 10,
tweet_fields: ["id", "text", "created_at", "author_id"]
)
# Get a post by ID
{:ok, post} = Xdk.Posts.get_by_id(client, "1234567890",
tweet_fields: ["id", "text", "author_id"]
)
```
### OAuth 1.0a (User Context)
For endpoints that require user-context authentication (posting, liking, following, DMs):
```elixir
credentials = OAuther.credentials(
consumer_key: System.get_env("CONSUMER_KEY"),
consumer_secret: System.get_env("SECRET_KEY"),
token: System.get_env("ACCESS_TOKEN"),
token_secret: System.get_env("ACCESS_TOKEN_SECRET")
)
client = Xdk.new(finch: MyApp.Finch, auth: {:oauth1, credentials})
# Post a tweet
{:ok, result} = Xdk.Posts.create(client, %{"text" => "Hello from XDK Elixir!"})
# Like a post
{:ok, _} = Xdk.Users.like_post(client, user_id, %{"tweet_id" => "1234567890"})
```
### Pagination
Use `Xdk.Paginator` to lazily page through results:
```elixir
fetch = fn token ->
opts = [query: "elixir", max_results: 100]
opts = if token, do: Keyword.put(opts, :pagination_token, token), else: opts
Xdk.Posts.search_recent(client, opts)
end
# Get all items across pages as a lazy stream
Xdk.Paginator.items(fetch)
|> Enum.take(500)
```
### Streaming
For streaming endpoints (filtered stream, sample stream):
```elixir
Xdk.Stream.get_filtered_stream(client)
|> Stream.each(fn
{:ok, tweet} -> IO.inspect(tweet)
{:error, err} -> IO.warn("Stream error: #{inspect(err)}")
end)
|> Stream.run()
```
## Available Modules
Every X API endpoint group has a corresponding module:
| Module | Description |
|---|---|
| `Xdk.Users` | User lookup, followers, following, muting, blocking, bookmarks |
| `Xdk.Posts` | Tweet CRUD, search, counts, quotes, reposts, analytics |
| `Xdk.Lists` | List management, members, followers |
| `Xdk.Spaces` | Twitter Spaces |
| `Xdk.Communities` | Communities |
| `Xdk.CommunityNotes` | Community Notes |
| `Xdk.DirectMessages` | DM conversations and messages |
| `Xdk.Media` | Media upload |
| `Xdk.Compliance` | Compliance streams and jobs |
| `Xdk.Stream` | Filtered and sample streams |
| `Xdk.Trends` | Trending topics |
| `Xdk.Usage` | API usage metrics |
| `Xdk.Connections` | User connections |
| `Xdk.Activity` | Activity feed |
| `Xdk.AccountActivity` | Account Activity API |
| `Xdk.Webhooks` | Webhook management |
| `Xdk.News` | News |
| `Xdk.Marketplace` | Marketplace |
| `Xdk.Chat` | Chat |
## Error Handling
All API calls return `{:ok, map()}` or `{:error, error}`. Errors are structured using [Splode](https://hex.pm/packages/splode):
```elixir
case Xdk.Users.get_by_username(client, "nonexistent_user_12345") do
{:ok, data} ->
IO.inspect(data)
{:error, %Xdk.Errors.RateLimitError{retry_after_ms: ms}} ->
Process.sleep(ms)
# retry...
{:error, %Xdk.Errors.ApiError{status: 404}} ->
IO.puts("User not found")
{:error, %Xdk.Errors.TransportError{reason: reason}} ->
IO.puts("Network error: #{inspect(reason)}")
end
```
## Configuration
Set defaults via application config so you don't have to pass them every time:
```elixir
# config/config.exs
config :xdk, :default_config,
finch: MyApp.Finch
```
Then just pass auth when creating a client:
```elixir
client = Xdk.new(bearer: "YOUR_BEARER_TOKEN")
```
## Code Generation
This package is auto-generated from the X API's OpenAPI specification. The generator lives at [mikehostetler/xdk](https://github.com/mikehostetler/xdk) (a fork of `xdevplatform/xdk`).
To regenerate:
```bash
cd xdk && cargo run -- elixir --latest true
cp -r xdk/elixir/lib xdk/elixir/test xdk/elixir/mix.exs ../xdk-elixir/
cd ../xdk-elixir && mix deps.get && mix test
```
## License
Apache-2.0 — see [LICENSE](LICENSE) for details.