# X (Twitter) Profile Scraper - Followers, Bio & Tweets (`socialscrapers/x-profile-scraper`) Actor

X (Twitter) profile scraper: get Twitter user info for any handle (followers, following, bio, links, location, join date, verification, affiliation, category) plus their latest tweets with likes, retweets and views. No login, no API key. $0.14 per 1,000 profiles.

- **URL**: https://apify.com/socialscrapers/x-profile-scraper.md
- **Developed by:** [Social Scrapers](https://apify.com/socialscrapers) (community)
- **Categories:** Social media, Lead generation, Marketing
- **Features:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.11 / 1,000 profiles

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**X (Twitter) Profile Scraper** extracts public profile data from X (formerly Twitter) for any list of handles or profile URLs: followers, following, posts count, bio, bio links, location, website, join date, checkmark type, affiliation badge, professional category, profile picture and banner, and optionally each profile's **latest posts** with likes, reposts, replies and views. No X account, login, cookies or API key needed. **$0.14 per 1,000 profiles**, plus $0.10 per 1,000 posts when you ask for posts.

### What does X (Twitter) Profile Scraper do?

It reads public X profiles the way a logged-out visitor sees them and turns each one into a clean dataset item. Paste handles (`NASA`, `@NASA`) or profile URLs (`https://x.com/NASA`, `https://twitter.com/NASA`), press **Start**, and download the results as JSON, CSV, Excel or HTML, or read them through the Apify API.

- 👤 **Profile details**: display name, handle, numeric account ID, bio, the links in the bio, location, website (the real link, not a t.co shortlink) and join date.
- 📊 **Counts**: followers, following, posts, likes, media posts, public lists, highlighted posts, subscriptions and, for organisations, affiliated accounts.
- ✅ **Verification and labels**: checkmark, gold (Business) or grey (Government) checkmark, X's reason for it, identity verification, the affiliation badge (the organisation an account belongs to), professional type and category, parody/commentary/fan labels.
- 📝 **Latest posts (optional)**: each post with full text, date, likes, reposts, replies, quotes, bookmarks, views, hashtags, mentions, links, photos and video URLs, and the reposted or quoted post. For most brand, media and organisation accounts that is the newest posts, up to about 800 back; for many personal accounts X shows logged-out visitors about 100 of their most popular posts instead, and the item says which (`tweetsOrder`).
- 🧾 **Stable columns**: every profile item has the same fields, so CSV and Excel exports line up.
- 💸 **Pay only for results**: handles that don't exist or are suspended are free, and a run stops exactly at the maximum cost you set.

### What data can you extract from X profiles?

| Field | What it is |
|---|---|
| `id`, `handle`, `name`, `url` | Numeric account ID (stable when the handle changes), current handle, display name, profile URL |
| `bio`, `bioLinks`, `bioLanguage` | Bio text, the links written in it (expanded) and its language |
| `location`, `website`, `createdAt` | Location as typed by the owner, website link (expanded), join date (ISO 8601) |
| `followersCount`, `followingCount` | Followers and accounts followed |
| `tweetsCount`, `likesCount`, `mediaCount`, `listedCount` | Posts, likes given, media posts, public lists |
| `isBlueVerified`, `verifiedType`, `verificationReason`, `isIdentityVerified` | Checkmark, Business/Government type, X's reason, ID verification |
| `affiliatedWith`, `affiliatedWithUrl`, `affiliationBadgeUrl`, `affiliatesCount` | The organisation badge on the profile, and how many accounts an organisation has affiliated |
| `professionalType`, `professionalCategory` | Business or Creator, and the category (e.g. Science & Technology) |
| `parodyLabel`, `subscriptionsCount`, `highlightedTweetsCount`, `birthDate` | Parody/Commentary/Fan label, creator subscriptions, highlighted posts, public birth or founding date |
| `profileImageUrl`, `bannerUrl`, `profileImageShape`, `hasDefaultProfileImage` | 400×400 avatar, header image, avatar shape (Square = organisation), default avatar flag |
| `isProtected`, `possiblySensitive`, `pinnedTweetId`, `scrapedAt` | Protected account, sensitive-media flag, pinned post, when it was read |
| `tweetsOrder`, `latestTweets` | Only when **Latest posts per profile** is above 0: `latest` or `top` (see Limits), and the posts described below |

Each post in `latestTweets` has `id`, `url`, `text`, `createdAt`, `lang`, `likeCount`, `retweetCount`, `replyCount`, `quoteCount`, `bookmarkCount`, `viewCount`, `isPinned`, `isRetweet`, `isQuote`, `isReply`, `inReplyToTweetId`, `inReplyToHandle`, `conversationId`, `hashtags`, `mentions`, `links`, `media` (type, image URL, MP4 URL, alt text), `retweetedTweet`, `quotedTweet`, `source` and `possiblySensitive`.

### How to scrape X (Twitter) profiles

1. Open the Actor and add handles or profile URLs to **X handles or profile URLs**, one per line.
2. Optional: set **Latest posts per profile** (for example `20`) to include each profile's most recent posts, and **Only posts newer than** (for example `30 days`) to stop at a date.
3. Keep the default proxy (Apify Residential).
4. Click **Start**. Profiles appear in the **Output** tab as they are read; the **Latest posts** view shows one row per post.
5. Export the dataset as JSON, CSV, Excel or HTML, or fetch it with the Apify API.

### How much does it cost to scrape X profiles?

You pay per result, platform usage and proxies included:

| What | Price per 1,000 | Silver plan | Gold plan |
|---|---|---|---|
| Profile | **$0.14** | $0.126 | $0.112 |
| Post included with a profile | **$0.10** | $0.09 | $0.08 |

- **10,000 profiles** without posts cost **$1.40**.
- **1,000 profiles with their latest 20 posts each** cost $0.14 + 20,000 × $0.10 / 1,000 = **$2.14**.

Handles that don't exist, are suspended or are invalid cost nothing. Set a **maximum cost per run** when you start the Actor: it stops cleanly before the first profile (or post) it could not pay for, and keeps everything read so far. Apify's free plan includes monthly platform credit, enough to try the Actor on thousands of profiles.

### Input example

```json
{
  "handles": ["NASA", "@nytimes", "https://x.com/Google"],
  "maxTweetsPerProfile": 20,
  "onlyTweetsNewerThan": "30 days",
  "includeRetweets": true,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

| Field | Default | Description |
|---|---|---|
| `handles` | | Handles (`NASA`, `@NASA`) or profile URLs on x.com or twitter.com, including links to a post (the author's profile is read). Duplicates are read once. |
| `maxTweetsPerProfile` | `0` | Latest posts to include per profile. `0` reads the profile only. |
| `onlyTweetsNewerThan` | | Only posts from this date on: `2026-01-31`, `2026-1-31`, `31/01/2026`, `Jan 31 2026`, `2026-01-31T18:00:00Z` or an age like `7 days`, `2w`, `3 months`, `yesterday`. Dates are UTC days; with both day and month 12 or less (`01/02/2026`) the date is read month/day/year. |
| `includeRetweets` | `true` | Keep reposts. Turn off for the account's own posts only. |
| `proxyConfiguration` | Residential | Proxy settings. |

### Output example

One dataset item per profile (trimmed to one post):

```json
{
  "id": "20536157",
  "handle": "Google",
  "name": "Google",
  "url": "https://x.com/Google",
  "bio": "Here to help.",
  "location": "Mountain View, CA",
  "website": "https://blog.google/newsletter-subscribe/",
  "createdAt": "2009-02-10T19:14:39.000Z",
  "followersCount": 31901418,
  "followingCount": 299,
  "tweetsCount": 252026,
  "likesCount": 5671,
  "mediaCount": 38351,
  "listedCount": 89934,
  "isVerified": false,
  "isBlueVerified": true,
  "verifiedType": "Business",
  "isProtected": false,
  "profileImageUrl": "https://pbs.twimg.com/profile_images/2042749771337564160/AgOFPEL3_400x400.jpg",
  "bannerUrl": "https://pbs.twimg.com/profile_banners/20536157/1780004508",
  "pinnedTweetId": "2115489522603466766",
  "bioLinks": [],
  "professionalType": "Business",
  "professionalCategory": "Science & Technology",
  "affiliatedWith": null,
  "affiliatedWithUrl": null,
  "affiliationBadgeUrl": null,
  "affiliatesCount": 39,
  "verificationReason": "This account is verified because it's an official organization on X.",
  "isIdentityVerified": false,
  "parodyLabel": null,
  "subscriptionsCount": 0,
  "highlightedTweetsCount": 77,
  "birthDate": null,
  "bioLanguage": null,
  "profileImageShape": "Square",
  "hasDefaultProfileImage": false,
  "possiblySensitive": false,
  "scrapedAt": "2026-11-08T10:50:22.223Z",
  "tweetsOrder": "latest",
  "latestTweets": [
    {
      "id": "2118808678149308701",
      "url": "https://x.com/Google/status/2118808678149308701",
      "text": "From fun idea → functional smart device 🏄‍♂️ ...",
      "createdAt": "2026-11-06T20:52:09.000Z",
      "lang": "en",
      "likeCount": 700,
      "retweetCount": 65,
      "replyCount": 94,
      "quoteCount": 25,
      "bookmarkCount": 144,
      "viewCount": 125035,
      "isPinned": false,
      "isRetweet": false,
      "isQuote": false,
      "isReply": false,
      "inReplyToTweetId": null,
      "inReplyToHandle": null,
      "conversationId": "2118808678149308701",
      "hashtags": [],
      "mentions": ["antigravity"],
      "links": [],
      "media": [
        {
          "type": "video",
          "url": "https://pbs.twimg.com/amplify_video_thumb/2118806487526965248/img/ubIcFbfarZzf12xX.jpg",
          "videoUrl": "https://video.twimg.com/amplify_video/2118806487526965248/vid/avc1/1920x1080/Q6SHSG_WRkkAiMOQ.mp4?tag=29",
          "altText": null
        }
      ],
      "retweetedTweet": null,
      "quotedTweet": null,
      "source": null,
      "possiblySensitive": false
    }
  ]
}
```

A field is `null` when the profile leaves it empty (no bio, no website) or X does not provide it. Handles that don't exist or are suspended are not added to the dataset; they are listed in the run log and in the `SUMMARY` record of the run's key-value store.

### Use cases

- **Influencer and creator research**: compare reach, activity and engagement on recent posts across a list of accounts.
- **Brand and competitor monitoring**: track follower growth and what brands, publishers and organisations post, on a schedule.
- **Lead enrichment**: add bios, websites, locations, categories and affiliations to a list of X handles.
- **Account checks**: find which handles exist, which are suspended, protected, verified, parody or using a default avatar.
- **Social listening and AI**: feed recent posts of chosen accounts into sentiment analysis, summaries or a RAG pipeline.
- **Academic and market research**: build datasets of public profile attributes and posting activity.

### Use it via API, Python, JavaScript and integrations

Run the X (Twitter) Profile Scraper from your own code with the [Apify API](https://docs.apify.com/api/v2):

```bash
curl -X POST "https://api.apify.com/v2/acts/socialscrapers~x-profile-scraper/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"handles": ["NASA", "Google"], "maxTweetsPerProfile": 10}'
```

Python, with the `apify-client` package:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("socialscrapers/x-profile-scraper").call(
    run_input={"handles": ["NASA", "Google"], "maxTweetsPerProfile": 10}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["handle"], item["followersCount"], len(item.get("latestTweets", [])))
```

JavaScript, with the `apify-client` package:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('socialscrapers/x-profile-scraper').call({ handles: ['NASA', 'Google'], maxTweetsPerProfile: 10 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((p) => `${p.handle}: ${p.followersCount} followers`));
```

It also works without code: connect it to **Make**, **Zapier**, **n8n**, **Google Sheets**, Slack or webhooks from the **Integrations** tab, run it on a **schedule** (for example daily follower tracking), and let AI agents call it through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp).

### Limits

- **Public data only, no login.** The Actor sees what X shows a logged-out visitor. Protected accounts return their profile details, without posts.
- **Which posts X shows.** For most brand, media and organisation accounts, X shows logged-out visitors the newest posts and lets them page back about 800 posts (`tweetsOrder`: `latest`). For many personal accounts it shows about 100 of their most popular posts from across the years instead, with no newer ones (`tweetsOrder`: `top`); **Only posts newer than** then filters that selection. The run's `SUMMARY` lists those profiles.
- **Replies to other accounts, likes, followers lists and keyword search** are not shown to logged-out visitors, so they are not collected. The owner's own threads (replies to themselves) are included.
- `likesCount`, `listedCount` and a few other fields are sometimes `null` when X leaves them out of its answer.
- **Datacenter proxy choices fall back to residential.** If you pick Apify Proxy without the RESIDENTIAL group, the first session X refuses moves the run to RESIDENTIAL, in the same country; custom proxy URLs are used exactly as given.
- **Share and short links work.** `t.co/…` links are followed to the profile or post they lead to, a post link without a handle (`x.com/i/web/status/…`) reads its author's profile, and `mobile.twitter.com`, `twitter.com` and `fxtwitter.com` links name their profile directly. Following a link is never charged.
- **A run with 0 results still succeeds when your input was fine.** When a handle does not exist, is suspended or withheld, or a link leads nowhere, the run ends as Succeeded with a status message naming each input and why, and nothing is charged. A run fails only when no input holds a handle or link at all, or when X refuses every one of 3 or more inputs.

### FAQ

**Is it legal to scrape X (Twitter) profiles?**
The X (Twitter) Profile Scraper only reads data that X shows publicly to anyone without an account. Whether you may use that data depends on your purpose and your jurisdiction (for example personal-data rules such as the GDPR), so check before you store or publish personal data.

**Do I need an X account, cookies or an API key?**
No. You only need a list of handles or profile URLs.

**How many profiles can I scrape?**
As many as you like: runs read profiles in parallel, and large lists of tens of thousands of handles are fine. Each profile's posts are limited to what X shows logged-out visitors: about 800 latest posts, or about 100 top posts (see Limits).

**Why did I get fewer posts than I asked for?**
The profile has fewer posts, the date in **Only posts newer than** was reached, the account is protected, X stopped showing older posts to logged-out visitors, or the run hit its maximum cost. The run log and the `SUMMARY` record say which, per profile.

**Why are some profiles' posts years old?**
For many personal accounts X shows logged-out visitors a selection of about 100 popular posts rather than the latest ones. Those profiles have `tweetsOrder` set to `top`; brand and organisation accounts usually have `latest`.

**How do I get only new posts each day?**
Schedule the Actor daily with **Only posts newer than** set to `1 day` (or `25 hours` for some overlap).

**What happens with handles that don't exist or are suspended?**
They are skipped, logged and not charged. The `SUMMARY` record lists them under `notFound` and `suspended`.

**Can I run it without a proxy?**
Yes, set the proxy to "No proxy". Small lists usually work, but the default residential proxy is more reliable for large lists.

### Other scrapers by Social Scrapers

| Actor | What it does |
|---|---|
| [Facebook Groups Scraper](https://apify.com/socialscrapers/facebook-groups-scraper) | Posts from public Facebook groups |
| [Facebook Profile Scraper](https://apify.com/socialscrapers/facebook-profile-scraper) | Public details of Facebook profiles and Pages |
| [Facebook Profile Posts Scraper](https://apify.com/socialscrapers/facebook-profile-posts-scraper) | Recent posts of Facebook profiles and Pages |
| [Facebook Ads Library Scraper](https://apify.com/socialscrapers/facebook-ads-library-scraper) | Ads from Meta's public Ad Library |
| [Threads Scraper](https://apify.com/socialscrapers/threads-scraper) | Threads profiles, posts, replies and search |
| [Instagram Profile Scraper](https://apify.com/socialscrapers/instagram-profile-scraper) | Instagram profiles with their latest posts |
| [Instagram Post Scraper](https://apify.com/socialscrapers/instagram-post-scraper) | Instagram posts and reels with comments |
| [Reddit Scraper](https://apify.com/socialscrapers/reddit-scraper) | Reddit posts, comments, users and search |
| [Quora Scraper](https://apify.com/socialscrapers/quora-scraper) | Quora questions, answers and profiles |
| [YouTube Scraper](https://apify.com/socialscrapers/youtube-scraper) | YouTube videos, channels, search and comments |
| [YouTube Transcript Scraper](https://apify.com/socialscrapers/youtube-transcript-scraper) | Transcripts and captions of YouTube videos |
| [LinkedIn Jobs Scraper](https://apify.com/socialscrapers/linkedin-jobs-scraper) | Public LinkedIn job listings |

# Actor input Schema

## `handles` (type: `array`):

One per line: a handle (NASA or @NASA) or a profile URL (https://x.com/NASA, https://twitter.com/NASA). A post link reads its author, and t.co short links are followed to the profile or post they lead to. Duplicates are read once. Example: NASA

## `maxTweetsPerProfile` (type: `integer`):

Also save each profile's latest posts (newest first) inside its item, under latestTweets: full text, date, likes, reposts, replies, views, media and the reposted or quoted post. 0 saves the profile only. Example: 20. Reposts and the owner's own threads are included; replies to other accounts are not shown to logged-out visitors.

## `onlyTweetsNewerThan` (type: `string`):

Keep only posts from this date on, and stop reading a profile's posts there. Pick a date or an age, or type one: a date (2026-01-31, 31/01/2026, Jan 31 2026; a UTC day), a date-time through the API (2026-01-31T18:00:00Z) or an age (7 days, 2w, 3 months, yesterday). Leave empty for no date limit.

## `includeRetweets` (type: `boolean`):

Keep reposts among the latest posts. Turn off for the account's own posts only (they still count toward Latest posts per profile).

## `proxyConfiguration` (type: `object`):

Residential proxies give the most reliable results and are the default. Example: Apify Proxy, RESIDENTIAL group.

## Actor input object example

```json
{
  "handles": [
    "NASA",
    "@nytimes",
    "https://x.com/Google"
  ],
  "maxTweetsPerProfile": 0,
  "includeRetweets": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "handles": [
        "NASA",
        "@nytimes",
        "https://x.com/Google"
    ],
    "maxTweetsPerProfile": 0,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("socialscrapers/x-profile-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "handles": [
        "NASA",
        "@nytimes",
        "https://x.com/Google",
    ],
    "maxTweetsPerProfile": 0,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("socialscrapers/x-profile-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "handles": [
    "NASA",
    "@nytimes",
    "https://x.com/Google"
  ],
  "maxTweetsPerProfile": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call socialscrapers/x-profile-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,socialscrapers/x-profile-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/02h8IywWrNlFZHhZ8/builds/Dmbq8bzWgEfy3SBmA/openapi.json
