Nimbryxo Studio

X (Twitter) Profile & Post Engagement Extractor

Get any public X profile - bio, followers, verification, avatar, banner - plus latest, top and media posts with full engagement metrics. No login required.

Input schema

Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.

FieldTypeRequiredDescription
usernamesarrayoptionalAccounts to collect. Paste handles like `elonmusk`, `@apify`, profile links like `https://x.com/elonmusk`, numeric ids like `11348282` or `id:11348282`. Handles, links and ids can be mixed.
includearrayoptionalPick one or more data groups. Everything works without cookies. `topPosts` is engagement-ranked from the candidate pool (X serves no public Top tab to guests). `replies` and `highlights` are best-effort: X sometimes refuses those guest timelines.
postUrlsarrayoptionalOptional. Collect these exact posts (focal post plus up to 3 replies each) in addition to the profiles above. Accepts `https://x.com/user/status/123` or a numeric post id.
Show all input fields
FieldTypeRequiredDescription
usernamesarrayoptionalAccounts to collect. Paste handles like `elonmusk`, `@apify`, profile links like `https://x.com/elonmusk`, numeric ids like `11348282` or `id:11348282`. Handles, links and ids can be mixed.
includearrayoptionalPick one or more data groups. Everything works without cookies. `topPosts` is engagement-ranked from the candidate pool (X serves no public Top tab to guests). `replies` and `highlights` are best-effort: X sometimes refuses those guest timelines.
postUrlsarrayoptionalOptional. Collect these exact posts (focal post plus up to 3 replies each) in addition to the profiles above. Accepts `https://x.com/user/status/123` or a numeric post id.
maxPostsPerProfileintegeroptionalTarget number of posts per account (1-1000). The actor follows X's guest timeline bottom cursor back to X's ceiling of about 3,200 posts; guest search top-ups are best effort. Filter-heavy runs page until the target number of matching rows is found or the source ceiling is reached. A pinned post can be added beyond this cap when it passes your filters; it stays in chronological order for latest and in ranked order for top.
includeRepliesbooleanoptionalKeep replies the account sent. Best effort: X only serves the reply timeline to logged-out callers on some attempts.
includeRetweetsbooleanoptionalKeep reposts in the results. Turn off to get only posts the account wrote itself.
includeHighlightsbooleanoptionalAlso collect the account's curated Highlights tab. The guest legacy userId query pages the whole set through its bottom cursor (observed 100-1,097 rows on large accounts); the live one-page query remains a fallback.
dateFromstringoptionalOptional. Only keep posts published on or after this date.
dateTostringoptionalOptional. Only keep posts published on or before this date.
languagestringoptionalOptional. Keep only posts detected in this language, e.g. `en`, `es`, `ja`. Locale forms such as `en-US` or `pt-BR` are normalized to their two-letter code in code, so old tasks keep working.
minimumLikesintegeroptionalOptional. Drop posts with fewer likes than this.
minimumRetweetsintegeroptionalOptional. Drop posts with fewer reposts than this.
minimumRepliesintegeroptionalOptional. Drop posts with fewer replies than this.
minimumViewsintegeroptionalOptional. Drop posts with fewer views than this. Posts whose view count X does not expose are dropped when this is set.
minimumQuotesintegeroptionalOptional. Drop posts with fewer quotes than this. Posts whose quote count X does not expose are dropped when this is set.
onlyWithMediabooleanoptionalKeep only posts that contain media.
mediaTypestringoptionalOptional. Keep only posts with this media type. `media` in What to collect already limits rows to media posts.
onlyVerifiedbooleanoptionalSkip accounts that do not carry a verified badge.
verifiedTypestringoptionalOptional. Keep only accounts with this verification type. X and FxTwitter labels are normalized in code.
minFollowersintegeroptionalOptional. Skip accounts below this follower count. 0 means any.
maxFollowersintegeroptionalOptional. Skip accounts above this follower count. 0 means any.
minAccountAgeDaysintegeroptionalOptional. Skip accounts younger than this. 0 means any. Accounts without a public join date are skipped when this is set.
withWebsiteOnlybooleanoptionalSkip accounts that expose no website link in their profile or bio.
bioKeywordsarrayoptionalOptional. Keep only accounts whose name or bio contains every keyword (case-insensitive).
activeWithinDaysintegeroptionalOptional. Skip accounts whose newest public post is older than this many days. 0 means any. Requires reading the latest posts to evaluate.
enrichEngagementbooleanoptionalRefresh each collected post through its public status page for views, bookmarks and complete media. Adds one request per post. Turn off for the cheapest profile-only runs.
proxyConfigurationobjectoptionalOptional. The actor works without a proxy and tries a direct connection first. Use this only if you want to override the built-in direct -> Apify AUTO escalation.

Download the published input schema

View complete JSON
{
  "description": "Collect any public X profile and its posts without login, cookies or API keys.",
  "properties": {
    "activeWithinDays": {
      "default": 0,
      "description": "Optional. Skip accounts whose newest public post is older than this many days. 0 means any. Requires reading the latest posts to evaluate.",
      "maximum": 36500,
      "minimum": 0,
      "prefill": 0,
      "title": "Active within days",
      "type": "integer"
    },
    "bioKeywords": {
      "default": [],
      "description": "Optional. Keep only accounts whose name or bio contains every keyword (case-insensitive).",
      "editor": "stringList",
      "title": "Bio keywords",
      "type": "array"
    },
    "dateFrom": {
      "description": "Optional. Only keep posts published on or after this date.",
      "editor": "datepicker",
      "title": "Posted after",
      "type": "string"
    },
    "dateTo": {
      "description": "Optional. Only keep posts published on or before this date.",
      "editor": "datepicker",
      "title": "Posted before",
      "type": "string"
    },
    "enrichEngagement": {
      "default": true,
      "description": "Refresh each collected post through its public status page for views, bookmarks and complete media. Adds one request per post. Turn off for the cheapest profile-only runs.",
      "prefill": false,
      "title": "Enrich engagement per post",
      "type": "boolean"
    },
    "include": {
      "default": [
        "profileInfo",
        "latestPosts"
      ],
      "description": "Pick one or more data groups. Everything works without cookies. `topPosts` is engagement-ranked from the candidate pool (X serves no public Top tab to guests). `replies` and `highlights` are best-effort: X sometimes refuses those guest timelines.",
      "editor": "select",
      "items": {
        "enum": [
          "profileInfo",
          "latestPosts",
          "topPosts",
          "media",
          "replies",
          "highlights",
          "about"
        ],
        "enumTitles": [
          "Profile info (bio, followers, verification)",
          "Latest posts (newest first)",
          "Top posts (engagement-ranked)",
          "Media posts only (photos, videos, GIFs)",
          "Replies the account sent (best effort)",
          "Highlights tab (cursor-paged)",
          "About panel (login-only; reported as unavailable)"
        ],
        "type": "string"
      },
      "prefill": [
        "profileInfo"
      ],
      "title": "What to collect",
      "type": "array",
      "uniqueItems": true
    },
    "includeHighlights": {
      "default": false,
      "description": "Also collect the account's curated Highlights tab. The guest legacy userId query pages the whole set through its bottom cursor (observed 100-1,097 rows on large accounts); the live one-page query remains a fallback.",
      "prefill": false,
      "title": "Include Highlights tab",
      "type": "boolean"
    },
    "includeReplies": {
      "default": false,
      "description": "Keep replies the account sent. Best effort: X only serves the reply timeline to logged-out callers on some attempts.",
      "prefill": false,
      "title": "Include replies",
      "type": "boolean"
    },
    "includeRetweets": {
      "default": false,
      "description": "Keep reposts in the results. Turn off to get only posts the account wrote itself.",
      "prefill": false,
      "title": "Include reposts",
      "type": "boolean"
    },
    "language": {
      "description": "Optional. Keep only posts detected in this language, e.g. `en`, `es`, `ja`. Locale forms such as `en-US` or `pt-BR` are normalized to their two-letter code in code, so old tasks keep working.",
      "editor": "textfield",
      "title": "Post language",
      "type": "string"
    },
    "maxFollowers": {
      "default": 0,
      "description": "Optional. Skip accounts above this follower count. 0 means any.",
      "maximum": 1000000000,
      "minimum": 0,
      "prefill": 0,
      "title": "Maximum followers",
      "type": "integer"
    },
    "maxPostsPerProfile": {
      "default": 50,
      "description": "Target number of posts per account (1-1000). The actor follows X's guest timeline bottom cursor back to X's ceiling of about 3,200 posts; guest search top-ups are best effort. Filter-heavy runs page until the target number of matching rows is found or the source ceiling is reached. A pinned post can be added beyond this cap when it passes your filters; it stays in chronological order for latest and in ranked order for top.",
      "maximum": 1000,
      "minimum": 1,
      "prefill": 3,
      "title": "Maximum posts per profile",
      "type": "integer"
    },
    "mediaType": {
      "default": "any",
      "description": "Optional. Keep only posts with this media type. `media` in What to collect already limits rows to media posts.",
      "editor": "select",
      "enum": [
        "any",
        "photos",
        "videos",
        "gifs"
      ],
      "enumTitles": [
        "Any media",
        "Photos only",
        "Videos only",
        "GIFs only"
      ],
      "prefill": "any",
      "title": "Media type",
      "type": "string"
    },
    "minAccountAgeDays": {
      "default": 0,
      "description": "Optional. Skip accounts younger than this. 0 means any. Accounts without a public join date are skipped when this is set.",
      "maximum": 100000,
      "minimum": 0,
      "prefill": 0,
      "title": "Minimum account age (days)",
      "type": "integer"
    },
    "minFollowers": {
      "default": 0,
      "description": "Optional. Skip accounts below this follower count. 0 means any.",
      "maximum": 1000000000,
      "minimum": 0,
      "prefill": 0,
      "title": "Minimum followers",
      "type": "integer"
    },
    "minimumLikes": {
      "description": "Optional. Drop posts with fewer likes than this.",
      "maximum": 1000000000,
      "minimum": 0,
      "title": "Minimum likes",
      "type": "integer"
    },
    "minimumQuotes": {
      "description": "Optional. Drop posts with fewer quotes than this. Posts whose quote count X does not expose are dropped when this is set.",
      "maximum": 1000000000,
      "minimum": 0,
      "title": "Minimum quotes",
      "type": "integer"
    },
    "minimumReplies": {
      "description": "Optional. Drop posts with fewer replies than this.",
      "maximum": 1000000000,
      "minimum": 0,
      "title": "Minimum replies",
      "type": "integer"
    },
    "minimumRetweets": {
      "description": "Optional. Drop posts with fewer reposts than this.",
      "maximum": 1000000000,
      "minimum": 0,
      "title": "Minimum reposts",
      "type": "integer"
    },
    "minimumViews": {
      "description": "Optional. Drop posts with fewer views than this. Posts whose view count X does not expose are dropped when this is set.",
      "maximum": 1000000000000,
      "minimum": 0,
      "title": "Minimum views",
      "type": "integer"
    },
    "onlyVerified": {
      "default": false,
      "description": "Skip accounts that do not carry a verified badge.",
      "prefill": false,
      "title": "Only verified accounts",
      "type": "boolean"
    },
    "onlyWithMedia": {
      "default": false,
      "description": "Keep only posts that contain media.",
      "prefill": false,
      "title": "Only posts with images, videos or GIFs",
      "type": "boolean"
    },
    "postUrls": {
      "default": [],
      "description": "Optional. Collect these exact posts (focal post plus up to 3 replies each) in addition to the profiles above. Accepts `https://x.com/user/status/123` or a numeric post id.",
      "editor": "stringList",
      "example": [
        "https://x.com/NASA/status/2114801524358406570"
      ],
      "title": "Specific post URLs or ids",
      "type": "array"
    },
    "proxyConfiguration": {
      "description": "Optional. The actor works without a proxy and tries a direct connection first. Use this only if you want to override the built-in direct -> Apify AUTO escalation.",
      "editor": "proxy",
      "prefill": {
        "useApifyProxy": false
      },
      "sectionCaption": "Advanced",
      "sectionDescription": "Optional extras. The actor works fully without these.",
      "title": "Proxy configuration",
      "type": "object"
    },
    "usernames": {
      "default": [],
      "description": "Accounts to collect. Paste handles like `elonmusk`, `@apify`, profile links like `https://x.com/elonmusk`, numeric ids like `11348282` or `id:11348282`. Handles, links and ids can be mixed.",
      "editor": "stringList",
      "example": [
        "NASA",
        "https://x.com/apify",
        "id:11348282"
      ],
      "prefill": [
        "NASA"
      ],
      "title": "X usernames, profile links or numeric ids",
      "type": "array"
    },
    "verifiedType": {
      "default": "any",
      "description": "Optional. Keep only accounts with this verification type. X and FxTwitter labels are normalized in code.",
      "editor": "select",
      "enum": [
        "any",
        "blue",
        "business",
        "government"
      ],
      "enumTitles": [
        "Any verification",
        "Blue / individual",
        "Business / organization",
        "Government"
      ],
      "prefill": "any",
      "title": "Verification type",
      "type": "string"
    },
    "withWebsiteOnly": {
      "default": false,
      "description": "Skip accounts that expose no website link in their profile or bio.",
      "prefill": false,
      "title": "Only accounts with a website",
      "type": "boolean"
    }
  },
  "required": [],
  "schemaVersion": 1,
  "title": "X (Twitter) Profile & Post Engagement Extractor",
  "type": "object"
}

Output schema

Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.

FieldTypeRequiredDescription
profilesstringoptionalOpen the default dataset: profile rows (bio, counters, verification, pinned post), post rows (engagement, media, provenance) and explicit status rows.

Download the published output schema

View complete JSON
{
  "actorOutputSchemaVersion": 1,
  "description": "One profile row per account, one post row per collected post (cursor-paged up to maxPostsPerProfile), and status rows for protected, missing, empty, filtered or limited accounts. Every row carries type, recordType, sourceUrl, collectedAt and scrapedAt.",
  "properties": {
    "profiles": {
      "description": "Open the default dataset: profile rows (bio, counters, verification, pinned post), post rows (engagement, media, provenance) and explicit status rows.",
      "template": "{{links.apiDefaultDatasetUrl}}/items",
      "title": "Profiles, posts and statuses",
      "type": "string"
    }
  },
  "title": "X (Twitter) Profile & Post Engagement Extractor Output",
  "type": "object"
}

Dataset schema

Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.

Download the published dataset schema

View complete JSON
{
  "actorSpecification": 1,
  "views": {
    "overview": {
      "display": {
        "component": "table",
        "properties": {}
      },
      "title": "Profiles, posts and statuses",
      "transformation": {
        "fields": [
          "type",
          "recordType",
          "status",
          "message",
          "username",
          "name",
          "text",
          "createdAt",
          "source",
          "likeCount",
          "retweetCount",
          "replyCount",
          "quoteCount",
          "viewCount",
          "bookmarkCount",
          "totalEngagement",
          "engagementRate",
          "mediaTypes",
          "isPinned",
          "isHighlight",
          "url",
          "sourceUrl",
          "profileUrl",
          "followers",
          "following",
          "postCount",
          "verifiedType",
          "collectedAt",
          "scrapedAt"
        ]
      }
    }
  }
}

Key-value schema

Published key-value store definitions, when provided by this build.

This definition is not present in the default published build. See the published README for available documentation.

Published README

Read the full published documentation

X (Twitter) Profile & Post Engagement Extractor

Collect public X profiles and posts without login, cookies, developer keys or API tokens. The actor returns a profile row, post rows with engagement and media, and explicit status rows when X limits or hides something.

What it collects

  • Profile header from the public profile page: handle, display name, bio, bio links and emails, location, website, join date, follower/following/post/media/like counters, verification type, avatar, banner, public birth date, pinned post and professional label.
  • Latest posts from the profile's server-rendered originals timeline, strictly newest first, topped up through the cursor-paged timeline when the target is higher than the 5-9 server-rendered rows. A pinned post is marked isPinned but stays in its chronological position (and may exceed the cap by one).
  • Top posts ranked by total engagement. X exposes no public Top tab to logged-out callers, so the actor combines the guest search Top product with the profile timeline candidate pool and ranks the merged set locally; a pinned post is not promoted above its earned rank.
  • Media posts by filtering the collected timelines to photos, videos and GIFs. X does not serve a full media tab to logged-out callers. Filter-heavy runs page the timeline until the requested number of matching rows is found or the source ceiling is reached, so rare GIF matches are not missed by a fixed buffer.
  • Replies and Highlights as best-effort groups. Highlights are cursor-paged through the legacy guest query, so large curated sets return their full observed depth; replies remain limited. X sometimes refuses those guest timelines; the actor reports repliesLimited or highlightsLimited instead of pretending the account has none.
  • Specific post URLs through postUrls: the focal post plus up to three server-rendered replies per URL.
  • Derived activity metrics on profile rows: account age, followers/following ratio, posts per day, last post date, days since the last post, recent average likes/replies/reposts/views and a recent engagement percentage.
  • Provenance and timestamps on every row: type, recordType, sourceUrl, collectedAt and scrapedAt.

The actor never signs in, never asks for cookies and never uses a user-supplied token.

Input

Field Type Default Description
usernames array – Optional; supply this, postUrls, or both. Handles (NASA, @NASA), profile links (https://x.com/NASA), numeric ids (11348282, id:11348282) or https://x.com/i/user/11348282.
include multi-select ["profileInfo", "latestPosts"] profileInfo, latestPosts, topPosts, media, replies, highlights; legacy about is accepted but reported as unavailable. Groups are additive.
postUrls array [] Exact post URLs or ids; can run alone or alongside profiles. Each returns the focal post and up to 3 replies.
maxPostsPerProfile integer 1-1000 50 Post target per account. The actor follows X's guest timeline bottom cursor (X's own ceiling is about 3,200 posts). When a target is still unmet, older posts are filled from search windows only when X serves them.
includeReplies boolean false Keep replies the account sent (best effort).
includeRetweets boolean false Keep reposts.
includeHighlights boolean false Also collect the Highlights tab. Cursor-paged up to maxPostsPerProfile; observed depths include 113, 287 and 1,097 rows on different accounts.
dateFrom, dateTo date picker – Client-side post date window. dateTo includes the whole day.
language text – Post language, e.g. en, es, ja. Locale forms such as en-US and pt-BR are normalized to en/pt in code, so old tasks keep working.
minimumLikes, minimumRetweets, minimumReplies, minimumViews, minimumQuotes integer ≥ 0 – Client-side engagement minimums. Posts missing the requested count are dropped when the minimum is set.
onlyWithMedia boolean false Keep only posts with photos, videos or GIFs.
mediaType select any any, photos, videos, gifs.
onlyVerified boolean false Skip accounts without a verification badge.
verifiedType select any any, blue, business, government. X and mirror labels are normalized in code.
minFollowers, maxFollowers integer ≥ 0 0 Account-level follower range. 0 means any.
minAccountAgeDays integer ≥ 0 0 Skip accounts younger than this. Accounts without a public join date are skipped when set.
withWebsiteOnly boolean false Skip accounts with no website in the profile or bio.
bioKeywords array [] Keep accounts whose name or bio contains every keyword (case-insensitive).
activeWithinDays integer ≥ 0 0 Skip accounts whose newest public post is older than this.
enrichEngagement boolean true Refresh each collected post through its public status page for views, bookmarks and complete media. One request per post; turn off for the cheapest profile-only runs.
proxyConfiguration proxy – Optional override. The actor works without a proxy and tries the direct connection first.

Examples

Profile only (the daily prefill shape):

{
  "usernames": ["NASA"],
  "include": ["profileInfo"],
  "maxPostsPerProfile": 3,
  "enrichEngagement": false
}

Profile plus latest and top posts:

{
  "usernames": ["NASA", "https://x.com/apify"],
  "include": ["profileInfo", "latestPosts", "topPosts"],
  "maxPostsPerProfile": 100
}

High-engagement photo posts from a date range:

{
  "usernames": ["NASA"],
  "include": ["latestPosts"],
  "mediaType": "photos",
  "minimumLikes": 5000,
  "minimumViews": 1000000,
  "dateFrom": "2026-01-01",
  "dateTo": "2026-10-27"
}

Lead-gen profile filter:

{
  "usernames": ["apify", "NASA"],
  "include": ["profileInfo"],
  "minFollowers": 10000,
  "minAccountAgeDays": 365,
  "withWebsiteOnly": true,
  "bioKeywords": ["space"]
}

Specific posts:

{
  "usernames": ["NASA"],
  "include": ["profileInfo"],
  "postUrls": ["https://x.com/NASA/status/2114801524358406570"]
}

Output

Every row has type (profile, post or status), recordType, collectedAt and scrapedAt. Profile and post rows, and status rows with a known account or post URL, also carry sourceUrl. Empty fields are omitted recursively: the dataset never contains null, "", [] or {}.

Profile row

Keeps the original fields: id, username, name, bio, bioLinks, bioEmails, location, website, joinedAt, birthDate, followers, following, postCount, mediaCount, likesGiven, listedCount, isBlueVerified, verified, verifiedType, label, isProtected, avatarUrl, bannerUrl, pinnedPostId, pinnedTweetId, pinnedTweet, mediaTweets, withReplies, profileStatus, collectedAt.

Adds: profileUrl, websiteDisplayUrl, bioMentions, accountAgeDays, followersToFollowingRatio, postsPerDay, lastPostAt, daysSinceLastPost, recentAvgLikes, recentAvgReplies, recentAvgReposts, recentAvgViews, recentEngagementRatePct, professionalType, professionalCategory, affiliateLabel, highlightCount, recordType, sourceUrl, scrapedAt.

websiteDisplayUrl is the label X shows for the profile's website entity (for example nasa.gov). Bio links stay in bioLinks and are never reused as the website label.

Conditional fields are omitted when X does not expose them. listedCount is not guaranteed by current cookieless payloads.

Post row

Keeps the original fields: id, url, text, createdAt, language, replyCount, retweetCount, likeCount, quoteCount, bookmarkCount, viewCount, engagementRate, isReply, isRetweet, isQuote, isNoteTweet, isPinned, conversationId, threadRootId, threadPosition, inReplyToTweetId, inReplyToUserId, inReplyToUsername, hashtags, mentions, urls, cashtags, media, quotedTweet, quotedTweetId, poll, card, possiblySensitive, place, postedFrom, withheldInCountries, isEdited, editControl, newsActionType, parentTweet, author, username, collectedAt.

Adds: totalEngagement, engagementPerView, mediaTypes, source, isHighlight, authorId, authorUsername, authorName, recordType, sourceUrl, scrapedAt.

source is provenance: profileLatest, profileTop, profileHighlight, profileReplies, syndication, search or status.

conversationId and threadRootId are set on postUrls replies from the focal post when the status page omits them, so every reply can be grouped by conversation.

totalEngagement is emitted only when likes, reposts, replies and quotes are all real numbers. engagementPerView additionally needs a positive view count. No count is invented.

Media item: type (photo / video / gif), url, previewImageUrl, width, height, durationSeconds, viewCount, altText, variants, and where X provides them mediaKey, allowDownload, mediaAvailability, focusRects. Video url prefers the best MP4 at or below 1080p; the full variant list (including 4K) remains in variants.

Status row

Status rows explain a skipped, protected, missing, empty or limited account:

Status Meaning
noInput No usable usernames or post URLs were supplied.
invalidUsername A username/link/id could not be parsed.
notFound The account does not exist, was removed or is suspended.
rateLimited X temporarily limited the profile request.
skipped An account-level filter excluded the account. The message names the filter.
protected The account is protected; only its public header is returned.
repliesLimited Replies were requested but X served none or fewer than the target this run; achieved and target are included.
highlightsLimited Highlights were requested but X returned no rows or refused the request this run.
noMediaPosts Media mode found no matching photos, videos or GIFs.
noPosts Posts were collected but every one was filtered out.
noVisiblePosts The account exists but X shows no public posts.
timelineUnavailable The profile loaded but its posts could not be collected.
searchLimited Fewer posts than requested were available; achieved and target are included.
aboutUnavailable A legacy task requested X's login-only About panel.
error An unexpected per-profile failure.

Data source and limits

  • Primary: the public profile HTML, including X's server-rendered GraphQLRequestStream.Completed payload. This is the same page any logged-out visitor sees.
  • Fallbacks: guest GraphQL (UserByScreenName header, cursor-paged UserTweets ranked timeline, Highlights), the syndication timeline, and the public FxTwitter mirror. Guest UserByScreenName is merged before FxTwitter; FxTwitter is a third-party mirror and remains the last resort for post views or a missing header field.
  • No credentials: no cookies, login, API key or user proxy is required.
  • Proxy: the actor tries a direct connection first, then the Apify AUTO datacenter tier after a real block. A paid residential tier is hardcoded as a last resort and is never a schema default. A user-supplied proxyConfiguration overrides the built-in ladder.

Honest limits

  • About panel: login-only. The legacy about value remains in the include enum for saved-task compatibility and is labeled as unavailable; requesting it emits an aboutUnavailable status instead of failing the task.
  • Full replies: X does not serve the full reply tab to logged-out callers, and the guest reply timeline/search refuses most attempts. Replies remain best effort and report repliesLimited when empty; use postUrls for a focal post plus up to three server-rendered replies.
  • Full media tab: X's /media route is a login wall for guests and the guest UserMedia operation returns 404. Media mode filters the collected timelines instead; cursor pagination can now collect far more media posts than the old one-page path.
  • Deep history: the guest UserTweets bottom cursor pages a profile timeline back to X's ceiling of about 3,200 posts, and maxPostsPerProfile supports 1-1000. Guest search is not reliable, so older-row top-ups beyond the cursor are best effort.
  • Highlights: the old userId Highlights query pages the curated set through its bottom cursor, not a fixed 20-row page. Observed depths include 113 (second-opinion sample), 287 (NASA) and 1,097 (elonmusk), bounded by the account's Highlights set and maxPostsPerProfile. The live screenName query remains a one-page fallback.
  • Syndication quota: about 30 profile timeline lookups per IP per 15 minutes. The actor falls back to guest GraphQL and FxTwitter instead of retrying forever.
  • listedCount: best effort only. Syndication returns a placeholder 0 and that value is never published; only a search author snapshot carries the real count, and guest search itself is limited.
  • postedFrom, place: conditional. postedFrom is filled when the timeline row carries a top-level source; place is not exposed by current cookieless payloads and is omitted.
  • Views: recent posts get views from the status page; tweets from before X introduced view counts have no view count anywhere.
  • 4K video: the 4K MP4 can be hundreds of megabytes. media.url prefers 1080p or lower; the 4K URL stays in variants.

FAQ

Do I need an X account or developer key? No. The actor only reads public pages and needs no credential input.

Why are there fewer posts than maxPostsPerProfile? The account may have fewer public posts in the reachable timeline, the filters may remove rows, or X may refuse the guest search top-up. The actor merges and cursor-pages every public source, then reports searchLimited with the achieved count only when a real source cap leaves the target unmet.

Why is a count missing instead of 0? A missing field means X did not expose it. The actor never substitutes a sentinel or an invented zero. A real zero (for example quoteCount: 0) is preserved.

Why did my account get a skipped row? An account-level filter (onlyVerified, verifiedType, follower range, account age, website, bio keywords or activeWithinDays) excluded it. The message says which one.

Can I collect protected accounts? Only their public header. Posts require approval from the account owner, which the actor does not attempt.

Is the data live? Yes. Every run reads X at request time; schedule the actor for hourly, daily or weekly refreshes.

How do I use the dataset? Use the run page, the Apify API, webhooks, or a no-code connector such as Zapier, Make or Google Sheets.

Is this affiliated with X? No. It is an independent third-party actor that reads publicly available pages.

Development

Run the offline tests from the actor folder:

python -m pytest tests/ -q

Run locally with the Apify CLI:

apify run --purge --input-file input.json

Python quickstart

Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.

Open the step-by-step Python guide →

Source build pCbOAmk0HXdMNmZOp. Documentation is regenerated from the default published build; private drafts are not included.