Twitter (X) Stats API — Three Ways to Get Post Metrics
When someone says 'I want Twitter stats via the API', they usually mean one of two things: the public counters on a tweet (likes, retweets, replies, quotes — what everyone sees on the timeline) or the private analytics counters (impressions, profile visits, link clicks — what only the tweet's author sees in their analytics dashboard). These are different categories with different auth requirements and different available paths.
This guide walks both. For public metrics: three API paths with runnable code and per-call cost. For non-public metrics: the OAuth user-context requirement that limits this surface to the tweet's author. Pricing references are URL-cited so you can verify; the multipliers are reduced from cited rates.
Public metrics vs non-public metrics — the split that matters
Public metrics are part of the tweet's public-facing view. Anyone can see them on the timeline; any API path that returns the tweet returns them. Fields: like_count, retweet_count, reply_count, quote_count, and (where surfaced) bookmark_count.
Non-public metrics are the analytics side: impression_count (how many timelines the tweet was shown in), url_link_clicks (how many clicked links inside), user_profile_clicks (how many clicked through to the author's profile). These are available only via the X official API with OAuth user-context auth, meaning the request runs as the account owner — you can fetch your own non-public metrics but not someone else's.
If your use case is monitoring competitors, sentiment analysis, viral-content detection, or anything tracking accounts you don't own → you're working with public metrics only. If your use case is analyzing your own account's reach → you can add non-public metrics on top via X user-context auth.
Path 1 — twitterapi.io batch tweet endpoint
twitterapi.io's /twitter/tweets endpoint accepts a batch of tweet IDs and returns the full tweet record for each, including public metrics. Authentication is X-API-Key. Pricing per twitterapi.io/pricing: $0.00015 per returned tweet (no developer onboarding, no monthly minimum).
Pick this when you need public metrics at scale — backfilling stats over an archive, polling a list of monitored accounts, computing engagement rates across many tweets.
import os, requests
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
def tweet_stats(tweet_ids: list[str]):
"""Batch fetch — returns tweets including public_metrics."""
r = requests.get(
f"{BASE}/twitter/tweets",
headers=HEADERS,
params={"tweet_ids": ",".join(tweet_ids)},
timeout=10,
)
r.raise_for_status()
return r.json().get("tweets", [])
# Example: refresh stats for 100 tweets we're tracking
ids = ["1234567890", "2345678901", "3456789012"]
rows = tweet_stats(ids)
for t in rows:
pm = t.get("public_metrics", {})
print(f" {t['id']} likes={pm.get('like_count')} rts={pm.get('retweet_count')} replies={pm.get('reply_count')}")
Path 2 — X official public metrics via xdk / tweepy / raw requests
X's /2/tweets/{id} with tweet.fields=public_metrics returns the same public counters. Auth is a bearer token from the X Developer Console. Pricing per docs.x.com/x-api/getting-started/pricing: $0.005 per post read, deduplicated within a 24-hour UTC window.
Pick this when you're already on the X bill for write operations (post create, like, retweet, follow) and the marginal stat-read cost rides on the same auth.
# pip install tweepy
import tweepy
client = tweepy.Client(bearer_token="YOUR_X_BEARER")
# Single tweet
resp = client.get_tweet(id="1234567890", tweet_fields=["public_metrics"])
pm = resp.data.public_metrics
print(pm) # {'like_count': N, 'retweet_count': N, 'reply_count': N, 'quote_count': N}
# Batch (X official allows up to 100 IDs per call)
ids = ["1234567890", "2345678901", "3456789012"]
resp = client.get_tweets(ids=ids, tweet_fields=["public_metrics"])
for t in resp.data:
print(t.id, t.public_metrics)
Path 3 — non-public metrics (impressions, clicks) via X user-context
Non-public metrics require OAuth user-context auth — the request runs AS the account owner. This means:
- You can fetch impression / click counts for tweets in YOUR own authenticated account.
- You cannot fetch them for an arbitrary public tweet by some other account.
Auth flow: OAuth 2.0 with PKCE (or OAuth 1.0a if you're maintaining legacy code) → you obtain an access token bound to a specific X account → calls made with that token are scoped to that account's data. Tweepy supports this via the OAuth2UserHandler helper.
Pricing is the same per-post-read rate ($0.005) as public metrics, plus whatever overhead your auth setup incurs (which is one-time, not per-call). The constraint is access, not cost.
# pip install tweepy
import tweepy
# OAuth user-context — obtain an access token bound to the account whose
# non-public metrics you want to read. See docs.tweepy.org for the full
# OAuth 2.0 PKCE flow; the snippet below assumes you already have one.
client = tweepy.Client(
consumer_key="YOUR_CONSUMER_KEY",
consumer_secret="YOUR_CONSUMER_SECRET",
access_token="USER_ACCESS_TOKEN", # bound to the account owner
access_token_secret="USER_ACCESS_TOKEN_SECRET",
)
resp = client.get_tweet(
id="YOUR_OWN_TWEET_ID",
tweet_fields=["non_public_metrics", "public_metrics"],
)
npm = resp.data.non_public_metrics # impression_count, url_link_clicks, user_profile_clicks
print(npm)
Side-by-side comparison — 5 dimensions
Same job (fetch stats for a list of tweets) across the three paths. Numbers are derived from cited pricing pages.
Two practical patterns: (a) for monitoring or competitive analysis (you don't own the accounts), the choice is between paths 1 and 2 — cost ratio is 33.33× per call at the cited rates; (b) for your own account analytics, you need path 3 — non-public metrics are not available via twitterapi.io because the auth model is fundamentally different (no user-context flow).
Batch patterns — fetching many tweets' stats efficiently
Batch IDs per call: both twitterapi.io and X official support multiple IDs per call. Use larger batches (up to the documented per-call limit) to reduce HTTP overhead.
Refresh cadence: tweet stats (likes, RTs) change rapidly in the first ~24h then settle. A reasonable cadence is poll every 5-15 minutes for fresh content, hourly for content 24-72h old, daily for older. Calibrate against your own retention needs.
Storage: persist the stat snapshots so you can compute velocity (delta per unit time) — that's the operationally interesting signal for content monitoring. A simple (tweet_id, captured_at, like_count, retweet_count, ...) row layout works.
Rate handling: 429s happen. Wrap with retry-on-429 + jittered backoff. Treat 5xx the same way.
Picking a path — the decision rule
Need stats for tweets you don't own at scale? → twitterapi.io path 1. Public metrics are available, no OAuth user-context, per-call cost ratio works against you on volume otherwise.
Already paying X for credits (you write posts, you read profiles)? → use X official bearer-token path 2 for marginal stat reads. Auth is unified.
Need your own account's non-public metrics (impressions, clicks)? → X official path 3 with OAuth user-context. This is the only path; twitterapi.io and X bearer-only can't see non-public.
Most analytics pipelines split: path 1 for the broad public-stats monitoring workload, path 3 for the deeper own-account analytics. They answer different questions.
# Practical example: track engagement velocity across N tweets, refresh hourly,
# compute delta-per-hour as the signal.
import os, requests, time
from datetime import datetime, timezone
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
def batch_stats(ids: list[str]):
r = requests.get(
f"{BASE}/twitter/tweets",
headers=HEADERS,
params={"tweet_ids": ",".join(ids)},
timeout=10,
)
r.raise_for_status()
return r.json().get("tweets", [])
def snapshot(ids: list[str]):
now = datetime.now(timezone.utc).isoformat()
out = []
for t in batch_stats(ids):
pm = t.get("public_metrics", {})
out.append({
"tweet_id": t["id"],
"captured_at": now,
"like_count": pm.get("like_count"),
"retweet_count": pm.get("retweet_count"),
"reply_count": pm.get("reply_count"),
"quote_count": pm.get("quote_count"),
})
return out
# Persist each snapshot row, compute delta per cadence downstream.
# Cost framing (math from cited pricing):
# 100 tweets × 24 hourly snapshots × $0.00015 = $0.36 per day at twitterapi.io rates;
# same workload at X official: 100 × 24 × $0.005 = $12 per day. Multiply by tracked count.Questions readers ask
Why are impressions not available via twitterapi.io?
Impressions are a non-public metric — they require OAuth user-context auth on X official, which scopes the request to a specific account owner. twitterapi.io operates on API-key auth without per-account OAuth, so the data simply isn't accessible through that surface. For impressions you must use X official user-context.
How fresh are public_metrics — is there a delay?
Public metrics update in near-real-time on X and propagate to read APIs quickly. Both twitterapi.io and X official return counts that reflect very recent state. For sub-minute precision validate against the live UI for your specific tweet — exact propagation latency can vary.
Is `bookmark_count` available?
Bookmark counts have been on/off in public surfaces over time. Check the field set returned by /twitter/tweets and the tweet.fields documentation at docs.x.com for the current availability before relying on it.
Can I get historical stats for an old tweet?
You can read CURRENT stats for any reachable tweet — old tweets returned by either path include their current public_metrics. There is no time-series of past stats available via the API — for that you must capture snapshots yourself and store the deltas.
How does cost scale if I need to refresh 10,000 tweets per hour?
Math from the cited pricing pages. twitterapi.io: 10,000 × $0.00015 = $1.50 per refresh, ~$36/day at hourly cadence. X official: 10,000 × $0.005 = $50 per refresh, ~$1,200/day. 24h dedup window on X reduces costs if you re-poll the same IDs same UTC day — but the per-fresh-poll cost is the relevant metric.
Do I need a verified X Developer account to use the official path?
You need a developer account to get the bearer token, yes. The application flow is at developer.x.com. twitterapi.io's signup path is shorter — sign up at twitterapi.io, get an API key, no developer review. If onboarding friction is a factor, that's a tie-breaker.
Continue
- X API — pricing (docs.x.com, 2026 verified)
- twitterapi.io — pricing
- X — Metrics (public vs non-public)
- Tweepy documentation
- Twitter (X) API — cluster hub
- Twitter account views tracking API
- Twitter analytics API developer guide
- Twitter post tracker API guide
- 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