Skip to content

How do you get posts from an X Community?

Last updated August 24, 2026

Call community/tweets with a numeric community id and you get that community's feed as full tweet objects, cursor paginated, with the pinned post returned in its own field. If you only have a name, community/search turns a keyword into an id first. All 7 community routes are pooled public reads billed at $0.0008 a call.

Every rate here is the pricing TwitterAPIs publishes. The billed rate is $0.0008 per call; $0.04 per 1,000 tweets is derived from it at a full 20-tweet page, which is the default page size rather than a guaranteed yield (source: twitterapis.com/pricing).

Turning a community name into an id

Every route in this family starts from the numeric id in a x.com/i/communities/<id> URL, and community/search is the endpoint that produces one. Pass a query of 1 to 500 characters and it matches community names and topics, never posts, so a search for build in public returns communities rather than tweets about building in public. Each hit is a compact record: id, name, member_count, is_nsfw, primary_topic, banner_url, custom_banner, default_banner and member_avatar_urls, which is the facepile X renders beside a search result rather than a roster. Paging is next_cursor plus has_more. One field to watch is primary_topic.topic_id, commonly null on this operation while primary_topic.name is populated, so key your topic logic off the name rather than treating the null id as an error.

Reading the feed, and the pinned post trap

community/tweets returns the community timeline as full tweet objects, and the pinned post is not in that array. X delivers it under a separate timeline instruction and never repeats it in the feed, so the response carries a pinned field beside tweets, holding either the pinned tweet object or null. A client that iterates tweets alone loses it without a trace, and since the pinned post is usually the rules post, that loss is both quiet and material. For one flat list, read pinned first and then tweets, with no risk of a duplicate. Each tweet also carries a caller relative moderation state upstream that is deliberately never extracted, for the same reason four fields on the community object come back null.

Ordering, page size and how fresh the feed is

count defaults to 20 and is clamped to 1 through 100, so count=500 returns 100 rather than erroring, while a non numeric value is a 400. ranking_mode is a real upstream parameter rather than a local re-sort. Recency is the default and the only value confirmed against a live capture. Relevance is accepted because X's own community tab offers exactly two orderings, but it is deliberately not advertised as confirmed until a capture shows it, and anything else is a 400. This timeline is cached for a shorter window than the other community reads, because it is the one surface callers poll for new posts, while metadata and rosters change slowly and are held longer. An empty first page, meaning no cursor sent and nothing returned, is a 404.

What the metadata call actually returns

community/info takes one community_id and answers with the full object: name, description, member_count, moderator_count, join_policy, invites_policy, is_nsfw, the join screen question, primary_topic, search_tags, rules, both banners, permalink, the admin and creator profiles, and members_facepile_ids. Two shapes surprise people. created_at is a millisecond epoch number rather than an ISO string, and it is 0 when X sent nothing. rules[].description is null on every rule, because X's community payload carries only the rule id and name on this surface and nothing is invented to fill the gap. Both banners are always present, so a community with its own artwork still returns default_banner; read the computed banner_url when you just want the one a client should render. Observed join_policy values include Open and RestrictedJoinRequestsRequireModeratorApproval.

The four fields that are always null

Four keys on the community object come back null on every call: role, can_join, is_pinned and viewer_relationship_type. That is deliberate, and it is not a parsing failure. Each one is caller relative, describing the account that made the upstream request, and community data is public, so these reads are served by a rotating pool account rather than by your own session. A populated role would report a stranger's standing in the community. The keys are kept rather than stripped so it stays visible that X sends them at all. Note that members[].role on a roster row is a completely different field: it describes that member, is identical for every caller, and is returned in full. Conflating the two is the usual mistake in this family.

Paging the member roster

community/members pages the roster with community_id, cursor and count, using the same 1 to 100 clamp the timeline uses. Each row pairs a user with a role. The user is a reduced profile carrying id, username, name, profile_image_url, is_blue_verified, verified and is_protected, and nothing beyond that. The missing keys are absent rather than null, so a consumer can never read not asked for as zero. There is no bio, no follower or following count, no location, no website and no created_at, and there is no per member joined at timestamp, because X does not send one on this operation. Paging is next_cursor only: X returns no total and no has_more of its own, so the absence of a cursor is the end of list signal and has_more is computed for you.

Why moderators need their own call

Admins and moderators do appear in the member roster, tagged by role, but they are not grouped. In the captured community the admin sat at index 0 and moderators at indexes 1 and 13, so filtering the first page would return whichever moderators happened to land in the first twenty rows while looking like a finished answer. That is why community/moderators exists as a separate upstream operation rather than a convenience filter, and the difference is correctness rather than speed. It returns the same row shape under the same members key, on purpose, so one type serves both endpoints and the two cannot drift apart. Read the role on each row instead of assuming everything here is a Moderator, since admins are included and a mixed set surfaces rather than being flattened away.

Full profiles from the About tab

Roster rows are reduced, so answering who runs this community from them costs one call plus a profile lookup per person. community/about avoids that. It returns moderators as full user profiles carrying bio, follower and following counts, tweet counts, location, website and join date, alongside a members preview in the same full shape. X caps that preview at 10 rows on the underlying tab, so treat it as a sample and page the real roster elsewhere. moderators_truncated and members_truncated are read off X's own show more marker rather than inferred from a row count, so a community whose moderators fill the section exactly reports correctly instead of being guessed at. Despite the name there is no metadata here: no description, no rules, no member_count. It takes no cursor, because the upstream operation is not paginated.

The inverse relation, and what a full sweep costs

community/memberships flips the direction: give it a numeric user id and it pages back every community that account belongs to, each row the full community object rather than a stub. It takes an id and never a handle, because resolving a handle here would add a second upstream call and a second billing event to a request you thought was one read. An empty list is a real 200 response, unlike the member and moderator rosters, which return 404 on an empty first page. On price, all seven routes bill identically at eight hundredths of a cent. Paging a 250,000 member roster at the 100 row cap is 2,500 calls, about two dollars, and the flat ceiling of 600 requests a minute per key puts that job at roughly four minutes of wall clock. Per our published rates: $0.0008 a call.

Which community route answers which question

RouteInputWhat comes back
community/searchKeywordCompact community records plus a cursor
community/infocommunity_idFull metadata, rules and both banners
community/memberscommunity_idRoster rows tagged Admin, Moderator or Member
community/moderatorscommunity_idModerator and admin rows only
community/aboutcommunity_idModerators plus a 10 row members preview, full profiles
community/tweetscommunity_idFeed tweets, with the pinned post separate
community/membershipsuser_idEvery community that account joined
This dictionary documents every available field for each object type.
X Developer Platform, data dictionary. Source

Questions and answers

How do I find the id of an X Community?
Take the digits out of a x.com/i/communities/<id> link, or call community/search with a keyword. Search matches community names and topics rather than posts, and each hit returns an id, a name, a member_count and an is_nsfw flag, along with the banner images. Feed that id into any other route in the family. The rows are reduced, so call community/info when you need description, rules or join policy.
Why is role null on the community object?
Because role answers what the calling account is inside that community, and these reads are served by a rotating pool account rather than by you. can_join, is_pinned and viewer_relationship_type are null for exactly the same reason. The keys are kept rather than dropped so it stays visible that X sends them at all. A value in any of them would describe a stranger's relationship to the community, not yours.
Does the member roster tell me who moderates?
Each roster row carries that member's own role, so Admin and Moderator rows are genuinely in there. They are not grouped though, and they sit at arbitrary positions: in one capture the admin was at index 0 and moderators at 1 and 13. Reading only the first page gives a partial answer that looks whole. Call community/moderators instead, which is a distinct upstream operation rather than a filter.
Why is my pinned community post missing from the feed?
It is not missing, it sits in the pinned field beside the tweets array. X delivers a community's pinned post under its own timeline instruction and never repeats it in the feed, so a loop that iterates tweets alone drops it silently. It is often the rules post, which makes the omission easy to miss for weeks. Read pinned first, then tweets, and you get one flat list with no duplicate.
How do I get full profiles for a community's moderators?
Use community/about. Rows from the members and moderators routes are reduced, with no bio, no follower counts and no created_at, so answering who runs a community from them costs one call plus a lookup per person. community/about returns moderators as full profiles in a single request, alongside a members preview X caps at 10 rows, plus truncation flags read off X's own show more marker.
Can I list the communities one account has joined?
Yes. community/memberships takes a numeric user id and pages back every community that account belongs to. Pass an id, not a handle: resolving a handle here would quietly turn one billed read into two, so resolve it with a user lookup first. Each row is the full community object rather than a stub, so the renderer you already wrote for community/info works unchanged, and an empty list is a valid 200.
Can I search for posts inside one community?
Not with community/search, which matches community names and topics and returns communities. To read posts inside a specific community, page community/tweets with that community's id, optionally setting ranking_mode. For post search across all of X rather than inside one community, the advanced search endpoint is the right route. The two are different questions and the search route deliberately answers only the first.
How large can one page of community results be?
The members, moderators, tweets and memberships routes all take count, which defaults to 20 and is clamped to a maximum of 100. Asking for 500 returns 100 rather than raising an error, while a non numeric value is a 400. Since a request costs the same whatever it returns, raising count to 100 is the straightforward way to cut the number of billed calls on a full sweep.
Why does every community rule have a null description?
Because X only sends the rule id and name on this surface. The rules array is real and the names are real, but the description field arrives empty on every entry and is left that way rather than being filled in from the rule name. Treat a rule as a label rather than a paragraph. The same honesty applies to created_at, which is a millisecond epoch number and reads 0 when X sent nothing.
What does snapshotting a whole community cost?
Every route in the family bills at $0.0008. One community/info call plus one community/about call is two reads. The roster is what drives the bill: at the 100 row page cap, a 250,000 member community takes 2,500 calls, around two dollars, and the timeline is charged the same way per page. Under a ceiling of 600 requests a minute, that roster sweep finishes in about four minutes.

Start with $0.50 in free credits

No credit card. Roughly 12,500 tweets to test every endpoint.