TwitterAPIs Docs
API ReferenceCommunities

X Community Tweets API | Read Community Posts

Fetch the post timeline of an X Community, cursor-paginated as full tweet objects, with optional ordering control. The community's pinned post is returned as its own field rather than mixed into the feed, because X delivers it under a different timeline instruction and does not repeat it in the feed. Billed at $0.0008 per call. Cost: $0.0008 per call.

GET community/tweets returns an X Community's own post timeline, cursor-paginated as full tweet objects, plus the community's PINNED post as its own separate pinned field rather than an item inside tweets. X delivers the pinned post under a different timeline instruction and does not repeat it in the feed, so a client that reads only tweets silently misses it, and it is often the community's rules post. ranking_mode is a real upstream parameter, not a local sort. $0.0008 per call.

GET
/community/tweets

Authorization

bearerAuth
AuthorizationBearer <token>

Pass your API key as a bearer token on every request: Authorization: Bearer <API_KEY>.

In: header

Query Parameters

community_id*string

The numeric community id, the digits in a x.com/i/communities/ URL.

cursor?string

Pagination cursor from a previous response's next_cursor. Omit on the first call.

count?integer

Page size. Defaults to 20 and is clamped to 1-100. A non-numeric value is a 400.

ranking_mode?string

Ordering, sent to X as a real upstream parameter rather than applied as a local 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 has not been confirmed live, so do not depend on it. Any other value is a 400.

fields?string

Comma-separated list of dotted field paths to keep in the response (one parameter, never repeated), applied to every object in the returned lists and to nested objects (for example fields=id,text,author.username; the list's own name may prefix a path, fields=tweets.id). A path prefix that lands on an array applies to each element (media.media_url_https). Pagination and envelope keys (next_cursor, cursor, has_more, count, partial, error, message, reason) are always kept. Unknown paths are ignored; up to 50 paths of up to 6 segments. Projection never changes status codes or billing, and applies to data reads only: account, feedback, session status, monitor (other than deliveries), webhook and stream-compat reads return their bodies unchanged.

compact?string

Set to 1 (or true) to apply the built-in compact preset: ids, url, text, created_at, lang, the engagement counts, the is_retweet/is_reply/is_quote flags, conversation ids, the author's id/username/name/followers_count/verification, and the quoted or retweeted tweet's id/url/author username. The preset trims what it recognises and leaves alone what it would empty, so it never turns a body into {}. Combine with fields to add paths on top of the preset. Typically cuts a tweet page by 70 to 90 percent for LLM tool callers.

paid_promotion?string

Filter this page's tweets by X's Paid partnership label: only keeps tweets whose is_paid_promotion is true, exclude keeps the rest. Omit it for every tweet. It filters the page X returned and does not fetch more, so a filtered page can hold fewer tweets than asked for, or none, while next_cursor still pages on. The response carries paid_promotion_filter { mode, kept, removed }, and count reflects what is returned. Any other value is a 400. Billing is the same as without it.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/community/tweets?community_id=1493446837214187523&count=20&ranking_mode=Recency&fields=id%2Ctext%2Ccreated_at%2Cauthor.username&compact=1&paid_promotion=only"
{  "pinned": {    "id": "1926022758195921145",    "text": "Community rules: be useful, no unsolicited DMs, no engagement bait.",    "created_at": "Fri May 23 09:14:02 +0000 2026",    "author": {      "id": "1281109705495130113",      "username": "pizzaboy",      "name": "Dan Holdsworth"    },    "favorite_count": 118  },  "tweets": [    {      "id": "1926104411223344556",      "text": "Shipped the billing rewrite this morning. Numbers in the thread.",      "created_at": "Fri May 23 14:41:19 +0000 2026",      "author": {        "id": "44196397",        "username": "devjane",        "name": "Jane"      },      "favorite_count": 26,      "reply_count": 4    }  ],  "next_cursor": "DAABCgABF...",  "has_more": true}
{  "error": "bad_request",  "message": "Missing or malformed parameter. Fix the request before retrying."}
{  "error": "unauthorized",  "message": "The API key is missing, malformed, or revoked. Check the Authorization header."}
{  "error": "insufficient_credits",  "message": "Your balance is exhausted. Top up your credits to continue."}
{  "error": "forbidden",  "message": "X refused this resource for the acting account: on a read, the account is not a participant in the DM thread, the target is protected or has blocked it, or the session was attestation-blocked for calling from a datacenter or rotating IP; on a write, the account is not authorized for the action or has no logged-in session. The credentials are still valid, so do not re-register."}
{  "error": "not_found",  "message": "The resource does not exist, for example a deleted tweet or a private account."}
{  "error": "rate_limited",  "message": "Too many requests. Back off and retry with exponential backoff."}
{  "error": "server_error",  "message": "Something failed on our side. Retry with backoff; if it persists, contact support."}

Pricing

UnitPrice
Per call$0.0008
Per 1,000 calls$0.80
Per 1,000 records (~20 per call)~$0.04

When to use

Use this to read what is being posted inside a community, for monitoring or for analysis. Use Advanced Tweet Search when you want posts across all of X rather than inside one community, and Community Info when you want the community itself rather than its posts.

Notes

  • THE PINNED POST IS ITS OWN FIELD AND IS NOT IN tweets. X delivers it under a different timeline instruction from the feed and does not duplicate it there, so a client that iterates only tweets loses it silently. It is usually the community's rules post. Read pinned first, then tweets, if you want one flat list.
  • ranking_mode is a REAL upstream parameter, not a local re-sort. Recency is the default and the only value confirmed against a live capture. Relevance is accepted on the strength of X's own community tab offering two orderings, and is deliberately not advertised as confirmed until a capture shows it.
  • This endpoint is cached for a shorter window than the other community reads, because it is the one surface callers poll for new posts. The rest of the community family is cached longer, since a community's metadata and roster change slowly.
  • Every tweet on this timeline carries a caller-relative moderation state upstream. It is never extracted, so it does not reach you: like the nulled community fields, it would describe our pool account rather than yours.
  • A 404 is returned only on an empty FIRST page, meaning the id did not resolve or the timeline is not visible. If X returns a shape this API does not recognise, you get a 502 parse_failed rather than a 200 with an empty feed, and that call is not billed to you.

Pagination

Pass the returned next_cursor straight back as ?cursor= for the next page. Stop when tweets comes back empty or has_more is false. Note that the pinned post is NOT part of the paged feed: it comes back on the page you ask for and is not an item you will meet again while paging.

FAQ

Why is the pinned post not in the tweets array?

Because X does not put it there. It arrives under a separate timeline instruction and is not repeated in the feed, so merging it in would place an item at a position X never gave it, and a client paging the feed would meet it on no page and at no stable offset. Returning it as its own field is the only way to hand it over without inventing an ordering.

Is ranking_mode a real X parameter or your own sorting?

It is a real upstream parameter sent to X. Recency is the default and is the only value confirmed against a live capture; Relevance is accepted because X's own community tab offers two orderings, but treat it as unconfirmed until it is documented as observed here.

How do I build one flat list of community posts?

Read pinned first if it is non-null, then iterate tweets. The pinned post is excluded from tweets, so this produces no duplicate.

How fresh is the data?

This endpoint is cached for a much shorter window than the other community reads, precisely because it is the one people poll for new posts. Community info and the rosters are cached longer, since they change slowly.