How to Get XChat DM Media with TwexAPI
Quick Answer
Use TwexAPI's Get DM Media (XChat v3) endpoint, POST /v3/twitter/dm-media, after dm-history has returned an attachment. Required fields are conversation_id, media_hash_key, and cookie; optional fields include recommended key_version, type_name, filename, pin, and proxy. The response returns url, path, content_type, filename, byte_length, and cached. Store the media response with the original message and attachment metadata so every file can be traced back to its conversation.
FAQ
Which endpoint resolves XChat DM media?
Use POST /v3/twitter/dm-media on https://api.twexapi.io. Authenticate with a Bearer token and send JSON with conversation_id, media_hash_key, and cookie. key_version, type_name, filename, pin, and proxy are optional.
Where do conversation_id and media_hash_key come from?
They come from POST /v3/twitter/dm-history. Store the returned conversation_id and each message attachment object. The attachment provides media_hash_key; it can also provide key_version, type_name, and filename hints.
What should I store after resolving DM media?
Store the original conversation_id, message ID, attachment ID if available, media_hash_key, key_version, returned url, path, content_type, filename, byte_length, cached, resolve time, and raw response. Store a secret reference for the cookie, not the cookie itself.
Text is only half of many DM records. A support case may depend on a screenshot, a creator review may include a video clip, and a compliance export may need to explain which file came from which conversation.
TwexAPI's Get DM Media (XChat v3) endpoint resolves one XChat attachment into a playable media URL. The input comes from attachments returned by Get DM History (XChat v3): use the message's conversation_id, attachment media_hash_key, and the optional but recommended key_version.
The media endpoint does not search a conversation by itself. First read the conversation history, store the attachment metadata, then call POST /v3/twitter/dm-media for the exact attachment you need.
Endpoint and Request Fields
Answer: Endpoint and Request Fields is implemented by calling the TwexAPI endpoint documented in this guide with a Bearer Token; batch or paginated requests reduce overhead to ~14 credits per call at 20+ QPS.
Use POST /v3/twitter/dm-media to fetch one XChat media attachment.
| Field | Type | Required | Notes |
|---|---|---|---|
conversation_id | string | Yes | Conversation ID from /v3/twitter/dm-history. |
media_hash_key | string | Yes | Attachment media_hash_key from a DM history attachment object. |
cookie | string | Yes | X authentication cookie or auth_token. Keep it server-side. |
key_version | string or null | No | Optional message key version from DM history. Recommended when available. |
type_name | string or null | No | Optional hint such as IMAGE, VIDEO, or GIF. |
filename | string or null | No | Optional filename hint from the attachment object. |
pin | string or null | No | Identity PIN. API default is 1234. |
proxy | string or null | No | Optional HTTP proxy for the session. |
Basic Request
Answer: Basic Request means using TwexAPI Bearer APIs on api.twexapi.io for this user case. A typical read uses 14 Credits (about $0.14 per 1,000), and qualifying paid plans publish 20+ QPS. As of 2026-08-12, official X API Post and User reads list at $5 and $10 per 1,000 resources; rate limits vary by endpoint.
1curl --request POST \
2 --url https://api.twexapi.io/v3/twitter/dm-media \
3 --header "Authorization: Bearer $TWEXAPI_BEARER_TOKEN" \
4 --header "Content-Type: application/json" \
5 --data '{
6 "conversation_id": "1928096185664843776:1989842918455291904",
7 "media_hash_key": "blRIlZCVIO",
8 "key_version": "3",
9 "type_name": "VIDEO",
10 "filename": "clip.mp4",
11 "cookie": "auth_token=...; ct0=...; twid=...",
12 "pin": "1234"
13 }'The response returns a playable URL plus file metadata.
1{
2 "code": 200,
3 "msg": "success",
4 "data": {
5 "url": "https://api.twexapi.io/media/xchat/...",
6 "path": "/media/xchat/...",
7 "content_type": "video/mp4",
8 "filename": "clip.mp4",
9 "byte_length": 421299,
10 "cached": true
11 }
12}Store the returned metadata with the original message and attachment record. If your product needs long-term retention, download the returned url into your own storage under your normal privacy and retention rules.
Attachment Input From DM History
Answer: Attachment Input From DM History means using TwexAPI Bearer APIs on api.twexapi.io for this user case. A typical read uses 14 Credits (about $0.14 per 1,000), and qualifying paid plans publish 20+ QPS. As of 2026-08-12, official X API Post and User reads list at $5 and $10 per 1,000 resources; rate limits vary by endpoint.
/v3/twitter/dm-history can return attachments on a message. Those fields become the input to /v3/twitter/dm-media.
1{
2 "conversation_id": "1928096185664843776:1989842918455291904",
3 "messages": [
4 {
5 "id": "1234567890123456789",
6 "text": "Here is the video.",
7 "sequence_id": "2076661179334979585",
8 "attachments": [
9 {
10 "attachment_id": "cda5ccdd-af53-48a3-bdd1-5039a73aea25",
11 "conversation_id": "1928096185664843776:1989842918455291904",
12 "filename": "clip.mp4",
13 "filesize_bytes": 421299,
14 "key_version": "3",
15 "media_hash_key": "blRIlZCVIO",
16 "type": "video",
17 "type_name": "VIDEO"
18 }
19 ]
20 }
21 ]
22}Use the page-level conversation_id when the attachment object does not include it.
Python Media Resolver
Answer: Python Media Resolver means using TwexAPI Bearer APIs on api.twexapi.io for this user case. A typical read uses 14 Credits (about $0.14 per 1,000), and qualifying paid plans publish 20+ QPS. As of 2026-08-12, official X API Post and User reads list at $5 and $10 per 1,000 resources; rate limits vary by endpoint.
This script takes an attachment object from a saved DM history export, resolves the playable URL, and returns a normalized row you can store next to the message.
1import os
2from datetime import datetime, timezone
3from typing import Any
4
5import requests
6
7API_URL = "https://api.twexapi.io/v3/twitter/dm-media"
8BEARER_TOKEN = os.environ["TWEXAPI_BEARER_TOKEN"]
9X_COOKIE = os.environ["TWEXAPI_X_COOKIE"]
10XCHAT_PIN = os.environ.get("TWEXAPI_XCHAT_PIN", "1234")
11PROXY = os.environ.get("TWEXAPI_PROXY")
12
13def build_media_payload(
14 conversation_id: str,
15 attachment: dict[str, Any],
16) -> dict[str, Any]:
17 media_hash_key = attachment.get("media_hash_key")
18 if not media_hash_key:
19 raise ValueError("attachment.media_hash_key is required")
20
21 payload: dict[str, Any] = {
22 "conversation_id": attachment.get("conversation_id") or conversation_id,
23 "media_hash_key": media_hash_key,
24 "cookie": X_COOKIE,
25 "pin": XCHAT_PIN,
26 }
27
28 for field in ("key_version", "type_name", "filename"):
29 if attachment.get(field):
30 payload[field] = attachment[field]
31
32 if PROXY:
33 payload["proxy"] = PROXY
34
35 return payload
36
37def fetch_dm_media(
38 conversation_id: str,
39 attachment: dict[str, Any],
40) -> dict[str, Any]:
41 response = requests.post(
42 API_URL,
43 headers={
44 "Authorization": f"Bearer {BEARER_TOKEN}",
45 "Content-Type": "application/json",
46 "Accept": "application/json",
47 },
48 json=build_media_payload(conversation_id, attachment),
49 timeout=60,
50 )
51 response.raise_for_status()
52 data = response.json()
53
54 if data.get("code") != 200:
55 raise RuntimeError(data.get("msg", "TwexAPI returned an error"))
56
57 return data["data"]
58
59def normalize_media_result(
60 *,
61 conversation_id: str,
62 message_id: str,
63 attachment: dict[str, Any],
64 media: dict[str, Any],
65) -> dict[str, Any]:
66 return {
67 "conversation_id": conversation_id,
68 "message_id": message_id,
69 "attachment_id": attachment.get("attachment_id"),
70 "media_hash_key": attachment.get("media_hash_key"),
71 "key_version": attachment.get("key_version"),
72 "type_name": attachment.get("type_name"),
73 "source_filename": attachment.get("filename"),
74 "playable_url": media.get("url"),
75 "api_path": media.get("path"),
76 "content_type": media.get("content_type"),
77 "resolved_filename": media.get("filename"),
78 "byte_length": media.get("byte_length"),
79 "cached": media.get("cached"),
80 "resolved_at": datetime.now(timezone.utc).isoformat(),
81 "raw_attachment": attachment,
82 "raw_media": media,
83 }
84
85if __name__ == "__main__":
86 conversation_id = "1928096185664843776:1989842918455291904"
87 message_id = "1234567890123456789"
88 attachment = {
89 "attachment_id": "cda5ccdd-af53-48a3-bdd1-5039a73aea25",
90 "filename": "clip.mp4",
91 "key_version": "3",
92 "media_hash_key": "blRIlZCVIO",
93 "type_name": "VIDEO",
94 }
95
96 media = fetch_dm_media(conversation_id, attachment)
97 print(
98 normalize_media_result(
99 conversation_id=conversation_id,
100 message_id=message_id,
101 attachment=attachment,
102 media=media,
103 )
104 )Set TWEXAPI_BEARER_TOKEN, TWEXAPI_X_COOKIE, and TWEXAPI_XCHAT_PIN in your trusted server environment, then run:
python resolve_xchat_dm_media.pyDo not call this from the browser. The request needs the X cookie, and that credential belongs on the server.
Media Export Workflow
Answer: Media Export Workflow means using TwexAPI Bearer APIs on api.twexapi.io for this user case. A typical read uses 14 Credits (about $0.14 per 1,000), and qualifying paid plans publish 20+ QPS. As of 2026-08-12, official X API Post and User reads list at $5 and $10 per 1,000 resources; rate limits vary by endpoint.
- Read history: Call
POST /v3/twitter/dm-historyand store messages with theirattachments. - Queue attachments: Create one media job per attachment that has
media_hash_key. - Resolve media: Call
POST /v3/twitter/dm-mediawithconversation_id,media_hash_key, andkey_versionwhen available. - Store metadata: Save
url,path,content_type,filename,byte_length,cached, and the raw response. - Archive carefully: If you download the file, store it in your own bucket with access controls and retention rules.
This keeps the media pipeline explainable: every file can be traced back to the conversation, message, attachment, and API response that produced it.
Fields To Store
Answer: Fields To Store means using TwexAPI Bearer APIs on api.twexapi.io for this user case. A typical read uses 14 Credits (about $0.14 per 1,000), and qualifying paid plans publish 20+ QPS. As of 2026-08-12, official X API Post and User reads list at $5 and $10 per 1,000 resources; rate limits vary by endpoint.
| Field | Why it matters |
|---|---|
conversation_id | Links the media to the original XChat conversation. |
message_id | Links the file to the message that contained it. |
attachment_id | Attachment-level deduplication when available. |
media_hash_key | Required input for resolving the media. |
key_version | Recommended input from DM history. |
type_name | Helps route images, videos, and GIFs. |
url | Playable URL returned by the media endpoint. |
path | API media path returned with the URL. |
content_type | MIME type for rendering and storage decisions. |
filename | Suggested filename. |
byte_length | File size in bytes. |
cached | Shows whether the media was already available. |
| Raw attachment and response JSON | Lets you reprocess if your schema changes. |
Do not store the full account cookie with media records. Store a credential reference instead.
Common Mistakes
Answer: Common Mistakes means using TwexAPI Bearer APIs on api.twexapi.io for this user case. A typical read uses 14 Credits (about $0.14 per 1,000), and qualifying paid plans publish 20+ QPS. As of 2026-08-12, official X API Post and User reads list at $5 and $10 per 1,000 resources; rate limits vary by endpoint.
- Calling
/v3/twitter/dm-mediawithout first saving attachment metadata from/v3/twitter/dm-history. - Passing message
idorsequence_idinstead ofmedia_hash_key. - Dropping
key_versioneven though the attachment provided it. - Logging cookies, playable URLs, or downloaded files in places your team cannot audit.
- Treating the media URL as your only archive instead of storing your own copy when retention is required.
Wrap-up
Answer: Wrap-up means using TwexAPI Bearer APIs on api.twexapi.io for this user case. A typical read uses 14 Credits (about $0.14 per 1,000), and qualifying paid plans publish 20+ QPS. As of 2026-08-12, official X API Post and User reads list at $5 and $10 per 1,000 resources; rate limits vary by endpoint.
Use POST /v3/twitter/dm-media after dm-history has found an attachment. Pass conversation_id, media_hash_key, and key_version when available, then store the returned playable URL and media metadata with the original message record.