How to Search Twitter (X) Videos via API
filter:videosORhas:media— filter for video-attached tweets- Combine with
min_faves:N/since:/from:@handlefor tight scope - Cost: $0.00015/tweet (twitterapi.io) vs $0.005 (X official) — about 33× cheaper per read
- Video-attached tweets are ~1-5% of raw keyword volume = naturally cheap
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.
Video-attached tweets are typically 1-5% of raw keyword volume — filter:videos naturally cuts 95%+ cost vs unfiltered pull per twitterapi.io/pricing
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: <keyword> 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.
Runnable — video search with filters
One end-to-end example showing the operator combo pattern:
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 extendedEntities.media (highest-bitrate MP4 variant)
for t in vids[:5]:
media = (t.get("extendedEntities") or {}).get("media", [])
for m in media:
if m.get("type") in ("video", "animated_gif"):
mp4s = [v for v in (m.get("video_info") or {}).get("variants", []) if v.get("content_type") == "video/mp4"]
best = max(mp4s, key=lambda v: v.get("bitrate", 0), default={})
print(f" @{t.get('author', {}).get('userName')}: {best.get('url', 'no-url')}")
# Cost per twitterapi.io/pricing: len(vids) × $0.000154 real-world video-search combos
Each pulls a signal-only stream at 1-5% of raw-keyword volume + high engagement density = cost-efficient by design.
What the response includes for video tweets
For each video tweet, the API returns:
Media object array (at extendedEntities.media): each media item has type (video / animated_gif / photo), media_url_https (image URL), and for videos and GIFs video_info.variants (multiple bitrate versions, each with content_type and url).
Author metadata: userName, followers, isBlueVerified — for filtering by author quality.
Engagement: likeCount, retweetCount, replyCount, quoteCount — standard tweet-level engagement fields.
Text + createdAt: the tweet text (often a caption/context for the video) + timestamp.
conversationId: link to the reply thread for context.
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.
# 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}")twitterapi.io vs X official — same grammar, different price
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.
# 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("extendedEntities") or {}).get("media", [])
video_urls = []
for m in media:
if m.get("type") not in ("video", "animated_gif"): continue
mp4s = [v for v in (m.get("video_info") or {}).get("variants", []) if v.get("content_type") == "video/mp4"]
if mp4s: video_urls.append(max(mp4s, key=lambda v: v.get("bitrate", 0))["url"])
if not video_urls: continue
rows.append({
"tweet_id": t.get("id"),
"author": t.get("author", {}).get("userName"),
"created_at": t.get("createdAt"),
"favorite_count": t.get("likeCount", 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 pipelineResearching this?Free starter credits cover end-to-end testing — Google sign-in, no card. Get a key.
Questions readers ask
How do I get the direct video download URL?
Each returned video tweet has a media array at extendedEntities.media; video items have video_info.variants (multi-bitrate, each with a url). Use the highest-bitrate video/mp4 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 via a search operator. Post-filter after pulling: each video item carries its length in milliseconds at extendedEntities.media[i].video_info.duration_millis, so keep the ones above 30000 for videos of 30 seconds or more. The field is usually missing on animated GIFs, so treat it as optional.
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 variant URLs on media objects point 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?
A paid twitterapi.io account gets 20 requests per second (0.2 before the first payment; see /qps-limits). For very large corpus pulls (10K+ videos), run parallel windowed queries with ThreadPoolExecutor(max_workers=10) and stay under that limit.
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.
Continue
- Twitter (X) API — cluster hub
- Twitter (X) search filters — programmatic API
- Twitter (X) search operators — complete reference
- Twitter (X) images API extraction guide (sibling media pull)
- Twitter (X) API pricing
- twitterapi.io pricing
- Twitter (X) video not playing: diagnosis and the API route to the MP4
- 01Pipedream Pricing in 2026: Connect Plans, Credits and the Workflows Shutdown
- 02Cloudflare Pay per Crawl: How the HTTP 402 Handshake Works, and What It Means for Twitter (X) Data
- 03ScrapingBee Pricing in 2026: Plans, Credit Costs and Alternatives
- 04Firecrawl Pricing: Plans, Credit Costs and What an X (Twitter) URL Costs
- 05LunarCrush API: What It Returns, What It Costs, and When Raw Twitter (X) Data Is the Better Input
- 06How to Search Twitter (X) by Location in 2026: Near Me, Operators, and the API
- 07Searching Twitter by Date
- 08Twitter Search Engine Optimization: How X Search and Google Rank Your Posts
Writing about something you built with TwitterAPI.io? Our community rewards program pays API credits for honest tutorials, build logs and videos.
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API keyAlready building with TwitterAPI.io? Publish an honest tutorial or build log about it and earn up to $200 a month in API credits.