# How to search X (Twitter) posts by keyword and export them

> Collect X (Twitter) posts for a keyword or advanced-search query, page by page, then re-read single posts by ID: parameters, operators, Python code and what a run costs.

Canonical page: https://okdata.online/guides/x-search-posts-by-keyword/

Use X post search to collect the posts for a query a page at a time, then X post detail to re-read single posts by ID or link. The worked example is 8 calls and costs $0.0109; a failed call is not charged.

Updated 2026-11-12

The job: you want what people posted on X about a product, a brand or a topic, as rows you can sort and share. Search with the same query you would type into the X search box, page through the results, and keep the posts that matter. A day later, read the standouts again to see whether they kept spreading. You end with a JSON Lines file of posts, each with its text, author, time, link and counts for views, likes, replies, reposts and bookmarks.

No X account, developer app or cookie is involved. Each call is an HTTPS request with your key.

## Step by step

### Step 1: search

Call [X post search](https://okdata.online/apis/x/posts/search/) with `query`. The query goes to X's own search as written, so the search box's operators work: `"exact phrase"`, `OR`, `-word`, `from:handle`, `lang:en`, `since:2026-11-05`, `until:2026-11-12`, `min_faves:50`, `-filter:replies`. The [operators answer](https://okdata.online/answers/x-search-operators-api/) lists the ones people use most. `sort` takes `latest` (the default, X's Latest tab) or `top` (the Top tab).

One call returns about 20 posts for $0.002, whatever operators you use. Each post has `id`, `url`, `text`, `lang`, `published_at`, `author` (with `username`, `name`, `followers` and `verified`) and `metrics`: `views`, `likes`, `comments` (replies), `reposts` and `favorites` (bookmarks). `metrics.shares` is always null, because X does not show it.

```bash
curl -G "https://api.okdata.online/v1/x/posts/search" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'query="standing desk" lang:en since:2026-11-05 -filter:replies' \
  --data-urlencode 'sort=latest' \
  --data-urlencode 'fields=id,url,text,published_at,author.username,metrics'
```

Use `--data-urlencode` for the query: quotes, colons and spaces must be encoded. For the next page, send `page.next_cursor` back as `cursor`, unchanged, and stop when `page.has_more` is `false`. `view=full` adds `ext.quotes`, `ext.is_reply` and `ext.conversation_id` to each post, at the same price.

### Step 2: re-read single posts

[X post detail](https://okdata.online/apis/x/posts/get/) takes a post's `id` or its link as `url` and returns the same post object for $0.0003. Two uses: posts someone sent you as links, and posts you already have whose counts you want to refresh. Views keep climbing for a day or two after a post goes up, so a second read tells you which posts still travel.

```bash
curl -G "https://api.okdata.online/v1/x/posts/get" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  -d id=1988412365027741953 \
  -d fields=id,metrics.views,metrics.likes,metrics.reposts
```

A deleted post, or one from an account that has gone protected, returns `not_found` and costs nothing.

### Step 3: put it together in Python

The script collects up to `MAX_PAGES` pages into `posts.jsonl` and saves the IDs of the three most viewed posts. Run it again with `recheck` a day later to read those three again.

```python
import json
import os
import sys
import requests

API = "https://api.okdata.online"
HEADERS = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
QUERY = '"standing desk" lang:en since:2026-11-05 -filter:replies'
MAX_PAGES = 5  # each page is one call


def get(path, **params):
    body = requests.get(API + path, headers=HEADERS, params=params, timeout=60).json()
    if body["code"] != "ok":
        raise RuntimeError(f"{body['code']}: {body['message']} (charged: {body['charged']})")
    return body


def collect():
    seen, cursor = {}, None
    for _ in range(MAX_PAGES):
        body = get("/v1/x/posts/search", query=QUERY, sort="latest", **({"cursor": cursor} if cursor else {}))
        for p in body["data"]["items"]:
            seen[p["id"]] = p
        if not body["page"]["has_more"]:
            break
        cursor = body["page"]["next_cursor"]
    with open("posts.jsonl", "w", encoding="utf-8") as f:
        for p in seen.values():
            f.write(json.dumps(p, ensure_ascii=False) + "\n")
    top = sorted(seen.values(), key=lambda p: p["metrics"]["views"] or 0, reverse=True)[:3]
    json.dump([p["id"] for p in top], open("watch.json", "w"))
    print(len(seen), "posts saved")


def recheck():
    for post_id in json.load(open("watch.json")):
        item = get("/v1/x/posts/get", id=post_id)["data"]["item"]
        print(post_id, item["metrics"]["views"], item["metrics"]["likes"], item["url"])


recheck() if sys.argv[1:] == ["recheck"] else collect()
```

The dictionary keyed by `id` drops duplicates, which matters when you run overlapping date windows.

## Good to know

- **Public posts only.** Posts from protected accounts never come back, from search or by ID.
- **Long periods.** For a month or more, run one query per week or per day with `since:` and `until:` and `sort=latest`. Short slices make it easy to see that every day was covered.
- **The official X API.** X sells its own API pay-per-use. Its [pricing page](https://docs.x.com/x-api/getting-started/pricing) lists post reads at half a US cent per post returned, which is $5 per 1,000 posts, with reads capped at 3 million posts per monthly billing cycle; a post you already paid for is not charged again within the same UTC day. X's [search documentation](https://docs.x.com/x-api/posts/search/introduction) offers recent search over the last 7 days to all developers and full-archive search back to 2006 on pay-per-use and Enterprise. If you also post, reply or need a contract with X, use theirs.
- **Retries without paying twice.** Send an `Idempotency-Key` header with each call. The same request with the same key within 24 hours returns the stored answer with `meta.replayed: true` and no new charge.

For a cost comparison with X's own API, see [what the X API costs in 2026](https://okdata.online/answers/x-api-pricing-2026/). To have an assistant write and run these queries, connect the [MCP server](https://okdata.online/mcp/).

## What the example costs

Five pages of the latest English posts about standing desks from the past week, then the three most viewed posts read again a day later

| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| [X post search](https://okdata.online/apis/x/posts/search/) | 5 | $0.002 | $0.01 |
| [X post detail](https://okdata.online/apis/x/posts/get/) | 3 | $0.0003 | $0.0009 |
| Total | 8 |  | $0.0109 |

Prices in force when this page was built. A failed call costs nothing.

## In your AI (MCP)

Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.

Say: Search X for "standing desk" in English from the last 7 days, without replies, latest first, five pages. Give me a table of author, text, views, likes, reposts and link. Then re-read the three most viewed posts and tell me how their views changed.

```json
{
  "endpoint": "x.posts.search",
  "params": {
    "query": "\"claude code\" lang:zh"
  },
  "idempotency_key": "<a new UUID>"
}
```

MCP: `https://api.okdata.online/mcp`

## Endpoints in this guide

- [X post search](https://okdata.online/apis/x/posts/search/) `x.posts.search`: $0.002 per call · about 20 posts per page
- [X post detail](https://okdata.online/apis/x/posts/get/) `x.posts.get`: $0.0003 per call

## More about X (Twitter)

- [X (Twitter) API](https://okdata.online/apis/x/)
- [How to record X (Twitter) trends for several countries every day](https://okdata.online/guides/x-trends-by-country/)
- [How do I get the view count of any tweet through an API?](https://okdata.online/answers/tweet-view-count-api/)
- [twscrape says No active accounts and my X accounts keep getting locked. What now?](https://okdata.online/answers/twscrape-no-active-accounts/)
- [How much does the X (Twitter) API cost in 2026?](https://okdata.online/answers/x-api-pricing-2026/)
- [If my agent retries a tool call after a timeout, will I be charged twice?](https://okdata.online/answers/agent-retry-charged-twice/)
- [How do I give my AI agent live X (Twitter) search without the official X API?](https://okdata.online/answers/ai-agent-live-x-search/)
- [How can Claude or another AI agent read a tweet from an x.com link?](https://okdata.online/answers/ai-agent-read-tweet-from-link/)
- [Official platform APIs vs third-party data APIs (2026)](https://okdata.online/compare/official-apis-vs-data-apis/)
- [How to choose a social media data API (2026)](https://okdata.online/compare/social-media-data-apis/)
- [Post detail API](https://okdata.online/roles/posts-get/)
- [Post search API](https://okdata.online/roles/posts-search/)
- [X trending topics: today’s list](https://okdata.online/tools/hot-search/x/)

## Sources

- [X API pay-per-usage pricing and credits](https://docs.x.com/x-api/getting-started/pricing) (read 2026-11-12)
- [X API: Search Posts introduction](https://docs.x.com/x-api/posts/search/introduction) (read 2026-11-12)
