twitterapi.io is an independent third-party service. Not affiliated with X Corp.

Blogsearch twitter videos

How to Search Twitter (X) Videos via API

By Alex Chen4 min read

Searching Twitter (X) for videos programmatically is one of the highest-signal workflows in social monitoring — video content typically has higher engagement, longer dwell, and stronger emotional payload than text-only tweets. Content moderators, OSINT analysts, brand-monitor teams, and journalists all need this endpoint pattern.

This guide walks the exact operator combos + endpoint call + cost math with runnable Python. Contrast with the twitter.com UI-based search which caps at manual page-by-page browsing — the API path unlocks bulk pulls, historical windows, and pipeline-ready JSON output.

01 — Section

The two operators — `filter:videos` vs `has:media`

filter:videos — narrower, matches only tweets with attached video content (uploaded native video, GIF-as-video). Best for pure video workflows.

has:media — broader, matches tweets with images OR videos OR GIFs. Use when you want ANY media attachment.

filter:native_video — narrowest, only X-native uploaded video (excludes linked YouTube/Vimeo URLs that render as video cards).

Combining: filter:videos lang:en min_faves:50 -filter:retweets — high-signal English original video tweets on the topic.

Practical rule: start with filter:videos for video-only workflows; drop to has:media if you want combined image+video signal.

02 — Section

Runnable — video search with filters

One end-to-end example showing the operator combo pattern:

python
import os, requests

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"

def search_videos(topic: str, min_faves: int = 20, max_pages: int = 10) -> list:
    query = f"{topic} filter:videos lang:en min_faves:{min_faves} -filter:retweets"
    tweets, cursor = [], None
    for _ in range(max_pages):
        params = {"query": query}
        if cursor: params["cursor"] = cursor
        r = requests.get(f"{BASE}/twitter/tweet/advanced_search", headers=HEADERS, params=params, timeout=15)
        r.raise_for_status()
        resp = r.json()
        tweets.extend(resp.get("tweets", []))
        cursor = resp.get("next_cursor")
        if not cursor: break
    return tweets

# High-signal AI news videos with 50+ engagement
vids = search_videos("OpenAI OR Anthropic OR GPT", min_faves=50)
print(f"{len(vids):,} video tweets found")

# Access video URLs from the attachments field
for t in vids[:5]:
    media = t.get("media", []) or t.get("attachments", [])
    for m in media:
        if m.get("type") in ("video", "animated_gif"):
            print(f"  @{t.get('author', {}).get('userName')}: {m.get('video_url', m.get('url', 'no-url'))}")

# Cost per twitterapi.io/pricing: len(vids) × $0.00015
03 — Section

4 real-world video-search combos

Use caseQuery
Crypto news videos (high-engagement)bitcoin filter:videos lang:en min_faves:100 -filter:retweets
Live event coverage (recent + video)"climate summit" filter:videos since:2024-11-01 -filter:retweets
Competitor product videos (brand watch)stripe filter:videos -paypal -square lang:en min_faves:20
Celebrity announcements (native only)from:@target_handle filter:native_video since:2024-01-01

Each pulls a signal-only stream at 1-5% of raw-keyword volume + high engagement density = cost-efficient by design.

04 — Section

What the response includes for video tweets

For each video tweet, the API returns:

Media object array: each media item has type (video / animated_gif / photo), video_url (streamable URL), preview_image_url (thumbnail), duration_ms (video length), variants (multiple bitrate versions).

Author metadata: userName, followers_count, verified — for filtering by author quality.

Engagement: favorite_count, retweet_count, reply_count, quote_count — standard tweet-level engagement fields.

Text + created_at: the tweet text (often a caption/context for the video) + timestamp.

conversation_id: link to the reply thread for context.

05 — Section

Bulk pattern — download video URLs for a large window

For workflows that need many video URLs across a date range (research corpus building, content moderation batch processing, competitive intel archives), use windowed pagination + concurrent workers within safe rate limits.

python
# 30-day bulk video pull for a topic — window-sliced pattern.
import os, requests, json
from datetime import date, timedelta
from pathlib import Path

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
OUT = Path("video_corpus.jsonl")

def pull_day(topic: str, d: date) -> int:
    query = f"{topic} filter:videos lang:en min_faves:10 -filter:retweets since:{d} until:{d + timedelta(days=1)}"
    n, cursor = 0, None
    with open(OUT, "a") as f:
        for _ in range(50):
            params = {"query": query}
            if cursor: params["cursor"] = cursor
            r = requests.get(f"{BASE}/twitter/tweet/advanced_search", headers=HEADERS, params=params, timeout=15)
            r.raise_for_status()
            resp = r.json()
            for t in resp.get("tweets", []):
                f.write(json.dumps(t) + "\n"); n += 1
            cursor = resp.get("next_cursor")
            if not cursor: break
    return n

TOPIC = "climate change"
END = date.today()
START = END - timedelta(days=30)

d = START
total = 0
while d < END:
    n = pull_day(TOPIC, d)
    print(f"  {d}: {n:,} video tweets")
    total += n
    d += timedelta(days=1)

print(f"\n30d total: {total:,} video tweets")
print(f"Cost per twitterapi.io/pricing: {total} × $0.00015 = ${total * 0.00015:.2f}")
06 — Section

twitterapi.io vs X official — same grammar, different price

Dimensiontwitterapi.ioX official /2/tweets/search/recent
Endpoint/twitter/tweet/advanced_search/2/tweets/search/recent (Basic)
Video operatorfilter:videos / has:mediasame grammar
Per-tweet cost$0.00015 (twitterapi.io/pricing)$0.005 (docs.x.com)
Historicalback to 2006 with since:7 days (Basic tier)
Video URL in response✓ direct video_url + variants✓ (requires media.fields=video_info expansion)
Best forany video-search workflowX-native OAuth-integrated apps
07 — Section

Common video-search workflows

Content moderation batch: pull videos matching harmful-content signal keywords, score each via computer-vision pipeline, queue for human review.

Brand crisis monitoring: watch for video tweets mentioning your brand + negative-sentiment keyword combos — video crises spread 2-3× faster than text.

OSINT event coverage: pull all videos from a specific date+location window for incident reconstruction (journalism, researcher, investigator use cases).

Competitor product demos: track videos where competitors demo their product — feature launches, price announcements, integration reveals.

Creator sourcing / UGC pull: pull high-engagement videos in your niche → build a curated list of creators to reach out to for partnerships.

python
# Video sentiment corpus pull for downstream ML pipeline.
import os, requests, csv, json
from pathlib import Path

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"

BRAND = "anthropic"
query = f"{BRAND} filter:videos lang:en min_faves:20 -filter:retweets"

tweets, cursor = [], None
for _ in range(20):
    params = {"query": query}
    if cursor: params["cursor"] = cursor
    r = requests.get(f"{BASE}/twitter/tweet/advanced_search", headers=HEADERS, params=params, timeout=15)
    r.raise_for_status()
    resp = r.json()
    tweets.extend(resp.get("tweets", []))
    cursor = resp.get("next_cursor")
    if not cursor: break

rows = []
for t in tweets:
    media = t.get("media", []) or t.get("attachments", [])
    video_urls = [m.get("video_url") for m in media if m.get("type") in ("video", "animated_gif") and m.get("video_url")]
    if not video_urls: continue
    rows.append({
        "tweet_id": t.get("id"),
        "author": t.get("author", {}).get("userName"),
        "created_at": t.get("created_at"),
        "favorite_count": t.get("favorite_count", 0),
        "text": (t.get("text") or "").replace("\n", " ")[:200],
        "video_url": video_urls[0],
    })

out = Path(f"{BRAND}_videos.csv")
with open(out, "w") as f:
    w = csv.DictWriter(f, fieldnames=["tweet_id", "author", "created_at", "favorite_count", "text", "video_url"])
    w.writeheader()
    w.writerows(rows)

print(f"saved {len(rows)} video tweets to {out}")
# Cost per twitterapi.io/pricing: len(tweets) × $0.00015
# Downstream: pipe video_url through your CV/audio-transcription pipeline
08 — Questions

Questions readers ask

How do I get the direct video download URL?

Each returned tweet has a media array; video items have video_url (streamable MP4) and variants (multi-bitrate). Use the highest bitrate variant for archive-quality downloads.

What's the difference between `filter:videos` and `filter:native_video`?

filter:videos includes any video-tagged tweet (native uploads + GIF-as-video). filter:native_video includes only X-native uploaded video (excludes GIFs + linked-out YouTube cards). For pure X video pulls, use filter:native_video.

Can I search for videos of a specific length?

Not directly via operator. Post-filter using the duration_ms field in the media object after pulling — e.g. duration_ms > 30000 for 30+ second videos.

How much video content is on X vs image content?

Typically ~10-20% of engaged media tweets are video (rest images). Native uploaded video is ~5-10% of all engaged media. Ratios shift over time — worth measuring for your specific keyword rather than assuming.

Do video URLs expire?

The video_url on media objects points to X's CDN. URLs are stable long-term for public tweets but not guaranteed indefinitely. For archival, download the video within your pull workflow rather than storing just the URL.

Any rate-limit concern for large video pulls?

Standard per-key throughput on twitterapi.io comfortably handles thousands of requests/hour. For very large corpus pulls (10K+ videos), pace across hours with ThreadPoolExecutor(max_workers=10-20) for parallel windowed queries.

Can I filter by video content (not just presence)?

Not via the search API directly. Approach: pull videos matching your keyword, then run each video_url through a CV / speech-to-text pipeline (OpenAI Whisper for audio, CLIP for visual similarity) to filter by content.

09 — Further reading

Continue

Sources & further reading
More from this series
Build it

Stop reading. Start building.

Starter credits cover real testing on real data. Google sign-in, no card, no application queue.

Get an API key
    Search Twitter (X) Videos via API — Guide | TwitterAPI.io