--- url: /getting-started/authentication.md --- # Authentication Before using emusks, you must authenticate your client. emusks supports two authentication methods: **auth token** login and **username/password** login. ## Auth token login The simplest way to authenticate is with a Twitter/X auth token. This is a cookie value (`auth_token`) from an active browser session. ```js import Emusks from "emusks"; const client = new Emusks(); await client.login("your_auth_token_here"); ``` You may also provide an object if you'd like to configure proxies or a custom client: ```js await client.login({ auth_token: "your_auth_token_here", // e.g. proxy: "protocol://user:pass@host:port", }); ``` ### Finding your auth token 1. Open [x.com](https://x.com) in your browser and log in 2. Open Developer Tools (`F12` or `Cmd+Shift+I`) 3. Go to **Application** → **Cookies** → `https://x.com` 4. Find the cookie named `auth_token` 5. Copy its value ## Username & password login You can also log in with your account credentials. This method handles the full login flow, including two-factor authentication and email verification challenges. ```js const client = new Emusks(); await client.login({ type: "password", username: "your_username", password: "your_password", }); ``` ::: warning We do **not** recommend using username/password login as it is very unreliable and may randomly break. Using an auth token is the recommended way to log in. ::: ### 2FA & email verification If your account has two-factor authentication enabled or Twitter requests an email/phone verification, use the `onRequest` callback to provide the required codes: ```js const client = new Emusks(); await client.login({ type: "password", username: "your_username", password: "your_password", onRequest: async (type) => { if (type === "two_factor_code") { // Return your 2FA code (e.g. from an authenticator app) return "123456"; } if (type === "email_code") { // Return the code sent to your email return "654321"; } }, }); ``` `onRequest` is blocking. If Twitter asks for a code, your callback must return it before the login can continue. We recommend making your app support email code verification if you rely on username/password login. Twitter commonly prompts for email/phone verification when logging in from emusks as it can't solve Castle challenges. If you do rely on this, I also recommend setting all data so it can handle as much of the login flow as possible without needing to prompt you: ```js const client = new Emusks(); await client.login({ type: "password", username: "your_username", password: "your_password", email: "your_email@example.com", phone: "+1234567890", onRequest: async (type) => { if (type === "two_factor_code") return "123456"; }, }); ``` ## Reference | Option | Type | Description | | ------------ | ---------- | ------------------------------------------------------------------------------- | | `auth_token` | `string` | Your Twitter/X auth token (use directly as the argument to `login()`) | | `type` | `string` | Set to `"password"` for username/password login | | `username` | `string` | Your Twitter/X username | | `password` | `string` | Your account password | | `email` | `string` | Email for alternate identifier challenges | | `phone` | `string` | Phone number for alternate identifier challenges | | `onRequest` | `function` | Async callback for interactive login challenges (2FA, email verification, etc.) | ## Elevated Access Some sensitive actions, like reading settings, require elevated access. After logging in, call `elevate()` with your password: ```js await client.login("your_auth_token"); // Elevate your session for sensitive operations await client.elevate("your_password"); // Now you can perform privileged actions ``` ::: tip You only need to elevate once per session. The elevated state persists until the session ends. ::: ## Checking your session After logging in, you can verify your session by fetching your own profile: ```js const me = await client.account.viewer(); console.log(`Logged in as @${me.username}`); console.log(`Followers: ${me.stats.followers.count}`); ``` or by checking the output of the `client.login()` method, which returns your user object on successful authentication. ## Next steps Head over to [Configuration](/getting-started/configuration) to learn how to choose which client to emulate, set up proxies, and customize your setup. --- --- url: /tweets/bookmarks.md --- # Bookmarks Bookmark tweets, manage bookmark folders, and search your bookmarks. ## `bookmarks.create(tweetId)` Bookmark a tweet. Uses the GraphQL `CreateBookmark` mutation. ```js await client.bookmarks.create("1234567890"); ``` ## `bookmarks.delete(tweetId)` Remove a tweet from your bookmarks. Uses the GraphQL `DeleteBookmark` mutation. ```js await client.bookmarks.delete("1234567890"); ``` ## `bookmarks.deleteAll()` Delete all your bookmarks. Uses the GraphQL `BookmarksAllDelete` mutation. ```js await client.bookmarks.deleteAll(); ``` ::: warning This action is irreversible. All bookmarks will be permanently removed. ::: ## `bookmarks.get(opts?)` Fetch your bookmarked tweets. Uses the GraphQL `Bookmarks` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets, nextCursor } = await client.bookmarks.get(); console.log(tweets[0].text); // Paginate const page2 = await client.bookmarks.get({ cursor: nextCursor }); // Fetch more const more = await client.bookmarks.get({ count: 50 }); ``` ## `bookmarks.search(query, opts?)` Search within your bookmarks. Uses the GraphQL `BookmarkSearchTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.bookmarks.search("javascript"); ``` ## `bookmarks.folders()` Get all your bookmark folders. Uses the GraphQL `BookmarkFoldersSlice` query. ```js const folders = await client.bookmarks.folders(); ``` ## `bookmarks.createFolder(name)` Create a new bookmark folder. Uses the GraphQL `createBookmarkFolder` mutation. ```js await client.bookmarks.createFolder("Read Later"); ``` ## `bookmarks.deleteFolder(folderId)` Delete a bookmark folder. Uses the GraphQL `DeleteBookmarkFolder` mutation. ```js await client.bookmarks.deleteFolder("1234567890"); ``` ## `bookmarks.editFolder(folderId, name)` Rename a bookmark folder. Uses the GraphQL `EditBookmarkFolder` mutation. ```js await client.bookmarks.editFolder("1234567890", "New Folder Name"); ``` ## `bookmarks.addToFolder(tweetId, folderId)` Add a bookmarked tweet to a specific folder. Uses the GraphQL `bookmarkTweetToFolder` mutation. ```js await client.bookmarks.addToFolder("9876543210", "1234567890"); ``` ## `bookmarks.removeFromFolder(tweetId, folderId)` Remove a tweet from a bookmark folder. Uses the GraphQL `RemoveTweetFromBookmarkFolder` mutation. ```js await client.bookmarks.removeFromFolder("9876543210", "1234567890"); ``` ## `bookmarks.folderTimeline(folderId, opts?)` Get bookmarked tweets within a specific folder. Uses the GraphQL `BookmarkFolderTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `folderId` | `string` | The bookmark folder ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets, nextCursor } = await client.bookmarks.folderTimeline("1234567890"); // Paginate const page2 = await client.bookmarks.folderTimeline("1234567890", { count: 40, cursor: nextCursor, }); ``` ## Full example ```js // Bookmark a tweet await client.bookmarks.create("1234567890"); // Create a folder and organize await client.bookmarks.createFolder("Tech"); const folders = await client.bookmarks.folders(); const techFolder = folders; // extract folder ID from response // Add the tweet to the folder await client.bookmarks.addToFolder("1234567890", techFolderId); // Browse folder contents const { tweets } = await client.bookmarks.folderTimeline(techFolderId); // Search bookmarks const { tweets: results } = await client.bookmarks.search("machine learning"); // Clean up await client.bookmarks.removeFromFolder("1234567890", techFolderId); await client.bookmarks.delete("1234567890"); await client.bookmarks.deleteFolder(techFolderId); ``` --- --- url: /users/communities.md --- # Communities Create, join, manage, and moderate Twitter/X communities. ## `communities.create(name, opts?)` Create a new community. Uses the GraphQL `CreateCommunity` mutation. | Option | Type | Description | | ------------------ | -------- | ---------------------------- | | `name` | `string` | The community name | | `opts.description` | `string` | Community description | | `opts.rules` | `array` | Initial community rules | | `opts.variables` | `object` | Additional GraphQL variables | ```js await client.communities.create("JavaScript Enthusiasts"); await client.communities.create("Rust Developers", { description: "A community for Rust programmers", rules: [{ name: "Be respectful" }, { name: "No spam" }], }); ``` ## `communities.get(communityId)` Get a community by its REST ID. Uses the GraphQL `CommunityByRestId` query. ```js const community = await client.communities.get("1234567890"); ``` ## `communities.join(communityId)` Join a community. Uses the GraphQL `JoinCommunity` mutation. ```js await client.communities.join("1234567890"); ``` ## `communities.leave(communityId)` Leave a community. Uses the GraphQL `LeaveCommunity` mutation. ```js await client.communities.leave("1234567890"); ``` ## `communities.requestJoin(communityId, opts?)` Request to join a community that requires approval. Uses the GraphQL `RequestToJoinCommunity` mutation. | Option | Type | Description | | ------------- | -------- | ---------------------------------- | | `opts.answer` | `string` | Answer to the community's question | ```js await client.communities.requestJoin("1234567890"); await client.communities.requestJoin("1234567890", { answer: "I love TypeScript and have 5 years of experience", }); ``` ## `communities.timeline(communityId, opts?)` Get the community's tweet timeline. Uses the GraphQL `CommunityTweetsTimeline` query. | Option | Type | Description | | ------------------ | -------- | -------------------------------------------------- | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.rankingMode` | `string` | `"Recency"` or `"Relevance"` (default `"Recency"`) | | `opts.variables` | `object` | Additional GraphQL variables | ```js const tweets = await client.communities.timeline("1234567890"); // Ranked by relevance const top = await client.communities.timeline("1234567890", { rankingMode: "Relevance", count: 50, }); // Paginate const next = await client.communities.timeline("1234567890", { cursor: "DAABCgAB...", }); ``` ## `communities.media(communityId, opts?)` Get media tweets from a community. Uses the GraphQL `CommunityMediaTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const media = await client.communities.media("1234567890", { count: 40 }); ``` ## `communities.about(communityId, opts?)` Get the community's about timeline. Uses the GraphQL `CommunityAboutTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const about = await client.communities.about("1234567890"); ``` ## `communities.hashtags(communityId, opts?)` Get trending hashtags for a community. Uses the GraphQL `CommunityHashtagsTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const hashtags = await client.communities.hashtags("1234567890"); ``` ## `communities.editName(communityId, name)` Rename a community. Uses the GraphQL `CommunityEditName` mutation. ```js await client.communities.editName("1234567890", "New Community Name"); ``` ## `communities.editPurpose(communityId, purpose)` Update a community's purpose/description. Uses the GraphQL `CommunityEditPurpose` mutation. ```js await client.communities.editPurpose("1234567890", "A place for web developers"); ``` ## `communities.editBanner(communityId, mediaId)` Update a community's banner image. Uses the GraphQL `CommunityEditBannerMedia` mutation. ```js await client.communities.editBanner("1234567890", "9876543210"); ``` ## `communities.removeBanner(communityId)` Remove a community's banner image. Uses the GraphQL `CommunityRemoveBannerMedia` mutation. ```js await client.communities.removeBanner("1234567890"); ``` ## `communities.createRule(communityId, name, opts?)` Add a rule to a community. Uses the GraphQL `CommunityCreateRule` mutation. | Option | Type | Description | | ------------------ | -------- | ---------------- | | `communityId` | `string` | The community ID | | `name` | `string` | Rule name/title | | `opts.description` | `string` | Rule description | ```js await client.communities.createRule("1234567890", "No spam", { description: "Do not post promotional content or repetitive links", }); ``` ## `communities.editRule(communityId, ruleId, name, opts?)` Edit an existing community rule. Uses the GraphQL `CommunityEditRule` mutation. | Option | Type | Description | | ------------------ | -------- | ------------------------ | | `communityId` | `string` | The community ID | | `ruleId` | `string` | The rule ID to edit | | `name` | `string` | Updated rule name | | `opts.description` | `string` | Updated rule description | ```js await client.communities.editRule("1234567890", "rule123", "Be kind", { description: "Treat all members with respect", }); ``` ## `communities.removeRule(communityId, ruleId)` Remove a community rule. Uses the GraphQL `CommunityRemoveRule` mutation. ```js await client.communities.removeRule("1234567890", "rule123"); ``` ## `communities.reorderRules(communityId, ruleIds)` Reorder community rules. Uses the GraphQL `CommunityReorderRules` mutation. ```js await client.communities.reorderRules("1234567890", ["rule3", "rule1", "rule2"]); ``` ## `communities.editQuestion(communityId, question)` Set the join question for a community. Uses the GraphQL `CommunityEditQuestion` mutation. ```js await client.communities.editQuestion("1234567890", "Why do you want to join?"); ``` ## `communities.updateRole(communityId, userId, role)` Update a member's role within a community. Uses the GraphQL `CommunityUpdateRole` mutation. ```js await client.communities.updateRole("1234567890", "44196397", "Moderator"); ``` ## `communities.invite(communityId, userId)` Invite a user to join a community. Uses the GraphQL `CommunityUserInvite` mutation. ```js await client.communities.invite("1234567890", "44196397"); ``` ## `communities.keepTweet(communityId, tweetId)` Keep a reported tweet in the community (moderation action). Uses the GraphQL `CommunityModerationKeepTweet` mutation. ```js await client.communities.keepTweet("1234567890", "9876543210"); ``` ## `communities.moderationCases(communityId, opts?)` Get pending moderation cases for a community. Uses the GraphQL `CommunityModerationTweetCasesSlice` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const cases = await client.communities.moderationCases("1234567890"); ``` ## `communities.moderationLog(communityId, opts?)` Get the moderation log for a community. Uses the GraphQL `CommunityTweetModerationLogSlice` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const log = await client.communities.moderationLog("1234567890", { count: 50 }); ``` ## `communities.explore(opts?)` Explore communities. Uses the GraphQL `CommunitiesExploreTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const explore = await client.communities.explore(); ``` ## `communities.discover(opts?)` Get community discovery suggestions. Uses the GraphQL `CommunitiesMainDiscoveryModule` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const suggestions = await client.communities.discover(); ``` ## `communities.ranked(opts?)` Get the ranked communities timeline. Uses the GraphQL `CommunitiesRankedTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const ranked = await client.communities.ranked(); ``` ## `communities.memberships(userId, opts?)` Get communities a user is a member of. Uses the GraphQL `CommunitiesMembershipsTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const memberships = await client.communities.memberships("44196397"); ``` ## `communities.memberSearch(communityId, query, opts?)` Search for members within a community. Uses the GraphQL `CommunityMemberRelationshipTypeahead` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `communityId` | `string` | The community ID | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | ```js const members = await client.communities.memberSearch("1234567890", "john"); ``` ## `communities.userSearch(communityId, query, opts?)` Search for users to invite to a community. Uses the GraphQL `CommunityUserRelationshipTypeahead` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `communityId` | `string` | The community ID | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | ```js const users = await client.communities.userSearch("1234567890", "jane"); ``` ## Full example ```js // Explore communities const explore = await client.communities.explore(); // Join a community await client.communities.join("1234567890"); // Browse the timeline const tweets = await client.communities.timeline("1234567890"); // Check media const media = await client.communities.media("1234567890"); // See your memberships const memberships = await client.communities.memberships(client.user.id); // Create your own community await client.communities.create("My Community", { description: "A community for developers", }); // Set up rules await client.communities.createRule(communityId, "Be respectful"); await client.communities.createRule(communityId, "Stay on topic", { description: "Keep discussions relevant to development", }); await client.communities.editQuestion(communityId, "What programming languages do you use?"); // Invite members await client.communities.invite(communityId, "44196397"); // Moderate const cases = await client.communities.moderationCases(communityId); await client.communities.keepTweet(communityId, tweetId); // Update settings await client.communities.editName(communityId, "Dev Community"); await client.communities.editPurpose(communityId, "Updated purpose"); // Leave when done await client.communities.leave("1234567890"); ``` --- --- url: /getting-started/configuration.md --- # Configuration emusks lets you customize how it connects to Twitter/X. You can choose which client to emulate, route traffic through a proxy, and access the raw API layers directly. ## Client selection Twitter/X uses different API configurations depending on which app you're using. emusks can emulate any of the official clients, each with its own bearer tokens and HTTP fingerprints. ```js import Emusks from "emusks"; const client = new Emusks(); await client.login({ auth_token: "your_auth_token_here", client: "tweetdeck", // emulate the tweetdeck client (premium-only) }); ``` Please note that the availability of clients constantly changes, and some may get your account suspended. Changing the client usually doesn't require getting a new auth token, but you may need to change it if you sign out or get CAPTCHA'd. The current built-in clients are `android`, `iphone`, `ipad`, `mac`, `old`, `web`, and `tweetdeck`. ::: tip Custom clients By default, emusks uses the "web" client, which is the most common and least likely to raise suspicion. However, if you want to emulate a different client or use a custom bearer token, you can do so with the `client` option. Custom clients may be useful for avoiding rate-limits and making sure your app to be stable and long-running. If you need a bearer, please [DM](https://x.com/0xtiago_) or [email](mailto:fancy-chewy-oblong@duck.com) me. To use a custom client, pass an object instead of a string: ```js { bearer: "AAAAAAAAAAAAAAAAAAAAA…", fingerprints: { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36", ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,35-5-27-16-0-10-13-23-45-65037-17613-18-65281-51-43-11,4588-29-23-24,0", ja4r: "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601", }, }, ``` ::: ## Proxy support Route all API traffic through an HTTP proxy. This is useful for avoiding rate limits, changing IP addresses, or accessing Twitter from restricted networks. ```js const client = new Emusks(); await client.login({ auth_token: "your_auth_token_here", proxy: "protocol://user:pass@host:port", // (supports http, socks4, socks5, socks5h) }); ``` Rotating proxies are not supported by default and I don't recommend using them. While you could constantly rotate the `proxy` sent to the client yourself, Twitter will most likely notice it and get suspicious of a single session being used from many IPs. ## Handling errors emusks throws errors sent from the Twitter GraphQL API by default. You can catch these errors using a try-catch block, and for long-running apps we recommnd setting a system for you to be notified in case your account gets locked or rate-limited: ``` 83 | method, 84 | ) 85 | ).json(); 86 | 87 | if (res?.errors?.[0]) { 88 | throw new Error(res.errors.map((err) => err.message).join(", ")); ^ error: Authorization: Denied by access control: Missing TwitterUserNotSuspended; To protect our users from spam and other malicious activity, this account is temporarily locked. Please log in to https://twitter.com to unlock your account. ``` ## Next steps You're all set! Explore the helper namespaces to start building: * **[Tweets](/tweets/tweets)** — Create, like, retweet, and manage tweets * **[Users](/users/users)** — Follow, block, mute, and look up users * **[Search](/discovery/search)** — Search tweets, users, and media * **[Account](/account/settings)** — Manage your account settings and security --- --- url: /more/credits.md --- # Credits * [fa0311](https://github.com/fa0311) for their amazing work [scraping Twitter's GraphQL queries](https://github.com/fa0311/TwitterInternalAPIDocument) * [re4k](https://github.com/re4k) for documenting a few of [Twitter client's API keys](https://gist.github.com/pvieito/ee6d2c8934a8f84b9aeb467585277b8a) * [zedeus](https://github.com/zedeus) for publishing their reverse-engineered Twitter login flow for [nitter](https://github.com/zedeus/nitter) * [lami](https://lami.zip/) for [documenting Twitter's `x-client-transaction-id` header](https://github.com/Lqm1/x-client-transaction-id) * part of emusks' helpers are written by LLMs due to the sheer amount of queries, most of which don't have documented parameters. The rest of the codebase is written by humans. --- --- url: /users/direct-messages.md --- # Direct messages Manage your DMs: inbox, conversations, search, blocking, and more. ## `dms.inbox(params?)` Get your DM inbox initial state. Uses the v1.1 `dm/inbox_initial_state` endpoint. ```js const inbox = await client.dms.inbox(); ``` ## `dms.conversation(conversationId, params?)` Get a specific DM conversation by ID. Uses the v1.1 `dm/conversation` endpoint. ```js const convo = await client.dms.conversation("1234567890-9876543210"); ``` ## `dms.search(query, opts?)` Search across all DM conversations. Uses the GraphQL `DmAllSearchSlice` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const results = await client.dms.search("meeting"); ``` ## `dms.searchGroups(query, opts?)` Search group DM conversations. Uses the GraphQL `DmGroupSearchSlice` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const groups = await client.dms.searchGroups("project"); ``` ## `dms.searchPeople(query, opts?)` Search for people in DMs. Uses the GraphQL `DmPeopleSearchSlice` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const people = await client.dms.searchPeople("john"); ``` ## `dms.block(userId)` Block a user from sending you DMs. Uses the GraphQL `dmBlockUser` mutation. ```js await client.dms.block("44196397"); ``` ## `dms.unblock(userId)` Unblock a user in DMs. Uses the GraphQL `dmUnblockUser` mutation. ```js await client.dms.unblock("44196397"); ``` ## `dms.deleteConversations(conversationIds)` Delete one or more DM conversations. Uses the v1.1 `dm/conversation/bulk_delete` endpoint. ```js // Single conversation await client.dms.deleteConversations("1234567890-9876543210"); // Multiple conversations await client.dms.deleteConversations(["1234567890-9876543210", "1111111111-2222222222"]); ``` ## `dms.updateLastSeen(eventId)` Mark messages as read up to a given event ID. Uses the v1.1 `dm/update_last_seen_event_id` endpoint. ```js await client.dms.updateLastSeen("1234567890123456789"); ``` ## `dms.muted(opts?)` Get your muted DM conversations. Uses the GraphQL `DmMutedTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const muted = await client.dms.muted(); ``` ## `dms.edit(messageId, conversationId, text)` Edit a DM message. Uses the v1.1 `dm/edit` endpoint. | Param | Type | Description | | ---------------- | -------- | ------------------------ | | `messageId` | `string` | The message ID to edit | | `conversationId` | `string` | The conversation ID | | `text` | `string` | New text for the message | ```js await client.dms.edit("111222333", "1234567890-9876543210", "Updated message"); ``` ## `dms.permissions(params?)` Check DM permissions. Uses the v1.1 `dm/permissions` endpoint. ```js const perms = await client.dms.permissions(); ``` ## `dms.nsfwFilter(enabled)` Toggle the NSFW media filter for DMs. Uses the GraphQL `DmNsfwMediaFilterUpdate` mutation. ```js // Enable filter await client.dms.nsfwFilter(true); // Disable filter await client.dms.nsfwFilter(false); ``` ## `dms.updateRelationship(userId, action)` Update your DM relationship state with a user. Uses the v1.1 `dm/user/update_relationship_state` endpoint. ```js await client.dms.updateRelationship("44196397", "trust"); ``` ## `dms.reportSpam(conversationId, messageId)` Report a DM as spam. Uses the v1.1 `direct_messages/report_spam` endpoint. ```js await client.dms.reportSpam("1234567890-9876543210", "111222333"); ``` ## `dms.report(conversationId, messageId)` Report a DM message. Uses the v1.1 `dm/report` endpoint. ```js await client.dms.report("1234567890-9876543210", "111222333"); ``` ## `dms.userUpdates(params?)` Get DM user updates. Uses the v1.1 `dm/user_updates` endpoint. ```js const updates = await client.dms.userUpdates(); ``` ## Full example ```js // Check your inbox const inbox = await client.dms.inbox(); // Search for a conversation const results = await client.dms.search("hello"); // Read a specific conversation const convo = await client.dms.conversation("1234567890-9876543210"); // Edit a message await client.dms.edit("111222333", "1234567890-9876543210", "corrected text"); // Block a spammer in DMs await client.dms.block("9999999999"); // Check your muted conversations const muted = await client.dms.muted(); // Clean up old conversations await client.dms.deleteConversations(["1234567890-9876543210", "1111111111-2222222222"]); ``` --- --- url: /getting-started.md --- # Getting started **emusks** is a reverse-engineered Twitter/X API client for JavaScript. emusks lets you interact with Twitter's internal API endpoints just like any official client would, giving you access to features that aren't available through the official API. ## Why emusks? Twitter's official API is limited, expensive, and restrictive. emusks gives you full access to every feature available on the platform — from posting tweets to managing communities, from searching content to handling direct messages — all through a clean, intuitive JavaScript interface. ## Quick start ```js import Emusks from "emusks"; const client = new Emusks(); await client.login("your_auth_token"); const tweet = await client.tweets.create("Hello from emusks! 🚀"); console.log(`i tweeted to https://x.com/i/status/${tweet.id}`); // like the tweet await client.tweets.like(tweet.id); // get a user const user = await client.users.getByUsername("elonmusk"); console.log(`${user.name} has ${user.stats.followers.count} followers`); // follow them await client.users.follow(user.id); // search for tweets const results = await client.search.tweets("javascript"); // get your home timeline const home = await client.timelines.home(); ``` ## What's next? * **[Installation](/getting-started/installation)** — Install the package and set up your project * **[Authentication](/getting-started/authentication)** — Log in with an auth token or username/password * **[Configuration](/getting-started/configuration)** — Choose clients, set up proxies, and configure options *** ::: tip Are you an LLM? Please read our [llms.txt](/llms.txt) or [llms-full.txt](/llms-full.txt). ::: --- --- url: /getting-started/installation.md --- # Installation Install emusks using your preferred package manager: ::: code-group ```sh [npm] npm install emusks ``` ```sh [yarn] yarn add emusks ``` ```sh [pnpm] pnpm add emusks ``` ```sh [bun] bun add emusks ``` ::: You'll now be able to import `emusks` in your project: ```ts import Emusks from "emusks"; const client = new Emusks(); await client.login("your_auth_token"); const user = await client.users.getByUsername("elonmusk"); console.log(user.name); ``` ## Next Steps Now that you have emusks installed, head over to [Authentication](/getting-started/authentication) to learn how to log in and start making requests. --- --- url: /users/lists.md --- # Lists Create, manage, and interact with Twitter/X lists. ## `lists.create(name, opts?)` Create a new list. Uses the GraphQL `CreateList` mutation. | Option | Type | Description | | ------------------ | --------- | ------------------------------------- | | `name` | `string` | The list name | | `opts.private` | `boolean` | Make the list private (default false) | | `opts.description` | `string` | List description | ```js await client.lists.create("Tech News"); // Private list with description await client.lists.create("My Favorites", { private: true, description: "People I follow closely", }); ``` ## `lists.delete(listId)` Delete a list you own. Uses the GraphQL `DeleteList` mutation. ```js await client.lists.delete("1234567890"); ``` ## `lists.update(listId, opts?)` Update a list's name, description, or visibility. Uses the GraphQL `UpdateList` mutation. | Option | Type | Description | | ------------------ | --------- | ----------------- | | `listId` | `string` | The list ID | | `opts.name` | `string` | New name | | `opts.description` | `string` | New description | | `opts.private` | `boolean` | Change visibility | ```js await client.lists.update("1234567890", { name: "Renamed List", description: "Updated description", private: false, }); ``` ## `lists.get(listId)` Get a list by its REST ID. Uses the GraphQL `ListByRestId` query. ```js const list = await client.lists.get("1234567890"); ``` ## `lists.getBySlug(slug, opts?)` Get a list by its slug and owner screen name. Uses the GraphQL `ListBySlug` query. | Option | Type | Description | | ---------------------- | -------- | ------------------------- | | `slug` | `string` | The list slug | | `opts.ownerScreenName` | `string` | The list owner's username | ```js const list = await client.lists.getBySlug("tech-news", { ownerScreenName: "elonmusk", }); ``` ## `lists.addMember(listId, userId)` Add a user to a list. Uses the GraphQL `ListAddMember` mutation. ```js await client.lists.addMember("1234567890", "44196397"); ``` ## `lists.removeMember(listId, userId)` Remove a user from a list. Uses the GraphQL `ListRemoveMember` mutation. ```js await client.lists.removeMember("1234567890", "44196397"); ``` ## `lists.members(listId, opts?)` Get the members of a list. Uses the GraphQL `ListMembers` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const members = await client.lists.members("1234567890", { count: 50 }); ``` ## `lists.subscribers(listId, opts?)` Get users who have subscribed to a list. Uses the GraphQL `ListSubscribers` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const subs = await client.lists.subscribers("1234567890"); ``` ## `lists.subscribe(listId)` Subscribe to a list. Uses the GraphQL `ListSubscribe` mutation. ```js await client.lists.subscribe("1234567890"); ``` ## `lists.unsubscribe(listId)` Unsubscribe from a list. Uses the GraphQL `ListUnsubscribe` mutation. ```js await client.lists.unsubscribe("1234567890"); ``` ## `lists.mute(listId)` Mute a list (hide its tweets from your timeline). Uses the GraphQL `MuteList` mutation. ```js await client.lists.mute("1234567890"); ``` ## `lists.unmute(listId)` Unmute a list. Uses the GraphQL `UnmuteList` mutation. ```js await client.lists.unmute("1234567890"); ``` ## `lists.timeline(listId, opts?)` Get the latest tweets from a list. Uses the GraphQL `ListLatestTweetsTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const tweets = await client.lists.timeline("1234567890"); // Paginate const next = await client.lists.timeline("1234567890", { cursor: "DAABCgAB..." }); ``` ## `lists.ranked(listId, opts?)` Get ranked (algorithmic) tweets from a list. Uses the GraphQL `ListRankedTweetsTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const ranked = await client.lists.ranked("1234567890"); ``` ## `lists.search(listId, query, opts?)` Search tweets within a list. Uses the GraphQL `ListSearchTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `listId` | `string` | The list ID | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const results = await client.lists.search("1234567890", "breaking news"); ``` ## `lists.ownerships(userId, opts?)` Get lists owned by a user. Uses the GraphQL `ListOwnerships` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const owned = await client.lists.ownerships("44196397"); ``` ## `lists.memberships(userId, opts?)` Get lists a user is a member of. Uses the GraphQL `ListMemberships` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const memberships = await client.lists.memberships("44196397"); ``` ## `lists.discover(opts?)` Discover suggested lists. Uses the GraphQL `ListsDiscovery` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const suggestions = await client.lists.discover(); ``` ## `lists.combined(opts?)` Get your combined lists view. Uses the GraphQL `CombinedLists` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const all = await client.lists.combined(); ``` ## `lists.manage(opts?)` Get the list management page timeline. Uses the GraphQL `ListsManagementPageTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const managed = await client.lists.manage(); ``` ## `lists.editBanner(listId, mediaId)` Update a list's banner image. Uses the GraphQL `EditListBanner` mutation. ```js await client.lists.editBanner("1234567890", "9876543210"); ``` ## `lists.deleteBanner(listId)` Remove a list's banner image. Uses the GraphQL `DeleteListBanner` mutation. ```js await client.lists.deleteBanner("1234567890"); ``` ## `lists.pinTimeline(timelineId)` Pin a list timeline to your home tabs. Uses the GraphQL `PinTimeline` mutation. ```js await client.lists.pinTimeline("list-1234567890"); ``` ## `lists.unpinTimeline(timelineId)` Unpin a list timeline from your home tabs. Uses the GraphQL `UnpinTimeline` mutation. ```js await client.lists.unpinTimeline("list-1234567890"); ``` ## `lists.pinned()` Get your pinned list timelines. Uses the GraphQL `PinnedTimelines` query. ```js const pinned = await client.lists.pinned(); ``` ## Full example ```js // Create a list await client.lists.create("JavaScript Devs", { description: "Great JS developers to follow", private: false, }); // Add members await client.lists.addMember(listId, "44196397"); await client.lists.addMember(listId, "12"); // Browse the list timeline const tweets = await client.lists.timeline(listId, { count: 30 }); // Search within the list const results = await client.lists.search(listId, "React"); // Pin it to home await client.lists.pinTimeline(`list-${listId}`); // Check who's subscribed const subs = await client.lists.subscribers(listId); // See all your lists const myLists = await client.lists.ownerships(client.user.id); // Discover new lists const suggestions = await client.lists.discover(); // Clean up await client.lists.removeMember(listId, "12"); await client.lists.delete(listId); ``` --- --- url: /tweets/media.md --- # Media Upload images, GIFs, and videos, then attach them to tweets. ## `media.create(source, opts?)` Upload media to Twitter. Returns a `media_id` you can pass to [`tweets.create()`](./tweets#tweets-create-text-opts). | Param | Type | Description | | --- | --- | --- | | `source` | `string \| Buffer \| Uint8Array \| ArrayBuffer \| Blob` | A **file path**, `Buffer`, typed array, or `Blob` containing the media | | `opts.alt_text` | `string` | Alt text for accessibility (applied automatically after upload) | | `opts.mediaType` | `string` | Explicit MIME type (e.g. `"image/png"`). Auto-detected when omitted | | `opts.type` | `"tweet" \| "dm"` | Upload context — defaults to `"tweet"`. Use `"dm"` for direct messages | **Returns:** `{ media_id, ... }` — use `media_id` with `tweets.create()`. **Auto-detected types:** JPEG, PNG, GIF, WebP, MP4, WebM. If detection fails, pass `opts.mediaType` explicitly. ### Size limits | Type | Max size | | ----- | -------- | | Image | 5 MB | | GIF | 15 MB | | Video | 512 MB | ### Examples ```js import { readFileSync } from "fs"; // Upload from a file path const img = await client.media.create("./photo.jpg"); const tweet = await client.tweets.create("Check this out!", { mediaIds: [img.media_id], }); // Upload a Buffer with alt text const buf = readFileSync("./chart.png"); const chart = await client.media.create(buf, { alt_text: "A bar chart showing monthly revenue growth", }); await client.tweets.create("Q4 results 📊", { mediaIds: [chart.media_id], }); // Upload a video const video = await client.media.create("./clip.mp4"); await client.tweets.create("Watch this!", { mediaIds: [video.media_id], }); // Upload a Blob (e.g. from fetch) const response = await fetch("https://example.com/image.png"); const blob = await response.blob(); const uploaded = await client.media.create(blob); // Upload for a DM const dm = await client.media.create("./sticker.gif", { type: "dm" }); // Explicit MIME type const raw = await client.media.create(someBuffer, { mediaType: "image/webp", }); ``` ### Upload multiple media ```js const [a, b, c] = await Promise.all([ client.media.create("./pic1.jpg", { alt_text: "First photo" }), client.media.create("./pic2.jpg", { alt_text: "Second photo" }), client.media.create("./pic3.jpg", { alt_text: "Third photo" }), ]); await client.tweets.create("Photo dump 🧵", { mediaIds: [a.media_id, b.media_id, c.media_id], }); ``` ::: tip Always add `alt_text`! It makes your media accessible to users who rely on screen readers — and it only takes one extra option. ::: ## `media.createMetadata(mediaId, altText, opts?)` Set alt text and metadata for an already-uploaded media item. Uses the v1.1 `media/metadata/create` endpoint. ::: tip `media.create()` will do this for you when passing an `alt_text` ::: | Param | Type | Description | | --------- | -------- | ----------------------------------- | | `mediaId` | `string` | The media ID returned from upload | | `altText` | `string` | Alt text description for the media | | `opts` | `object` | Additional metadata fields to merge | ```js await client.media.createMetadata( "1234567890", "A photo of a sunset over the ocean", ); ``` ## `media.createSubtitles(mediaId, subtitles)` Attach subtitles / captions to a video. Uses the v1.1 `media/subtitles/create` endpoint. | Param | Type | Description | | --- | --- | --- | | `mediaId` | `string` | The video media ID | | `subtitles` | `object \| array` | A subtitle object or array of subtitle objects | Each subtitle object: | Field | Type | Description | | --------------- | -------- | ------------------------------- | | `media_id` | `string` | Media ID of the subtitle file | | `language_code` | `string` | Language code (e.g. `"en"`) | | `display_name` | `string` | Display name (e.g. `"English"`) | ```js // Single subtitle track await client.media.createSubtitles("1234567890", { media_id: "9876543210", language_code: "en", display_name: "English", }); // Multiple subtitle tracks await client.media.createSubtitles("1234567890", [ { media_id: "9876543210", language_code: "en", display_name: "English" }, { media_id: "1111111111", language_code: "es", display_name: "Español" }, ]); ``` --- --- url: /account/notifications.md --- # Notifications Access your notification timeline, manage notification settings, and check badge counts. ## `notifications.timeline(opts?)` Get your notifications timeline. Uses the GraphQL `NotificationsTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const notifs = await client.notifications.timeline(); // Fetch more const more = await client.notifications.timeline({ count: 50 }); // Paginate const next = await client.notifications.timeline({ cursor: "DAABCgAB..." }); ``` ## `notifications.enableWebNotifications()` Enable logged-out web push notifications. Uses the GraphQL `EnableLoggedOutWebNotifications` mutation. ```js await client.notifications.enableWebNotifications(); ``` ## `notifications.saveSettings(params?)` Save notification settings. Uses the v1.1 `notifications/settings/save` endpoint. ```js await client.notifications.saveSettings({ // notification setting parameters }); ``` ## `notifications.loginSettings(params?)` Update notification settings on login. Uses the v1.1 `notifications/settings/login` endpoint. ```js await client.notifications.loginSettings({ // login notification parameters }); ``` ## `notifications.checkin(params?)` Check in for notification settings. Uses the v1.1 `notifications/settings/checkin` endpoint. ```js await client.notifications.checkin(); ``` ## `notifications.badge(params?)` Get the notification badge count (unread count). Uses the v2 `badge_count/badge_count` endpoint. ```js const badge = await client.notifications.badge(); console.log(badge); ``` ## Full example ```js // Check unread notification count const badge = await client.notifications.badge(); // Fetch your notifications const notifs = await client.notifications.timeline(); // Get more notifications with pagination const page2 = await client.notifications.timeline({ count: 40, cursor: "DAABCgAB...", }); // Enable web push notifications await client.notifications.enableWebNotifications(); // Save custom notification settings await client.notifications.saveSettings({ // your settings }); ``` --- --- url: /account/preferences.md --- # Preferences Manage your content preferences: muted keywords, advanced mute filters, and personalization settings. ## Muted Keywords Muted keywords let you hide tweets containing specific words, phrases, or hashtags from your timelines and notifications. ### `account.mutedKeywords()` Get all your currently muted keywords. Uses the v1.1 `mutes/keywords/list` endpoint. ```js const keywords = await client.account.mutedKeywords(); console.log(`${keywords.length} muted keywords`); ``` ### `account.updateMutedKeyword(params?)` Create or update a muted keyword. Uses the v1.1 `mutes/keywords/update` endpoint. | Param | Type | Description | | ---------------------- | -------- | ----------------------------------------------------------------- | | `params.id` | `string` | The keyword mute ID (omit to create a new mute) | | `params.keyword` | `string` | The word, phrase, or hashtag to mute | | `params.mute_surfaces` | `string` | Where to apply the mute (e.g. `"home_timeline,notifications"`) | | `params.duration` | `number` | Duration in seconds (`0` for forever, `86400` for 24 hours, etc.) | ```js // Mute a keyword everywhere, forever await client.account.updateMutedKeyword({ keyword: "spoiler", mute_surfaces: "home_timeline,notifications", duration: 0, }); // Mute a keyword for 24 hours await client.account.updateMutedKeyword({ keyword: "gameofthrones", mute_surfaces: "home_timeline,notifications", duration: 86400, }); // Update an existing muted keyword await client.account.updateMutedKeyword({ id: "keyword_id_123", keyword: "spoiler", mute_surfaces: "home_timeline", duration: 604800, // 7 days }); ``` ### `account.deleteMutedKeyword(keywordId)` Remove a muted keyword. Uses the v1.1 `mutes/keywords/destroy` endpoint. ```js await client.account.deleteMutedKeyword("keyword_id_123"); ``` ## Advanced Mute Filters Advanced filters let you automatically hide tweets from accounts that match certain criteria, such as new accounts, accounts without profile pictures, or unverified accounts. ### `account.advancedFilters()` Get your current advanced mute filter settings. Uses the v1.1 `mutes/advanced_filters` endpoint (GET). ```js const filters = await client.account.advancedFilters(); console.log(filters); ``` ### `account.updateAdvancedFilters(params?)` Update your advanced mute filter settings. Uses the v1.1 `mutes/advanced_filters` endpoint (POST). | Param | Type | Description | | ----------------------------------- | --------- | --------------------------------------------------- | | `params.mute_new_accounts` | `boolean` | Hide tweets from recently created accounts | | `params.mute_not_following` | `boolean` | Hide tweets from accounts you don't follow | | `params.mute_default_profile_image` | `boolean` | Hide tweets from accounts with default avatars | | `params.mute_no_confirmed_email` | `boolean` | Hide tweets from accounts without a confirmed email | | `params.mute_no_confirmed_phone` | `boolean` | Hide tweets from accounts without a confirmed phone | ```js await client.account.updateAdvancedFilters({ mute_new_accounts: true, mute_not_following: false, mute_default_profile_image: true, mute_no_confirmed_email: true, mute_no_confirmed_phone: false, }); ``` ::: tip Advanced mute filters are a great way to reduce spam and low-quality content in your notifications without having to manually block or mute individual accounts. ::: ## Personalization ### `account.personalizationInterests()` Get the interests Twitter has inferred about you based on your activity. These interests are used to personalize your timeline, ads, and recommendations. Uses the v1.1 `account/personalization/twitter_interests` endpoint. ```js const interests = await client.account.personalizationInterests(); console.log(interests); ``` ### `account.preferences()` Get your general user preferences. Uses the GraphQL `UserPreferences` query. ```js const prefs = await client.account.preferences(); console.log(prefs); ``` ## Full example ```js // View all muted keywords const keywords = await client.account.mutedKeywords(); console.log(`Currently muting ${keywords.length} keywords`); // Mute spoilers for a week await client.account.updateMutedKeyword({ keyword: "spoilers", mute_surfaces: "home_timeline,notifications", duration: 604800, }); // Mute a hashtag forever await client.account.updateMutedKeyword({ keyword: "#Spoiler", mute_surfaces: "home_timeline,notifications", duration: 0, }); // Remove a muted keyword when you're done await client.account.deleteMutedKeyword("keyword_id_123"); // Check current advanced filters const filters = await client.account.advancedFilters(); console.log(`Advanced filters: ${JSON.stringify(filters)}`); // Enable aggressive spam filtering await client.account.updateAdvancedFilters({ mute_new_accounts: true, mute_default_profile_image: true, mute_no_confirmed_email: true, mute_no_confirmed_phone: true, }); // See what Twitter thinks you're interested in const interests = await client.account.personalizationInterests(); console.log(`Twitter thinks you're interested in: ${JSON.stringify(interests)}`); // Get general preferences const prefs = await client.account.preferences(); console.log(`Preferences: ${JSON.stringify(prefs)}`); ``` --- --- url: /account/profile.md --- # Profile View your profile information, manage account labels, and check the availability of emails, phone numbers, and usernames. ## `account.viewer()` Get the currently authenticated user's full profile. Uses the GraphQL `Viewer` query. ```js const me = await client.account.viewer(); console.log(me.id); console.log(me.username); console.log(me.name); console.log(me.stats.followers.count); console.log(me.stats.following); ``` **Returns:** A parsed user object with all profile fields, stats, and verification status. ## `account.claims()` Get user claims such as verified status claims. Uses the GraphQL `GetUserClaims` query. ```js const claims = await client.account.claims(); console.log(claims); ``` ## `account.phoneState()` Get your profile's phone verification state. Uses the GraphQL `ProfileUserPhoneState` query. ```js const state = await client.account.phoneState(); console.log(state); ``` ## `account.accountLabel()` Get your current account label status (e.g. government, business, bot). Uses the GraphQL `UserAccountLabel` query. ```js const label = await client.account.accountLabel(); console.log(label); ``` ## `account.disableAccountLabel()` Remove your account label. Uses the GraphQL `DisableUserAccountLabel` mutation. ```js await client.account.disableAccountLabel(); ``` ## `account.enableVerifiedPhoneLabel()` Enable the verified phone label on your profile. Uses the GraphQL `EnableVerifiedPhoneLabel` mutation. ```js await client.account.enableVerifiedPhoneLabel(); ``` ## `account.disableVerifiedPhoneLabel()` Disable the verified phone label on your profile. Uses the GraphQL `DisableVerifiedPhoneLabel` mutation. ```js await client.account.disableVerifiedPhoneLabel(); ``` ## `account.emailAvailable(email)` Check if an email address is available for registration. Uses the v1.1 `users/email_available` endpoint. ```js const result = await client.account.emailAvailable("test@example.com"); console.log(result.valid); // true if available ``` ## `account.phoneAvailable(phone)` Check if a phone number is available. Uses the v1.1 `users/phone_number_available` endpoint. ```js const result = await client.account.phoneAvailable("+1234567890"); console.log(result); ``` ## `account.usernameAvailable(username)` Check if a username (handle) is available and get alternative suggestions. Uses the GraphQL `GetUsernameAvailabilityAndSuggestions` mutation. ```js const result = await client.account.usernameAvailable("desired_username"); console.log(result); ``` ## `account.emailPhoneInfo(params?)` Get the email and phone information associated with the account. Uses the v1.1 `users/email_phone_info` endpoint. ```js const info = await client.account.emailPhoneInfo(); console.log(info); ``` ## `account.resendConfirmationEmail()` Resend the confirmation email for your account if it hasn't been verified yet. Uses the v1.1 `account/resend_confirmation_email` endpoint. ```js await client.account.resendConfirmationEmail(); ``` ## Updating Your Profile Profile updates like changing your display name, bio, location, website, and profile images are available through the **Users** namespace. See the [Users documentation](/users/users) for details. ```js // Update display name, bio, location, and website await client.users.updateProfile({ name: "New Display Name", description: "My updated bio ✨", location: "San Francisco, CA", url: "https://example.com", }); // Update profile picture (base64 image data) await client.users.updateProfileImage(base64ImageData); // Update banner image await client.users.updateProfileBanner(base64BannerData); // Remove banner await client.users.removeProfileBanner(); ``` ## Full example ```js // Get your full profile const me = await client.account.viewer(); console.log(`Logged in as @${me.username}`); console.log(`Name: ${me.name}`); console.log(`Followers: ${me.stats.followers.count}`); console.log(`Following: ${me.stats.following}`); console.log(`Tweets: ${me.stats.posts}`); // Check your account label const label = await client.account.accountLabel(); console.log(`Account label: ${JSON.stringify(label)}`); // Check phone verification state const phoneState = await client.account.phoneState(); console.log(`Phone state: ${JSON.stringify(phoneState)}`); // Get claims (verification info) const claims = await client.account.claims(); // Check availability before changing your username const available = await client.account.usernameAvailable("new_handle"); console.log(`Username available: ${JSON.stringify(available)}`); // Check email availability const emailCheck = await client.account.emailAvailable("newemail@example.com"); // Get email and phone info on file const contactInfo = await client.account.emailPhoneInfo(); // Manage labels await client.account.enableVerifiedPhoneLabel(); // or await client.account.disableVerifiedPhoneLabel(); // Update your profile via the users namespace await client.users.updateProfile({ name: "Updated Name", description: "Building cool things 🚀", location: "Earth", }); ``` --- --- url: /more/queries.md --- # Running queries Beyond the high-level helper namespaces, emusks gives you direct access to Twitter's underlying API layers. This is useful when you need to call endpoints that aren't wrapped by a helper, experiment with new or undocumented features, or get the raw unprocessed response from Twitter. Twitter/X uses three main API layers internally. emusks exposes all three. ## GraphQL API Twitter's primary modern API layer. Most features — tweets, users, timelines, communities, and more — are powered by GraphQL endpoints. ```js const res = await client.graphql("UserByScreenName", { variables: { screen_name: "elonmusk" }, }); console.log(res.data.user.result); ``` The first argument is the **operation name** — the name of the GraphQL query or mutation as defined by Twitter internally. The second argument is an options object where you can pass `variables`, `features`, and other GraphQL parameters. You can get these from devtools. emusks will automatically handle obtaining query IDs and other necessary metadata for you. ## v1.1 REST API The legacy REST API. Still actively used for account management, media uploads, user lookups, friendships, mutes, blocks, and many other features. ```js const res = await client.v1_1("users/lookup", { params: { screen_name: "elonmusk" }, }); console.log(res); ``` The first argument is the **endpoint path** (without the `/1.1/` prefix or `.json` suffix). The second argument is an options object where you can pass `params` (query parameters) and other request options. ## v2 API Twitter's v2 API layer, used for adaptive search, guide settings, badge counts, and some other features. ```js const res = await client.v2("search/adaptive", { params: { q: "hello", count: 10 }, }); console.log(res); ``` ## When to use raw query APIs These APIs are extremely powerful and flexible, and in fact what emusks uses internally to implement everything else. However, they also require more work to use effectively, since you need to understand Twitter's internal API structure and handle the raw responses yourself. You'll also need to beware that, unlike query APIs, all other functions parse and normalize Twitter's deeply nested, inconsistent API responses into clean JavaScript objects. Raw API methods return the response exactly as Twitter sends it, which are harder to work with and subject to change without warning. --- --- url: /discovery/search.md --- # Search Search tweets, users, media, and more across Twitter/X. Most of these support [Search operators](../more/search-operators.md). ## `search.tweets(query, opts?)` Search for tweets (Top results). Uses the GraphQL `SearchTimeline` query with `product: "Top"`. | Option | Type | Description | | --- | --- | --- | | `query` | `string` | Search query (supports Twitter search operators) | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.querySource` | `string` | Query source (default `"typed_query"`) | | `opts.product` | `string` | Override the product type (default `"Top"`) | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets, nextCursor } = await client.search.tweets("javascript"); // With search operators const { tweets: elonTweets } = await client.search.tweets("from:elonmusk AI"); // Paginate const page2 = await client.search.tweets("javascript", { cursor: nextCursor }); ``` ## `search.latest(query, opts?)` Search for tweets sorted by latest (chronological). Uses the GraphQL `SearchTimeline` query with `product: "Latest"`. | Option | Type | Description | | ------------------ | -------- | -------------------------------------- | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.querySource` | `string` | Query source (default `"typed_query"`) | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.search.latest("breaking news"); ``` ## `search.users(query, opts?)` Search for user profiles. Uses the GraphQL `SearchTimeline` query with `product: "People"`. | Option | Type | Description | | ------------------ | -------- | -------------------------------------- | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.querySource` | `string` | Query source (default `"typed_query"`) | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { users } = await client.search.users("elon"); ``` ## `search.media(query, opts?)` Search for tweets containing media (images, videos, GIFs). Uses the GraphQL `SearchTimeline` query with `product: "Media"`. | Option | Type | Description | | ------------------ | -------- | -------------------------------------- | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.querySource` | `string` | Query source (default `"typed_query"`) | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.search.media("cute cats"); ``` ## `search.lists(query, opts?)` Search for Twitter/X lists. Uses the GraphQL `SearchTimeline` query with `product: "Lists"`. | Option | Type | Description | | ------------------ | -------- | -------------------------------------- | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.querySource` | `string` | Query source (default `"typed_query"`) | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.search.lists("tech news"); ``` ## `search.typeahead(query, params?)` Get typeahead/autocomplete suggestions. Uses the v1.1 `search/typeahead` endpoint. Returns events, users, topics, and lists matching the query. | Param | Type | Description | | --- | --- | --- | | `query` | `string` | Search query | | `params.src` | `string` | Source context (default `"search_box"`) | | `params.result_type` | `string` | Types to return (default `"events,users,topics,lists"`) | ```js const suggestions = await client.search.typeahead("elon"); console.log(suggestions.users); console.log(suggestions.topics); ``` ## `search.communities(query, opts?)` Search for posts within communities globally. Uses the GraphQL `GlobalCommunitiesPostSearchTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.search.communities("web development"); ``` ## `search.communitiesLatest(query, opts?)` Search for the latest posts within communities globally. Uses the GraphQL `GlobalCommunitiesLatestPostSearchTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.search.communitiesLatest("open source"); ``` ## `search.adaptive(query, params?)` Perform an "adaptive search" via the v2 API. Uses the v2 `search/adaptive` endpoint. Returns raw search results with full tweet and user objects. ::: warning This hasn't been fully tested yet and may have some quirks. It also doesn't support all the operators and features of the normal GraphQL search. **Using the [normal search methods](#search-tweets-query-opts) over this one is recommended for most use cases** unless you have a specific reason for needing to use v2 search or want to experiment with it. ::: | Param | Type | Description | | --- | --- | --- | | `query` | `string` | Search query | | `params.count` | `number` | Number of results (default 20) | | `params.query_source` | `string` | Query source (default `"typed_query"`) | | `params.pc` | `number` | Promoted content flag (default 1) | | `params.spelling_corrections` | `number` | Enable spelling corrections (default 1) | ```js const results = await client.search.adaptive("machine learning", { count: 50 }); ``` ## Full example ```js // Search tweets with different modes const { tweets: top } = await client.search.tweets("React vs Vue"); const { tweets: latest } = await client.search.latest("React vs Vue"); const { tweets: mediaTweets } = await client.search.media("React vs Vue"); // Search for users const { users: devs } = await client.search.users("javascript developer"); // Get autocomplete suggestions const suggestions = await client.search.typeahead("type"); // Search with the v2 adaptive endpoint const adaptive = await client.search.adaptive("web development", { count: 30 }); // Search within communities const { tweets: communityResults } = await client.search.communities("open source"); // Advanced search with operators const { tweets: advanced } = await client.search.tweets( 'from:github "open source" min_faves:100 lang:en since:2025-01-01', ); // Paginate through results let cursor; for (let i = 0; i < 5; i++) { const page = await client.search.tweets("javascript", { count: 20, cursor }); // process page.tweets... cursor = page.nextCursor; if (!cursor) break; } // Find lists about a topic const { tweets: listResults } = await client.search.lists("machine learning"); ``` --- --- url: /more/search-operators.md --- # Search operators Twitter supports dozens of advanced search filters and operators that let you find specific tweets, users, or media. These operators work with [`search.tweets()`](/discovery/search#search-tweets-query-opts) along with most official clients. They mostly do **not work** with [`search.adaptive`](/discovery/search#search-adaptive-query-params), as it uses v1.1 search. ## Operators ### Tweet content | Operator | Finds tweets… | | | :--------------------- | :----------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | | `nasa esa` | Containing both "nasa" and "esa". (Spaces = **AND**) | [🔗](https://x.com/search?q=esa%20nasa\&f=live) | | `nasa OR esa` | Either "nasa" or "esa" (or both). **OR** must be capitalized. | [🔗](https://x.com/search?q=nasa%20OR%20esa\&f=live) | | `"state of the art"` | The exact phrase. Also matches "state-of-the-art". | [🔗](https://x.com/search?q=%22state%20of%20the%20art%22\&f=live) | | `"this is the * time"` | Quoted phrase with a **wildcard**. `*` only works inside quotes. | [🔗](https://x.com/search?q=%22this%20is%20the%20*%20time%20this%20week%22\&f=live) | | `+radiooooo` | Forces the term as-is. Prevents "did you mean" corrections. | [🔗](https://x.com/search?q=%2Bradiooooo\&f=live) | | `-love` | Excludes tweets containing "love". Works with phrases: `-"live laugh love"`. | [🔗](https://x.com/search?q=bears%20-chicagobears\&f=live) | | `#tgif` | Tweets containing a specific hashtag. | [🔗](https://x.com/search?q=%23tgif\&f=live) | | `$TWTR` | Tweets containing a cashtag (stock symbols). | [🔗](https://x.com/search?q=%24TWTR\&f=live) | | `What ?` | Finds tweets where a question mark is used. | [🔗](https://x.com/search?q=\(Who%20OR%20What\)%20%3F\&f=live) | | `:)` / `:(` | Matches positive (`:)`, `:-)`, `:P`, `:D`) or negative (`:(`, `:-(`) emoticons. | [🔗](https://x.com/search?q=%3A%29%20OR%20%3A%28\&f=live) | | `👀` | Matches specific emojis. Usually requires a keyword or `lang:` to work. | [🔗](https://x.com/search?q=%F0%9F%91%80%20lang%3Aen\&f=live) | | `url:google.com` | Matches URLs. Tokenized: use underscores for hyphens (e.g., `url:t_mobile.com`). | [🔗](https://x.com/search?q=url%3Agu.com\&f=live) | | `lang:en` | Filters by language (e.g., `en`, `es`, `fr`, `ja`). See [Language codes](#language-codes). | [🔗](https://x.com/search?q=lang%3Aen\&f=live) | ### User & account | Operator | Finds tweets… | Example | | :--------------------- | :-------------------------------------------------------------------------- | :---------------------- | | `from:user` | Sent by a specific `@username`. | `dogs from:NASA` | | `to:user` | Tweets that are a direct reply to `@username`. | `to:NASA #MoonTunes` | | `@user` | Tweets mentioning `@username`. Combine with `-from:user` for mentions only. | `@cern -from:cern` | | `list:ID` | Tweets from members of a public List (Use ID or `user/slug`). | `list:esa/astronauts` | | `filter:verified` | From legacy verified accounts. | `filter:verified` | | `filter:blue_verified` | From accounts with X Premium (paid checkmark). | `filter:blue_verified` | | `filter:follows` | Only from accounts you follow. (Cannot be negated). | `kitten filter:follows` | | `filter:social` | From your extended network (based on activity). | `filter:social` | ### Location | Operator | Finds tweets… | Example | | :------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------- | | `near:city` | Geotagged in a specific location. Supports phrases: `near:"The Hague"`. | `near:London` | | `near:me` | Geotagged near your current IP/detected location. | `near:me` | | `within:radius` | Use with `near` to set a radius (km or mi). | `fire near:SF within:10km` | | `geocode:lat,long,r` | Tweets within radius `r` of exact coordinates. | `geocode:37.77,-122.41,1km` | | `place:ID` | Search by X [Place Object ID](https://developer.twitter.com/en/docs/twitter-api/v1/data-dictionary/object-model/geo). | `place:96683cc9126741d1` | ### Time & date | Operator | Finds tweets… | Example | | :------------------- | :------------------------------------------------------------------------ | :------------------------------ | | `since:YYYY-MM-DD` | Sent on or after this date. (Inclusive). | `since:2024-01-01` | | `until:YYYY-MM-DD` | Sent before this date. (**Not** inclusive). | `until:2024-01-01` | | `since:DATE_TIME_TZ` | Granular time: `since:2023-01-01_23:59:59_UTC`. | `since:2023-10-13_00:00:00_UTC` | | `since_time:12345` | On or after a specific Unix timestamp (seconds). | `since_time:1561720321` | | `until_time:12345` | Before a specific Unix timestamp (seconds). | `until_time:1562198400` | | `since_id:ID` | After (not inclusive) a specific Snowflake Tweet ID. | `since_id:1138872932` | | `max_id:ID` | At or before (inclusive) a specific Snowflake Tweet ID. | `max_id:1144730280` | | `within_time:10m` | Within the last `d` (days), `h` (hours), `m` (minutes), or `s` (seconds). | `nasa within_time:30s` | ### Tweet type & logic | Operator | Finds tweets… | Example | | :----------------------- | :----------------------------------------------------------------------- | :-------------------------------- | | `filter:nativeretweets` | Only "retweet button" retweets. (Last 7-10 days only). | `from:nasa filter:nativeretweets` | | `include:nativeretweets` | Includes retweets in results (hidden by default). | `include:nativeretweets` | | `filter:replies` | Only tweets that are replies. | `filter:replies -to:nasa` | | `filter:self_threads` | Only self-replies (the author replying to themselves). | `filter:self_threads` | | `filter:quote` | Only contains Quote Tweets. | `filter:quote` | | `conversation_id:ID` | Every tweet in a specific thread/conversation. | `conversation_id:1140437409` | | `quoted_tweet_id:ID` | Quotes of a specific Tweet ID. | `quoted_tweet_id:113863184` | | `quoted_user_id:ID` | All quotes of a specific User ID (numeric). | `quoted_user_id:11348282` | | `card_name:pollXchoice` | Tweets with polls. Options: `poll2choice_text_only`, `poll3choice`, etc. | `card_name:poll4choice_text_only` | ### Engagement & media | Operator | Finds tweets… | Example | | :----------------------- | :------------------------------------------------------------ | :----------------------- | | `filter:has_engagement` | Has at least one like/reply/RT. Negate to find "dead" tweets. | `-filter:has_engagement` | | `min_retweets:X` | Minimum number of Retweets. | `min_retweets:500` | | `min_faves:X` | Minimum number of Likes (Faves). | `min_faves:1000` | | `min_replies:X` | Minimum number of Replies. | `min_replies:100` | | `-min_faves:X` | Maximum number of Likes. | `-min_faves:10` | | `filter:media` | Any media (images, video, GIFs). | `cat filter:media` | | `filter:images` | Tweets containing images. | `filter:images` | | `filter:twimg` | Specifically native X/Twitter images (`pic.twitter.com`). | `filter:twimg` | | `filter:videos` | Any video (Native, YouTube, etc.). | `filter:videos` | | `filter:consumer_video` | Native X video only. | `filter:consumer_video` | | `filter:pro_video` | Amplify/Pro video only. | `filter:pro_video` | | `filter:spaces` | Live or recorded Twitter Spaces. | `filter:spaces` | | `card_name:animated_gif` | Tweets containing GIFs. | `card_name:animated_gif` | ## Matching ### Logical grouping Spaces are implicit **AND**. Use parentheses to group conditions. * **Query:** `(puppy OR kitten) (sweet OR cute) -filter:nativeretweets min_faves:10` * **Meaning:** Find tweets about puppies or kittens AND sweet or cute, excluding retweets, with 10+ likes. ### Force literal matches X uses "signal words." If you search for the word `photo`, X thinks you want `filter:images`. To avoid this, wrap it in quotes, like `"photo"`. ### URL tokenization X strips hyphens from parameters and you should use underscore instead. `url:t-mobile.com` won't work, but `url:t_mobile.com` will. This applies to all URL-based operators (`url:`, `filter:links`, etc.). ### Quote-Tweet Permalinks From a technical perspective, quote-tweets are tweets with a URL of another tweet. It's possible to find Tweets that quote a specific Tweet by searching for the URL of that Tweet. To find everyone quoting a specific tweet, search for the tweet's URL and exclude the original author, like `https://x.com/jack/status/20 -from:jack`. Any parameters must be removed or only Tweets that contain the parameter as well are found. This includes the automatic client parameter added when using the share menu (eg. ?s=20 for the Web App and ?s=09 for the Android app) ## Snowflakes All X IDs (tweets, users) embed a timestamp. This allows you to search with precise time boundaries using `since_id` or `max_id`. To convert an **ID to millisecond epoch**, use `milliepoch = (tweet_id >> 22) + 1288834974657`. To convert an **epoch to ID**, use `tweet_id = (milliepoch - 1288834974657) << 22`. ```python def milliepoch_to_id(ms): if ms <= 1288834974657: raise ValueError("Too early") return (ms - 1288834974657) << 22 def id_to_milliepoch(id): if id <= 0: raise ValueError("Invalid ID") return (id >> 22) + 1288834974657 ``` ## Language codes Twitter supports several unique language filters for specific tweet structures: | Code | Purpose | | :--------- | :------------------------------------------------ | | `lang:und` | Unknown (usually just links, numbers, or emojis). | | `lang:qam` | Tweets containing **Mentions only**. | | `lang:qct` | Tweets containing **Cashtags only**. | | `lang:qht` | Tweets containing **Hashtags only**. | | `lang:qme` | Tweets containing **Media links only**. | | `lang:qst` | Tweets with **Very short text**. | | `lang:zxx` | **No text** (Media or Twitter Card only). | ## Searching by client You can filter tweets by the app they were sent from. These should work for every single client, official or not. If it isn't, try putting the client name in quotes or replace spaces with underscores. You cannot copy an existing name. This operator needs to be combined with something else to work, eg `lang:en`. **Examples:** * `source:twitter_web_client` * `source:twitter_web_app` * `source:twitter_for_iphone` * `source:twitter_for_ipad` * `source:twitter_for_mac` * `source:twitter_for_android` * `source:twitter_ads` (promoted tweets) * `source:tweetdeck` * `source:tweetdeck_web_app` * `source:twitter_for_advertisers` * `source:twitter_media_studio` * `source:cloudhopper` (sms gateway) * custom clients, like `source:IFTTT` or `source:Instagram` ## News site filter When using `filter:news`, Twitter makes sure every result includes a link to a [whitelist of "news" websites](https://gist.github.com/igorbrigadir/ef143d2f3167258359007a0ff7ac401d). ## Limitations * **Operator limit:** You can typically only use ~22-23 operators in a single query. * **Card names:** `card_name:` searches are usually limited to the last 7-8 days of data. * **Private accounts:** Tweets from private (protected), suspended, or locked accounts will never appear in search results. * **Time logic:** Date/Time operators usually must be combined with a keyword or user filter to function correctly. --- --- url: /account/security.md --- # Security Manage your account security: passwords, two-factor authentication, sessions, backup codes, and connected applications. ## `account.verifyPassword(password)` Verify a password against the current account. Useful for confirming the user's identity before sensitive operations. Uses the v1.1 `account/verify_password` endpoint. ```js const result = await client.account.verifyPassword("my_password"); console.log(result.status); // "ok" ``` ## `account.changePassword(currentPassword, newPassword)` Change your account password. Uses the v1.1 `account/change_password` endpoint. ::: warning Requires elevated access. Call `client.elevate(password)` first. ::: ```js await client.elevate("current_password"); await client.account.changePassword("current_password", "new_secure_password"); ``` ## `account.passwordStrength(password)` Check the strength of a password before setting it. Uses the v1.1 `account/password_strength` endpoint. ```js const strength = await client.account.passwordStrength("my_new_password_123"); console.log(strength); ``` ::: tip Always check password strength before calling `changePassword()` to ensure the new password meets Twitter's requirements. ::: ## Two-Factor Authentication ### `account.disable2FA()` Disable two-factor authentication on your account. Uses the v1.1 `account/login_verification_enrollment` endpoint. ::: warning Requires elevated access. Call `client.elevate(password)` first. ::: ```js await client.elevate("your_password"); await client.account.disable2FA(); ``` ### `account.remove2FAMethod(methodId)` Remove a specific 2FA method, such as a security key or authenticator app. Uses the v1.1 `account/login_verification/remove_method` endpoint. ```js await client.account.remove2FAMethod("method_id_here"); ``` ### `account.tempPassword()` Generate a temporary password for app-specific login. This is useful when your account has 2FA enabled and you need to authenticate in a context that doesn't support interactive 2FA. Uses the v1.1 `account/login_verification/temporary_password` endpoint. ```js const temp = await client.account.tempPassword(); console.log(temp); ``` ### `account.renameSecurityKey(methodId, name)` Rename a registered security key (e.g. a YubiKey or passkey). Uses the v1.1 `account/login_verification/rename_security_key_method` endpoint. ```js await client.account.renameSecurityKey("method_id", "My YubiKey 5"); ``` ## Backup Codes ### `account.backupCode()` Get your current backup code. Backup codes can be used to regain access to your account if you lose your 2FA device. Uses the v1.1 `account/backup_code` endpoint (GET). ```js const code = await client.account.backupCode(); console.log(`Your backup code: ${code}`); ``` ### `account.generateBackupCode()` Generate a new backup code, invalidating the previous one. Uses the v1.1 `account/backup_code` endpoint (POST). ```js const newCode = await client.account.generateBackupCode(); console.log(`New backup code: ${newCode}`); ``` ::: danger Generating a new backup code will invalidate your previous code. Make sure to store the new code in a safe place. ::: ## Sessions ### `account.sessions()` Get a list of all active sessions on your account, including device info, location, and login time. Uses the GraphQL `UserSessionsList` query. ```js const sessions = await client.account.sessions(); console.log(sessions); ``` ## Connected Apps ### `account.connectedApps()` List all third-party applications that have been granted access to your account. Uses the v1.1 `oauth/list` endpoint. ```js const apps = await client.account.connectedApps(); console.log(`${apps.length} connected apps`); ``` ### `account.revokeApp(token)` Revoke access for a connected third-party application. Uses the v1.1 `oauth/revoke` endpoint. ```js await client.account.revokeApp("app_token_here"); ``` ## Single Sign-On ### `account.deleteSSOConnection(connectionId)` Delete an SSO (Single Sign-On) connection linked to your account (e.g. Google or Apple sign-in). Uses the v1.1 `sso/delete_connection` endpoint. ```js await client.account.deleteSSOConnection("connection_id"); ``` ## `account.emailYourData()` Request an email containing a copy of your Twitter data (tweets, DMs, profile info, etc.). Uses the v1.1 `account/personalization/email_your_data` endpoint. ```js await client.account.emailYourData(); ``` ## Full example ```js // Verify current password const result = await client.account.verifyPassword("my_password"); console.log(`Password valid: ${result.status}`); // Check strength of a new password const strength = await client.account.passwordStrength("new_p@ssw0rd_2025!"); // Elevate session for sensitive operations await client.elevate("my_password"); // Change password await client.account.changePassword("my_password", "new_p@ssw0rd_2025!"); // View active sessions const sessions = await client.account.sessions(); console.log(`Active sessions: ${JSON.stringify(sessions)}`); // Manage backup codes const currentCode = await client.account.backupCode(); console.log(`Current backup code: ${currentCode}`); const newCode = await client.account.generateBackupCode(); console.log(`New backup code: ${newCode}`); // Review and revoke connected apps const apps = await client.account.connectedApps(); for (const app of apps) { console.log(`Connected app: ${JSON.stringify(app)}`); } // Revoke a specific app await client.account.revokeApp("app_token_here"); // Manage 2FA await client.elevate("your_password"); await client.account.disable2FA(); // Rename a security key await client.account.renameSecurityKey("method_id", "Office YubiKey"); // Remove a specific 2FA method await client.account.remove2FAMethod("method_id"); // Generate a temporary password for app login const temp = await client.account.tempPassword(); // Remove an SSO connection await client.account.deleteSSOConnection("connection_id"); // Request a copy of your data await client.account.emailYourData(); ``` --- --- url: /account/settings.md --- # Settings Manage your general account settings including language, timezone, discoverability, and data saver preferences. ## `account.settings()` Get your current account settings. Uses the v1.1 `account/settings` endpoint. ```js const settings = await client.account.settings(); console.log(settings.screen_name); console.log(settings.language); console.log(settings.time_zone); ``` ## `account.updateSettings(params?)` Update your account settings. Uses the v1.1 `account/settings` endpoint (POST). | Param | Type | Description | | ------------------------------ | --------- | ----------------------------------- | | `params.language` | `string` | Language code (e.g. `"en"`, `"ja"`) | | `params.time_zone` | `string` | Timezone name (e.g. `"US/Eastern"`) | | `params.sleep_time_enabled` | `boolean` | Enable quiet hours | | `params.start_sleep_time` | `number` | Quiet hours start (0–23) | | `params.end_sleep_time` | `number` | Quiet hours end (0–23) | | `params.trend_location_woeid` | `number` | Trend location WOEID | | `params.discoverable_by_email` | `boolean` | Allow others to find you by email | | `params.discoverable_by_phone` | `boolean` | Allow others to find you by phone | ```js await client.account.updateSettings({ language: "en", time_zone: "America/New_York", discoverable_by_email: false, discoverable_by_phone: false, }); ``` ### Quiet Hours Suppress notifications during specific hours: ```js await client.account.updateSettings({ sleep_time_enabled: true, start_sleep_time: 23, end_sleep_time: 7, }); ``` ## `account.dataSaverMode()` Get the current data saver mode setting. Uses the GraphQL `DataSaverMode` query. ```js const mode = await client.account.dataSaverMode(); console.log(mode); ``` ## `account.setDataSaver(dataSaverMode)` Enable or disable data saver mode. When enabled, images load at lower quality and videos don't autoplay. ```js // Enable data saver await client.account.setDataSaver(true); // Disable data saver await client.account.setDataSaver(false); ``` ## `account.helpSettings()` Get help and support settings. Uses the v1.1 `help/settings` endpoint. ```js const help = await client.account.helpSettings(); ``` ## `account.rateLimitStatus(params?)` Check your current rate limit status across API endpoints. Uses the v1.1 `application/rate_limit_status` endpoint. | Param | Type | Description | | ------------------ | -------- | ------------------------------------------ | | `params.resources` | `string` | Comma-separated resource families to check | ```js // Check all rate limits const limits = await client.account.rateLimitStatus(); // Check specific resource families const specific = await client.account.rateLimitStatus({ resources: "statuses,friends,followers", }); ``` ## `account.emailNotificationSettings(params?)` Update email notification settings. Uses the GraphQL `WriteEmailNotificationSettings` mutation. ```js await client.account.emailNotificationSettings({ setting: "email_new_follower", enabled: false, }); ``` ## `account.viewerEmailSettings()` Get the current email notification settings. Uses the GraphQL `ViewerEmailSettings` query. ```js const emailSettings = await client.account.viewerEmailSettings(); ``` ## `account.multiList()` List all accounts in your multi-account session. Uses the v1.1 `account/multi/list` endpoint. ```js const accounts = await client.account.multiList(); ``` ## `account.logout()` Log out the current session. Uses the v1.1 `account/logout` endpoint. ```js await client.account.logout(); ``` ## `account.deactivate()` Deactivate your account. Uses the v1.1 `account/deactivate` endpoint. ::: danger This will deactivate your account. You have 30 days to reactivate before permanent deletion. ::: ```js await client.account.deactivate(); ``` ## Full example ```js // Check your current settings const settings = await client.account.settings(); console.log(`Language: ${settings.language}`); console.log(`Timezone: ${settings.time_zone}`); // Update language and timezone await client.account.updateSettings({ language: "en", time_zone: "America/New_York", }); // Disable discoverability await client.account.updateSettings({ discoverable_by_email: false, discoverable_by_phone: false, }); // Enable quiet hours overnight await client.account.updateSettings({ sleep_time_enabled: true, start_sleep_time: 23, end_sleep_time: 7, }); // Toggle data saver mode const mode = await client.account.dataSaverMode(); await client.account.setDataSaver(true); // Check rate limits before a batch operation const limits = await client.account.rateLimitStatus({ resources: "statuses,friends", }); // Manage email notification preferences const emailSettings = await client.account.viewerEmailSettings(); await client.account.emailNotificationSettings({ setting: "email_new_follower", enabled: false, }); // View all accounts in multi-account session const accounts = await client.account.multiList(); ``` --- --- url: /discovery/spaces.md --- # Spaces Get, search, and interact with Twitter/X Spaces (live audio rooms). ## `spaces.get(spaceId)` Get details about a Space by its ID. Uses the GraphQL `AudioSpaceById` query. ```js const space = await client.spaces.get("1eaKbrPZlbwKX"); console.log(space); ``` ## `spaces.search(query, opts?)` Search for Spaces. Uses the GraphQL `AudioSpaceSearch` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `query` | `string` | Search query | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const results = await client.spaces.search("tech talk"); // Paginate const next = await client.spaces.search("tech talk", { cursor: "DAABCgAB..." }); ``` ## `spaces.browseTopics(opts?)` Browse available Space topics. Uses the GraphQL `BrowseSpaceTopics` query. | Option | Type | Description | | ---------------- | -------- | ---------------------------- | | `opts.variables` | `object` | Additional GraphQL variables | ```js const topics = await client.spaces.browseTopics(); ``` ## `spaces.subscribe(spaceId)` Subscribe to a scheduled Space to get notified when it starts. Uses the GraphQL `SubscribeToScheduledSpace` mutation. ```js await client.spaces.subscribe("1eaKbrPZlbwKX"); ``` ## `spaces.unsubscribe(spaceId)` Unsubscribe from a scheduled Space. Uses the GraphQL `UnsubscribeFromScheduledSpace` mutation. ```js await client.spaces.unsubscribe("1eaKbrPZlbwKX"); ``` ## `spaces.addSharing(spaceId)` Share a Space (add sharing). Uses the GraphQL `AudioSpaceAddSharing` mutation. ```js await client.spaces.addSharing("1eaKbrPZlbwKX"); ``` ## `spaces.deleteSharing(spaceId)` Remove sharing from a Space. Uses the GraphQL `AudioSpaceDeleteSharing` mutation. ```js await client.spaces.deleteSharing("1eaKbrPZlbwKX"); ``` ## Full example ```js // Search for live spaces const results = await client.spaces.search("AI discussion"); // Get details about a specific space const space = await client.spaces.get("1eaKbrPZlbwKX"); // Browse available topics const topics = await client.spaces.browseTopics(); // Subscribe to an upcoming space await client.spaces.subscribe("1eaKbrPZlbwKX"); // Share a space await client.spaces.addSharing("1eaKbrPZlbwKX"); // Later, unsubscribe await client.spaces.unsubscribe("1eaKbrPZlbwKX"); ``` --- --- url: /more/syndication.md --- # Syndication API Syndication is the internal Twitter API used for generating tweet embeds, unauthenticated. It's great for converting thousands of tweet IDs to actual tweets, fast, and needing an account that could get suspended. As far as I know, this API has no ratelimits. This API only supports fetching tweets as profile timelines have been effectively removed from the embeds API and no longer work. ## `syndication.getTweet(tweetId)` ```js import Emusks from "emusks"; const client = new Emusks(); // you don't have to authenticate! const tweet = await client.syndication.getTweet("2023357708301455483"); ``` ```json { "__typename": "Tweet", "lang": "en", "favorite_count": 14, "created_at": "2026-02-16T11:24:04.000Z", "display_text_range": [ 0, 155 ], "entities": { "hashtags": {}, "urls": {}, "user_mentions": {}, "symbols": {} }, "id_str": "2023357708301455483", "text": "Underdocumented: `bun build --compile` supports `--splitting —format=esm` Lazy load code embedded in your single-file executable to improve CLI start time", "user": { "id_str": "2489440172", "name": "Jarred Sumner", "screen_name": "jarredsumner", "is_blue_verified": true, "profile_image_shape": "Circle", "verified": false, "profile_image_url_https": "https://pbs.twimg.com/profile_images/1342417825483300864/Vz4ChOFG_normal.jpg", "highlighted_label": { "description": "Bun", "badge": { "url": "https://pbs.twimg.com/profile_images/1809959835884216320/MDn6rbnJ_bigger.jpg" }, "url": { "url": "https://twitter.com/bunjavascript", "url_type": "DeepLink" }, "user_label_type": "BusinessLabel", "user_label_display_type": "Badge" } }, "edit_control": { "edit_tweet_ids": [ "2023357708301455483" ], "editable_until_msecs": "1771244644000", "is_edit_eligible": true, "edits_remaining": "5" }, "conversation_count": 1, "news_action_type": "conversation", "isEdited": false, "isStaleEdit": false } ``` ### Videos ```json { "__typename": "Tweet", "lang": "und", "favorite_count": 1101, "possibly_sensitive": false, "created_at": "2026-02-16T01:18:42.000Z", "display_text_range": [0, 130], "entities": { "hashtags": [], "urls": [], "user_mentions": [], "symbols": [], "media": [ { "display_url": "pic.x.com/5xrYfslWJc", "expanded_url": "https://x.com/ExtremeBlitz__/status/2023205364368351634/video/1", "indices": [86, 109], "url": "https://t.co/5xrYfslWJc" } ] }, "id_str": "2023205364368351634", "text": "…", "user": { … }, "edit_control": { … }, "mediaDetails": [ { "additional_media_info": {}, "display_url": "pic.x.com/5xrYfslWJc", "expanded_url": "https://x.com/ExtremeBlitz__/status/2023205364368351634/video/1", "ext_media_availability": { "status": "Available" }, "indices": [86, 109], "media_url_https": "https://pbs.twimg.com/amplify_video_thumb/2023205304318488576/img/ljIYhvhxV9SKP3IB.jpg", "original_info": { "height": 514, "width": 1080, "focus_rects": [] }, "sizes": { "large": { "h": 514, "resize": "fit", "w": 1080 }, "medium": { … }, "small": { … }, "thumb": { … } }, "type": "video", "url": "https://t.co/5xrYfslWJc", "video_info": { "aspect_ratio": [540, 257], "duration_millis": 9516, "variants": [ { "content_type": "application/x-mpegURL", "url": "https://video.twimg.com/amplify_video/2023205304318488576/pl/MC4uaCsa2h7OlCkT.m3u8" }, { "bitrate": 256000, "content_type": "video/mp4", "url": "https://video.twimg.com/amplify_video/2023205304318488576/vid/avc1/566x270/AYxnjkt3c60VBmoa.mp4" }, … ] } } ], "photos": [], "video": { "aspectRatio": [540, 257], "contentType": "media_entity", "durationMs": 9516, "mediaAvailability": { "status": "available" }, "poster": "https://pbs.twimg.com/amplify_video_thumb/2023205304318488576/img/ljIYhvhxV9SKP3IB.jpg", "variants": [ { "type": "application/x-mpegURL", "src": "https://video.twimg.com/amplify_video/2023205304318488576/pl/MC4uaCsa2h7OlCkT.m3u8" }, { "type": "video/mp4", "src": "https://video.twimg.com/amplify_video/2023205304318488576/vid/avc1/566x270/AYxnjkt3c60VBmoa.mp4" }, … ], "videoId": { "type": "tweet", "id": "2023205364368351634" }, "viewCount": 0 }, "conversation_count": 23, "news_action_type": "conversation", "isEdited": false, "isStaleEdit": false } ``` ### Deleted tweets If a tweet is deleted or the account suspended, it'll show up as a `TweetTombstone`: ```json { "__typename": "TweetTombstone", "tombstone": { "text": { "text": "This Post was deleted by the Post author. Learn more", "entities": [ { "from_index": 42, "to_index": 52, "ref": { "__typename": "TimelineUrl", "url": "https://help.twitter.com/rules-and-policies/notices-on-twitter", "url_type": "ExternalUrl" } } ], "rtl": false } } } ``` --- --- url: /tweets/timelines.md --- # Timelines Fetch home timelines and other feed-like content. ## Response format All timeline methods return a parsed object: ```js { tweets: [ /* parsed tweet objects */ ], users: [ /* parsed user objects (when applicable) */ ], nextCursor: "DAABCgAB...", // pass to opts.cursor for the next page previousCursor: "DAABCgAA...", // pass to opts.cursor to go back raw: { /* original Twitter response */ }, } ``` ## `timelines.home(opts?)` Get your home timeline (algorithmic). Uses the GraphQL `HomeTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets, nextCursor } = await client.timelines.home(); console.log(tweets[0].text); // Paginate const page2 = await client.timelines.home({ cursor: nextCursor }); // Fetch more const big = await client.timelines.home({ count: 50 }); ``` ## `timelines.homeLatest(opts?)` Get your home timeline sorted by latest (chronological). Uses the GraphQL `HomeLatestTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.timelines.homeLatest(); ``` ## `timelines.connect(opts?)` Get the "Connect" tab timeline (who to follow suggestions, etc.). Uses the GraphQL `ConnectTabTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { users } = await client.timelines.connect(); ``` ## `timelines.moderated(opts?)` Get the moderated timeline (tweets hidden from your profile). Uses the GraphQL `ModeratedTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.timelines.moderated(); ``` ## `timelines.creatorSubscriptions(opts?)` Get the creator subscriptions timeline. Uses the GraphQL `CreatorSubscriptionsTimeline` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.timelines.creatorSubscriptions(); ``` ## Pagination All timeline methods return `nextCursor` and `previousCursor` automatically. Pass them as `opts.cursor`: ```js // First page const page1 = await client.timelines.home({ count: 20 }); console.log(page1.tweets); // Next page const page2 = await client.timelines.home({ cursor: page1.nextCursor }); // Collect multiple pages const allTweets = []; let cursor; for (let i = 0; i < 5; i++) { const page = await client.timelines.home({ count: 20, cursor }); allTweets.push(...page.tweets); cursor = page.nextCursor; if (!cursor) break; } ``` --- --- url: /discovery/topics.md --- # Topics Follow, unfollow, and manage Twitter/X topics. ## `topics.follow(topicId)` Follow a topic. Uses the GraphQL `TopicFollow` mutation. ```js await client.topics.follow("123456789"); ``` ## `topics.unfollow(topicId)` Unfollow a topic. Uses the GraphQL `TopicUnfollow` mutation. ```js await client.topics.unfollow("123456789"); ``` ## `topics.notInterested(topicId)` Mark a topic as "Not Interested". Uses the GraphQL `TopicNotInterested` mutation. ```js await client.topics.notInterested("123456789"); ``` ## `topics.undoNotInterested(topicId)` Undo a "Not Interested" action on a topic. Uses the GraphQL `TopicUndoNotInterested` mutation. ```js await client.topics.undoNotInterested("123456789"); ``` ## `topics.get(topicId)` Get a topic by its REST ID. Uses the GraphQL `TopicByRestId` query. ```js const topic = await client.topics.get("123456789"); ``` ## `topics.landingPage(topicId, opts?)` Get the landing page for a topic (tweets, related content). Uses the GraphQL `TopicLandingPage` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const page = await client.topics.landingPage("123456789"); // Paginate const next = await client.topics.landingPage("123456789", { cursor: "DAABCgAB...", }); // Fetch more const more = await client.topics.landingPage("123456789", { count: 50 }); ``` ## `topics.toFollow(opts?)` Get suggested topics to follow (sidebar suggestions). Uses the GraphQL `TopicToFollowSidebar` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const suggestions = await client.topics.toFollow(); ``` ## `topics.manage(opts?)` Get your topics management page (all followed topics, not interested topics). Uses the GraphQL `TopicsManagementPage` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const managed = await client.topics.manage(); ``` ## `topics.picker(opts?)` Get the topic picker page (for onboarding or discovery). Uses the GraphQL `TopicsPickerPage` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const picker = await client.topics.picker(); ``` ## `topics.pickerById(topicId, opts?)` Get the topic picker page for a specific topic. Uses the GraphQL `TopicsPickerPageById` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `topicId` | `string` | The topic ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const picker = await client.topics.pickerById("123456789"); ``` ## `topics.viewing(userId, opts?)` View the topics another user follows. Uses the GraphQL `ViewingOtherUsersTopicsPage` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const userTopics = await client.topics.viewing("44196397"); ``` ## Full example ```js // Discover topics to follow const suggestions = await client.topics.toFollow(); // Browse the topic picker const picker = await client.topics.picker(); // Follow a topic await client.topics.follow("123456789"); // Get topic details const topic = await client.topics.get("123456789"); // Browse topic content const content = await client.topics.landingPage("123456789", { count: 30 }); // Paginate through topic content const page2 = await client.topics.landingPage("123456789", { cursor: "DAABCgAB...", }); // Mark a topic as not interesting await client.topics.notInterested("987654321"); // Undo that action await client.topics.undoNotInterested("987654321"); // Manage your followed topics const managed = await client.topics.manage(); // See what topics another user follows const theirTopics = await client.topics.viewing("44196397"); // Unfollow a topic await client.topics.unfollow("123456789"); ``` --- --- url: /discovery/trends.md --- # Trends Explore trending topics, manage trend settings, and interact with trends. ## `trends.available()` Get available trend locations. Uses the v1.1 `trends/available` endpoint. ```js const locations = await client.trends.available(); ``` ## `trends.history(opts?)` Get your trend history. Uses the GraphQL `TrendHistory` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const history = await client.trends.history(); // Paginate const next = await client.trends.history({ cursor: "DAABCgAB..." }); ``` ## `trends.relevantUsers(trendName, opts?)` Get users relevant to a trend. Uses the GraphQL `TrendRelevantUsers` query. | Option | Type | Description | | ---------------- | -------- | ---------------------------- | | `trendName` | `string` | The trend name or hashtag | | `opts.variables` | `object` | Additional GraphQL variables | ```js const users = await client.trends.relevantUsers("JavaScript"); ``` ## `trends.explore(opts?)` Get the Explore page content (trending topics, events, etc.). Uses the GraphQL `ExplorePage` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const explore = await client.trends.explore(); // Paginate const next = await client.trends.explore({ count: 40, cursor: "DAABCgAB..." }); ``` ## `trends.exploreSidebar(opts?)` Get the Explore sidebar content. Uses the GraphQL `ExploreSidebar` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const sidebar = await client.trends.exploreSidebar(); ``` ## `trends.report(trendId)` Report a trend. Uses the GraphQL `ReportTrend` mutation. ```js await client.trends.report("trend_id_123"); ``` ## `trends.save(trendId)` Save a trend. Uses the GraphQL `SaveTrend` mutation. ```js await client.trends.save("trend_id_123"); ``` ## `trends.action(trendId, action)` Perform an action on a trend. Uses the GraphQL `ActionTrend` mutation. | Param | Type | Description | | --------- | -------- | --------------------- | | `trendId` | `string` | The trend ID | | `action` | `string` | The action to perform | ```js await client.trends.action("trend_id_123", "dismiss"); ``` ## `trends.getById(trendId)` Get an AI-generated trend summary by its REST ID. Uses the GraphQL `AiTrendByRestId` query. ```js const trend = await client.trends.getById("trend_id_123"); ``` ## `trends.exploreSettings()` Get your Explore page settings (location, etc.). Uses the v2 `guide/get_explore_settings` endpoint. ```js const settings = await client.trends.exploreSettings(); ``` ## `trends.setExploreSettings(params?)` Update your Explore page settings. Uses the v2 `guide/set_explore_settings` endpoint. ```js await client.trends.setExploreSettings({ location: { woeid: 23424977 }, // United States }); ``` ## Full example ```js // See what's trending on the Explore page const explore = await client.trends.explore(); // Check available trend locations const locations = await client.trends.available(); // Get explore settings and update location const settings = await client.trends.exploreSettings(); await client.trends.setExploreSettings({ location: { woeid: 44418 }, // London }); // See who's involved in a trend const users = await client.trends.relevantUsers("#JavaScript"); // Get AI summary for a trend const summary = await client.trends.getById("trend_id_123"); // View sidebar content const sidebar = await client.trends.exploreSidebar(); // Browse your trend history const history = await client.trends.history(); // Save an interesting trend await client.trends.save("trend_id_456"); // Report a problematic trend await client.trends.report("trend_id_789"); ``` --- --- url: /tweets/tweets.md --- # Tweets Create, delete, like, retweet, pin, schedule, and manage tweets. ## `tweets.create(text, opts?)` Create a new tweet. Uses the GraphQL `CreateTweet` mutation. | Option | Type | Description | | --- | --- | --- | | `text` | `string` | The tweet text | | `opts.mediaIds` | `string[]` | Array of [media IDs](./media) to attach | | `opts.replyTo` | `string` | Tweet ID to reply to | | `opts.quoteTweetId` | `string` | Tweet ID to quote | | `opts.sensitive` | `boolean` | Mark media as possibly sensitive | | `opts.conversationControl` | `string` | Who can reply: `"ByInvitation"` or `"Community"` | | `opts.poll` | `object` | Attach a [poll](#polls) | | `opts.cardUri` | `string` | A Twitter Card URI to attach (cannot be combined with `poll`) | | `opts.variables` | `object` | Additional GraphQL variables to merge | ```js const tweet = await client.tweets.create("Hello world!"); // Reply to a tweet const reply = await client.tweets.create("Great point!", { replyTo: "1234567890", }); // Quote tweet const qt = await client.tweets.create("Check this out", { quoteTweetId: "1234567890", }); // With media const media = await client.tweets.create("Photo time", { mediaIds: ["9876543210"], sensitive: false, }); // Restrict replies const limited = await client.tweets.create("Only mentioned can reply", { conversationControl: "ByInvitation", }); // With a poll const poll = await client.tweets.create("Best language?", { poll: { choices: ["JavaScript", "Python", "Rust"], duration_minutes: 1440, }, }); // Upload media and tweet const { media_id } = await client.media.create("./photo.jpg", { alt_text: "A gorgeous sunset", }); const mediaTweet = await client.tweets.create("Golden hour", { mediaIds: [media_id], }); ``` **Returns:** Parsed tweet object. ### Polls Create tweets with polls by passing a `poll` option to `tweets.create()`. #### `opts.poll` | Field | Type | Required | Description | | --- | --- | --- | --- | | `choices` | `string[] \| object[]` | **Yes** | 2–4 choices — strings for text polls, objects for media polls | | `duration_minutes` | `number` | No | How long the poll stays open (default: **1440** = 24 h, max 7 days) | Each choice can be: * A **string** — for text-only polls (up to 25 characters) * An **object** `{ label, image }` — for media polls, where `image` is a `media_id` from `media.create()` #### Text poll ```js await client.tweets.create("Tabs or spaces?", { poll: { choices: ["Tabs", "Spaces"], }, }); ``` #### Media poll Upload images first, then reference their `media_id` in each choice: ```js const [a, b] = await Promise.all([ client.media.create("./option-a.jpg"), client.media.create("./option-b.jpg"), ]); await client.tweets.create("Which design do you prefer?", { poll: { choices: [ { label: "Design A", image: a.media_id }, { label: "Design B", image: b.media_id }, ], duration_minutes: 2880, // 2 days }, }); ``` #### Custom duration ```js // 7-day poll await client.tweets.create("Best frontend framework in 2026?", { poll: { choices: ["React", "Vue", "Svelte", "Angular"], duration_minutes: 10080, }, }); // Quick 30-minute poll await client.tweets.create("Should I mass-refactor right now?", { poll: { choices: ["Do it", "Don't you dare"], duration_minutes: 30, }, }); ``` #### Duration limits | Min | Max | Default | | --------- | ----------------------- | -------------------- | | 5 minutes | 10 080 minutes (7 days) | 1 440 minutes (24 h) | ### Long tweets You **must** use `tweets.createNote(text, opts?)` to write tweets longer than 260 characters, if your account has Premium. This uses the GraphQL `CreateNoteTweet` mutation. | Option | Type | Description | | -------------------- | ---------- | -------------------------------- | | `text` | `string` | The note tweet text | | `opts.mediaIds` | `string[]` | Array of media IDs to attach | | `opts.replyTo` | `string` | Tweet ID to reply to | | `opts.sensitive` | `boolean` | Mark media as possibly sensitive | | `opts.richtext_tags` | `array` | Rich text formatting tags | | `opts.variables` | `object` | Additional GraphQL variables | ```js const note = await client.tweets.createNote("This is a very long note..."); ``` **Returns:** Parsed tweet object. ## `tweets.createThread(items)` Post a thread (multiple tweets chained as replies). Each item after the first automatically replies to the previous tweet. | Param | Type | Description | | --- | --- | --- | | `items` | `(string \| object)[]` | Array of 2+ tweets — strings for text-only, objects for full opts | Each object item accepts the same options as `tweets.create()` (`mediaIds`, `poll`, `sensitive`, etc.) plus a `text` field. ```js // Simple text thread const thread = await client.tweets.createThread([ "1/ Here's a mass thread about JavaScript engines", "2/ V8 powers Chrome and Node.js", "3/ SpiderMonkey powers Firefox", "4/ JavaScriptCore powers Safari", ]); // Mixed thread with media and polls const img = await client.media.create("./diagram.png"); const thread = await client.tweets.createThread([ "Let me explain how this works 🧵", { text: "Here's the architecture diagram:", mediaIds: [img.media_id], }, { text: "What do you think?", poll: { choices: ["Love it", "Needs work"] }, }, ]); // Each item in the returned array is a parsed tweet object console.log(thread[0].id); // first tweet console.log(thread[thread.length - 1].id); // last tweet ``` **Returns:** Array of parsed tweet objects, one per tweet in the thread. ## `tweets.delete(tweetId)` Delete a tweet. Uses the GraphQL `DeleteTweet` mutation. ```js await client.tweets.delete("1234567890"); ``` ## `tweets.like(tweetId)` Like a tweet. Uses the GraphQL `FavoriteTweet` mutation. ```js await client.tweets.like("1234567890"); ``` ## `tweets.unlike(tweetId)` Unlike a tweet. Uses the GraphQL `UnfavoriteTweet` mutation. ```js await client.tweets.unlike("1234567890"); ``` ## `tweets.retweet(tweetId)` Retweet a tweet. Uses the GraphQL `CreateRetweet` mutation. ```js await client.tweets.retweet("1234567890"); ``` ## `tweets.unretweet(tweetId)` Undo a retweet. Uses the GraphQL `DeleteRetweet` mutation. ```js await client.tweets.unretweet("1234567890"); ``` ## `tweets.pin(tweetId)` Pin a tweet to your profile. Uses the GraphQL `PinTweet` mutation. ```js await client.tweets.pin("1234567890"); ``` ## `tweets.unpin(tweetId)` Unpin a tweet from your profile. Uses the GraphQL `UnpinTweet` mutation. ```js await client.tweets.unpin("1234567890"); ``` ## `tweets.get(tweetId)` Get a single tweet by ID. Uses the GraphQL `TweetResultByRestId` query. ```js const tweet = await client.tweets.get("1234567890"); console.log(tweet.text); console.log(tweet.user.username); console.log(tweet.stats.likes); ``` **Returns:** Parsed tweet object. ## `tweets.getMany(tweetIds)` Get multiple tweets by their IDs. Uses the GraphQL `TweetResultsByRestIds` query. ```js const tweets = await client.tweets.getMany(["123", "456", "789"]); tweets.forEach((t) => console.log(t.text)); ``` **Returns:** Array of parsed tweet objects. ## `tweets.detail(tweetId, opts?)` Get a tweet with its full conversation thread. Uses the GraphQL `TweetDetail` query. | Option | Type | Description | | ---------------- | -------- | ---------------------------- | | `opts.variables` | `object` | Additional GraphQL variables | ```js const detail = await client.tweets.detail("1234567890"); ``` ## `tweets.editHistory(tweetId)` Get the edit history of a tweet. Uses the GraphQL `TweetEditHistory` query. ```js const history = await client.tweets.editHistory("1234567890"); ``` ## `tweets.retweeters(tweetId, opts?)` Get users who retweeted a tweet. Uses the GraphQL `Retweeters` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const retweeters = await client.tweets.retweeters("1234567890"); ``` ## `tweets.highlight(tweetId)` Highlight a tweet on your profile. Uses the GraphQL `CreateHighlight` mutation. ```js await client.tweets.highlight("1234567890"); ``` ## `tweets.unhighlight(tweetId)` Remove a tweet highlight. Uses the GraphQL `DeleteHighlight` mutation. ```js await client.tweets.unhighlight("1234567890"); ``` ## `tweets.schedule(text, scheduledAt, opts?)` Schedule a tweet for future posting. Uses the GraphQL `CreateScheduledTweet` mutation. | Option | Type | Description | | --------------- | -------------- | --------------------------------- | | `text` | `string` | The tweet text | | `scheduledAt` | `string\|Date` | When to post (ISO string or Date) | | `opts.mediaIds` | `string[]` | Media IDs to attach | | `opts.replyTo` | `string` | Tweet ID to reply to | ```js await client.tweets.schedule("Good morning!", "2025-01-01T09:00:00Z"); await client.tweets.schedule("With media", new Date("2025-06-01"), { mediaIds: ["111222333"], }); ``` ## `tweets.deleteScheduled(scheduledTweetId)` Delete a scheduled tweet. Uses the GraphQL `DeleteScheduledTweet` mutation. ```js await client.tweets.deleteScheduled("9876543210"); ``` ## `tweets.getScheduled()` Fetch all your scheduled tweets. Uses the GraphQL `FetchScheduledTweets` query. ```js const scheduled = await client.tweets.getScheduled(); ``` ## `tweets.moderate(tweetId)` Moderate a tweet (hide it from your replies). Uses the GraphQL `ModerateTweet` mutation. ```js await client.tweets.moderate("1234567890"); ``` ## `tweets.unmoderate(tweetId)` Unmoderate a tweet. Uses the GraphQL `UnmoderateTweet` mutation. ```js await client.tweets.unmoderate("1234567890"); ``` ## `tweets.pinReply(tweetId)` Pin a reply under your tweet. Uses the GraphQL `PinReply` mutation. ```js await client.tweets.pinReply("1234567890"); ``` ## `tweets.unpinReply(tweetId)` Unpin a reply. Uses the GraphQL `UnpinReply` mutation. ```js await client.tweets.unpinReply("1234567890"); ``` ## `tweets.setConversationControl(tweetId, mode)` Change who can reply to your tweet. Uses the GraphQL `ConversationControlChange` mutation. | Mode | Description | | ---------------- | -------------------- | | `"ByInvitation"` | Only mentioned users | | `"Community"` | Only followers | ```js await client.tweets.setConversationControl("1234567890", "ByInvitation"); ``` ## `tweets.removeConversationControl(tweetId)` Remove reply restrictions from a tweet. Uses the GraphQL `ConversationControlDelete` mutation. ```js await client.tweets.removeConversationControl("1234567890"); ``` ## `tweets.unmention(tweetId)` Remove yourself from a conversation. Uses the GraphQL `UnmentionUserFromConversation` mutation. ```js await client.tweets.unmention("1234567890"); ``` ## `tweets.similar(tweetId)` Find similar posts. Uses the GraphQL `SimilarPosts` query. ```js const similar = await client.tweets.similar("1234567890"); ``` ## Tweet schema All methods that return tweets parse them into a consistent format: ```js { id: "1234567890", text: "Hello world!", created_at: "Mon Jan 01 00:00:00 +0000 2025", conversation_id: "1234567890", in_reply_to_status_id: null, in_reply_to_user_id: null, in_reply_to_screen_name: null, user: { /* parsed user object */ }, stats: { retweets: 10, likes: 42, replies: 3, quotes: 1, bookmarks: 5, views: 1000, }, engagement: { retweeted: false, liked: true, bookmarked: false, }, media: [], urls: [], hashtags: [], user_mentions: [], source: "Twitter Web App", lang: "en", quoting: null, // parsed quoted tweet or null edit_control: {}, card: null, misc: { ... }, } ``` --- --- url: /users/users.md --- # Users Get user profiles, follow, block, mute, manage relationships, and fetch user content. ## `users.get(userId)` Get a user by their REST ID. Uses the GraphQL `UserByRestId` query. ```js const user = await client.users.get("44196397"); console.log(user.name); // "Elon Musk" console.log(user.username); // "elonmusk" ``` **Returns:** Parsed user object. ## `users.getByUsername(username)` Get a user by their screen name. Uses the GraphQL `UserByScreenName` query. ```js const user = await client.users.getByUsername("elonmusk"); console.log(user.id); console.log(user.stats.followers.count); ``` **Returns:** Parsed user object. ## `users.getMany(userIds)` Get multiple users by their REST IDs. Uses the GraphQL `UsersByRestIds` query. ```js const users = await client.users.getMany(["44196397", "12"]); users.forEach((u) => console.log(u.username)); ``` **Returns:** Array of parsed user objects. ## `users.getManyByUsername(screenNames)` Get multiple users by their screen names. Uses the GraphQL `UsersByScreenNames` query. ```js const users = await client.users.getManyByUsername(["elonmusk", "jack"]); users.forEach((u) => console.log(u.id)); ``` **Returns:** Array of parsed user objects. ## User content Fetch a user's tweets, replies, media, and highlights. ### `users.tweets(userId, opts?)` Get a user's tweets. Uses the GraphQL `UserTweets` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets, nextCursor } = await client.users.tweets("44196397"); console.log(tweets[0].text); // Paginate const page2 = await client.users.tweets("44196397", { cursor: nextCursor }); ``` ### `users.replies(userId, opts?)` Get a user's tweets and replies. Uses the GraphQL `UserTweetsAndReplies` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.users.replies("44196397"); ``` ### `users.media(userId, opts?)` Get a user's media tweets. Uses the GraphQL `UserMedia` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.users.media("44196397", { count: 40 }); ``` ### `users.highlights(userId, opts?)` Get a user's highlighted tweets. Uses the GraphQL `UserHighlightsTweets` query. | Option | Type | Description | | ---------------- | -------- | ------------------------------ | | `userId` | `string` | The user's REST ID | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | | `opts.variables` | `object` | Additional GraphQL variables | ```js const { tweets } = await client.users.highlights("44196397"); ``` ## Relationships ### `users.followers(userId, opts?)` Get a user's followers. Uses the GraphQL `Followers` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users, nextCursor } = await client.users.followers("44196397", { count: 50, }); ``` ### `users.following(userId, opts?)` Get users that a user is following. Uses the GraphQL `Following` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.following("44196397"); ``` ### `users.verifiedFollowers(userId, opts?)` Get a user's verified (Blue) followers. Uses the GraphQL `BlueVerifiedFollowers` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.verifiedFollowers("44196397"); ``` ### `users.followersYouKnow(userId, opts?)` Get followers of a user that you also follow. Uses the GraphQL `FollowersYouKnow` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.followersYouKnow("44196397"); ``` ### `users.follow(userId)` Follow a user. Uses the v1.1 `friendships/create` endpoint. ```js const user = await client.users.follow("44196397"); console.log(user.username); // "elonmusk" ``` **Returns:** Parsed user object. ### `users.unfollow(userId)` Unfollow a user. Uses the v1.1 `friendships/destroy` endpoint. ```js await client.users.unfollow("44196397"); ``` **Returns:** Parsed user object. ### `users.block(userId)` Block a user. Uses the v1.1 `blocks/create` endpoint. ```js await client.users.block("44196397"); ``` **Returns:** Parsed user object. ### `users.unblock(userId)` Unblock a user. Uses the v1.1 `blocks/destroy` endpoint. ```js await client.users.unblock("44196397"); ``` **Returns:** Parsed user object. ### `users.mute(userId)` Mute a user. Uses the v1.1 `mutes/users/create` endpoint. ```js await client.users.mute("44196397"); ``` **Returns:** Parsed user object. ### `users.unmute(userId)` Unmute a user. Uses the v1.1 `mutes/users/destroy` endpoint. ```js await client.users.unmute("44196397"); ``` **Returns:** Parsed user object. ### `users.removeFollower(userId)` Remove a user from your followers. Uses the GraphQL `RemoveFollower` mutation. ```js await client.users.removeFollower("44196397"); ``` ### `users.blocked(opts?)` Get all accounts you have blocked. Uses the GraphQL `BlockedAccountsAll` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.blocked({ count: 100 }); ``` ### `users.muted(opts?)` Get all accounts you have muted. Uses the GraphQL `MutedAccounts` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.muted(); ``` ## Profile management ### `users.lookup(params?)` Look up users via the v1.1 `users/lookup` endpoint. Supports lookup by `user_id` or `screen_name`. ```js // By user IDs const users = await client.users.lookup({ user_id: "44196397,12" }); // By screen names const users = await client.users.lookup({ screen_name: "elonmusk,jack" }); ``` ### `users.updateProfile(params?)` Update your profile. Uses the v1.1 `account/update_profile` endpoint. | Param | Type | Description | | ------------- | -------- | ------------ | | `name` | `string` | Display name | | `description` | `string` | Bio | | `location` | `string` | Location | | `url` | `string` | Website URL | ```js const updated = await client.users.updateProfile({ name: "New Name", description: "Updated bio", location: "San Francisco, CA", url: "https://example.com", }); ``` **Returns:** Parsed user object. ### `users.updateProfileImage(imageData)` Update your profile picture. Uses the v1.1 `account/update_profile_image` endpoint. ```js await client.users.updateProfileImage(base64ImageData); ``` ### `users.updateProfileBanner(bannerData)` Update your profile banner. Uses the v1.1 `account/update_profile_banner` endpoint. ```js await client.users.updateProfileBanner(base64BannerData); ``` ### `users.removeProfileBanner()` Remove your profile banner. Uses the v1.1 `account/remove_profile_banner` endpoint. ```js await client.users.removeProfileBanner(); ``` ## Subscriptions ### `users.subscriptions(userId, opts?)` Get a user's creator subscriptions. Uses the GraphQL `UserCreatorSubscriptions` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.subscriptions("44196397"); ``` ### `users.subscribers(userId, opts?)` Get a user's creator subscribers. Uses the GraphQL `UserCreatorSubscribers` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.subscribers("44196397"); ``` ### `users.superFollowers(opts?)` Get your super followers. Uses the GraphQL `SuperFollowers` query. | Option | Type | Description | | ------------- | -------- | ------------------------------ | | `opts.count` | `number` | Number of results (default 20) | | `opts.cursor` | `string` | Pagination cursor | ```js const { users } = await client.users.superFollowers(); ``` ### `users.recommendations(params?)` Get user recommendations. Uses the v1.1 `users/recommendations` endpoint. ```js const recs = await client.users.recommendations(); ``` ## Paginated response format All paginated methods (`tweets`, `replies`, `media`, `highlights`, `followers`, `following`, `blocked`, `muted`, etc.) return: ```js { tweets: [ /* parsed tweet objects */ ], users: [ /* parsed user objects */ ], nextCursor: "DAABCgAB...", previousCursor: "DAABCgAA...", raw: { /* original Twitter response */ }, } ``` Content methods populate `tweets`, relationship methods populate `users`. Pass `nextCursor` as `opts.cursor` to fetch the next page. ## User schema All methods that return users parse them into a consistent format: ```js { id: "44196397", name: "Elon Musk", username: "elonmusk", description: "...", banner: "https://pbs.twimg.com/...", url: "https://t.co/...", location: "Earth", protected: false, created_at: "Tue Jun 02 20:12:29 +0000 2009", backgroundColor: "...", profile_picture: { url: "https://pbs.twimg.com/...", shape: "Circle", }, stats: { followers: { count: 200000000, fast_followers: 0, normal_followers: 200000000, }, following: 900, subscriptions_count: 0, likes: 50000, listed: 150000, media: 2000, posts: 40000, }, verification: { verified: false, premium_verified: true, }, pinned_tweets: ["1234567890"], birthdate: {}, labels: { parody_commentary_fan_label: "None", highlightedLabel: undefined, }, misc: { ... }, } ```