Count Tweets — Twitter (X) API Programmatic Guide
Counting tweets — how many tweets did @user post last month, how many #ClimateChange mentions in 2024, how many tweets in a specific date range — is one of the most common analytics queries. Frustratingly, both major API paths make you iterate through the actual tweets to count them; there's no single-call scalar-count endpoint on twitterapi.io.
X official has a tweet counts endpoint but it only handles time-bucketed keyword counts. For per-user counts, per-operator-combo counts, or any grouping X doesn't natively support, you iterate. This guide walks the patterns + cost math + a runnable Python that counts efficiently.
Why no scalar count endpoint
X's own API surface treats tweet counts as either (a) full result iteration for search or (b) time-bucketed aggregation via /2/tweets/counts/recent (Basic+ tier).
The time-bucket endpoint is useful when your workflow specifically needs bucket-per-day or bucket-per-hour on a single keyword search. Everything else — per-user tweet counts, per-operator-combo counts, per-language filtered counts — requires iterating the search results yourself.
twitterapi.io mirrors X's search grammar so the same iteration pattern works. No shortcut — you pull the tweets, you count them.
Runnable — 4 counting patterns
One script covering the 4 most common count workflows:
import os, requests
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
def count_query(query: str, max_pages: int = 50) -> int:
n, cursor = 0, 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()
n += len(resp.get("tweets", []))
cursor = resp.get("next_cursor")
if not cursor: break
return n
# Pattern 1: count tweets by a user in a month
n1 = count_query("from:elonmusk since:2024-01-01 until:2024-02-01")
print(f"@elonmusk tweets in Jan 2024: {n1:,}")
# Pattern 2: count hashtag mentions in a week
n2 = count_query("#OpenAI since:2024-06-01 until:2024-06-08")
print(f"#OpenAI mentions week of 6/1/24: {n2:,}")
# Pattern 3: count filtered signal tweets (with operator combo)
n3 = count_query("bitcoin lang:en min_faves:100 -filter:retweets since:2024-01-01")
print(f"high-signal bitcoin tweets Jan-now: {n3:,}")
# Pattern 4: count replies to a specific tweet
n4 = count_query("conversation_id:1712345678901234567 -is:retweet")
print(f"replies to specific tweet: {n4:,}")
# Total cost per twitterapi.io/pricing:
# (n1 + n2 + n3 + n4) × $0.00015X official `/2/tweets/counts/recent` — the shortcut for time-bucket only
X's dedicated counts endpoint: /2/tweets/counts/recent per docs.x.com/x-api/tweets/counts/introduction. Returns tweet counts bucketed by day/hour/minute for a single keyword search in the last 7 days.
Wins when: your workflow is specifically 'time-series of daily tweet counts for keyword X over the last week'. Loses when: you need per-user counts, per-operator-combo, historical (>7 day), or any non-time-bucket grouping.
Cost: charged as a single API request against the tweets request budget (not per-count). For simple time-series workflows, this is cheaper than iterating. For any other counting workflow, iterate.
# X official time-bucketed counts (recent 7d only)
import os, tweepy
client = tweepy.Client(bearer_token=os.environ["X_BEARER_TOKEN"])
# Daily bucket for #OpenAI mentions in the last 7 days
resp = client.get_recent_tweets_count(query="#OpenAI", granularity="day")
print(f"total mentions last 7d: {resp.meta.get('total_tweet_count', 0):,}")
for bucket in resp.data or []:
print(f" {bucket['start'][:10]}: {bucket['tweet_count']:,}")
# For historical (>7d) counts, use twitterapi.io iteration patternCost efficiency — how to count cheaply
Add engagement threshold: typically cuts 90%+ of the count-target volume. If you only care about counting signal tweets (not the zero-engagement long tail), this saves 90%+ of cost.
Use time-bucket if fit: for pure hashtag+time-series in last 7d, X's /2/tweets/counts/recent = 1 request instead of iterating N pages.
Batch cross-count: if counting 20 similar queries, don't iterate 20 times — pull one broad search then post-filter in memory (if the queries share a superset).
Avoid double-counting on cursor edge cases: cursor pagination is stable but check for occasional duplicate IDs across pages if you need exact counts (deduplicate on tweet.id).
Cost math at 4 counting volumes
Practical: sub-cent for single-user or narrow-hashtag counts; single-digit-to-tens-of-dollars for anything crossing 100K+ tweets. Always add min_faves: if you're happy with signal-only counts.
Comparison — 3 counting paths
Common counting workflows
Author posting rhythm: from:@handle since:2024-01-01 until:2024-12-31 — count tweets per author per year. Useful for measuring engagement/output over time.
Hashtag campaign ROI: count mentions before + during + after a marketing push. Delta = campaign lift.
Election/event tracking: "topic" (bucket:daily equivalent via day-window iteration) since:2024-10-01 until:2024-11-15 — mention volume around the event.
Cashtag heat map: 30 cashtags × 30 days matrix → identify which crypto tickers spiked when.
Reply-thread analysis: conversation_id: counts all replies in a thread; useful for viral-tweet forensics.
Bot-detection proxy: count tweets per unique author for a hashtag; high skew (many tweets from few authors) = coordinated inauthentic activity signal.
# Multi-workflow: count tweets across 20 crypto cashtags for the past 30 days.
import os, requests, csv
from pathlib import Path
from datetime import date, timedelta
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
def count_day(cashtag: str, d: date, min_faves: int = 5) -> int:
query = f"{cashtag} since:{d} until:{d + timedelta(days=1)} min_faves:{min_faves} -filter:retweets"
n, cursor = 0, None
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()
n += len(resp.get("tweets", []))
cursor = resp.get("next_cursor")
if not cursor: break
return n
CASHTAGS = ["$BTC", "$ETH", "$SOL", "$XRP", "$ADA", "$DOGE", "$SHIB", "$MATIC",
"$LINK", "$AVAX", "$DOT", "$UNI", "$LTC", "$XLM", "$ATOM",
"$ETC", "$FIL", "$NEAR", "$APT", "$ARB"]
END = date.today()
START = END - timedelta(days=30)
rows = []
d = START
while d < END:
row = {"date": d.isoformat()}
total_day = 0
for c in CASHTAGS:
n = count_day(c, d, min_faves=5)
row[c] = n
total_day += n
rows.append(row)
print(f" {d}: {total_day:,} total signal mentions across 20 cashtags")
d += timedelta(days=1)
with open("cashtag_counts_30d.csv", "w") as f:
w = csv.DictWriter(f, fieldnames=["date"] + CASHTAGS)
w.writeheader()
w.writerows(rows)
# Cost per twitterapi.io/pricing:
# sum of all counts × $0.00015 — typically $5-30 for 20-cashtag × 30-day at min_faves:5Questions readers ask
Why can't I just get a total count without iterating?
Neither twitterapi.io nor X official (for non-time-bucket queries) exposes a scalar total endpoint. You iterate cursor pagination and increment. X's /2/tweets/counts/recent is the exception for time-bucketed hashtag counts in the last 7 days.
How do I avoid counting the same tweet twice at cursor boundaries?
Track a set of seen tweet IDs. Add each tweet's ID to the set before incrementing counter; skip if already present. Cursor pagination is usually stable but occasional duplicates at cursor edges are possible.
Can I count deleted tweets?
No — deleted tweets don't appear in current search results. For historical count reconstruction (including tweets since deleted), you'd need to have snapshotted the search results at the time — retroactive is impossible. See /blog/deleted-tweet-search for detection patterns.
Does the `total_tweet_count` field in X's counts response include hidden tweets?
Excludes tweets from protected accounts, deleted tweets, and (via X's typical query behavior) tweets from suspended accounts. It's a 'currently-visible-to-public' count.
How to count DMs or private tweets?
Not available via public search. DMs require Direct Message API with user OAuth consent. Private tweets are inaccessible without the tweet author's authorization.
Best way to count with hourly granularity?
Use since: / until: with hourly windows (e.g. since:2024-06-01T14:00:00Z until:2024-06-01T15:00:00Z) if the API accepts full ISO timestamps; otherwise pull the day + post-filter by created_at field on returned tweets.
Rate limits when running large counting batches?
twitterapi.io per-key throughput comfortably handles thousands of requests/hour. For very large counts (millions of tweets across many queries), pace across hours + use ThreadPoolExecutor(max_workers=10-20) for parallel query counts. Don't burst all-at-once.
Continue
- Twitter (X) API — cluster hub
- Twitter (X) search operators API reference
- Twitter (X) search filters — programmatic API
- Twitter (X) hashtag popularity tracker API
- Twitter (X) API pricing breakdown
- twitterapi.io pricing
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API key