Migrate from the X API v2 to Weir: Endpoint and Field Map
Moving read-only X API v2 code to Weir: each endpoint's twin, public_metrics to likeCount, auth and paging side by side, and a Python before and after.
Tarun Khatri
Published · Updated · 7 min read
To migrate from the X API v2 to Weir, swap each read endpoint for its twin, rename a few fields, and page with cursor.
This guide is for developers who read posts, profiles, followers or Lists through the X (Twitter) API v2. We run Weir, so weigh our view knowing that. Weir vs the official X API makes the case; this page moves the code.
The first call to move, as curl:
curl https://api.weirapp.io/v1/x/search \
-H "Authorization: Bearer $WEIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "from:XDevelopers -filter:nativeretweets", "limit": 5}'
Why read workloads leave the X API v2
X moved its API to pay-per-use on 6 February 2026, says its changelog. X's pricing page listed these rates on 30 October 2026:
There is no free tier for new apps. Legacy Basic plans moved to pay-per-use after 1 June 2026, as @XDevelopers announced that May.
And on 4 May 2026, X's changelog says, keyword search stopped returning retweets.
Weir bills 1 credit per post plus 1 per call, with 5,000 free credits a month. The X API price index sets X's rates beside every provider we track.
That is 500 pages, or 10,500 credits.
X API v2 to Weir endpoint map
Each row is a read call on X and its twin on Weir, as of October 2026:
| X API v2 | Weir | What changes |
|---|---|---|
GET /2/tweets/search/recent, /all |
POST /v1/x/search |
No 7-day window; about 20 a page |
GET /2/tweets/:id |
GET /v1/x/posts/{id} |
Author inline |
GET /2/tweets?ids= |
GET /v1/x/posts?ids= |
100 ids; misses in meta.notFound |
GET /2/users/by/username/:username |
GET /v1/x/users/{user} |
Username or numeric id |
GET /2/users/:id/tweets |
GET /v1/x/users/{user}/posts |
20 a page; replies at /replies |
GET /2/users/:id/mentions |
POST /v1/x/search + mentions |
A search, not a timeline |
GET /2/users/:id/followers |
GET /v1/x/users/{user}/followers |
About 70 a page, no limit |
GET /2/lists/:id/members |
GET /v1/x/lists/{id}/members |
Up to 100 a page |
Weir takes a username where X wants a numeric id, so the lookup step goes away. More twins:
GET /2/tweets/:id/quote_tweets GET /v1/x/posts/{id}/quotes
GET /2/tweets/:id/retweeted_by GET /v1/x/posts/{id}/retweeters
GET /2/users?ids= GET /v1/x/users?ids=
GET /2/users/:id/following GET /v1/x/users/{user}/following
GET /2/users/search POST /v1/x/users/search
GET /2/lists/:id/tweets GET /v1/x/lists/{id}/posts
Weir's query passes operators to X's search as typed, so most of a v2 query moves with renames:
- Same in both:
from:,to:,@,#,url:,list:,conversation_id:,lang:,OR,-and quoted phrases. - Renamed:
min_likes:ismin_faves:or theminLikesfield,min_reposts:ismin_retweets:, and-is:retweetis-filter:nativeretweets.is:reply,is:quote,has:mediaandhas:linksbecomefilter:replies,filter:quote,filter:mediaandfilter:links. - Dates and sort:
start_timeandend_timebecome thesinceanduntilfields, exact instants in any zone; thesince:day operator follows Japan time through Weir.sort_order=relevancyissort: "top";recencyislatest. - No twin:
context:,entity:,place_country:,point_radius:,bounding_box:and-is:nullcast.
Our search operators guide re-tests each operator every month.
Field map: public_metrics.like_count becomes likeCount
Weir returns one flat post object with its author inside. The fields most v2 code reads, from Weir's reference and X's data dictionary:
| X API v2 | Weir | Note |
|---|---|---|
public_metrics.like_count |
likeCount |
Same for retweet, reply, quote, bookmark |
public_metrics.impression_count |
viewCount |
No exact twin |
author_id + includes.users |
author.username |
Inline object |
created_at |
createdAt |
Both ISO 8601, UTC |
note_tweet.text |
text |
Always the full text |
referenced_tweets |
inReplyToId, quotedPost, retweetedPost |
Nested, no includes |
entities.hashtags[].tag |
entities.hashtags[] |
Plain strings; urls expanded |
includes.media[].url |
media[].url |
Best MP4 for video |
When you parse:
- Profiles:
followers_countisfollowersCount,tweet_countisstatusesCount,pinned_tweet_idispinnedPostIds, a list. Nolisted_countorverified_type. - Reposts: a repost's own
likeCountis 0 and itstextstarts withRT @; read the original fromretweetedPost. - Nulls: a
nullcount means X did not report it. Keepida string; it passes 2^53. - Nothing to request: every field comes on every call; there is no
tweet.fieldsorexpansions.
Our tweet JSON example walks a full post, field by field.
Auth, pagination and rate limits side by side
- Auth. X wants an app inside a Project, a bearer token for public reads and user context for private data. Weir wants one key with the
read:xscope in a Bearer header; no app, no review. - Pagination. X returns
meta.next_token, sent back aspagination_token, and omits it on the last page. Weir returnsmeta.nextCursor, sent back ascursor: body for search, query string forGET.nullis the only stop, even after an empty page; a changed filter gets a400. - Rate limits. X budgets each endpoint per 15 minutes, by token type. Weir has one rolling budget a minute, and heavier calls count for more.
The five endpoints above, side by side:
X API v2 (15 min, app token) Weir (per minute)
recent search 450 search counts 2
user lookup 300 profile lookup counts 1
user posts 10,000 user posts counts 2
followers 300 followers counts 3
list members 900 list members counts 3
The free tier allows 30 a minute, and paid tiers go up to 600. Responses carry ratelimit-* headers, and a 429 adds Retry-After.
A 429 costs nothing. A 402 means the account is out of credits and never clears by retrying. Our X API rate limits guide has X's full table and a tested retry loop.
Before and after: recent search in Python
The same job on each API: the latest original posts from @XDevelopers. The X half follows X's expansions example; we did not run it.
import os, requests
r = requests.get(
"https://api.x.com/2/tweets/search/recent",
headers={"Authorization": f"Bearer {os.environ['X_BEARER_TOKEN']}"},
params={"query": "from:XDevelopers -is:retweet", "max_results": 10,
"tweet.fields": "created_at,public_metrics", "expansions": "author_id"},
timeout=30,
)
page = r.json()
users = {u["id"]: u for u in page.get("includes", {}).get("users", [])}
for post in page.get("data", []):
print(post["created_at"][:10], post["public_metrics"]["like_count"],
users[post["author_id"]]["username"])
The Weir half uses only the standard library, since agent sandboxes often lack pip:
import json, os, urllib.request
req = urllib.request.Request(
"https://api.weirapp.io/v1/x/search",
data=json.dumps({"query": "from:XDevelopers -filter:nativeretweets", "limit": 5}).encode(),
headers={"Authorization": f"Bearer {os.environ['WEIR_API_KEY']}",
"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=60) as r:
page = json.load(r)
print("credits:", r.headers["x-credits-charged"])
for post in page["data"]:
print(post["createdAt"][:10], post["likeCount"], post["author"]["username"], post["url"])
We ran it on 30 October 2026. It printed five posts and charged 6 credits; GET /v1/usage, which is free, showed 4,741 credits before and 4,735 after.
credits: 6
2026-10-27 991 XDevelopers https://x.com/XDevelopers/status/2114879022433800258
2026-10-16 125 XDevelopers https://x.com/XDevelopers/status/2111172163776708859
2026-10-14 116 XDevelopers https://x.com/XDevelopers/status/2110414269036794108
2026-10-07 758 XDevelopers https://x.com/XDevelopers/status/2107953725071626260
2026-10-07 6 XDevelopers https://x.com/XDevelopers/status/2107953088623747102
The third post, as Weir returned it under data, trimmed:
{
"id": "2110414269036794108",
"text": "ZohoSocial is making it even easier for teams to power their brands on X! ...",
"createdAt": "2026-10-14T16:55:45.000Z",
"inReplyToId": null,
"author": { "username": "XDevelopers", "followersCount": 697679 },
"likeCount": 116,
"viewCount": 13906,
"entities": { "hashtags": [], "mentions": ["ZohoSocial"], "urls": [] },
"quotedPost": { "author": { "username": "ZohoSocial" }, "likeCount": 19 },
"retweetedPost": null
}
The quoted post arrives whole, where v2 gives a referenced_tweets entry plus an includes.tweets lookup.
What does not move: when not to use Weir
Weir reads public data and nothing else, so these stay on the X API:
- Posting, replies, likes, follows and DMs. Weir has no write endpoint.
- Filtered and volume streams. Weir has no real-time feed; monitors poll hourly on paid tiers, daily or weekly on free.
- Private metrics.
non_public_metricsandorganic_metricsneed X's user context. - OAuth user context.
/2/users/me, home timeline, bookmarks, liked posts, blocks, mutes and Owned Reads at $0.001. - Compliance jobs and streams, Activity API webhooks, post counts and trends.
- Protected accounts. Their posts and follower lists return
404. - A contract with X. Weir is not affiliated with or licensed by X, so access can change; read X's terms.
Cutover checklist
- Map every callMatch each v2 path to its twin above.
- Swap authOne weir_live_ key in the Bearer header; drop the app and OAuth code.
- Rename fieldspublic_metrics.* to likeCount; author_id lookups to post.author.
- Replace the pagernext_token to nextCursor as cursor; stop only on null, with a page cap.
- Split 402 from 429Retry a 429 after Retry-After; stop on a 402 and add credits.
- Run both for a dayCompare counts and x-credits-charged, then cut over.
FAQ
Can I migrate from the X API v2 to Weir without a rewrite?
Not without changes, but they are small: paths, field names and the paging token. The search dialect mostly carries over. Keep any code that posts on the X API.
Does Weir have a full-archive search like /2/tweets/search/all?
Not as a guarantee. Weir's search has no 7-day window and returns what X's public search serves, which reached 2022 posts in our tests. There is no counts endpoint.
What do 10,000 posts cost on Weir versus the X API?
As of October 2026, $50 on the X API at $0.005 a post. On Weir it is about 10,500 credits: $2.10 on the $5 pack, less on larger packs. Weir pricing lists every pack.
Sources
- X API docs, checked 30 October 2026: pricing https://docs.x.com/x-api/getting-started/pricing, free credits https://docs.x.com/x-api/getting-started/free-credits, changelog https://docs.x.com/changelog, rate limits https://docs.x.com/x-api/fundamentals/rate-limits, pagination https://docs.x.com/x-api/fundamentals/pagination, expansions https://docs.x.com/x-api/fundamentals/expansions, metrics https://docs.x.com/x-api/fundamentals/metrics, data dictionary https://docs.x.com/x-api/fundamentals/data-dictionary, operators https://docs.x.com/x-api/posts/search/integrate/operators
- X API references for the mapped endpoints: https://docs.x.com/x-api/posts/search-recent-posts, https://docs.x.com/x-api/posts/search-all-posts, https://docs.x.com/x-api/posts/get-post-by-id, https://docs.x.com/x-api/users/get-user-by-username, https://docs.x.com/x-api/users/get-posts, https://docs.x.com/x-api/users/get-mentions, https://docs.x.com/x-api/users/get-followers, https://docs.x.com/x-api/lists/get-list-members
- @XDevelopers on legacy Basic plans (21 May 2026), read through Weir: https://x.com/XDevelopers/status/2057572111020134462
- Weir docs: https://docs.weirapp.io/reference/x/searchX, https://docs.weirapp.io/billing, https://docs.weirapp.io/authentication, https://docs.weirapp.io/pagination, https://docs.weirapp.io/errors, https://docs.weirapp.io/monitors
- Our search, profile, post and usage calls through Weir's API, 30 October 2026