# Twitter's free academic API is gone. How can researchers collect tweets now?

> Building a tweet dataset after X ended free Academic Research access: the official full-archive route, a per-page search API, cost for 20,000 posts, and method notes.

Canonical page: https://okdata.online/answers/twitter-academic-api-alternative/

**Short answer: **X ended free academic access in 2023. Today a researcher can pay X directly ($0.005 per post through full-archive search, so 20,000 posts cost $100) or collect public posts with X post search at $0.006 per page of about 20, which puts 20,000 posts at $6.00. Slice queries by month and keep the exact queries for your methods section.

A PhD student studying how a policy debate unfolded on X needs about 20,000 posts from three months of 2026. Until 2023 the Academic Research track would have given her full-archive search for free. Twitter announced its end in February 2023, and a survey that autumn by the Coalition for Independent Technology Research found more than 100 studies cancelled, suspended or changed as a result. The data is still reachable; it now costs money, so plan the budget the way you would plan lab time.

## Two routes and what they cost

| Route | 20,000 posts | Notes |
|---|---|---|
| X full-archive search, pay-per-use | $100 | back to 2006, X's own operators, X developer account |
| [X post search](/apis/x/posts/search/) here | $6.00 | X search-box operators, about 20 posts per page |

Account-level work uses [X account posts](/apis/x/users/posts/list/) ($0.006 per page) and [X account profile](/apis/x/users/get/) ($0.001 per account), for example to record each author's follower count at collection time.

## Collect month by month

```python
import json, os, requests

API = "https://api.okdata.online/v1"
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
BASE = '("carbon tax" OR #carbontax) lang:en -filter:replies'
MONTHS = [("2026-07-01", "2026-08-01"), ("2026-08-01", "2026-09-01"), ("2026-09-01", "2026-10-01")]

with open("corpus.jsonl", "a", encoding="utf-8") as out:
    for since, until in MONTHS:
        query, cursor = f"{BASE} since:{since} until:{until}", None
        for _ in range(350):  # page budget per month
            params = {"query": query, "sort": "latest", "view": "full", **({"cursor": cursor} if cursor else {})}
            body = requests.get(f"{API}/x/posts/search", headers=H, params=params, timeout=60).json()
            if body["code"] != "ok":
                raise RuntimeError(f"{body['code']}: {body['message']} ({body['request_id']})")
            for post in body["data"]["items"]:
                out.write(json.dumps({"query": query, "fetched_at": body["meta"]["fetched_at"], **post}, ensure_ascii=False) + "\n")
            if not body["page"]["has_more"]:
                break
            cursor = body["page"]["next_cursor"]
```

Storing the query and `fetched_at` with every record makes the dataset reproducible to the extent a live platform allows.

## Method notes reviewers will ask about

- The posts are what X search returns to the public for each query. That is neither a random sample nor a complete census. Say so.
- Engagement numbers change after collection. Report when you collected them.
- Deleted posts and protected accounts are not included, which can bias studies of contentious topics.
- Follow your ethics board's rules on public data. Sharing post IDs with your paper, rather than full text, is the common practice.

## Other doors worth knowing

In the EU, Article 40 of the Digital Services Act gives vetted researchers a legal route to request platform data, which suits large studies with institutional backing. X's official full-archive search is the cleanest choice when completeness and provenance matter most. Existing published datasets of post IDs can sometimes save collection altogether. For multi-platform designs, note that many researchers moved to Reddit, TikTok and YouTube; the [trend research guide](/solutions/trend-research/) shows a cross-platform collection, and the [pricing page](/pricing/) lists every endpoint.

## Endpoints used

- [X post search](https://okdata.online/apis/x/posts/search/) `x.posts.search`: $0.006 per call · about 20 posts per page
- [X account posts](https://okdata.online/apis/x/users/posts/list/) `x.users.posts.list`: $0.006 per call · about 20 posts per page
- [X account profile](https://okdata.online/apis/x/users/get/) `x.users.get`: $0.001 per call

Updated 2026-11-12
