X (Twitter) APIX (Twitter)
X (Twitter) search API
Call GET /v1/twitter/search/posts?q={query} with your keyword. Each request returns one page of public posts and a cursor for the next page.
You pay one price per page, however many posts it holds. You do not need an X developer account.
GET /v1/twitter/search/posts?q=…&sort=latest
curl "https://api.openhandle.dev/v1/twitter/search/posts?q=synthetic&sort=latest" \
-H "Authorization: Bearer $OPENHANDLE_API_KEY"TypeScript SDK
import { OpenHandle } from '@openhandle/sdk';
const openhandle = new OpenHandle({ apiKey: process.env.OPENHANDLE_API_KEY! });
let page = await openhandle.twitter.search.posts.list({ q: 'synthetic', sort: 'latest' });
while (page) {
for (const post of page.data) console.log(post.author.handle, post.text);
page = await page.next();
}200 OK
{
"platform": "twitter",
"resource": "post",
"capturedAt": "2026-09-18T09:42:19Z",
"source": "cache",
"data": [
{
"id": "940100000000000001",
"url": "https://x.com/copperfield_lab_x_test/status/940100000000000001",
"text": "A deterministic text post from a fictional lab. #synthetic",
"createdAt": "2026-08-17T14:00:00Z",
"author": { "id": "940000000001", "handle": "copperfield_lab_x_test" },
"metrics": { "likes": 42, "reposts": 3, "comments": 3, "quotes": 0, "views": null }
}
],
"meta": { "cursors": { "next": "b3BhcXVlLWN1cnNvcg" } }
}Trimmed to one post and the fields most apps need. The example is a Test query, so you can run it on a free Test key.
What you get
- —Posts. Each post has
id,url,text,createdAt, and theauthorwith ID and handle. - —Counts. Likes, reposts, comments, quotes, and views sit in
metrics. - —Latest or top. Use
sort=latestfor the newest posts first. Usesort=topfor the posts X ranks highest. - —More search. Search users with
GET /v1/twitter/search/users. Search images or videos withGET /v1/twitter/search/media.
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
freshnessto 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
Empty is an answer. A cursor from another query is a mistake.
| Situation | Status | Code |
|---|---|---|
| No public posts match the query | 200 | empty data |
| The cursor came from another query or sort | 400 | CURSOR_MISMATCH |
400 Bad Request
{
"error": {
"code": "CURSOR_MISMATCH",
"message": "The cursor does not belong to this request.",
"requestId": "req_01j8…",
"retryable": false
}
}An empty page bills as an answered request. A cursor is bound to its query, sort, and freshness. Change any of them and start without a cursor.
Read next
More X (Twitter) data
Send your agent ahead.
Paste the prompt into your agent and it sets everything up. Or come yourself, it takes five minutes.
100 free requests. No card needed.