# X (Twitter) Profile & Post Engagement Extractor (`nimbryxo-studio/x-twitter-profile-and-post-engagement-extractor`) Actor

Get any public X profile - bio, followers, verification, avatar, banner - plus latest, top and media posts with full engagement metrics. No login required.

- **URL**: https://apify.com/nimbryxo-studio/x-twitter-profile-and-post-engagement-extractor.md
- **Developed by:** [Nimbryxo-studio](https://apify.com/nimbryxo-studio) (community)
- **Categories:** Social media, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 & Post Engagement Extractor

Get any public X profile and its posts without login. Returns the profile header (bio, followers, verification, avatar, banner, pinned post) plus the latest posts, top posts, media posts and optional replies, each with full engagement metrics (likes, retweets, replies, quotes, bookmarks, views).

### What this actor does

- **Profile lookup by handle or link:** collects bio, links, followers, following, post count, verification, avatar, banner, pinned post and public join date
- **Post groups:** `latestPosts`, `topPosts` and optional `includeReplies` replies, merged and de-duplicated by post ID; selecting `media` limits every collected post to ones with photos, videos or GIFs
- **History windows:** up to 1,000 posts per profile when X serves older posts publicly; `maxPostsPerProfile` is a target, not just a cap
- **Filters:** date range, language, minimum likes/retweets/replies, media-only, verified-only
- **Honest rows:** every row carries a `type` (`profile`, `post`, `status`); protected accounts report a `protected` status row and login-only panels are reported as unavailable instead of being guessed at
- **Empty fields are omitted** — the dataset never contains `null`, `""`, `[]` or `{}`

### Output per profile row

One `profile` row per account, written only when `include` contains `profileInfo` or `about`:

- `id`, `username`, `name`, `bio`, `bioLinks`, `bioEmails` (addresses written in the bio)
- `location`, `website` (first link in the bio)
- `joinedAt`, `birthDate` — when public
- `followers`, `following`, `postCount`, `mediaCount`, `listedCount`, `likesGiven`
- `isBlueVerified`, `verified`, `verifiedType`, `label`
- `isProtected`
- `avatarUrl`, `bannerUrl`
- `pinnedPostId` (mirrored as `pinnedTweetId`), `pinnedTweet` — the full pinned post when X serves it
- `mediaTweets` — summary list (`id`, `url`, `mediaTypes`) when `media` is selected
- `withReplies` — `true` when reply collection was requested (omitted otherwise)
- `type: "profile"`, `collectedAt`

### Output per post row

- `id`, `url`, `text`, `createdAt`, `language`
- `replyCount`, `retweetCount`, `likeCount`, `quoteCount`, `bookmarkCount`, `viewCount`
- `engagementRate` — derived `(likes + retweets + replies + quotes) / followers`; present only when the author's follower count is known and greater than zero
- `isReply`, `isRetweet`, `isQuote`, `isNoteTweet`, `isPinned`
- `conversationId`, `threadRootId`, `threadPosition`
- `inReplyToTweetId`, `inReplyToUserId`, `inReplyToUsername`
- `hashtags`, `mentions`, `urls`, `cashtags`
- `media` — photo/video/GIF objects
- `quotedTweet`, `quotedTweetId`, `poll`, `card`
- `possiblySensitive`, `place`, `postedFrom`
- `author` — author snapshot; `username` — canonical author handle on the row
- `type: "post"`, `collectedAt`

Depending on which public X view a post was read from, it may also carry `withheldInCountries`, `isEdited`, `editControl`, `newsActionType` and `parentTweet` (the post it replies to).

The pinned post is collected separately: when it passes your filters but wasn't already among the collected posts, it is added as an extra row, so a profile can return one more post than `maxPostsPerProfile`.

**Media item:** `type` (`photo` / `video` / `gif`), `url`, `previewImageUrl`, `width`, `height`, `durationSeconds`, `viewCount`, `altText`, `variants`, and where X provides them `mediaKey`, `contentType`, `bitrate`, `allowDownload`, `mediaAvailability`, `focusRects`.

#### Sample profile record

```json
{
  "type": "profile",
  "id": "44196397",
  "username": "elonmusk",
  "name": "Elon Musk",
  "bio": "https://t.co/ZdBx5WABYx",
  "bioLinks": ["http://terafab.ai"],
  "website": "http://terafab.ai",
  "joinedAt": "2009-06-02T20:12:29+00:00",
  "followers": 241717759,
  "following": 1413,
  "postCount": 109515,
  "isBlueVerified": true,
  "avatarUrl": "https://pbs.twimg.com/profile_images/...jpg",
  "pinnedPostId": "2106665276758491194",
  "pinnedTweetId": "2106665276758491194",
  "collectedAt": "2026-10-06T00:00:00+00:00"
}
```

#### Sample post record

```json
{
  "type": "post",
  "id": "2107495983328464979",
  "url": "https://x.com/i/status/2107495983328464979",
  "text": "Hello world",
  "createdAt": "2026-10-04T08:38:36+00:00",
  "language": "en",
  "replyCount": 4070,
  "retweetCount": 4161,
  "likeCount": 91635,
  "quoteCount": 762,
  "bookmarkCount": 7462,
  "viewCount": 62683289,
  "isReply": false,
  "isRetweet": false,
  "isPinned": false,
  "conversationId": "2107495983328464979",
  "threadRootId": "2107495983328464979",
  "threadPosition": 0,
  "engagementRate": 0.000413,
  "username": "elonmusk",
  "collectedAt": "2026-10-06T00:00:00+00:00"
}
```

### Output per status row

A `status` row explains a skipped or failed account. It always has a `message`:

- `type: "status"`
- `status` — `noInput`, `invalidUsername`, `notFound`, `rateLimited`, `skipped`, `protected`, `repliesLimited`, `aboutUnavailable`, `noMediaPosts`, `noPosts`, `timelineUnavailable` or `error`
- `username`, `url`, `collectedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `usernames` | array | – | **Required.** Handles (`elonmusk`, `@apify`) or profile links (`https://x.com/elonmusk`). Mixed input is fine. |
| `include` | array | `["profileInfo", "latestPosts"]` | What to collect: `profileInfo`, `latestPosts`, `topPosts`, `media`, `about`. Any of the three post groups turns on post collection (topped up with older posts up to the target); `media` also keeps only posts with media. With only `profileInfo`, no posts are collected. |
| `maxPostsPerProfile` | integer | `50` | Post target per account (1–1000). All collected posts are merged, de-duplicated, filtered and capped at this number (plus the pinned post, see above). |
| `includeReplies` | boolean | `false` | Also collect replies the account sent; when off, replies are dropped from every post group. May be empty when X does not serve a reply timeline publicly. |
| `includeRetweets` | boolean | `false` | Keep retweets in the results. |
| `dateFrom` | string | – | Only keep posts published on or after this date. |
| `dateTo` | string | – | Only keep posts published on or before this date. |
| `language` | string | – | Keep only posts X tagged with this language code, e.g. `en`, `es`, `ja` (exact match). |
| `minimumLikes` | integer | – | Drop posts with fewer likes. |
| `minimumRetweets` | integer | – | Drop posts with fewer retweets. |
| `minimumReplies` | integer | – | Drop posts with fewer replies. |
| `onlyWithMedia` | boolean | `false` | Keep only posts with photos, videos or GIFs. |
| `onlyVerified` | boolean | `false` | Skip accounts without a verified badge (a `skipped` status row is written instead). |
| `proxyConfiguration` | object | – | Optional. Helps on large runs or repeated runs from the same IP. |

#### Example: profile info plus latest and top posts

```json
{
  "usernames": ["elonmusk", "https://x.com/apify"],
  "include": ["profileInfo", "latestPosts", "topPosts"],
  "maxPostsPerProfile": 100
}
```

#### Example: high-engagement media posts only

```json
{
  "usernames": ["NASA"],
  "include": ["profileInfo", "latestPosts", "media"],
  "onlyWithMedia": true,
  "minimumLikes": 500,
  "dateFrom": "2026-01-01"
}
```

#### Example: replies and retweets included

```json
{
  "usernames": ["apify"],
  "include": ["profileInfo", "latestPosts"],
  "includeReplies": true,
  "includeRetweets": true,
  "maxPostsPerProfile": 300
}
```

### Use cases

- **Influencer and competitor research** — compare followers, engagement and posting cadence across accounts
- **Brand monitoring** — track an account's latest posts and high-engagement highlights over time
- **Audience intelligence** — profile bios, locations and links for market segmentation
- **Media analysis** — export posts with media and metrics into dashboards
- **Recruiting and sourcing** — build structured records from lists of handles
- **Academic research** — archive public profiles and posts with timestamps

### FAQ

**Do I need an X account, login or developer key?**  No. The actor reads the same public profile pages any visitor sees without signing in, and there is no credential input at all.

**Why do I get fewer posts than `maxPostsPerProfile`?**  X serves a limited number of posts to logged-out visitors (roughly 5–22 latest posts and about 100 top posts per account). The actor fills the target with older posts when X exposes them and reports the achieved count; rows are never invented.

**Why are replies sometimes missing?**  X only serves a profile's reply timeline publicly on some attempts. The actor retries and reports `repliesLimited` when X refuses; everything else on the profile is still collected.

**Can I collect private or protected profiles?**  No. Protected accounts return their public header plus a `protected` status row.

**What is the `about` option?**  X's account analytics panel is login-only. Selecting `about` adds an `aboutUnavailable` status row explaining that; location and bio are still collected from the public profile.

**Will the account being scraped get banned?**  No. The actor never signs in and only makes public requests, so the profile owner is not affected.

**How fresh is the data?**  Live — every run reads X at request time. Schedule the actor for hourly, daily or weekly refreshes.

**How do I integrate the results?**  Use the dataset from the run page, the Apify API, webhooks, or a no-code connection such as Zapier, Make or Google Sheets.

**Is this affiliated with X?**  No. This is a third-party actor using publicly available profile pages.

# Actor input Schema

## `usernames` (type: `array`):

Accounts to collect. Paste handles like `elonmusk`, `@apify`, or full links like `https://x.com/elonmusk`. Handles and links can be mixed.

## `include` (type: `array`):

Pick one or more data groups. Everything works without cookies; X's login-only About panel is reported as unavailable.

## `maxPostsPerProfile` (type: `integer`):

Target number of posts per account (1-1000). The actor merges the profile timeline, top posts, the replies timeline and profile-scoped search windows, then de-duplicates by post id before applying this cap. High values are filled with older posts when X search serves logged-out callers.

## `includeReplies` (type: `boolean`):

Also collect replies the account sent. Uses the guest UserTweetsAndReplies burst plus profile-scoped `filter:replies` search windows (may be empty when X refuses logged-out callers).

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

Keep retweets in the results. Turn off to get only posts the account wrote itself.

## `dateFrom` (type: `string`):

Optional. Only keep posts published on or after this date.

## `dateTo` (type: `string`):

Optional. Only keep posts published on or before this date.

## `language` (type: `string`):

Optional. Keep only posts detected in this language, e.g. `en`, `es`, `ja`. Leave empty for all languages.

## `minimumLikes` (type: `integer`):

Optional. Drop posts with fewer likes than this.

## `minimumRetweets` (type: `integer`):

Optional. Drop posts with fewer retweets than this.

## `minimumReplies` (type: `integer`):

Optional. Drop posts with fewer replies than this.

## `onlyWithMedia` (type: `boolean`):

Keep only posts that contain photos, videos or GIFs.

## `onlyVerified` (type: `boolean`):

Skip accounts that do not carry a verified badge.

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

Optional. Use a proxy if you plan large runs or see frequent rate limits.

## Actor input object example

```json
{
  "usernames": [
    "elonmusk",
    "https://x.com/apify"
  ],
  "include": [
    "profileInfo",
    "latestPosts"
  ],
  "maxPostsPerProfile": 50,
  "includeReplies": false,
  "includeRetweets": false,
  "onlyWithMedia": false,
  "onlyVerified": false
}
```

# Actor output Schema

## `profiles` (type: `string`):

One profile row per account plus one row per post and any status rows.

# 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 = {
    "usernames": [
        "elonmusk"
    ],
    "include": [
        "profileInfo",
        "latestPosts"
    ],
    "includeReplies": false,
    "includeRetweets": false,
    "onlyWithMedia": false,
    "onlyVerified": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("nimbryxo-studio/x-twitter-profile-and-post-engagement-extractor").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 = {
    "usernames": ["elonmusk"],
    "include": [
        "profileInfo",
        "latestPosts",
    ],
    "includeReplies": False,
    "includeRetweets": False,
    "onlyWithMedia": False,
    "onlyVerified": False,
}

# Run the Actor and wait for it to finish
run = client.actor("nimbryxo-studio/x-twitter-profile-and-post-engagement-extractor").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 '{
  "usernames": [
    "elonmusk"
  ],
  "include": [
    "profileInfo",
    "latestPosts"
  ],
  "includeReplies": false,
  "includeRetweets": false,
  "onlyWithMedia": false,
  "onlyVerified": false
}' |
apify call nimbryxo-studio/x-twitter-profile-and-post-engagement-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nimbryxo-studio/x-twitter-profile-and-post-engagement-extractor"
        }
    }
}
```

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/Sf31Jgh0Kbad3YVuJ/builds/GFdtGdd08OmOsClGR/openapi.json
