Skip to main content
This guide covers the key concepts you need to integrate the Direct Messages lookup endpoints into your application.

Authentication

DM endpoints require user authentication to access private conversations:
MethodDescription
OAuth 2.0 Authorization Code with PKCERecommended
OAuth 1.0a User ContextLegacy support
App-Only authentication is not supported. All Direct Messages are private.

Required scopes (OAuth 2.0)

ScopeRequired for
dm.readReading DM events
tweet.readRequired with dm.read
users.readRequired with dm.read

Conversation types

One-to-one

Always has exactly two participants. Conversation ID format: {smaller_user_id}-{larger_user_id}

Group

Two or more participants. Membership can change over time.

Event types

EventDescriptionKey fields
MessageCreateA message was senttext, sender_id
ParticipantsJoinUser joined groupparticipant_ids, sender_id
ParticipantsLeaveUser left groupparticipant_ids

Example events

{
  "id": "1582838499983564806",
  "event_type": "MessageCreate",
  "text": "Hi everyone.",
  "sender_id": "944480690",
  "dm_conversation_id": "1578398451921985538",
  "created_at": "2022-10-19T20:58:00.000Z"
}
{
  "id": "1582835469712138240",
  "event_type": "ParticipantsJoin",
  "participant_ids": ["944480690"],
  "sender_id": "17200003",
  "dm_conversation_id": "1578398451921985538",
  "created_at": "2022-10-19T20:45:58.000Z"
}
{
  "id": "1582838535115067392",
  "event_type": "ParticipantsLeave",
  "participant_ids": ["944480690"],
  "dm_conversation_id": "1578398451921985538",
  "created_at": "2022-10-19T20:58:09.000Z"
}

Fields and expansions

Default fields

Event typeDefault fields
MessageCreateid, event_type, text
ParticipantsJoin/Leaveid, event_type, participant_ids

Available fields

FieldDescriptionEvents
dm_conversation_idConversation IDAll
created_atEvent timestampAll
sender_idWho sent/invitedMessageCreate, Join
attachmentsMedia attachmentsMessageCreate
referenced_tweetsShared PostsMessageCreate

Available expansions

ExpansionReturns
sender_idUser object for sender
participant_idsUser objects for participants
attachments.media_keysMedia objects
referenced_tweets.idPost objects

Example with expansions

cURL
curl "https://api.x.com/2/dm_events?\
dm_event.fields=created_at,sender_id,attachments&\
expansions=sender_id,attachments.media_keys&\
user.fields=username,profile_image_url&\
media.fields=url,type" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN"
from xdk import Client

client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN")

# Get DM events with expansions
for page in client.dm_events.list(
    dm_event_fields=["created_at", "sender_id", "attachments"],
    expansions=["sender_id", "attachments.media_keys"],
    user_fields=["username", "profile_image_url"],
    media_fields=["url", "type"],
    max_results=100
):
    for event in page.data:
        print(f"Event: {event.event_type} - {event.text}")
import { Client } from "@xdevplatform/xdk";

const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" });

const paginator = client.dmEvents.list({
  dmEventFields: ["created_at", "sender_id", "attachments"],
  expansions: ["sender_id", "attachments.media_keys"],
  userFields: ["username", "profile_image_url"],
  mediaFields: ["url", "type"],
  maxResults: 100,
});

for await (const page of paginator) {
  page.data?.forEach((event) => {
    console.log(`Event: ${event.event_type} - ${event.text}`);
  });
}

Pagination

DM events are returned in reverse chronological order (newest first):
cURL
# First request
curl "https://api.x.com/2/dm_events?max_results=100" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN"

# Subsequent request with pagination token
curl "https://api.x.com/2/dm_events?max_results=100&pagination_token=NEXT_TOKEN" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN"
from xdk import Client

client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN")

# The SDK handles pagination automatically
all_events = []

for page in client.dm_events.list(max_results=100):
    if page.data:
        all_events.extend(page.data)

print(f"Found {len(all_events)} DM events")
import { Client } from "@xdevplatform/xdk";

const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" });

async function getAllDMEvents() {
  const allEvents = [];

  // The SDK handles pagination automatically
  const paginator = client.dmEvents.list({ maxResults: 100 });

  for await (const page of paginator) {
    if (page.data) {
      allEvents.push(...page.data);
    }
  }

  return allEvents;
}

// Usage
const events = await getAllDMEvents();
console.log(`Found ${events.length} DM events`);
Events from up to 30 days ago are available.

ID compatibility with v1.1

Conversation and event IDs are shared between v1.1 and v2 endpoints. This means you can:
  • Use v2 to retrieve events, then use v1.1 to delete specific messages
  • Reference conversation IDs from x.com URLs in API requests

Next steps

Quickstart

Make your first DM lookup request

Send DMs

Send Direct Messages

API Reference

Full endpoint documentation

Sample code

Working code examples