Skip to main content

Overview

Twikit makes it easy to download media content from tweets. You can download photos, videos, and animated GIFs with just a few lines of code.
Make sure you have the necessary permissions and comply with Twitter’s terms of service when downloading media content.

Basic media download

Here’s the simplest way to download all media from a tweet:
import asyncio
from twikit import Client

client = Client('en-US')

async def main():
    await client.login(
        auth_info_1='USERNAME',
        auth_info_2='EMAIL',
        password='PASSWORD'
    )
    
    # Get a tweet by ID
    tweet = await client.get_tweet_by_id('1234567890')
    
    # Download all media
    for i, media in enumerate(tweet.media):
        if media.type == 'photo':
            await media.download(f'media_{i}.jpg')

asyncio.run(main())

Download different media types

Tweets can contain three types of media: photos, videos, and animated GIFs. Here’s how to handle each type:
import asyncio
from twikit import Client

client = Client('en-US')

async def main():
    tweet = await client.get_tweet_by_id('...')
    
    for i, media in enumerate(tweet.media):
        if media.type == 'photo':
            await media.download(f'photo_{i}.jpg')

asyncio.run(main())
For videos and GIFs, media.streams[-1] gives you the highest quality stream. Use media.streams[0] for the lowest quality if you want smaller file sizes.

Complete working example

Here’s the actual code from Twikit’s examples that handles all media types:
import asyncio
from twikit import Client

AUTH_INFO_1 = '...'
AUTH_INFO_2 = '...'
PASSWORD = '...'

client = Client('en-US')


async def main():
    # Login
    await client.login(
        auth_info_1=AUTH_INFO_1,
        auth_info_2=AUTH_INFO_2,
        password=PASSWORD
    )
    
    # Get tweet
    tweet = await client.get_tweet_by_id('...')
    
    # Download all media from the tweet
    for i, media in enumerate(tweet.media):
        if media.type == 'photo':
            await media.download(f'media_{i}.jpg')
        if media.type == 'animated_gif':
            await media.streams[-1].download(f'media_{i}.mp4')
        if media.type == 'video':
            await media.streams[-1].download(f'media_{i}.mp4')

asyncio.run(main())

Understanding media streams

For videos and GIFs, Twitter provides multiple quality streams. Here’s how to work with them:
import asyncio
from twikit import Client

client = Client('en-US')

async def main():
    await client.login(
        auth_info_1='USERNAME',
        auth_info_2='EMAIL',
        password='PASSWORD'
    )
    
    tweet = await client.get_tweet_by_id('1234567890')
    
    for media in tweet.media:
        if media.type == 'video':
            print(f'Available streams: {len(media.streams)}')
            
            # Download lowest quality (smallest file)
            await media.streams[0].download('low_quality.mp4')
            
            # Download highest quality (largest file)
            await media.streams[-1].download('high_quality.mp4')
            
            # Download specific quality (middle option)
            if len(media.streams) > 2:
                mid_index = len(media.streams) // 2
                await media.streams[mid_index].download('medium_quality.mp4')

asyncio.run(main())

Organizing downloads

For better file organization, create directories and use descriptive filenames:
import asyncio
import os
from twikit import Client

client = Client('en-US')

async def main():
    await client.login(
        auth_info_1='USERNAME',
        auth_info_2='EMAIL',
        password='PASSWORD'
    )
    
    tweet = await client.get_tweet_by_id('1234567890')
    
    # Create directory for this tweet's media
    media_dir = f'tweet_{tweet.id}_media'
    os.makedirs(media_dir, exist_ok=True)
    
    # Download with organized structure
    for i, media in enumerate(tweet.media):
        if media.type == 'photo':
            filepath = os.path.join(media_dir, f'photo_{i}.jpg')
            await media.download(filepath)
            print(f'Downloaded photo to {filepath}')
            
        elif media.type == 'animated_gif':
            filepath = os.path.join(media_dir, f'gif_{i}.mp4')
            await media.streams[-1].download(filepath)
            print(f'Downloaded GIF to {filepath}')
            
        elif media.type == 'video':
            filepath = os.path.join(media_dir, f'video_{i}.mp4')
            await media.streams[-1].download(filepath)
            print(f'Downloaded video to {filepath}')

asyncio.run(main())

Batch download from multiple tweets

Download media from multiple tweets efficiently:
import asyncio
from twikit import Client

client = Client('en-US')

async def download_tweet_media(tweet_id: str, output_dir: str):
    """Download all media from a specific tweet."""
    tweet = await client.get_tweet_by_id(tweet_id)
    
    if not tweet.media:
        print(f'No media found in tweet {tweet_id}')
        return
    
    for i, media in enumerate(tweet.media):
        filename = f'{output_dir}/tweet_{tweet_id}_media_{i}'
        
        if media.type == 'photo':
            await media.download(f'{filename}.jpg')
        elif media.type in ['video', 'animated_gif']:
            await media.streams[-1].download(f'{filename}.mp4')
        
        print(f'Downloaded media {i+1}/{len(tweet.media)} from tweet {tweet_id}')

async def main():
    await client.login(
        auth_info_1='USERNAME',
        auth_info_2='EMAIL',
        password='PASSWORD'
    )
    
    tweet_ids = ['1234567890', '0987654321', '1111111111']
    
    for tweet_id in tweet_ids:
        await download_tweet_media(tweet_id, './downloads')
        await asyncio.sleep(1)  # Rate limiting

asyncio.run(main())
When downloading media from multiple tweets, add delays between requests to respect rate limits and avoid being temporarily restricted.

Checking media availability

Not all tweets contain media. Always check before attempting downloads:
import asyncio
from twikit import Client

client = Client('en-US')

async def main():
    await client.login(
        auth_info_1='USERNAME',
        auth_info_2='EMAIL',
        password='PASSWORD'
    )
    
    tweet = await client.get_tweet_by_id('1234567890')
    
    # Check if tweet has media
    if not tweet.media:
        print('This tweet has no media')
        return
    
    print(f'Found {len(tweet.media)} media items')
    
    # Check media types
    for i, media in enumerate(tweet.media):
        print(f'Media {i}: {media.type}')
        
        if media.type == 'photo':
            await media.download(f'media_{i}.jpg')
        elif media.type in ['video', 'animated_gif']:
            await media.streams[-1].download(f'media_{i}.mp4')

asyncio.run(main())

Key points

1

Check media type

Always check media.type to determine if it’s a photo, video, or animated GIF
2

Use streams for videos

Videos and GIFs require accessing media.streams[-1] for the highest quality
3

Handle missing media

Check if tweet.media exists before attempting downloads
4

Organize files

Use descriptive filenames and directories to keep downloads organized

Build docs developers (and LLMs) love