Nimbryxo Studio

Use X (Twitter) Profile & Post Engagement Extractor with Python

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

1. Choose the data you need

Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.

2. Prepare a source-based input

This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.

{
  "activeWithinDays": 0,
  "enrichEngagement": false,
  "include": [
    "profileInfo"
  ],
  "includeHighlights": false,
  "includeReplies": false,
  "includeRetweets": false,
  "maxFollowers": 0,
  "maxPostsPerProfile": 3,
  "mediaType": "any",
  "minAccountAgeDays": 0,
  "minFollowers": 0,
  "onlyVerified": false,
  "onlyWithMedia": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "usernames": [
    "NASA"
  ],
  "verifiedType": "any",
  "withWebsiteOnly": false
}

Download input.example.json and save your reviewed copy as input.json.

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.

3. Start an Actor run

Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.

Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.

import json
import os
import urllib.request

# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
    actor_input = json.load(stream)

token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
    "https://api.apify.com/v2/acts/Sf31Jgh0Kbad3YVuJ/runs?timeout=120&maxTotalChargeUsd=0.50",
    data=json.dumps(actor_input).encode("utf-8"),
    headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
    method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
    run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.

4. Inspect the actual results

Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.

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.

Questions before you run

Which inputs are required?

No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.

What does this guide verify?

The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.

What if the run request times out?

Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.

Generated from source build pCbOAmk0HXdMNmZOp and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.