Skip to main content
The PostsClient provides methods for interacting with posts (tweets) on X. It handles authentication, pagination, and request/response management automatically.

Initialization

The PostsClient is accessed through the main XDK client:
from xdk import Client

client = Client(bearer_token="your_bearer_token")
posts = client.posts

Methods

create()

Creates a new post for the authenticated user, or edits an existing post when edit_options are provided.
body
CreateRequest
required
The request body containing post details
Returns: CreateResponse
data
object
The created post data
errors
array
Array of error objects if any occurred
Example:
from xdk.posts.models import CreateRequest

# Create a simple post
request = CreateRequest(text="Hello, world!")
response = client.posts.create(body=request)
print(f"Created post: {response.data.id}")

# Create a post with media
request = CreateRequest(
    text="Check out this image!",
    media={"media_ids": ["1234567890"]}
)
response = client.posts.create(body=request)

# Create a reply
request = CreateRequest(
    text="Great post!",
    reply={"in_reply_to_tweet_id": "9876543210"}
)
response = client.posts.create(body=request)

get_by_id()

Retrieves details of a specific post by its ID.
id
string
required
A single post ID
tweet_fields
string[]
Tweet fields to include in the response. Options: “attachments”, “author_id”, “context_annotations”, “conversation_id”, “created_at”, “edit_controls”, “entities”, “geo”, “id”, “in_reply_to_user_id”, “lang”, “public_metrics”, “referenced_tweets”, “reply_settings”, “source”, “text”, “withheld”
expansions
string[]
Fields to expand. Options: “attachments.media_keys”, “attachments.poll_ids”, “author_id”, “entities.mentions.username”, “geo.place_id”, “in_reply_to_user_id”, “referenced_tweets.id”, “referenced_tweets.id.author_id”
media_fields
string[]
Media fields to display when media is expanded
poll_fields
string[]
Poll fields to display when polls are expanded
user_fields
string[]
User fields to display when users are expanded
place_fields
string[]
Place fields to display when places are expanded
Returns: GetByIdResponse
data
object
The post object with requested fields
includes
object
Expanded objects (users, media, polls, places) based on expansions parameter
errors
array
Array of error objects if any occurred
Example:
# Get basic post data
post = client.posts.get_by_id(id="1234567890")
print(f"Post text: {post.data.text}")

# Get post with detailed fields
post = client.posts.get_by_id(
    id="1234567890",
    tweet_fields=["created_at", "public_metrics", "author_id"],
    expansions=["author_id"],
    user_fields=["username", "name", "profile_image_url"]
)
print(f"Author: {post.includes.users[0].name}")
print(f"Likes: {post.data.public_metrics.like_count}")

get_by_ids()

Retrieves details of multiple posts by their IDs (up to 100 per request).
ids
string[]
required
A list of post IDs (max 100)
tweet_fields
string[]
Tweet fields to include in the response
expansions
string[]
Fields to expand
media_fields
string[]
Media fields to display when media is expanded
poll_fields
string[]
Poll fields to display when polls are expanded
user_fields
string[]
User fields to display when users are expanded
place_fields
string[]
Place fields to display when places are expanded
Returns: GetByIdsResponse
data
array
Array of post objects
includes
object
Expanded objects based on expansions parameter
errors
array
Array of error objects if any occurred
Example:
# Get multiple posts
posts = client.posts.get_by_ids(
    ids=["1234567890", "9876543210", "1111111111"],
    tweet_fields=["created_at", "public_metrics"]
)

for post in posts.data:
    print(f"Post {post.id}: {post.text}")

delete()

Deletes a specific post by its ID (must be owned by the authenticated user).
id
string
required
The ID of the post to delete
Returns: DeleteResponse
data
object
Deletion confirmation
errors
array
Array of error objects if any occurred
Example:
# Delete a post
response = client.posts.delete(id="1234567890")
if response.data.deleted:
    print("Post deleted successfully")

search_recent()

Searches for posts from the last 7 days matching a query. Returns an iterator for automatic pagination.
query
string
required
Search query using X’s search syntax (e.g., “python #coding”)
start_time
string
Start time in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ)
end_time
string
End time in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ)
since_id
string
Returns results with post ID greater than (more recent than) this ID
until_id
string
Returns results with post ID less than (older than) this ID
max_results
integer
Maximum number of results per page (10-100, default 10)
pagination_token
string
Token to get a specific page of results
sort_order
string
Sort order: “recency” or “relevancy”
tweet_fields
string[]
Tweet fields to include
expansions
string[]
Fields to expand
media_fields
string[]
Media fields to display
poll_fields
string[]
Poll fields to display
user_fields
string[]
User fields to display
place_fields
string[]
Place fields to display
Returns: Iterator[SearchRecentResponse] - Yields one page at a time
data
array
Array of posts matching the search query
meta
object
Metadata about the search results including pagination tokens
includes
object
Expanded objects based on expansions parameter
Example:
# Search recent posts with automatic pagination
for page in client.posts.search_recent(
    query="python programming",
    max_results=100,
    tweet_fields=["created_at", "public_metrics"]
):
    for post in page.data:
        print(f"{post.text} - Likes: {post.public_metrics.like_count}")

# Get just the first page
results = next(client.posts.search_recent(query="AI"))
print(f"Found {len(results.data)} posts")

# Search with time range
from datetime import datetime, timedelta

end_time = datetime.utcnow()
start_time = end_time - timedelta(days=3)

for page in client.posts.search_recent(
    query="#python",
    start_time=start_time.isoformat() + "Z",
    end_time=end_time.isoformat() + "Z",
    sort_order="relevancy"
):
    print(f"Page has {len(page.data)} posts")

get_liking_users()

Retrieves a list of users who liked a specific post. Returns an iterator for automatic pagination.
id
string
required
A single post ID
max_results
integer
Maximum number of results per page (1-100, default 100)
pagination_token
string
Token to get a specific page of results
user_fields
string[]
User fields to display. Options: “created_at”, “description”, “entities”, “id”, “location”, “name”, “pinned_tweet_id”, “profile_image_url”, “protected”, “public_metrics”, “url”, “username”, “verified”, “withheld”
expansions
string[]
Fields to expand
tweet_fields
string[]
Tweet fields for expanded tweets
Returns: Iterator[GetLikingUsersResponse] - Yields one page at a time
data
array
Array of user objects who liked the post
meta
object
Metadata including pagination tokens and result count
includes
object
Expanded objects based on expansions parameter
Example:
# Get all users who liked a post
for page in client.posts.get_liking_users(
    id="1234567890",
    user_fields=["username", "name", "public_metrics"]
):
    for user in page.data:
        print(f"@{user.username} ({user.name}) - {user.public_metrics.followers_count} followers")

# Get just the first 50 users
results = next(client.posts.get_liking_users(
    id="1234567890",
    max_results=50
))
print(f"{len(results.data)} users liked this post")

Authentication

The PostsClient supports multiple authentication methods:
  • Bearer Token: App-only authentication (read operations)
  • OAuth 2.0 User Context: User authentication (read and write)
  • OAuth 1.0a: Legacy user authentication (read and write)
The client automatically selects the appropriate authentication method based on:
  1. Available credentials
  2. Endpoint requirements
  3. Operation type (read vs write)
For write operations (create, delete), OAuth is required:
from xdk import Client

# OAuth 2.0 User Context
client = Client(access_token="user_access_token")

# OAuth 1.0a
from xdk.auth import OAuth1
auth = OAuth1(
    consumer_key="your_key",
    consumer_secret="your_secret",
    access_token="user_token",
    access_token_secret="user_secret"
)
client = Client(auth=auth)

Pagination

Methods that return multiple results (search_recent, get_liking_users, etc.) return iterators that automatically handle pagination:
# Automatic pagination - iterate through all results
for page in client.posts.search_recent(query="AI"):
    for post in page.data:
        process_post(post)

# Manual pagination - get specific pages
page1 = next(client.posts.search_recent(query="AI"))
page2 = next(client.posts.search_recent(
    query="AI",
    pagination_token=page1.meta.next_token
))

# Get just first page and stop
results = next(client.posts.search_recent(query="AI"))

Error Handling

All methods may raise HTTP errors. Wrap calls in try-except blocks:
from requests.exceptions import HTTPError

try:
    post = client.posts.get_by_id(id="invalid_id")
except HTTPError as e:
    if e.response.status_code == 404:
        print("Post not found")
    elif e.response.status_code == 401:
        print("Authentication failed")
    else:
        print(f"Error: {e}")
except ValueError as e:
    print(f"Authentication error: {e}")

Rate Limits

X API has rate limits that vary by endpoint and authentication method. The client does not automatically handle rate limiting, but you can add delays:
import time

for page in client.posts.search_recent(query="python"):
    for post in page.data:
        process_post(post)
    time.sleep(1)  # Wait 1 second between pages
Consider implementing exponential backoff for production applications.

Build docs developers (and LLMs) love