# X / Twitter API for agents

> API for Agents · https://apiforagents.dev/platforms/x_twitter · HTML twin: https://apiforagents.dev/platforms/x_twitter

Profiles, posts and search from X, formerly Twitter.

Endpoint: `POST https://apiforagents.dev/v1/x_twitter`. One op per call. Quote first with `"estimateOnly": true`.

## Prices (USD per record, pay as you go)

| Record | Per record |
|---|---|
| followers | $0.000225 |
| replies | $0.000225 |
| retweeters | $0.000225 |
| tweets | $0.000225 |
| profiles | $0.00045 |

## Operations

### profiles

Profile cards for up to ten accounts, by @handle or pasted profile URL. `include.posts` adds each account's recent posts to the same answer.

| Field | Required | Type | Meaning |
|---|---|---|---|
| subjects | yes | list of string (1-10 items) | @handles or profile URLs. |
| include | no | object |  |
| include.posts | no | object |  |
| include.posts.limit | no | integer (1-50) | Recent posts to fetch per account, newest first; 1-50. |

Response fields (each row of data; every field present, null when the source had nothing):

| Field | Type |
|---|---|
| username | string |
| accountId | string |
| displayName | string |
| bio | string |
| location | string |
| website | string |
| avatarUrl | string |
| bannerUrl | string |
| isProtected | boolean |
| isBlueVerified | boolean |
| verifiedType | string |
| accountType | string |
| joinedAt | string |
| pinnedPostUrl | string |
| followers | integer |
| following | integer |
| postsCount | integer |
| mediaCount | integer |
| likesGiven | integer |
| canonicalUsername | string |
| details | object |

`details` is always the last field: everything else the source sent for that row, under our own field names. Its keys depend on the source that answered, so read them defensively; it is `{}` when there is nothing more.

Example request:

```bash
curl -X POST https://apiforagents.dev/v1/x_twitter \
  -H "Authorization: Bearer $AFA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"op":"profiles","subjects":["@NASA"]}'
```

Example response rows (made-up names, real field names; a live row carries every field above):

```json
[
  {
    "username": "northfoldhq",
    "displayName": "Northfold",
    "bio": "Close your books in days, not weeks.",
    "location": "London",
    "isBlueVerified": true,
    "accountType": "business",
    "joinedAt": "2020-06-01",
    "followers": 24100,
    "following": 410,
    "postsCount": 3812,
    "accountId": "1354972458",
    "avatarUrl": "https://img.x.com/northfold.jpg",
    "bannerUrl": "https://img.x.com/northfold.jpg",
    "isProtected": false,
    "pinnedPostUrl": "https://x.com/northfold-1000",
    "details": {}
  },
  {
    "username": "ananya_builds",
    "displayName": "Ananya Rao",
    "bio": "Marketing @paylane. Notes on growth.",
    "location": "Bengaluru",
    "isBlueVerified": false,
    "accountType": "person",
    "joinedAt": "2014-02-18",
    "followers": 8920,
    "following": 690,
    "postsCount": 5210,
    "accountId": "1119122908",
    "avatarUrl": "https://img.x.com/ananya-rao.jpg",
    "bannerUrl": "https://img.x.com/ananya-rao.jpg",
    "isProtected": false,
    "pinnedPostUrl": "https://x.com/ananya-rao-1001",
    "details": {}
  }
]
```

Try it in the playground: https://apiforagents.dev/dashboard/run?platform=x_twitter&op=profiles (sign in first).

### posts

One account's recent posts with engagement. Includes the author's profile card when held.

| Field | Required | Type | Meaning |
|---|---|---|---|
| subject | yes | string (≤400 chars) | One @handle or profile URL. |
| limit | no | integer (1-50) |  |

Response fields (each row of data; every field present, null when the source had nothing):

| Field | Type |
|---|---|
| postId | string |
| body | string |
| kind | string |
| postedAt | string |
| permalink | string |
| language | string |
| mediaUrl | string |
| likes | integer |
| replies | integer |
| reposts | integer |
| quotes | integer |
| views | integer |
| bookmarks | integer |
| isReply | boolean |
| isQuote | boolean |
| isPinned | boolean |
| replyToHandle | string |
| quotedPostUrl | string |
| repostedPostUrl | string |
| hashtags | list of string |
| mentions | list of string |
| authorUsername | string |
| authorId | string |
| author | object |
| conversationId | string |
| media | list of object |
| outboundLinks | list of string |
| details | object |

`details` is always the last field: everything else the source sent for that row, under our own field names. Its keys depend on the source that answered, so read them defensively; it is `{}` when there is nothing more.

Example request:

```bash
curl -X POST https://apiforagents.dev/v1/x_twitter \
  -H "Authorization: Bearer $AFA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"op":"posts","subject":"@NASA","limit":20}'
```

Example response rows (made-up names, real field names; a live row carries every field above):

```json
[
  {
    "authorUsername": "northfoldhq",
    "body": "Month-end close in 2 days, not 10. Here's how teams do it ↓",
    "kind": "post",
    "postedAt": "2026-10-14T13:00:00Z",
    "views": 84200,
    "likes": 1210,
    "reposts": 240,
    "replies": 61,
    "quotes": 18,
    "isReply": false,
    "replyToHandle": null,
    "postId": "2327853664",
    "permalink": "https://x.com/month-end-close-in-2-days-not-10-here-s--1000",
    "mediaUrl": "https://x.com/month-end-close-in-2-days-not-10-here-s--1000",
    "isQuote": false,
    "isPinned": false,
    "quotedPostUrl": "https://x.com/month-end-close-in-2-days-not-10-here-s--1000",
    "repostedPostUrl": "https://x.com/month-end-close-in-2-days-not-10-here-s--1000",
    "authorId": "804340720",
    "details": {}
  },
  {
    "authorUsername": "northfoldhq",
    "body": "We're at Finance Leaders London next week. Come say hi!",
    "kind": "post",
    "postedAt": "2026-10-10T09:00:00Z",
    "views": 12100,
    "likes": 140,
    "reposts": 21,
    "replies": 9,
    "quotes": 1,
    "isReply": false,
    "replyToHandle": null,
    "postId": "482952772",
    "permalink": "https://x.com/we-re-at-finance-leaders-london-next-wee-1001",
    "mediaUrl": "https://x.com/we-re-at-finance-leaders-london-next-wee-1001",
    "isQuote": false,
    "isPinned": false,
    "quotedPostUrl": "https://x.com/we-re-at-finance-leaders-london-next-wee-1001",
    "repostedPostUrl": "https://x.com/we-re-at-finance-leaders-london-next-wee-1001",
    "authorId": "1870987843",
    "details": {}
  }
]
```

Try it in the playground: https://apiforagents.dev/dashboard/run?platform=x_twitter&op=posts (sign in first).

### search

Find ACCOUNTS by keyword. A name, a topic, a bio phrase.

| Field | Required | Type | Meaning |
|---|---|---|---|
| query | yes | string (≤300 chars) |  |
| limit | no | integer (1-25) |  |

Response fields (each row of data; every field present, null when the source had nothing):

| Field | Type |
|---|---|
| username | string |
| accountId | string |
| displayName | string |
| bio | string |
| location | string |
| website | string |
| avatarUrl | string |
| bannerUrl | string |
| isProtected | boolean |
| isBlueVerified | boolean |
| verifiedType | string |
| accountType | string |
| joinedAt | string |
| pinnedPostUrl | string |
| followers | integer |
| following | integer |
| postsCount | integer |
| mediaCount | integer |
| likesGiven | integer |
| canonicalUsername | string |
| details | object |

`details` is always the last field: everything else the source sent for that row, under our own field names. Its keys depend on the source that answered, so read them defensively; it is `{}` when there is nothing more.

Example request:

```bash
curl -X POST https://apiforagents.dev/v1/x_twitter \
  -H "Authorization: Bearer $AFA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"op":"search","query":"climate scientist","limit":10}'
```

Example response rows (made-up names, real field names; a live row carries every field above):

```json
[
  {
    "username": "pilates_daily",
    "displayName": "Pilates Daily",
    "bio": "One pilates tip a day.",
    "location": "Sydney",
    "isBlueVerified": false,
    "accountType": "person",
    "joinedAt": "2019-01-03",
    "followers": 41200,
    "following": 310,
    "postsCount": 2210,
    "accountId": "519961039",
    "avatarUrl": "https://img.x.com/pilates-daily.jpg",
    "bannerUrl": "https://img.x.com/pilates-daily.jpg",
    "isProtected": false,
    "pinnedPostUrl": "https://x.com/pilates-daily-1000",
    "details": {}
  },
  {
    "username": "mira_moves",
    "displayName": "Mira | Pilates",
    "bio": "Classes on YouTube, links below.",
    "location": "Mumbai",
    "isBlueVerified": true,
    "accountType": "person",
    "joinedAt": "2018-05-21",
    "followers": 96400,
    "following": 120,
    "postsCount": 4120,
    "accountId": "2620162896",
    "avatarUrl": "https://img.x.com/mira-pilates.jpg",
    "bannerUrl": "https://img.x.com/mira-pilates.jpg",
    "isProtected": false,
    "pinnedPostUrl": "https://x.com/mira-pilates-1001",
    "details": {}
  }
]
```

Try it in the playground: https://apiforagents.dev/dashboard/run?platform=x_twitter&op=search (sign in first).

### searchPosts

Find POSTS by keyword. What X is saying about something. The content itself, across all authors, newest-relevant first.

| Field | Required | Type | Meaning |
|---|---|---|---|
| query | yes | string (≤300 chars) |  |
| limit | no | integer (1-50) |  |

Response fields (each row of data; every field present, null when the source had nothing):

| Field | Type |
|---|---|
| postId | string |
| body | string |
| kind | string |
| postedAt | string |
| permalink | string |
| language | string |
| mediaUrl | string |
| likes | integer |
| replies | integer |
| reposts | integer |
| quotes | integer |
| views | integer |
| bookmarks | integer |
| isReply | boolean |
| isQuote | boolean |
| isPinned | boolean |
| replyToHandle | string |
| quotedPostUrl | string |
| repostedPostUrl | string |
| hashtags | list of string |
| mentions | list of string |
| authorUsername | string |
| authorId | string |
| author | object |
| conversationId | string |
| media | list of object |
| outboundLinks | list of string |
| details | object |

`details` is always the last field: everything else the source sent for that row, under our own field names. Its keys depend on the source that answered, so read them defensively; it is `{}` when there is nothing more.

Example request:

```bash
curl -X POST https://apiforagents.dev/v1/x_twitter \
  -H "Authorization: Bearer $AFA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"op":"searchPosts","query":"electric cars","limit":25}'
```

Example response rows (made-up names, real field names; a live row carries every field above):

```json
[
  {
    "authorUsername": "fintech_weekly",
    "body": "Three fintechs raised $100M+ this week. Thread 🧵",
    "kind": "post",
    "postedAt": "2026-10-15T11:00:00Z",
    "views": 210000,
    "likes": 3400,
    "reposts": 880,
    "replies": 140,
    "quotes": 96,
    "postId": "989531282",
    "permalink": "https://x.com/three-fintechs-raised-100m-this-week-thr-1000",
    "mediaUrl": "https://x.com/three-fintechs-raised-100m-this-week-thr-1000",
    "isReply": false,
    "isQuote": false,
    "isPinned": false,
    "quotedPostUrl": "https://x.com/three-fintechs-raised-100m-this-week-thr-1000",
    "repostedPostUrl": "https://x.com/three-fintechs-raised-100m-this-week-thr-1000",
    "authorId": "2124078388",
    "details": {}
  },
  {
    "authorUsername": "marcus_sells",
    "body": "Fintech funding is back, but only for revenue.",
    "kind": "post",
    "postedAt": "2026-10-14T08:20:00Z",
    "views": 41000,
    "likes": 920,
    "reposts": 110,
    "replies": 64,
    "quotes": 12,
    "postId": "2595211473",
    "permalink": "https://x.com/fintech-funding-is-back-but-only-for-rev-1001",
    "mediaUrl": "https://x.com/fintech-funding-is-back-but-only-for-rev-1001",
    "isReply": false,
    "isQuote": false,
    "isPinned": false,
    "quotedPostUrl": "https://x.com/fintech-funding-is-back-but-only-for-rev-1001",
    "repostedPostUrl": "https://x.com/fintech-funding-is-back-but-only-for-rev-1001",
    "authorId": "4058817374",
    "details": {}
  }
]
```

Try it in the playground: https://apiforagents.dev/dashboard/run?platform=x_twitter&op=searchPosts (sign in first).

### history (free)

The stored time series. Free, reads only what captures already wrote.

| Field | Required | Type | Meaning |
|---|---|---|---|
| subjects | yes | list of string (1-10 items) | @handles or profile URLs. |
| metric | no | enum |  Values: followers, following, posts_count. |
| days | no | integer (1-730) |  |

Request shape:

```json
{
  "op": "history",
  "subjects": [
    "example"
  ]
}
```

Try it in the playground: https://apiforagents.dev/dashboard/run?platform=x_twitter&op=history (sign in first).

## Notes for the agent

Measured data from X. The platform formerly known as Twitter (x.com / twitter.com). One operation per call. Handles come from evidence (a pasted x.com URL, a handle you SAW, a prior answer). Never typed from a person's name. The blue badge is a paid subscription and is reported as `isBlueVerified`, never as verification. ops: profiles (batch of @handles or profile URLs; add include.posts to get each account's recent posts IN THE SAME CALL), posts (one account, deep. Likes, replies, reposts, quotes, views, bookmarks and the reply/quote lineage per post; includes the author card when held), search (find ACCOUNTS by keyword), searchPosts (find POSTS by keyword. What X is saying about something, across authors), history (stored follower/following/post-count series, free). Ops are independent. Issue several as PARALLEL tool calls. Fresh fetches are charged per record; your own answer is free while fresh.
