X's API is not free. It has no free plan today, apart from free access for apps X classes as Public Utility Apps: it bills per resource returned, $0.005 per post read and $0.010 per user or follower read, with no subscription. New accounts can get up to $70 in one-time credits, but $50 of that matches money you pay in first. We read X's documentation on 2026-10-16.
What X charges to read
X bills reads per resource returned. X's pricing page opens: "The X API uses pay-per-usage pricing. No subscriptions—pay only for what you use." Reads are "Charged per resource returned in the response."
| Read | Price per resource |
|---|---|
| Posts | $0.005 |
| User | $0.010 |
| Following and followers | $0.010 |
| Your own posts, followers or bookmarks ("Owned Reads") | $0.001 |
The page also says "Pay-per-usage plans are capped at 3 million Post reads per monthly billing cycle", and that asking for the same resource again within a 24-hour UTC day is charged once. It lists no separate search price. A search returns posts, so we count each one as a Post read. X adds that prices are subject to change and that its Developer Console shows current rates.
Is there still a free tier?
X's pricing page shows no free plan, and X retired its fixed plans in stages:
- 2026-02-06, changelog: "Public Utility Apps continue to receive free scaled access. Recently active Legacy Free tier users receive a one-time $10 voucher. Basic and Pro plans remain available, and existing subscribers can opt in to Pay-Per-Use."
- 2026-02-13, an X staff reply in the developer forum: "Legacy packages like Free, Basic, and Pro have been deprecated."
- 2026-05-21, X staff: "Our legacy Basic plans (both monthly and annual) are being deprecated."
- 2026-08-14, X staff: "Our legacy Pro plans (both monthly and annual) are being deprecated." Pro subscriptions end at the first billing cutover after September 1, 2026.
The changelog line saying Basic and Pro "remain available" has not been updated. X's Developer Guidelines still mention a "free tier" that is "non-commercial only", but the pricing page prints no Free plan and no limits for one, so we don't repeat the 500 or 1,500 posts a month that older write-ups list.
What the free credits buy
A new account can get up to $70 in one-time credits, which is 14,000 post reads. X gives $20 when you save your first eligible credit or debit card, and matches your first auto-recharge up to $50, so that part needs you to pay in first. The credits "expire 3 months after they are granted". X also says the incentives are "currently rolling out": until they are enabled for your account, saving a card issues no credit.
At the prices above, the $70 is 14,000 post reads or 7,000 follower reads (our arithmetic). Apps X classifies as Public Utility Apps get free scaled access.
Limits for search, posts and followers on X
The limits are rate limits plus your credit balance; X says they are separate. From X's rate-limit table and endpoint references (read 2026-10-16):
- Recent search: posts from the last 7 days, 100 posts per request, 450 requests per 15 minutes per app.
- A user's posts: up to 100 per request.
- Followers: up to 1,000 per request, 300 requests per 15 minutes, $0.01 each. We priced a full export in the Twitter API followers list.
Read X posts and followers with ToolzerHub
We make ToolzerHub. Our X API returns public posts, search results and follower lists as JSON with one API key and no X developer account. Credits per call, from our OpenAPI file on 2026-10-16: profile 1, profile posts 5, search 5, followers 3.
We ran 10 hand-run calls that day on NASA, GitHub, BBCWorld and nytimes. All 10 returned data. A posts call returned 20 posts, a search 19 or 20, a followers call 200 profiles, and each came with a cursor for the next page. The script below follows the cursor and writes posts to CSV. Give it handles, or search: plus a query.
import csv
import os
import sys
import time
import requests
BASE = "https://api.toolzerhub.com/v1/x"
HEADERS = {"x-api-key": os.environ["TOOLZERHUB_API_KEY"]}
FIELDS = ["source", "post_id", "handle", "posted", "likes", "replies", "reposts", "quotes",
"views", "bookmarks", "text", "url"]
calls = 0
def cell(value):
# Spreadsheets run text that starts with = + - or @ as a formula; an apostrophe keeps it text.
if isinstance(value, str) and value[:1] in ("=", "+", "-", "@", "\t", "\r"):
return "'" + value
return value
def get(path, params, tries=5):
# 429 and 503 are retryable; the error body may say how long to wait.
global calls
for attempt in range(tries):
r = requests.get(f"{BASE}/{path}", headers=HEADERS, params=params, timeout=90)
calls += 1
if r.status_code == 400: # malformed handle or query: skip it, keep the rest of the run
print("bad request:", r.json()["error"]["message"].splitlines()[0])
return None
if r.status_code not in (429, 503) or attempt == tries - 1:
r.raise_for_status()
return r.json().get("data") # None when the account does not exist
try:
wait = r.json()["error"]["details"]["retryAfter"]
except (ValueError, KeyError, TypeError):
wait = 5 * (attempt + 1)
time.sleep(min(wait, 60))
def posts(source, max_pages):
# source is a handle ("NASA") or a search ("search:artemis"); follow the cursor page by page.
if source.startswith("search:"):
path, params = "search", {"query": source[7:].strip(), "sort": "latest"}
else:
path, params = "profile/posts", {"handle": source.lstrip("@")}
for page in range(max_pages):
data = get(path, params)
if not data or not data["posts"]:
if page == 0:
print(f"skipped {source}: no record or no posts")
return
yield from data["posts"]
if not data["has_more"]:
return
params["cursor"] = data["cursor"]
def row(source, p):
return {
"source": source, "post_id": p["id"], "handle": p["author"]["handle"],
"posted": p["created_at"], "likes": p["like_count"], "replies": p["reply_count"],
"reposts": p["repost_count"], "quotes": p["quote_count"], "views": p["view_count"],
"bookmarks": p["bookmark_count"], "text": (p["text"] or "").replace("\n", " "),
"url": p["url"],
}
def main(sources, pages="3", out="x_posts.csv"):
seen = set()
with open(out, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=FIELDS)
writer.writeheader()
for source in [s.strip() for s in sources.split(",") if s.strip()]:
for p in posts(source, int(pages)):
if p["id"] not in seen:
seen.add(p["id"])
writer.writerow({k: cell(v) for k, v in row(source, p).items()})
print(f"wrote {len(seen)} posts to {out} in {calls} calls")
if __name__ == "__main__":
main(*sys.argv[1:])
An unknown handle returns HTTP 200 with {"data": null} on the profile endpoint and an empty list on the posts endpoint. The script skips both with a message. cell() puts an apostrophe in front of any text that starts with =, +, - or @, so a spreadsheet shows it as text instead of running it as a formula.
Run the script
export TOOLZERHUB_API_KEY=your_key
python x_posts.py "NASA, github, nobody_here_qq9, search:artemis" 3 x_posts.csv
Output on 2026-10-16:
skipped nobody_here_qq9: no record or no posts
wrote 180 posts in 10 calls
It took 14 seconds. The three pages for each source gave 60 posts:
| Source | Posts | Date range | Median likes | Median views |
|---|---|---|---|---|
| NASA | 60 | 2026-09-23 to 2026-10-16 | 2,513 | 974,391 |
| github | 60 | 2026-09-17 to 2026-10-16 | 242 | 69,200 |
| search:artemis | 60 | 2026-10-16, 18 minutes | 0 | 1 |
The search sorts by latest, so it returns posts minutes old with no engagement yet. For engagement use sort=top: from:github with sort=top returned 19 posts dated 2021-09-21 to 2026-10-16, older than the 7 days X's recent search covers. All 180 post ids were unique, so each page gave new posts.
Where X's API is the better pick
- Posting, DMs and anything that writes. Our API reads public data only.
- Your own account's bookmarks and blocks (X's Owned Reads are $0.001 per resource), and streaming.
- Bigger pages. X returns up to 100 posts or 1,000 followers per request; we returned 20 and 200.
- Enterprise access, compliance feeds and a contract with X.
What it costs
On our side, 1,000 posts are 50 calls at 5 credits, or 250 credits. We charge $0.99 to $1.90 per 1,000 credits: $0.99 on the $495 plan, $1.45 on the $29 plan, $1.90 on the smallest pack. The free tier is 100 credits a month, enough for 20 posts calls, about 400 posts.
| Job | X, pay-per-use | ToolzerHub |
|---|---|---|
| 1,000 posts | $5.00 | $0.25 to $0.48 (250 credits) |
| 1,000 followers | $10.00 | $0.015 to $0.029 (15 credits) |
| 1 profile | $0.010 | $0.001 to $0.002 (1 credit) |
| The 180-post run above | $0.90 | $0.05 to $0.10 (50 credits) |
The run's cost is 10 calls at 5 credits, and it includes the skipped handle's call. X's column assumes every post is a first read that day. X charges a repeated resource once per UTC day, so a same-day re-run costs less than the table shows.
Reddit and Meta also limit or charge for their APIs: see what Reddit's API costs and Threads keyword search limits.
We are not affiliated with X Corp.
Sources
- X, Pay-per-usage pricing and credits, https://docs.x.com/x-api/getting-started/pricing, read 2026-10-16
- X, Free credit incentives for pay-per-use accounts, https://docs.x.com/x-api/getting-started/free-credits, read 2026-10-16
- X, API changelog (2026-02-06, 2026-04-16 and 2026-10-02 entries), https://docs.x.com/changelog, read 2026-10-16
- X, Announcing the launch of X API pay-per-use pricing, https://devcommunity.x.com/t/announcing-the-launch-of-x-api-pay-per-use-pricing/256476, read 2026-10-16
- X, "No free plan?" (staff reply, 2026-02-13), https://devcommunity.x.com/t/no-free-plan/257026, read 2026-10-16
- X, Legacy Basic plans are moving to pay-per-use (2026-05-21), https://devcommunity.x.com/t/important-update-legacy-x-api-basic-plans-are-moving-to-pay-per-use-ppu/266305, read 2026-10-16
- X, Legacy Pro plans are moving to pay-per-use (2026-08-14), https://devcommunity.x.com/t/273255, read 2026-10-16
- X, Rate limits, https://docs.x.com/x-api/fundamentals/rate-limits, read 2026-10-16
- X, Make your first request (recent search covers the last 7 days), https://docs.x.com/make-your-first-request, read 2026-10-16
- X, Get users' followers, https://docs.x.com/x-api/users/get-followers, read 2026-10-16
- X, Developer guidelines, https://docs.x.com/developer-guidelines, read 2026-10-16
- ToolzerHub X reference, https://docs.toolzerhub.com/reference/x-twitter, checked 2026-10-16
- ToolzerHub pricing, https://toolzerhub.com/pricing, checked 2026-10-16