Skip to main content
RapidDev - Software Development Agency
API AutomationsX (Twitter)OAuth 2.0

How to Automate X (Twitter) Analytics Reports using the API

Automate X (Twitter) analytics reports using GET /2/users/{id}/tweets with tweet.fields=public_metrics on the Twitter API v2. Requires OAuth 2.0 with offline.access scope for token refresh. Cost warning: reading your own tweets costs $0.001 per read on pay-per-use; Basic tier ($200/month) includes 15,000 reads/month. Note: follower/following counts were removed from self-serve tiers in April 2026.

Need help automating? Talk to an expert
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate7 min read30-60 minutesX (Twitter)May 2026RapidDev Engineering Team
TL;DR

Automate X (Twitter) analytics reports using GET /2/users/{id}/tweets with tweet.fields=public_metrics on the Twitter API v2. Requires OAuth 2.0 with offline.access scope for token refresh. Cost warning: reading your own tweets costs $0.001 per read on pay-per-use; Basic tier ($200/month) includes 15,000 reads/month. Note: follower/following counts were removed from self-serve tiers in April 2026.

API Quick Reference

Auth

OAuth 2.0

Rate limit

15,000 reads/month (Basic) or $0.001/read pay-per-use

Format

JSON

SDK

Available

Understanding the X (Twitter) API v2

X API v2 is a REST JSON API available at https://api.twitter.com/2/. Since February 2026, X has moved to pay-per-use pricing in addition to monthly tiers: reading your own tweets costs $0.001 per read, reading others' tweets costs $0.005 per read, and posting costs $0.015–$0.20 depending on whether the tweet contains a link. The Basic tier ($200/month, doubled from $100 in January 2025) includes 15,000 tweet reads per month.

For analytics automation, the primary endpoint is GET /2/users/{id}/tweets which returns a user's recent tweets with optional metrics fields. Public metrics (impressions, likes, retweets, replies, quotes) are available with any authentication. Non-public and organic metrics require user-context OAuth 2.0 with the tweet author's access token. The API returns up to 100 tweets per request with cursor-based pagination.

Important breaking changes: follower counts and following counts were removed from all self-serve tiers in April 2026. Engagement rate calculations based on audience size are no longer possible without the Pro tier ($5,000/month). Official docs: https://developer.twitter.com/en/docs/twitter-api

Base URLhttps://api.twitter.com/2

Setting Up X (Twitter) API Authentication

X API v2 uses OAuth 2.0 with PKCE for user-context access. You redirect the user to Twitter's authorization page, they approve your app, and you exchange the code for an access token and refresh token. The refresh token is only issued if you include offline.access in the scope. Access tokens expire after 2 hours — always implement refresh logic.

  1. 1Go to https://developer.twitter.com/en/portal/dashboard and create a new project and app
  2. 2In the app settings, enable OAuth 2.0 under 'User authentication settings'
  3. 3Set the app permissions to 'Read' (tweet.read, users.read)
  4. 4Add a callback URL (e.g. http://localhost:3000/callback or your production URL)
  5. 5Copy the Client ID (OAuth 2.0 Client ID, not the API Key) from the app dashboard
  6. 6If your app is confidential (server-side), also copy the Client Secret
  7. 7Build the authorization URL: https://twitter.com/i/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=tweet.read%20users.read%20offline.access&state=STATE&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
  8. 8After user authorizes, exchange the code at https://api.twitter.com/2/oauth2/token for access_token and refresh_token
auth.py
1import os
2import secrets
3import hashlib
4import base64
5import requests
6
7CLIENT_ID = os.environ['TWITTER_CLIENT_ID']
8CLIENT_SECRET = os.environ['TWITTER_CLIENT_SECRET'] # For confidential apps
9REDIRECT_URI = os.environ['TWITTER_REDIRECT_URI']
10
11def generate_pkce():
12 code_verifier = secrets.token_urlsafe(50)
13 code_challenge = base64.urlsafe_b64encode(
14 hashlib.sha256(code_verifier.encode()).digest()
15 ).decode().rstrip('=')
16 return code_verifier, code_challenge
17
18def refresh_access_token(refresh_token):
19 resp = requests.post(
20 'https://api.twitter.com/2/oauth2/token',
21 auth=(CLIENT_ID, CLIENT_SECRET),
22 data={
23 'grant_type': 'refresh_token',
24 'refresh_token': refresh_token,
25 'client_id': CLIENT_ID,
26 }
27 )
28 resp.raise_for_status()
29 return resp.json() # Contains new access_token and refresh_token

Security notes

  • Store CLIENT_ID, CLIENT_SECRET, and tokens in environment variables — never hardcode them
  • The offline.access scope is required to get a refresh token — without it, users must re-authorize every 2 hours
  • Always use PKCE (code_challenge/code_verifier) — never use the implicit flow
  • When you get a new refresh_token after refreshing, store it immediately — the old one is invalidated
  • Never expose your Client Secret in frontend code — OAuth 2.0 token exchange must happen server-side

Key endpoints

GET/2/users/{id}/tweets

Returns the most recent tweets for a user. Add tweet.fields=public_metrics,non_public_metrics,organic_metrics to get engagement data. This is the primary endpoint for analytics reports.

ParameterTypeRequiredDescription
idstringrequiredThe user ID (not username) of the account to fetch tweets for
tweet.fieldsstringoptionalComma-separated fields: public_metrics,non_public_metrics,organic_metrics,created_at,entities
max_resultsnumberoptionalTweets per page: 5-100, default 10
pagination_tokenstringoptionalCursor token from meta.next_token for pagination
start_timestringoptionalISO 8601 start date filter, e.g. 2026-05-13T00:00:00Z

Response

json
1{"data": [{"id": "1234567890", "text": "Just shipped a new feature!", "public_metrics": {"impression_count": 12847, "like_count": 342, "retweet_count": 89, "reply_count": 23, "quote_count": 14}, "organic_metrics": {"impression_count": 12847, "like_count": 342, "retweet_count": 89, "reply_count": 23}, "created_at": "2026-05-20T14:30:00.000Z"}], "meta": {"next_token": "7140dibdnow9c7btw3w29n4v4idp", "result_count": 10, "newest_id": "1234567890", "oldest_id": "1234567800"}}
GET/2/tweets/{id}

Returns a single tweet with optional metrics fields. Use for enriching individual tweet data or verifying specific post performance.

ParameterTypeRequiredDescription
idstringrequiredTweet ID to look up
tweet.fieldsstringoptionalFields to include: public_metrics, created_at, entities, context_annotations

Response

json
1{"data": {"id": "1234567890", "text": "Product launch tweet", "public_metrics": {"impression_count": 45231, "like_count": 1247, "retweet_count": 389, "reply_count": 124, "quote_count": 67}, "created_at": "2026-05-15T09:00:00.000Z"}}
GET/2/users/me

Returns the authenticated user's profile data. Use to get the user ID needed for the tweets endpoint, and to fetch basic profile metrics.

ParameterTypeRequiredDescription
user.fieldsstringoptionalFields to include: public_metrics, created_at, description, verified

Response

json
1{"data": {"id": "987654321", "name": "Your Company", "username": "yourcompany", "public_metrics": {"tweet_count": 1247, "listed_count": 89}}}

Step-by-step automation

1

Get the Authenticated User's ID

Why: The tweets endpoint requires a user ID, not a username — GET /2/users/me gives you the correct ID for the authenticated account.

Call GET /2/users/me with user.fields=public_metrics to get the user ID and current profile stats. Store the ID for subsequent calls — it never changes. Note that follower_count was removed from the public_metrics response for self-serve tiers in April 2026.

request.sh
1curl 'https://api.twitter.com/2/users/me?user.fields=public_metrics,created_at' \
2 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Pro tip: Cache the user ID — it never changes and calling /2/users/me on every run wastes your monthly read quota

Expected result: User object with id, username, and public_metrics (tweet_count, listed_count). Follower/following counts NOT available on Basic tier as of April 2026.

2

Fetch Recent Tweets with Metrics

Why: The /users/{id}/tweets endpoint with tweet.fields=public_metrics,organic_metrics gives you impression counts and engagement data for the past 7 days.

GET /2/users/{id}/tweets with max_results=100 and tweet.fields=public_metrics,organic_metrics,created_at. Public metrics (impressions, likes, retweets, replies) are available to all. Organic metrics (same data broken out for organic vs promoted) require user-context auth. Note: this costs $0.001 per tweet read on pay-per-use, or counts toward the 15,000/month Basic tier cap.

request.sh
1curl 'https://api.twitter.com/2/users/YOUR_USER_ID/tweets?max_results=100&tweet.fields=public_metrics,organic_metrics,created_at' \
2 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Pro tip: Use start_time parameter to limit to the last 7 days: start_time=2026-05-13T00:00:00Z — this reduces read costs and speeds up the request

Expected result: Array of tweet objects each with id, text, created_at, and public_metrics containing impression_count, like_count, retweet_count, reply_count, quote_count

3

Calculate Engagement Metrics

Why: Raw counts are not enough — you need engagement rate, best performing content, and week-over-week trends to make the report actionable.

Calculate engagement rate as (likes + retweets + replies + quotes) / impressions * 100 for each tweet. Sort by total engagement to identify top content. Group tweets by day or week to calculate trend direction. Without follower counts (removed April 2026), use impression-based engagement rates rather than follower-based ones.

request.sh
1# No direct API call for this step calculation is done in code
2# Fetch the tweets first, then calculate engagement metrics locally

Pro tip: Impression-based engagement rate is more meaningful than follower-based since April 2026 — an account with 10k followers but bad reach will show honest engagement rates this way

Expected result: Array of tweet objects enriched with total_engagement and engagement_rate, sorted by performance for inclusion in the report

4

Generate and Deliver the Weekly Report

Why: Turning raw numbers into a formatted summary delivered to email or Slack is what makes this automation genuinely useful rather than just a data pull.

Aggregate all tweet metrics for the week: total impressions, total engagements, average engagement rate, top tweet, and period-over-period comparison if you store historical data. Format as a Slack Block Kit message or plain text email. For Slack delivery, use the Incoming Webhooks URL from your Slack app settings.

request.sh
1# Send weekly report to Slack
2curl -X POST YOUR_SLACK_WEBHOOK_URL \
3 -H 'Content-Type: application/json' \
4 -d '{
5 "text": "*X (Twitter) Weekly Report — Week of May 19-26, 2026*",
6 "attachments": [
7 {
8 "fields": [
9 {"title": "Total Impressions", "value": "142,831", "short": true},
10 {"title": "Total Engagement", "value": "3,847", "short": true},
11 {"title": "Avg Engagement Rate", "value": "2.69%", "short": true},
12 {"title": "Top Tweet", "value": "Just shipped a new feature! (1,247 likes)", "short": false}
13 ]
14 }
15 ]
16 }'

Pro tip: Store weekly summary data to a database or spreadsheet — the X API only returns the last 7 days for Basic tier, so you need to build your own historical record

Expected result: Formatted Slack message delivered with total impressions, engagement totals, average engagement rate, tweet count, and top performing tweet for the week

Complete working code

This script fetches the last 7 days of tweets for the authenticated user, calculates engagement metrics, identifies top performing content, and delivers a weekly report to Slack. It handles OAuth2 token refresh automatically and tracks read costs.

automate_twitter_analytics.py
1#!/usr/bin/env python3
2import os
3import logging
4import requests
5from datetime import datetime, timedelta, timezone
6
7logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
8
9ACCESS_TOKEN = os.environ['TWITTER_ACCESS_TOKEN']
10REFRESH_TOKEN = os.environ['TWITTER_REFRESH_TOKEN']
11CLIENT_ID = os.environ['TWITTER_CLIENT_ID']
12CLIENT_SECRET = os.environ['TWITTER_CLIENT_SECRET']
13SLACK_WEBHOOK = os.environ['SLACK_WEBHOOK_URL']
14
15def refresh_token():
16 global ACCESS_TOKEN, REFRESH_TOKEN
17 resp = requests.post(
18 'https://api.twitter.com/2/oauth2/token',
19 auth=(CLIENT_ID, CLIENT_SECRET),
20 data={'grant_type': 'refresh_token', 'refresh_token': REFRESH_TOKEN, 'client_id': CLIENT_ID}
21 )
22 resp.raise_for_status()
23 data = resp.json()
24 ACCESS_TOKEN = data['access_token']
25 if 'refresh_token' in data:
26 REFRESH_TOKEN = data['refresh_token'] # Store new refresh token!
27 logging.info('Token refreshed successfully')
28
29def twitter_get(path, params=None):
30 resp = requests.get(
31 f'https://api.twitter.com/2{path}',
32 params=params,
33 headers={'Authorization': f'Bearer {ACCESS_TOKEN}'}
34 )
35 if resp.status_code == 401:
36 refresh_token()
37 resp = requests.get(
38 f'https://api.twitter.com/2{path}',
39 params=params,
40 headers={'Authorization': f'Bearer {ACCESS_TOKEN}'}
41 )
42 resp.raise_for_status()
43 return resp.json()
44
45def get_user_id():
46 data = twitter_get('/users/me', {'user.fields': 'public_metrics'})
47 return data['data']['id']
48
49def get_recent_tweets(user_id):
50 start_time = (datetime.now(timezone.utc) - timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%SZ')
51 tweets = []
52 pagination_token = None
53 while True:
54 params = {
55 'max_results': 100,
56 'tweet.fields': 'public_metrics,organic_metrics,created_at',
57 'start_time': start_time
58 }
59 if pagination_token:
60 params['pagination_token'] = pagination_token
61 data = twitter_get(f'/users/{user_id}/tweets', params)
62 tweets.extend(data.get('data', []))
63 pagination_token = data.get('meta', {}).get('next_token')
64 if not pagination_token:
65 break
66 logging.info(f'Fetched {len(tweets)} tweets (cost: ~${len(tweets) * 0.001:.4f} on pay-per-use)')
67 return tweets
68
69def analyze_tweets(tweets):
70 results = []
71 for t in tweets:
72 m = t.get('public_metrics', {})
73 impressions = m.get('impression_count', 0)
74 total_eng = sum(m.get(k, 0) for k in ['like_count', 'retweet_count', 'reply_count', 'quote_count'])
75 results.append({
76 'id': t['id'], 'text': t['text'][:100],
77 'impressions': impressions, 'total_engagement': total_eng,
78 'engagement_rate': round(total_eng / impressions * 100, 2) if impressions > 0 else 0,
79 'created_at': t.get('created_at', '')
80 })
81 return sorted(results, key=lambda x: x['total_engagement'], reverse=True)
82
83def send_slack_report(analyzed):
84 total_imp = sum(t['impressions'] for t in analyzed)
85 total_eng = sum(t['total_engagement'] for t in analyzed)
86 avg_rate = total_eng / total_imp * 100 if total_imp > 0 else 0
87 top = analyzed[0] if analyzed else {}
88 payload = {
89 'text': '*X (Twitter) Weekly Analytics Report*',
90 'attachments': [{
91 'color': '#1DA1F2',
92 'fields': [
93 {'title': 'Impressions (7d)', 'value': f"{total_imp:,}", 'short': True},
94 {'title': 'Engagements (7d)', 'value': f"{total_eng:,}", 'short': True},
95 {'title': 'Avg Eng Rate', 'value': f"{avg_rate:.2f}%", 'short': True},
96 {'title': 'Tweets Published', 'value': str(len(analyzed)), 'short': True},
97 {'title': 'Top Tweet', 'value': top.get('text', 'N/A'), 'short': False}
98 ]
99 }]
100 }
101 requests.post(SLACK_WEBHOOK, json=payload)
102 logging.info('Slack report sent')
103
104if __name__ == '__main__':
105 user_id = get_user_id()
106 tweets = get_recent_tweets(user_id)
107 if tweets:
108 analyzed = analyze_tweets(tweets)
109 send_slack_report(analyzed)
110 else:
111 logging.info('No tweets in the last 7 days')

Error handling

401{"title": "Unauthorized", "status": 401, "detail": "Unauthorized"}
Cause

Access token has expired (expires after 2 hours) or is invalid. Missing Authorization header also triggers this.

Fix

Use the refresh_token to get a new access_token via POST /2/oauth2/token with grant_type=refresh_token. The new refresh_token in the response must be stored immediately — the old one is invalidated.

Retry strategy

Refresh token immediately, then retry the original request once

403{"title": "Forbidden", "detail": "non_public_metrics and organic_metrics are not available for tweets that are not from the authenticated user"}
Cause

Attempting to read non_public_metrics or organic_metrics on tweets that don't belong to the authenticated user.

Fix

Only request non_public_metrics and organic_metrics for your own tweets. Use public_metrics only for tweets by other users.

Retry strategy

Remove non_public_metrics from the request and retry with public_metrics only

429{"title": "Too Many Requests", "status": 429, "detail": "Too Many Requests"}
Cause

You have exceeded the monthly read cap (15,000 on Basic tier) or hit a short-term rate limit (typically 15 requests per 15 minutes on the tweets endpoint).

Fix

Check the x-rate-limit-remaining and x-rate-limit-reset headers to know when to retry. For monthly cap exhaustion, you will need to wait until the monthly reset or upgrade your tier.

Retry strategy

Read x-rate-limit-reset header (Unix timestamp) and sleep until then; for monthly cap, stop automation and send an alert

400{"errors": [{"message": "Invalid value for \"tweet.fields\" field"}]}
Cause

Requesting tweet fields not available on your tier, or using an invalid field name.

Fix

Check that all fields in tweet.fields are valid for your access tier. Remove non_public_metrics if you don't have user-context OAuth2 auth with the tweet author.

Retry strategy

Fix the request parameters and retry immediately

Rate Limits for X (Twitter) API

ScopeLimitWindow
Tweets read (Basic tier)15,000 tweet readsper month
GET /users/{id}/tweets1,500 requestsper 15 minutes (user auth)
Pay-per-use (own tweets)$0.001per tweet read
retry-handler.ts
1import time
2import requests
3
4def twitter_get_with_retry(url, headers, params=None, max_retries=3):
5 for attempt in range(max_retries):
6 resp = requests.get(url, headers=headers, params=params)
7 if resp.status_code == 429:
8 reset_time = int(resp.headers.get('x-rate-limit-reset', time.time() + 900))
9 wait = max(reset_time - time.time(), 0) + 5
10 print(f'Rate limited. Waiting {wait:.0f}s...')
11 time.sleep(wait)
12 continue
13 resp.raise_for_status()
14 return resp.json()
15 raise Exception('Max retries exceeded')
  • Cache the user ID and store it persistently — calling /users/me on every run wastes read quota
  • Use the start_time parameter to limit to the last 7 days — fetching all-time tweets is expensive and unnecessary for weekly reports
  • Request max_results=100 per page to minimize the number of API calls for pagination
  • Implement token refresh proactively (check expiry time) rather than reactively (on 401 error) to avoid failed report runs
  • Monitor your monthly read consumption using the x-rate-limit-remaining header and alert when approaching the 15,000 cap

Security checklist

  • Store access_token and refresh_token in environment variables or a secrets manager — never hardcode them
  • When you receive a new refresh_token after a token refresh, store it immediately — the previous one is permanently invalidated
  • Use PKCE (code_challenge + code_verifier) for all OAuth 2.0 flows — never use the implicit flow
  • Only request the minimum scopes needed: tweet.read and users.read for analytics (add offline.access for refresh token)
  • Never expose your Client Secret in frontend code — all token exchanges must happen server-side
  • Log API call counts and costs so you can track pay-per-use spending before your monthly bill arrives
  • Validate that you are only fetching your own tweets for non_public_metrics — requesting these for other users' tweets triggers 403 errors

Automation use cases

Weekly Performance Email Digest

intermediate

Automatically send a formatted email every Monday with last week's impressions, engagement rate, top tweet, and week-over-week comparison.

Best-Time-to-Post Analysis

intermediate

Analyze historical tweet performance by hour-of-day and day-of-week to identify when your audience engages most.

Content Type Performance Breakdown

advanced

Categorize tweets by type (plain text, thread, link, media) and compare average engagement rates to identify what content format works best.

Google Sheets Analytics Dashboard

intermediate

Append weekly tweet metrics to a Google Sheet automatically for tracking historical trends and sharing with stakeholders.

No-code alternatives

Don't want to write code? These platforms can automate the same workflows visually.

Zapier

Free tier available; paid from $19.99/month

Zapier's X (Twitter) integration can trigger on new tweets and log metrics to spreadsheets, though real-time tweet.fields metrics are limited.

Pros
  • + No code required
  • + Easy Sheets/email integration
  • + Reliable scheduling
Cons
  • - Limited access to impression/organic metrics
  • - Cannot aggregate across multiple tweets
  • - Costly for high-volume tweet tracking

Make

Free tier available; paid from $9/month

Make has an X (Twitter) module supporting tweet lookup with metrics, enabling scheduled report builds with data transformation.

Pros
  • + More flexible data transformation than Zapier
  • + Lower cost per operation
  • + Can aggregate metrics across tweet sets
Cons
  • - Limited to Make's X module capabilities
  • - No native spike detection
  • - Requires paid plan for scheduled automation

n8n

Self-hosted free; cloud from €20/month

n8n's Twitter node supports fetching user tweets with metrics; combined with Code nodes, you can build full report logic including engagement rate calculation.

Pros
  • + Self-hostable — no per-operation costs
  • + Full code flexibility with Code nodes
  • + Can output to any destination including email, Slack, Notion
Cons
  • - Requires self-hosting setup
  • - Twitter node may lag behind API v2 changes
  • - No built-in cost tracking for pay-per-use

Best practices

  • Include offline.access in your OAuth scopes — without it you cannot get a refresh token and users must re-authorize every 2 hours
  • Track your monthly read consumption proactively — on pay-per-use, 100 tweets fetched daily costs ~$3/month, while Basic tier at $200/month includes 15,000 reads
  • Store user_id persistently after the first lookup — /users/me costs a read each time and the ID never changes
  • Use impression-based engagement rates now that follower counts are unavailable on self-serve tiers (removed April 2026)
  • Always save historical report data locally — X API Basic tier only returns the last 7 days, so once the window passes, the data is gone
  • Check x-rate-limit-remaining on every response and back off proactively when below 20% remaining
  • Build token refresh into every API call function rather than as a separate step — token expiry can happen mid-run on long reports

Ask AI to help

Copy one of these prompts to get a personalized, working implementation.

ChatGPT / Claude Prompt

I'm building a Twitter/X analytics report automation using the X API v2 in Python. I use GET /2/users/{id}/tweets with tweet.fields=public_metrics,organic_metrics to fetch recent tweets. My OAuth 2.0 access tokens expire every 2 hours and I need to use refresh_token to get a new one. The issue is that when I refresh the token, I get back a NEW refresh_token that replaces the old one, and I need to store it somewhere persistent. Can you help me write a token management class that stores access and refresh tokens to a file and automatically refreshes before expiry?

Lovable / V0 Prompt

Build an X (Twitter) analytics dashboard UI. It should display a weekly metrics summary: total impressions, total engagements, average engagement rate, and number of tweets. Show a bar chart of daily impressions for the past 7 days. Include a table of recent tweets sorted by engagement rate with columns: tweet text (truncated), impressions, likes, retweets, replies, and engagement rate percentage. Add a note that follower counts are not available on the Basic tier. The data comes from a Python script that polls the X API weekly — design the frontend with mock data that matches the expected API schema.

Frequently asked questions

Is the X (Twitter) API free for analytics reports?

The Free tier is effectively unusable for analytics — it provides zero tweet reads. You need Basic ($200/month, 15,000 reads/month) or pay-per-use ($0.001/read for own tweets). For a weekly report fetching 100 tweets weekly, that is 400 reads/month on pay-per-use, costing about $0.40/month — well under the Basic tier cost.

Why can't I get follower count data anymore?

X removed follower_count and following_count from the public_metrics response for all self-serve API tiers (Free, Basic, and Pro's lower access levels) in April 2026. This data is only available on the Enterprise tier ($42,000+/year). Use impression-based engagement rates instead — they are more accurate anyway since they reflect actual content reach.

What happens when I hit the rate limit?

The API returns HTTP 429 with x-rate-limit-reset (Unix timestamp) and x-rate-limit-remaining (0) headers. Sleep until the reset time plus a 5-second buffer, then retry. For the monthly cap (15,000 reads on Basic), there is no retry — the cap resets on your billing cycle date.

My access token expired and I got a 401 error. How do I refresh it?

POST to https://api.twitter.com/2/oauth2/token with grant_type=refresh_token and your current refresh_token. You will get a new access_token (and usually a new refresh_token). Critical: immediately replace your stored refresh_token with the new one — the old token is permanently invalidated and cannot be used again.

Can I get analytics data for other users' tweets?

Only public_metrics (likes, retweets, replies, quotes) are available for other users' tweets. Non-public and organic metrics require the tweet author's OAuth token. Each other-user tweet read costs $0.005 on pay-per-use (vs $0.001 for your own tweets). Competitor analytics requires significant API budget.

How far back can I fetch tweet history?

Basic tier: GET /2/users/{id}/tweets returns the 3,200 most recent tweets. You can use start_time to filter by date. Full archive search (all tweets ever) requires the Academic Research product or the Pro/Enterprise tiers. For your own tweet history, the 3,200 tweet cap is usually sufficient for analytics.

Can RapidDev build a custom X analytics dashboard for my brand?

Yes. RapidDev has built 600+ integrations including social media analytics platforms. We can build production-grade X analytics automation with cost monitoring, multi-account support, and custom reporting. Book a free consultation at rapidevelopers.com.

RapidDev

Need this automated?

Our team has built 600+ apps with API automations. We can build this for you.

Book a free consultation

Skip the coding — we'll build it for you

Our experts have built 600+ API automations. From prototype to production in days, not weeks.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.