Skip to main content
This workflow returns a hosted media URL for tweet posts. It also returns an uploaded media ID for direct messages. Use it when a product, support, CRM, or AI agent system needs either. Xquik accepts local files with multipart/form-data and HTTPS media URLs with application/json. You may already have public image URLs or a public MP4 video URL. If so, skip POST /x/media for a tweet or reply. Pass those URLs in the media array on POST /x/tweets. Send up to 4 images or exactly 1 MP4 video up to 100 MB. POST /x/media hosts local files, validates a generated media URL, or produces a DM mediaId. Use it when you need one of those.
Need to post an MP4 tweet? If the MP4 is already a public HTTPS URL, skip upload. In that case call Create Tweet with media: ["https://example.com/video.mp4"]. Use POST /x/media first only to host a local file or validate a generated URL. Then pass the returned mediaUrl to POST /x/tweets.

When to use this workflow

Upload a local file

Upload a local image, GIF, or MP4 with POST /x/media and multipart/form-data.

Upload generated media

Upload AI-generated media from a URL with POST /x/media and application/json.

Post tweets with public media URLs

Pass the URLs directly in the media array on POST /x/tweets.

Post tweets with media uploaded through Xquik

Pass returned mediaUrl in the media array on POST /x/tweets.

Post tweet replies with uploaded media

Pass mediaUrl plus reply_to_tweet_id on POST /x/tweets.

Send a DM with uploaded media

Pass returned mediaId as the only item in media_ids on POST /x/dm/{userId}.

Data you get

DM attachment ID

mediaId is the uploaded media ID for one-item DM media_ids arrays. Media IDs are valid for 24 hours after upload.

Tweet media URL

mediaUrl is the public media URL for tweet media arrays.

Upload confirmation

success is true after upload completes.

Tweet confirmation

POST /x/tweets returns tweetId after Xquik confirms the media tweet or reply.

End-to-end media handoff

Use one checkpoint object after upload and the downstream tweet, reply, or DM write. Keep the 2 fields separate. Tweets and replies use public mediaUrl values in media. DMs use the uploaded mediaId as the only media_ids item.

Upload checkpoint

Store returned mediaId, mediaUrl, success, source URL or filename, and the upload endpoint.

Tweet URL checkpoint

Store the public mediaUrl in media, plus returned tweetId, chargedCredits, and optional writeActionId.

Reply URL checkpoint

Store reply_to_tweet_id, public mediaUrl, returned tweetId, and pending confirmation state when present.

DM ID checkpoint

Store exactly one uploaded mediaId in media_ids, plus returned messageId and recipient ID.

Field boundary

Reject tweet handoffs that put uploaded media IDs in media. Also reject those that send media_ids to POST /x/tweets.

Audit row

Store upload, tweet, reply, and DM IDs together. Downstream systems can then reconcile each media path.

Step 1. Upload media by URL

AI agents, MCP clients, or workflow tools may hold generated image or MP4 URLs. If Xquik should validate and host one, use JSON URL upload. Some tweet-only workflows already have public image URLs or exactly 1 public MP4 URL. The MP4 can be up to 100 MB. For those workflows, call POST /x/tweets directly with media. The URL must use HTTPS and resolve to a public address. It must return a supported media content type within 30 seconds. It must stay under the 15,728,640-byte URL download cap.

URL upload checklist

HTTPS URL

Non-HTTPS URLs return 422 media_download_failed.

Public host

Xquik rejects private or reserved IP targets.

Supported content type

Xquik accepts AVIF, GIF, JPEG, PNG, WebP, and MP4.

Size cap

Larger URL downloads than 15,728,640 bytes return 422 media_download_failed.

30-second response window

Slow origins can time out before upload starts.

Step 2. Post a tweet or reply with mediaUrl

POST /x/tweets accepts public media URLs in media. Send up to 4 images or exactly 1 MP4 video up to 100 MB. Use the mediaUrl returned by POST /x/media. Text-only tweet or reply writes cost 30 credits. Attached media adds 2 credits per started MB across all files.
To post a media reply, send the same media URL array and add reply_to_tweet_id.
Store the action id, request.hash, billing, result, original mediaUrl, and parent reply_to_tweet_id. Poll Get write action status while terminal is false. Retry only when safeToRetry is true, using a new Idempotency-Key. Do not send media_ids to POST /x/tweets. That endpoint rejects media_ids and expects the media URL array instead.

Step 3. Send a DM with mediaId

POST /x/dm/{userId} accepts one uploaded media ID in media_ids. Use the mediaId returned by POST /x/media.
DMs accept exactly one uploaded media item. Send no media_ids field for text-only DMs. Store message_id, media_id, recipient, account, and send status in shared handoff rows. Keep full DM body text in private systems only.

JSON Lines handoff

For queues, CRM syncs, warehouse loads, or agent memory, write to xquik-media-handoff.jsonl. Write one record per upload and downstream write.

Media upload row

Tweet or reply row

DM media row

Use media_url for tweet and reply media arrays. Use media_id for the single DM media_ids item.

Step 4. Upload a local file

Use multipart upload when your app has the file bytes. Supported formats are AVIF, GIF, JPEG, PNG, WebP, and MP4.
For MP4 files longer than 140 seconds, add is_long_video=true.

Cost and error handling

Upload media costs 10 credits per upload call. Posting a tweet or reply is a separate 30-credit create-tweet call before media surcharges. Sending the DM is a separate 10-credit write call.

Invalid input

Status 400. Error invalid_input. Check account, file type, file presence, URL presence, and is_long_video.

Billing or credits

Status 402. Errors no_subscription or insufficient_credits. Subscribe or top up credits before retrying.

Reconnect account

Status 403. Error account_needs_reauth. Reconnect the X account from the dashboard.

Media download failed

Status 422. Error media_download_failed. Use an HTTPS URL that returns a supported media file. Or switch to multipart upload.

Rate limit or temporary failure

Status 429 or 503. Retry with exponential backoff and respect Retry-After when present.

Handoff checklist

Tweet workflow

Save mediaUrl and pass it to POST /x/tweets as media.

Reply workflow

Save the parent tweet ID, mediaUrl, returned tweetId, and any writeActionId.

DM workflow

Save mediaId and pass it to POST /x/dm/{userId} as one media_ids item.

JSON Lines

Store upload, tweet, reply, or DM handoff rows in xquik-media-handoff.jsonl with media_id and media_url.

Agent workflow

JSON URL upload validates or hosts a generated URL, or returns a DM mediaId. Use it only when the agent needs one of those. For tweet-only public URLs, pass them directly to POST /x/tweets.

File workflow

Prefer multipart upload when your app owns the file bytes.