Twitter / X Data API & Scraper
Twitter / X Data API returns live Twitter / X Data data as clean JSON for twitter / x data The primary endpoint, profile, returns user data including user id, name, username, description and followers.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
Developers reach for it when they need to pull public profiles, posts, comments and engagement counts from a logged-out surface without maintaining one-off scraping code or separate API contracts. If you were about to build or fix a Twitter / X Data scraper, this API is the maintained alternative — it returns the same data as clean JSON, with the proxies, rotation and anti-bot handling already solved. This page covers the live example, request shape, response shape and available actions: profile, user_by_id, user_tweets, user_replies. Every request uses the same ReefAPI envelope, one API key and one shared credit pool, so it fits alongside the rest of your data stack.
Field types that are not what you would guess
Most of the confusion on this API is not about missing data, it is about types. Ids are strings that look like numbers, one engagement counter is a string while the four beside it are integers, and created_at is not ISO 8601. Everything below came out of measured responses on @NASA and on a live Latest search.
| Field | Type and format | Measured value |
|---|---|---|
| tweet_id, user_id, list_id, conversation_id | String, always. Never parse them as numbers. | "2092721435663798658" and "11348282" |
| created_at (on a tweet) | Twitter's own format, EEE MMM dd HH:mm:ss +0000 yyyy, always UTC. | "Wed Aug 26 21:10:45 +0000 2026" |
| likes, retweets, replies, quotes, bookmarks | Integers. | 381, 47, 18, 7, 17 |
| views | String, not an integer. Cast it yourself. | "155736" |
| verified vs verified_type | verified is a boolean; verified_type names the badge and is null for a plain paid check. | @NASA: true plus "Government". Another account on the same page: true plus null |
| urls[] | Already expanded. text still holds the t.co shortlink. | text carried a t.co link while urls[] held https://www.youtube.com/watch?v=tGjffGccQig |
| created_at and protected (on a user) | Both came back null on every profile measured, via profile, user_by_id, search product=People and the embedded author object alike. | "created_at": null, "protected": null |
| search product=People | Returns users[] instead of tweets[]. The other three products return tweets[]. | data keys were query, product, users, count, next_cursor |
| An unknown handle | NOT_FOUND, not an empty result. | profile on a made-up handle returned NOT_FOUND with retryable false |
Every media[] entry carries url, which is the poster frame. A video entry adds video_url and duration_ms; a photo entry has neither. A measured video came back with duration_ms 28361 and an mp4 under video.twimg.com.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/twitter/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "openai",
"limit": 10
}
}{
"ok": true,
"meta": {
"api": "twitter",
"endpoint": "search",
"mode": "live",
"latency_ms": 2171.7,
"record_count": 10,
"bytes": 92684,
"cache_hit": false,
"charged_credits": 1,
"version": "0.2.0"
},
"data": {
"query": "openai",
"product": "Top",
"tweets": [
{
"tweet_id": "2102460975790137662",
"text": "Please welcome GPT-6 Sol and GPT-6 Luna to the GPT-6 universe.\n\nGPT-6 Sol and Luna build on the advances behind GPT-6 Astra, bringing much of its strengths into faster and more affordable models to support work at scale.\n\nWe’ve also made caching and inference more efficient, and we’re passing the savings directly to you: 50% lower API prices for Sol and Luna compared with GPT‑5.6 promotional pricing.",
"created_at": "Tue Sep 22 18:12:13 +0000 2026",
"lang": "en",
"likes": 53252,
"retweets": 5271,
"replies": 2201,
"quotes": 3022,
"bookmarks": 6433,
"views": "9585831",
"conversation_id": "2102460975790137662",
"reply_to_tweet_id": null,
"reply_to_user": null,
"is_quote": false,
"possibly_sensitive": false,
"hashtags": [],
"mentions": [],
"urls": [],
"media": [
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]",
"alt": "[trimmed-depth]",
"video_url": "[trimmed-depth]",
"duration_ms": "[trimmed-depth]"
}
],
"author": {
"user_id": "4398626122",
"name": "OpenAI",
"username": "OpenAI",
"description": "OpenAI’s mission is to ensure that artificial general intelligence benefits all of humanity. We’re hiring: https://t.co/dJGr6LgzPA",
"followers": 5374322,
"following": 4,
"tweets": 2143,
"likes": 2484,
"media_count": 725,
"listed_count": 32403,
"verified": true,
"verified_type": "Business",
"protected": null,
"created_at": "Sun Dec 06 22:51:08 +0000 2015",
"location": null,
"website": "https://openai.com",
"profile_image": "https://pbs.twimg.com/profile_images/1885410181409820672/ztsaR0JW_normal.jpg",
"profile_banner": "https://pbs.twimg.com/profile_banners/4398626122/1777662919",
"pinned_tweet_ids": [
"[trimmed-depth]"
],
"professional_category": null,
"url": "https://x.com/OpenAI"
},
"url": "https://x.com/i/status/2102460975790137662"
},
{
"tweet_id": "2102462020339613966",
"text": "GPT-6 Sol and GPT-6 Luna, it’s your time to shine.\n\nRolling out today in ChatGPT Work and Codex for Plus, Pro, Business, Enterprise, and Edu users. https://t.co/UtpG1Tbhd3",
"created_at": "Tue Sep 22 18:16:22 +0000 2026",
"lang": "en",
"likes": 10667,
"retweets": 882,
"replies": 455,
"quotes": 268,
"bookmarks": 702,
"views": "991503",
"conversation_id": "2102462020339613966",
"reply_to_tweet_id": null,
"reply_to_user": null,
"is_quote": false,
"possibly_sensitive": false,
"hashtags": [],
"mentions": [],
"urls": [],
"media": [
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]",
"alt": "[trimmed-depth]",
"video_url": "[trimmed-depth]",
"duration_ms": "[trimmed-depth]"
}
],
"author": {
"user_id": "1714438092655149056",
"name": "ChatGPT",
"username": "ChatGPT",
"description": "ChatGPT is for the people.",
"followers": 731224,
"following": 261,
"tweets": 552,
"likes": 1580,
"media_count": 149,
"listed_count": 4011,
"verified": true,
"verified_type": "Business",
"protected": null,
"created_at": "Wed Oct 18 00:27:53 +0000 2023",
"location": "Supercomputer",
"website": "https://chat.com/",
"profile_image": "https://pbs.twimg.com/profile_images/2081980424666935296/qU5G7mQg_normal.jpg",
"profile_banner": "https://pbs.twimg.com/profile_banners/1714438092655149056/1789989030",
"pinned_tweet_ids": [
"[trimmed-depth]"
],
"professional_category": [
"[trimmed-depth]"
],
"url": "https://x.com/ChatGPT"
},
"url": "https://x.com/i/status/2102462020339613966"
},
{
"tweet_id": "2098811563415150910",
"text": "I agree with Dario that we need to pace the frontier. This has been a primary topic of discussions we've had at OpenAI in recent weeks.\n\nCommitting to having independent evaluators with employee-like access is a great idea, and we will do the same. We'll have more to share soon.",
"created_at": "Sat Sep 12 16:30:45 +0000 2026",
"lang": "en",
"likes": 67653,
"retweets": 7206,
"replies": 5173,
"quotes": 4375,
"bookmarks": 11018,
"views": "17011464",
"conversation_id": "2098811563415150910",
"reply_to_tweet_id": null,
"reply_to_user": null,
"is_quote": true,
"possibly_sensitive": null,
"hashtags": [],
"mentions": [],
"urls": [],
"media": [],
"author": {
"user_id": "1605",
"name": "Sam Altman",
"username": "sama",
"description": "The mission of OpenAI is to ensure that AGI benefits all of humanity",
"followers": 6282806,
"following": 1031,
"tweets": 7979,
"likes": 1019,
"media_count": 394,
"listed_count": 31588,
"verified": true,
"verified_type": null,
"protected": null,
"created_at": "Sun Jul 16 22:01:55 +0000 2006",
"location": "SF",
"website": "http://blog.samaltman.com",
"profile_image": "https://pbs.twimg.com/profile_images/2046764873200394240/r7BxVezs_normal.jpg",
"profile_banner": "",
"pinned_tweet_ids": [],
"professional_category": null,
"url": "https://x.com/sama"
},
"url": "https://x.com/i/status/2098811563415150910",
"quoted": {
"tweet_id": "2098773920774074715",
"text": "We Must Pace the Frontier: I’ve written a new essay on why the AI industry should slow down, with a three-part plan for doing so.\n\nAnthropic is unilaterally committing to the first of these steps. We’ll provide third-party evaluators with permanent, employee-level access to our systems, so that they can verify adherence to our safety measures, report on incidents, and assess models’ alignment during training.\n\nYou can read the full post here: https://t.co/OGyPb7yaYt",
"created_at": "Sat Sep 12 14:01:10 +0000 2026",
"lang": "en",
"likes": 87815,
"retweets": 16378,
"replies": 10652,
"quotes": 11344,
"bookmarks": 41259,
"views": "76401757",
"conversation_id": "2098773920774074715",
"reply_to_tweet_id": null,
"reply_to_user": null,
"is_quote": false,
"possibly_sensitive": null,
"hashtags": [],
"mentions": [],
"urls": [],
"media": [],
"author": {
"user_id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"username": "[trimmed-depth]",
"description": "[trimmed-depth]",
"followers": "[trimmed-depth]",
"following": "[trimmed-depth]",
"tweets": "[trimmed-depth]",
"likes": "[trimmed-depth]",
"media_count": "[trimmed-depth]",
"listed_count": "[trimmed-depth]",
"verified": "[trimmed-depth]",
"verified_type": "[trimmed-depth]",
"protected": "[trimmed-depth]",
"created_at": "[trimmed-depth]",
"location": "[trimmed-depth]",
"website": "[trimmed-depth]",
"profile_image": "[trimmed-depth]",
"profile_banner": "[trimmed-depth]",
"pinned_tweet_ids": "[trimmed-depth]",
"professional_category": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
"url": "https://x.com/i/status/2098773920774074715"
}
}
],
"count": 10,
"next_cursor": "DAACCgACHTGKDuvAJxAKAAMdMYoO67_Y8AgABAAAAAILAAUAAACIRW1QQzZ3QUFBZlEvZ0dKTjB2R3AvQUFBQUFvZEZRMDVWcHJ4VkIweFh6alhHaEV5SFM3VW5ZYmIwWFFkTFhSNjlsb0JXQjB0Y1I1QUY2RStIU0I1L3pmWG9UNGRNRWQ3TzlkaGxoMHRjaEYwRjhFT0hTOVlBVmJiVVdzZE1MRVEwdHR4UkE9PQgABgAAAAAIAAcAAAAADAAICgABHRUNOVaa8VQAAAA"
}
}What the Twitter / X Data API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| profile | User profile by @username — name, bio, followers, following, tweet/like counts, verified, created date, location, website, avatar/banner. | Social-listening tools call profile to get user profile by @username. | username |
| user_by_id | User profile by numeric user_id (same fields as profile). | Creator and influencer platforms call user_by_id to get user profile by numeric user_id (same fields as profile).. | user_id |
| user_tweets | A user's tweets — full text, engagement (likes/retweets/replies/quotes/views), media, quoted/retweeted, with cursor pagination. | Brand-monitoring teams call user_tweets to get a user's tweets. | username, user_id, limit, cursor |
| user_replies | A user's tweets AND replies (timeline incl. replies). Each reply also carries reply_to_text (the parent tweet's text, ≤280 chars) + reply_to_author so a reply is interpretable without a second call — resolved from the conversation already in the response. | Audience analysts call user_replies to get a user's tweets AND replies (timeline incl. | username, user_id, limit, cursor |
| user_media | A user's media tweets — photos & videos (with video_url + duration), engagement, pagination. | Social-listening tools call user_media to get a user's media tweets. | username, user_id, limit, cursor |
| user_likes | Tweets a user has liked (if the account exposes likes). | Creator and influencer platforms call user_likes to get tweets a user has liked (if the account exposes likes).. | username, user_id, limit, cursor |
| tweet | A single tweet by id — full text, engagement, media, quoted/retweeted, author. | Brand-monitoring teams call tweet to get a single tweet by id. | tweet_id |
| tweet_replies | A tweet PLUS its replies/comments (threaded conversation) with DEEP cursor pagination. | Audience analysts call tweet_replies to get a tweet PLUS its replies/comments (threaded conversation) with DEEP cursor pagination.. | tweet_id, limit, cursor |
| search | Search tweets or people. product=Top|Latest|People|Media. Supports X search operators in the query. Cursor pagination. | Social-listening tools call search to search tweets or people. | query, product, limit, cursor |
| followers | A user's followers (each a full user object), paginated. | Creator and influencer platforms call followers to get a user's followers (each a full user object), paginated.. | username, user_id, limit, cursor |
| following | Accounts a user follows (each a full user object), paginated. | Brand-monitoring teams call following to get accounts a user follows (each a full user object), paginated.. | username, user_id, limit, cursor |
| list_tweets | Latest tweets from a Twitter List by id, paginated. | Audience analysts call list_tweets to get latest tweets from a Twitter List by id, paginated.. | list_id, limit, cursor |
| trends | Current trending topics on X / Twitter (the Explore feed) — each with its name, location/category context, post-volume, and a search URL. | Social-listening tools call trends to get current trending topics on X / Twitter (the Explore feed). | limit |
| community_search | Search posts across X Communities by keyword — returns matching community tweets (same rich tweet shape as search). | Creator and influencer platforms call community_search to search posts across X Communities by keyword. | query, product, limit, cursor |
Call search from your stack
curl -X POST https://api.reefapi.com/twitter/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"openai","limit":10}'import requests
r = requests.post(
"https://api.reefapi.com/twitter/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "openai",
"limit": 10
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/twitter/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "openai",
"limit": 10
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.twitter.search with {"query":"openai","limit":10}.Who uses this API and why
- Social-listening tools use Twitter / X Data to get user profile by @username.
- Creator and influencer platforms use Twitter / X Data to get user profile by numeric user_id (same fields as profile)..
- Brand-monitoring teams use Twitter / X Data to get a user's tweets.
- Audience analysts use Twitter / X Data to get a user's tweets AND replies (timeline incl.
- Social-listening tools use Twitter / X Data to get a user's media tweets.
Questions developers ask before integrating
Why is tweet_id a string, and what breaks if I turn it into a number?
Because snowflake ids passed JavaScript's safe integer range years ago. A measured tweet id, 2092721435663798658, is larger than 2^53, so JSON.parse turns it into 2092721435663798784: off by 126, and pointing at nothing. The same applies to user_id and conversation_id. Keep them as strings end to end, in your database column, in your own JSON and in any URL you build. Every id this API returns is already a string for exactly this reason.
Why is views a string when likes is a number?
That is how X's own payload ships it, and we pass the type through rather than quietly coercing it. A measured tweet returned likes 381 as an integer alongside views "155736" as a string. Cast views before you sum or sort on it, otherwise a language that compares strings lexically will tell you "9" beats "155736". Views can also be absent entirely on very old tweets, so guard for that as well.
A retweet's text is cut off at 140 characters. Where is the full text?
In retweeted{}. When a row is a retweet it carries is_retweet true and a nested retweeted object holding the original tweet, and it is the outer text that is truncated with an ellipsis, not the inner one. A measured @NASA retweet had text ending "...long exposure star trails from t" while retweeted.text held the whole 408-character original along with its own author, media and counts. Read the outer row for who retweeted and when, and the inner one for what was actually said.
The retweet shows 0 likes. Is that right?
Yes, and it is the correct reading. Engagement lands on the original tweet, not on the retweet row. A measured retweet came back with likes 0, replies 0 and quotes 0 but retweets 120, while retweeted.likes was 788 and retweeted.retweets was also 120. So the retweet count mirrors and everything else sits at zero. If you are scoring a timeline, take engagement from retweeted{} on retweet rows or you will systematically undercount an account that retweets a lot.
How do I parse created_at?
It is Twitter's legacy format, not ISO 8601: "Wed Aug 26 21:10:45 +0000 2026". In Python that is strptime with "%a %b %d %H:%M:%S %z %Y"; in Go the layout is "Mon Jan 02 15:04:05 -0700 2006". It is always +0000, so you can treat it as UTC. Note that created_at on the user object is a different thing entirely (account creation date) and came back null on every profile measured, so do not use it to age an account.
How do I tell a real verified account from a paid checkmark?
Read verified_type, not verified. Measured on one search page: @NASA came back verified true with verified_type "Government", while an ordinary account on the same page came back verified true with verified_type null. verified alone is now true for anyone paying, so verified_type is what carries the distinction. The values observed are "Government", "Business" and null.
How do replies come back, and can I read one without a second call?
Usually yes. tweet_replies returns the focal tweet first and then the replies in the same tweets[] array, so index 0 is the parent. On timeline and search rows a reply carries reply_to_tweet_id and reply_to_user, and where the conversation was already in the response it also carries reply_to_text and reply_to_author. A measured search row included the parent's text inline, so the reply was interpretable without another fetch. reply_to_tweet_id is null on an original tweet.
Do the counts drift between two calls on the same tweet?
Yes, and that is the live site moving rather than an inconsistency. The same tweet read three times inside a minute returned views "153649", "155736" and "155557", with likes 379, 381 and 382. The author object embedded in each row carries a follower count snapshotted at that moment too, so @NASA showed 92354072 and 92366117 seconds apart. If you need a stable number, timestamp your own read rather than expecting the source to hold still.
What is the Twitter / X Data API?
Twitter / X Data API is a ReefAPI endpoint group for twitter / x data It returns live JSON through POST requests under /twitter/v1.
Is the Twitter / X Data API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Twitter / X Data calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Twitter / X Data login or account?
No login to Twitter / X Data is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Twitter / X Data data?
The page example is captured from a live profile call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Twitter / X Data API use?
Twitter / X Data actions currently cost 1 credit per successful call. Failed or blocked calls are free. All APIs draw from one credit pool.
Can I call Twitter / X Data from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call twitter actions with the same key, credit pool and JSON envelope used by normal REST requests.