twitterapi.io is an independent third-party service. Not affiliated with X Corp.

Blogtwitter user search

Twitter (X) User Search: Find Accounts by Name, Keyword or Bio

By Michael Park•14 min read
Diagram of three routes for Twitter user search: the native People tab in the X app that matches name, handle and bio with only follow and location filters; the twitterapi.io user search endpoint with cursor pagination, client-side filters and CSV export, plus user info and batch enrichment endpoints; and tweet-search-derived discovery that collects the authors of posts on a topic.
Three ways to find X accounts. The People tab is fast but unfilterable; the API search returns full profiles you can filter and export; tweet search finds people by what they post.

This page is about finding accounts, not posts. If you want to search what people have written, the advanced-search and search-operators guides linked at the bottom cover tweet search in depth; here the output is always a list of profiles. The two groups of people who search "twitter user search" want different things. One group wants to find a specific person or a type of account from inside the X app and keeps running into the limits of the People tab. The other group, mostly developers and analysts, wants a list they can filter and keep: creators in a niche, accounts with a keyword in the bio, people in a city, candidates for a follow list or an outreach sheet.

Both get a working answer here. The first half explains how X's own people search behaves in 2026, what it matches on and what it cannot do, so you stop fighting it. The second half gives the API route: one search endpoint that returns full profile objects, two enrichment endpoints, one indirect discovery path through post search, and a complete Python script that paginates, filters by follower count, bio keyword and verification, and exports a CSV. Every endpoint named is a real path, every price is quoted from the vendor's own pricing page, and the official X API is covered honestly as the more expensive alternative.

Nothing here touches your own X account. The API calls are read-only against public profile data, authenticated with an API key from the twitterapi.io dashboard, so there is no OAuth dance, no browser extension and no risk to the account you log in with.

01 — Section

How people search works inside X, and where it stops

X's own help page on finding people describes the feature in one paragraph: type a name or username into the search box at the top of the Home timeline (or the Explore tab in the app), then narrow the results with the People filter at the top of the results page. The filter panel adds two toggles, From anyone or People you follow, and Anywhere or Near you. The match runs across display name, username and bio text, so a keyword search such as "machine learning" does surface accounts with that phrase in their bio, ranked by X's own relevance model, which weights follower count and your social graph in ways X does not document.

Two constraints shape everything else on this page. First, search is for logged-in accounts only: X removed search for logged-out visitors in April 2023, so there is no way to run a people search from a plain browser session or a script that is not signed in. Second, the Advanced search form filters posts, not people. Its Accounts section (from these accounts, to these accounts, mentioning these accounts) narrows which posts you see; none of its fields applies to the People tab. There is no operator for follower count, for text in the location field, for account age, for verification, or for excluding a word from the bio.

What you want to doPeople tab in the X app
Find an account by name or handleYes, this is what it is built for
Find accounts with a keyword in the bioYes, but ranked by X, no way to see all matches
Filter by follower countNo
Filter by location text or countryOnly the Near you toggle, which uses your own location
Filter by verification or account ageNo
Exclude a word, combine conditionsNo
Export the listNo
Use it without logging in or from a scriptNo, logged-in sessions only since April 2023

If your need sits in a row with a "no", the rest of this page is for you. If you only need to find one person whose name you know, the People tab is the right tool and nothing below will be faster.

02 — Section

Three routes to find X accounts, compared

The honest comparison, with prices quoted from the twitterapi.io pricing page and the official X API pricing page on docs.x.com (both verified in 2026). The official route is covered properly because it is the reference implementation; it is also the one most people abandon once they see the per-profile price and the user-context authentication it requires.

People tab in the X apptwitterapi.io GET /twitter/user/searchOfficial X API GET /2/users/search
Query matchesName, handle, bioKeyword across the profileName, username, bio; query limited to 50 characters of letters, digits, spaces, underscores and apostrophes
FiltersFollow, Near youNone server-side; filter the returned fields in your codeNone server-side; user.fields selects fields, filtering is yours
Results per requestScroll until X stopsOne page per call, continue with cursormax_results 1 to 1,000, default 100, continue with next_token
AuthenticationLogged-in X accountX-API-Key headerOAuth 2.0 user context with users.read and tweet.read scopes, or a user bearer token
Rate limitUndocumentedPer-plan QPS, see the rate-limit calculator300 requests per 15 minutes per app, 900 per user
Cost per 1,000 profilesFree, manual$0.18$10.00 ($0.010 per user read)
Minimum chargen/a$0.00015 per call (15 credits), waived for bulk responsesNone, but pay-per-use credits are bought up front
ExportNoYes, you hold the JSONYes, you hold the JSON

The cost row is the one that decides most projects. At $0.010 per user read, a 50,000-profile sweep costs $500 on the official API; at $0.18 per 1,000 it costs $9 on twitterapi.io, roughly 55 times less. The authentication row decides the rest: the official user-search endpoint is documented with user-context scopes, which means an OAuth flow and a real X account behind every request, where the third-party route needs one header.

A note on the official rate limit so the comparison is fair: 300 requests per 15 minutes at up to 1,000 results each is a lot of throughput, and the 3-million-post monthly cap on X's pay-per-use plans applies to post reads, not user reads (docs.x.com, verified 2026). The official route is not slow; it is expensive and awkward to authenticate.

Grouped bar chart on a log scale comparing the cost of Twitter user search by number of profiles fetched: for 1,000 profiles twitterapi.io costs $0.18 and the official X API $10; for 10,000 profiles $1.80 versus $100; for 100,000 profiles $18 versus $1,000. The People tab in the X app is free but manual and not exportable.
Cost of fetching profiles through user search. twitterapi.io at $0.18 per 1,000 users versus the official X API at $0.010 per user read (docs.x.com, verified 2026). Log scale.
03 — Section

Route 2: search users by keyword with the API

The endpoint is GET https://api.twitterapi.io/twitter/user/search, authenticated with an X-API-Key header. It takes two query parameters: query, the keyword to search, and cursor, which is an empty string for the first page and the next_cursor from the previous response afterwards. The response is a JSON object with a users array, a boolean has_next_page, a string next_cursor, and status set to success or error with a msg on failure.

Each element of users is a full profile object, the same shape the user-info endpoint returns, so you do not need a second call to get the fields you filter on. The ones that matter for people search are userName, name, id, description (the bio), location (free text, whatever the account typed), followers, following, statusesCount, createdAt, isBlueVerified, verifiedType, profilePicture and url. Billing is per profile returned, $0.18 per 1,000, with a $0.00015 per-call floor that only matters on an empty page (a single returned user already costs $0.00018).

The query behaves like the search box, not like a database filter: it is a relevance-ranked keyword match, and the result ordering is X's. Pagination with cursor is how you get past the first screen. Expect the useful results to thin out after a few pages for broad queries; for a narrow query such as a product name or a city plus a profession, you can usually walk to the end. The snippet below fetches one page and prints the handles; the full script further down turns it into a filtered CSV.

python
import os
import requests

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"


def search_users_page(query: str, cursor: str = "") -> dict:
    """One page of Twitter user search. Returns the raw JSON: users, has_next_page, next_cursor."""
    r = requests.get(
        f"{BASE}/twitter/user/search",
        headers=HEADERS,
        params={"query": query, "cursor": cursor},
        timeout=30,
    )
    r.raise_for_status()
    return r.json()


if __name__ == "__main__":
    page = search_users_page("climate scientist")
    for u in page.get("users") or []:
        print(f"@{u.get('userName'):<20} {u.get('followers', 0):>9,}  {u.get('location') or '-'}")
    print("more pages:", page.get("has_next_page"), "cursor:", page.get("next_cursor"))
04 — Section

Filter the results: followers, bio keywords, location, verification

Neither the native People tab nor either API offers server-side filters, so filtering is a loop over the profile objects you already paid for. That has a cost implication worth stating plainly: you pay for every profile the search returns, not for the ones you keep, so a tight query saves more money than a clever filter. Search for "solar installer texas" rather than "solar" and filter from there.

GoalField to testTypical rule
Minimum audiencefollowersfollowers >= 1000
Real humans, not broadcast accountsfollowing, followersdrop following == 0, keep follower-to-following ratio under 50
Bio keyworddescriptionlower-case substring or word match; keep a stop list for "opinions my own" style noise
Locationlocationsubstring match on city or country names; it is free text, so expect "NYC", "New York" and "ny"
VerifiedisBlueVerified, verifiedTypeisBlueVerified for paid blue; verifiedType for organisation or government labels
Active accountsstatusesCount, createdAtstatusesCount >= 100; parse createdAt (format like Tue Mar 21 20:50:14 +0000 2006) for account age
Already followed or blockedyour own listjoin against an export of your following list

Two practical notes from running this on real queries. The location field is whatever the account typed, so a country filter needs a small alias table ("UK", "United Kingdom", "England", "London") rather than one string. And isBlueVerified is a paid-subscription flag in 2026, not an identity check; if you want institutions, look at verifiedType instead. The full script below applies all three filter families and writes the survivors to a CSV with the fields above as columns.

05 — Section

Enrich or refresh: user/info and batch_info_by_ids

Search results carry full profiles, but two cases need a second endpoint. The first is when you already have the handles or IDs (a CSV from last month, a list someone sent you) and want current follower counts and bios. The second is when a search result looks stale and you want to re-read one account before acting on it.

For a single handle use GET /twitter/user/info with a userName parameter; the profile is under the data key of the response. For a list of IDs use GET /twitter/user/batch_info_by_ids with userIds set to the IDs joined by commas; the response has a users array. Both are billed at the same $0.18 per 1,000 users as search, and the batch endpoint is the one to reach for when refreshing hundreds of accounts, since it avoids one HTTP round trip per profile. For the official-API equivalent, GET /2/users and GET /2/users/by accept up to 100 IDs or usernames per request at $0.010 per user read (docs.x.com, verified 2026).

The snippet refreshes a list of IDs in chunks and prints the follower delta against the stored value. The username-lookup reference linked below goes deeper on resolving handles to IDs and on edge cases such as suspended or renamed accounts.

python
import os
import requests

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"


def user_by_handle(handle: str) -> dict | None:
    r = requests.get(
        f"{BASE}/twitter/user/info",
        headers=HEADERS,
        params={"userName": handle},
        timeout=30,
    )
    if r.status_code != 200:
        return None
    return r.json().get("data")


def users_by_ids(user_ids: list[str], chunk: int = 100) -> list[dict]:
    out: list[dict] = []
    for i in range(0, len(user_ids), chunk):
        r = requests.get(
            f"{BASE}/twitter/user/batch_info_by_ids",
            headers=HEADERS,
            params={"userIds": ",".join(user_ids[i:i + chunk])},
            timeout=60,
        )
        r.raise_for_status()
        out.extend(r.json().get("users") or [])
    return out


if __name__ == "__main__":
    stored = {"2244994945": 650_000}  # id -> follower count you saved last time
    for u in users_by_ids(list(stored)):
        delta = (u.get("followers") or 0) - stored.get(str(u.get("id")), 0)
        print(f"@{u.get('userName')}: {u.get('followers'):,} followers ({delta:+,} since last run)")
    nasa = user_by_handle("NASA")
    if nasa:
        print(nasa.get("name"), "-", (nasa.get("description") or "")[:80])
06 — Section

Route 3: find accounts through the posts they write

Bio search finds people by how they describe themselves. Many of the accounts you actually want, the ones active on a topic, say nothing about it in their bio. The indirect route is to search posts on the topic with GET /twitter/tweet/advanced_search (parameters query, queryType set to Latest or Top, and cursor) and collect the author object attached to each returned post. Dedupe on author.id, count how many matching posts each author produced, and you have a ranked list of people who talk about the subject, each with the same profile fields as the user-search results.

The query syntax is X's own operator set, which the search-operators guide linked below documents in full. For discovery the useful ones are a quoted phrase or a hashtag for the topic, lang:en to keep one language, min_faves: to drop noise, -filter:replies to keep original posts, and -from:handle to exclude accounts you already know. X's near:city within:15mi operators are accepted in the syntax but only match posts that carry location data, which in our experience is a small share of results, so treat them as a supplement to the location field on the profile, not as a replacement for it.

Cost works differently on this route: you pay $0.15 per 1,000 posts returned (the author objects ride along free), so a topic with many posts per author is cheap to mine and a topic where every post is a new author costs about the same as user search. The snippet walks a few pages of posts and prints the authors by frequency; feed the resulting IDs to users_by_ids above if you need to refresh them later.

python
import os
from collections import Counter

import requests

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"


def authors_from_posts(query: str, pages: int = 5) -> dict[str, dict]:
    """Collect distinct authors of posts matching an X search query."""
    authors: dict[str, dict] = {}
    hits: Counter = Counter()
    cursor = ""
    for _ in range(pages):
        r = requests.get(
            f"{BASE}/twitter/tweet/advanced_search",
            headers=HEADERS,
            params={"query": query, "queryType": "Latest", "cursor": cursor},
            timeout=30,
        )
        r.raise_for_status()
        data = r.json()
        for t in data.get("tweets") or []:
            a = t.get("author") or {}
            if a.get("id"):
                authors[a["id"]] = a
                hits[a["id"]] += 1
        if not data.get("has_next_page"):
            break
        cursor = data.get("next_cursor") or ""
    for uid, a in authors.items():
        a["matching_posts"] = hits[uid]
    return authors


if __name__ == "__main__":
    found = authors_from_posts('"solid state battery" lang:en min_faves:5 -filter:replies')
    for a in sorted(found.values(), key=lambda x: -x["matching_posts"])[:20]:
        print(f"{a['matching_posts']:>2}  @{a.get('userName'):<18} {a.get('followers', 0):>8,}  {(a.get('description') or '')[:60]}")
07 — Section

What Twitter user search costs at scale

The arithmetic behind the chart above. Prices: twitterapi.io $0.18 per 1,000 user profiles and $0.15 per 1,000 posts, with a $0.00015 minimum per call (twitterapi.io pricing page, verified 2026); official X API $0.010 per user read and $0.005 per post read on pay-per-use, resources deduplicated within a 24-hour UTC window (docs.x.com, verified 2026). Native search is free and manual.

JobPeople tabtwitterapi.ioOfficial X API
1,000 profiles from user searchFree, hours of scrolling, no export1,000 x $0.00018 = $0.181,000 x $0.010 = $10.00
10,000 profilesNot practical$1.80$100.00
100,000 profilesNot practical$18.00$1,000.00
Refresh 5,000 known accounts (batch lookup)Not possible5,000 x $0.00018 = $0.905,000 x $0.010 = $50.00
Discover authors from 2,000 topic postsNot possible2,000 x $0.00015 = $0.30, authors included2,000 x $0.005 = $10.00 for the posts, plus user reads for any profile expansion
Per-call minimumn/a$0.00015, only bites on empty pagesNone stated

Two things the table hides. The first is that filtering happens after billing on every route, so the cheapest optimisation is a narrower query, not a faster filter. The second is that the official API's 24-hour deduplication means re-reading the same user twice in a day is charged once there, while on twitterapi.io each returned profile is billed each time; if your pipeline refreshes the same accounts several times a day, cache the responses yourself. At these prices the dollar amounts are small on the third-party route either way; the decision usually comes down to authentication effort and to the 50-character query limit on the official endpoint.

08 — Section

Which route should you use?

You know the person's name or handle. Use the People tab in the X app. Nothing else is faster, and it is free.

You want a filterable, exportable list of accounts matching a keyword. GET /twitter/user/search with the script below. Narrow query, paginate with cursor, filter on followers, description, location and isBlueVerified, write the CSV.

You already have handles or IDs and want current numbers. GET /twitter/user/batch_info_by_ids in chunks of IDs, or GET /twitter/user/info for one handle. Same price per profile as search, far fewer calls.

You want people active on a topic, whatever their bio says. The tweet-search route: /twitter/tweet/advanced_search on the topic, collect authors, rank by matching posts. Pay per post rather than per profile.

You need a location filter. Combine a place name in the query with a substring match on the location field and an alias table, and accept that the field is self-reported. The near: operator on post search is a weak supplement, not a filter.

You must use the official X API. GET /2/users/search with max_results up to 1,000 and next_token, under OAuth 2.0 user context, at $0.010 per user read; budget about 55 times the third-party cost and keep the query under 50 characters (docs.x.com, verified 2026).

You want to grow a following from the results. Keep it human-paced. Export the list, review it, and follow at the pace you would by hand; the API supports follow actions but automated bulk following is what gets accounts restricted.

python
"""Twitter (X) user search to CSV: search by keyword, paginate, filter, export.

Usage:
  TWITTERAPI_IO_KEY=... python3 user_search.py "climate scientist" --min-followers 1000 \
      --bio "phd,professor,researcher" --location "london,uk,united kingdom" --out scientists.csv

Filters run on the profile fields the search already returns, so there is no second call per user.
Billing is per profile returned ($0.18 per 1,000), so keep the query as specific as you can.
"""
import argparse
import csv
import os
import time

import requests

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
FIELDS = ["userName", "name", "id", "followers", "following", "statusesCount",
          "isBlueVerified", "verifiedType", "location", "createdAt", "url", "description"]


def search_users(query: str, max_pages: int = 10):
    """Yield profile dicts across pages until has_next_page is false or max_pages is hit."""
    cursor = ""
    for _ in range(max_pages):
        r = requests.get(
            f"{BASE}/twitter/user/search",
            headers=HEADERS,
            params={"query": query, "cursor": cursor},
            timeout=30,
        )
        if r.status_code == 429:
            time.sleep(2)
            continue
        r.raise_for_status()
        data = r.json()
        if data.get("status") == "error":
            raise RuntimeError(data.get("msg"))
        yield from data.get("users") or []
        if not data.get("has_next_page"):
            return
        cursor = data.get("next_cursor") or ""


def keep(u: dict, args) -> bool:
    if (u.get("followers") or 0) < args.min_followers:
        return False
    if args.verified_only and not u.get("isBlueVerified"):
        return False
    bio = (u.get("description") or "").lower()
    if args.bio and not any(w in bio for w in args.bio):
        return False
    loc = (u.get("location") or "").lower()
    if args.location and not any(w in loc for w in args.location):
        return False
    return True


def main():
    p = argparse.ArgumentParser()
    p.add_argument("query")
    p.add_argument("--min-followers", type=int, default=0)
    p.add_argument("--bio", default="", help="comma-separated words, any match keeps the account")
    p.add_argument("--location", default="", help="comma-separated place aliases")
    p.add_argument("--verified-only", action="store_true")
    p.add_argument("--pages", type=int, default=10)
    p.add_argument("--out", default="users.csv")
    args = p.parse_args()
    args.bio = [w.strip().lower() for w in args.bio.split(",") if w.strip()]
    args.location = [w.strip().lower() for w in args.location.split(",") if w.strip()]

    seen, kept, fetched = set(), [], 0
    for u in search_users(args.query, args.pages):
        fetched += 1
        if u.get("id") in seen:
            continue
        seen.add(u.get("id"))
        if keep(u, args):
            kept.append({k: (u.get(k) if u.get(k) is not None else "") for k in FIELDS})

    with open(args.out, "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=FIELDS)
        w.writeheader()
        for row in kept:
            row["description"] = str(row["description"]).replace("\n", " ")
            w.writerow(row)
    print(f"fetched {fetched} profiles, kept {len(kept)}, wrote {args.out}; "
          f"approx cost ${fetched * 0.00018:.4f}")


if __name__ == "__main__":
    main()
09 — Questions

Questions readers ask

How do I search for a Twitter (X) user without an account?

You cannot through X itself: search has been limited to logged-in accounts since April 2023. A direct profile URL such as x.com/handle still loads when you know the handle. For search by keyword without logging in, use a read-only API such as GET /twitter/user/search on twitterapi.io, which needs an API key rather than an X login.

Can I search Twitter users by bio keyword?

Partly in the app: the People tab matches bio text, but you cannot combine conditions, exclude words or see every match. With the API, search for the keyword and then filter the description field of the returned profiles in your own code; the script on this page does this with a comma-separated word list.

Can I search Twitter users by location?

The X app only offers a Near you toggle based on your own location. Through the API, the location field on each profile is free text typed by the account, so filter it with a list of aliases (city, country, abbreviations). Putting the place name into the search query itself also helps, because it matches bios and locations that mention it.

How do I find Twitter accounts with a minimum follower count?

Neither X's People tab nor any user-search API filters by follower count server-side. Fetch the search results, which include a followers field on every profile, and keep the ones above your threshold. Because you pay per profile returned, a more specific query is the cheapest way to raise the share of results that pass the filter.

Is there an API for Twitter people search, and what does it cost?

Yes. On twitterapi.io, GET /twitter/user/search takes a query and a cursor and returns pages of full profiles at $0.18 per 1,000 users. The official X API has GET /2/users/search with up to 1,000 results per request, under OAuth 2.0 user context, at $0.010 per user read on its pay-per-use pricing (docs.x.com, verified 2026), about 55 times the per-profile price.

What is the difference between Twitter user search and tweet search?

User search returns accounts matched on name, handle and bio; tweet search returns posts matched on their text and operators such as from:, lang: and min_faves:. They are different endpoints with different prices ($0.18 per 1,000 users versus $0.15 per 1,000 posts on twitterapi.io). You can combine them: search posts on a topic and collect the author objects to discover accounts that never mention the topic in their bio.

How many results does Twitter user search return?

The X app shows results until its ranking runs out, with no count. The twitterapi.io endpoint returns one page per call and a has_next_page flag with a next_cursor, so you continue until the flag is false or you stop. The official endpoint lets you set max_results between 1 and 1,000 per request and paginates with next_token (docs.x.com, verified 2026).

10 — Further reading

Continue

Sources & further reading
More from this series
Build it

Stop reading. Start building.

Starter credits cover real testing on real data. Google sign-in, no card, no application queue.

Get an API key