# X (Twitter) followers API

Platform: X (Twitter).

Call `GET /v1/twitter/profiles/{identifier}/followers` with a handle or a user ID. Each request returns one page of follower profiles and a cursor for the next page.

You pay one price per page, however many followers it holds. You do not need an X developer account.

## GET /v1/twitter/profiles/{identifier}/followers

```bash
curl "https://api.openhandle.dev/v1/twitter/profiles/@paper_signal_x_test/followers" \
  -H "Authorization: Bearer $OPENHANDLE_API_KEY"
```

```ts
import { OpenHandle } from '@openhandle/sdk';

const openhandle = new OpenHandle({ apiKey: process.env.OPENHANDLE_API_KEY! });
let page = await openhandle.twitter.profile('@paper_signal_x_test').followers.list();

while (page) {
    for (const follower of page.data) console.log(follower.handle, follower.metrics.followers);
    page = await page.next();
}
```

200 OK:

```json
{
  "platform": "twitter",
  "resource": "profile",
  "capturedAt": "2026-10-27T09:18:25Z",
  "source": "live",
  "data": [
    {
      "id": "940000000001",
      "handle": "copperfield_lab_x_test",
      "displayName": "Copperfield Lab",
      "url": "https://x.com/copperfield_lab_x_test",
      "isPrivate": false,
      "isVerified": true,
      "metrics": { "followers": 32840, "following": 184, "posts": 912 }
    }
  ],
  "meta": { "cursors": { "next": "b3BhcXVlLWN1cnNvcg" } }
}
```

Trimmed to one follower and the fields most apps need. Each follower is a full profile. The example is a Test account, so you can run it on a free Test key.

## What you get

- **Follower profiles.** Each follower comes back as a profile with `id`, `handle`, `displayName`, `url`, and verification.
- **Counts.** Each follower has its own follower, following, and post counts in `metrics`.
- **One page per request.** Pass `meta.cursors.next` back as `cursor` to get the next page. When it is `null`, there are no more pages.
- **Following.** Call `GET /v1/twitter/profiles/{identifier}/following` for the accounts this account follows.

## Price

- **Per request.** $0.0025 per live request. $0.002 per request after 100,000 requests in a month. One page of results is one request.
- **Older data costs less.** Set `freshness` to say how old the data may be. Data up to 24 hours old: $0.0005. Data up to 7 days old: $0.0001. Data up to 30 days old: free.
- **Failed means free.** Errors and invalid requests are never billed. You pay for answers, and a confirmed not-found is an answer.
- **Free to start.** Every new workspace starts with 100 free live requests, no card needed. Test keys are never charged.

## When this does not work

These answers are definitive. Store them, do not retry them.

| Situation | Status | Code |
|---|---|---|
| The account is protected | 403 | `PROFILE_PRIVATE` |
| The account is deleted, suspended, or never existed | 404 | `PROFILE_NOT_FOUND` |
| The cursor came from another request | 400 | `CURSOR_MISMATCH` |

```json
{
  "error": {
    "code": "PROFILE_PRIVATE",
    "message": "This profile is private.",
    "requestId": "req_01j8…",
    "retryable": false
  }
}
```

An account can turn on protection while you page. Handle `PROFILE_PRIVATE` on any page, not only the first.

## Related

- [X (Twitter) API](https://openhandle.dev/apis/twitter): Every X endpoint with examples.
- [List profile followers](https://openhandle.dev/docs/api-reference/twitter-profile-followers-list): The endpoint reference with every parameter and SDK.
- [Is the X (Twitter) API free to read posts?](https://openhandle.dev/questions/is-the-x-twitter-api-free-to-read-posts): What the official API costs and what you can use instead.
