# Twitter Profile Scraper - Accounts by Handle (`toolzerhub/twitter-profile-scraper`) Actor

Turn a list of Twitter (X) handles or profile links into rows with name, bio, location, website, follower and post counts, verification and join date. Tick one box to add the About panel: verification date, country, handle changes and affiliation.

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

## Pricing

from $2.69 / 1,000 results

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

## Twitter Profile Scraper — Accounts by Handle

Turn a list of Twitter (X) handles or profile links into one row per account: name, bio, location, website, follower and post counts, verification and join date. Tick one box to add the About panel — when the verification mark was granted, the country the account is based in, handle changes and affiliation.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| **`handles`** | list of text | — | **Required.** Handles (`elonmusk`, `@elonmusk`) or profile links (`https://x.com/elonmusk`). One list can mix all three. |
| **`addonAbout`** | boolean | `false` | Adds the About panel to every row. One extra lookup per account, billed separately. |
| **`maxItems`** | integer | `100` | Caps the run. `0` means no limit. |

```json
{
  "handles": ["elonmusk", "https://x.com/X"],
  "addonAbout": false,
  "maxItems": 100
}
```

A handle X would not accept — more than 15 characters, or anything but letters, digits and underscores — is left out before any lookup and named in the log. The same account listed twice, as `X` and as a link, is one lookup.

### Output

One row per account.

| Field | What it holds |
|---|---|
| **`id`** | The account's ID. It survives a handle change. |
| **`handle`**, **`name`**, **`url`** | Current handle in X's casing, display name, profile link |
| **`description`**, **`location`**, **`website`** | Bio, the free text typed into the location box, the profile link |
| **`avatar_url`**, **`banner_url`** | Image URLs. The banner is null when none is set. |
| **`followers_count`**, **`following_count`**, **`posts_count`**, **`media_count`**, **`likes_count`** | The five counters |
| **`is_blue_verified`**, **`is_verified`**, **`verified_type`** | Current mark, legacy check, and a category such as `Business` or `Government` |
| **`protected`**, **`is_automated`** | Posts limited to approved followers, and the automated-account label |
| **`created_at`** | When the account was created, ISO 8601 UTC |
| **`pinned_post_ids`** | IDs of pinned posts |
| **`input_handle`** | The handle you gave, reduced to the bare handle |

```json
{
  "id": "783214",
  "handle": "X",
  "name": "X",
  "description": "what's happening?!",
  "location": "everywhere",
  "website": "https://x.com",
  "avatar_url": "https://pbs.twimg.com/profile_images/1955359038532653056/OSHY3ewP_normal.jpg",
  "banner_url": "https://pbs.twimg.com/profile_banners/783214/1690175171",
  "followers_count": 60673821,
  "following_count": 1,
  "posts_count": 15732,
  "media_count": 2492,
  "likes_count": 5969,
  "is_verified": false,
  "is_blue_verified": true,
  "verified_type": "Business",
  "protected": false,
  "created_at": "2007-02-20T14:35:54.000Z",
  "pinned_post_ids": [],
  "is_automated": false,
  "url": "https://x.com/X",
  "input_handle": "X"
}
```

With **`addonAbout`** on, each row also carries these six fields. They are null when the box is off:

| Field | What it holds |
|---|---|
| **`verified_since`** | When the verification mark was first granted |
| **`is_identity_verified`** | Whether X has verified the owner's identity |
| **`account_based_in`** | The country X shows in the About panel |
| **`connected_via`** | The store or channel the account was created through |
| **`username_changes_count`** | How many times the handle has changed |
| **`affiliation`** | The organization named in an affiliate badge |

```json
{
  "handle": "NASA",
  "verified_since": "2009-08-07T19:53:50.941Z",
  "is_identity_verified": false,
  "account_based_in": null,
  "connected_via": null,
  "username_changes_count": 0,
  "affiliation": null
}
```

### Pricing

You pay for each account saved. An account that does not exist is skipped and never charged.

| Plan | Per 1,000 accounts |
|---|---|
| Free | $5.00 |
| Bronze | $4.23 |
| Silver | $3.46 |
| Gold and above | $2.69 |

The About add-on is billed on top, per account, and only when X returns the panel:

| Plan | Per 1,000 About lookups |
|---|---|
| Free | $5.00 |
| Bronze | $4.23 |
| Silver | $3.46 |
| Gold and above | $2.69 |

Each run also carries a $0.00005 start charge. The Free plan covers the first 50 rows you save with this Actor, and the About add-on needs a paid plan.

### Questions

**`is_verified` is false but `is_blue_verified` is true. Which one do I read?**
`is_blue_verified`. `is_verified` is the pre-2023 legacy flag, and it reads false on many accounts that carry the current mark — like the one above. `verified_type` adds a category where X gives one.

**Why is `description` null, or just a link?**
X returns whatever the account typed. Some accounts have no bio, and some have only a short link. The field is passed through as it is.

**Can I look accounts up by numeric ID?**
No — handles and profile links only. Every row carries `id`, so you can store it and still find the account after a rename.

**What happens to a handle that does not exist?**
No row, no charge, and a line in the log. The rest of the list carries on.

**Why does `following_count` read 1 for an account with millions of followers?**
Because it is the number of accounts that profile follows. It is unrelated to its audience. For the people behind the counts, use the Followers Scraper.

**Are the counts exact?**
They are live. Two runs minutes apart will differ on followers and posts.

### Related Actors

| Actor | Use it when |
|---|---|
| [Twitter Followers Scraper](https://apify.com/toolzerhub/twitter-followers-scraper) | You want the accounts behind the follower count |
| [Twitter Tweets Scraper](https://apify.com/toolzerhub/twitter-tweets-scraper) | You want what these accounts have posted |
| [Twitter Scraper](https://apify.com/toolzerhub/twitter-scraper) | You want to find accounts by what they tweet |

### Support

Questions, bugs, or feature requests: **contact@toolzerhub.com**

Browse the rest: [apify.com/toolzerhub](https://apify.com/toolzerhub)

# Actor input Schema

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

One or more accounts. A handle (`openai`), an @handle (`@openai`) or a profile link (`https://x.com/openai`) all work, and one list can mix them.

## `addonAbout` (type: `boolean`):

Add the About panel to every account: when verification was granted, the country it is based in, how it was created, handle changes and affiliation. One extra lookup per account.

## `maxItems` (type: `integer`):

Most rows to save in this run, across everything in the list. Set 0 for no limit.

## Actor input object example

```json
{
  "handles": [
    "openai"
  ],
  "addonAbout": false,
  "maxItems": 50
}
```

# Actor output Schema

## `dataset` (type: `string`):

Every record collected during this run

# 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": [
        "openai"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("toolzerhub/twitter-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": ["openai"],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("toolzerhub/twitter-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": [
    "openai"
  ],
  "maxItems": 50
}' |
apify call toolzerhub/twitter-profile-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,toolzerhub/twitter-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/CMbBz9cK8zyrVeWfe/builds/gWEmnLTYQSRzSfhnB/openapi.json
