This guide covers everything about working with media in Twikit, including uploading images and videos, downloading media from tweets, and handling different media types.
Twikit supports three types of media:
- Photo: Static images (JPEG, PNG, etc.)
- Video: Video files (MP4, MOV, etc.)
- AnimatedGif: Animated GIF files
Each media type has specific properties and methods for handling.
Upload an image
# Upload from file path
media_id = await client.upload_media('photo.jpg')
print(f'Uploaded media ID: {media_id}')
# Use in a tweet
await client.create_tweet(
text='Check out this photo!',
media_ids=[media_id]
)
Upload from bytes
# Read file as bytes
with open('photo.jpg', 'rb') as f:
image_bytes = f.read()
# Upload bytes directly
media_id = await client.upload_media(
source=image_bytes,
media_type='image/jpeg'
)
Upload video
For videos, set wait_for_completion=True to ensure processing finishes:
media_id = await client.upload_media(
'video.mp4',
wait_for_completion=True,
status_check_interval=1.0 # Check status every second
)
await client.create_tweet(
text='New video!',
media_ids=[media_id]
)
Twitter processes videos after upload. Using wait_for_completion=True makes the method wait until processing is complete before returning.
Upload GIF
GIFs require a media category to be specified:
media_id = await client.upload_media(
'animation.gif',
wait_for_completion=True,
media_category='tweet_gif' # or 'dm_gif' for direct messages
)
await client.create_tweet(
text='Funny GIF!',
media_ids=[media_id]
)
Upload long videos (Premium)
Twitter Premium allows videos longer than 2 minutes and 20 seconds:
media_id = await client.upload_media(
'long_video.mp4',
wait_for_completion=True,
is_long_video=True # Premium feature
)
The is_long_video parameter requires Twitter Premium/Blue subscription.
Check upload status
Manually check the processing status of uploaded media:
media_id = await client.upload_media('video.mp4')
# Check status
status = await client.check_media_status(media_id)
print(f"Status: {status['processing_info']['state']}")
if 'check_after_secs' in status['processing_info']:
print(f"Check again in {status['processing_info']['check_after_secs']} seconds")
Add alt text and content warnings to uploaded media:
media_id = await client.upload_media('photo.jpg')
# Add alt text for accessibility
await client.create_media_metadata(
media_id=media_id,
alt_text='A beautiful sunset over the ocean with orange and pink clouds',
sensitive_warning=['other'] # Options: 'adult_content', 'graphic_violence', 'other'
)
# Now use in tweet
await client.create_tweet(
text='Gorgeous sunset tonight',
media_ids=[media_id]
)
Adding alt text makes your content accessible to visually impaired users and is considered a best practice.
Download photos, videos, and GIFs from tweets.
tweet = await client.get_tweet_by_id('1234567890')
if tweet.media:
print(f'Tweet has {len(tweet.media)} media item(s)')
for media in tweet.media:
print(f'Type: {media.type}')
print(f'URL: {media.media_url}')
Download photos
tweet = await client.get_tweet_by_id('1234567890')
for i, media in enumerate(tweet.media):
if media.type == 'photo':
# Download using media object method
await media.download(f'photo_{i}.jpg')
print(f'Downloaded photo_{i}.jpg')
Download videos
Videos have multiple quality streams. Choose the one you want:
tweet = await client.get_tweet_by_id('1234567890')
for i, media in enumerate(tweet.media):
if media.type == 'video':
# Get available streams
streams = media.streams
print(f'Available streams: {len(streams)}')
for stream in streams:
print(f' Bitrate: {stream.bitrate}, Type: {stream.content_type}')
# Download highest quality (last stream usually has highest bitrate)
best_stream = streams[-1]
await best_stream.download(f'video_{i}.mp4')
print(f'Downloaded video_{i}.mp4')
Download animated GIFs
tweet = await client.get_tweet_by_id('1234567890')
for i, media in enumerate(tweet.media):
if media.type == 'animated_gif':
# GIFs are stored as MP4 videos
stream = media.streams[-1]
await stream.download(f'gif_{i}.mp4')
print(f'Downloaded gif_{i}.mp4')
Complete download example
Download all media from a tweet:
tweet = await client.get_tweet_by_id('1234567890')
for i, media in enumerate(tweet.media):
if media.type == 'photo':
await media.download(f'media_{i}.jpg')
print(f'Downloaded photo: media_{i}.jpg')
elif media.type == 'video':
# Get highest quality stream
best_stream = media.streams[-1]
await best_stream.download(f'media_{i}.mp4')
print(f'Downloaded video: media_{i}.mp4')
elif media.type == 'animated_gif':
stream = media.streams[-1]
await stream.download(f'media_{i}_gif.mp4')
print(f'Downloaded GIF: media_{i}_gif.mp4')
See the examples/download_tweet_media.py file in the Twikit repository for a complete implementation.
Photo attributes
photo = tweet.media[0] # Assuming first media is a photo
print(photo.id) # Media ID
print(photo.media_url) # Direct URL to image
print(photo.display_url) # Display URL
print(photo.expanded_url) # Expanded URL
print(photo.width) # Image width
print(photo.height) # Image height
print(photo.sizes) # Available sizes (thumb, small, medium, large)
Video attributes
video = tweet.media[0] # Assuming first media is a video
print(video.duration_millis) # Duration in milliseconds
print(video.aspect_ratio) # Aspect ratio tuple (width, height)
print(video.streams) # List of Stream objects
# Stream details
for stream in video.streams:
print(f'URL: {stream.url}')
print(f'Bitrate: {stream.bitrate}')
print(f'Content type: {stream.content_type}')
Get video subtitles
Some videos have closed captions/subtitles:
video = tweet.media[0]
subtitles = await video.get_subtitles()
if subtitles:
for caption in subtitles:
print(f'{caption.start} --> {caption.end}')
print(caption.text)
print()
else:
print('No subtitles available')
Practical examples
Download media from multiple tweets:
import os
async def download_media_from_user(screen_name, count=50):
user = await client.get_user_by_screen_name(screen_name)
media_tweets = await user.get_tweets('Media', count=count)
# Create directory
os.makedirs(f'media_{screen_name}', exist_ok=True)
media_count = 0
for tweet in media_tweets:
for i, media in enumerate(tweet.media):
filename = f'media_{screen_name}/{tweet.id}_{i}'
try:
if media.type == 'photo':
await media.download(f'{filename}.jpg')
media_count += 1
elif media.type in ['video', 'animated_gif']:
stream = media.streams[-1]
await stream.download(f'{filename}.mp4')
media_count += 1
print(f'Downloaded: {filename}')
except Exception as e:
print(f'Error downloading {filename}: {e}')
print(f'\nTotal media downloaded: {media_count}')
await download_media_from_user('elonmusk', count=20)
Automatically retweet tweets with images:
import asyncio
async def retweet_images_with_keyword(keyword):
seen_tweets = set()
while True:
try:
# Search for tweets with media
tweets = await client.search_tweet(
query=f'{keyword} filter:images',
product='Latest',
count=10
)
for tweet in tweets:
if tweet.id not in seen_tweets:
# Check if it has images
if tweet.media and any(m.type == 'photo' for m in tweet.media):
await tweet.retweet()
print(f'Retweeted: {tweet.text[:50]}...')
seen_tweets.add(tweet.id)
await asyncio.sleep(300) # Check every 5 minutes
except Exception as e:
print(f'Error: {e}')
await asyncio.sleep(300)
await retweet_images_with_keyword('python programming')
Video quality analyzer
Analyze video quality options:
tweet = await client.get_tweet_by_id('1234567890')
for media in tweet.media:
if media.type == 'video':
print(f'Video duration: {media.duration_millis / 1000} seconds')
print(f'Aspect ratio: {media.aspect_ratio[0]}:{media.aspect_ratio[1]}')
print('\nAvailable quality options:')
for i, stream in enumerate(media.streams, 1):
bitrate_mbps = stream.bitrate / 1_000_000 if stream.bitrate else 'N/A'
print(f'{i}. Bitrate: {bitrate_mbps:.2f} Mbps')
print(f' Content type: {stream.content_type}')
print(f' URL: {stream.url[:50]}...')
Create an HTML gallery of tweet media:
import os
async def create_media_gallery(screen_name, count=20):
user = await client.get_user_by_screen_name(screen_name)
media_tweets = await user.get_tweets('Media', count=count)
# Download media
os.makedirs('gallery', exist_ok=True)
html_content = f'<html><head><title>Gallery: @{screen_name}</title></head><body>'
html_content += f'<h1>Media Gallery: @{screen_name}</h1>'
for tweet in media_tweets:
for i, media in enumerate(tweet.media):
if media.type == 'photo':
filename = f'gallery/{tweet.id}_{i}.jpg'
await media.download(filename)
html_content += f'<img src="{tweet.id}_{i}.jpg" width="300" />'
html_content += f'<p>{tweet.text}</p>'
html_content += '</body></html>'
with open('gallery/index.html', 'w') as f:
f.write(html_content)
print('Gallery created at gallery/index.html')
await create_media_gallery('nasa', count=10)
Upload and tweet multiple images
import os
async def tweet_image_album(directory, caption):
media_ids = []
# Get all image files
image_files = [f for f in os.listdir(directory)
if f.endswith(('.jpg', '.jpeg', '.png'))]
# Twitter allows up to 4 images per tweet
for image_file in image_files[:4]:
filepath = os.path.join(directory, image_file)
media_id = await client.upload_media(filepath)
media_ids.append(media_id)
print(f'Uploaded: {image_file}')
# Create tweet with all images
tweet = await client.create_tweet(
text=caption,
media_ids=media_ids
)
print(f'Tweet created: {tweet.id}')
return tweet
await tweet_image_album('vacation_photos', 'Amazing trip to Hawaii! 🌺🏝️')
Complete reference for upload_media() parameters:
media_id = await client.upload_media(
source='path/to/file.jpg', # File path or bytes
wait_for_completion=False, # Wait for processing (required for videos/GIFs)
status_check_interval=None, # Seconds between status checks
media_type=None, # MIME type (auto-detected if None)
media_category=None, # 'tweet_gif' or 'dm_gif' for GIFs
is_long_video=False # True for videos > 2:20 (Premium only)
)
Best practices
Optimize image sizes
Compress images before uploading to reduce upload time and bandwidth usage.
Always use wait_for_completion for videos
Videos and GIFs require server-side processing. Wait for completion to ensure they’re ready.
Add alt text
Make your media accessible by adding descriptive alt text to all images.
Handle download errors
Media URLs may expire or become unavailable. Always wrap downloads in try-except blocks.
Respect video limits
Free accounts are limited to videos under 2:20. Premium accounts can upload longer videos.
Images
- JPEG/JPG
- PNG
- GIF (static)
- WEBP
Videos
- MP4 (recommended)
- MOV
- Maximum file size varies by account type
- Free: 512MB
- Premium: 2GB-8GB (varies by subscription)
Animated GIFs
- GIF format
- Converted to MP4 by Twitter
- Maximum file size: 15MB