WEIR

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.


Published · Updated · 7 min read

Cover image: Their call, our call

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:

$0.005per post read on the X API
$0.010per profile or follower read
3Mpost reads a month, then Enterprise
$70starter credits, expiring in 3 months

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.

Reading 10,000 public posts, October 2026
X API v2$50
Weir, $5 packabout $2.10

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: is min_faves: or the minLikes field, min_reposts: is min_retweets:, and -is:retweet is -filter:nativeretweets. is:reply, is:quote, has:media and has:links become filter:replies, filter:quote, filter:media and filter:links.
  • Dates and sort: start_time and end_time become the since and until fields, exact instants in any zone; the since: day operator follows Japan time through Weir. sort_order=relevancy is sort: "top"; recency is latest.
  • 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_count is followersCount, tweet_count is statusesCount, pinned_tweet_id is pinnedPostIds, a list. No listed_count or verified_type.
  • Reposts: a repost's own likeCount is 0 and its text starts with RT @; read the original from retweetedPost.
  • Nulls: a null count means X did not report it. Keep id a string; it passes 2^53.
  • Nothing to request: every field comes on every call; there is no tweet.fields or expansions.

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:x scope in a Bearer header; no app, no review.
  • Pagination. X returns meta.next_token, sent back as pagination_token, and omits it on the last page. Weir returns meta.nextCursor, sent back as cursor: body for search, query string for GET. null is the only stop, even after an empty page; a changed filter gets a 400.
  • 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_metrics and organic_metrics need 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

  1. Map every callMatch each v2 path to its twin above.
  2. Swap authOne weir_live_ key in the Bearer header; drop the app and OAuth code.
  3. Rename fieldspublic_metrics.* to likeCount; author_id lookups to post.author.
  4. Replace the pagernext_token to nextCursor as cursor; stop only on null, with a page cap.
  5. Split 402 from 429Retry a 429 after Retry-After; stop on a 402 and add credits.
  6. 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