# Twitter Search API: 20/Page + Full Long Posts (https://www.socialcrawl.dev/blog/twitter-api-pagination)
> Twitter search API returned 20 Artemis hits/page; 12 of 20 past 280 chars (max 1,173). Cursor timelines, replies, followers — live curl + JSON, 2026-09-04.
A Twitter search API call against SocialCrawl returns about **20 keyword matches per page**, with `sort=latest` or `sort=top`, and long posts come back as **full bodies** — not a 280-character clamp. On 2026-09-04 we searched `NASA Artemis` (`sort=latest`) and got **20** hits; **12 of 20** exceeded 280 characters, and the longest (`@NASAStennis`) was **1,173**.
The same API key also cursor-pages user timelines, tweet replies, retweeters, followers, following, the media tab, and account search — each at **1 credit per page** (account search is a fixed single page). Everything below is real curl plus trimmed JSON from that harvest against `@nasa` and a live NASA spacewalk thread. Ten successful calls, **10 credits**.
This is a measured walkthrough — not the [X API pricing field guide](/blog/x-twitter-api-2026), not the [scrape-from-scratch tutorial](/blog/how-to-scrape-twitter-2026), and not a multi-provider matrix.
Keyword search pages at ~20 results with `sort=latest|top` and returns full long-post bodies (12/20 Artemis hits over 280 chars this pull; max 1,173). User timelines, replies, retweeters, followers, following, and media also cursor-page at 1 credit/page. Page sizes below are observed on 2026-09-04, not contracted guarantees.
## What can one API key pull from X right now?
The September 2026 X wave added seven new reads, and user timelines now paginate. Observed page sizes from this harvest:
| Endpoint | Page size this pull | Cursor? |
|---|---:|---|
| `/v1/twitter/user/tweets` | 13 | yes |
| `/v1/twitter/search/tweets` | 20 | yes |
| `/v1/twitter/tweet/replies` | 36 | yes |
| `/v1/twitter/tweet/retweeters` | 20 | yes |
| `/v1/twitter/user/followers` | 70 | yes |
| `/v1/twitter/user/following` | 67 | yes |
| `/v1/twitter/user/media` | 19 | yes |
| `/v1/twitter/search/users` | 10 | **no** |
Billing is uniform: **1 credit per page**. Empty-reply tweets are refunded (`credits_used: 0`). We did not hit that path this run — the parent we picked had replies.
Page sizes are source-set and can drift; cite them as "this pull," not as hard guarantees. A previously measured walk of `@nasa` returned **174 posts over 9 pages**; this harvest proves the cursor mechanism over two pages, not that full walk.
Honest field gaps on this surface: follower / following / retweeter rows carry `verified: null` (not `false`); media-tab posts omit view and bookmark counts; account search returns no follower counts.
## Does the Twitter search API support latest and top results?
Yes. `GET /v1/twitter/search/tweets` takes a `query` string and `sort=latest|top`. Operators such as `since:`, `from:`, and quoted phrases live inside `query` — matching X's documented [search operators](https://docs.x.com/x-api/posts/search/integrate/build-a-query).
```bash
curl -s -H "x-api-key: $SOCIALCRAWL_API_KEY" \
"https://www.socialcrawl.dev/v1/twitter/search/tweets?query=NASA%20Artemis&sort=latest"
```
Trimmed response from the 2026-09-04 harvest (`req-A75WeV786f4GThYw`, 1 credit). The live envelope is `{ success, data: { items }, pagination }` — `pagination` sits **next to** `data`, not inside it. Page had **20** items, `has_more=true`. Featured long post from that page (`@NASAStennis`, full body on `post.content.text`):
```json
{
"success": true,
"data": {
"items": [
{
"post": {
"id": "2095617131295486161",
"url": "https://x.com/NASAStennis/status/2095617131295486161",
"published_at": "2026-09-03T20:57:13.000Z",
"author": { "username": "NASAStennis" },
"engagement": {
"views": 986,
"likes": 35,
"comments": 1,
"shares": 7,
"saves": 2
},
"content": {
"text": "Artemis II crew brings Moon Joy to NASA Stennis! 🚀\n\nNASA astronauts Reid Wiseman, Victor Glover, Christina Koch, and Canadian Space Agency astronaut Jeremy Hansen visited us on Thursday, Sept. 3, to celebrate the historic @NASAArtemis II test flight around the Moon.\n\nAs the first astronauts to fly aboard NASA’s Orion spacecraft, the crew traveled farther in space than humans have ever before.\n\nDuring their visit to Stennis, they stopped at the Fred Haise Test Stand, where rigorous RS-25 engine testing ensures our astronauts fly safely on NASA’s SLS rocket.\n\nThe crew thanked everyone for tackling hard challenges and reminded us during an employee event: “You put humans in space.”\n\nToday provided a great reminder for all: Whether you work at America’s largest rocket propulsion test site or look up at the Moon from your backyard, you are a part of America’s return to the Moon.\n\nAnd the Artemis II mission is just the beginning!\n\nNext comes Artemis III with a mission intended to demonstrate integrated operations between the Orion spacecraft and test versions of commercial human landing systems before Artemis IV returns astronauts to the lunar surface."
}
}
}
]
},
"pagination": { "has_more": true, "page_size": 20, "next_cursor": "…" },
"credits_used": 1
}
```
`sort=latest` is chronological; `sort=top` ranks by engagement. The important part for RAG ingest and monitoring: long posts come back in full — not truncated to 280. SocialCrawl puts the full body on `post.content.text`. We counted **1,173** characters on that Stennis post (`String.length` / UTF-16); there is no `text_length` field on the wire.
Text lengths on this Artemis page: 224, 302, 117, 330, 1137, 299, 285, 94, 208, **1173**, 751, 258, 347, 140, 292, 270, 101, 650, 294, 364 — **12 of 20** over 280.
## How does Twitter API pagination work with cursors?
Pass the envelope's `pagination.next_cursor` from page *n* back as the `cursor` query param on page *n+1*. Stop when `pagination.has_more` is false. That object is a sibling of `data`, not a child of it. Each page costs 1 credit.
```bash
# page 1
curl -s -H "x-api-key: $SOCIALCRAWL_API_KEY" \
"https://www.socialcrawl.dev/v1/twitter/user/tweets?handle=nasa"
# page 2 — paste pagination.next_cursor from page 1
curl -s -H "x-api-key: $SOCIALCRAWL_API_KEY" \
"https://www.socialcrawl.dev/v1/twitter/user/tweets?handle=nasa&cursor=CURSOR"
```
Live two-page walk of `@nasa` (2026-09-04):
| | Page 1 | Page 2 |
|---|---|---|
| Items | **13** | **13** |
| Newest `published_at` | 2026-09-03T21:13:15Z | 2026-08-31T18:22:20Z |
| Oldest on page | 2026-08-31T21:34:46Z | 2026-08-30T10:20:09Z |
| `has_more` | true | true |
| Id overlap | — | **0** |
Newest-first chronology holds across the cursor: page 2 starts older than page 1 ends, with zero shared ids. Official X API v2 uses the same opaque-token idea (`next_token` → `pagination_token`) for list endpoints ([docs.x.com pagination](https://docs.x.com/x-api/fundamentals/pagination)); SocialCrawl exposes it as `pagination.next_cursor` / `cursor` on the unified envelope.
Sample non-RT from page 1 (`data.items[n].post`):
```json
{
"post": {
"id": "2095585125627003244",
"url": "https://x.com/NASA/status/2095585125627003244",
"published_at": "2026-09-03T18:50:02.000Z",
"author": { "username": "NASA", "verified": true },
"engagement": {
"views": 346164,
"likes": 1461,
"comments": 82,
"shares": 205,
"saves": 57
},
"flags": { "pinned": false },
"content": {
"text": "An uncrewed Progress cargo spacecraft carrying food, fuel, and supplies is scheduled to lift off on a two-day flight to the @Space_Station on Wednesday, Sept. 9.\n\nWe will be broadcasting its launch and arrival live. How to tune in: https://go.nasa.gov/4gzfYyG https://t.co/JOKpBt3VME"
}
}
}
```
`post.flags.pinned` is present on every item. This harvest had **no** pinned tweet on either page (all `false`) — absence of a true pin does not mean the field is missing. The earlier product measurement of **174 posts over 9 pages** still stands as a prior walk; the two pages above are what this run proved.
## How do you fetch tweet replies and retweeters via API?
Conversation reconstruction needs two reads on the same parent URL.
**Replies** arrive as a CommentList — not nested inside the parent post:
```bash
curl -s -H "x-api-key: $SOCIALCRAWL_API_KEY" \
"https://www.socialcrawl.dev/v1/twitter/tweet/replies?url=https://x.com/NASA/status/2094742199736926232"
```
| Field | Value this pull |
|---|---|
| Parent | `2094742199736926232` (417 comments on the parent engagement metric) |
| Items | **36** |
| `has_more` | true |
| Credits | 1 |
```json
{
"data": {
"items": [
{
"comment": {
"id": "2094773667884347874",
"parent_id": "2094742199736926232",
"author": { "username": "NASA", "verified": true },
"engagement": { "likes": 509, "replies": 26 },
"text": "@Astro_Jessica @Soph_astro Time for the spacewalk! Astronauts @Soph_astro and @astro_jessica have begun their busy day installing and replacing hardware on the @Space_Station. https://t.co/iAC6E7Ul62"
}
}
]
},
"pagination": { "has_more": true, "page_size": 36, "next_cursor": "…" },
"credits_used": 1
}
```
Replies are the Comment archetype: the body is `comment.text`, not `post.content.text`.
**Retweeters** use the same parent and return an AuthorList (~**20**/page this pull, cursor present). First row this page: `ravenclawdegrl` (23 followers, joined 2016-07-20). Every row had `author.verified: null` — write `null`, not `false`.
| Endpoint | Page size | Shape | `verified` |
|---|---:|---|---|
| `/v1/twitter/tweet/replies` | 36 | CommentList | populated on reply authors |
| `/v1/twitter/tweet/retweeters` | 20 | AuthorList | **null** on every row this page |
Empty-reply parents refund the credit. We did not hit that path — the spacewalk thread had replies.
## How do you get Twitter followers, following, media, and accounts?
Four more reads, one credit each.
**Followers** — `@nasa`, **70**/page, cursor present. Sample first row: SpaceX (41,900,244 followers). `verified` is **null** on every row this page.
```bash
curl -s -H "x-api-key: $SOCIALCRAWL_API_KEY" \
"https://www.socialcrawl.dev/v1/twitter/user/followers?handle=nasa"
```
```json
{
"data": {
"items": [
{
"author": {
"id": "34743251",
"username": "SpaceX",
"display_name": "SpaceX",
"verified": null,
"followers": 41900244,
"following": 127,
"posts_count": 11701,
"joined_at": "2009-04-23",
"bio": "SpaceX designs, manufactures and launches the world’s most advanced rockets and spacecraft"
}
}
]
},
"pagination": { "has_more": true, "page_size": 70, "next_cursor": "…" },
"credits_used": 1
}
```
**Following** — `@nasa`, **67**/page. First ten usernames this pull: NASAHubble, NASAWebb, Space_Station, esa, NASAArtemis, NASAEarth, NASAJPL, NASAJohnson, NASAUniverse, NASAMars. Same `verified: null` caveat as followers.
**Media tab** — `@nasa`, **19** photo/video posts/page, cursor present. `engagement.views` and `engagement.saves` are **null** on every item. Use `/v1/twitter/user/tweets` or `/v1/twitter/tweet` when those metrics matter.
**Account search** — `query=nasa` returned **10** matches, **one page, no cursor** (`has_more=false`, `next_cursor` absent). Verification is populated here (official accounts show `true`); follower / following / posts counts are **null**. Use it as typeahead to resolve handles before spending timeline or follower credits.
```bash
curl -s -H "x-api-key: $SOCIALCRAWL_API_KEY" \
"https://www.socialcrawl.dev/v1/twitter/search/users?query=nasa"
```
| # | Username | Display | Verified |
|---|---|---|---|
| 1 | NASA | NASA | true |
| 2 | NASAHubble | Hubble | true |
| 3 | NASAEarth | NASA Earth | true |
| 4 | NASAJPL | NASAJPL | true |
| 5 | NASAWebb | NASA Webb Telescope | true |
(…plus NASAKennedy, NASASolarSystem, NASAPersevere, nasahqphoto, NASAJohnson — ten total, then stop.)
## How do you start calling these endpoints?
1. Get an API key at [socialcrawl.dev](https://www.socialcrawl.dev) (free credits on signup; no card for the free tier).
2. Export it and hit either search or a timeline page:
```bash
curl -s -H "x-api-key: $SOCIALCRAWL_API_KEY" \
"https://www.socialcrawl.dev/v1/twitter/search/tweets?query=NASA%20Artemis&sort=latest"
```
3. Paste an X URL into the [Visual Explorer](/explorer) if you want to see the response shape before writing a loop.
4. Read the [Twitter platform reference](/platforms/twitter) and the [API quickstart](/docs/quickstart) for the full endpoint list and auth model.
For pricing math on the official X API, see the [X (Twitter) API 2026 field guide](/blog/x-twitter-api-2026). For an end-to-end scrape walkthrough (profile → tweets → cursor loop), see [How to Scrape Twitter (X) in 2026](/blog/how-to-scrape-twitter-2026).
## Frequently asked questions
### How does Twitter API pagination / cursor paging work?
List endpoints return `pagination.has_more` and `pagination.next_cursor`. Pass that cursor back as the `cursor` query parameter on the next request. Stop when `has_more` is false. On 2026-09-04, `@nasa` timeline page 1 returned 13 posts and page 2 returned 13 older posts with zero id overlap — both pages still had `has_more=true`.
### How do I get more than one page of a user's tweets?
Call `GET /v1/twitter/user/tweets?handle=…`, read `pagination.next_cursor`, then call again with `&cursor=…`. Each page is 1 credit. A previously measured walk returned 174 posts over 9 pages; this harvest walked two pages to prove the cursor.
### Does the Twitter search API support latest and top results?
Yes. Pass `sort=latest` for chronological results or `sort=top` for engagement ranking on `GET /v1/twitter/search/tweets`. The Artemis `sort=latest` call returned 20 items with a cursor for the next page.
### How do I fetch Twitter replies via an API?
`GET /v1/twitter/tweet/replies?url=…` returns a CommentList. The NASA spacewalk parent returned **36** replies on page 1 with `has_more=true`. Empty-reply parents are refunded (`credits_used: 0`).
### How do I get Twitter followers (and following) programmatically?
`GET /v1/twitter/user/followers?handle=…` returned **70**/page for `@nasa`; `/v1/twitter/user/following` returned **67**/page. Both cursor-paginate at 1 credit/page. On these surfaces `author.verified` is `null` — account search is the read that populates verification.
### Can an API return full-length tweets over 280 characters?
Yes on this surface. Of 20 Artemis search hits, **12** exceeded 280 characters; the longest (`@NASAStennis`) was **1,173**. The body lands on `post.content.text` — not a truncated preview.
### How do I call the Twitter search API from Python?
```python
r = requests.get(
"https://www.socialcrawl.dev/v1/twitter/search/tweets",
params={"query": "NASA Artemis", "sort": "latest"},
headers={"x-api-key": os.environ["SOCIALCRAWL_API_KEY"]},
timeout=60,
)
r.raise_for_status()
page = r.json()
print(len(page["data"]["items"]), page["pagination"])
```
Same envelope as curl. Loop on `page["pagination"]["next_cursor"]` the same way you would for timelines. `pagination` is a sibling of `data`.
---
Docs and the live surface: [/docs/quickstart](/docs/quickstart) · [/platforms/twitter](/platforms/twitter).