From b702cd85ca257ca818a59e4400a5e093d27d0ea5 Mon Sep 17 00:00:00 2001 From: Taylor Caldwell Date: Fri, 16 Jan 2026 19:43:17 -0800 Subject: [PATCH 1/4] Add pay-per-usage docs --- changelog.mdx | 22 +- developer-terms/agreement.mdx | 2 +- developer-terms/ppu-pilot-agreement.mdx | 2 +- docs.json | 6 +- .../guides/authentication-best-practices.mdx | 2 +- .../authentication/guides/log-in-with-x.mdx | 2 +- .../oauth-1-0a/api-key-and-secret.mdx | 6 +- .../oauth-1-0a/authorizing-a-request.mdx | 4 +- .../oauth-1-0a/creating-a-signature.mdx | 2 +- .../obtaining-user-access-tokens.mdx | 2 +- .../oauth-2-0/authorization-code.mdx | 8 +- .../oauth-2-0/bearer-tokens.mdx | 4 +- .../authentication/oauth-2-0/overview.mdx | 2 +- .../oauth-2-0/user-access-token.mdx | 2 +- fundamentals/authentication/overview.mdx | 2 +- fundamentals/counting-characters.mdx | 140 ++- fundamentals/developer-apps.mdx | 334 ++---- fundamentals/developer-portal.mdx | 139 ++- fundamentals/projects.mdx | 108 -- fundamentals/rate-limits.mdx | 102 +- fundamentals/security.mdx | 203 +++- fundamentals/x-ids.mdx | 107 +- overview.mdx | 116 +- status.mdx | 2 +- tutorials/explore-a-users-posts.mdx | 12 +- ...using-the-full-archive-search-endpoint.mdx | 18 +- ...ing-started-with-r-and-v2-of-the-x-api.mdx | 4 +- x-ads-api/creatives.mdx | 2 +- .../getting-started/step-by-step-guide.mdx | 4 +- x-ads-api/measurement/web-conversions.mdx | 4 +- x-api/account-activity/introduction.mdx | 949 ++------------- x-api/account-activity/migrate/overview.mdx | 4 +- x-api/activity/introduction.mdx | 159 ++- x-api/activity/quickstart.mdx | 415 +++---- x-api/communities/lookup/introduction.mdx | 117 +- x-api/communities/search/introduction.mdx | 159 +-- x-api/community-notes/quickstart.mdx | 539 ++++----- .../batch-compliance/introduction.mdx | 159 ++- .../batch-compliance/quickstart.mdx | 283 +++-- x-api/compliance/streams/introduction.mdx | 2 +- x-api/direct-messages/lookup/integrate.mdx | 366 +++--- x-api/direct-messages/lookup/introduction.mdx | 95 +- x-api/direct-messages/lookup/migrate.mdx | 2 +- x-api/direct-messages/lookup/quickstart.mdx | 369 ++++-- x-api/direct-messages/manage/integrate.mdx | 310 +++-- x-api/direct-messages/manage/introduction.mdx | 117 +- x-api/direct-messages/manage/migrate.mdx | 2 +- x-api/direct-messages/manage/quickstart.mdx | 351 ++++-- .../fundamentals/account-activity.mdx | 20 +- .../fundamentals/engagement-api.mdx | 4 +- x-api/fundamentals/consistency.mdx | 216 ++-- x-api/fundamentals/conversation-id.mdx | 302 ++--- x-api/fundamentals/data-dictionary.mdx | 38 +- x-api/fundamentals/edit-posts.mdx | 254 ++-- x-api/fundamentals/expansions.mdx | 313 ++--- x-api/fundamentals/fields.mdx | 222 +++- x-api/fundamentals/metrics.mdx | 265 +++-- x-api/fundamentals/pagination.mdx | 209 +++- x-api/fundamentals/post-annotations.mdx | 479 ++++---- x-api/fundamentals/post-cap.mdx | 157 ++- x-api/fundamentals/rate-limits.mdx | 1046 ++++++++--------- .../response-codes-and-errors.mdx | 318 ++--- x-api/fundamentals/versioning.mdx | 205 +++- x-api/getting-started/about-x-api.mdx | 209 ++-- x-api/getting-started/getting-access.mdx | 133 ++- x-api/getting-started/important-resources.mdx | 129 +- .../make-your-first-request.mdx | 239 +++- x-api/getting-started/pricing.mdx | 98 ++ x-api/introduction.mdx | 262 +++-- x-api/lists/list-lookup/integrate.mdx | 294 ++++- x-api/lists/list-lookup/introduction.mdx | 90 +- x-api/lists/list-lookup/migrate/overview.mdx | 2 +- .../migrate/standard-to-twitter-api-v2.mdx | 2 +- x-api/lists/list-lookup/quickstart.mdx | 262 ++++- x-api/lists/list-members/integrate.mdx | 77 +- x-api/lists/list-members/introduction.mdx | 103 +- ...bers-lookup-standard-to-twitter-api-v2.mdx | 2 +- ...ist-members-standard-to-twitter-api-v2.mdx | 2 +- x-api/lists/list-members/migrate/overview.mdx | 2 +- .../quickstart/list-members-lookup.mdx | 278 +++-- .../quickstart/manage-list-members.mdx | 181 ++- .../list-members/quickstart/overview.mdx | 102 +- x-api/lists/list-tweets/integrate.mdx | 66 +- x-api/lists/list-tweets/introduction.mdx | 81 +- x-api/lists/list-tweets/migrate/overview.mdx | 2 +- .../migrate/standard-to-twitter-api-v2.mdx | 92 +- x-api/lists/list-tweets/quickstart.mdx | 216 ++-- x-api/lists/manage-lists/integrate.mdx | 143 ++- x-api/lists/manage-lists/introduction.mdx | 99 +- x-api/lists/manage-lists/migrate/overview.mdx | 2 +- .../migrate/standard-to-twitter-api-v2.mdx | 137 ++- x-api/lists/manage-lists/quickstart.mdx | 285 ++++- x-api/lists/pinned-lists/integrate.mdx | 61 +- x-api/lists/pinned-lists/introduction.mdx | 94 +- .../quickstart/manage-pinned-lists.mdx | 185 ++- .../pinned-lists/quickstart/overview.mdx | 103 +- .../quickstart/pinned-list-lookup.mdx | 166 ++- .../media/quickstart/media-upload-chunked.mdx | 418 ++++--- x-api/migrate/overview.mdx | 4 +- x-api/news/introduction.mdx | 2 +- x-api/posts/bookmarks/integrate.mdx | 96 +- x-api/posts/bookmarks/introduction.mdx | 99 +- .../bookmarks/quickstart/bookmarks-lookup.mdx | 224 ++-- .../bookmarks/quickstart/manage-bookmarks.mdx | 176 ++- .../posts/counts/integrate/build-a-query.mdx | 4 +- x-api/posts/counts/integrate/overview.mdx | 73 +- x-api/posts/counts/introduction.mdx | 180 ++- .../migrate/enterprise-to-twitter-api-v2.mdx | 4 +- x-api/posts/counts/migrate/overview.mdx | 12 +- .../quickstart/full-archive-tweet-counts.mdx | 411 ++++--- .../counts/quickstart/recent-tweet-counts.mdx | 297 +++-- .../integrate/build-a-rule.mdx | 309 ++--- x-api/posts/filtered-stream/introduction.mdx | 214 +++- .../filtered-stream/migrate/overview.mdx | 31 +- ...rtrack-api-migration-to-twitter-api-v2.mdx | 6 +- .../migrate/standard-to-twitter-api-v2.mdx | 124 +- x-api/posts/filtered-stream/quickstart.mdx | 390 +++--- x-api/posts/hide-replies/introduction.mdx | 96 +- x-api/posts/hide-replies/migrate.mdx | 79 +- x-api/posts/hide-replies/quickstart.mdx | 188 ++- x-api/posts/likes/introduction.mdx | 106 +- ...ikes-lookup-standard-to-twitter-api-v2.mdx | 123 +- ...anage-likes-standard-to-twitter-api-v2.mdx | 132 ++- x-api/posts/likes/migrate/overview.mdx | 2 +- x-api/posts/likes/quickstart/likes-lookup.mdx | 306 ++--- x-api/posts/likes/quickstart/manage-likes.mdx | 181 ++- x-api/posts/lookup/integrate.mdx | 314 +++-- x-api/posts/lookup/introduction.mdx | 91 +- x-api/posts/lookup/migrate/overview.mdx | 4 +- .../migrate/standard-to-twitter-api-v2.mdx | 141 ++- x-api/posts/lookup/quickstart.mdx | 266 +++-- x-api/posts/manage-tweets/integrate.mdx | 199 +++- x-api/posts/manage-tweets/introduction.mdx | 164 ++- .../posts/manage-tweets/migrate/overview.mdx | 4 +- .../migrate/standard-to-twitter-api-v2.mdx | 104 +- x-api/posts/manage-tweets/quickstart.mdx | 445 ++++++- x-api/posts/quote-tweets/introduction.mdx | 97 +- x-api/posts/quote-tweets/quickstart.mdx | 344 ++---- x-api/posts/retweets/integrate.mdx | 129 +- x-api/posts/retweets/introduction.mdx | 106 +- ...ge-retweets-standard-to-twitter-api-v2.mdx | 132 ++- x-api/posts/retweets/migrate/overview.mdx | 6 +- ...eets-lookup-standard-to-twitter-api-v2.mdx | 59 +- .../retweets/quickstart/manage-retweets.mdx | 186 ++- .../retweets/quickstart/retweets-lookup.mdx | 220 ++-- .../retweets/quickstart/retweets-of-me.mdx | 219 +++- .../posts/search/integrate/build-a-query.mdx | 302 +++-- x-api/posts/search/integrate/overview.mdx | 295 +++-- x-api/posts/search/introduction.mdx | 194 ++- .../migrate/enterprise-to-twitter-api-v2.mdx | 2 +- x-api/posts/search/migrate/overview.mdx | 102 +- .../migrate/standard-to-twitter-api-v2.mdx | 80 +- .../search/quickstart/full-archive-search.mdx | 306 +++-- .../posts/search/quickstart/recent-search.mdx | 324 +++-- x-api/posts/timelines/integrate.mdx | 355 ++++-- x-api/posts/timelines/introduction.mdx | 168 ++- x-api/posts/timelines/migrate/overview.mdx | 42 +- .../migrate/standard-to-twitter-api-v2.mdx | 159 ++- .../quickstart/reverse-chron-quickstart.mdx | 303 +++-- .../quickstart/user-mention-quickstart.mdx | 323 +++-- x-api/posts/volume-streams/introduction.mdx | 2 +- x-api/powerstream/introduction.mdx | 4 - x-api/spaces/lookup/introduction.mdx | 111 +- x-api/spaces/lookup/quickstart.mdx | 319 +++-- x-api/spaces/search/introduction.mdx | 108 +- x-api/spaces/search/quickstart.mdx | 293 +++-- x-api/tools-and-libraries/overview.mdx | 225 ++-- x-api/tools-and-libraries/sdks.mdx | 292 +++-- .../personalized-trends/introduction.mdx | 112 +- x-api/trends/trends-by-woeid/introduction.mdx | 135 ++- x-api/usage/introduction.mdx | 122 +- x-api/users/blocks/integrate.mdx | 200 +++- x-api/users/blocks/introduction.mdx | 89 +- x-api/users/blocks/migrate.mdx | 150 ++- x-api/users/blocks/quickstart.mdx | 319 +++-- x-api/users/follows/introduction.mdx | 102 +- x-api/users/follows/migrate/overview.mdx | 6 +- .../migrate/standard-to-twitter-api-v2.mdx | 145 ++- x-api/users/follows/quickstart.mdx | 323 ++++- x-api/users/lookup/integrate.mdx | 383 +++++- x-api/users/lookup/introduction.mdx | 108 +- x-api/users/lookup/migrate/overview.mdx | 2 +- .../migrate/standard-to-twitter-api-v2.mdx | 178 ++- .../quickstart/authenticated-lookup.mdx | 230 +++- x-api/users/lookup/quickstart/user-lookup.mdx | 319 +++-- x-api/users/mutes/integrate.mdx | 246 +++- x-api/users/mutes/introduction.mdx | 92 +- ...anage-mutes-standard-to-twitter-api-v2.mdx | 144 ++- ...utes-lookup-standard-to-twitter-api-v2.mdx | 82 +- x-api/users/mutes/migrate/overview.mdx | 6 +- .../quickstart/manage-mutes-quickstart.mdx | 207 +++- x-api/users/mutes/quickstart/mutes-lookup.mdx | 206 +++- x-api/users/search/introduction.mdx | 95 +- x-api/webhooks/introduction.mdx | 415 ++----- x-api/webhooks/stream/introduction.mdx | 2 +- x-api/what-to-build.mdx | 220 +++- xdks/python/authentication.mdx | 8 +- xdks/python/quickstart.mdx | 2 +- 198 files changed, 19921 insertions(+), 10561 deletions(-) delete mode 100644 fundamentals/projects.mdx create mode 100644 x-api/getting-started/pricing.mdx diff --git a/changelog.mdx b/changelog.mdx index dc3d5481c..b096829de 100644 --- a/changelog.mdx +++ b/changelog.mdx @@ -251,7 +251,7 @@ Today, we’re launching improvements to the [reposts lookup](https://developer. ### Support for OAuth 2.0 Authentication -Today, all developers can now authenticate using OAuth 2.0 by selecting OAuth 2.0 as an authentication method in the developer portal. We’ve added support for confidential and public clients and all relevant v2 endpoints to use this authentication method as part of this release. +Today, all developers can now authenticate using OAuth 2.0 by selecting OAuth 2.0 as an authentication method in the Developer Console. We’ve added support for confidential and public clients and all relevant v2 endpoints to use this authentication method as part of this release. OAuth 2.0 is an industry-standard authorization protocol that provides developers more control over an application’s scopes and improves authorization flows across multiple devices. In other words, developers building applications for people on X will have more control over the information their App requests from its users, so that you only have to ask your end-users for the data and information you need. @@ -407,7 +407,7 @@ Today, we’re launching the new [post counts endpoints](/x-api/posts/counts/int ### Improvements to Teams -Today, we’re launching key improvements to the functionality of teams based on feedback we’ve heard from the community. We have updated the style and design of the team page. We also made changes to the invitation flow to make it easier to invite team members or to learn more about why an invitation might have failed. If you have a team account you can check out the changes by viewing the [teams page](https://developer.x.com/en/portal/teams) of the developer portal. To learn more about teams check out our [documentation on the subject](https://developer.x.com/en/docs/developer-portal/overview#team) and related [FAQ](https://developer.x.com/en/docs/developer-portal/faq#dev-portal-management-faq). +Today, we’re launching key improvements to the functionality of teams based on feedback we’ve heard from the community. We have updated the style and design of the team page. We also made changes to the invitation flow to make it easier to invite team members or to learn more about why an invitation might have failed. If you have a team account you can check out the changes by viewing the [teams page](https://developer.x.com/en/portal/teams) of the Developer Console. To learn more about teams check out our [documentation on the subject](https://developer.x.com/en/docs/Developer Console/overview#team) and related [FAQ](https://developer.x.com/en/docs/Developer Console/faq#dev-portal-management-faq). To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-improvements-to-the-functionality-of-teams/155447/2). @@ -463,13 +463,13 @@ Today, we’re launching the new [manage follows](/x-api/users/follows) endpoint ### Introduction of Academic Research Product Track -Today, we are introducing the new [Academic Research product track](/resources/fundamentals/projects) to [X API v2](/x-api/introduction). This update introduces a new application process for Academic Researchers that will provide those that are approved with an Academic Research [Project](/resources/fundamentals/projects) that will unlock greater access and advanced functionality. +Today, we are introducing the new [Academic Research product track](/resources/fundamentals/developer-apps) to [X API v2](/x-api/introduction). This update introduces a new application process for Academic Researchers that will provide those that are approved with an Academic Research [Project](/resources/fundamentals/developer-apps) that will unlock greater access and advanced functionality. With the new Academic Research product track, you will be able to access the following: * The new X API v2 [full-archive search endpoint](/x-api/posts/full-archive-search), which is only available to the Academic Research product track at this time. - * An increased [post cap](/x-api/fundamentals/post-cap) of 10 million, up from the 500,000 that is available to the [Standard product track](/resources/fundamentals/projects) at the Basic [access level](https://developer.x.com/en/products/x-api/early-access/guide#na_2). + * An increased [post cap](/x-api/fundamentals/post-cap) of 10 million, up from the 500,000 that is available to the [Standard product track](/resources/fundamentals/developer-apps) at the Basic [access level](https://developer.x.com/en/products/x-api/early-access/guide#na_2). * New filter operators available to [recent search](/x-api/posts/recent-search), [full-archive search](/x-api/posts/full-archive-search), and [filtered stream](/x-api/posts/filtered-stream), including `$` (aka cashtag), `bio` (only available via filtered stream), `bio_name` (only available via filtered stream), `bio_location` (only available via filtered stream), `place`, `place_country`, `point_radius`, `bounding_box`, `-is:nullcast`, `has:cashtags` and `has:geo`. @@ -529,11 +529,11 @@ On August 12th, 2020, we launched the new X API v2: Early access. With this rele If you have any questions, please reach out to our [Labs forum category](https://devcommunity.x.com/c/labs/57). - + ### Addition of Post Consumption Status Bar -In the [main dashboard page](https://developer.x.com/en/portal/dashboard) of the new developer portal, you can now check your usage towards the [post cap](/x-api/fundamentals/post-cap) of 500,000 posts per month. This cap is applied at the Project level, across the following v2 endpoints in Basic access: filtered stream, and recent search. +In the [main dashboard page](https://developer.x.com/en/portal/dashboard) of the new Developer Console, you can now check your usage towards the [post cap](/x-api/fundamentals/post-cap) of 500,000 posts per month. This cap is applied at the Project level, across the following v2 endpoints in Basic access: filtered stream, and recent search. - In order to see this feature and use the new v2 endpoints, you will need to activate the [new developer portal experience](https://developer.x.com/en/portal/opt-in.html). + In order to see this feature and use the new v2 endpoints, you will need to activate the [new Developer Console experience](https://developer.x.com/en/portal/opt-in.html). @@ -911,7 +911,7 @@ Today, we are introducing a new operator, is:reply, to help you narrow conversat -### Access to Apps in Developer Portal +### Access to Apps in Developer Console Today, we've added the ability for developers to view and edit their existing [X apps](/resources/fundamentals/developer-apps) via the [X app dashboard](https://developer.x.com/content/developer-twitter/en/apps) on developer.x.com as long as they're logged into their X account. Previously, you could only view and edit your existing X apps on developer.x.com if you had applied or been approved for a [developer account](/resources/fundamentals/developer-portal). You still must have an approved developer account to be able to create new X apps. @@ -1020,7 +1020,7 @@ The [Account Activity API DM Beta](/x-api/enterprise-gnip-2.0/fundamentals/accou ### Changes to App Creation and Rate Limits -As of today, you will no longer be able to create new [X apps](/resources/fundamentals/developer-apps) via [apps.x.com](https://apps.x.com/). You will now be redirected to either your [developer portal](/resources/fundamentals/developer-portal) account or, if you don't have a developer portal account yet, to the page where [you can apply](https://developer.x.com/content/developer-twitter/en/apply-for-access). +As of today, you will no longer be able to create new [X apps](/resources/fundamentals/developer-apps) via [apps.x.com](https://apps.x.com/). You will now be redirected to either your [Developer Console](/resources/fundamentals/developer-portal) account or, if you don't have a Developer Console account yet, to the page where [you can apply](https://developer.x.com/content/developer-twitter/en/apply-for-access). We also announced that we will be implementing new app-level rate limits to the following POST endpoints on September 10th, 2018. @@ -1040,8 +1040,8 @@ As of today, you will no longer be able to create new [X apps](/resources/fundam -### App Management in Developer Portal -If you have a [developer portal](/resources/fundamentals/developer-portal) account, you can now create and manage your [X apps](/resources/fundamentals/developer-apps). Please read our [forum post](https://devcommunity.x.com/t/app-creation-and-management-now-available-in-the-developer-portal/107723) for more details. +### App Management in Developer Console +If you have a [Developer Console](/resources/fundamentals/developer-portal) account, you can now create and manage your [X apps](/resources/fundamentals/developer-apps). Please read our [forum post](https://devcommunity.x.com/t/app-creation-and-management-now-available-in-the-Developer Console/107723) for more details. diff --git a/developer-terms/agreement.mdx b/developer-terms/agreement.mdx index 4f3b6f7ee..8ffbb7008 100644 --- a/developer-terms/agreement.mdx +++ b/developer-terms/agreement.mdx @@ -93,7 +93,7 @@ In this Agreement, the following definitions apply: **J. Access Tiers.** X provides different tiers of access (as described at [developer.x.com/en](http://developer.x.com/en)) to the Licensed Material, and you shall subscribe to the tier that best fits your use case. X may, at any time, review your use of its Licensed Materials and require a change in the access tier to which you are subscribed, including but not limited to, application for Enterprise access (as described at [developer.x.com/en](http://developer.x.com/en)). -**K. Prohibition on I-Framing:** You shall not, under any circumstances, embed, display, or otherwise incorporate any Licensed Material, X Content, X API, or elements of the X Applications within an iframe, inline frame, or any similar embedding mechanism on your Services or any other platform. This prohibition is absolute and includes, but is not limited to, attempts to frame X Content for display, integration, or redistribution purposes. Violation of this clause may result in immediate termination of your Developer Agreement and your access to the Licensed Material (e.g., X API, X Data License, Developer Portal) as outlined in Section VII.I. +**K. Prohibition on I-Framing:** You shall not, under any circumstances, embed, display, or otherwise incorporate any Licensed Material, X Content, X API, or elements of the X Applications within an iframe, inline frame, or any similar embedding mechanism on your Services or any other platform. This prohibition is absolute and includes, but is not limited to, attempts to frame X Content for display, integration, or redistribution purposes. Violation of this clause may result in immediate termination of your Developer Agreement and your access to the Licensed Material (e.g., X API, X Data License, Developer Console) as outlined in Section VII.I. **IV. Updates and Removals.** diff --git a/developer-terms/ppu-pilot-agreement.mdx b/developer-terms/ppu-pilot-agreement.mdx index 015d825eb..171c43ff5 100644 --- a/developer-terms/ppu-pilot-agreement.mdx +++ b/developer-terms/ppu-pilot-agreement.mdx @@ -95,7 +95,7 @@ In this Agreement, the following definitions apply: **J. Usage Levels Under Agreement.** X may, at any time, review your use of its Licensed Materials under this Agreement, and suspend or terminate your use and require you to file an application for Enterprise access (as described at [**developer.x.com/en**](http://developer.x.com/en)) in order for X to consider your proposed continued use of Licensed Materials. -**K. Prohibition on I-Framing:** You shall not, under any circumstances, embed, display, or otherwise incorporate any Licensed Material, X Content, X API, or elements of the X Applications within an iframe, inline frame, or any similar embedding mechanism on your Services or any other platform. This prohibition is absolute and includes, but is not limited to, attempts to frame X Content for display, integration, or redistribution purposes. Violation of this clause may result in immediate termination of your Developer Agreement and your access to the Licensed Material (e.g., X API, X Data License, Developer Portal) as outlined in Section VII.I. +**K. Prohibition on I-Framing:** You shall not, under any circumstances, embed, display, or otherwise incorporate any Licensed Material, X Content, X API, or elements of the X Applications within an iframe, inline frame, or any similar embedding mechanism on your Services or any other platform. This prohibition is absolute and includes, but is not limited to, attempts to frame X Content for display, integration, or redistribution purposes. Violation of this clause may result in immediate termination of your Developer Agreement and your access to the Licensed Material (e.g., X API, X Data License, Developer Console) as outlined in Section VII.I. **IV. Updates and Removals.** diff --git a/docs.json b/docs.json index bc70c3517..c844c786d 100644 --- a/docs.json +++ b/docs.json @@ -26,7 +26,6 @@ "group": "Fundamentals", "pages": [ "fundamentals/developer-apps", - "fundamentals/projects", "fundamentals/developer-portal", { "group": "Authentication", @@ -113,6 +112,7 @@ "group": "Getting started", "pages": [ "x-api/getting-started/about-x-api", + "x-api/getting-started/pricing", "x-api/getting-started/getting-access", "x-api/getting-started/make-your-first-request", "x-api/getting-started/important-resources" @@ -1938,7 +1938,7 @@ "global": { "anchors": [ { - "anchor": "Developer Portal", + "anchor": "Developer Console", "href": "https://developer.x.com/en/portal/petition/essential/basic-info", "icon": "circle-play" }, @@ -1977,7 +1977,7 @@ ], "primary": { "type": "button", - "label": "Developer portal", + "label": "Developer Console", "href": "https://developer.x.com/en/portal/petition/essential/basic-info" } }, diff --git a/fundamentals/authentication/guides/authentication-best-practices.mdx b/fundamentals/authentication/guides/authentication-best-practices.mdx index 3a3638b0f..3c57d928b 100644 --- a/fundamentals/authentication/guides/authentication-best-practices.mdx +++ b/fundamentals/authentication/guides/authentication-best-practices.mdx @@ -16,7 +16,7 @@ The following sections include best practices that should be considered when man In the event that you believe that your API keys has been exposed, you should regenerate your API keys by following these steps: -1. Navigate to the [developer portal's "Projects and Apps" page](https://developer.x.com/en/portal/projects-and-apps.html). +1. Navigate to the [Developer Console's "Apps" page](https://developer.x.com/en/portal/projects-and-apps.html). 2. Click on the "Keys and tokens" icon (🗝 ) next to the relevant App. 3. Click on the "Regenerate" button next to the set of keys and tokens that you would like to regenerate.  diff --git a/fundamentals/authentication/guides/log-in-with-x.mdx b/fundamentals/authentication/guides/log-in-with-x.mdx index 32b3692f5..cb8624250 100644 --- a/fundamentals/authentication/guides/log-in-with-x.mdx +++ b/fundamentals/authentication/guides/log-in-with-x.mdx @@ -36,7 +36,7 @@ The three steps for implementing Log in with X through obtaining a request token To start a sign-in flow, your [X app](/resources/fundamentals/developer-apps) must obtain a request token by sending a signed message to [POST oauth/request_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token). The only unique parameter in this request is oauth_callback, which must be a URL-encoded version of the URL you wish your user to be redirected to when they complete step 2. The remaining parameters are added by the OAuth signing process. -**Note:** Any [callback URL](/resources/fundamentals/developer-apps#callback-urls) that you use with the [POST oauth/request_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) endpoint will have to be registered within the [X app settings](/resources/fundamentals/developer-apps) in the [developer portal](/resources/fundamentals/developer-portal). +**Note:** Any [callback URL](/resources/fundamentals/developer-apps#callback-urls) that you use with the [POST oauth/request_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) endpoint will have to be registered within the [X app settings](/resources/fundamentals/developer-apps) in the [Developer Console](/resources/fundamentals/developer-portal). diff --git a/fundamentals/authentication/oauth-1-0a/api-key-and-secret.mdx b/fundamentals/authentication/oauth-1-0a/api-key-and-secret.mdx index 44702e950..bca8121fc 100644 --- a/fundamentals/authentication/oauth-1-0a/api-key-and-secret.mdx +++ b/fundamentals/authentication/oauth-1-0a/api-key-and-secret.mdx @@ -17,7 +17,7 @@ These credentials can be used by [authentication endpoints](/resources/fundament To acquire a X API Key and Secret, please follow these steps: 1. [Sign up for a X developer account](https://developer.x.com/en/apply-for-access) -2. Create a [X App](/resources/fundamentals/developer-apps) within the [developer portal](/resources/fundamentals/developer-portal). Note that if you would like to use [X API v2](/x-api/introduction), you must add your X App to a [Project](/resources/fundamentals/projects). +2. Create a [X App](/resources/fundamentals/developer-apps) within the [Developer Console](/resources/fundamentals/developer-portal). Note that if you would like to use [X API v2](/x-api/introduction), you must use keys and tokens from a developer App.   When you create your X App, you will be presented with your API Key and Secret, along with a Bearer Token. Please note that we only display these credentials once, so make sure to save them in your password manager or somewhere secure. @@ -29,8 +29,8 @@ We have more recommendations on how to handle your keys and tokens within our [a If you've already created an App and need to find or regenerate your API Key and Secret, please follow these steps: -1. Navigate to the developer portal -2. Expand the 'Projects and Apps' dropdown in the sidenav +1. Navigate to the Developer Console +2. Expand the 'Apps' dropdown in the sidenav 3. Open the App which is associated with the API Key and Secret that you would like to find or regenerate 4. Navigate to the Keys and tokens tab diff --git a/fundamentals/authentication/oauth-1-0a/authorizing-a-request.mdx b/fundamentals/authentication/oauth-1-0a/authorizing-a-request.mdx index 8ef898b12..82202bf1e 100644 --- a/fundamentals/authentication/oauth-1-0a/authorizing-a-request.mdx +++ b/fundamentals/authentication/oauth-1-0a/authorizing-a-request.mdx @@ -60,7 +60,7 @@ You should be able to see that the header contains 7 key/value pairs, where the **Consumer key** -The oauth\_consumer\_key identifies which application is making the request. Obtain this value from the settings page for your [X app](/resources/fundamentals/developer-apps) in the [developer portal](/resources/fundamentals/developer-portal). +The oauth\_consumer\_key identifies which application is making the request. Obtain this value from the settings page for your [X app](/resources/fundamentals/developer-apps) in the [Developer Console](/resources/fundamentals/developer-portal). | | | | :--- | :--- | @@ -104,7 +104,7 @@ The oauth_timestamp parameter indicates when the request was created. This val The oauth_token parameter typically represents a user’s permission to share access to their account with your application. There are a few authentication requests where this value is not passed or is a different form of token, but those are covered in detail in [Obtaining access tokens](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens). For most general-purpose requests, you will use what is referred to as an **access token**. -You can generate a valid [access token](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) for your account on the settings page for your [X app](/resources/fundamentals/developer-apps) on the [developer portal](/resources/fundamentals/developer-portal). +You can generate a valid [access token](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) for your account on the settings page for your [X app](/resources/fundamentals/developer-apps) on the [Developer Console](/resources/fundamentals/developer-portal). | | | | :--- | :--- | diff --git a/fundamentals/authentication/oauth-1-0a/creating-a-signature.mdx b/fundamentals/authentication/oauth-1-0a/creating-a-signature.mdx index c077b5749..193ce0902 100644 --- a/fundamentals/authentication/oauth-1-0a/creating-a-signature.mdx +++ b/fundamentals/authentication/oauth-1-0a/creating-a-signature.mdx @@ -120,7 +120,7 @@ Make sure to percent encode the parameter string. The signature base string shou The last pieces of data to collect are secrets which identify the [X app](/resources/fundamentals/developer-apps) making the request, and the user the request is on behalf of. It is very important to note that these values are incredibly sensitive and should never be shared with anyone. -The value which identifies your app to X is called the **consumer secret** and can be found in the [developer portal](/resources/fundamentals/developer-portal) by viewing the [app details page](/resources/fundamentals/developer-apps). This will be the same for every request your X app sends. +The value which identifies your app to X is called the **consumer secret** and can be found in the [Developer Console](/resources/fundamentals/developer-portal) by viewing the [app details page](/resources/fundamentals/developer-apps). This will be the same for every request your X app sends. | | | | :--- | :--- | diff --git a/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens.mdx b/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens.mdx index 1055e7354..0e8a533f3 100644 --- a/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens.mdx +++ b/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens.mdx @@ -61,7 +61,7 @@ Create a request for a consumer application to obtain a request token. The only unique parameter in this request is oauth_callback, which must be a [URL encoded](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters) version of the URL you wish your user to be redirected to when they complete step 2. The remaining parameters are added by the OAuth signing process. -Please note - any callback URL that you use with the [POST oauth/request_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) endpoint will have to be configured within your [developer App's](/resources/fundamentals/developer-apps) settings in the app details page of developer portal. +Please note - any callback URL that you use with the [POST oauth/request_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) endpoint will have to be configured within your [developer App's](/resources/fundamentals/developer-apps) settings in the app details page of Developer Console.   **Request includes:** diff --git a/fundamentals/authentication/oauth-2-0/authorization-code.mdx b/fundamentals/authentication/oauth-2-0/authorization-code.mdx index 7d15d7bf1..284c0377a 100644 --- a/fundamentals/authentication/oauth-2-0/authorization-code.mdx +++ b/fundamentals/authentication/oauth-2-0/authorization-code.mdx @@ -9,7 +9,7 @@ keywords: ["OAuth 2.0 PKCE", "authorization code flow", "PKCE", "OAuth 2.0 flow" OAuth 2.0 is an industry-standard authorization protocol that allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the Developer Console. #### How long will my credentials stay valid?   @@ -41,7 +41,7 @@ OAuth 2.0 can be used with the X API v2 only. If you have selected OAuth 2.0 you [Confidential clients](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) can hold credentials in a secure way without exposing them to unauthorized parties and securely authenticate with the authorization server they keep your client secret safe. Public clients as they’re usually running in a browser or on a mobile device and are unable to use your client secrets. If you select a type of App that is a confidential client, you will be provided with a client secret.  -If you selected a type of client that is a confidential client in the developer portal, you will also be able to see a Client Secret. Your options are Native App, Single page App, Web App, Automated App, or bot. Native App and Single page Apps are public clients and Web App and Automated App or bots are confidential clients. +If you selected a type of client that is a confidential client in the Developer Console, you will also be able to see a Client Secret. Your options are Native App, Single page App, Web App, Automated App, or bot. Native App and Single page Apps are public clients and Web App and Automated App or bots are confidential clients. You don’t need client id for confidential clients with a valid Authorization Header. You still are required to include Client Id in the body for the requests with a public client.  @@ -95,7 +95,7 @@ OAuth 2.0 uses a similar flow to what we are currently using for OAuth 1.0a. You | Public client | Clients cannot use registered client secrets, such as applications running in a browser or mobile device. | | Authorization code flow | Used by both confidential and public clients to exchange an authorization code for an access token. | | PKCE | An extension to the authorization code flow to prevent several attacks and to be able to perform the OAuth exchange from public clients securely. | -| Client ID | Can be found in the keys and tokens section of the developer portal under the header "Client ID." If you don't see this, please get in touch with our team directly. The Client ID will be needed to generate the authorize URL. | +| Client ID | Can be found in the keys and tokens section of the Developer Console under the header "Client ID." If you don't see this, please get in touch with our team directly. The Client ID will be needed to generate the authorize URL. | | Redirect URI | Your callback URL. You will need to have [exact match validation](https://datatracker.ietf.org/doc/html/rfc6749#section-10.6). | | Authorization code | This allows an application to hit APIs on behalf of users. Known as the auth\_code. The auth\_code has a time limit of 30 seconds once the App owner receives an approved auth\_code from the user. You will have to exchange it with an access token within 30 seconds, or the auth\_code will expire. | | Access token | Access tokens are the token that applications use to make API requests on behalf of a user. | @@ -110,7 +110,7 @@ To construct an OAuth 2.0 authorize URL, you will need to ensure you have the fo | :--- | :--- | | **Parameter** | **Description** | | response_type | You will need to specify that this is a code with the word “code”. | -| client_id | Can be found in the developer portal under the header "Client ID". | +| client_id | Can be found in the Developer Console under the header "Client ID". | | redirect_uri | Your callback URL. This value must correspond to one of the Callback URLs defined in your App’s settings. For OAuth 2.0, you will need to have [exact match validation](https://datatracker.ietf.org/doc/html/rfc6749#section-10.6) for your callback URL. | | state | A random string you provide to verify against [CSRF attacks](https://auth0.com/docs/protocols/state-parameters).  The length of this string can be up to 500 characters. | | code_challenge | A [PKCE](https://www.oauth.com/oauth2-servers/pkce/authorization-request/) parameter, a random secret for each request you make. | diff --git a/fundamentals/authentication/oauth-2-0/bearer-tokens.mdx b/fundamentals/authentication/oauth-2-0/bearer-tokens.mdx index 988982da4..ddd0f6ec3 100644 --- a/fundamentals/authentication/oauth-2-0/bearer-tokens.mdx +++ b/fundamentals/authentication/oauth-2-0/bearer-tokens.mdx @@ -8,7 +8,7 @@ keywords: ["Bearer token", "app-only token", "Bearer Token", "OAuth 2.0 Bearer", A bearer token allows developers to have a more secure point of entry for using the X APIs, and are one of the core features of OAuth 2.0.  -Authentication, which uses a Bearer Token, is also known as application-only authentication. A Bearer Token is a byte array of unspecified format that you generate using a script like a curl command. You can also obtain a Bearer Token from the developer portal inside the keys and tokens section of your App's settings. More information about this feature can be found on [OAuth's official documentation](https://oauth.net/2/bearer-tokens/). +Authentication, which uses a Bearer Token, is also known as application-only authentication. A Bearer Token is a byte array of unspecified format that you generate using a script like a curl command. You can also obtain a Bearer Token from the Developer Console inside the keys and tokens section of your App's settings. More information about this feature can be found on [OAuth's official documentation](https://oauth.net/2/bearer-tokens/). #### When are they used? @@ -22,7 +22,7 @@ The products that require the use of a Bearer Token are as follows: #### Prerequisites -You will need to [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info) and to have created a [X App](/resources/fundamentals/developer-apps). Once you have those, you'll also need to obtain the API keys found in the [developer portal](/resources/fundamentals/developer-portal). Follow the steps below: +You will need to [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info) and to have created a [X App](/resources/fundamentals/developer-apps). Once you have those, you'll also need to obtain the API keys found in the [Developer Console](/resources/fundamentals/developer-portal). Follow the steps below: 1. Login to your X account on developer.x.com. 2. Navigate to the [X App dashboard](https://developer.x.com/content/developer-twitter/en/apps) and open the X App for which you would like to generate access tokens. diff --git a/fundamentals/authentication/oauth-2-0/overview.mdx b/fundamentals/authentication/oauth-2-0/overview.mdx index 00ded8153..21b6afc81 100644 --- a/fundamentals/authentication/oauth-2-0/overview.mdx +++ b/fundamentals/authentication/oauth-2-0/overview.mdx @@ -16,7 +16,7 @@ This authentication method requires for you to pass a Bearer Token with your req ``` API calls using app-only authentication are [rate limited](/resources/fundamentals/rate-limits) per endpoint at the App level. -To use this method, you'll need a Bearer Token, which you can generate by passing your API Key and Secret through the [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token) endpoint, or by generating it in the "keys and token" section of your App settings in the [developer portal](/resources/fundamentals/developer-portal). +To use this method, you'll need a Bearer Token, which you can generate by passing your API Key and Secret through the [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token) endpoint, or by generating it in the "keys and token" section of your App settings in the [Developer Console](/resources/fundamentals/developer-portal). If you'd like to revoke a Bearer Token, you can use the [POST oauth2/invalidate_token](/resources/fundamentals/authentication/api-reference#post-oauth2-invalidate-token) endpoint, or click where it says "revoke" next to the Bearer Token in the "keys and tokens" section of your App settings. diff --git a/fundamentals/authentication/oauth-2-0/user-access-token.mdx b/fundamentals/authentication/oauth-2-0/user-access-token.mdx index 0d6c03ba3..52d361156 100644 --- a/fundamentals/authentication/oauth-2-0/user-access-token.mdx +++ b/fundamentals/authentication/oauth-2-0/user-access-token.mdx @@ -27,7 +27,7 @@ If the user agent wishes to send the Client ID "Aladdin" and password "open sesa `Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==` -To create the basic authorization header you will need to base64 encoding on your Client ID and Client Secret which can be obtained from your App’s “Keys and Tokens” page inside of the [developer portal.](https://developer.x.com/en/portal/dashboard) +To create the basic authorization header you will need to base64 encoding on your Client ID and Client Secret which can be obtained from your App’s “Keys and Tokens” page inside of the [Developer Console.](https://developer.x.com/en/portal/dashboard) #### Steps to connect using OAuth 2.0 diff --git a/fundamentals/authentication/overview.mdx b/fundamentals/authentication/overview.mdx index c152c437e..2c994ce39 100644 --- a/fundamentals/authentication/overview.mdx +++ b/fundamentals/authentication/overview.mdx @@ -39,7 +39,7 @@ You can find a list of available client libraries on our [Tools and libraries](/ **Note:** -Your App's API Keys and App only Access Token, as well as your personal Access Token and Access Token Secret can be obtained from the [X developer Apps](/resources/fundamentals/developer-apps) section found in the [developer portal](/resources/fundamentals/developer-portal). +Your App's API Keys and App only Access Token, as well as your personal Access Token and Access Token Secret can be obtained from the [X developer Apps](/resources/fundamentals/developer-apps) section found in the [Developer Console](/resources/fundamentals/developer-portal). **If you would like to make requests on behalf of another user**, you will need to generate a separate set of Access Tokens for that user using the [3-legged OAuth flow](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens), and pass that user's tokens with your OAuth 1.0a User Context or OAuth 2.0 user context requests. diff --git a/fundamentals/counting-characters.mdx b/fundamentals/counting-characters.mdx index 0bb17e7b6..8df288d65 100644 --- a/fundamentals/counting-characters.mdx +++ b/fundamentals/counting-characters.mdx @@ -1,82 +1,124 @@ --- -title: Counting characters when composing Tweets -sidebarTitle: Counting characters +title: Counting Characters +sidebarTitle: Counting Characters icon: text-size -keywords: ["character counting", "tweet length", "280 characters", "character limits", "Unicode", "emoji characters", "CJK characters", "URL length", "tweet composition", "twitter-text", "character encoding", "UTF-8"] +description: How X counts characters in posts +keywords: ["character counting", "tweet length", "280 characters", "character limits", "Unicode", "emoji characters", "URL length", "tweet composition"] --- -This page describes how characters are treated when composing Tweets and across the X API. For more information on the implementation, X provides an Open Source [twitter-text](http://github.com/twitter/twitter-text) library that can be found on [GitHub](https://github.com/twitter). +Posts on X can contain up to **280 characters**. However, not all characters count equally—emojis, URLs, and certain Unicode ranges have special counting rules. -## Background +--- + +## Character weights + +X uses a weighted character counting system. Most characters count as 1, but some count as 2: + +| Character type | Weight | Max characters | +|:---------------|:-------|:---------------| +| Latin, punctuation, common symbols | 1 | 280 | +| Emojis | 2 | 140 emojis | +| CJK (Chinese, Japanese, Korean) | 2 | 140 characters | +| Other Unicode | 2 (default) | Varies | + + +Use the open-source [twitter-text](https://github.com/twitter/twitter-text) library to accurately count characters in your app. + + +--- -X began as an SMS text-based service. This limited the original Tweet length to 140 characters (which was partly driven by the 160 character limit of SMS, with 20 characters reserved for commands and usernames). Over time as X evolved, the maximum Tweet length grew to 280 characters - still short and brief, but enabling more expression. +## Emoji counting -## Definition of a Character +All emojis count as **2 characters**, regardless of complexity: -In most cases, the text content of a Tweet can contain up to 280 characters or [Unicode](https://unicode.org/) glyphs. Some glyphs will count as more than one character. +| Emoji | Display | Character count | Unicode | +|:------|:--------|:----------------|:--------| +| 👾 | Single emoji | 2 | U+1F47E | +| 🙋🏽 | With skin tone | 2 | 🙋 + 🏽 modifier | +| 👨‍🎤 | Combined with ZWJ | 2 | 👨 + ZWJ + 🎤 | +| 👨‍👩‍👧‍👦 | Family sequence | 2 | 4 emojis + 3 ZWJs | -We refer to whether a glyph counts as one or more characters as its weight. The exact definition of which characters have weights greater than one character is found in the [configuration file](https://github.com/twitter/twitter-text/tree/master/config) for the [twitter-text Tweet parsing library](https://github.com/twitter/twitter-text). +Zero-width joiners (ZWJ) combine emojis visually but don't add to the count. -The current version of the configuration file defines a default two-character weight and four ranges of [Unicode code points](https://unicode.org/charts/About.html) that are weighted differently. Currently code points in these ranges are all counted as a single character. +--- -- The first range covers characters across the Latin-1 code pages. (U+0000 - U+10FF). -- The second range is general punctuation up to and including the Zero Width Joiner (used to combine emoji and other glyphs) (U+2000-U+200D). -- The third range is general punctuation excluding U+200E and U+200F, which are Unicode directional marks (U+2010-U+201F). -- The final range covers quotation marks (U+2032-U+2037). +## URL handling -Examples of Tweet text and lengths calculated by the twitter-text library can be found in the library's [validate.yml](https://github.com/twitter/twitter-text/blob/master/conformance/validate.yml) test configuration file. +All URLs are wrapped with `t.co` shortener and count as **23 characters**, regardless of the original length: -**Examples** +``` +https://example.com → 23 characters +https://example.com/very/long/path → 23 characters +``` -| Displayed character | Length | Description | Unicode sequence | -|:---------------------|:--------|:-------------|:------------------| -| a | 1 | Latin Small Letter a | U+0061 | -| á | 1 | Latin Small Letter A with acute | U+00E1 | -| ӑ | 1 | Cyrillic Small Letter A with breve | U+04D1 | -| Ồ | 1 | Latin Small Letter o with circumflex and acute | U+1ED2 | + +This applies to any valid URL detected in post text. + -## Emojis +--- -Emoji supported by [twemoji](https://twemoji.x.com/) always count as two characters, regardless of combining modifiers. This includes emoji which have been modified by [Fitzpatrick skin tone](https://emojipedia.org/modifiers/) or [gender modifiers](https://blog.emojipedia.org/unicode-and-the-emoji-gender-gap/), even if they are composed of significantly more Unicode code points. Emoji weight is defined by a regular expression in twitter-text that looks for sequences of standard emoji combined with one or more Unicode Zero Width Joiners (U+200D). +## Special cases -**Examples** +| Content | Counting rule | +|:--------|:--------------| +| **@mentions in replies** | Auto-populated @mentions at the start of replies don't count | +| **New @mentions** | @mentions you add manually count normally | +| **Media** | Attached media (via official clients) counts as 0 characters | +| **Hashtags** | Count normally (# + tag text) | -| Displayed Emoji | Length | Description | Unicode sequence | -|:-----------------|:--------|:-------------|:------------------| -| 👾 | 2 | Default length of known emoji | -- | -| 🙋🏽 | 2 | Emoji with skin tone modifier | 🙋 U+1F64B, 🏽 U+1F3FD | -| 👨‍🎤 | 2 | Emoji sequence using combining glyph (zero-width joiner) | [👨 U+1F468](https://emojipedia.org/emoji/%F0%9F%91%A8/), [U+200D](https://emojipedia.org/emoji/%E2%80%8D/), [🎤 U+1F3A4](https://emojipedia.org/emoji/%F0%9F%8E%A4/) | -| 👨‍👩‍👧‍👦 | 2 | Emoji sequence using multiple combining glyphs (zero-width joiners) | [👨 U+1F468](https://emojipedia.org/emoji/%F0%9F%91%A8/), [U+200D](https://emojipedia.org/emoji/%E2%80%8D/), [👩 U+1F469](https://emojipedia.org/emoji/%F0%9F%91%A9/), [U+200D](https://emojipedia.org/emoji/%E2%80%8D/), [👧 U+1F467](https://emojipedia.org/emoji/%F0%9F%91%A7/), [U+200D](https://emojipedia.org/emoji/%E2%80%8D/), [👦 U+1F466](https://emojipedia.org/emoji/%F0%9F%91%A6/) | +--- -## Chinese / Japanese / Korean Glyphs +## Text encoding -Glyphs used in CJK (Chinese / Japanese / Korean) languages also count as two characters. Therefore, a Tweet composed of only CJK text can only have a maximum of 140 of these types of glyphs. +The X API requires **UTF-8** encoding. Character length is calculated using Unicode Normalization Form C (NFC). -## Entity Objects +Example with `café`: -Tweets can contain [Entity Objects](/x-api/fundamentals/data-dictionary), some of which impact the length of a Tweet. +| Form | Bytes | Characters | +|:-----|:------|:-----------| +| NFC (composed) | `c a f é` | 4 | +| NFD (decomposed) | `c a f e ́` | 5 | -URLs: [All URLs are wrapped in t.co links](https://developer.x.com/content/developer-twitter/en/docs/basics/tco). This means a URL's length is defined by the `transformedURLLength` parameter in the [twitter-text configuration file](https://github.com/twitter/twitter-text/tree/master/config). The current length of a URL in a Tweet is 23 characters, even if the length of the URL would normally be shorter. +X normalizes to NFC, so both encode to 4 characters. -Replies: @names that auto-populate at the start of a reply Tweet will not count towards the character limit. New non-reply Tweets starting with a @mention will count, as will @mentions added explicitly by the user in the body of the Tweet. +--- -Media: media attached to a Tweet, represented as a pic.x.com URL, if posted from an official client, counts for 0 characters. +## Implementation -For more on Entity Objects, see the [developer documentation](/x-api/fundamentals/data-dictionary). +Use the official [twitter-text](https://github.com/twitter/twitter-text) library for accurate character counting: -## X Character Encoding + + +```javascript +import { parseTweet } from 'twitter-text'; -X API endpoints only accept UTF-8 encoded text. All other encodings must be converted to UTF-8 before sending the the text to the API. +const result = parseTweet('Hello, world! 👋'); +console.log(result.weightedLength); // 16 +console.log(result.valid); // true +``` + + +```python +from twitter_text import parse_tweet -X counts the length of a Tweet using the Normalization Form C (NFC) version of the text. +result = parse_tweet('Hello, world! 👋') +print(result.weightedLength) # 16 +print(result.valid) # True +``` + + -As an example: the word "café". There are two byte sequences that visually look and read the same, but use a different number of bytes: +The library handles all edge cases including emoji sequences, URL detection, and Unicode normalization. -| | | | -|:-----|:---|:---| -| café | 0x63 0x61 0x66 0xC3 0xA9 | Using the "é" character, the "composed character". | -| café | 0x63 0x61 0x66 0x65 0xCC 0x81 | Using the combining diacritical, which overlaps the "e" | +--- -Normalization Form C favors the use of a fully combined character (0xC3 0xA9 from the café example) over the long-form version (0x65 0xCC 0x81). +## Resources -X counts the number of code points in the text, rather than UTF-8 bytes. The 0xC3 0xA9 from the café example is one code point (U+00E9) that is encoded as two bytes in UTF-8, whereas 0x65 0xCC 0x81 is two code points encoded as three bytes. \ No newline at end of file + + + Official open-source library for text parsing. + + + Character weight definitions and Unicode ranges. + + diff --git a/fundamentals/developer-apps.mdx b/fundamentals/developer-apps.mdx index d8a248b86..ba2f86851 100644 --- a/fundamentals/developer-apps.mdx +++ b/fundamentals/developer-apps.mdx @@ -1,267 +1,163 @@ --- title: Apps -icon: wrench -keywords: ["X apps", "developer apps", "API keys", "OAuth", "authentication", "app management", "developer portal", "API credentials", "access tokens", "OAuth 1.0a", "OAuth 2.0", "API key and secret", "client ID", "client secret", "app permissions", "callback URLs"] +sidebarTitle: Apps +icon: cube +description: Create and configure apps to access the X API +keywords: ["X apps", "developer apps", "API keys", "OAuth", "authentication", "app management", "API credentials", "access tokens", "OAuth 1.0a", "OAuth 2.0", "app permissions", "callback URLs"] --- -## Overview +Apps are containers for your API credentials. Each app has its own keys, tokens, and settings. -If you have existing Apps, you can view, edit, or delete them via the [developer portal's App page](https://developer.x.com/en/portal/projects-and-apps). - -Accessing the X API and X Ads API requires a set of [authentication](/resources/fundamentals/authentication) credentials, also known as keys and tokens, that you must pass with each request. These credentials can come in different forms depending on the type of authentication that is required by the specific endpoint that you are using. - -Here are the different credentials that you can generate in your App and how to use them: - -- **[API Key and Secret](/resources/fundamentals/authentication#api-key-and-secret):** Essentially the username and password for your App. You will use these to authenticate requests that require [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2), or to generate other tokens such as user Access Tokens or App Access Token. - -- **[Access Token and Secret](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow):** In general, Access Tokens represent the user that you are making the request on behalf of. The ones that you can generate via the developer portal represent the user that owns the App. You will use these to authenticate requests that require [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2). If you would like to make requests on behalf of another user, you will need to use the 3-legged OAuth flow for them to authorize you. - -- **[Client ID and Client Secret](/resources/fundamentals/authentication#oauth-2-0):** These credentials are used to obtain a user Access Token with OAuth 2.0 authentication. Similar to OAuth 1.0a, the user Access Tokens are used to authenticate requests that provide private user account information or perform actions on behalf of another account but, with fine-grained scope for greater control over what access the client application has on the user. - -- **[App only Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only):** You will use this token when making requests to endpoints that responds with information publicly available on X. - -In addition to generating the keys and tokens necessary to make X API requests, you will also be able to set access [permissions](/resources/fundamentals/developer-apps#app-permissions), document the use case or purpose for the App, define a [callback URL](/resources/fundamentals/developer-apps#callback-urls), and modify other settings related to your App developer environment from within the [management dashboard](https://developer.x.com/en/portal/projects-and-apps). - -### Apps and Projects - -You can use Apps and [Projects](/resources/fundamentals/projects) to help organize your work with the X Developer Platform by use case. Each Project can include one App with the Free X API plan, up to two Apps with the Basic plan, and up to three Apps with the Pro plan. - -If you would like to access the new [X API v2](/x-api/introduction) endpoints, you will be required to use keys and tokens from an App that is associated with a Project. - -If you have Apps that were created before we launched Projects, they will be visible in the section entitled "Standalone Apps". Standalone Apps are Apps outside of the Project structure. If you attach a Standalone App to a Project, it will then be able to make requests to the v2 endpoints. - -### Developer portal dashboard - -You can [visit the dashboard](https://developer.x.com/content/developer-twitter/en/apps) to manage the Apps associated with your account. To learn more, please visit our documentation page on the [developer portal](/resources/fundamentals/developer-portal). The dashboard allows developers to quickly and easily perform the following tasks: - -- View your existing Standalone Apps and their associated App ID. -- Create a new Project, App, or standalone App. -- Delete an unused Project or App. -- Review or update a specific App's settings, including updating name, description, website, callback URL, and [permissions](/resources/fundamentals/developer-apps#app-permissions). -- Regenerate App specific credentials like API Key & Secret, App Access Token, and the App owner's user Access Tokens. - -### Signing up for access - -If you have existing X Apps, you can view and edit your Apps via the X [App dashboard](https://developer.x.com/content/developer-twitter/en/portal/projects-and-apps) if you are logged into your X account on developer.x.com. Please note you will **not** need to sign up for an account to manage any and all Apps that were previously created on developer.x.com. - -If you need to create a new X App, you will need to have [signed up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). If you are a member of a [team account](/resources/fundamentals/developer-portal#team-management), you must be assigned an admin role to create a new App. +--- -### Automated Account labeling for bots +## App credentials -You can add an Automated Account label to your bot accounts to let users on X know that your bot is an automated account. These bots perform programmed actions through the X API. When you add an Automated Account label to your bot, you build trust with your audience, legitimize your account, and set yourself apart from spammy bots. This helps people on X better understand your account's purpose when interacting with your bot. +When you create an app, you can generate these credentials: -To attach a label to your bot account, follow these steps: +| Credential | Use case | +|:-----------|:---------| +| **API Key & Secret** | Authenticate with OAuth 1.0a. Used to sign requests or generate user tokens. | +| **Access Token & Secret** | Make requests on behalf of your own account (OAuth 1.0a). | +| **Client ID & Secret** | Authenticate with OAuth 2.0. Used for authorization code flow. | +| **Bearer Token** | App-only authentication for public data endpoints. | -1. Go to your account settings -2. Select "Your account" -3. Select "Automation" -4. Select "Managing account" -5. Next, select the X account, which runs your bot account -6. Enter your password to log in -7. Finally, you should see confirmation that the label has been applied to your account. + +Choose **OAuth 2.0** for new projects. It offers fine-grained scopes and is required for X API v2 user-context endpoints. + --- -## App management - -### Introduction +## Creating an app + + + + Go to [console.x.com](https://console.x.com) and sign in. + + + Enter a name, description, and use case for your app. + + + After creation, generate the keys and tokens you need. + + + Save credentials immediately—they're only shown once. + + -The [X App dashboard](https://developer.x.com/en/portal/projects-and-apps) allows developers to quickly and easily perform the following tasks: +--- -- View your existing Apps and [Projects](/resources/fundamentals/projects) and their associated App ID. -- Create a new Project or standalone App. -- Delete a Project, App, or standalone App. -- Open up a specific App's settings by clicking into the App's settings. Within the settings, you can see the App details, keys and tokens, and permissions. -- Update your App's user authentication settings to use either OAuth 1.0a or OAuth 2.0. +## App permissions (OAuth 1.0a) + +OAuth 1.0a apps have three permission levels: + + + + - View posts, users, and public data + - Cannot post, like, or modify anything + - Cannot access Direct Messages + + + - All read permissions + - Post and delete posts + - Follow/unfollow users + - Like and repost + - Cannot access Direct Messages + + + - All read and write permissions + - Send and read Direct Messages + + -**Note:** - -All App keys and tokens are no longer viewable within the Developer Portal and must be saved securely once generated. We recommend using a password manager to store your keys and tokens. - -You can reveal an API Key hint to help you match your credential to their corresponding App. +Changing permissions requires users to re-authorize your app to get new tokens with the updated scope. -### App Settings - -#### App details - -Here you can edit the App icon, App name, App description, your website URL, [callback URLs/redirect URIs](/resources/fundamentals/developer-apps#callback-urls), terms of service URL, privacy policy URL, organization name, organization URL, and purpose or use case of the App. - -OAuth 2.0 and OAuth 1.0a are authentication methods that allow users to sign in to your App with X. They also allow your App to make specific requests on behalf of authenticated users. You can turn on one, or both methods for your App. - -It is important to keep this information up to date. Your App name and website URL will be shown as the source within metadata for any Tweets created programmatically by your application. If you change the use case of a X App, be sure to update the use case in these settings in order to ensure you are in compliance with the [Developer Terms](https://developer.x.com/content/developer-twitter/en/developer-terms/agreement-and-policy). - -If your application has a tag showing 'suspended' this is because your app is in violation of one or more of X's [developer terms](https://developer.x.com/en/developer-terms.html) such as our [automation rules](https://help.x.com/en/rules-and-policies/twitter-automation). The developer platform policy team will communicate with developers through the email address set up on the App owner's X account, to review this email address please review your [X account settings](https://x.com/settings/your_twitter_data/account). Notification emails about suspensions will be sent to this email address with the title similar to "Application suspension notice" and will have specific information on what to do next. To work with the X team to address suspensions, please check your email for specific instructions, or use our [platform help form](https://help.x.com/forms/platform). - -#### Keys and tokens - -Inside of an App in a Project or via a standalone App you can view, regenerate, or revoke the following tokens: - -- [API Key (Consumer Key) and API Secret (Consumer Secret)](/resources/fundamentals/authentication#api-key-and-secret) -- [App Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) -- [User Access Token and Access Token Secret](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) - -The Access Token and Secret available within the developer portal relates to the user that owns the App. - -**Please note** - If you would like to make requests on behalf of a different user (in other words, not the user that owns the App), you will have to use either the [OAuth 1.0a 3-legged OAuth flow](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens) or OAuth 2.0 [Authorization Code with PKCE flow](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) to generate a set of user Access Tokens. You will then use these user-specific tokens in your request to the API. - -#### User Authentication Settings - -You can select your App's authentication settings to be OAuth 1.0a or OAuth 2.0. OAuth 2.0 can be used with the X API v2 only. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. OAuth 1.0a can be used with X API v1.1 and v2 and uses broad authorization with coarse scopes. - -#### OAuth 1.0a Application-user Permissions - -If you are the App owner, you can adjust the permissions of the App (read-only; read and write; or read, write and direct messages). This controls which resources and events you have access to via X APIs (note: some resources require further permission from X directly). - -You can also toggle on and off your Apps' ability to ask for user email addresses on this page (this requires a Terms of Service URL and a Privacy Policy URL to be present on the "App details" page). - -#### OAuth 2.0 Type of App - -If you select OAuth 2.0 as your user authentication method you will need to select the type of App you are creating. Your options are Native App, Single page App, Web App, Automated App or bot. Native App and Single page Apps are public clients and Web App and Automated App or bots are confidential clients. - -Confidential clients securely authenticate with the authorization server. They keep your client secret safe. Public clients are applications usually running in a browser or on a mobile device and are unable to use your client secrets. If you select a type of App that is a confidential client, you will be provided with a client secret. - --- -## App permissions - -### OAuth 1.0a App permissions - -App permissions describe the access level for OAuth 1.0a application-user authentication. App permissions are configured per application within your [X App](/resources/fundamentals/developer-apps) settings. - -There are three levels of permission available: - -1. Read only -2. Read and write -3. Read, write and access Direct Messages - -An additional permission exists to request visibility of a user's email address - this can be combined with any of the three levels listed above. - -If a permission level is changed, any user tokens already issued to that X app must be discarded and users must re-authorize the App in order for the token to inherit the updated permissions. +## OAuth 2.0 app types -A good practice is to request *only the minimum level of access* to a user's account data that an application or service requires. +When configuring OAuth 2.0, select your app type: -### Read only +| Type | Client | Use case | +|:-----|:-------|:---------| +| **Web App** | Confidential | Server-side applications that can securely store secrets | +| **Automated App / Bot** | Confidential | Bots and automated services running on servers | +| **Native App** | Public | Mobile or desktop apps that can't secure secrets | +| **Single Page App** | Public | Browser-based JavaScript apps | -This permission level permits read access to X resources, including (for example) a user's Tweets, home timeline, and profile information. It does not allow access to read a user's Direct Messages, and it does not allow to update any element or object. - -### Read and write - -This permission level permits read and write access to X resources. In addition to allowing read access, it also allow to post Tweets, follow users, or update elements of a user's profile information. It also allow to hide replies on behalf of the authenticating user. This permission level does not allow any access to Direct Messages (including read, write, or delete). - -### Read, write and access Direct Messages - -This permission level includes access to all of the above and adds the ability to read, write and delete Direct Messages on behalf of a user. - -- [POST /2/dm_conversations/:dm_conversation_id/messages](/x-api/direct-messages/send-a-new-message-to-a-dm-conversation) -- [POST /2/dm_conversations/](/x-api/direct-messages/create-a-new-dm-conversation) -- [POST /2/dm_conversations/with/:participant_id/messages](/x-api/direct-messages/send-a-new-message-to-a-user) -- [GET /2/dm_conversations/with/:user_id/dm_events](/x-api/direct-messages/get-dm-events-for-a-dm-conversation) -- [GET /2/dm_conversations/:dm_conversation_id/dm_events](/x-api/direct-messages/get-dm-events-for-a-dm-conversation-1) -- [GET /2/dm_events](/x-api/direct-messages/get-recent-dm-events) - -### Determining permissions - -All authenticated API requests return an `x-access-level` header in the HTTP response. The value of the header shows the current permission level in use. Possible values are read, read-write, and read-write-directmessages. +**Confidential clients** receive a Client Secret. **Public clients** use PKCE only. --- ## Callback URLs -The OAuth 1.0a User Context and OAuth 2.0 Authorization Code with PKCE authentication methods enable developers to make requests on behalf of different X users that have worked through a specific sign-in flow. Currently, there are two flows that you can use to enable users to authorize your application: - -- [OAuth 2.0 authorization code flow with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -- [OAuth 1.0a 3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (and separately, the [Sign in with X flow](/resources/fundamentals/authentication#log-in-with-x)) - -As users work through these flows, they need a web page or location to be sent to after they have successfully logged in and provided authorization to the developer's App. This follow-up webpage or location is called a callback URL. +Callback URLs (redirect URIs) are required for OAuth flows. After a user authorizes your app, they're redirected to your callback URL with an authorization code. -When setting up these flows for their potential users to work through, developers must pass a callback URL with their requests to the authentication endpoints that make up the flows mentioned earlier. For example, developers using OAuth 1.0a User Context must pass the `callback_url` parameter when making a request to the [GET oauth/request_token](/resources/fundamentals/authentication#post-oauth-request-token) endpoint. Similarly, developers using OAuth 2.0 Authorization Code with PKCE must pass the `redirect_uri` parameter with their request to the GET oauth2/authorize endpoint. +### Requirements -In addition to using these parameters, the developer must also make sure that the callback URL has also been added to their [App's](/resources/fundamentals/developer-apps) callback URL allowlist, which can be found on the [developer portal's](/resources/fundamentals/developer-portal) App settings page. +- Add callback URLs to your app's allowlist in the Developer Console +- URLs must match exactly (including trailing slashes) +- Maximum of **10 callback URLs** per app +- Use `https://` in production +- For local development, use `http://127.0.0.1` (not `localhost`) -If set up properly, developers will be directed to the callback URL after successfully signing in to X as part of these flows. +### Disallowed protocols -### Things to keep in mind +These protocols cannot be used: `javascript`, `data`, `file`, `ftp`, `mailto`, `telnet`, and other non-standard schemes. -When you are setting up your callback URLs, there are a few things that you should keep in mind: + +`vbscript`, `javascript`, `vbs`, `data`, `mocha`, `keyword`, `livescript`, `ftp`, `file`, `gopher`, `acrobat`, `callto`, `daap`, `itpc`, `itms`, `firefoxurl`, `hcp`, `ldap`, `mailto`, `mmst`, `mmsu`, `msbd`, `rtsp`, `mso-offdap`, `snews`, `news`, `nntp`, `outlook`, `stssync`, `rlogin`, `telnet`, `tn3270`, `shell`, `sip` + -**HTTP encode your query parameters** - -Since you are passing the callback URL as a query parameter with the `callback_url` or `redirect_uri` parameters, please make sure that you HTTP encode the URL. - -**Callback URL limits** - -There is a hard limit of 10 callback URLs in the X Apps dashboard. Your callback URL should always be an exact match between your allow listed callback URL that you add to the Apps dashboard and the parameter you add in the authorization flow. - -If you wish to include request-specific data in the callback URL, you can use the `state` parameter to store data that will be included after the user is redirected. It can either encode the data in the `state` parameter itself or use the parameter as a session ID to store the state on the server. - -**Don't use localhost as a callback URL** -Instead of using localhost, please use a custom host locally or http(s)://127.0.0.1 - -**Custom protocol URLs** -If you would like to take advantage of mobile deep linking, you can utilize custom protocol URLs with a path and domain part, such as twitter://callback/path. However, we do have a list of disallowed protocols that you will need to avoid. You can review the list of disallowed protocols below. - -**Disallowed protocols** - -| | | -|:----------|:----------| -| `vbscript` | `ldap` | -| `javascript` | `mailto` | -| `vbs` | `mmst` | -| `data` | `mmsu` | -| `mocha` | `msbd` | -| `keyword` | `rtsp` | -| `livescript` | `mso-offdap` | -| `ftp` | `snews` | -| `file` | `news` | -| `gopher` | `nntp` | -| `acrobat` | `outlook` | -| `callto` | `stssync` | -| `daap` | `rlogin` | -| `itpc` | `telnet` | -| `itms` | `tn3270` | -| `firefoxurl` | `shell` | -| `hcp` | `sip` | +--- -### Error Example +## Best practices + + + + Create different apps for development, staging, and production. + + + Regenerate keys periodically and if you suspect a compromise. + + + Request only the permissions your app actually needs. + + + Check the Developer Console regularly to track API usage. + + -If you use a callback URL that hasn't been properly added to your App's settings in the developer portal, you will receive the following error message: +--- -**OAuth 1.0a** +## Automated account labels -```json HTTP 403 - Forbidden +If your app runs a bot account, you can label it as automated: -{ - "errors": [ - { - "code":415,"message":"Callback URL not approved for this client application. Approved callback URLs can be adjusted in your application settings." - } - ] -} -``` +1. Go to your bot account's **Settings** +2. Select **Your account** → **Automation** +3. Link your managing account -OR +This builds trust with users and distinguishes your bot from spam. -```xml - - -Callback URL not approved for this client application. Approved callback URLs can be adjusted in your application settings -/oauth/request_token - -``` +--- -**OAuth 2.0** +## Troubleshooting -```json HTTP 400 + +Ensure your callback URL is exactly as registered in the Developer Console, including protocol and any trailing slashes. HTTP-encode the URL when passing it as a query parameter. +```json { - "error": "invalid_request", - "error_description": "Value passed for the redirect uri did not match the uri of the authorization code." + "errors": [{ + "code": 415, + "message": "Callback URL not approved for this client application." + }] } ``` + -**Troubleshooting** - -If you receive an error, please make sure that the callback URL that you are using in your authentication requests is HTTP encoded, and that it matches a callback URL that has been added to the allowlist for the App whose keys and tokens you are using in your request. - ---- + +If your app shows as suspended, check your email for a notice from the X platform team. Use the [Platform Help Form](https://help.x.com/forms/platform) to appeal. + diff --git a/fundamentals/developer-portal.mdx b/fundamentals/developer-portal.mdx index 155d8b8d7..e06ed82ba 100644 --- a/fundamentals/developer-portal.mdx +++ b/fundamentals/developer-portal.mdx @@ -1,94 +1,103 @@ --- -title: "Developer Portal" -icon: "globe" -keywords: ["developer portal", "X developer portal", "API dashboard", "developer account", "project management", "app management", "team management", "developer onboarding", "API access", "developer tools"] +title: Developer Console +sidebarTitle: Developer Console +icon: grid-2 +description: Manage your apps, monitor usage, and access API credentials +keywords: ["Developer Console", "X Developer Console", "API dashboard", "developer account", "app management", "team management", "developer onboarding", "API access", "developer tools", "usage monitoring", "billing"] --- import { Button } from '/snippets/button.mdx'; -## Overview +The [Developer Console](https://console.x.com) is your central hub for managing X API access. Create apps, generate credentials, monitor usage, and manage billing—all in one place. -### Introduction + -The X developer portal contains a set of self-serve tools that developers can use to manage their access to the X API and X Ads API. - -In the portal, you have the opportunity to: - -- Create and manage your X [Projects](/resources/fundamentals/projects) and [Apps](/resources/fundamentals/developer-apps) (and the authentication keys and tokens that they provide). -- Manage your access levels and integrations with the X API standard v1.1 and v2 endpoints. -- Learn more about different endpoints and features available. - -### Ready to get access? - -You can get started using the X API by signing up for an account. - -If you need additional functionality or higher [Tweet caps](/x-api/fundamentals/post-cap), you can purchase Basic or Pro within the developer portal. For those interested in Enterprise, please apply [here](https://docs.x.com/enterprise/forms/enterprise-api-interest). - - - -## What to expect within the developer portal - -### Onboarding +--- -The onboarding wizard guides you through the process of setting up your first [Project](/resources/fundamentals/projects) and [App](/resources/fundamentals/developer-apps). You will need to create a Project and App to receive the credentials required to authenticate your API requests. You will see the wizard if you are accessing your developer account for the first time. +## What you can do + + + + Set up apps to get API credentials. Configure authentication, permissions, and callback URLs. + + + Track API usage in real-time. View costs per endpoint and manage your credit balance. + + + Create API keys, access tokens, and OAuth credentials for your apps. + + + Buy credits for pay-per-usage billing. No subscriptions or commitments required. + + -Through this process, you will receive a set of authentication keys and tokens, which you can learn more about on our [App overview page](/resources/fundamentals/developer-apps#overview). To learn more about what is needed to authenticate with the X API, take a look at the [authentication section](/resources/fundamentals/authentication). +--- -**Please note:** You will need to [store your keys and tokens in a secure location](/resources/fundamentals/authentication/guides/authentication-best-practices) so you can access them later on. There is no way to reference these credentials without regenerating them. +## Getting started + + + + Go to [console.x.com](https://console.x.com) and sign in with your X account. Accept the Developer Agreement. + + + Click **New App** and provide a name and description. This generates your API credentials. + + + Copy and securely store your API Key, API Secret, and Access Tokens. These won't be shown again. + + + Use your credentials to authenticate API requests. Check out the [quickstart guide](/x-api/getting-started/make-your-first-request). + + + + +**Store credentials securely.** API keys and tokens are only displayed once when generated. Use a password manager or secure vault. If lost, you'll need to regenerate them, which invalidates the old credentials. + -### Project and App management +--- -One of the primary roles of the developer account is to enable to you [manage your Projects and Apps](/resources/fundamentals/developer-apps#app-management). Developers can both create and manage X Projects and Apps from the Dashboard in the developer portal. This is where you can find your App IDs; edit an App's setting, permissions, and callback URLs; and generate and revoke keys and tokens. +## Billing & credits -### Learn more about what's available with X API v2 +The X API uses pay-per-usage pricing with a credit-based system: -The developer portal hosts a products section where you can go to learn more about the different versions and access levels of the X API. +| Feature | Description | +|:--------|:------------| +| **No monthly fees** | Pay only for what you use—no subscriptions | +| **Credit-based** | Purchase credits upfront, deducted as you use the API | +| **Real-time tracking** | Monitor usage and costs in the console dashboard | +| **Per-endpoint pricing** | Different endpoints have different costs | +| **Deduplication** | Same resource requested twice in 24 hours is only charged once | -The X API v2 product section contains important information about the Free, Basic, Pro, and Enterprise access tiers. This page contains details on Project-level App limitations, Tweet cap, and costs, as well as endpoint-specific rate limits and special attributes. You can also compare and contrast the different access levels and apply for additional access if available. +View current pricing and purchase credits in the [Developer Console](https://console.x.com). -Review [all v2 access levels](/x-api/introduction#access-levels). +[Learn more about pricing →](/x-api/getting-started/pricing) --- ## Team management - **Only available to Enterprise v2 accounts.** +Team management is available for **Enterprise accounts** only. -[**View the "team" page within the developer portal.**](https://developer.twitter.com/en/portal/teams) - -### Why use team functionality? - -Team functionality facilitates collaborative development of Projects and Apps within the X Developer Platform. Often, teams have different people responsible for access control, billing/payments, and this allows you to invite those people to contribute to your project. - -### Inviting team members - -In order to invite someone to join a team, an admin can invite them via their X handle. They will receive an email and they can accept it via that email invitation. Once they accept, they will need to agree to the Developer Agreement & Policy, and can then access the main account's team page. +Enterprise accounts can invite team members to collaborate: -**Please note:** Team management does not currently grant/limit API access based on [App](/resources/fundamentals/developer-apps) credentials. It is not possible to share App management across your team. Apps (keys/tokens) cannot be edited, created, or deleted by non-owners. +| Role | Capabilities | +|:-----|:-------------| +| **Administrator** | Full access: manage apps, billing, team members, and roles | +| **Developer** | Manage own apps, read-only access to team apps | -### Team dashboard +To invite a team member, navigate to the Team page in your console and enter their X handle. -On the "members" tab of the team dashboard, you will view all the members and their roles. If you are an administrator of a team, you will be able to manage developer access and edit the roles of each member. - -Administrators also have access to the "pending" tab of the team dashboard. Here, admins can view the details and manage each invitation that has been sent out. - -### Team roles - -**Administrator role** - -- Ability to manage team projects and apps -- Ability to manage all app environments -- Ability to choose/upgrade subscriptions -- Ability to update billing/payment methods -- Ability to add/remove team members -- Ability to edit roles of team members +--- -**Developer role** +## Next steps -- Ability to manage own projects and apps -- Read-only access to team projects and apps -- Ability to leave the team \ No newline at end of file + + + Learn about app settings, permissions, and credentials. + + + Use your credentials to call the API. + + diff --git a/fundamentals/projects.mdx b/fundamentals/projects.mdx deleted file mode 100644 index 0463f753c..000000000 --- a/fundamentals/projects.mdx +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: Projects -icon: folder-open -keywords: ["X projects", "API projects", "developer projects", "API access levels", "free tier", "basic tier", "pro tier", "enterprise tier", "tweet caps", "API subscription", "project management", "standalone apps"] ---- - -## Introduction - -Projects allow you to organize your work based on how you intend to use the X API so you can effectively manage your access to the API and monitor your usage. Each Project can contain one or multiple [Apps](/resources/fundamentals/developer-apps) depending on your access level (described later on this page). You will use these Apps to generate [authentication](/resources/fundamentals/authentication) credentials such as [API Keys and Secrets](/resources/fundamentals/authentication#api-key-and-secret), user [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow), and [App Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). - -While you can use these keys and tokens from any App to access the X API or X Ads API (you must [apply for additional access](/x-ads-api/getting-started/step-by-step-guide) to use the Ads API), you must use keys and tokens from an App associated with a Project to be able to use the [X API v2](/x-api/introduction) endpoints. - -If you have a developer account, you can view and manage your Projects on the [Projects & Apps](https://developer.x.com/en/portal/projects-and-apps) page within the developer portal. [Sign up](https://developer.x.com/en/portal/petition/essential/basic-info) for an account if you don't have one already. - -### Projects and X API access levels - -At this time, there are 4 different tiers of access that are applied at the Project-level: - -- Free -- Basic -- Pro -- Enterprise - -To learn more about what each of these access levels provides, please visit the [about X API](/x-api/getting-started/about-x-api) page. - -You can only have one Project with either Free, Basic, or Pro. - -We will describe a few Project-specific differences here for you: - -#### Free - -This access tier is provided to anyone who has [signed up](https://developer.twitter.com/en/portal/petition/essential/basic-info) for a developer account. - -Number of Apps within that Project: 1 -[Tweet consumption cap](/x-api/fundamentals/post-cap): 1,500 Tweets per month - -Authentication methods: - -- [OAuth 2.0 with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -- App only - -#### Basic - -This access tier is provided to anyone who has subscribed for Basic access via the developer portal. - -Number of Apps within that Project: 2 -[Tweet consumption cap](/x-api/fundamentals/post-cap): 10,000 Tweets per month -Authentication methods: - -- [OAuth 2.0 with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -- [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) -- App only - -#### Pro - -This access tier is provided to anyone who has subscribed for Pro access via the developer portal. - -Number of Apps within that Project: 3 -[Tweet consumption cap](/x-api/fundamentals/post-cap): 1 million Tweets per month - -Authentication methods: - -- [OAuth 2.0 with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -- [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) -- App only - -#### Enterprise - -This access tier is provided to anyone who has applied for Enterprise, been approved by our team, and signed a monthly auto renew contract via X account manager. [Apply here](https://docs.google.com/forms/d/e/1FAIpQLScO3bczKWO2jFHyVZJUSEGfdyfFaqt2MvmOfl_aJp0KxMqtDA/viewform). - -Number of Apps within that Project: 3+ -[Tweet consumption cap](/x-api/fundamentals/post-cap): 50 million+ Tweets per month - -Authentication methods: - -- [OAuth 2.0 with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -- [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) -- App only - -### Tweet caps and Projects - -Tweet consumption caps apply at the Project-level, effectively limiting the volume of Tweets you can retrieve from certain X API v2 endpoints within a given month. - -Learn more about [Tweet caps](/x-api/fundamentals/post-cap). - -### Configuring Projects - -#### Creating a Project - -To create a Project, click on "New Project" in your [dashboard](https://developer.twitter.com/en/portal/dashboard) or the [Projects & Apps](https://developer.twitter.com/en/portal/projects-and-apps) page within the developer portal. You'll only be able to see this option if you haven't already created a Project. You will be prompted to create a Project name, description, and use case. You will also be asked to create a new App or connect an existing standalone App. - -#### Creating or Connecting an App for your Project - -If your Project doesn't include an App, you can add one by clicking on the Project name in the dashboard. From there, you can either create a new App or select an existing standalone App to connect to your Project. The App is where you can generate the authentication keys and tokens listed at the beginning of this guide. - -#### Editing a Project - -To edit a Project, click on the name of your Project from the dashboard or Projects & Apps page within the developer portal. From there you will see the details of your Project and can select "edit" to make changes. - -### Standalone Apps - -Standalone Apps are Apps that exist outside of the Project structure. The authentication credentials associated with these standalone Apps can make successful requests to X's API's standard v1.1, premium v1.1, enterprise, or to the X Ads API. Standalone Apps will fail when trying to make requests to the X API v2 endpoints unless you connect them to a Project. - -If you created an App before August 2020, they will be visible in the "Standalone Apps" section of the developer portal under [Projects & Apps](https://developer.twitter.com/en/portal/projects-and-apps). You will be limited to ten Apps in total, including those that are connected to your Project. - -If you're part of a team account, you will see the Apps that you own under Standalone Apps. If a teammate owns an App that's part of a Project, you will be able to see the App's name and owner's info, but you will not be able to change its settings, access its keys and tokens, or regenerate its keys and tokens. You should contact the App owner to make any changes to their App. - ---- diff --git a/fundamentals/rate-limits.mdx b/fundamentals/rate-limits.mdx index 95e87a238..dbf6bc171 100644 --- a/fundamentals/rate-limits.mdx +++ b/fundamentals/rate-limits.mdx @@ -1,13 +1,99 @@ --- -title: Rate limits -icon: gauge-simple-high -keywords: ["rate limits", "API rate limits", "request limits", "API throttling", "rate limiting", "X API rate limits", "Ads API rate limits", "enterprise API rate limits"] +title: Rate Limits +sidebarTitle: Rate Limits +icon: gauge-high +description: Understand API rate limits across X developer products +keywords: ["rate limits", "API rate limits", "request limits", "API throttling", "rate limiting"] --- -Everyday many thousands of developers make requests to the X developer platform. To help manage the sheer volume of these requests, limits are placed on the number of requests that can be made. These limits help us provide the reliable and scalable API that our developer community relies on. +Rate limits control how many API requests you can make in a given time period. They ensure fair usage and system stability across the platform. -Each of our APIs use rate limits in different ways. To learn more about these differences between platforms, please review the specific rate limit pages within our specific API sections: +--- + +## Rate limits by product + +Each X API product has its own rate limiting approach: + + + + Per-endpoint limits based on 15-minute windows. View detailed tables and recovery tips. + + + Custom limits based on your enterprise agreement and data package. + + + Limits specific to ad management and analytics endpoints. + + + +--- + +## How rate limits work + +| Concept | Description | +|:--------|:------------| +| **Time window** | Most limits reset every 15 minutes | +| **Per-user limits** | Apply when using OAuth 1.0a or OAuth 2.0 user tokens | +| **Per-app limits** | Apply when using Bearer Token (app-only) authentication | +| **Endpoint-specific** | Each endpoint has its own limit | + +--- + +## Checking your limits + +Every API response includes headers showing your current rate limit status: + +``` +x-rate-limit-limit: 900 +x-rate-limit-remaining: 847 +x-rate-limit-reset: 1705420800 +``` + +| Header | Description | +|:-------|:------------| +| `x-rate-limit-limit` | Maximum requests allowed in the current window | +| `x-rate-limit-remaining` | Requests remaining in the current window | +| `x-rate-limit-reset` | Unix timestamp when the limit resets | + +--- + +## Rate limit errors + +When you exceed a rate limit, you'll receive a **429 Too Many Requests** response: + +```json +{ + "errors": [{ + "code": 88, + "message": "Rate limit exceeded" + }] +} +``` + +--- + +## Best practices + + + + Store API responses locally to reduce repeated requests for the same data. + + + When rate limited, wait before retrying. Double the wait time with each retry. + + + Monitor rate limit headers to avoid hitting limits proactively. + + + For real-time data, use filtered stream instead of polling search endpoints. + + + +--- + +## Next steps + +For detailed rate limit tables by endpoint, see the rate limits page for your specific API: -- [X API v2](/x-api/fundamentals/rate-limits) -- [X API: Enterprise](/x-api/enterprise-gnip-2.0/fundamentals/rate-limits) -- [X Ads API](/x-ads-api/fundamentals/rate-limiting) \ No newline at end of file +- [X API v2 Rate Limits](/x-api/fundamentals/rate-limits) +- [X Ads API Rate Limits](/x-ads-api/fundamentals/rate-limiting) diff --git a/fundamentals/security.mdx b/fundamentals/security.mdx index 93a7451e8..f7d762e53 100644 --- a/fundamentals/security.mdx +++ b/fundamentals/security.mdx @@ -1,98 +1,189 @@ --- title: Security -icon: lock -keywords: ["security", "API security", "developer security", "secure coding", "OAuth security", "API keys security", "token security", "XSS", "CSRF", "SQL injection", "encryption", "TLS", "vulnerability reporting", "security best practices"] +sidebarTitle: Security +icon: shield-halved +description: Security best practices for X API developers +keywords: ["security", "API security", "OAuth security", "API keys security", "token security", "secure coding", "vulnerability reporting"] --- -We believe privacy is a right, not a privilege, and it is built into the foundations of our company. By using the X developer platform and abiding by our developer policy, you play a critical role in making sure the platform serves the public conversation on X and safeguards our commitment to privacy. +Building secure applications protects both your users and the X platform. This guide covers essential security practices for X API developers. -We want to remind you of the importance of building securely in order to protect both your own and your apps' users' data. It is your responsibility to protect against the threat of security breaches, and we have a shared responsibility to protect the people who use X. This page describes expectations around building secure applications and keeping data and access as safe as possible. - -Please be aware of the security technologies available for the X Developer Platform including [authentication](/resources/fundamentals/authentication), TLS, and [app permissions](/resources/fundamentals/developer-apps#app-permissions), as well as from the X user perspective for [using third party applications and sessions](https://help.x.com/en/managing-your-account/connect-or-revoke-access-to-third-party-apps). - -## Reporting security issues - -X Developer Platform users must notify X no more than 48 hours after initial suspicion a security incident has occurred, through the [X's vulnerability reporting program](https://hackerone.com/twitter). - -## Security best practices +--- -Please keep them in mind as you build on the X developer platform, and elsewhere across the internet. +## Core requirements -### Security by design + + + All API requests must use HTTPS. Plain HTTP is rejected. + + + Never expose API keys or tokens in client-side code, logs, or repositories. + + -Consider hiring security professionals to do a threat model audit and/or penetration test. A good security firm will dig deep to uncover issues. Read more about how X has adopted this mindset [here in our blog post](https://blog.x.com/en_us/topics/company/2019/privacy_data_protection.html). +--- -Additionally, X holds all partners accountable for the following: +## Protecting credentials + +Your API keys and tokens are the keys to your app. Keep them secure: + + + + Store credentials in environment variables, not in code. + ```bash + export X_API_KEY="your-api-key" + export X_API_SECRET="your-api-secret" + ``` + + + Add credential files to `.gitignore`. Use tools like `git-secrets` to prevent accidental commits. + + + Regenerate keys periodically and immediately if you suspect a compromise. + + + Only request the OAuth scopes your app actually needs. + + + +### If credentials are compromised + +1. **Regenerate immediately** in the [Developer Console](https://console.x.com) +2. **Revoke old tokens** — regenerating automatically invalidates old credentials +3. **Audit usage** — check for unauthorized API activity +4. **Update your app** — deploy new credentials to all environments -- Maintain code within a secure repository. -- Perform risk analysis throughout the Systems development life cycle (SDLC) process. -- Ensure security issues are identified and mitigated throughout SDLC. -- Ensure there exists segregation of environments throughout the SDLC process. -- Ensure all test defects are fixed, re-tested and closed out. +--- -### Monitor and get alerted +## Application security -If you think there's an issue with your web app, how do you find out for sure? Be sure to keep good logs, and that you are notified of critical exceptions and errors. You may want to put together a dashboard of critical statistics so that you can see at a glance if something is going wrong. +### Input validation -### Create a reporting channel +Never trust user input. Validate and sanitize all data before using it: -Make it easy for your users to contact you about potential security issues that they've experienced with your app. If an issue is discovered which affects X users and data, it's your responsibility to [report this issue to X](#report) as well. Have an action plan/process ready for notifying affected users, should a security incident occur. +```python +# Bad - vulnerable to injection +query = f"from:{user_input}" -### Adequate testing +# Good - validate input first +import re +if re.match(r'^[a-zA-Z0-9_]{1,15}$', user_input): + query = f"from:{user_input}" +``` -Ensure that your end-to-end tests are thorough and updated to include expected failures for security scenarios such as unauthorized access. Put yourself in an attacker's mindset and set up system tests that are expected to block an attacker gaining unauthorized access to X data or authorized functionality. +### Output encoding -### Securing API keys and tokens +Escape X API data before displaying in HTML to prevent XSS: -As a developer on the X platform, you have programmatic access to both your data and your users data stored by X, assuming they've authorized your developer App. All API requests must be [authenticated](/resources/fundamentals/authentication) using OAuth with your developer App's key and secret and in some cases an authorizing user's [access tokens](/resources/fundamentals/authentication). It is your responsibility to keep your credentials safe. +```javascript +// Bad - vulnerable to XSS +element.innerHTML = tweet.text; -Some suggested best practices include the following: +// Good - escape HTML +element.textContent = tweet.text; +``` -- Create a password/token refresh rotation. -- Always encrypt sensitive data as needed and to not decrypt data too far upstream. -- Store your users' access tokens in an encrypted store. -- [Regenerate](/resources/fundamentals/authentication#regenerate-api-keys-and-tokens) or [invalidate](/resources/fundamentals/authentication#post-oauth2-invalidate-token) keys and tokens if you believe they have been compromised. +### Common vulnerabilities to prevent -For more discussion on debugging and building with OAuth for X please visit the community forum's [security category](https://devcommunity.x.com/c/oauth/12). +| Vulnerability | Prevention | +|:--------------|:-----------| +| **XSS** | Escape all user-generated content before rendering | +| **CSRF** | Use anti-CSRF tokens in forms; verify OAuth state parameter | +| **SQL Injection** | Use parameterized queries, never concatenate user input | +| **Open redirects** | Validate callback URLs against an allowlist | -### Input validation +--- -Don't assume that your users will provide you with valid, trustworthy data. Sanitize all data coming from your users that can end up in X API requests. Allowlist the types of input that are acceptable to your application and discard everything that isn't on the allowlist. +## OAuth security -### Encrypted communication +### State parameter -X requires all API requests to be made over TLS. Communication made to your own servers should also be encrypted wherever possible. +Always use the `state` parameter in OAuth flows to prevent CSRF: -### Exposed debugging information +```python +import secrets -Be sure that you're not exposing sensitive X data or credentials through debugging screens/logs. Some web frameworks make it easy to access debugging information if your application is not properly configured. For desktop and mobile developers, it's easy to accidentally ship a build with debugging flags or symbols enabled. Build checks for these configurations into your deployment/build process. Additionally, if sharing stack traces or crash dumps for reporting, ensure that private X users' data are redacted. +# Generate state before authorization +state = secrets.token_urlsafe(32) +session['oauth_state'] = state -### Unfiltered input, unescaped output +# Verify state after callback +if request.args.get('state') != session.get('oauth_state'): + abort(403) # State mismatch - possible CSRF +``` -One easy-to-remember approach to input validation is FIEO: Filter Input, Escape Output +### Token storage -Filter anything from outside your application, including X API data, cookie data, user-supplied form input, URL parameters, data from databases, etc. Escape all output being sent by your application, including SQL sent to your database server, HTML to you send to users' browsers, JSON output sent to other systems, and commands sent to shell programs. +| Token type | Storage recommendation | +|:-----------|:-----------------------| +| **Access tokens** | Encrypted database or secure vault | +| **Refresh tokens** | Encrypted database with additional access controls | +| **Bearer tokens** | Environment variables or secure configuration | -### Cross-site scripting (XSS) +--- -[XSS](https://owasp.org/) attacks are, by most measures, the most common form of security problem on the web. If an attacker can get their own JavaScript code into your application, they can do bad things. Anywhere you store and display untrusted, user-supplied data needs to be checked, sanitized, and HTML escaped. Getting this right is hard, because hackers have [many different ways to land XSS attacks](https://www.owasp.org/index.php/Types_of_Cross-Site_Scripting). Your language or web development framework probably has a popular, well-tested mechanism for defending against cross-site scripting; please make use of it. +## Secure development practices + + + + Conduct regular security reviews and penetration testing. + + + Keep dependencies updated. Use tools to detect vulnerable packages. + + + Log security events but never log credentials or sensitive data. + + + Set up alerts for unusual API usage patterns. + + -### SQL injection +--- -If your application makes use of a database, you need to be aware of [SQL injection](http://en.wikipedia.org/wiki/SQL_injection). Anywhere you accept input is a potential target for an attacker to break out of their input field and into your database. Use database libraries that protect against SQL injection in a systematic way. If you break out of that approach and write custom SQL, write aggressive tests to be sure you aren't exposing yourself to this form of attack. The two main approaches to defending against SQL injection are escaping before constructing your SQL statement, and using parameterized input to create statements. The latter is recommended, as it's less prone to programmer error. +## Reporting security issues -### Cross-site request forgery (CSRF) +If you discover a security vulnerability affecting X: -Are you sure that requests to your application are coming from your application? [CSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) attacks exploit this lack of knowledge by forcing logged-in users of your site to silently open URLs that perform actions. In the case of a developer App, this could mean that attackers are using your app to force users to post unwanted Tweets or follow spam accounts. + +**Report within 48 hours.** X Developer Platform users must notify X no more than 48 hours after suspecting a security incident. + -The most thorough way to deal with CSRF is to include a random token in every form that's stored someplace trusted; if a form doesn't have the right token, throw an error. Modern web frameworks have systematic ways of handling this, and might even be doing it by default if you're lucky. A simple preventative step (but by no means the only step you should take) is to make any actions that create, modify, or destroy data require a POST request. + + + Report vulnerabilities in X's systems through HackerOne. + + + If your app using X data is breached, report through the same channel. + + -### Lack of rate limiting +--- -Use CAPTCHAs where appropriate to slow down potential spammers and attackers. +## Compliance checklist + + +- [ ] All API requests use TLS/HTTPS +- [ ] Credentials stored securely (not in code or logs) +- [ ] User tokens encrypted at rest +- [ ] Input validation on all user-supplied data +- [ ] Output encoding to prevent XSS +- [ ] CSRF protection on OAuth flows +- [ ] Security logging enabled (without sensitive data) +- [ ] Incident response plan documented +- [ ] Dependencies regularly updated +- [ ] Minimal OAuth scopes requested + --- - -If you've discovered a security issue that directly affects X itself, we have a [bug bounty program for vulnerabilities](https://hackerone.com/twitter). - \ No newline at end of file +## Resources + + + + Implement OAuth correctly. + + + Configure minimal required permissions. + + diff --git a/fundamentals/x-ids.mdx b/fundamentals/x-ids.mdx index e0dd6f63b..e76ba1cf0 100644 --- a/fundamentals/x-ids.mdx +++ b/fundamentals/x-ids.mdx @@ -1,21 +1,108 @@ --- title: X IDs -icon: id-card-clip -keywords: ["X IDs", "Twitter IDs", "user ID", "tweet ID", "snowflake ID", "64-bit integers", "ID format", "ID string", "unique identifiers"] +sidebarTitle: X IDs +icon: fingerprint +description: Understanding unique identifiers in the X API +keywords: ["X IDs", "Twitter IDs", "user ID", "tweet ID", "snowflake ID", "64-bit integers", "ID format", "unique identifiers"] --- -Each object within X - a Tweet, Direct Message, User, List, and so on - has a unique ID. +Every object in the X API—posts, users, lists, DMs, spaces—has a unique ID. Understanding how these IDs work helps you build reliable integrations. -At the very beginning of the platform, these IDs were small enough numbers that they could be generated sequentially. Over time, to accommodate growth, the IDs moved from being 32-bit, to 64-bit. Today, X IDs are unique 64-bit unsigned integers, which are based on time, instead of being sequential. The full ID is composed of a timestamp, a worker number, and a sequence number. X developed an internal service known as "Snowflake" in order to consistently generate these IDs ([read more about this on the X blog](https://blog.x.com/engineering/en_us/a/2010/announcing-snowflake.html)). +--- + +## ID format + +X IDs are **64-bit unsigned integers** generated using a system called "Snowflake." Each ID encodes: + +- **Timestamp** — When the object was created +- **Worker number** — Which server generated the ID +- **Sequence number** — Order within that millisecond + +This means IDs are roughly time-ordered: higher IDs generally represent newer objects. + + +IDs are globally unique across all of X, not just within a single object type. + + +--- -Numbers as large as 64-bits can cause issues with programming languages that represent integers with fewer than 64-bits. An example of this is JavaScript, where integers are limited to 53-bits in size. In order to provide a workaround for this, in the original designs of the X API (v1/1.1), ID values were returned in two formats: both as integers, and as strings. +## String vs. integer representation -```json -{"id": 10765432100123456789, "id_str": "10765432100123456789"} + +**Always use string IDs in your code.** Some programming languages (like JavaScript) can't accurately represent 64-bit integers. + + +In JavaScript, integers are limited to 53 bits. This causes precision loss with large IDs: + +```javascript +// This loses precision! +const id = 10765432100123456789; +console.log(id.toString()); // "10765432100123458000" — wrong! + +// Use strings instead +const id = "10765432100123456789"; +console.log(id); // "10765432100123456789" — correct! ``` -If you run the command `(10765432100123456789).toString()` in a browser JavaScript console, the result will be `"10765432100123458000"` - the 64-bit integer loses accuracy as a result of the translation (this is sometimes called "munging" - a destructive change to a piece of data). +### API versions + +| Version | ID format | +|:--------|:----------| +| **X API v2** | IDs are returned as strings by default | +| **X API v1.1** | Returns both `id` (integer) and `id_str` (string) — always use `id_str` | + +--- + +## Working with IDs + +### Storing IDs + +Store IDs as strings or 64-bit integers in your database: + +| Database | Recommended type | +|:---------|:-----------------| +| PostgreSQL | `BIGINT` or `TEXT` | +| MySQL | `BIGINT UNSIGNED` or `VARCHAR(20)` | +| MongoDB | String | +| SQLite | `TEXT` (SQLite integers max at 63 bits) | + +### Comparing IDs + +When comparing IDs for chronological ordering: + +```python +# Python - safe for 64-bit integers +if int(id1) > int(id2): + print("id1 is newer") + +# JavaScript - compare as strings (lexicographically works for same-length IDs) +# Or use BigInt +if (BigInt(id1) > BigInt(id2)) { + console.log("id1 is newer"); +} +``` + +--- + +## Common ID types + +| Object | Example ID | Notes | +|:-------|:-----------|:------| +| Post (Tweet) | `1234567890123456789` | Also called Tweet ID | +| User | `2244994945` | Older accounts have shorter IDs | +| List | `1234567890` | | +| Space | `1YqGodQbNXDxv` | Alphanumeric, not Snowflake format | +| DM Event | `1234567890123456789` | | + +--- -In X APIs up to version 1.1, you should always use the string representation of the number to avoid losing accuracy. +## Related resources -In newer versions of the API, all large integer values are represented as strings by default. \ No newline at end of file + + + See ID fields for each object type. + + + Retrieve posts by ID. + + diff --git a/overview.mdx b/overview.mdx index 2c6586f6a..b6d3f7e13 100644 --- a/overview.mdx +++ b/overview.mdx @@ -1,15 +1,14 @@ --- -title: Welcome to the X Developer Platform +title: X Developer Platform sidebarTitle: Overview mode: wide -icon: hand-wave +icon: house +description: Build with X's real-time data and APIs keywords: ["X API", "Twitter API", "developer platform", "API documentation", "X developer", "API keys", "SDK", "Python SDK", "TypeScript SDK", "API integration", "developer tools", "API quickstart", "X Ads API", "X for Websites"] --- import { Button } from '/snippets/button.mdx'; -Build, analyze, and innovate with X's real-time, global data and APIs. Whether you're creating new apps, integrating with X, or analyzing trends, our platform gives you the tools to get started quickly. -
---- - -## Jump right in - -Get started quickly with these popular resources and guides. +Build, analyze, and innovate with X's real-time global data. Access posts, users, trends, and more through modern APIs with flexible pay-per-usage pricing. -
- -
-
- Create an API key and make your first request to the X API in minutes. -
-
- -
-
+ + + Get your API keys and make your first request in minutes. - -
-
- Step-by-step guides for common use cases and integrations. -
-
- -
-
+ + Explore endpoints for posts, users, spaces, DMs, lists, and more. - -
-
- Official and community libraries to speed up your development. -
-
- -
-
+ + Official Python and TypeScript SDKs for faster development. -
+ --- ## Products -Explore the main products of the X Developer Platform. Each product is designed to help you build, analyze, and integrate with X in different ways. - - - Programmatic access to X's core data: posts, users, spaces, DMs, lists, trends, media, and more. + + Access posts, users, spaces, DMs, lists, trends, media, and more. Search, stream, and analyze X's public conversation. - - Automate and manage ad campaigns, targeting, creatives, and analytics on the X Ads platform. + + Programmatically manage ad campaigns, targeting, creatives, and analytics. + + + Embed posts, timelines, and follow buttons on your website. + + + +--- + +## Pricing + +The X API uses **pay-per-usage** pricing. No monthly subscriptions—pay only for what you use. + + + + Scale up or down based on your needs. Costs grow with your usage. - - Embed X content, timelines, and engagement tools directly into your website or app. + + Lower barriers for small projects. No tier jumps or commitments. +
+ +
+ +For enterprise needs with dedicated support, complete streams, and custom integrations: + + + --- -## Support & Community +## Resources -- [Support hub](https://developer.x.com/en/support) — Troubleshooting, FAQs, and contact info -- [Developer forum](https://devcommunity.x.com) — Join the conversation -- [Newsletter](https://docs.x.com/resources/newsletter) - Get monthly updates + + + Step-by-step guides for common use cases and integrations. + + + Official SDKs and community libraries for every language. + + + Get help from the community and X team. + + + Stay updated on API changes and new features. + + --- -## Stay informed +## Quick Links -- [API status](https://docs.x.com/status) -- [Changelog](https://docs.x.com/changelog) \ No newline at end of file +| Resource | Description | +|:---------|:------------| +| [Developer Console](https://console.x.com) | Manage apps, view usage, and purchase credits | +| [API Status](/status) | Check current API availability | +| [Support](https://developer.x.com/en/support) | Troubleshooting and FAQs | +| [Newsletter](/newsletter) | Monthly platform updates | diff --git a/status.mdx b/status.mdx index aa248d8d9..d130c1df0 100644 --- a/status.mdx +++ b/status.mdx @@ -14,7 +14,7 @@ All systems are operational Normal - + Normal diff --git a/tutorials/explore-a-users-posts.mdx b/tutorials/explore-a-users-posts.mdx index 6d882b2f6..6790e4a68 100644 --- a/tutorials/explore-a-users-posts.mdx +++ b/tutorials/explore-a-users-posts.mdx @@ -13,10 +13,10 @@ keywords: ["user timeline", "user mentions", "tweet timeline", "mention timeline ## Prerequisites * In order to use the user Tweet timeline and user mention timeline endpoints, you will need to have a valid developer account.  -* You will also need a [Project](/resources/fundamentals/projects) created. -* You must have [signed up](https://developer.x.com/en/portal/petition/essential/basic-info) for a developer account, and have activated the [new developer portal experience](https://developer.x.com/en/portal/opt-in.html).  -* Access is available with active keys and tokens for a developer App that is attached to a [Project](/resources/fundamentals/projects) created in the [developer portal](/resources/fundamentals/developer-portal). -* A Bearer Token from your App in the [X developer portal](/resources/fundamentals/developer-portal). +* You will also need a [Project](/resources/fundamentals/developer-apps) created. +* You must have [signed up](https://developer.x.com/en/portal/petition/essential/basic-info) for a developer account, and have activated the [new Developer Console experience](https://developer.x.com/en/portal/opt-in.html).  +* Access is available with active keys and tokens for a developer App that is attached to a [Project](/resources/fundamentals/developer-apps) created in the [Developer Console](/resources/fundamentals/developer-portal). +* A Bearer Token from your App in the [X Developer Console](/resources/fundamentals/developer-portal). * If you do not have an approved developer account, you can [apply for one](https://developer.x.com/en/apply-for-access). ## Approved developer account @@ -25,7 +25,7 @@ If you do not have one yet, you can [apply for one](https://developer.x.com/en/ ## Create a Project and connect an App -In the [developer portal](https://developer.x.com/en/portal/dashboard), click create a new Project. +In the [Developer Console](https://developer.x.com/en/portal/dashboard), click create a new App. @@ -48,7 +48,7 @@ Once you click complete, you will get your API keys and the bearer token that yo Click the (+) next to API key, API secret key and Bearer token and copy these values to a safe place on your local machine. You will need these to make the API calls in the next step. -Note: The keys in the screenshot above are hidden, but in your own developer portal, you will be able to see the actual values for the API key, API secret key and Bearer token. +Note: The keys in the screenshot above are hidden, but in your own Developer Console, you will be able to see the actual values for the API key, API secret key and Bearer token.   ## How to get the user ID for a user to use in the user Tweet timeline and user mention timeline endpoints diff --git a/tutorials/getting-historical-posts-using-the-full-archive-search-endpoint.mdx b/tutorials/getting-historical-posts-using-the-full-archive-search-endpoint.mdx index 8de4adc76..5accc9795 100644 --- a/tutorials/getting-historical-posts-using-the-full-archive-search-endpoint.mdx +++ b/tutorials/getting-historical-posts-using-the-full-archive-search-endpoint.mdx @@ -41,14 +41,14 @@ Learn more about the ### Connect an app to the academic project Once you are approved to use the Academic Research product track, you will see -your Academic [Project](/resources/fundamentals/projects) in the -[developer portal](https://developer.x.com/en/portal/dashboard). From the -"Projects and Apps" section, click on "Add App" to connect your +your Academic [Project](/resources/fundamentals/developer-apps) in the +[Developer Console](https://developer.x.com/en/portal/dashboard). From the +"Apps" section, click on "Add App" to connect your [X App](/resources/fundamentals/developer-apps) to the Project. [](https://res.cloudinary.com/practicaldev/image/fetch/s--gHFOyuDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gb7aevhqyfvfjznd0pnd.png) -![This image displays an Academic Project in the developer portal that does not have an App added to it yet](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/docs/tutorials/getting-historical-tweets-using-the-full-archive-search-endpoint/dev-portal-1.png.twimg.1920.png) +![This image displays an Academic Project in the Developer Console that does not have an App added to it yet](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/docs/tutorials/getting-historical-tweets-using-the-full-archive-search-endpoint/dev-portal-1.png.twimg.1920.png) Then, you can either choose an existing App and connect it to your project (as shown below). @@ -68,7 +68,7 @@ then use to connect to the full-archive search endpoint. **Please note** -The keys in the screenshot above are hidden, but in your own developer portal, +The keys in the screenshot above are hidden, but in your own Developer Console, you will be able to see the actual values for the API Key, API Secret Key, and Bearer Token. Save these keys and the Bearer Token because you will need those for calling the full-archive search endpoint. @@ -193,13 +193,7 @@ curl --request GET This way, you can keep checking if a next_token is available and if you have not reached your desired number of Posts to be collected, you can keep calling the -full-archive endpoint with the new next_token for each request. Keep in mind -that full-archive search endpoint counts towards your total -[Post cap](/x-api/fundamentals/post-cap), in other words the number of -Posts per month that you can get from the X API per -[Project](/resources/fundamentals/projects), so be mindful of your code logic when paging -through the results in order to make sure you do not end up inadvertently -exhausting your Post cap. +full-archive endpoint with the new next_token for each request. Below are some resources that can help you when using the full-archive search endpoint. We would love to hear your feedback. Reach out to us on diff --git a/tutorials/getting-started-with-r-and-v2-of-the-x-api.mdx b/tutorials/getting-started-with-r-and-v2-of-the-x-api.mdx index 7ede101d2..5d3ead0d1 100644 --- a/tutorials/getting-started-with-r-and-v2-of-the-x-api.mdx +++ b/tutorials/getting-started-with-r-and-v2-of-the-x-api.mdx @@ -29,7 +29,7 @@ Before you can use the X API v2, you will need to for a developer account. Once you have an approved developer account, you will need to first create a -[Project](/resources/fundamentals/projects). Projects allow you to organize your work +[Project](/resources/fundamentals/developer-apps). Projects allow you to organize your work based on how you intend to use the X API, so you can effectively manage your access to the API, and monitor your usage. @@ -57,7 +57,7 @@ For the code examples, I'm going to be showing today, you will want to create an environment variable for your bearer token. The Bearer Token is what allows you to authenticate to the X API and start making requests. First, replace “your-bearer-token” with your own bearer token, which can be obtained from the -keys and tokens section of your App in the developer portal. You'll need to run +keys and tokens section of your App in the Developer Console. You'll need to run this line of code in the console before you start writing a script. ```r diff --git a/x-ads-api/creatives.mdx b/x-ads-api/creatives.mdx index 39b5accb3..77cc23225 100644 --- a/x-ads-api/creatives.mdx +++ b/x-ads-api/creatives.mdx @@ -1117,7 +1117,7 @@ Creative partners can assign metadata tags on new or existing creative assets wi `exiftool -date="" -creative_file.jpg` -The app\_id  can be found in the [Developer Portal](https://developer.x.com/en/portal/dashboard) under Projects & Apps. Example: 16489123 +The app\_id  can be found in the [Developer Console](https://developer.x.com/en/portal/dashboard) under Projects & Apps. Example: 16489123 The following example adds app\_id as the contributor tag and date as the date tag for an image: diff --git a/x-ads-api/getting-started/step-by-step-guide.mdx b/x-ads-api/getting-started/step-by-step-guide.mdx index 406eb5a04..c890fbd00 100644 --- a/x-ads-api/getting-started/step-by-step-guide.mdx +++ b/x-ads-api/getting-started/step-by-step-guide.mdx @@ -15,7 +15,7 @@ import { Button } from "/snippets/button.mdx"; ### Step one: Signup for a developer account -To make a request to any of X's API products, you must first sign up for a developer account. Within the developer portal, create a Project and developer App. This will provide you a set of credentials that you will use to authenticate all requests to the API. +To make a request to any of X's API products, you must first sign up for a developer account. Within the Developer Console, create an App and developer App. This will provide you a set of credentials that you will use to authenticate all requests to the API. -
\ No newline at end of file + + + Set up your first subscription + + + Webhook-based alternative + + + Full endpoint documentation + + diff --git a/x-api/activity/quickstart.mdx b/x-api/activity/quickstart.mdx index 18e6d1c36..32abb85dd 100644 --- a/x-api/activity/quickstart.mdx +++ b/x-api/activity/quickstart.mdx @@ -1,261 +1,184 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["activity API quickstart", "X Activity API quickstart", "activity quickstart", "activity tutorial", "activity guide"] +description: Set up activity stream subscriptions and receive events +keywords: ["activity quickstart", "activity stream tutorial", "subscription guide"] --- -This guide explains how to subscribe for and receive events using the X Activity API endpoints. There generally 3 steps involved: -1. Identify the User ID for the User who's events you want to filter on -2. Create a subscription for the type of event you want to filter for that User -3. Receive the events using webhook or persistent http stream connection +import { Button } from '/snippets/button.mdx'; -## Getting User IDs +This guide walks you through setting up activity stream subscriptions to receive real-time account activity events. -Before creating subscriptions, you'll need to know the user ID of the account you want to filter on. In this example, we will use the XDevelopers handle. You can look up user IDs in a few of ways including: + +**Prerequisites** -**Look up a user's ID by username:** -```bash -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" "https://api.x.com/2/users/by/username/xdevelopers" -``` +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token + -**Get your own user ID:** -```bash -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" https://api.x.com/2/users/me -``` - -Both endpoints return user information including the `id` field, which you can use in subscription filters. Example json response is shown below: +--- -```json -{ - "data": { - "id": "2244994945", - "name": "Developers", - "username": "XDevelopers" + + + Subscribe to a user's activity events: + + + + ```bash + curl -X POST "https://api.x.com/2/activity/subscriptions" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "user_id": "2244994945", + "event_types": ["tweet_create_events", "favorite_events", "follow_events"] + }' + ``` + + + ```python + import requests + + bearer_token = "YOUR_BEARER_TOKEN" + + url = "https://api.x.com/2/activity/subscriptions" + headers = { + "Authorization": f"Bearer {bearer_token}", + "Content-Type": "application/json" } -} -``` - -## Creating a Subscription - -Next step is to create a subscription. In this example, we will subscribe to XDevelopers's bio updates. In order to do so, we will pass the `user_id` and `event_type` in the JSON body. In this case, the `event_type` is `ProfileBioUpdate`. - -We'll pass X Developer's user ID: `2244994945`, and an optional tag: - -```json -{ - "event_type": "ProfileBioUpdate", - "filter": { - "user_id": "2244994945" - }, - "tag": "Xdevelopers' bio updates" -} -``` - -We'll use our [bearer token](https://docs.x.com/fundamentals/authentication/oauth-2-0/overview#bearer-token-also-known-as-app-only) (from the developer portal) for authorization for all endpoints related to XAA: - -```bash -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ - https://api.x.com/2/activity/subscriptions \ - -X POST \ - -d '{ - "event_type": "ProfileBioUpdate", - "filter": { - "user_id": "2244994945" - }, - "tag": "Xdevelopers' bio updates" - }' -``` - -Upon successful request, your subscription will be created: - -```json -{ - "data":[ - { - "created_at":"2025-10-09T16:35:08.000Z", - "event_type":"ProfileBioUpdate", - "filter":{ - "user_id":"2244994945" - }, - "subscription_id":"1976325569252868096", - "tag": "Xdevelopers' bio updates", - "updated_at":"2025-10-09T16:35:08.000Z" + payload = { + "user_id": "2244994945", + "event_types": ["tweet_create_events", "favorite_events", "follow_events"] } - ], - "meta": { - "total_subscriptions": 1 - } -} -``` - -## Getting the events - -Once we have created the subscription, we can receive the events via [webhooks](https://docs.x.com/x-api/webhooks/introduction) or a persistent HTTP stream. In this example, we will open the persistent HTTP stream: - -```bash -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" https://api.x.com/2/activity/stream -``` - -When Xdevelopers account updates their profile bio, the event will be delivered through the stream: - -```json -{ - "data": { - "filter": { - "user_id": "2244994945" - }, - "event_type": "ProfileBioUpdate", - "tag": "Xdevelopers' bio updates", - "payload": { - "before": "Mars & Cars", - "after": "Mars, Cars & AI" - } - } -} -``` - -## Subscription Management - -The X Activity API provides endpoints to manage your subscriptions through standard CRUD operations. - -### Create Subscription - -Create a new subscription to receive events: - -```bash -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ - -X POST \ - https://api.x.com/2/activity/subscriptions \ - -d '{ - "event_type": "ProfileBioUpdate", - "filter": { - "user_id": "123456789" - }, - "tag": "my bio updates", - "webhook_id": "1976325569252868099" - }' -``` - -**Note:** - -- The `tag` field is optional. This can be used to help identify events on delivery. -- The `webhook_id` field is also optional. See our [webhook docs](https://docs.x.com/x-api/webhooks/introduction) for help setting up a webhook. If a `webhook_id` is specified, the event will be delivered to the provided webhook, in addition to the stream if it is open. - -**Response:** - -```json -{ - "data": { - "subscription_id": "1976325569252868096", - "event_type": "ProfileBioUpdate", - "filter": { - "user_id": "123456789" - }, - "created_at": "2025-10-09T16:35:08.000Z", - "updated_at": "2025-10-09T16:35:08.000Z", - "tag": "my bio updates", - "webhook_id": "1976325569252868099" - } -} -``` - -### List Subscriptions - -Retrieve all active subscriptions for your application: - -```bash -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ - https://api.x.com/2/activity/subscriptions -``` - -**Response:** - -```json -{ - "data": [ + + response = requests.post(url, headers=headers, json=payload) + print(response.json()) + ``` + + + + **Response:** + + ```json { - "subscription_id": "1976325569252868096", - "event_type": "ProfileBioUpdate", - "filter": { - "user_id": "123456789" - }, - "created_at": "2025-10-09T16:35:08.000Z", - "updated_at": "2025-10-10T03:50:59.000Z", - }, + "data": { + "id": "1234567890", + "user_id": "2244994945", + "event_types": ["tweet_create_events", "favorite_events", "follow_events"], + "created_at": "2024-01-15T10:00:00.000Z" + } + } + ``` + + + + Open a persistent connection to receive events: + + + + ```bash + curl "https://api.x.com/2/activity/stream" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + + ```python + import requests + + bearer_token = "YOUR_BEARER_TOKEN" + + url = "https://api.x.com/2/activity/stream" + headers = {"Authorization": f"Bearer {bearer_token}"} + + response = requests.get(url, headers=headers, stream=True) + + for line in response.iter_lines(): + if line: + print(line.decode("utf-8")) + ``` + + + + + + Events stream as JSON objects: + + ```json { - "subscription_id": "1976325569252868097", - "event_type": "ProfilePictureUpdate", - "filter": { - "user_id": "987654321" - }, - "created_at": "2025-10-08T14:35:08.000Z", - "updated_at": "2025-10-08T14:35:08.000Z", + "for_user_id": "2244994945", + "event_type": "tweet_create_events", + "created_at": "2024-01-15T10:30:00.000Z", + "tweet_create_events": [ + { + "id": "1234567890", + "text": "Hello from the stream!", + "author_id": "2244994945" + } + ] } - ], - "meta": { - "total_subscriptions": 2 - } -} -``` - -### Delete Subscription - -Remove a subscription: - -```bash -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ - -X DELETE \ - https://api.x.com/2/activity/subscriptions/1976325569252868096 -``` - -**Response:** - -```json -{ - "data": { - "deleted": true - }, - "meta": { - "total_subscriptions": 0 - } -} -``` - -`total_subscriptions` shows the remaining number of subscriptions associated with your app after the delete operation. - -### Update Subscription - -The PUT endpoint allows you to update a subscription's delivery method or tag. - -Updating the `filter` or `event_type` requires deleting the existing subscription and adding a new one. - -``` -curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ - -X PUT \ - https://api.x.com/2/activity/subscriptions/1976325569252868096 \ - -d '{ - "tag": "my new tag", - "webhook_id": "192846273860294839" - }' -``` - -**Response:** - -```json -{ - "data": { - "subscription_id": "1976325569252868096", - "event_type": "ProfileBioUpdate", - "filter": { - "user_id": "123456789" - }, - "created_at": "2025-10-09T16:35:08.000Z", - "updated_at": "2025-10-10T17:10:58.000Z", - "tag": "my new tag", - "webhook_id": "192846273860294839" - }, - "meta": { - "total_subscriptions": 1 - } -} -``` \ No newline at end of file + ``` + + + +--- + +## Available event types + +| Event | Description | +|:------|:------------| +| `tweet_create_events` | User posts a new Post | +| `favorite_events` | User likes a Post | +| `follow_events` | User follows or is followed | +| `direct_message_events` | User sends or receives a DM | +| `block_events` | User blocks or unblocks | +| `mute_events` | User mutes or unmutes | + +--- + +## Manage subscriptions + + + + Get all active subscriptions: + + ```bash + curl "https://api.x.com/2/activity/subscriptions" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + + + Modify event types for a subscription: + + ```bash + curl -X PUT "https://api.x.com/2/activity/subscriptions/1234567890" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "event_types": ["tweet_create_events", "favorite_events"] + }' + ``` + + + + Remove a subscription: + + ```bash + curl -X DELETE "https://api.x.com/2/activity/subscriptions/1234567890" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + + +--- + +## Next steps + + + + Webhook-based alternative + + + Full endpoint documentation + + diff --git a/x-api/communities/lookup/introduction.mdx b/x-api/communities/lookup/introduction.mdx index 4256621b7..f437c5a17 100644 --- a/x-api/communities/lookup/introduction.mdx +++ b/x-api/communities/lookup/introduction.mdx @@ -1,88 +1,75 @@ --- -title: Introduction +title: Communities Lookup sidebarTitle: Introduction -keywords: ["communities lookup", "get communities", "community lookup", "community by ID", "community information", "communities API"] +description: Retrieve Community details by ID +keywords: ["communities lookup", "get communities", "community by ID", "community details", "communities API"] --- import { Button } from '/snippets/button.mdx'; - -**Account setup** - -To access these endpoints, you will need: - -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  - -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - -**Getting started** +The Communities lookup endpoint lets you retrieve information about X Communities by their ID. -### Authentication +## Overview -You can authenticate this endpoint with either [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 App-Only](https://developer.x.com(/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). -  +Communities are public or private groups on X where members can share Posts, discuss topics, and connect around shared interests. -### Making a request + + + Get details for a specific Community + + + Find Communities by keyword + + -You can call the Communities Lookup endpoint by providing an ID as shown below (using the ID `1758747817642700922`): - - ``` - curl --location 'https://api.x.com/2/communities/1758747817642700922' --header 'Authorization: ••••••' - ``` - -If the request is successful, you should see the JSON response as shown below: +--- -``` -{ - "data": { - "access": "Public", - "description": "Welcome to the Anime Community! Where anime fans gather to share their favorite shows and discuss everything anime-related.", - "join_policy": "Open", - "name": "Anime Community" - } -} -``` +## Endpoint -### Community fields +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/communities/:id`](/x-api/communities/get-community-by-id) | Get Community by ID | -You can fetch additional data associated with a community by specifying Community Fields. +--- -The following Community Fields are available: +## Response fields -| Field value | Type | Description | | -| :--- | :--- | :--- | :--- | -| created_at | date (ISO 8601) | Creation time of the Community. | | -| id | string | The unique identifier of the Community. | | -| name | string | The name of the Community. | | -| description | string | The text of the Community’s description, if provided. | | -| access | string | The access level of the Community.

Can be one of:

* `Public`
* `Closed` | | -| join_policy | string | The join policy for the Community.

Can be one of:

* `Open`
* `RestrictedJoinRequestsDisabled`
* `RestrictedJoinRequestsRequireAdminApproval`
* `RestrictedJoinRequestsRequireModeratorApproval`
* `SuperFollowRequired` | | -| member_count | integer | The number of members that have joined the Community. | | +| Field | Description | +|:------|:------------| +| `id` | Community ID | +| `name` | Community name | +| `description` | Community description | +| `created_at` | Creation date | +| `member_count` | Number of members | +| `is_private` | Whether Community is private | -#### Example Request +--- -You can call the Communities Lookup endpoint with additional community fields as shown below (using the ID `1758747817642700922`): +## Example request -``` -curl --location 'https://api.x.com/2/communities/1758747817642700922?community.fields=access,created_at,description,id,join_policy,member_count,name' --header 'Authorization: ••••••' +```bash +curl "https://api.x.com/2/communities/1234567890?\ +community.fields=name,description,member_count,created_at" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` +--- -**Example Response** +## Getting started -If the request is successful, you should see the JSON response as shown below: + +**Prerequisites** -``` -{ - "data": { - "description": "Welcome to the Anime Community! Where anime fans gather to share their favorite shows and discuss everything anime-related.", - "access": "Public", - "member_count": 40369, - "created_at": "2024-02-17T06:58:50.000Z", - "join_policy": "Open", - "name": "Anime Community", - "id": "Q29tbXVuaXR5OjE3NTg3NDc4MTc2NDI3MDA5MjI=" - } -} -``` +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + + + + + Find Communities by keyword + + + Full endpoint documentation + + diff --git a/x-api/communities/search/introduction.mdx b/x-api/communities/search/introduction.mdx index e03d626ce..4ba9b6e3d 100644 --- a/x-api/communities/search/introduction.mdx +++ b/x-api/communities/search/introduction.mdx @@ -1,131 +1,72 @@ --- -title: Introduction +title: Communities Search sidebarTitle: Introduction -keywords: ["communities search", "search communities", "find communities", "community search", "communities discovery", "search communities API"] +description: Search for Communities by keyword +keywords: ["communities search", "search communities", "find communities", "communities API"] --- import { Button } from '/snippets/button.mdx'; -This endpoint allows you to search and discover Communities based on keywords. +The Communities Search endpoint lets you search for Communities by keyword. Discover Communities around topics of interest. -The endpoint accepts one or more keywords as a query. By default, a request will return both public and closed Communities that match the specified query. - -**Account setup** - -To access these endpoints, you will need: - -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  - -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - -**Getting started** -### Authentication +## Overview -You can authenticate this endpoint with either [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 App-Only](https://developer.x.com(/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). -  + + + Search Communities by name and description + + + Find Communities to join + + -### Making a request - -You can call the Search Communities endpoint as shown below: - - ``` - curl --location 'https://api.x.com/2/communities/search?query=anime' --header 'Authorization: ••••••' - ``` +--- -If the request is successful, you should see the JSON response as shown below: +## Endpoint -``` -{ - "data": [ - { - "access": "Public", - "description": "Welcome to the Anime Community! Where anime fans gather to share their favorite shows and discuss everything anime-related.", - "join_policy": "Open", - "name": "Anime Community" - }, - { - "access": "Public", - "description": "Join and text about anime 🥰", - "join_policy": "Open", - "name": "Anime World 🌸" - }, - { - "access": "Public", - "description": "For all anime lovers and creators!", - "join_policy": "Open", - "name": "Anime" - }, - ], - "meta": { - "next_token": "7140dibdnow9c7btw481s8m561gat797rboud5r80xvzm" - } -} -``` +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/communities/search`](/x-api/communities/search-communities) | Search for Communities | -### Community fields - -You can fetch additional data associated with a community by specifying Community Fields. +--- -The following Community Fields are available: +## Parameters -| Field value | Type | Description | | -| :--- | :--- | :--- | :--- | -| created_at | date (ISO 8601) | Creation time of the Community. | | -| id | string | The unique identifier of the Community. | | -| name | string | The name of the Community. | | -| description | string | The text of the Community’s description, if provided. | | -| access | string | The access level of the Community.

Can be one of:

* `Public`
* `Closed` | | -| join_policy | string | The join policy for the Community.

Can be one of:

* `Open`
* `RestrictedJoinRequestsDisabled`
* `RestrictedJoinRequestsRequireAdminApproval`
* `RestrictedJoinRequestsRequireModeratorApproval`
* `SuperFollowRequired` | | -| member_count | integer | The number of members that have joined the Community. | | +| Parameter | Description | +|:----------|:------------| +| `query` | Search query (required) | +| `max_results` | Results per page (max 100) | +| `community.fields` | Additional Community fields | -#### Example Request +--- -You can call the Search Communities endpoint with additional community fields as shown below: +## Example request - ``` - curl --location 'https://api.x.com/2/communities/search?query=anime&community.fields=access,created_at,description,id,join_policy,member_count,name' --header 'Authorization: ••••••' - ``` +```bash +curl "https://api.x.com/2/communities/search?\ +query=Python&\ +max_results=10&\ +community.fields=name,description,member_count" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` +--- -**Example Response** +## Getting started -If the request is successful, you should see the JSON response as shown below: + +**Prerequisites** -``` -{ - "data": [ - { - "id": "Q29tbXVuaXR5OjE3NTg3NDc4MTc2NDI3MDA5MjI=", - "description": "Welcome to the Anime Community! Where anime fans gather to share their favorite shows and discuss everything anime-related.", - "join_policy": "Open", - "access": "Public", - "member_count": 39915, - "name": "Anime Community", - "created_at": "2024-02-17T06:58:50.000Z" - }, - { - "id": "Q29tbXVuaXR5OjE1MDY3OTM5NTMxMDYwNDI4OTE=", - "description": "Join and text about anime 🥰", - "join_policy": "Open", - "access": "Public", - "member_count": 26019, - "name": "Anime World 🌸", - "created_at": "2022-03-24T00:44:07.000Z" - }, - { - "id": "Q29tbXVuaXR5OjE0OTY3NzYyMTU5Mzk1MzQ4NDk=", - "description": "For all anime lovers and creators!", - "join_policy": "Open", - "access": "Public", - "member_count": 5612, - "name": "Anime", - "created_at": "2022-02-24T09:17:13.000Z" - } - ], - "meta": { - "next_token": "7140dibdnow9c7btw481s8m561gat797rboud5r80xvzm" - } -} -``` +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + + + + Look up Communities by ID + + + Full endpoint documentation + + diff --git a/x-api/community-notes/quickstart.mdx b/x-api/community-notes/quickstart.mdx index bfee4058f..9a2294b6c 100644 --- a/x-api/community-notes/quickstart.mdx +++ b/x-api/community-notes/quickstart.mdx @@ -1,154 +1,190 @@ -In this guide, we will walk you through using the new Community Notes endpoints using Python. - -In order to use the Community Notes endpoints, please ensure you have a [valid X Developers Account](https://developer.x.com/) and are enrolled as a [Community Notes AI Note Writer](https://communitynotes.x.com/guide/en/api/overview) in the Community Notes Guide). Once approved, please ensure you have the correct API keys and tokens to use in the examples listed below. Check out [this fundamentals section](https://docs.x.com/resources/fundamentals/developer-apps#keys-and-tokens) for instructions on obtaining your keys and tokens - please save your API key, API secret, access token and token secret as we will be using it in the examples in this guide. The Community Notes endpoints support OAuth 1.0 and 2.0 authentication. In this guide, we will be using OAuth 1.0. - -## Search for X Posts that are eligible to receive a Community Note +--- +title: Quickstart +sidebarTitle: Quickstart +description: Create and search Community Notes via the API +keywords: ["community notes quickstart", "community notes tutorial", "create notes guide"] +--- + +import { Button } from '/snippets/button.mdx'; + +This guide walks you through using the Community Notes API to search for eligible Posts and submit notes. + + +**Prerequisites** + +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Enrollment as a [Community Notes AI Note Writer](https://communitynotes.x.com/guide/en/api/overview) +- User Access Token (OAuth 1.0a) + + + +Currently, `test_mode` must be set to `true` for all requests. Test notes are not publicly visible. + + +--- + +## Find Posts eligible for notes + + + + + + ```bash + curl "https://api.x.com/2/notes/search/posts_eligible_for_notes?\ + test_mode=true&\ + max_results=100" \ + -H "Authorization: OAuth ..." + ``` + + + ```python + from requests_oauthlib import OAuth1Session + import json + + oauth = OAuth1Session( + client_key='YOUR_API_KEY', + client_secret='YOUR_API_SECRET', + resource_owner_key='YOUR_ACCESS_TOKEN', + resource_owner_secret='YOUR_ACCESS_TOKEN_SECRET', + ) + + url = "https://api.x.com/2/notes/search/posts_eligible_for_notes" + params = {"test_mode": True, "max_results": 100} + + response = oauth.get(url, params=params) + print(json.dumps(response.json(), indent=2)) + ``` + + + + + + ```json + { + "data": [ + { + "id": "1933207126262096118", + "text": "Join us to learn more about our new analytics endpoints...", + "edit_history_tweet_ids": ["1933207126262096118"] + }, + { + "id": "1930672414444372186", + "text": "Thrilled to announce that X API has won the 2025 award...", + "edit_history_tweet_ids": ["1930672414444372186"] + } + ], + "meta": { + "newest_id": "1933207126262096118", + "oldest_id": "1930672414444372186", + "result_count": 2 + } + } + ``` + + Use the Post `id` from the response to write a Community Note. + + + +--- + +## Submit a Community Note + + + + A Community Note requires: + - `post_id` — The Post you're adding context to + - `text` — Your note (1-280 characters, must include a source URL) + - `classification` — Either `misinformed_or_potentially_misleading` or `not_misleading` + - `misleading_tags` — Required if classification is misleading + - `trustworthy_sources` — Boolean indicating if source is trustworthy + + + + + + ```bash + curl -X POST "https://api.x.com/2/notes" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{ + "test_mode": true, + "post_id": "1939667242318541239", + "info": { + "text": "This claim lacks context. See the full report: https://example.com/report", + "classification": "misinformed_or_potentially_misleading", + "misleading_tags": ["missing_important_context"], + "trustworthy_sources": true + } + }' + ``` + + + ```python + from requests_oauthlib import OAuth1Session + import json + + oauth = OAuth1Session( + client_key='YOUR_API_KEY', + client_secret='YOUR_API_SECRET', + resource_owner_key='YOUR_ACCESS_TOKEN', + resource_owner_secret='YOUR_ACCESS_TOKEN_SECRET', + ) + + payload = { + "test_mode": True, + "post_id": "1939667242318541239", + "info": { + "text": "This claim lacks context. See the full report: https://example.com/report", + "classification": "misinformed_or_potentially_misleading", + "misleading_tags": ["missing_important_context"], + "trustworthy_sources": True, + } + } -Developers can retrieve a list of X Posts that are eligible to receive a Community Note using the `GET https://api.x.com/2/notes/search/posts_eligible_for_notes` endpoint. The endpoint requires you to specify a test_mode parameter and set it to true in order to use these endpoints. + response = oauth.post("https://api.x.com/2/notes", json=payload) + print(json.dumps(response.json(), indent=2)) + ``` + + + -**Note:** For now, test_mode can only be set to true, otherwise these endpoints will return an error like: + + ```json + { + "data": { + "note_id": "1938678124100886981" + } + } + ``` + + -```json -{ - "errors": [ - { - "message": "The `test_mode` query parameter is invalid." - } - ], - "title": "Invalid Request", - "detail": "One or more parameters to your request was invalid.", - "type": "https://api.twitter.com/2/problems/invalid-request" -} -``` +--- -You can specify the maximum number of Posts you want returned per request using the max_results parameter. By default, 10 Posts are returned and a maximum of 100 Posts can be retrieved per request. If you want additional results, pass in the pagination_token. +## Get your submitted notes -The code below will call the search endpoint that returns Posts that are eligible for Community Notes: +Retrieve notes you've written: ```python from requests_oauthlib import OAuth1Session import json -# API endpoint and parameters -url = "https://api.x.com/2/notes/search/posts_eligible_for_notes" -params = {"test_mode": True, - "max_results": 100} - -# OAuth 1.0 credentials oauth = OAuth1Session( - client_key='REPLACE_ME', - client_secret='REPLACE_ME', - resource_owner_key='REPLACE_ME', - resource_owner_secret='REPLACE_ME', + client_key='YOUR_API_KEY', + client_secret='YOUR_API_SECRET', + resource_owner_key='YOUR_ACCESS_TOKEN', + resource_owner_secret='YOUR_ACCESS_TOKEN_SECRET', ) -# Make the request -try: - response = oauth.get(url, params=params) - response.raise_for_status() # Raises an HTTPError for bad responses - - print("Response code: {}".format(response.status_code)) - json_response = response.json() - print(json.dumps(json_response, indent=4, sort_keys=True)) - -except Exception as e: - print(f"Request failed: {e}") -``` - -The response will be something like: - -```json -{ - "data": [ - { - "text": "Join us to learn more about our new analytics endpoints available in the X API v2 📊 https://t.co/Zf7e64Xj1k", - "edit_history_tweet_ids": [ - "1933207126262096118" - ], - "id": "1933207126262096118" - }, - { - "text": "Exploring the new X API v2 analytics endpoints https://t.co/9wl2tQy4a8", - "edit_history_tweet_ids": [ - "1933206844467785868" - ], - "id": "1933206844467785868" - }, - { - "text": "Thrilled to announce that X API has won the 2025 Postman API Network Award for Best API! Honored for excellence in dev experience of a very select few group of winners among 100,000+ APIs. Thank you, @getpostman, and our amazing dev community! https://t.co/BjMZrfAoQo", - "edit_history_tweet_ids": [ - "1930672414444372186" - ], - "id": "1930672414444372186" - } - ], - "meta": { - "newest_id": "1933207126262096118", - "oldest_id": "1930672414444372186", - "result_count": 3 - } -} -``` - -You can use the Post ID from the response above to write a Community Note. - -By default, you get Post id, text and edit history. If you want additional fields, you can use [fields](https://docs.x.com/x-api/fundamentals/fields) and [expansions](https://docs.x.com/x-api/fundamentals/expansions) - -## Search for Community Notes that have been written on X Posts - -Similarly, a developer can retrieve a list of Community Notes that have been written by the authenticating user using the GET https://api.x.com/2/notes/search/notes_written. This endpoint also requires the test_mode parameter. When test_mode is set to true: all test notes written by the user are returned. - -**Note:** For now, test_mode can only be set to true, otherwise these endpoints will return an error like: - -```json -{ - "errors":[ - { - "message":"The `test_mode` query parameter is invalid." - } - ], - "title":"Invalid Request", - "detail":"One or more parameters to your request was invalid.", - "type":"https://api.twitter.com/2/problems/invalid-request" -} -``` - -You can specify the maximum number of Notes you want returned per request using the max_results parameter. By default, 10 Notes are returned and a maximum of 100 Notes can be retrieved per request. If you want additional results, pass in the pagination_token. - -The code below will call the search endpoint that returns Notes that are written on X Posts: - -```python -from requests_oauthlib import OAuth1Session -import json - - -# API endpoint and parameters url = "https://api.x.com/2/notes/search/notes_written" -params = {"test_mode": True, - "max_results": 100, } - -# OAuth 1.0 credentials -oauth = OAuth1Session( - client_key='REPLACE_ME', - client_secret='REPLACE_ME', - resource_owner_key='REPLACE_ME', - resource_owner_secret='REPLACE_ME', -) - -# Make the request -try: - response = oauth.get(url, params=params) - response.raise_for_status() # Raises an HTTPError for bad responses - - print("Response code: {}".format(response.status_code)) - json_response = response.json() - print(json.dumps(json_response, indent=4, sort_keys=True)) +params = {"test_mode": True, "max_results": 100} -except Exception as e: - print(f"Request failed: {e}") +response = oauth.get(url, params=params) +print(json.dumps(response.json(), indent=2)) ``` -The response will be something like: +**Response:** ```json { @@ -156,183 +192,80 @@ The response will be something like: { "id": "1939827717186494817", "info": { - "text": "test note text. http://source.com", + "text": "This claim lacks context. https://example.com/report", "classification": "misinformed_or_potentially_misleading", - "misleading_tags": [ - "missing_important_context" - ], + "misleading_tags": ["missing_important_context"], "post_id": "1939719604957577716", "trustworthy_sources": true } - }, - { - "id": "1939827486533222881", - "info": { - "text": "test note tex 2t. http://source.com", - "classification": "misinformed_or_potentially_misleading", - "misleading_tags": [ - "missing_important_context" - ], - "post_id": "1939769235158237565", - "trustworthy_sources": true - } } ], "meta": { - "result_count": 2, - "next_token": "[token]" + "result_count": 1 } } ``` -## Manage Community Notes - -Developers can submit Community Notes on X Posts using the POST https://api.x.com/2/notes endpoint. Similar to the previous endpoints, this endpoint also supports the test_mode query parameter. When test_mode is set to true, the note being submitted is only for testing, and won't be publicly visible. - -**Note:** For now, test_mode can only be set to true, otherwise these endpoints will return an error like: - -```json -{ - "errors": [ - { - "message": "The `test_mode` query parameter is invalid." - } - ], - "title": "Invalid Request", - "detail": "One or more parameters to your request was invalid.", - "type": "https://api.twitter.com/2/problems/invalid-request" -} -``` - - -In order to do so, you will need to provide the *post_id* for which you want to submit the Community Note in the request body for this endpoint, along with the required information to create a note including: -* *text*: the text for the note, which should contain at least 1 and at most 280 characters (urls count as a single character) and must contain a source url -* *classification*: which can be either misinformed_or_potentially_misleading or not_misleading -* *misleading_tags*: which is a non-empty list of tags that is either "disputed_claim_as_fact", "factual_error", "manipulated_media", "misinterpreted_satire", "missing_important_context", "outdated_information" or "other". This field is only needed when the classification is of type misinformed_or_potentially_misleading. -* *trustworthy_sources*: which is a boolean value to indicate whether a trustworthy source is provided in “text”. - -Below is an example request to this endpoint - -```python -from requests_oauthlib import OAuth1Session -import json - - -# Replace with your Note information -payload = {"test_mode": True, - "post_id": "1939667242318541239" - , - "info": { - "text": "test note text. http://source.com", - "classification": "misinformed_or_potentially_misleading", - "misleading_tags": ["missing_important_context"], - "trustworthy_sources": True, - }} - - -# Make the request -oauth = OAuth1Session( - client_key='REPLACE_ME', - client_secret='REPLACE_ME', - resource_owner_key='REPLACE_ME', - resource_owner_secret='REPLACE_ME', -) - - -# Making the request -response = oauth.post( - "https://api.twitter.com/2/notes", - json=payload, -) - - -if response.status_code != 201: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - - -print("Response code: {}".format(response.status_code)) - - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) -``` - -If the request is successful, the response will look like this: - -```json -{ - "data": { - "note_id": "1938678124100886981" - } -} -``` - -## Error Troubleshooting - -Below is a list of common error messages and resolutions when working with the Community Notes endpoints: - -### 401 Unauthorized - -```json -{ - "title": "Unauthorized", - "type": "about:blank", - "status": 401, - "detail": "Unauthorized" -} -``` - -**Explanation**: This error is returned with the request is not properly authenticated - -### 403 Forbidden - -```json -{ - "detail": "User: [userId] must be an API Note Writer to access this endpoint.", - "type": "about:blank", - "title": "Forbidden", - "status": 403 -} -``` -**Explanation**: This error is returned when a user is not enrolled to use this endpoint - -### 400 Invalid Request (test_mode) - -```json -{ - "errors": [ - { - "message": "The `test_mode` query parameter is invalid." - } - ], - "title": "Invalid Request", - "detail": "One or more parameters to your request was invalid.", - "type": "https://api.twitter.com/2/problems/invalid-request" -} -``` - -**Explanation**: This endpoint is returned when a user tries to make request with test_mode set to false - -### 400 Invalid Request (Duplicate Note) - -```json -{ - "errors": [ - { - "message": "User already created a note: [noteId] for this post." - } - ], - "title": "Invalid Request", - "detail": "One or more parameters to your request was invalid.", - "type": "https://api.twitter.com/2/problems/invalid-request" -} -``` - -**Explanation**: This error is returned when a user tries to make duplicate Community Notes - -## Resources - -You can find code samples in other programming languages on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). You can also get started with these API endpoints using our [Postman collection](https://www.postman.com/xapidevelopers). If you have any technical questions about these endpoints, please feel free to reach out to us on the [X Developer Community support forums](https://devcommunity.x.com/). +--- + +## Classification options + + + + When classification is `misinformed_or_potentially_misleading`, include one or more tags: + + | Tag | Description | + |:----|:------------| + | `disputed_claim_as_fact` | Presents disputed claim as fact | + | `factual_error` | Contains factual errors | + | `manipulated_media` | Media has been altered | + | `misinterpreted_satire` | Satire taken out of context | + | `missing_important_context` | Lacks key context | + | `outdated_information` | Information is no longer current | + | `other` | Other reasons | + + + + When classification is `not_misleading`, no misleading tags are required. + + + +--- + +## Common errors + + + + ```json + {"title": "Unauthorized", "status": 401, "detail": "Unauthorized"} + ``` + **Resolution:** Check your OAuth credentials are correct. + + + + ```json + {"detail": "User must be an API Note Writer to access this endpoint."} + ``` + **Resolution:** Enroll as a [Community Notes AI Note Writer](https://communitynotes.x.com/guide/en/api/overview). + + + + ```json + {"message": "User already created a note for this post."} + ``` + **Resolution:** You can only submit one note per Post. + + + +--- + +## Next steps + + + + Official Community Notes documentation + + + Working code examples + + diff --git a/x-api/compliance/batch-compliance/introduction.mdx b/x-api/compliance/batch-compliance/introduction.mdx index 414a9c65e..a08d72e37 100644 --- a/x-api/compliance/batch-compliance/introduction.mdx +++ b/x-api/compliance/batch-compliance/introduction.mdx @@ -1,55 +1,116 @@ --- -title: Introduction +title: Batch Compliance sidebarTitle: Introduction -keywords: ["batch compliance", "compliance batch", "batch compliance API", "compliance jobs", "compliance data", "batch compliance check"] +description: Check compliance status for Posts and users in bulk +keywords: ["batch compliance", "compliance API", "GDPR", "data compliance", "bulk compliance"] --- import { Button } from '/snippets/button.mdx'; -X is committed to our community of developers who build with the X API. As part of this commitment, we aim to make our API open and fair to developers, safe for people on X, and beneficial for the X platform as a whole. It is crucial that any developer who stores X content offline, ensures the data reflects user intent and the current state of content on X. For example,when someone on X deletes a Post or their account, protects their Posts, or scrubs the geo information from their Posts, it is critical for both X and our developers to honor that person’s expectations and intent. The batch compliance endpoints provide developers an easy tool to help maintain X data in compliance with the [X Developer Agreement and Policy](https://developer.x.com/en/developer-terms/policy).  - -These batch compliance endpoints allow you to upload large datasets of Post or user IDs to retrieve their compliance status in order to determine what data requires action in order to bring your datasets into compliance. Please note, use of the batch compliance endpoints is restricted to aforementioned use cases, and any other purpose is prohibited and may result in enforcement action. - -Typically, there are 4 steps involved in working with this endpoint: - -1. **Create a compliance job** - You can specify the job type (with the value tweets or users to indicate whether the dataset you want to upload has Post IDs or user IDs. You can have one concurrent job per job type at any time. -2. **Upload your dataset to the upload_url** - Next, you upload your dataset as a plain text file to the provided upload_url, with each line of the file containing a single Post ID or user ID. The upload_url expires after 15 minutes. -3. **(Optional) Check the job status** - You can check the status of your compliance job to see whether it is created, in_progress, failed or complete. -4. **Download the results** - When your job is complete, you can download the results using the download_url. The download_url expires after one week (from when the job was created). - - This result will contain a set of JSON objects (one object per line). Each object will contain a Post ID, the Post's creation date (useful to locate Posts organized by date), required action, the reason for the compliance action, and the date the user was suspended. - -You will receive the following compliance event types in your results: - -* deleted \- indicates that the Post or User account was deleted -* deactivated \- indicates that the Post or User account has been deactivated -* scrub_geo \- indicates that the geo information associated with the Post or User was removed -* protected \- indicates the account that made the Post became private -* suspended \- indicates the account that made the Post was suspended - -**Account setup** - -To access these endpoints, you will need: - -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  - -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). -
- - - - -
+The Batch Compliance endpoints let you upload datasets of Post IDs or user IDs and receive their current compliance status. Use this to identify deleted Posts, suspended accounts, and other compliance events. + +## Overview + + + + Check status of Posts in bulk + + + Check status of users in bulk + + + Submit jobs and retrieve results later + + + Process millions of IDs + + + +--- + +## How it works + +1. **Create a job** — Specify the type (Posts or users) and upload URL +2. **Upload IDs** — Upload your dataset to the provided URL +3. **Wait for processing** — Job processes asynchronously +4. **Download results** — Get compliance status for each ID + +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | [`/2/compliance/jobs`](/x-api/compliance/create-compliance-job) | Create a new compliance job | +| GET | [`/2/compliance/jobs/:id`](/x-api/compliance/get-compliance-job-by-id) | Get job status | +| GET | [`/2/compliance/jobs`](/x-api/compliance/get-compliance-jobs) | List all jobs | + +--- + +## Job types + +| Type | Description | +|:-----|:------------| +| `tweets` | Check Post compliance status | +| `users` | Check user compliance status | + +--- + +## Compliance events + +### Post events + +| Event | Description | +|:------|:------------| +| `deleted` | Post was deleted by user | +| `bounced` | Post failed compliance check | +| `protected` | Account became protected | +| `suspended` | Account was suspended | +| `scrub_geo` | Geo data was removed | + +### User events + +| Event | Description | +|:------|:------------| +| `deleted` | Account was deleted | +| `suspended` | Account was suspended | +| `protected` | Account became protected | +| `deactivated` | Account was deactivated | + +--- + +## Example: Create a job + +```bash +curl -X POST "https://api.x.com/2/compliance/jobs" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "tweets", + "name": "my-compliance-job" + }' +``` + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [Bearer Token](/resources/fundamentals/authentication) + + + + + Create your first compliance job + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/compliance/batch-compliance/quickstart.mdx b/x-api/compliance/batch-compliance/quickstart.mdx index 99a85536b..213a89c5f 100644 --- a/x-api/compliance/batch-compliance/quickstart.mdx +++ b/x-api/compliance/batch-compliance/quickstart.mdx @@ -1,138 +1,189 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["batch compliance quickstart", "compliance batch quickstart", "compliance jobs quickstart", "batch compliance tutorial"] +description: Create and run a batch compliance job +keywords: ["compliance quickstart", "batch compliance tutorial", "compliance job guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the batch compliance endpoints +This guide walks you through creating a batch compliance job to check the compliance status of Posts or users. -Working with the batch compliance endpoints generally involves 5 steps: - -1. Creating a compliance job -2. Preparing the list of Post IDs or user IDs -3. Uploading the list of Post IDs or user IDs -4. Checking the status of the compliance job -5. Downloading the results - -In this section, we will learn how to go through each of these steps using the command line. If you would like to see sample code in different programming languages, please visit our [X API v2 sample code GitHub repository](https://github.com/xdevplatform/Twitter-API-v2-sample-code). - -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. - -#### Step one: Create a compliance job - -First, you will have to create a compliance job and specify whether you will be uploading Post IDs or user IDs (using the type parameter). Optionally, you can also give your job a name (using the name parameter).  - -To authorize this request, you will need to use [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token). To do so, make sure to replace the $APP\_ACCESS\_TOKEN below with your [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) that you can generate in your [X App](/resources/fundamentals/developer-apps) on the developer portal. - - `curl --request POST 'https://api.x.com/2/compliance/jobs' \ ---header 'Authorization: Bearer $APP_ACCESS_TOKEN' \ ---header 'Content-Type: application/json' \ ---data-raw '{"type": "tweets"}'` - -If your API call is successful, you will get a response similar to the following: - -``` -{ - "data": { - "download_expires_at": "2021-08-18T19:42:55.000Z", - "status": "created", - "upload_url": "https://storage.googleapis.com/twttr-tweet-compliance/1425543269983784962/submission/1202726487847104512_1425543269983784962?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210811%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210811T194255Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=355e4c4739ae508304d3df15b4e13e64b6c7752d8d79d73676a4d8e60dc5241f83924ad2a1f8b7bddcc768062bb9c64d39b8e8f7cce7f66ffbea9f9ed33a4da975b3a2c127fb738c1c1ff3c3964bd4d9dc0706e6c8a70e67522160ea774e090d2793e06f890d1158ce86be3031c1c471b74f961b6f18743a28730611000336286ad0111b41fb5d14aa813ff00cf06b3572dc68d0b3c6fdc07f25c1b1196c1af4325a9ead68994944bbef0d2123585ea051deb9765aa7f5832446440bc9ba76af327b69df1fd7b1a99bd4419c128f1f697dbbacbc62bbc7c2c9aebc82a2128be0ed05d48a54d814162daad1232a0d13081e9543ab8557f567149af82281193f37", - "created_at": "2021-08-11T19:42:55.000Z", - "resumable": false, - "id": "1425543269983784962", - "type": "tweets", - "download_url": "https://storage.googleapis.com/twttr-tweet-compliance/1425543269983784962/delivery/1202726487847104512_1425543269983784962?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210811%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210811T194255Z&X-Goog-Expires=604800&X-Goog-SignedHeaders=host&X-Goog-Signature=0a11dd5a3c5adb508f32ce904568abada863dc9499ba2adeafb3452ccee0dcb3dade17910dbc502dcbe54c130ac4d8638eb176c8b7344de068139b06c970794efa6312f0a5149f40da441eafcaf475f670c93ca73951999902a531d34dfab1e5490918929e5b06ae803b5604e0c0c26852255ccdbc79a2c1e2eefe924e5e6bf5b6603a7f287d1621333b9548ec6cc203716070528bebc2e67c12e92b1f4e54471db92c15a54799f2b855ae224250ca44c47993fd7d79a4940a0f68fe09f73fc8b291e88cfd10ade860b4b35c2b964d1777c1d93cd300c313138d9ca90aa8b3ecd3bf9dc73d3ebe32ba7634228fe07e1e4ecdda57cd94c802afc520162735d5a3", - "upload_expires_at": "2021-08-11T19:57:55.000Z" - } -} -``` - -Take note of the value from the upload_url, download_url, and id fields; you will need those in the following steps. - -#### Step two: Prepare a list of Post IDs or user IDs - -Create a text file with Post IDs or user IDs, where each line contains a Post ID or user ID. The contents of the text file can look something like this: - -1417856744319971329 - -1415457498803232770 - -1415348607813832708 - -1413515358766452738 - -. . . - -. . . -**Note**: The file above can either contain Post IDs or User IDs and can not be a mix of both. +**Prerequisites** -You can upload your file directly to this URL via a **PUT** request. Note that the URL is already signed with an authentication token, which means you will not authenticate by passing your App Access Token again. Make sure to pass a Content-Type header to signal you are uploading a text file and replace the $FILE_LOCATION below with the path to your file. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token -``` -curl -X PUT \ - -H "Content-Type: text/plain" \ - --data-binary @$FILE_LOCATION \ -"https://storage.googleapis.com/twitter-compliance/customer_test_object_123456_d8ske9.json?X-Goog-Algorithm=\nGOOG4-RSA-SHA256&X-Goog-Credential=example%40example-project.iam.gserviceaccount\n.com%2F20181026%2Fus-central-1%2Fstorage%2Fgoog4_request&X-Goog-Date=20181026T18\n1309Z&X-Goog-Expires=900&X-Goog-SignedHeaders=host&X-Goog-Signature=247a2aa45f16\n9edf4d187d54e7cc46e4731b1e6273242c4f4c39a1d2507a0e58706e25e3a85a7dbb891d62afa849\n6def8e260c1db863d9ace85ff0a184b894b117fe46d1225c82f2aa19efd52cf21d3e2022b3b868dc\nc1aca2741951ed5bf3bb25a34f5e9316a2841e8ff4c530b22ceaa1c5ce09c7cbb5732631510c2058\n0e61723f5594de3aea497f195456a2ff2bdd0d13bad47289d8611b6f9cfeef0c46c91a455b94e90a\n66924f722292d21e24d31dcfb38ce0c0f353ffa5a9756fc2a9f2b40bc2113206a81e324fc4fd6823\na29163fa845c8ae7eca1fcf6e5bb48b3200983c56c5ca81fffb151cca7402beddfc4a76b13344703\n2ea7abedc098d2eb14a7" -``` - -A status code 200 will indicate that the upload was successful. - -#### Step three (optional): Check your job status - -Large uploads may take some time to process. You can request a status update from the content compliance job endpoint by specifying the job ID you received in the first step. - -Again, make sure to replace the $APP\_ACCESS\_TOKEN below with your App Access Token, and replace the $ID with your job ID from the first step. - - `curl --request GET 'https://api.x.com/2/compliance/jobs/$ID' \ ---header 'Authorization: Bearer $APP_ACCESS_TOKEN'` - -The response will include information about the job. -``` -{ - "data": { - "upload_expires_at": "2021-08-05T01:50:11.000Z", - "type": "tweets", - "resumable": false, - "download_url": "https://storage.googleapis.com/twttr-tweet-compliance/1423095206576984067/delivery/1202726487847104512_1423095206576984067?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210805%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210805T013511Z&X-Goog-Expires=604800&X-Goog-SignedHeaders=host&X-Goog-Signature=09de4feae68a6d4449eb7ce1f8f3551996552e7fba103005b3bd50ab318bb5215e4f5396ef29d17755deb6bf172b9d1dab61a04b249d39e87f6e2dbb31632b7e5f2d35f4f534e1f1522c9d7958b8745dd62471deb8d6992c80fd418628404f5f14eda3f557adf709403058910ea009e0c88ce81458ec9b915016a5c5901e2365b130db00b18fcb7da1b082e1a5c75f7bf7eeab8783675d1b6a56441ac6e9ffc972b1278a5853d2b94dda55e1a6e2068bc0ddd3cddc9213ec9cebb7cb5be931977bb28dda12c7c5e69d1f876b243f0f224076bf1b81149603319a2fc9cb82337bdbe05e7bbf184bcbdc17d43b3f5efbae72ea386d955ca10e702e00df31aabf32", - "id": "1423095206576984067", - "status": "expired", - "created_at": "2021-08-05T01:35:11.000Z", - "upload_url": "https://storage.googleapis.com/twttr-tweet-compliance/1423095206576984067/submission/1202726487847104512_1423095206576984067?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210805%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210805T013511Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=ba08f588bea3873aa0465cf22015e583c2851a5ff14891d22430b1127288728f1aa303673e6895694e7017739871ff5ae59bbcde7d4ac7a14aaaafba98ad22ca818e99fb3ec7eaaf74b3ecfecbfb33711869b2e85d7666609276666ef4a8b396ae9616743a0cbd773962e5850f2942cd76be7373d608a140e041ca8492017d43fac9220fa145d0b2ecaf9f752d71fc8c4b81b67c5c22aa59ac87666f7d83714fdace72894d2911a3e36dd42028d0222e71054d6b28c8ef63d0f0000f228c8680bab9c8011b87d1a6c9a60e8cc9e8b6a83abf7c47a57772746c83b19849f5b4c938ccd0922990da5f2a81ff806edcb4667bb402fb1f1f6f5162768e0661648b21", - "download_expires_at": "2021-08-12T01:35:11.000Z" - } -} -``` +--- -A complete status means the results are ready for you to download. Note: The other values of statuses can be created, complete, in_progress, failed and expired + + + Create a new compliance job specifying the type (tweets or users): + + + + ```bash + curl -X POST "https://api.x.com/2/compliance/jobs" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "tweets", + "name": "my-compliance-job" + }' + ``` + + + ```python + import requests + + bearer_token = "YOUR_BEARER_TOKEN" + + url = "https://api.x.com/2/compliance/jobs" + headers = { + "Authorization": f"Bearer {bearer_token}", + "Content-Type": "application/json" + } + payload = { + "type": "tweets", + "name": "my-compliance-job" + } + + response = requests.post(url, headers=headers, json=payload) + print(response.json()) + ``` + + + + **Response:** + + ```json + { + "data": { + "id": "1234567890", + "type": "tweets", + "name": "my-compliance-job", + "status": "created", + "upload_url": "https://storage.googleapis.com/...", + "download_url": "https://storage.googleapis.com/...", + "created_at": "2024-01-15T10:00:00.000Z" + } + } + ``` + + Save the `upload_url` and `download_url` for the next steps. + + + + Create a text file with one ID per line: + + ``` + 1234567890 + 1234567891 + 1234567892 + 1234567893 + ``` + + Save as `ids.txt`. + + + + Upload the file to the provided `upload_url`: + + ```bash + curl -X PUT "UPLOAD_URL_FROM_RESPONSE" \ + -H "Content-Type: text/plain" \ + --data-binary @ids.txt + ``` + + + + Poll the job status until it's complete: + + ```bash + curl "https://api.x.com/2/compliance/jobs/1234567890" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + **Job statuses:** + + | Status | Description | + |:-------|:------------| + | `created` | Job created, awaiting upload | + | `in_progress` | Processing data | + | `complete` | Results ready for download | + | `failed` | Job failed | + | `expired` | Job expired before completion | + + + + Once status is `complete`, download from the `download_url`: + + ```bash + curl "DOWNLOAD_URL_FROM_RESPONSE" -o results.json + ``` + + **Result format** (one JSON object per line): + + ```json + {"id": "1234567890", "action": "delete", "created_at": "2024-01-10T12:00:00.000Z", "redacted_at": "2024-01-12T08:30:00.000Z", "reason": "deleted"} + {"id": "1234567891", "action": "delete", "created_at": "2024-01-10T12:00:00.000Z", "redacted_at": "2024-01-13T14:20:00.000Z", "reason": "suspended"} + ``` + + Only IDs with compliance events appear in the results. IDs not in the results are still valid. + + -#### Step four: Download the results +--- -As you receive a job completion status, you can download the compliance results from the URL indicated in the download_url field (also generated in the first step). This URL is already signed with an authentication token, which means you will not need authenticate by passing your App Access Token again. +## Compliance actions + + + + | Action | Reason | Description | + |:-------|:-------|:------------| + | `delete` | `deleted` | Post was deleted | + | `delete` | `bounced` | Post failed compliance check | + | `delete` | `protected` | Account became protected | + | `delete` | `suspended` | Account was suspended | + | `delete` | `scrub_geo` | Geo data was removed | + + + + | Action | Reason | Description | + |:-------|:-------|:------------| + | `delete` | `deleted` | Account was deleted | + | `delete` | `suspended` | Account was suspended | + | `delete` | `protected` | Account became protected | + | `delete` | `deactivated` | Account was deactivated | + + -``` -curl --request GET \ -'https://storage.googleapis.com/twttr-tweet-compliance/1423047488781488129/delivery/1202726487847104512_1423047488781488129?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210804%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210804T222534Z&X-Goog-Expires=604800&X-Goog-SignedHeaders=host&X-Goog-Signature=8ed0fe9e9748f6be5e7e052f6635c8b5cbe62fb2d3a165278b922b28a48fb79b02d74f0bd31b4fdb32532bc6746c6082aaff2154cdab4b59c4c6561ff2c840e5f32dd13c09ff5b52376dfac1b7f97807c72209d2844a6c078b71fddf22a5493f88118802e98a60e16ce5715fce0242baddd17d4598d31be59393e1dacd22fc1eeb532572cc4e784402c5fbeb84a22dd308922e937a26fa99cb717bb26fb61b657403010121a996691814b7aeb00bc05ed25f15d394fd46899dd9390be6d5da44960e81d8018318c325c70b39d0a4fc9d65fea2b8b3355d4c7dd7c386eac1d9c09233462bde40fa3f4023d1cd6470b0346f9f36d74665dde3f716940312019703' -``` +--- +## List all jobs -The result will contain a set of JSON objects (one object per line). Each object will contain a Post or user ID, the Post or user’s creation date (useful to locate Posts organized by date), required action, the reason for the compliance action, and its date: +Get all compliance jobs for your App: -``` -{"id":"1265324480517361664","action":"delete","created_at":"2019-10-29T17:02:47.000Z","redacted_at":"2020-07-29T17:02:47.000Z","reason":"deleted"} -{"id":"1263926741774581761","action":"delete","created_at":"2019-10-29T17:02:47.000Z","redacted_at":"2020-07-29T17:02:47.000Z","reason":"protected"} -{"id":"1265324480517361669","action":"delete","created_at":"2019-10-29T17:02:47.000Z","redacted_at":"2020-07-29T17:02:47.000Z","reason":"suspended"} +```bash +curl "https://api.x.com/2/compliance/jobs?type=tweets" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` -Your code can parse each JSON line to locate the Post or user ID and delete the Posts and users with those IDs from your dataset to stay in compliance. If there is no corresponding JSON object for an ID you uploaded, you can assume that ID is in compliance. +--- -**Note**: Not all compliance events will include the redacted_atfield. +## Next steps + + + + Key concepts and best practices + + + Real-time compliance events + + + Full endpoint documentation + + diff --git a/x-api/compliance/streams/introduction.mdx b/x-api/compliance/streams/introduction.mdx index 932deb40e..458508625 100644 --- a/x-api/compliance/streams/introduction.mdx +++ b/x-api/compliance/streams/introduction.mdx @@ -48,7 +48,7 @@ These streams provide the following events:  To access these endpoints, you will need: * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access).
diff --git a/x-api/direct-messages/lookup/integrate.mdx b/x-api/direct-messages/lookup/integrate.mdx index 90b16e1fe..27a326dd5 100644 --- a/x-api/direct-messages/lookup/integrate.mdx +++ b/x-api/direct-messages/lookup/integrate.mdx @@ -1,184 +1,272 @@ --- -title: Integration guide -sidebarTitle: Integration guide -keywords: ["direct messages integration", "DM integration", "DM lookup integration", "direct message integration guide", "DM setup"] +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for integrating DM lookup endpoints +keywords: ["direct messages integration", "DM integration", "DM lookup integration"] --- import { Button } from '/snippets/button.mdx'; -The Direct Messages endpoints v2 introduce conversations and conversation events as core X API objects, and make a distinction between 1-1 and group conversations. 1-1 conversations always have two, and only two, participants, while group conversations can have two or more and memberships that can change.    +This guide covers the key concepts you need to integrate the Direct Messages lookup endpoints into your application. -This page contains information on several tools and key concepts that you should be aware of as you integrate the Direct Messages lookup endpoints into your system. We’ve broken the page into two sections: - -* Key Concepts - * [Direct Message conversations](#direct-message-conversations) - * [Shared conversation and event IDs across v1.1 and v2](#shared-conversation-and-event-ids) - * [Direct Message event fields and expansions](#direct-message-event-fields-and-expansions) - * [Conversation event types](#conversation-event-types) - * [Authentication](#authentication) - * [Developer portal, Projects, and Apps](#developer-portal-projects-and-apps) - * [Rate limits](#rate-limits) - * [Pagination](#pagination) -* [Helpful tools](#helpful-tools) - -### Key Concepts - -### Direct Message conversations - -All Direct Messages are part of a Direct Message conversation. These conversations can be one-to-one conversations or group conversations. This launch provides the foundational endpoints needed to create Direct Messages and retrieve events associated with Direct Message conversations. All conversations have a unique dm\_conversation\_id, and the events that make up that conversation all have a unique dm\_event\_id.   - -The Direct Message lookup endpoints provide methods for retrieving events associated with conversations. These GET endpoints are used to retrieve the messages that make up a conversation, and for group conversations, can be used to understand who has joined and left group conversations. - -This initial release of Direct Messages lookup includes three GET methods: - -* **GET /2/dm\_conversations/with/:participant\_id/dm_events** \- Retrieves Direct Message events associated with a one-to-one conversation. The :participant_id path parameter is the numeric User ID of the account having the conversation with the authenticated user making this request.   - -* **GET /2/dm\_conversations/:dm\_conversation\_id/dm\_events** \- Retrieves Direct Message events associated with a specific conversation ID, as indicated by the :dm\_conversation\_id path parameter. Both one-to-one and group conversations IDs are supported.   - -* **GET /2/dm_events** \- Retrieves Direct Message events associated with the authenticating user, including both one-to-one and group conversations. Events from up to 30 days ago are available.   - - -These GET endpoints all support retrieving dm_events by event type with an event_types request query parameters. Currently, there are three conversation event types supported: - -* **MessageCreate** \- Created when a new Direct Message is created. This event object can include the time and text of the message, along with the account ID of who sent the message, and the conversation and event IDs.  - -* **ParticipantsJoin** \- Created when a new participant joins a group conversation. This dm_event object includes the ID of the participant joining, along with the created\_at time and the sender\_id of the 'invite' event.  - -* **ParticipantsLeave** \- Created when a participant leaves a conversation.This event object includes the ID of the participant leaving, along with the time of the event.  - - -For more information see the [Direct Messages lookup API References](/x-api/direct-messages/get-dm-events-for-a-dm-conversation). - -### Shared conversation and event IDs across v1.1 and v2 - -An important concept is that conversation and event IDs are shared across v1.1 and v2 versions of the X Platform. This means both versions can be used together. For example, the Direct Messages v1.1 endpoints provide methods for returning a single event and for deleting events, methods not yet available with v2. Since IDs are common across v1.1 and v2, you can make v1.1 requests based on IDs provided by v2, or by referencing conversation IDs displayed in conversation URLs on the X application.   - -### Direct Message event fields and expansions - -The X API v2 allows users to select exactly which data they want to return from the API using a set of tools called fields and expansions. For example, Direct Message lookup endpoints support the following dm_events fields:  - -* id, event_type, and text are the defaults for MessageCreate events.  - -* id, event_type, and participant_ids are the defaults for ParticipantsJoin and ParticipantsLeave events. - -* dm\_conversation\_id and created_at are available for all events. - -* attachments and referenced_tweets are available for MessageCreate events.  - -* sender_id is available for MessageCreate and ParticipantsJoin events.  - -* participant_ids is available for ParticipantsJoin and ParticipantsLeave events.  - - -In addition, the Direct Message lookup endpoints support the following [expansions](/x-api/fundamentals/expansions): - -* sender_id \- Expands the User object associated with who sent the message or who invited someone to the conversation.  - -* referenced_tweets.id \- Expands the Post object if the Direct Message text includes a link to a Post.  - -* attachments.media_keys \- Expands the Media object if the Direct Message includes a media attachment.  - -* participant_ids \- Expands the User object associated with who joined or left a group conversation. - - -Since expansion include Posts, Users, and Media objects, you can also use the tweet.fields, user.fields, and media.fields request query parameters. See our guide on how to [use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) for more information. +--- -### Conversation event types +## Authentication -Below are example JSON objects for Direct Message the MessageCreate, ParticipantsJoin, and ParticipantsLeave event types.  +DM endpoints require user authentication to access private conversations: -Available dm_event object fields: id, text, event_type, dm\_conversation\_id, created_at, sender_id, attachments, referenced_tweets, participant_ids. See the the Fields and Expansion section for more details on selecting these fields in your requests.  +| Method | Description | +|:-------|:------------| +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | -Example MessageCreate event:  + +App-Only authentication is not supported. All Direct Messages are private. + -With all the dm_event fields specified, here is the response for a simple text Direct Message:  +### Required scopes (OAuth 2.0) - `{ - "text": "Hi everyone.", - "sender_id": "944480690", - "dm_conversation_id": "1578398451921985538", - "id": "1582838499983564806", - "event_type": "MessageCreate", - "created_at": "2022-10-19T20:58:00.000Z" -}` +| Scope | Required for | +|:------|:-------------| +| `dm.read` | Reading DM events | +| `tweet.read` | Required with dm.read | +| `users.read` | Required with dm.read | +--- -Example ParticipantsJoin event: +## Conversation types -With all the dm_event fields specified, here is the response for a participant joining a conversation: + + + Always has exactly two participants. Conversation ID format: `{smaller_user_id}-{larger_user_id}` + + + Two or more participants. Membership can change over time. + + - `{ - "participant_ids": [ - "944480690" - ], - "sender_id": "17200003", - "dm_conversation_id": "1578398451921985538", - "id": "1582835469712138240", - "event_type": "ParticipantsJoin", - "created_at": "2022-10-19T20:45:58.000Z" -}` +--- +## Event types + +| Event | Description | Key fields | +|:------|:------------|:-----------| +| `MessageCreate` | A message was sent | `text`, `sender_id` | +| `ParticipantsJoin` | User joined group | `participant_ids`, `sender_id` | +| `ParticipantsLeave` | User left group | `participant_ids` | + +### Example events + + + +```json +{ + "id": "1582838499983564806", + "event_type": "MessageCreate", + "text": "Hi everyone.", + "sender_id": "944480690", + "dm_conversation_id": "1578398451921985538", + "created_at": "2022-10-19T20:58:00.000Z" +} +``` + + + +```json +{ + "id": "1582835469712138240", + "event_type": "ParticipantsJoin", + "participant_ids": ["944480690"], + "sender_id": "17200003", + "dm_conversation_id": "1578398451921985538", + "created_at": "2022-10-19T20:45:58.000Z" +} +``` + + + +```json +{ + "id": "1582838535115067392", + "event_type": "ParticipantsLeave", + "participant_ids": ["944480690"], + "dm_conversation_id": "1578398451921985538", + "created_at": "2022-10-19T20:58:09.000Z" +} +``` + + -Example ParticipantsLeave event: +--- -With all the dm_event fields specified, here is the response for a participant leaving a conversation: +## Fields and expansions + +### Default fields + +| Event type | Default fields | +|:-----------|:---------------| +| MessageCreate | `id`, `event_type`, `text` | +| ParticipantsJoin/Leave | `id`, `event_type`, `participant_ids` | + +### Available fields + +| Field | Description | Events | +|:------|:------------|:-------| +| `dm_conversation_id` | Conversation ID | All | +| `created_at` | Event timestamp | All | +| `sender_id` | Who sent/invited | MessageCreate, Join | +| `attachments` | Media attachments | MessageCreate | +| `referenced_tweets` | Shared Posts | MessageCreate | + +### Available expansions + +| Expansion | Returns | +|:----------|:--------| +| `sender_id` | User object for sender | +| `participant_ids` | User objects for participants | +| `attachments.media_keys` | Media objects | +| `referenced_tweets.id` | Post objects | + +### Example with expansions + + + +```bash cURL +curl "https://api.x.com/2/dm_events?\ +dm_event.fields=created_at,sender_id,attachments&\ +expansions=sender_id,attachments.media_keys&\ +user.fields=username,profile_image_url&\ +media.fields=url,type" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get DM events with expansions +for page in client.dm_events.list( + dm_event_fields=["created_at", "sender_id", "attachments"], + expansions=["sender_id", "attachments.media_keys"], + user_fields=["username", "profile_image_url"], + media_fields=["url", "type"], + max_results=100 +): + for event in page.data: + print(f"Event: {event.event_type} - {event.text}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +const paginator = client.dmEvents.list({ + dmEventFields: ["created_at", "sender_id", "attachments"], + expansions: ["sender_id", "attachments.media_keys"], + userFields: ["username", "profile_image_url"], + mediaFields: ["url", "type"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((event) => { + console.log(`Event: ${event.event_type} - ${event.text}`); + }); +} +``` + + - `{ - "participant_ids": [ - "944480690" - ], - "dm_conversation_id": "1578398451921985538", - "id": "1582838535115067392", - "event_type": "ParticipantsLeave", - "created_at": "2022-10-19T20:58:09.000Z" - }` +--- +## Pagination -### Authentication +DM events are returned in reverse chronological order (newest first): -All X API v2 endpoints require for you to authenticate your requests with a set of credentials, also known as keys and tokens. All Direct Messages are private and require user authorization to access them.  + -These Direct Message endpoints require the use of [OAuth 2.0 Authorization Flow with PKCE](/x-api/posts/manage-tweets) or [1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are requesting on behalf of. If you want to generate a set of Access Tokens for another user, they must authorize or authenticate your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). +```bash cURL +# First request +curl "https://api.x.com/2/dm_events?max_results=100" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" -Please note that OAuth user-context can be tricky to use. If you are not familiar with this authentication method, we recommend using a [library](/x-api/tools-and-libraries/overview) or a tool like Postman to properly authenticate your requests.  +# Subsequent request with pagination token +curl "https://api.x.com/2/dm_events?max_results=100&pagination_token=NEXT_TOKEN" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -[OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. The Direct Messages lookup endpoints require these scopes:  dm.read, post.read, user.read +```python Python SDK +from xdk import Client -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -### Developer portal, Projects, and developer Apps +# The SDK handles pagination automatically +all_events = [] -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have an approved developer account, set up a Project within that account, and create a developer App within that Project. You can then find your keys and tokens within your developer App.  +for page in client.dm_events.list(max_results=100): + if page.data: + all_events.extend(page.data) -### Rate limits +print(f"Found {len(all_events)} DM events") +``` -Everyday many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, rate limits are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user.  +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -The Direct Message lookup endpoints are rate limited at the user-level, meaning that the authenticated user that you are making the request on behalf of can only make a certain number of requests with your X App. There is a user rate limit of 300 requests per 15 minutes for the GET methods. These rate limits are shared across the GET endpoints.  +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -### Pagination +async function getAllDMEvents() { + const allEvents = []; -These endpoints utilize pagination so that responses are returned quickly. In cases where there are more results than what can be sent in a single response (up to 100 events) you will need to paginate. Use the max_results parameter to identify how many results will return per page, and the pagination_token parameter to return the next page of results. You can learn more by reviewing our [pagination guide](/x-api/fundamentals/pagination). + // The SDK handles pagination automatically + const paginator = client.dmEvents.list({ maxResults: 100 }); -**Helpful tools** + for await (const page of paginator) { + if (page.data) { + allEvents.push(...page.data); + } + } -Here are some helpful tools we encourage you to explore as you work with the Direct Messages lookup endpoints:  + return allEvents; +} -****Postman**** +// Usage +const events = await getAllDMEvents(); +console.log(`Found ${events.length} DM events`); +``` -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) page.  + -****Code samples**** + +Events from up to **30 days ago** are available. + -Python sample code for the v2 Direct Messages endpoints is available in our [X API v2 sample code GitHub](https://github.com/xdevplatform/Twitter-API-v2-sample-code) repository. The "Manage-Direct-Messages" folder contains examples for the POST methods, and the "Direct-Messages-lookup" folder contains examples for the GET methods. +--- -****XDev Software Development Kits (SDKs)**** +## ID compatibility with v1.1 -These [libraries](/x-api/tools-and-libraries/overview) are being updated for the v2 Direct Messages endpoints and should be ready soon: +Conversation and event IDs are shared between v1.1 and v2 endpoints. This means you can: -* [X API Java SDK](https://github.com/xdevplatform/twitter-api-java-sdk) \- Official Java SDK for the X API v2 -* [X API TypeScript/JavaScript SDK](https://github.com/xdevplatform/twitter-api-typescript-sdk) \- Official TS/JS SDK for the X API v2 +- Use v2 to retrieve events, then use v1.1 to delete specific messages +- Reference conversation IDs from x.com URLs in API requests -**Third-party libraries** +--- -There is a growing number of [third-party libraries](/x-api/tools-and-libraries/overview#community-tools-and-libraries-for-v2) developed by our community. These libraries are designed to help you get started, and several are expected to support v2 Direct Messages endpoints soon. You can find a library that works with the v2 endpoints by looking for the proper version tag. +## Next steps + + + + Make your first DM lookup request + + + Send Direct Messages + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/direct-messages/lookup/introduction.mdx b/x-api/direct-messages/lookup/introduction.mdx index 683e5f3bf..a8b4cdb12 100644 --- a/x-api/direct-messages/lookup/introduction.mdx +++ b/x-api/direct-messages/lookup/introduction.mdx @@ -1,49 +1,82 @@ --- -title: Introduction +title: Direct Messages Lookup sidebarTitle: Introduction +description: Retrieve Direct Message events and conversations keywords: ["direct messages lookup", "DM lookup", "get DMs", "direct message lookup", "DM API", "messages lookup"] --- import { Button } from '/snippets/button.mdx'; -This initial release of Direct Messages lookup includes three GET methods: +The Direct Messages lookup endpoints let you retrieve DM events for the authenticated user, including messages from both one-to-one and group conversations. -* **GET /2/dm\_conversations/with/:participant\_id/dm_events** \- Retrieves Direct Message events associated with a one-to-one conversation. The :participant_id path parameter is the User ID of the account having the conversation with the authenticated user making this request.  -* **GET /2/dm\_conversations/:dm\_conversation\_id/dm\_events** \- Retrieves Direct Message events associated with a specific conversation ID, as indicated by the :dm\_conversation\_id path parameter.  -* **GET /2/dm_events** \- Retrieves Direct Message events associated with a user, including both one-to-one and group conversations. Events from up to 30 days ago are available.   +## Overview -Note that Direct Message event IDs are common across the v1.1 and v2 (as well as the X App), so the v1.1 method to list a single event can be used along with these new v2 endpoints. Also note that the Enterprise and Premium Account Activity APIs support v2 one-to-one messages, but do not yet support group conversations.    + + + Get all DM events for the user + + + Get events from a specific conversation + + + Get events by conversation ID + + + Messages, joins, and leaves + + -With this release, three event types are supported, and these endpoints support query parameters to filter on them: +--- + +## Endpoints -* **MessageCreate** \- A message has been created.  -* **ParticipantsJoin** \- A new participant has joined a conversation.  -* **ParticipantsLeave** \- A participant has left a conversation.  +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/dm_events`](/x-api/direct-messages/get-dm-events) | Get all DM events for the user | +| GET | [`/2/dm_conversations/with/:participant_id/dm_events`](/x-api/direct-messages/get-dm-events-for-a-dm-conversation) | Get events from one-to-one conversation | +| GET | [`/2/dm_conversations/:dm_conversation_id/dm_events`](/x-api/direct-messages/get-dm-events-for-a-dm-conversation-1) | Get events by conversation ID | -There is a user rate limit of 300 requests per 15 minutes for the GET methods. This rate limit is shared across these GET endpoints. +--- -Since you are making requests on behalf of a user with Direct Message v2 endpoints, you must authenticate with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3), using Access Tokens associated with users that have authorized your X App. To generate these Access Tokens with OAuth 1.0a, you can use the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). To generate user Access Tokens with OAuth 2.0, you can use the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +## Event types -**Account setup** +| Event | Description | +|:------|:------------| +| `MessageCreate` | A message was sent in the conversation | +| `ParticipantsJoin` | A user joined the conversation | +| `ParticipantsLeave` | A user left the conversation | + +--- + +## Data retention + + +Events from up to **30 days ago** are available through these endpoints. + + +--- -To access these endpoints, you will need: +## Getting started -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  + +**Prerequisites** -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [3-legged OAuth](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) + -
- - - - -
\ No newline at end of file + + + Make your first DM lookup request + + + Key concepts and best practices + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/direct-messages/lookup/migrate.mdx b/x-api/direct-messages/lookup/migrate.mdx index 8e4ed3e77..4f237a93a 100644 --- a/x-api/direct-messages/lookup/migrate.mdx +++ b/x-api/direct-messages/lookup/migrate.mdx @@ -42,7 +42,7 @@ The following table compares fundamental aspects of the v1.1 and v2 Direct Messa | Supports Group Direct Messages | | ✔ | | Event types supported | message_create | MessageCreate, ParticipantsJoin, ParticipantsLeave | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2 User Context (scopes: dm.read, tweet.read, user.read) | -| Requires the use of credentials from a [developer App](/resources/fundamentals/authentication) associated with a X API v2 [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/authentication) associated with a X API v2 [Project](/resources/fundamentals/developer-apps) | | ✔ | | Default request [rate limits](/x-api/fundamentals/rate-limits)*
*All requests require user tokens | | GET requests: 300 requests per 15 mins

Rate limit is applied across all three endpoints | The following tables compare the v2 GET methods with version v1.1. Note that these v2 offerings expand the available capabilities by supporting group conversations.  diff --git a/x-api/direct-messages/lookup/quickstart.mdx b/x-api/direct-messages/lookup/quickstart.mdx index 271ca3831..822328a43 100644 --- a/x-api/direct-messages/lookup/quickstart.mdx +++ b/x-api/direct-messages/lookup/quickstart.mdx @@ -1,163 +1,346 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["direct messages quickstart", "DM quickstart", "DM lookup quickstart", "direct message tutorial", "DM tutorial"] +description: Retrieve Direct Message events and conversations +keywords: ["DM lookup quickstart", "direct messages quickstart", "get DMs tutorial", "DM lookup guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the manage Direct Message endpoints +This guide walks you through retrieving Direct Message events for the authenticated user. -This quick start guide will help you make your first request to the Direct Message endpoints using Postman, a tool for managing and making HTTP requests. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) guide. - -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to review Python-based examples. In addition, the official [X Developer Platform software development kits (SDKs)](/x-api/tools-and-libraries/sdks) will be updated to support these Direct Message endpoints.   **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to building Direct Message lookup requests -In this example, in one request, we'll create a new group conversation and add our first message to it. We'll then add a second message to the created conversation. +--- -#### Step one: Start with a tool or library +## Get all DM events -To begin working with the manage Direct Message endpoints we are going to use the Postman tool to simplify the process. A XDevelopers-authored collection of example X API v2 requests will be used to explore six endpoints used to create new Direct Messages and to list Direct Message conversation events. +Retrieve all DM events for the authenticated user: -While most of the collection is pre-filled, there are a few details that you'll need to provide that are based on the X App created to host these API requests. First, let's get the collection loaded/updated. + -To load X API v2 Postman collection into your environment, please click on the following button: +```bash cURL +curl "https://api.x.com/2/dm_events?\ +dm_event.fields=created_at,sender_id,text&\ +max_results=100" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get all DM events with pagination +for page in client.dm_events.list( + dm_event_fields=["created_at", "sender_id", "text"], + max_results=100 +): + for event in page.data: + print(f"{event.event_type}: {event.text}") +``` - +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -Once you have the X API v2 collection loaded in Postman, navigate to the “Manage Direct Messages” folder. This folder's Authorization tab has been pre-filled where possible. You will need to update a few settings to share your X App's authentication details. +// Get all DM events with pagination +const paginator = client.dmEvents.list({ + dmEventFields: ["created_at", "sender_id", "text"], + maxResults: 100, +}); -This folder also contains three endpoints for creating new Direct Messages. Note that there is also a "Direct Message lookup" folder with three available endpoints for retrieving Direct Message conversation events, including sending and receiving messages, and when conversation participants join and leave. +for await (const page of paginator) { + page.data?.forEach((event) => { + console.log(`${event.event_type}: ${event.text}`); + }); +} +``` -Since creating group conversations is a new feature of the X API v2, this example will focus on that. We will be working with the "New group DM and conversation" example. We will use this request to create a Direct Message group conversation. + -The next step is to authenticate with the endpoint. +### Response -#### Step two: Authenticate your request +```json +{ + "data": [ + { + "id": "1234567890", + "event_type": "MessageCreate", + "text": "Hello! How are you?", + "sender_id": "9876543210", + "created_at": "2024-01-15T10:30:00.000Z" + } + ], + "meta": { + "result_count": 1, + "next_token": "abc123" + } +} +``` -To properly make a request to the X API, you need to verify that you have permission to do so. To make a successful request to this endpoint, we will be using [OAuth 2.0 Authorization Code Flow with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). You can generate an access token within Postman.  +--- -With Postman you can set the authentication method at the folder level or at the request level. Here we will be configuring the authentication details at the folder level. Navigate to the "Mange Direct Messages" folder, select the "Authorization" tab and confirm that the Type to set to “OAuth 2.0”, and "Add auth data to" is set to "Request Headers." In the "Current Token" section, make sure the "header Prefix" is set to Bearer.   +## Get one-to-one conversation -To configure and generate a new token: +Retrieve DM events from a specific one-to-one conversation: -1. Create a Token Name, such as "DM lookup." + -2. Confirm that **Grant Type** is set to Authorization Code (with PKCE). +```bash cURL +curl "https://api.x.com/2/dm_conversations/with/9876543210/dm_events?\ +dm_event.fields=created_at,sender_id,text" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -3. Set your **Callback URL**. You will want to update your Callback URL to exactly match the Callback URL associated with your application in the [v2 Dev Portal](https://developer.x.com/en/portal/dashboard). With the X App used with this example, the Callback URL is set to - [https://www.example.com.](https://www.example.com/) (Note that since this must match exactly, [https://example.com](https://example.com) would not work.)  +```python Python SDK +from xdk import Client -4. Confirm that **Auth URL** is set to [https://x.com/i/oauth2/authorize](https://x.com/i/oauth2/authorize) +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -5. Confirm that **Access Token URL** is set to [https://api.x.com/2/oauth2/token.](https://api.x.com/2/oauth2/token)**Client ID** \- Copy and paste OAuth 2.0 client ID from the Developer Portal - **Client Secret** \- You will need this only if you are using an App type that is a confidential client. If so, copy and paste the OAuth 2.0 Client Secret from the Developer Portal.  +# Get DM events from a one-to-one conversation +for page in client.dm_events.get_by_participant( + participant_id="9876543210", + dm_event_fields=["created_at", "sender_id", "text"] +): + for event in page.data: + print(f"{event.created_at}: {event.text}") +``` -6. Confirm that **Scope** is set to dm.read dm.write tweet.read users.read. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -7. Confirm that **State** is set to “state.” +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -8. Confirm that **Client Authentication** is set to Send as Basic Auth header. +// Get DM events from a one-to-one conversation +const paginator = client.dmEvents.getByParticipant("9876543210", { + dmEventFields: ["created_at", "sender_id", "text"], +}); -9. Click where it says “Get New Access Token”, click "Authorize app" as part of the "Sign-in with X" process. +for await (const page of paginator) { + page.data?.forEach((event) => { + console.log(`${event.created_at}: ${event.text}`); + }); +} +``` -10. Click the "Proceed" button and then the "Use Token" to generate a token.  + -11. Click on the "Save" button to save these configuration details. +Replace `9876543210` with the other participant's user ID. +--- -You may get a message that you are not logged into X. If you get this error, you will need to log in to the X account that you are trying to post on behalf of inside of Postman. +## Get conversation by ID -Now that these OAuth 2.0 details have been set at the folder level, navigate to each of the examples and their "Authorization" tab and confirm that they have their Type set to "Inherit auth from parent."  +Retrieve DM events from a specific conversation ID: -Note that this token will expire soon, and you'll need to regenerate it by clicking on the "Get New Access Token" button. Clicking that will trigger the "Sign-in with X" process and generate a fresh token to make requests with. + -#### Step three: Retrieve Direct Messages conversation events +```bash cURL +curl "https://api.x.com/2/dm_conversations/1234567890-9876543210/dm_events?\ +dm_event.fields=created_at,sender_id,text" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -When retrieving Direct Message conversation events with this endpoint, you need to specify a conversation ID. The conversation ID is part of the endpoint path: https://api.x.com/2/dm\_conversations/:dm\_conversation\_id/dm\_events +```python Python SDK +from xdk import Client -In Postman, navigate to the “Params” tab and enter the ID of the conversation you want to retrieve events for in the "Path Variables" section. +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -The setting would be: +# Get DM events from a conversation by ID +for page in client.dm_events.get_by_conversation( + dm_conversation_id="1234567890-9876543210", + dm_event_fields=["created_at", "sender_id", "text"] +): + for event in page.data: + print(f"{event.created_at}: {event.text}") +``` -| | | -| :--- | :--- | -| **Key** | **Value** | -| `dm_conversation_id` | `1228393702244134912` | +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -With this conversation specified, the resulting path becomes https://api.x.com/2/dm\_conversations/1582103724607971328/dm\_events +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -If you click the "Send" button you will receive the default Direct Message object fields in your response: id,  text, and event_type. There will also be a "meta" object with the number of results, along with pagination tokens used for retrieving more events if available. +// Get DM events from a conversation by ID +const paginator = client.dmEvents.getByConversation("1234567890-9876543210", { + dmEventFields: ["created_at", "sender_id", "text"], +}); +for await (const page of paginator) { + page.data?.forEach((event) => { + console.log(`${event.created_at}: ${event.text}`); + }); +} ``` -{ - "data": [ - { - "event_type": "MessageCreate", - "id": "1580705921830768647", - "text": "hello to you two, this is a new group conversation." - } - ], - "meta": { - "result_count": 1, - "next_token": "18LAA5FFPEKJA52G0G00ZZZZ", - "previous_token": "1BLC45FFPEKJA52G0S00ZZZZ" - } + + + +--- + +## Filter by event type + +Get only specific event types: + + + +```bash cURL +curl "https://api.x.com/2/dm_events?\ +event_types=MessageCreate&\ +dm_event.fields=created_at,sender_id,text" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get only MessageCreate events +for page in client.dm_events.list( + event_types=["MessageCreate"], + dm_event_fields=["created_at", "sender_id", "text"] +): + for event in page.data: + print(f"{event.text}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Get only MessageCreate events +const paginator = client.dmEvents.list({ + eventTypes: ["MessageCreate"], + dmEventFields: ["created_at", "sender_id", "text"], +}); + +for await (const page of paginator) { + page.data?.forEach((event) => { + console.log(event.text); + }); } ``` -If you would like to receive additional fields, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. + -For this exercise, we will request additional sets of fields of the dm_event object: +### Event types -1. The default Direct Message object fields, id, text, and event_type. +| Type | Description | +|:-----|:------------| +| `MessageCreate` | A message was sent | +| `ParticipantsJoin` | User joined conversation | +| `ParticipantsLeave` | User left conversation | -2. Additional Direct Message object fields: dm\_conversation\_id, created\_at, sender\_id, attachments, participant\_ids, referenced\_tweets +--- +## Include user data -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +Expand sender information: -| | | -| :--- | :--- | -| Key | Value | -| dm_event.fields | dm\_conversation\_id,created\_at,sender\_id,attachments,participant\_ids,referenced\_tweets | + -You should now see the following URL next to the "Send" button: +```bash cURL +curl "https://api.x.com/2/dm_events?\ +dm_event.fields=created_at,sender_id,text&\ +expansions=sender_id&\ +user.fields=username,profile_image_url" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client -https://api.x.com/2/dm\_conversations/:dm\_conversation\_id/dm\_events?dm\_event.fields=id,text,event\_type,dm\_conversation\_id,created\_at,sender\_id,attachments,participant\_ids,referenced\_tweets +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -#### Step four: Make your request and review your response +# Get DM events with sender info +for page in client.dm_events.list( + dm_event_fields=["created_at", "sender_id", "text"], + expansions=["sender_id"], + user_fields=["username", "profile_image_url"] +): + for event in page.data: + # Match sender from includes + print(f"{event.sender_id}: {event.text}") +``` -Once you have everything set up, hit the "Send" button again, and you will receive a response similar to the below response. Note that this response includes all the available dm_event fields. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Get DM events with sender info +const paginator = client.dmEvents.list({ + dmEventFields: ["created_at", "sender_id", "text"], + expansions: ["sender_id"], + userFields: ["username", "profile_image_url"], +}); + +for await (const page of paginator) { + page.data?.forEach((event) => { + console.log(`${event.sender_id}: ${event.text}`); + }); + // Sender user objects are in page.includes.users +} ``` + + + +### Response with expansion + +```json { - "data": [ - { - "text": "hello to you two, this is a new group conversation.", - "id": "1580705921830768647", - "dm_conversation_id": "1580705921830768643", - "event_type": "MessageCreate", - "sender_id": "17200003", - "created_at": "2022-10-13T23:43:54.000Z" - } - ], - "meta": { - "result_count": 1, - "next_token": "18LAA5FFPEKJA52G0G00ZZZZ", - "previous_token": "1BLC45FFPEKJA52G0S00ZZZZ" - } + "data": [ + { + "id": "1234567890", + "event_type": "MessageCreate", + "text": "Hello!", + "sender_id": "9876543210" + } + ], + "includes": { + "users": [ + { + "id": "9876543210", + "username": "example_user", + "profile_image_url": "https://..." + } + ] + } } ``` + +--- + +## Common parameters + +| Parameter | Description | +|:----------|:------------| +| `max_results` | Events per page (1-100, default 100) | +| `pagination_token` | Token for next page | +| `dm_event.fields` | Event fields to return | +| `event_types` | Filter by event type | +| `expansions` | Related objects to include | + +--- + +## Next steps + + + + Send Direct Messages + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/direct-messages/manage/integrate.mdx b/x-api/direct-messages/manage/integrate.mdx index cfaee369c..6301776fd 100644 --- a/x-api/direct-messages/manage/integrate.mdx +++ b/x-api/direct-messages/manage/integrate.mdx @@ -1,170 +1,290 @@ --- -title: Integration guide -sidebarTitle: Integration guide -keywords: ["manage direct messages integration", "DM management integration", "send DM integration", "DM integration guide", "direct message setup"] +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for sending Direct Messages +keywords: ["manage direct messages integration", "DM management integration", "send DM integration"] --- import { Button } from '/snippets/button.mdx'; -The Direct Messages endpoints v2 introduce conversations and conversation events as core X API objects, and makes a distinction between one-to-one and group conversations. One-to-one conversations always have two, and only two, participants, while group conversations can have two or more and memberships that can change.    +This guide covers the key concepts you need to integrate the manage Direct Messages endpoints into your application. -This page contains information on several tools and key concepts that you should be aware of as you integrate the Manage Direct Messages endpoints into your system. We’ve broken the page into two sections: - -* Key Concepts - * [Direct Message conversations](#direct-message-conversations) +--- - * [Shared conversation and event IDs across v1.1 and v2](#shared-conversation-and-event-ids) - * [Including media attachments and referencing Posts](#including-media-attachments-and-referencing-tweets) - * [Authentication](#authentication) +## Authentication - * [Developer portal, Projects, and developer Apps](#developer-portal-projects-and-developer-apps) +DM endpoints require user authentication: - * [Rate limits](#rate-limits) +| Method | Description | +|:-------|:------------| +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | -* [Helpful tools](#helpful-tools) + +App-Only authentication is not supported. All Direct Messages are private. + -**Key Concepts** +### Required scopes (OAuth 2.0) -### Direct Message conversations +| Scope | Required for | +|:------|:-------------| +| `dm.write` | Sending and deleting messages | +| `dm.read` | Required with dm.write | +| `tweet.read` | Required with dm scopes | +| `users.read` | Required with dm scopes | -All Direct Messages are part of a Direct Message conversation. These conversations can be one-to-one conversations or group conversations. This launch provides the foundational endpoints needed to create Direct Messages and retrieve events associated with Direct Message conversations. All conversations have a unique dm\_conversation\_id, and the events that make up that conversation all have a unique dm\_event\_id.   +--- -The Manage Direct Message endpoints provide three POST methods for creating new messages and adding them to conversations: +## Endpoints overview -* **POST /2/dm\_conversations/with/:participant\_id/messages** \- Creates a one-to-one Direct Message. This method either adds the message to an existing one-to-one conversation or creates a new one. The :participant_id path parameter is the User ID of the account receiving the message.  - Here is an example JSON request body for sending a one-to-one Direct Message: +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | `/2/dm_conversations/with/:participant_id/messages` | Send one-to-one message | +| POST | `/2/dm_conversations` | Create group conversation | +| POST | `/2/dm_conversations/:dm_conversation_id/messages` | Add message to conversation | +| DELETE | `/2/dm_events/:event_id` | Delete a message | +--- -``` -{ - "text": "Hello just you. This will appear as a new conversation if we have never messaged, or will be added to our existing thread. " -} -``` +## Sending messages +### One-to-one message +Send a message to a specific user. Creates a new conversation if one doesn't exist: -* **POST /2/dm_conversations** \- Creates a new group conversation and adds a Direct Message to it. These requests require a list of conversation participants. Note that you can create multiple conversations with the same participant list. These requests will always return a new conversation ID.Below is an example JSON request body for creating a new group conversation and adding a Direct Message. Note that this requires a "conversation_type" field and that must be set to "Group" (case sensitive). + -``` -{ - "message": {"text": "Hello to just you two, this is a new group conversation."}, - "participant_ids": ["944480690","906948460078698496"], - "conversation_type": "Group" -} +```bash cURL +curl -X POST "https://api.x.com/2/dm_conversations/with/9876543210/messages" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello!"}' ``` +```python Python SDK +from xdk import Client +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") +# Send a one-to-one DM +response = client.dm_conversations.create_message( + participant_id="9876543210", + text="Hello!" +) +print(response.data) +``` -* **POST /2/dm\_conversations/:dm\_conversation_id/messages** \- Creates a Direct Message and adds it to an existing conversation. The :dm\_conversation\_id path parameter is the ID of the conversation that the message will be added to. This method can be used to add a message to both one-to-one and group conversations. - Here is an example JSON request body for sending Direct Message to both one-to-one and group conversations: +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -``` -{ - "text": "Here is a new message." -} +// Send a one-to-one DM +const response = await client.dmConversations.createMessage({ + participantId: "9876543210", + text: "Hello!", +}); +console.log(response.data); ``` + -These POST methods support attaching a single piece of media. POST request bodies must include either or both the "text" and "attachments" fields. The "text" attribute is required if the "attachments" field is not included, and the "attachments" field is required if the "text" field is not included. See the next section for more information. +### Group conversation -For more information see the [Manage Direct Messages API References](/x-api/direct-messages/create-a-new-dm-conversation).  +Create a new group and send the first message: -### Shared conversation and event IDs across v1.1 and v2 + -An important concept is that conversation and event IDs are shared across v1.1 and v2 versions of the X Platform. This means both versions can be used together. +```bash cURL +curl -X POST "https://api.x.com/2/dm_conversations" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "conversation_type": "Group", + "participant_ids": ["944480690", "906948460078698496"], + "message": {"text": "Welcome to our group!"} + }' +``` -For example, the Direct Messages v1.1 endpoints provide methods for returning a single event and for deleting events. These methods are not yet available with v2. Since IDs are common across v1.1 and v2, you can make v1.1 requests based on IDs provided by v2, or by referencing conversation IDs displayed in conversation URLs on the X application.   +```python Python SDK +from xdk import Client -### Including media attachments and referencing Posts +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -The Manage Direct Message endpoints all support attaching one piece of media (photo, video, or GIF). Attaching media requires a media ID generated by the v1.1 [Upload media](https://developer.x.com/en/docs/x-api/v1/media/upload-media/overview) endpoint. The authenticated user posting the Direct Message must have also uploaded the media. Once uploaded, media is available for 24 hours for including with the message. +# Create a group conversation +response = client.dm_conversations.create( + conversation_type="Group", + participant_ids=["944480690", "906948460078698496"], + message={"text": "Welcome to our group!"} +) +print(response.data) +``` -To illustrate how to include media, the following is an example JSON request body: +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -``` -{ - "text": "Here's a photo...", - "attachments": [{"media_id": "1583157113245011970}] -} +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Create a group conversation +const response = await client.dmConversations.create({ + conversationType: "Group", + participantIds: ["944480690", "906948460078698496"], + message: { text: "Welcome to our group!" }, +}); +console.log(response.data); ``` -The resulting MessageCreate event will include the following metadata: + -``` -{ - "attachments": { - "media_keys": [ - "5_1583157113245011970" - ] - } -} -``` + +The `conversation_type` field must be set to `"Group"` (case sensitive). + + +### Add to existing conversation + +Send a message to any conversation you're part of: -Posts can also be included by including the Post URL in the message text. To illustrate how to include Posts in messages, the following is an example JSON request body: + +```bash cURL +curl -X POST "https://api.x.com/2/dm_conversations/1582103724607971328/messages" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"text": "Another message"}' ``` -{ - "text": "Here's the Tweet I has talking about: https://x.com/SnowBotDev/status/1580559079470145536" -} + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Add message to existing conversation +response = client.dm_conversations.add_message( + dm_conversation_id="1582103724607971328", + text="Another message" +) +print(response.data) ``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -The resulting MessageCreate event will include the following metadata: +// Add message to existing conversation +const response = await client.dmConversations.addMessage({ + dmConversationId: "1582103724607971328", + text: "Another message", +}); +console.log(response.data); ``` + + + +--- + +## Media attachments + +Attach one piece of media (photo, video, or GIF) per message. + + + + Use the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked) to upload your file and get a `media_id`. + + + +```json { - "referenced_tweets": [ - { - "id": "1580559079470145536" - } - ] + "text": "Check out this image!", + "attachments": [{"media_id": "1583157113245011970"}] } ``` + + -### Authentication + +- The authenticated user must have uploaded the media +- Media is available for 24 hours after upload +- Only one attachment per message is supported + -All X API v2 endpoints require for you to authenticate your requests with a set of credentials, also known as keys and tokens. All Direct Messages are private and require user authorization to access them.  +--- -These Direct Message endpoints require the use of [OAuth 2.0 Authorization Flow with PKCE](/x-api/posts/manage-tweets) or [1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are requesting on behalf of. If you want to generate a set of Access Tokens for another user, they must authorize or authenticate your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). +## Sharing Posts -Please note that OAuth user-context can be tricky to use. If you are not familiar with this authentication method, we recommend using a [library](/x-api/tools-and-libraries/overview) or a tool like Postman to properly authenticate your requests.  +Include a Post in your message by adding the Post URL to the text: -[OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes, which give you specific permissions on behalf of a user. The Direct Messages lookup endpoints require these scopes:  dm.write, dm.read, tweet.read, user.read. +```json +{ + "text": "Have you seen this? https://x.com/XDevelopers/status/1580559079470145536" +} +``` -To enable OAuth 2.0 in your App, you must enable it in your App’s authentication settings found in the App settings section of the developer portal. +The response will include a `referenced_tweets` field with the Post ID. -### Developer portal, Projects, and developer Apps +--- -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have an approved developer account, set up a Project within that account, and create a developer App within that Project. You can then find your keys and tokens within your developer App.  +## Message requirements -### Rate limits +| Field | Required | Notes | +|:------|:---------|:------| +| `text` | Yes* | Required if no attachments | +| `attachments` | Yes* | Required if no text | -Everyday many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, rate limits are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user.  +*At least one of `text` or `attachments` must be provided. -The Manage Direct Message endpoints are rate limited at both the per-user and X App levels. This means that the authenticated user that you are making the request on behalf of can only send a certain number of messages with your X App. In addition, there is a daily limit on how many Direct Messages can be sent by your X App.  +--- -There is a user rate limit of 200 requests/messages per 15 minutes for the POST methods. Users are also limited to 1000 Direct Messages per 24 hours. In addition, X Apps have a limit of 15000 messages per 24 hours. These rate limits are shared across the POST endpoints.  +## ID compatibility with v1.1 -**Helpful tools** +Conversation and event IDs are shared between v1.1 and v2 endpoints. This enables hybrid workflows: -Here are some helpful tools we encourage you to explore as you work with the Direct Messages lookup endpoints:  +- Create messages with v2 +- Delete messages with v1.1 (not yet available in v2) +- Reference conversation IDs from x.com URLs -****Postman**** +--- -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) page.  +## Error handling -****Code samples**** +| Status | Error | Solution | +|:-------|:------|:---------| +| 400 | Invalid request | Check request body format | +| 401 | Unauthorized | Verify access token | +| 403 | Forbidden | Check scopes and user permissions | +| 429 | Too Many Requests | Wait and retry | -Python sample code for the v2 Direct Messages endpoints is available in our [X API v2 sample code GitHub](https://github.com/xdevplatform/Twitter-API-v2-sample-code)[](https://github.com/xdevplatform/Twitter-API-v2-sample-code) repository. The "Manage-Direct-Messages" folder contains examples for the POST methods, and the "Direct-Messages-lookup" folder contains examples for the GET methods. +### Common issues -**XDev Software Development Kits (SDKs)** + + + The recipient may have DM settings that prevent messages from unknown users, or may have blocked you. + -These [libraries](/x-api/tools-and-libraries/overview) are being updated for the v2 Direct Messages endpoints and should be ready soon: + + Ensure the media was uploaded by the same authenticated user and is less than 24 hours old. + -* [X API Java SDK](https://github.com/xdevplatform/twitter-api-java-sdk) \- Official Java SDK for the X API v2 -* [X API TypeScript/JavaScript SDK](https://github.com/xdevplatform/twitter-api-typescript-sdk) \- Official TS/JS SDK for the X API v2 + + Verify all participant IDs are valid and the users allow group DM invites. + + -**Third-party libraries** +--- -There is a growing number of [third-party libraries](/x-api/tools-and-libraries/overview#community-tools-and-libraries-for-v2) developed by our community. These libraries are designed to help you get started, and several are expected to support v2 Direct Messages endpoints soon. You can find a library that works with the v2 endpoints by looking for the proper version tag. +## Next steps + + + + Send your first Direct Message + + + Retrieve DM conversations + + + Upload media for attachments + + + Full endpoint documentation + + diff --git a/x-api/direct-messages/manage/introduction.mdx b/x-api/direct-messages/manage/introduction.mdx index e52ee0796..63eef6017 100644 --- a/x-api/direct-messages/manage/introduction.mdx +++ b/x-api/direct-messages/manage/introduction.mdx @@ -1,45 +1,102 @@ --- -title: Introduction +title: Manage Direct Messages sidebarTitle: Introduction -keywords: ["direct messages", "DMs", "manage DMs", "DM management", "direct message API", "DM API", "messages"] +description: Send and delete Direct Messages +keywords: ["manage DMs", "send DM", "delete DM", "direct message API", "create DM"] --- import { Button } from '/snippets/button.mdx'; -Direct Messages enable private conversations on X. Direct Messages are one of the most popular features of X, with a wide variety of use cases. These use cases range from group chats among friends, to powering customer support for brands around the world. New v2 versions of Direct Messages endpoints will be introduced in stages, and this first stage includes fundamental endpoints for creating Direct Messages and listing Direct Message conversation events. For the first time, the X API v2 supports _group_ conversations. +The Manage Direct Messages endpoints let you send and delete Direct Messages on behalf of authenticated users. -This initial release of Manage Direct Messages includes three POST methods for creating Direct Messages: +## Overview -* **POST /2/dm\_conversations/with/:participant\_id/messages** \- Creates a one-to-one Direct Message. This method either creates a new 1-1 conversation or retrieves the current conversation and adds the Direct Message to it. The :participant_idpath parameter is the User ID of the account receiving the message.  -* **POST /2/dm_conversations** \- Creates a new group conversation and adds a Direct Message to it. These requests require a list of conversation participants. Note that you can create multiple conversations with the same participant list. These requests will always return a new conversation ID.  -* **POST /2/dm\_conversations/:dm\_conversation_id/messages** - Creates a Direct Message and adds it to an existing conversation. The :dm\_conversation\_id path parameter is the ID of the conversation that the message will be added to.  + + + Send a DM to another user + + + Delete a DM for yourself + + + Start a new conversation + + + Send to group conversations + + -Note that Direct Message event IDs are common across the v1.1 and v2 (as well as the X App), so the v1.1 methods to hide/delete Direct Messages can be used along with this new v2 endpoint. Also note that the Enterprise and Premium Account Activity APIs support v2 one-to-one messages, but do not yet support group conversations.    +--- -There is a user rate limit of 200 requests per 15 minutes for the POST method. There is also a rate limit of 1000 requests per 24 hours per user. Additionally, there is a rate limit of 15000 requests per 24 hours. Note that these rate limits are shared across these POST endpoints. +## Endpoints -Since you are making requests on behalf of a user with the manage Posts endpoints, you must authenticate with either [OAuth 1.0a User Context](https://developer-staging.x.com/resources/fundamentals/authentication)or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and use a user Access Tokens associated with a user that has authorized your App. To generate this user Access Token with OAuth 1.0a, you can use the [3-legged OAuth flow](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token). To generate a user Access Token with OAuth 2.0, you can use the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)). - -**Account setup** +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | `/2/dm_conversations` | Create a new conversation | +| POST | `/2/dm_conversations/with/:participant_id/messages` | Send to one-to-one conversation | +| POST | `/2/dm_conversations/:dm_conversation_id/messages` | Send to existing conversation | +| DELETE | `/2/dm_events/:id` | Delete a DM event | + +--- + +## Example: Send a message -To access these endpoints, you will need: +```bash +curl -X POST "https://api.x.com/2/dm_conversations/with/1234567890/messages" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello! How are you?"}' +``` + +## Example response + +```json +{ + "data": { + "dm_conversation_id": "1234567890-0987654321", + "dm_event_id": "1122334455667788990" + } +} +``` + +--- -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +## Message types -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +You can send text messages and attach media: + +```json +{ + "text": "Check out this photo!", + "attachments": [{ + "media_id": "1234567890123456789" + }] +} +``` + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -
- - - - -
+ + + + Send your first DM + + + Key concepts and best practices + + + Retrieve DM events + + + Full endpoint documentation + + diff --git a/x-api/direct-messages/manage/migrate.mdx b/x-api/direct-messages/manage/migrate.mdx index ca7d7f799..9a88886fa 100644 --- a/x-api/direct-messages/manage/migrate.mdx +++ b/x-api/direct-messages/manage/migrate.mdx @@ -41,7 +41,7 @@ The following table compares fundamental aspects of the v1.1 and v2 manage Direc | Supports Group Direct Messages | | ✔ | | Event types supported | message_create | MessageCreate, ParticipantsJoin, ParticipantsLeave | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2 User Context (scopes: dm.read, dm.write) | -| Requires the use of credentials from a [developer App](/resources/fundamentals/authentication) associated with a X API v2 [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/authentication) associated with a X API v2 [Project](/resources/fundamentals/developer-apps) | | ✔ | | Default request [rate limits](/x-api/fundamentals/rate-limits)*
*All requests require user tokens | 1000 requests per user per 24 hours
15000 requests per app per 24 hours | 200 requests per 15 minutes per user

1000 requests per user per 24 hours

15000 requests per app per 24 hours

These rate limits are shared across all dm_conversations POST endpoints. | The following tables compare the v2 POST methods with version v1.1. Note that these v2 offerings expand the available capabilities by supporting group conversations.  diff --git a/x-api/direct-messages/manage/quickstart.mdx b/x-api/direct-messages/manage/quickstart.mdx index 984998c4c..cd99f5466 100644 --- a/x-api/direct-messages/manage/quickstart.mdx +++ b/x-api/direct-messages/manage/quickstart.mdx @@ -1,141 +1,340 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["manage direct messages quickstart", "manage DMs quickstart", "send DM quickstart", "DM management quickstart"] +description: Send Direct Messages and create group conversations +keywords: ["manage direct messages quickstart", "send DM quickstart", "DM management quickstart", "create group DM"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the manage Direct Message endpoints - -This quick start guide will help you make your first request to the Direct Message endpoints using Postman, a tool for managing and making HTTP requests. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) guide, - -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to review Python-based examples. In addition, the official [X Developer Platform software development kits (SDKs)](/x-api/tools-and-libraries/sdks) will be updated to support these Direct Message endpoints.   +This guide walks you through sending Direct Messages and creating group conversations. **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 2.0 PKCE with `dm.write` and `dm.read` scopes) -### Steps to building manage Direct Message requests -In this example, in one request, we'll create a new group conversation and add our first message to it. We'll then add a second message to the created conversation. +--- -#### Step one: Start with a tool or library +## Send a one-to-one message -To begin working with the manage Direct Message endpoints, we are going to use the Postman tool to simplify the process. A XDev-authored collection of example X API v2 requests will be used to explore six endpoints used to create new Direct Messages and to list Direct Message conversation events. + + + You need the user ID of the person you want to message. You can get this from the [User lookup endpoint](/x-api/users/lookup/introduction). + -While most of the collection is pre-filled, there are a few details that you'll need to provide that are based on the X App created to host these API requests. First, let's get the collection loaded/updated. + -To load the X API v2 Postman collection into your environment, please click on the following button: + - +```bash cURL +curl -X POST "https://api.x.com/2/dm_conversations/with/9876543210/messages" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello! This is a message from the API."}' +``` -Once you have the X API v2 collection loaded in Postman, navigate to the “Manage Direct Messages” folder. This folder's Authorization tab has been pre-filled where possible, and you can update a few settings to share your X App's authentication details. This folder also contains three endpoints for creating new Direct Messages. Note that there is also a "Direct Message lookup" folder with three available endpoints for retrieving Direct Message conversation events, including sending and receiving messages, and when conversation participants join and leave..  +```python Python SDK +from xdk import Client -Since creating group conversations is an exciting new feature of the X API v2, this example will focus on that. We will be working with the "New group DM and conversation" example. We will use this request to create a Direct Message group conversation. +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -The next step is to authenticate with the endpoint. +# Send a one-to-one message +response = client.dm.send_message( + participant_id="9876543210", + text="Hello! This is a message from the API." +) -**Step two: Authenticate your request** +print(f"Message sent: {response.data.dm_event_id}") +print(f"Conversation: {response.data.dm_conversation_id}") +``` -To properly make a request to the X API, you need to verify that you have permission to do so. To make a successful request to this endpoint, we will be using [OAuth 2.0 Authorization Code Flow with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). You can generate an access token within Postman.  +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -With Postman you can set the authentication method at the folder level or at the request level. Here we will be configuring the authentication details at the folder level. +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -Navigate to the "Manage Direct Messages" folder, select the "Authorization" tab and confirm that the Type to set to “OAuth 2.0,” and "Add auth data to" is set to "Request Headers." In the "Current Token" section, make sure the "header Prefix" is set to Bearer.   +// Send a one-to-one message +const response = await client.dm.sendMessage({ + participantId: "9876543210", + text: "Hello! This is a message from the API.", +}); -To configure and generate a new token: +console.log(`Message sent: ${response.data?.dm_event_id}`); +console.log(`Conversation: ${response.data?.dm_conversation_id}`); +``` -1. Create a Token Name, such as "Manage DMs." + -2. Confirm that **Grant Type** is set to Authorization Code (with PKCE). + -3. Set your **Callback URL**. You will want to update your Callback URL to exactly match the Callback URL associated with your application in the [v2 Dev Portal](https://developer.x.com/en/portal/dashboard). With the X App used with this example, the Callback URL is set to - [_https://www.example.com_.](https://www.example.com/) (Note that since this must match exactly, [_https://example.com_](https://example.com) would not work.)  + + ```json + { + "data": { + "dm_conversation_id": "1234567890-9876543210", + "dm_event_id": "1582103724607971332" + } + } + ``` + + -4. Confirm that **Auth URL** is set to [https://x.com/i/oauth2/authorize](https://x.com/i/oauth2/authorize). +--- -5. Confirm that **Access Token URL** is set to [https://api.x.com/2/oauth2/token.](https://api.x.com/2/oauth2/token)**Client ID** \- Copy and paste OAuth 2.0 client ID from the Developer Portal - **Client Secret** \- You will need this only if you are using an App type that is a confidential client. If so, copy and paste the OAuth 2.0 Client Secret from the Developer Portal.  +## Create a group conversation -6. Confirm that **Scope** is set to dm.read, dm.write, tweet.read, and users.read. + + + Gather the user IDs of everyone you want in the group (excluding yourself). + -7. Confirm that **State** is set to “state.” + -8. Confirm that **Client Authentication** is set to Send as Basic Auth header. + -9. Click where it says **Get New Access Token**, click "Authorize app" as part of the "Sign-in with X" process. +```bash cURL +curl -X POST "https://api.x.com/2/dm_conversations" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "conversation_type": "Group", + "participant_ids": ["944480690", "906948460078698496"], + "message": {"text": "Welcome to our new group!"} + }' +``` -10. Click the "Proceed" button and then the "Use Token" to generate a token.  +```python Python SDK +from xdk import Client -11. Click on the "Save" button to save these configuration details. +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") +# Create a group conversation +response = client.dm.create_conversation( + conversation_type="Group", + participant_ids=["944480690", "906948460078698496"], + message={"text": "Welcome to our new group!"} +) -You may get a message that you are not logged into X. If you get this error, you will need to log in to the X account you are trying to post on behalf of inside of Postman. +print(f"Group created: {response.data.dm_conversation_id}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Create a group conversation +const response = await client.dm.createConversation({ + conversationType: "Group", + participantIds: ["944480690", "906948460078698496"], + message: { text: "Welcome to our new group!" }, +}); -Now that these OAuth 2.0 details have been set at the folder level, navigate to each of the examples and their "Authorization" tab and confirm that they have their Type set to "Inherit auth from parent."  +console.log(`Group created: ${response.data?.dm_conversation_id}`); +``` + + -Note that this token will expire soon, and you'll need to regenerate it by clicking on the "Get New Access Token" button. Clicking that will trigger the "Sign-in with X" process and generate a fresh token to make requests with. + -#### Step three: Specify the Direct Message conversation participants and message contents + + ```json + { + "data": { + "dm_conversation_id": "1582103724607971328", + "dm_event_id": "1582103724607971332" + } + } + ``` -Navigate to the “Body” tab and make updates to the example JSON object. Set the participant_ids attribute to the accounts you want to send the Direct Message to. + Save the `dm_conversation_id` to add more messages later. + + - `{ - "message": {"text": "Hello to just you two, this is a new group conversation."}, - "participant_ids": ["944480690","906948460078698496"], - "conversation_type": "Group" -}` +--- +## Add a message to an existing conversation -#### Step four: Make your request and review the response +Send a message to a conversation you're already part of: -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the example response below. A reminder that if your token has expired since you created it above, you can return to the folder's Authorization tab and click on the "Get New Access Token" to create a fresh token. + +```bash cURL +curl -X POST "https://api.x.com/2/dm_conversations/1582103724607971328/messages" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"text": "Adding another message to the conversation."}' ``` -{ - "data": { - "dm_conversation_id": "1582103724607971328", - "dm_event_id": "1582103724607971332" - } -} + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Add message to existing conversation +response = client.dm.send_message_to_conversation( + dm_conversation_id="1582103724607971328", + text="Adding another message to the conversation." +) + +print(f"Message sent: {response.data.dm_event_id}") ``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Add message to existing conversation +const response = await client.dm.sendMessageToConversation( + "1582103724607971328", + { text: "Adding another message to the conversation." } +); + +console.log(`Message sent: ${response.data?.dm_event_id}`); +``` -If the returned response "data" object contains a dm\_conversation\_id and an dm\_event\_id, you have successfully created a new Direct Message conversation. To start looking up events associated with this conversation, head over to the Direct Message lookup Quick start guide. + -#### Step five: Add another message to that group conversation +--- -Now select the "Add DM to conversation" example, and select the "Params" tab. Under "Path Variables" , update the dm\_conversation\_id to the ID of the conversation you created above. +## Send a message with media -| | | -| :--- | :--- | -| **Key** | **Value** | -| dm\_conversation\_id | 1582103724607971328 | + + + First, upload your media using the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked). + -Using this conversation ID, the request path will resolve to: https://api.x.com/2/dm_conversations/1582103724607971328/messages + -Also, update the "Body" tab with request JSON containing the message text you want to send:  + +```bash cURL +curl -X POST "https://api.x.com/2/dm_conversations/with/9876543210/messages" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Check out this image!", + "attachments": [{"media_id": "1234567890123456789"}] + }' ``` -{ - "text": "Adding a new message to our group conversation..." -} + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Send message with media +response = client.dm.send_message( + participant_id="9876543210", + text="Check out this image!", + attachments=[{"media_id": "1234567890123456789"}] +) + +print(f"Message with media sent: {response.data.dm_event_id}") ``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Send message with media +const response = await client.dm.sendMessage({ + participantId: "9876543210", + text: "Check out this image!", + attachments: [{ mediaId: "1234567890123456789" }], +}); + +console.log(`Message with media sent: ${response.data?.dm_event_id}`); +``` + + + + + + +--- + +## Delete a message + +Delete a message you sent: + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/dm_events/1582103724607971332" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Delete a message +response = client.dm.delete_message("1582103724607971332") +print(f"Deleted: {response.data.deleted}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Delete a message +const response = await client.dm.deleteMessage("1582103724607971332"); +console.log(`Deleted: ${response.data?.deleted}`); ``` + + + +**Response:** + +```json { - "data": { - "dm_conversation_id": "1582103724607971328", - "dm_event_id": "1582106224379559940" - } + "data": { + "deleted": true + } } -``` \ No newline at end of file +``` + + +You can only delete messages you sent, not messages from other participants. + + +--- + +## Required scopes + +When using OAuth 2.0 PKCE, your access token must have these scopes: + +| Scope | Description | +|:------|:------------| +| `dm.write` | Send and delete messages | +| `dm.read` | Read conversations (required with dm.write) | +| `tweet.read` | Required for some expansions | +| `users.read` | Required for user expansions | + +--- + +## Next steps + + + + Retrieve DM conversations + + + Key concepts and best practices + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/enterprise-gnip-2.0/fundamentals/account-activity.mdx b/x-api/enterprise-gnip-2.0/fundamentals/account-activity.mdx index b88a6e12a..b93e96563 100644 --- a/x-api/enterprise-gnip-2.0/fundamentals/account-activity.mdx +++ b/x-api/enterprise-gnip-2.0/fundamentals/account-activity.mdx @@ -199,12 +199,12 @@ There are several 'plumbing' details that need attention before you can start re ### 1\. Create a X app. -* Create a [X app](/resources/fundamentals/developer-apps) with an approved developer account from the [developer portal](/resources/fundamentals/developer-portal). If you are creating the app on behalf of your company, it is recommended you create the app with a corporate X account. To apply for a developer account, [click here](/resources/fundamentals/developer-apps). +* Create a [X app](/resources/fundamentals/developer-apps) with an approved developer account from the [Developer Console](/resources/fundamentals/developer-portal). If you are creating the app on behalf of your company, it is recommended you create the app with a corporate X account. To apply for a developer account, [click here](/resources/fundamentals/developer-apps). * Enable “Read, Write and Access direct messages” on the [permissions](/resources/fundamentals/developer-apps#oauth-1-0a-app-permissions) tab of your app page. * On the "Keys and Access Tokens" tab, take note of your app's Consumer Key (API Key) and Consumer Token (API Secret). * On the same tab, generate your app's [Access Token and Access Token Secret](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). You will need these Access Tokens to register your webhook URL, which is where X will send account events. * If you are unfamiliar with [X Sign-in](/resources/fundamentals/authentication#log-in-with-x) and how user contexts work with the X API review [Obtaining Access Tokens](https://dev.x.com/webhooks/access-tokens). As you add accounts for which to receive events, you will subscribe them using that account's access tokens. -* Take note of your app's numeric ID, as seen in the ["Apps"](/resources/fundamentals/developer-apps) page of the [developer portal](/resources/fundamentals/developer-portal). When you apply for Account Activity API access, you'll need this app ID. +* Take note of your app's numeric ID, as seen in the ["Apps"](/resources/fundamentals/developer-apps) page of the [Developer Console](/resources/fundamentals/developer-portal). When you apply for Account Activity API access, you'll need this app ID.   ### 2\. Get Account Activity API access @@ -457,7 +457,7 @@ For those endpoints that require OAuth 1.0a user context authentication, you wil * Consumer Keys (API Key and Secret) * Access Tokens (Access Token and Secret) -In the case of the following three endpoints, you perform write actions within the context of your application (no X users are involved). Therefore, the Access Tokens you need to provide are the ones belonging to your developer App. These can be generated directly from within the [developer portal](https://developer.x.com/en/portal/projects-and-apps), under the “Keys and tokens” tab for your App.   +In the case of the following three endpoints, you perform write actions within the context of your application (no X users are involved). Therefore, the Access Tokens you need to provide are the ones belonging to your developer App. These can be generated directly from within the [Developer Console](https://developer.x.com/en/portal/projects-and-apps), under the “Keys and tokens” tab for your App.   * [POST account_activity/webhooks](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#post-account-activity-webhooks): Register a new webhook URL for the given application context * [PUT account\_activity/webhooks/:webhook\_id](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#put-account-activity-webhooks-webhook-id): Trigger a challenge response check (CRC) for a given webhook's URL @@ -509,7 +509,7 @@ For those endpoints that require OAuth 1.0a user context authentication, you wil * Consumer Keys (API Key and Secret) * Access Tokens (Access Token and Secret) -In the case of the following three endpoints, you perform write actions within the context of your application (no X users are involved). Therefore, the Access Tokens you need to provide are the ones belonging to your developer App. These can be generated directly from within the [developer portal](https://developer.x.com/en/portal/projects-and-apps), under the “Keys and tokens” tab for your App.   +In the case of the following three endpoints, you perform write actions within the context of your application (no X users are involved). Therefore, the Access Tokens you need to provide are the ones belonging to your developer App. These can be generated directly from within the [Developer Console](https://developer.x.com/en/portal/projects-and-apps), under the “Keys and tokens” tab for your App.   * [POST account_activity/webhooks](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#post-account-activity-webhooks): Register a new webhook URL for the given application context * [PUT account\_activity/webhooks/:webhook\_id](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#put-account-activity-webhooks-webhook-id): Trigger a challenge response check (CRC) for a given webhook's URL @@ -1071,9 +1071,9 @@ Depending on how you are currently operating with User Streams or Site Streams, * The level of support you'd prefer from X (forum support or managed enterprise level 1:1 support) * Price of each package -**Step 2:** **Check the Setup of your X app in the developer portal** +**Step 2:** **Check the Setup of your X app in the Developer Console** -The [X app](/resources/fundamentals/developer-apps) currently used for User Streams or Site Streams will be listed for the owning user within the [developer portal](/resources/fundamentals/developer-portal). This X app can also be used for Account Activity API to retain authorized users for that application.  A new app can also be created, and users can be re-authorized for this new app if desired.  If you are creating a new app on behalf of a business, it is recommended that you create the app with a corporate X @handle account. +The [X app](/resources/fundamentals/developer-apps) currently used for User Streams or Site Streams will be listed for the owning user within the [Developer Console](/resources/fundamentals/developer-portal). This X app can also be used for Account Activity API to retain authorized users for that application.  A new app can also be created, and users can be re-authorized for this new app if desired.  If you are creating a new app on behalf of a business, it is recommended that you create the app with a corporate X @handle account. * Enable “Read, Write and Access direct messages” on the [permissions](/resources/fundamentals/developer-apps#app-permissions) tab of your X app page.  *Note that changing these settings is not retroactive, any authorized users will keep the authorization settings from the time at which they were authorized. If a user has not already given you read, write and direct message access, you will need to have that user re-authorize your application. @@ -1150,7 +1150,7 @@ We've created a sample app to make testing the Account Activity API a little qui | | | | :--- | :--- | | Blank lines | Blank lines will no longer be delivered in the Account Activity API as they were used as keep-alive messages in User Streams and Site Streams. | -| Limit notices | Limit notices will no longer be sent to a given webhook.  Instead, users can call the API to get current usage of available handles. This will be included in the developer portal at some time in the future. | +| Limit notices | Limit notices will no longer be sent to a given webhook.  Instead, users can call the API to get current usage of available handles. This will be included in the Developer Console at some time in the future. | | Disconnect messages | Disconnect notices will no longer be necessary as webhooks do not rely on an active connection. | | Stall warnings | Stall warnings will no longer be necessary as webhooks do not rely on an active connection being able to handle large numbers of incoming messages. | | Friends list | Friends lists will no longer be sent proactively. There will now be a REST endpoint to get this information. | @@ -1410,9 +1410,9 @@ No, this is not possible. As it currently stands, we only have the `/all/` produ At this point, Direct Messages permissions are required because there is no way to 'filter out' the Direct Messages activities for this API.    -**Is there a free version of the Account Activity API?** +**Is there a sandbox version of the Account Activity API?** -Yes, we offer the sandbox version as a free tier. Our sandbox option is limited to a single webhook with a limit of a maximum of 15 subscriptions. You can read more about the sandbox option in [our documentation](/x-api/enterprise-gnip-2.0/fundamentals/account-activity).  +Yes, we offer a sandbox option for testing. Our sandbox option is limited to a single webhook with a limit of a maximum of 15 subscriptions. You can read more about the sandbox option in [our documentation](/x-api/enterprise-gnip-2.0/fundamentals/account-activity).  **Is it possible to use the Account Activity API to get Retweets of Posts that mention subscribed users? ** @@ -1445,7 +1445,7 @@ Enterprise **How can I add my app to an allowlist or check if my app is already on the allowlist?** -To manage the [X apps](/resources/fundamentals/developer-apps) that you have added to an allowlist for access via the Enterprise APIs, please reach out to your account manager with your app ID. You can find your app ID by navigating to the ["Apps"](/resources/fundamentals/developer-apps) page in the [developer portal](/resources/fundamentals/developer-portal). +To manage the [X apps](/resources/fundamentals/developer-apps) that you have added to an allowlist for access via the Enterprise APIs, please reach out to your account manager with your app ID. You can find your app ID by navigating to the ["Apps"](/resources/fundamentals/developer-apps) page in the [Developer Console](/resources/fundamentals/developer-portal).   **If I have access to three webhooks, can I use three webhooks for each of the apps that I have registered for enterprise use?** diff --git a/x-api/enterprise-gnip-2.0/fundamentals/engagement-api.mdx b/x-api/enterprise-gnip-2.0/fundamentals/engagement-api.mdx index 159674121..8687919c0 100644 --- a/x-api/enterprise-gnip-2.0/fundamentals/engagement-api.mdx +++ b/x-api/enterprise-gnip-2.0/fundamentals/engagement-api.mdx @@ -153,7 +153,7 @@ After discussing getting access to the Engagement API, we’ll walk through maki If you are reading this document, you have most likely already obtained access to the Engagement API. If not, please reach out to your Enterprise account manager, or apply for Enterprise access [here](/x-api/enterprise-gnip-2.0/enterprise-gnip). -The first step is creating a [X app](/resources/fundamentals/developer-apps) using an approved developer account via the [developer portal](/resources/fundamentals/developer-portal).  Your account manager will need the numeric App ID associated with this application to provide access. If you need to apply for a developer account, you can do so [here](/resources/fundamentals/developer-apps). +The first step is creating a [X app](/resources/fundamentals/developer-apps) using an approved developer account via the [Developer Console](/resources/fundamentals/developer-portal).  Your account manager will need the numeric App ID associated with this application to provide access. If you need to apply for a developer account, you can do so [here](/resources/fundamentals/developer-apps). ##### **Making a request** @@ -393,7 +393,7 @@ When sending a request with OAuth 1.0a, you need to include the Access Tokens (A The Engagement API will not allow you to fetch engagement data for [protected Posts](https://help.x.com/en/safety-and-security/public-and-protected-tweets), even if you are authenticating on behalf of the user who owns these Posts. Attempting to do so will return a `400 Bad Request` error, with the message `"Tweet ID(s) are unavailable"`. -If you are sending a request on behalf of your own X account (in other words, the account that owns the developer App), you can generate the required Access Tokens directly from within the [developer portal](https://developer.x.com/en/portal/projects-and-apps), under the “Keys and tokens” tab for the developer App. +If you are sending a request on behalf of your own X account (in other words, the account that owns the developer App), you can generate the required Access Tokens directly from within the [Developer Console](https://developer.x.com/en/portal/projects-and-apps), under the “Keys and tokens” tab for the developer App. If you are making a request on behalf of any other user, you will need to use the 3-legged OAuth flow to obtain the required Access Tokens. The following documentation contains more information on how to do this: [OAuth 1.0a: how to obtain a user’s access tokens](/resources/fundamentals/authentication#oauth-1-0a-2). diff --git a/x-api/fundamentals/consistency.mdx b/x-api/fundamentals/consistency.mdx index 8a6e182b0..15c2fe0be 100644 --- a/x-api/fundamentals/consistency.mdx +++ b/x-api/fundamentals/consistency.mdx @@ -1,108 +1,184 @@ --- -title: Consistency -keywords: ["consistency", "API consistency", "uniform API", "consistent endpoints", "API design", "endpoint consistency"] +title: API Consistency +sidebarTitle: Consistency +description: Consistent patterns across X API v2 endpoints +keywords: ["consistency", "API consistency", "uniform API", "consistent endpoints", "API design"] --- +X API v2 is designed with consistent patterns across all endpoints. Once you learn how one endpoint works, the same patterns apply everywhere. -## Consistency across the X API v2 endpoints +--- -One of the main aspects of the new v2 version of the X API is consistency across endpoints. This means that objects returned, features, and behaviors of the API are uniform across similar endpoints. +## Consistent patterns -You can expect the following elements to be the same across all endpoints: +### URL structure -### Path naming +All v2 endpoints follow a predictable pattern: -Path naming always includes the endpoint [version](/x-api/fundamentals/versioning), followed by the **resource**. Beyond that, the path can contain an **ID** that relates to the earlier resource, a **selection verb** which helps to determine which data to return (e.g., `search` or `sample`), a **delivery verb** which helps to determine how the data will deliver (e.g., `stream`), or other resources that have a **relationship** with the primary resource (e.g., `/user/12/tweets`). Finally, you can append a **query parameter** to the end if the endpoint includes any query parameters. +``` +/version/resource/{id}?parameters +/version/resource/verb?parameters +``` -Here are some examples of how these path and query items could be organized: +Examples: -`/version/resource/id?param1=value¶m2=value /version/resource/delivery/selection?param1=value¶m2=value` +``` +/2/tweets/1234567890 # Get a specific post +/2/tweets/search/recent # Search recent posts +/2/users/by/username/xdevelopers # Get user by username +/2/users/1234/followers # Get user's followers +``` +### Response structure -Examples of actual requests: +All responses use the same top-level structure: -`/2/tweets/1067094924124872705?expansions=attachments.media_keys&tweet.fields=author_id /2/users/2244994945?user.fields=created_at,description /2/tweets/search/stream /2/tweets/search/recent?query=snow` +```json +{ + "data": { ... }, // Primary object(s) + "includes": { ... }, // Expanded objects + "meta": { ... }, // Pagination, counts + "errors": [ ... ] // Partial errors (if any) +} +``` +### ID format -### JSON Schema +All IDs are returned as strings to ensure language compatibility: -Responses to requests are defined using [JSON Schema](http://json-schema.org/). This means that requests consistently return sets of objects as arrays, with each element having an ID. Requests do not return maps with IDs as keys. +```json +{ + "id": "1234567890123456789", + "author_id": "2244994945" +} +``` -### Response object and parameters +--- + +## Fields and expansions + +The same [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters work consistently: + +| Object | Fields parameter | Works across | +|:-------|:-----------------|:-------------| +| Post | `tweet.fields` | All endpoints returning posts | +| User | `user.fields` | All endpoints returning users | +| Media | `media.fields` | All endpoints with media expansions | +| Poll | `poll.fields` | All endpoints with poll expansions | +| Place | `place.fields` | All endpoints with place expansions | + +--- -The default object returned is the same for each endpoint of that object type: +## Object schemas -- `id` objects are always strings. -- Parameters and response fields consistently use American-English spelling. -- Fields are empty or not returned if there is no value. -- The `entities` object only contains entities sourced from the Post text: this includes `urls`, `hashtags`, `mentions`, and `cashtags`. -- All cards-related information, including the `media_keys` and `poll_ids` fields, are returned in the `attachments` object. +The same object type has the same fields regardless of which endpoint returns it: -Here is an example response object from the [single Post lookup](/x-api/posts/post-lookup-by-post-id) endpoint (with the [tweet.fields](/x-api/fundamentals/data-dictionary#tweet) parameter set to `author_id`, `entities`): +- A Post from search has the same fields as a Post from lookup +- A User from followers has the same fields as a User from search +- Expanded objects match their standalone counterparts + +--- + +## Authentication + +All endpoints use the same authentication methods: + +| Method | Header format | +|:-------|:--------------| +| Bearer Token | `Authorization: Bearer {token}` | +| OAuth 1.0a | `Authorization: OAuth {parameters}` | +| OAuth 2.0 | `Authorization: Bearer {user_token}` | + +--- + +## Error handling + +Errors follow a consistent format: ```json { - "data": { - "id": "1278747501642657792", - "text": "It's been a year since Twitter's Developer Labs launched.\n\nAs we build towards the next generation of the #TwitterAPI (coming VERY soon), see what we've learned and changed along the way. https://t.co/WvjuEWCa6G", - "author_id": "2244994945", - "entities": { - "urls": [ - { - "start": 188, - "end": 211, - "url": "https://t.co/WvjuEWCa6G", - "expanded_url": "https://blog.x.com/developer/en_us/topics/tools/2020/a-year-with-twitter-developer-labs.html", - "display_url": "blog.x.com/developer/en_u…", - "images": [ - { - "url": "https://pbs.twimg.com/news_img/1278747527043362816/7HQRkQeV?format=jpg&name=orig", - "width": 1600, - "height": 600 - }, - { - "url": "https://pbs.twimg.com/news_img/1278747527043362816/7HQRkQeV?format=jpg&name=150x150", - "width": 150, - "height": 150 - } - ], - "status": 200, - "title": "A year with Twitter Developer Labs: What we've learned and changed", - "description": "Labs has been invaluable in helping us understand what works well and what doesn’t, what you liked and what you didn’t.", - "unwound_url": "https://blog.x.com/developer/en_us/topics/tools/2020/a-year-with-twitter-developer-labs.html" - } - ], - "hashtags": [ - { - "start": 106, - "end": 117, - "tag": "TwitterAPI" - } - ] - } - } + "title": "Invalid Request", + "detail": "The query parameter is missing", + "type": "https://api.x.com/2/problems/invalid-request" } ``` -### Authentication -All X API v2 endpoints require authentication. Many of them accept the [OAuth 2.0 Bearer Token](/resources/fundamentals/authentication#oauth-2-0) method, requiring a Bearer Token with the request to the endpoint to make a successful request. +[See all error types →](/x-api/fundamentals/response-codes-and-errors) + +--- + +## Pagination + +All paginated endpoints use the same token system: + +| Parameter | Description | +|:----------|:------------| +| `max_results` | Results per page | +| `pagination_token` | Token from `next_token` or `previous_token` | + +[Learn more about pagination →](/x-api/fundamentals/pagination) + +--- + +## Naming conventions -For endpoints requiring authorization to create, manipulate, or retrieve data on behalf of another user, use [OAuth 1.0a User Context](/resources/fundamentals/authentication). This means providing your [developer App’s](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) [API keys and tokens](/resources/fundamentals/authentication#api-key-and-secret) as well as a set of user [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) that you generate for the user on behalf of whom you’re making a request. The [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) can help you retrieve Access Tokens. Use a [tool or library](/x-api/tools-and-libraries/overview) that automatically builds this authorization header. +- American English spelling (`favorites` not `favourites`) +- Snake_case for field names (`author_id`, `created_at`) +- Consistent terminology (`retweet_count`, not `repost_count` in fields) -More information on authentication can be found in our documentation on [authentication](/resources/fundamentals/authentication). +--- + +## Empty values + +Fields with no value are omitted rather than returned as `null`: + +```json +// User without a bio +{ + "id": "1234", + "name": "Example User", + "username": "example" + // "description" is omitted, not null +} +``` --- -### Fields +## Entity consistency + +The `entities` object only contains entities parsed from text: -The object returned on each endpoint is condensed. To allow developers more customization in the response they get back from the API, the [`fields`](/x-api/fundamentals/fields) parameter is used to request the fields desired. Fields will remain consistent across endpoints. The Post object will return the same fields across all endpoints where the Post object is returned. The same set of fields can be queried across similar endpoints. +- `urls` +- `hashtags` +- `mentions` +- `cashtags` + +Media and polls are in `attachments`, not `entities`. + +--- -For example, the same Post fields can be queried in the [Posts lookup](/x-api/posts/lookup/introduction) and for the expanded pinned Post in the [Users lookup](/x-api/users/lookup/introduction). +## What this means for you + + + + Patterns you learn on one endpoint apply to all endpoints. + + + Same object types have same structures across the API. + + + Build reusable functions for common patterns. + + + Consistent error formats simplify troubleshooting. + + --- -### Expansions +## Report inconsistencies -Where appropriate, [expansions](/x-api/fundamentals/expansions) are available for all nested id fields (e.g., all fields named `*_id`, such as `author_id`). Expansions are also available for all fields that have an id that is not the top-level identifier of the current object. For example, in the [Posts lookup](/x-api/posts/lookup/introduction), the Post is the current object with field `id` as the top-level identifier. The `author_id` or `referenced_tweets.id` fields are available to expand into complete user or Post objects by adding these comma-separated values to the `expansions` parameter. +Found an inconsistency? Let us know: -Please [report any inconsistencies](https://twitterdevfeedback.uservoice.com/) that you notice, related to these fields. \ No newline at end of file +- [Developer Forum](https://devcommunity.x.com) +- [Developer Feedback](https://t.co/devfeedback) diff --git a/x-api/fundamentals/conversation-id.mdx b/x-api/fundamentals/conversation-id.mdx index f816f8d9a..fd518add0 100644 --- a/x-api/fundamentals/conversation-id.mdx +++ b/x-api/fundamentals/conversation-id.mdx @@ -1,172 +1,188 @@ --- title: Conversation ID -keywords: ["conversation ID", "conversation threads", "reply threads", "thread tracking", "conversation tracking", "reply chains", "thread reconstruction"] +sidebarTitle: Conversation ID +description: Track and reconstruct conversation threads +keywords: ["conversation ID", "conversation threads", "reply threads", "thread tracking", "conversation tracking"] --- +Every reply on X belongs to a conversation thread. The `conversation_id` field lets you identify, track, and reconstruct entire conversation trees. -### Conversation threads using the X API -If you look at how conversations evolve on X, one Post can spark several conversation threads, each of which can grow in length and complexity as more people chime in. Identifying relationships between Posts and understanding conversation threads is a feature of the X API v2 payload and search capabilities. When Posts are posted in response to a Post (known as a reply), or in response to a reply, there is now a defined `conversation_id` on each reply, which matches the Post ID of the original Post that started the conversation. +--- + +## How it works + +When someone posts and others reply, all replies share the same `conversation_id`—the ID of the original post that started the conversation. + +``` +Original post (ID: 1234567890) ← conversation_id for all replies +├── Reply 1 (ID: 1234567891) → conversation_id: 1234567890 +│ └── Reply to Reply 1 → conversation_id: 1234567890 +└── Reply 2 (ID: 1234567892) → conversation_id: 1234567890 + └── Reply to Reply 2 → conversation_id: 1234567890 +``` + +No matter how deep the thread goes, all posts share the same `conversation_id`. + +--- -Replies to a given Post, as well as replies to those replies, are all included in the conversation stemming from the single original Post. Regardless of how many reply threads result, they will all share a common `conversation_id` to the original Post that sparked the conversation. Using the X API v2, you can retrieve and reconstruct an entire conversation thread, allowing you to understand what is being said and how conversations and ideas evolve. +## Requesting conversation_id -#### Example conversation thread -Below is an example conversation thread involving five different people, including one reply to a reply. +Add `conversation_id` to your `tweet.fields`: + +```bash +curl "https://api.x.com/2/tweets/1234567891?tweet.fields=conversation_id,in_reply_to_user_id,referenced_tweets" \ + -H "Authorization: Bearer $TOKEN" +``` + +Response: ```json { - "data": [ - { - "conversation_id": "1279944223114900000", - "in_reply_to_user_id": "1102323333", - "author_id": "63044444", - "created_at": "2020-07-06T15:58:10.000Z", - "id": "1280169177479744444", - "referenced_tweets": [ - { - "type": "replied_to", - "id": "1280155225706433333" - } - ], - "text": "@ThirdPerson333 @OriginalPerson000 Reply to the third reply!" - }, - { - "conversation_id": "1279944223114900000", - "in_reply_to_user_id": "3001960000", - "author_id": "1102323333", - "created_at": "2020-07-06T15:02:44.000Z", - "id": "1280155225706433333", - "referenced_tweets": [ - { - "type": "replied_to", - "id": "1279944223114900000" - } - ], - "text": "@OriginalPerson000 Third reply" - }, - { - "conversation_id": "1279944223114900000", - "in_reply_to_user_id": "3001960000", - "author_id": "199562222", - "created_at": "2020-07-06T15:02:36.000Z", - "id": "1280155190306340864", - "referenced_tweets": [ - { - "type": "replied_to", - "id": "1279944223114900000" - } - ], - "text": "@OriginalPerson000 Second Reply" - } - ], - "includes": { - "users": [ - { - "name": "Original person", - "id": "3001960000", - "username": "OriginalPerson000" - }, - { - "name": "First person", - "id": "179201111", - "username": "FirstPerson111" - } - ] - }, - "meta": { - "newest_id": "1280169177479744444", - "oldest_id": "1279945722494811111", - "result_count": 4 + "data": { + "id": "1234567891", + "text": "@user Great point!", + "conversation_id": "1234567890", + "in_reply_to_user_id": "2244994945", + "referenced_tweets": [{ + "type": "replied_to", + "id": "1234567890" + }] } } ``` -Retrieving `conversation_id` as a `tweet.fields` parameter -To request the `conversation_id` for all Posts returned on a v2 endpoint, add the tweet.fields=conversation_id field to the request parameters. The conversation_id field is always the Post ID of the original Post in the conversation reply thread. All Posts within the same reply thread, including reply threads that are created from earlier reply threads, will show the same conversation_id. -### Request with `conversation_id` parameter -``` -curl --request GET \ - --url 'https://api.x.com/2/tweets?ids=1225917697675886593&tweet.fields=author_id,conversation_id,created_at,in_reply_to_user_id,referenced_tweets&expansions=author_id,in_reply_to_user_id,referenced_tweets.id&user.fields=name,username' \ - --header 'Authorization: Bearer $ACCESS_TOKEN' -``` -### Response +--- + +## Getting a full conversation + +Use `conversation_id` as a search operator to retrieve all posts in a thread: + +```bash +curl "https://api.x.com/2/tweets/search/recent?\ +query=conversation_id:1234567890&\ +tweet.fields=author_id,created_at,in_reply_to_user_id&\ +expansions=author_id" \ + -H "Authorization: Bearer $TOKEN" ``` -{ - "data": [ - { - "id": "1225917697675886593", - "text": "@TwitterEng *ahem* https://t.co/aroJHt2zQ1", - "created_at": "2020-02-07T23:02:10.000Z", - "author_id": "2244994945", - "in_reply_to_user_id": "6844292", - "conversation_id": "1225912275971657728", - "referenced_tweets": [ - { - "type": "quoted", - "id": "1200517737669378053" - }, - { - "type": "replied_to", - "id": "1225912275971657728" - } - ] - } - ], - "includes": { - "users": [ - { - "username": "TwitterDev", - "name": "Twitter Dev", - "id": "2244994945" - }, - { - "username": "TwitterEng", - "name": "Twitter Engineering", - "id": "6844292" - } - ], - "tweets": [ - { - "id": "1200517737669378053", - "text": "| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|\n don't push \n to prod on \n Fridays \n|___________| \n(\\__/)", - "created_at": "2019-11-29T20:51:47.000Z", - "author_id": "2244994945", - "conversation_id": "1200517737669378053" - } - ] - } + +This returns all replies to the original post, sorted reverse-chronologically. + +--- + +## Use cases + + + +Build the full conversation tree: + +```python +import requests + +conversation_id = "1234567890" +url = f"https://api.x.com/2/tweets/search/recent" +params = { + "query": f"conversation_id:{conversation_id}", + "tweet.fields": "author_id,in_reply_to_user_id,referenced_tweets,created_at", + "max_results": 100 } -``` -### Using `conversation_id` as a filter operator -The `conversation_id` can be used as a search query parameter in recent search or as an operator within a rule for [filtered stream](/x-api/posts/filtered-stream/introduction). This will return the entire conversation thread of Posts either in real-time through filtered stream or in reverse chronological order from [search Tweets](/x-api/posts/search/introduction). You can also get a count of the Posts in a conversation using this operator with [Posts counts](/x-api/posts/counts/introduction). -## Request to query by `conversation_id` +response = requests.get(url, headers=headers, params=params) +replies = response.json()["data"] + +# Sort by created_at to get chronological order +replies.sort(key=lambda x: x["created_at"]) ``` -curl --request GET \ - --url 'https://api.x.com/2/tweets/search/recent?query=conversation_id:1279940000004973111&tweet.fields=in_reply_to_user_id,author_id,created_at,conversation_id' \ - --header 'Authorization: Bearer $ACCESS_TOKEN' + + +Stream replies to specific conversations in real-time: + +```bash +# Add a filtered stream rule for a conversation +curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Authorization: Bearer $TOKEN" \ + -d '{"add": [{"value": "conversation_id:1234567890"}]}' ``` + + +Count replies in a conversation: -### Response -_Note: Results for search Posts are in reverse chronological order._ +```bash +curl "https://api.x.com/2/tweets/counts/recent?\ +query=conversation_id:1234567890" \ + -H "Authorization: Bearer $TOKEN" ``` + + + +--- + +## Related fields + +| Field | Description | +|:------|:------------| +| `conversation_id` | ID of the original post that started the thread | +| `in_reply_to_user_id` | User ID of the post being replied to | +| `referenced_tweets` | Array with `type: "replied_to"` and the parent post ID | + +--- + +## Example: Full thread retrieval + +```json { "data": [ { - "id": "1280169000000704333", - "text": "@attributeisland @iterationjoe What beautiful creatures! Happy #seaturtleweek", - "conversation_id": "1279940000004973111", - "public_metrics": { - "retweet_count": 0, - "reply_count": 0, - "like_count": 7, - "quote_count": 0 - } + "id": "1234567893", + "text": "@user2 @user1 I agree with you both!", + "conversation_id": "1234567890", + "author_id": "3333333333", + "created_at": "2024-01-15T12:05:00.000Z", + "in_reply_to_user_id": "2222222222", + "referenced_tweets": [{"type": "replied_to", "id": "1234567892"}] + }, + { + "id": "1234567892", + "text": "@user1 That's interesting!", + "conversation_id": "1234567890", + "author_id": "2222222222", + "created_at": "2024-01-15T12:03:00.000Z", + "in_reply_to_user_id": "1111111111", + "referenced_tweets": [{"type": "replied_to", "id": "1234567890"}] + }, + { + "id": "1234567891", + "text": "@user1 Great point!", + "conversation_id": "1234567890", + "author_id": "4444444444", + "created_at": "2024-01-15T12:02:00.000Z", + "in_reply_to_user_id": "1111111111", + "referenced_tweets": [{"type": "replied_to", "id": "1234567890"}] } ], "meta": { - "newest_id": "1280169000000704333", - "oldest_id": "1279940000004973111", - "result_count": 5 + "result_count": 3 } } -``` \ No newline at end of file +``` + +--- + +## Notes + +- The original post's `conversation_id` equals its own `id` +- `conversation_id` is available on all v2 endpoints that return posts +- Use with [filtered stream](/x-api/posts/filtered-stream/introduction) to monitor conversations in real-time +- Combine with [pagination](/x-api/fundamentals/pagination) for large threads + +--- + +## Next steps + + + + Search by conversation_id. + + + Monitor conversations in real-time. + + diff --git a/x-api/fundamentals/data-dictionary.mdx b/x-api/fundamentals/data-dictionary.mdx index bd145543d..887ec793c 100644 --- a/x-api/fundamentals/data-dictionary.mdx +++ b/x-api/fundamentals/data-dictionary.mdx @@ -1,13 +1,41 @@ --- -title: "X API v2 data dictionary" +title: Data Dictionary +sidebarTitle: Data Dictionary +description: Complete field reference for X API v2 objects keywords: ["data dictionary", "object model", "tweet object", "user object", "API objects", "data structure", "JSON schema", "API response", "tweet fields", "user fields"] --- -## Object Model -### Tweet -Tweets are the basic building block of all things Twitter. The Tweet object has a long list of ‘root-level’ fields, such as `id`, `text`, and `created_at`. Tweet objects are also the ‘parent’ object to several child objects including `user`, `media`, `poll`, and `place`. Use the field parameter `tweet.fields` when requesting these root-level fields on the Tweet object. +The X API returns structured JSON objects representing posts, users, media, and more. This reference documents every field available for each object type. + +--- + +## Quick navigation + +| Object | Description | Fields parameter | +|:-------|:------------|:-----------------| +| [Post (Tweet)](#post-tweet) | Posts, replies, reposts, quotes | `tweet.fields` | +| [User](#user) | Account profiles and metadata | `user.fields` | +| [Space](#space) | Live audio conversations | `space.fields` | +| [List](#list) | Curated collections of accounts | `list.fields` | +| [Media](#media) | Images, videos, GIFs | `media.fields` | +| [Poll](#poll) | Poll questions and options | `poll.fields` | +| [Place](#place) | Location and geo data | `place.fields` | + + +Use [fields parameters](/x-api/fundamentals/fields) to request specific fields, and [expansions](/x-api/fundamentals/expansions) to include related objects. + + +--- + +## Post (Tweet) + +Posts are the core content unit on X. Each Post object includes text, metadata, and references to related objects like authors, media, and polls. + +**Default fields:** `id`, `text`, `edit_history_tweet_ids` + +Use `tweet.fields` to request additional fields and `expansions` to include related objects. -The Tweet object that can be found and expanded in the user resource. Additional Tweets related to the requested Tweet can also be found and expanded in the Tweet resource. The object is available for expansion with `?expansions=pinned_tweet_id` in the user resource or `?expansions=referenced_tweets.id` in the Tweet resource to get the object with only default fields. Use the expansion with the field parameter: `tweet.fields` when requesting additional fields to complete the object. +### All Post fields | Field Value | Type | Description | How it Can Be Used | |:-----------------------|:--------|:---------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------| diff --git a/x-api/fundamentals/edit-posts.mdx b/x-api/fundamentals/edit-posts.mdx index 2e5deafb5..957b9f182 100644 --- a/x-api/fundamentals/edit-posts.mdx +++ b/x-api/fundamentals/edit-posts.mdx @@ -1,146 +1,206 @@ --- title: Edit Posts -keywords: ["edit posts", "edit tweets", "tweet editing", "edit history", "post edits", "edit metadata", "edit window", "edited tweets"] +sidebarTitle: Edit Posts +description: Working with edited posts in the X API +keywords: ["edit posts", "edit tweets", "edit history", "post edits", "edit metadata", "edit window"] --- -# Introduction -The X API v2 endpoints provide edited Post metadata. The _Edit Posts_ feature was first introduced for testing among X employees on September 1, 2022. Starting on that date, eligible Posts are editable for 30 minutes and up to 5 times. _All objects for Posts created since September 29, 2022_ include Post edit metadata, even if the Post was never edited. Each time a Post is edited, a new Post ID is created. A Post's edit history can be described by chaining these IDs together, starting with the original ID.  Additionally, if any Post in the edit chain is deleted, all Posts in that chain are deleted as well. +Posts on X can be edited within 30 minutes of posting, up to 5 times. The X API provides full access to edit history and metadata. -Using the X API v2, a developer can find out: - -* If a Post was edit eligible at the time of creation. Some Posts, such as those with polls or scheduled Posts, can not be edited. -* Posts are editable for 30 minutes, and can be edited up to 5 times. For editable POsts you can see if time for editing remains and how many more edits are possible. -* If you are viewing an edited version of a Post (in most cases the API will return the most recent version of a Post, unless a specific past version is requested by Post ID). -* The entire edit history of the Post. -* The engagement attributed to each version of the Post. - -There are three components to a Post's edit history: - -1. By default, the Post payload will contain an array of Post IDs that are part of a Post's edit history. This information is specified by the `edit_history_tweet_ids`, which is a default field in the Post payload. This array will contain at least one ID, the ID of the original, unedited Post. When there is only one ID that means the Post has no edit history.  -2. You can get information such as whether a Post was editable at the time it was created, how much time, if any, is remaining for a Post to be edited, and how many edits remain by specifying `edit_controls` in your [tweet.fields](/x-api/fundamentals/fields) parameter. -3. Finally, you can get the Post objects for each Post in a Post's edit history, by specifying the `edit_history_tweet_ids` using the [expansion](/x-api/fundamentals/expansions) parameter +--- -Most Posts are eligible for editing. However, the following types of Posts are not:  +## Edit rules -* Post is promoted -* Post has a poll -* Post is a non-self-thread reply -* Post is a Retweet (note that Quote Tweets are eligible for edit) -* Post is nullcast -* Community Post -* Superfollow Post -* Collaborative Post +| Rule | Details | +|:-----|:--------| +| **Time window** | 30 minutes from original post | +| **Edit limit** | 5 edits maximum | +| **ID behavior** | Each edit creates a new post ID | +| **Deletion** | Deleting any version deletes the entire chain | -Examples +--- ------------ +## What can't be edited -The examples below demonstrate how a developer can request edited Post metadata using the X API v2.  +Some post types are not editable: -**Note:** The examples below use the User Post Timeline endpoint, but you can request this metadata using the same parameters (with fields and expansions) for all endpoints that return Posts (e.g. Posts lookup, search, filtered stream,  etc.) +- Promoted posts (ads) +- Posts with polls +- Replies to others (non-self-thread) +- Reposts (Quote posts *can* be edited) +- Community posts +- Collaborative posts +- Scheduled posts -### Default behavior +--- -By default, an API request to any endpoint that returns Post objects in the X API v2, you get: +## Edit data in responses -* The Post ID -* The Post Text -* An array of Post IDs that are part of a Post's edit history. If there is only one ID provided, that means the Post has not been edited. +### Default fields -**Request:** +All post responses include `edit_history_tweet_ids` by default: - `curl --request GET 'https://api.x.com/2/users/:id/tweets' --header 'Authorization: Bearer $BEARER_TOKEN'` +```json +{ + "data": { + "id": "1234567891", + "text": "Updated text (edited)", + "edit_history_tweet_ids": ["1234567890", "1234567891"] + } +} +``` +- Single ID = never edited +- Multiple IDs = edit history (oldest first) +### Edit controls +Request `edit_controls` for edit status: -**Sample Response:** +```bash +curl "https://api.x.com/2/tweets/1234567891?tweet.fields=edit_controls" \ + -H "Authorization: Bearer $TOKEN" +``` -```{ - "data": [ - { - "id": "1514991667853602823", - "text": "we have an edit button", - "edit_history_tweet_ids": ["1514991667853602822", "1514991667853602823"] +```json +{ + "data": { + "id": "1234567891", + "text": "Updated text (edited)", + "edit_history_tweet_ids": ["1234567890", "1234567891"], + "edit_controls": { + "is_edit_eligible": true, + "editable_until": "2024-01-15T12:30:00.000Z", + "edits_remaining": 3 } - ] + } } ``` -### Getting additional data with edit_controls +| Field | Description | +|:------|:------------| +| `is_edit_eligible` | Whether the post can be edited | +| `editable_until` | Timestamp when edit window closes | +| `edits_remaining` | Number of edits left (0-5) | -If you want additional edited Post metadata such as whether a Post was eligible to be edited when it was created and how much time is remaining for a Post to be editable, you can request this information using the tweet.fields parameter and setting it to edit_control. +--- -**Request:** +## Getting edit history - `curl --request GET 'https://api.x.com/2/users/:id/tweets?tweet.fields=edit_control' --header 'Authorization: Bearer $BEARER_TOKEN'` +Use the `edit_history_tweet_ids` expansion to get full post objects for all versions: -**Sample Response:** +```bash +curl "https://api.x.com/2/tweets/1234567891?\ +tweet.fields=edit_controls&\ +expansions=edit_history_tweet_ids" \ + -H "Authorization: Bearer $TOKEN" +``` - ``` - { - "data": [ - { - "id": "1514991667853602823", - "text": "we have an edit button", - "edit_history_tweet_ids": ["1514991667853602822", "1514991667853602823"], +```json +{ + "data": { + "id": "1234567891", + "text": "Updated text (edited)", + "edit_history_tweet_ids": ["1234567890", "1234567891"], + "edit_controls": { + "is_edit_eligible": true, + "editable_until": "2024-01-15T12:30:00.000Z", + "edits_remaining": 3 + } + }, + "includes": { + "tweets": [{ + "id": "1234567890", + "text": "Original text (with typo)", + "edit_history_tweet_ids": ["1234567890", "1234567891"], "edit_controls": { "is_edit_eligible": true, - "editable_until": "2022-04-21 09:35:20.311", - "edits_remaining": 4 + "editable_until": "2024-01-15T12:30:00.000Z", + "edits_remaining": 3 } - } - ] + }] + } } ``` -### Getting Post objects for all Posts in a Post's edit history +--- + +## Which version is returned? -The examples above provide an array of Post IDs in a Post's edit history. If you want the actual Post object for each of these Post IDs, you can use the expansion parameter and set it to `edit_history_tweet_ids`. The Post objects that make up the edit history will be provided in the "includes" object.  +By default, the API returns the **most recent version** of an edited post. -**Request:** +To get a specific version, request it by its post ID directly: - ```bash - curl --location --request GET 'https://api.x.com/2/users/:id/tweets?tweet.fields=edit_control&expansions=edit_history_tweet_ids' --header 'Authorization: Bearer $BEARER_TOKEN'` +```bash +# Get original version +curl "https://api.x.com/2/tweets/1234567890" -H "Authorization: Bearer $TOKEN" + +# Get edited version +curl "https://api.x.com/2/tweets/1234567891" -H "Authorization: Bearer $TOKEN" ``` +--- + +## Metrics for edited posts +Each version of an edited post has its own engagement metrics. The metrics are attributed to the version that was visible when the engagement occurred. -**Sample Response:** +--- - ``` - { - "data": [ - { - "id": "1514991667853602823", - "text": "we have an edit button", - "edit_history_tweet_ids": ["1514991667853602822", "1514991667853602823"], - "edit_controls": { - "is_edit_eligible": true, - "editable_until": "2022-04-21 09:35:20.311", - "edits_remaining": 4 - } - } - ], - "includes": { - "tweets": [ - { - "id": "1514991667853602822", - "text": "we need an eidt button", - "edit_history_tweet_ids": [ - "1514991667853602822", - "1514991667853602823" - ], - "edit_controls": { - "is_edit_eligible": true, - "editable_until": "2022-04-21 09:35:20.311", - "edits_remaining": 4 - } - } - ] - } +## Availability + +Edit metadata is available: + +- For all posts created since September 29, 2022 +- On all v2 endpoints that return posts +- Including search, timelines, stream, and lookup + +Posts created before this date do not have edit metadata. + +--- + +## Use cases + + + +Monitor posts for edits and log the differences: + +```python +def check_for_edits(post): + history = post.get("edit_history_tweet_ids", []) + if len(history) > 1: + print(f"Post {post['id']} has been edited {len(history) - 1} times") +``` + + +Display an "edited" badge in your UI: + +```javascript +const isEdited = post.edit_history_tweet_ids?.length > 1; +if (isEdited) { + showEditedBadge(); } ``` + + +Retrieve the original version of an edited post: +```python +original_id = post["edit_history_tweet_ids"][0] +original = api.get_tweet(original_id) +``` + + + +--- +## Next steps + + + Retrieve posts with edit history. + + + Complete post object reference. + + diff --git a/x-api/fundamentals/expansions.mdx b/x-api/fundamentals/expansions.mdx index 9a1ade825..c2aac997d 100644 --- a/x-api/fundamentals/expansions.mdx +++ b/x-api/fundamentals/expansions.mdx @@ -1,181 +1,226 @@ --- title: Expansions -keywords: ["expansions", "expand objects", "object expansion", "nested objects", "related objects", "expand users", "expand media", "expand tweets"] +sidebarTitle: Expansions +description: Include related objects in API responses +keywords: ["expansions", "expand objects", "object expansion", "nested objects", "related objects"] --- +Expansions let you include related objects in a single API response. Instead of making multiple requests, get a post and its author, media, or referenced posts in one call. -With expansions, developers can expand objects referenced in the payload. Objects available for expansion are referenced by ID. For example, the `referenced_tweets.id` and `author_id` fields returned in the [Posts lookup](/x-api/posts/lookup/introduction) payload can be expanded into complete objects. If you would like to request fields related to the user that posted that Post, or the media, poll, or place that was included in that Post, you will need to pass the related expansion query parameter in your request to receive that data in your response. Currently, v2 endpoints that return Posts, Users, Lists, Spaces, and Direct Message event objects all support expansions (see examples below). +--- + +## How expansions work -When including an expansion in your request, we will include that expanded object’s default fields within the same response. It helps return additional data in the same response without the need for separate requests. If you would like to request additional [fields](/x-api/fundamentals/fields) related to the expanded object, you can include the field parameter associated with that expanded object, along with a comma-separated list of fields that you would like to receive in your response. Please note fields are not always returned in the same order they were requested in the query. +When you request an expansion, the API includes the full object in the `includes` section of the response: + +```bash +curl "https://api.x.com/2/tweets/1234567890?expansions=author_id" \ + -H "Authorization: Bearer $TOKEN" +``` -The Post payload below contains reference IDs for complementary objects we can expand on, including the `author_id` of who posted the Post, the `id` of a *referenced* Post, and a `media_key` for a media attachment. +Response: ```json { - "data": { - "attachments": { - "media_keys": ["16_1211797899316740096"] - }, - "author_id": "2244994945", - "id": "1212092628029698048", - "referenced_tweets": [ - { - "type": "replied_to", - "id": "1212092627178287104" - } - ], - "text": "We believe the best future version of our API will come from building it with YOU. Here’s to another great year with everyone who builds on the Twitter platform. We can’t wait to continue working with you in the new year. https://t.co/yvxdK6aOo2" - } + "data": { + "id": "1234567890", + "text": "Hello world!", + "author_id": "2244994945" + }, + "includes": { + "users": [{ + "id": "2244994945", + "name": "X Developers", + "username": "xdevelopers" + }] + } } ``` -### Available Expansions for Post Payloads -| Expansion | Description | -|:--------------------------------|:------------------------------------------------------------------------------------| -| `author_id` | Returns a user object representing the Post's author | -| `referenced_tweets.id` | Returns a Post object that this Post is referencing (either as a Retweet, Quoted Tweet, or reply) | -| `edit_history_tweet_ids` | Returns Post objects that are part of a Post's edit history | -| `in_reply_to_user_id` | Returns a user object representing the Post author this requested Post is a reply of | -| `attachments.media_keys` | Returns a media object representing the images, videos, GIFs included in the Post | -| `attachments.poll_ids` | Returns a poll object containing metadata for the poll included in the Post | -| `geo.place_id` | Returns a place object containing metadata for the location tagged in the Post | -| `entities.mentions.username` | Returns a user object for the user mentioned in the Post | -| `referenced_tweets.id.author_id` | Returns a user object for the author of the referenced Post | +The `author_id` in `data` links to the user object in `includes`. + +--- + +## Post expansions + +| Expansion | Returns | Use case | +|:----------|:--------|:---------| +| `author_id` | User object | Get post author details | +| `referenced_tweets.id` | Post object(s) | Get quoted/replied-to posts | +| `referenced_tweets.id.author_id` | User object(s) | Get authors of referenced posts | +| `in_reply_to_user_id` | User object | Get user being replied to | +| `attachments.media_keys` | Media object(s) | Get images, videos, GIFs | +| `attachments.poll_ids` | Poll object | Get poll options and votes | +| `geo.place_id` | Place object | Get location details | +| `entities.mentions.username` | User object(s) | Get mentioned users | +| `edit_history_tweet_ids` | Post object(s) | Get previous versions of edited posts | -### Available Expansion for User Payloads +--- -| Expansion | Description | -|:--------------------|:-----------------------------------------------------------| -| `pinned_tweet_id` | Returns a Post object representing the Post pinned to the top of the user’s profile | +## User expansions -### Available Expansions for Direct Message Event Payloads +| Expansion | Returns | Use case | +|:----------|:--------|:---------| +| `pinned_tweet_id` | Post object | Get user's pinned post | -| Expansion | Description | -|:--------------------------|:--------------------------------------------------------------------------| -| `attachments.media_keys` | Returns a Media object that was attached to a Direct Message | -| `referenced_tweets.id` | Returns a Post object that was referenced in a Direct Message | -| `sender_id` | Returns a User object representing the author of a Direct Message and who invited a participant to join a conversation | -| `participant_ids` | Returns a User object representing a participant that joined or left a conversation | +--- -### Available Expansions for Spaces Payloads +## Space expansions -| Expansion | Description | -|:--------------------|:-----------------------------------------------------------| -| `invited_user_ids` | Returns User objects representing what accounts were invited | -| `speaker_ids` | Returns User objects representing what accounts spoke during a Space | -| `creator_id` | Returns a User object representing what account created the Space | -| `host_ids` | Returns User objects representing what accounts were set up as hosts | -| `topics_ids` | Returns topic descriptions that were set up by the creator | +| Expansion | Returns | Use case | +|:----------|:--------|:---------| +| `creator_id` | User object | Get Space creator | +| `host_ids` | User object(s) | Get Space hosts | +| `speaker_ids` | User object(s) | Get Space speakers | +| `invited_user_ids` | User object(s) | Get invited users | -### Available Expansion for Lists Payloads +--- -| Expansion | Description | -|:-------------|:----------------------------------------------------------------| -| `owner_id` | Returns a User object representing what account created and maintains the List | +## DM expansions -## Expanding the Media Object +| Expansion | Returns | Use case | +|:----------|:--------|:---------| +| `sender_id` | User object | Get message sender | +| `participant_ids` | User object(s) | Get conversation participants | +| `attachments.media_keys` | Media object | Get attached media | +| `referenced_tweets.id` | Post object | Get referenced post | -In the following request, we are requesting the `geo.place_id` expansion to include alongside the default Post fields: +--- -**Sample Request** -``` -{`curl 'https://api.x.com/2/tweets/:ID?expansions=geo.place_id' --header 'Authorization: Bearer $ACCESS_TOKEN'`} -``` +## List expansions -**Sample Response** +| Expansion | Returns | Use case | +|:----------|:--------|:---------| +| `owner_id` | User object | Get list owner | +--- + +## Combining with fields + +Expansions return default fields for each object. To get additional fields, combine expansions with field parameters: + +```bash +curl "https://api.x.com/2/tweets/1234567890?\ +expansions=author_id,attachments.media_keys&\ +user.fields=description,public_metrics&\ +media.fields=url,alt_text" \ + -H "Authorization: Bearer $TOKEN" ``` -{`{ + +Response: + +```json +{ "data": { - "geo": { - "place_id": "01a9a39529b27f36" - }, - "id": "ID", - "text": "Test" + "id": "1234567890", + "text": "Check out this image!", + "author_id": "2244994945", + "attachments": { + "media_keys": ["3_1234567890"] + } }, "includes": { - "places": [ - { - "full_name": "Manhattan, NY", - "id": "01a9a39529b27f36" - } - ] + "users": [{ + "id": "2244994945", + "name": "X Developers", + "username": "xdevelopers", + "description": "The voice of the X Developer Platform", + "public_metrics": { + "followers_count": 570842 + } + }], + "media": [{ + "media_key": "3_1234567890", + "type": "photo", + "url": "https://pbs.twimg.com/media/example.jpg", + "alt_text": "Example image" + }] } -}`} - +} ``` -## Expanding the Poll Object +--- -In the following request, we are requesting the `attachments.poll_ids` expansion to include alongside the default Post fields: +## Multiple expansions -**Sample Request** +Request multiple expansions as a comma-separated list: - -{`curl 'https://api.x.com/2/tweets/1199786642791452673?expansions=attachments.poll_ids' --header 'Authorization: Bearer $ACCESS_TOKEN'`} - +```bash +expansions=author_id,referenced_tweets.id,attachments.media_keys +``` -**Sample Response** +--- +## Common patterns + + + +Get a post with author, media, and referenced posts: + +```bash +expansions=author_id,attachments.media_keys,referenced_tweets.id +tweet.fields=created_at,public_metrics,conversation_id +user.fields=username,name,profile_image_url +media.fields=url,preview_image_url,type ``` -{`{ - "data": { - "attachments": { - "poll_ids": ["1199786642468413448"] - }, - "id": "1199786642791452673", - "text": "C#" - }, - "includes": { - "polls": [ - { - "id": "1199786642468413448", - "options": [ - { - "position": 1, - "label": "“C Sharp”", - "votes": 795 - }, - { - "position": 2, - "label": "“C Hashtag”", - "votes": 156 - } - ] - } - ] - } -}`} + + +Get replies with their authors: + +```bash +expansions=author_id,in_reply_to_user_id,referenced_tweets.id +tweet.fields=conversation_id,in_reply_to_user_id,created_at +user.fields=username,name ``` -## Expanding the Place Object + + +Get a user profile with their pinned post: + +```bash +expansions=pinned_tweet_id +user.fields=description,public_metrics,verified +tweet.fields=created_at,public_metrics +``` + + -In the following request, we are requesting the `geo.place_id` expansion to include alongside the default Post fields: +--- -**Sample Request** +## Linking data and includes - -{`curl 'https://api.x.com/2/tweets/:ID?expansions=geo.place_id' --header 'Authorization: Bearer $ACCESS_TOKEN'`} - +The objects in `includes` don't contain position information. Link them using IDs: -**Sample Response** +```python +# Python example +response = api_call() +post = response["data"] +users = {u["id"]: u for u in response["includes"]["users"]} -```s -{`{ - "data": { - "geo": { - "place_id": "01a9a39529b27f36" - }, - "id": "ID", - "text": "Test" - }, - "includes": { - "places": [ - { - "full_name": "Manhattan, NY", - "id": "01a9a39529b27f36" - } - ] - } -}`} +# Get the author +author = users.get(post["author_id"]) +print(f"{author['name']} said: {post['text']}") ``` +```javascript +// JavaScript example +const { data: post, includes } = response; +const users = Object.fromEntries( + includes.users.map(u => [u.id, u]) +); + +const author = users[post.author_id]; +console.log(`${author.name} said: ${post.text}`); +``` + +--- + +## Next steps + + + + Request specific fields for each object. + + + Complete object schemas. + + diff --git a/x-api/fundamentals/fields.mdx b/x-api/fundamentals/fields.mdx index 944bd0409..d91ccdc24 100644 --- a/x-api/fundamentals/fields.mdx +++ b/x-api/fundamentals/fields.mdx @@ -1,90 +1,198 @@ --- -title: "Fields" -keywords: ["fields", "tweet fields", "user fields", "field parameters", "API fields", "data fields", "customize response", "field selection"] +title: Fields +sidebarTitle: Fields +description: Request specific data fields in API responses +keywords: ["fields", "tweet fields", "user fields", "field parameters", "API fields", "data fields", "customize response"] --- -The X API v2 endpoints are equipped with a set of parameters called *fields*, which allows you to select just the data that you want from each of the objects in your endpoint response. +The X API v2 returns minimal data by default. Use **fields parameters** to request additional data for each object type. -By default, the Post object only returns the `id` and the `text` fields, and for Posts created since September 29, 2022, the `edit_history_tweet_ids` field. If you need the Post's created date or public metrics, you will need to use the `tweet.fields` parameters to request them. This provides a higher degree of customization by enabling you to only request the fields you require depending on your use case. For example, you would include this query string in your request: +--- + +## How fields work + +By default, a post lookup returns only `id`, `text`, and `edit_history_tweet_ids`. To get more data, add field parameters to your request: + +```bash +# Default response - minimal fields +curl "https://api.x.com/2/tweets/1234567890" \ + -H "Authorization: Bearer $TOKEN" + +# With additional fields +curl "https://api.x.com/2/tweets/1234567890?tweet.fields=created_at,public_metrics,author_id" \ + -H "Authorization: Bearer $TOKEN" +``` -`?tweet.fields=created_at,public_metrics` +--- -Each object has its own parameter which is used to specifically request the fields that are associated with that object. Here are the different fields parameters that are currently available: +## Available field parameters -- [Post](/x-api/fundamentals/data-dictionary#tweet) → `tweet.fields` -- [User](/x-api/fundamentals/data-dictionary#user) → `user.fields` -- [Media](/x-api/fundamentals/data-dictionary#media) → `media.fields` -- [Poll](/x-api/fundamentals/data-dictionary#poll) → `poll.fields` -- [Place](/x-api/fundamentals/data-dictionary#place) → `place.fields` +Each object type has its own fields parameter: -When using an endpoint that primarily returns a particular object, simply use the matching field parameter and specify the field(s) desired in a comma-separated list as the value to that parameter to retrieve those fields in the response. +| Object | Parameter | Documentation | +|:-------|:----------|:--------------| +| Post (Tweet) | `tweet.fields` | [Post fields](/x-api/fundamentals/data-dictionary#tweet) | +| User | `user.fields` | [User fields](/x-api/fundamentals/data-dictionary#user) | +| Media | `media.fields` | [Media fields](/x-api/fundamentals/data-dictionary#media) | +| Poll | `poll.fields` | [Poll fields](/x-api/fundamentals/data-dictionary#poll) | +| Place | `place.fields` | [Place fields](/x-api/fundamentals/data-dictionary#place) | -### Example +--- -If you are using the [GET /tweets/search/recent](/x-api/posts/recent-search) endpoint, you will primarily receive [Tweet objects](/x-api/fundamentals/data-dictionary#tweet) in that response. Without specifying any fields parameters, you will just receive the default values, `id` and `text`. If you are interested in receiving the public metrics of the Posts that are returned in the response, you will want to include the `tweet.fields` parameter in your request, with `public_metrics` set as the value. +## Example: Post fields -This request would look like the following. If you would like to use this request, make sure to replace `$BEARER_TOKEN` with your [Bearer Token](/resources/fundamentals/authentication) and send it using your command line tool. +Request specific post fields with `tweet.fields`: ```bash -curl --request GET \ - --url 'https://api.x.com/2/tweets/search/recent?query=from%3Atwitterdev&tweet.fields=public_metrics' \ - --header 'Authorization: Bearer $BEARER_TOKEN' +curl "https://api.x.com/2/tweets/1234567890?tweet.fields=created_at,public_metrics,lang" \ + -H "Authorization: Bearer $TOKEN" ``` -If you send this request in your terminal, then each of the Posts that return will include the following fields: -``` +Response: + +```json { - "data": { - "id": "1263150595717730305", - "public_metrics": { - "retweet_count": 12, - "reply_count": 14, - "like_count": 49, - "quote_count": 7 - }, - "text": "Do you 👀our new Tweet settings?\n\nWe want to know how and why you’d use a feature like this in the API. Get the details and let us know what you think👇\nhttps://t.co/RtMhhfAcIB https://t.co/8wxeZ9fJER" - } + "data": { + "id": "1234567890", + "text": "Hello world!", + "edit_history_tweet_ids": ["1234567890"], + "created_at": "2024-01-15T12:00:00.000Z", + "lang": "en", + "public_metrics": { + "retweet_count": 10, + "reply_count": 5, + "like_count": 100, + "quote_count": 2 + } + } } ``` -If you would like to retrieve a set of fields from a secondary object that is associated with the primary object returned by an endpoint, you will need to include an additional expansions parameter. For example, if you were using the same GET search/tweets/recent endpoint as earlier, and you wanted to retrieve the author's profile description, you will have to pass the `expansions=author_id` and `user.fields=description` with your request. Here is an example of what this might look like. If you would like to try this request, make sure to replace the `$BEARER_TOKEN` with your [Bearer Token](/resources/fundamentals/authentication) before pasting it into your command line tool. +--- + +## Example: User fields + +Request specific user fields with `user.fields`: ```bash - curl --request GET \ - --url 'https://api.x.com/2/tweets/search/recent?query=from%3Atwitterdev&tweet.fields=public_metrics&expansions=author_id&user.fields=description' \ - --header 'Authorization: Bearer $BEARER_TOKEN' +curl "https://api.x.com/2/users/by/username/xdevelopers?user.fields=created_at,description,public_metrics" \ + -H "Authorization: Bearer $TOKEN" ``` -If you specify this in the request, then each of the Posts that deliver will have the following fields, and the related [user object's](/x-api/fundamentals/data-dictionary#user) default and specified fields will return within `includes`. The user object can be mapped back to the corresponding Post(s) by matching the `tweet.author_id` and `users.id fields`. +Response: -``` +```json { - "data": [ - { - "id": "1263150595717730305", - "author_id": "2244994945", - "text": "Do you 👀our new Tweet settings?\n\nWe want to know how and why you’d use a feature like this in the API. Get the details and let us know what you think👇\nhttps://t.co/RtMhhfAcIB https://t.co/8wxeZ9fJER", - "public_metrics": { - "retweet_count": 12, - "reply_count": 13, - "like_count": 51, - "quote_count": 7 - } + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "xdevelopers", + "created_at": "2013-12-14T04:35:55.000Z", + "description": "The voice of the X Developer Platform", + "public_metrics": { + "followers_count": 570842, + "following_count": 2048, + "tweet_count": 14052, + "listed_count": 1672 } - ], + } +} +``` + +--- + +## Fields for related objects + +To get fields on related objects (like the author of a post), you need two things: + +1. An **expansion** to include the related object +2. The **fields parameter** for that object type + +```bash +# Get post with author details +curl "https://api.x.com/2/tweets/1234567890?expansions=author_id&user.fields=description,public_metrics" \ + -H "Authorization: Bearer $TOKEN" +``` + +Response: + +```json +{ + "data": { + "id": "1234567890", + "text": "Hello world!", + "author_id": "2244994945" + }, "includes": { - "users": [ - { - "id": "2244994945", - "username": "TwitterDev", - "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", - "name": "Twitter Dev" + "users": [{ + "id": "2244994945", + "name": "X Developers", + "username": "xdevelopers", + "description": "The voice of the X Developer Platform", + "public_metrics": { + "followers_count": 570842, + "following_count": 2048 } - ] + }] } } ``` -Bear in mind that you cannot request specific subfields (for example, `public_metrics.retweet_count`). All subfields will be returned when the top-level field (`public_metrics`) is specified. We have listed all possible fields that you can request in each endpoints' API reference page's parameters table.  +[Learn more about expansions →](/x-api/fundamentals/expansions) + +--- + +## Common field combinations + + + +``` +tweet.fields=created_at,public_metrics,possibly_sensitive +``` + + +``` +user.fields=created_at,description,location,public_metrics,verified +``` + + +``` +tweet.fields=created_at,author_id,conversation_id,in_reply_to_user_id,referenced_tweets +expansions=author_id,referenced_tweets.id +user.fields=username,name +``` + + +``` +tweet.fields=attachments +expansions=attachments.media_keys +media.fields=url,preview_image_url,alt_text,public_metrics +``` + + + +--- + +## Important notes + + +**You cannot request subfields.** When you request `public_metrics`, you get all metrics (likes, reposts, replies, quotes). You can't request just `public_metrics.like_count`. + + +- Field order in responses may differ from request order +- Missing fields in responses mean the value is `null` or empty +- Some fields require specific authentication (e.g., private metrics need user context) +- Check each endpoint's API reference for available fields + +--- + +## Next steps -A full list of fields are listed in the [object model](/x-api/fundamentals/data-dictionary#tweet). To expand and request fields on an object that is not that endpoint’s primary resource, use the [expansions](/x-api/fundamentals/expansions) parameter with fields. + + + Include related objects in responses. + + + Complete field reference for all objects. + + diff --git a/x-api/fundamentals/metrics.mdx b/x-api/fundamentals/metrics.mdx index 006d98a5b..cd7eab4ae 100644 --- a/x-api/fundamentals/metrics.mdx +++ b/x-api/fundamentals/metrics.mdx @@ -1,106 +1,221 @@ --- -title: "Metrics" -keywords: ["metrics", "engagement metrics", "tweet metrics", "impressions", "retweets", "likes", "video views", "analytics", "public metrics", "private metrics", "organic metrics", "promoted metrics"] +title: Metrics +sidebarTitle: Metrics +description: Access engagement metrics for posts and media +keywords: ["metrics", "engagement metrics", "tweet metrics", "impressions", "likes", "retweets", "video views", "analytics"] --- -## Overview -The metrics field allows developers to access public and private engagement metrics for Post and media objects. Public metrics are accessible by anyone with a developer account, while private metrics are accessible from owned/authorized accounts (definition below). Metrics include the total count of impressions, Retweets, Quote Tweets, likes, replies, video views, video view quartiles, URL and profile link clicks for each Post specified in the request. There is also the option to view a breakdown of metrics earned in an organic or promoted context, if the Post was promoted as an [Ad](https://ads.x.com/). +The X API provides engagement metrics for posts and media. Access public metrics with any authentication, or private metrics for your own content with user authentication. -Public metrics can be requested with [App-only Token](/resources/fundamentals/authentication#oauth-2-0) authentication. Non-public metrics can be requested for owned/authorized Posts only, meaning developers need to authenticate using [OAuth 2.0](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) or [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) user context authorization. +--- + +## Metric types + +| Type | Authentication | Description | +|:-----|:---------------|:------------| +| **Public** | Bearer Token | Visible to anyone (likes, reposts, replies) | +| **Non-public** | User context | Private metrics (impressions, clicks) | +| **Organic** | User context | Metrics from non-promoted views | +| **Promoted** | User context | Metrics from ad views | + + +**30-day limit**: Non-public, organic, and promoted metrics are only available for posts created within the last 30 days. + -**Non-public, organic, and promoted metrics are only available for Posts created within the last 30 days.** +--- + +## Available metrics -### Terminology -- **Authorized account**: An X account that has authorized your [X developer app](/resources/fundamentals/developer-apps) by granting it access to that account (any [app permission level](/resources/fundamentals/developer-apps#app-permissions) will permit access to Post metrics). -- **Owned account**: An X account linked to your [X developer app](/resources/fundamentals/developer-apps). -- **Public metrics**: Totals that are available for anyone to access on [X](https://x.com/home), such as the number of likes and number of Retweets. -- **Non-public metrics**: Totals not available for public viewing on [X](https://x.com/home), such as the number of impressions and video view quartiles. Requires [OAuth 2.0](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) or [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) user context authentication. -- **Organic metrics**: A grouping of public and non-public metrics attributed to an organic context (posted and viewed in a regular manner). Requires [OAuth 2.0](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) or [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) user context authentication. -- **Promoted metrics**: A grouping of public and non-public metrics attributed to a promoted context (posted or viewed as part of an Ads campaign). Requires [OAuth 2.0](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) or [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) user context authentication and that the Post was promoted in an Ad. +### Post metrics -## Available Metrics +| Metric | Type | Field path | +|:-------|:-----|:-----------| +| Reposts | Public | `public_metrics.retweet_count` | +| Quotes | Public | `public_metrics.quote_count` | +| Likes | Public | `public_metrics.like_count` | +| Replies | Public | `public_metrics.reply_count` | +| Impressions | Non-public | `non_public_metrics.impression_count` | +| URL clicks | Non-public | `non_public_metrics.url_link_clicks` | +| Profile clicks | Non-public | `non_public_metrics.user_profile_clicks` | -| Metric | API Representations | Description | -|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **Impressions** | `data.non_public_metrics.impression_count`, `data.organic_metrics.impression_count`, `data.promoted_metrics.impression_count` | A count of how many times the Post has been viewed (not unique by user). A view is counted if any part of the Post is visible on the screen. Requires OAuth 1.0a User Context authentication. | -| **Retweets** | `data.public_metrics.retweet_count`, `data.organic_metrics.retweet_count`, `data.promoted_metrics.retweet_count` | A count of how many times the Post has been Retweeted. Does not include Quote Tweets (“Retweets with comment”). To get the "Retweets and comments" total as displayed on X clients, add `retweet_count` and `quote_count`. | -| **Quote Tweets** | `data.public_metrics.quote_count` | A count of how many times the Post has been Retweeted with a new comment (message). There are no Quote Tweets from a paid context, so all Quote Tweets are organic. | -| **Likes** | `data.public_metrics.like_count`, `data.organic_metrics.like_count`, `data.promoted_metrics.like_count` | A count of how many times the Post has been liked. The `public_metrics` field returns the total count of likes from both organic and paid contexts to maintain consistency with counts shown publicly on X. | -| **Replies** | `data.public_metrics.reply_count`, `data.organic_metrics.reply_count`, `data.promoted_metrics.reply_count` | A count of how many times the Post has been replied to. The `public_metrics` field returns the total count of replies from both organic and paid contexts. | -| **URL Link Clicks** | `data.non_public_metrics.url_link_clicks`, `data.organic_metrics.url_link_clicks`, `data.promoted_metrics.url_link_clicks` | A count of the number of times a user clicks on a URL link or URL preview card in a Post. Requires OAuth 1.0a User Context authentication. | -| **User Profile Clicks** | `data.non_public_metrics.user_profile_clicks`, `data.organic_metrics.user_profile_clicks`, `data.promoted_metrics.user_profile_clicks` | A count of the number of times a user clicks on portions of a Post: display name, user name, profile picture. Requires OAuth 1.0a User Context authentication. | -| **Video views** | `includes.media.public_metrics.view_count`, `includes.media.organic_metrics.view_count`, `includes.media.promoted_metrics.view_count` | A count of how many times the video included in the Post has been viewed. This is the number of video views aggregated across all Posts in which the given video has been posted. Requires media expansion `expansions=attachment.media_keys`. | -| **Video view quartiles** | `includes.media.non_public_metrics.playback_0_count`, `includes.media.non_public_metrics.playback_25_count`, `includes.media.non_public_metrics.playback_50_count`, `includes.media.non_public_metrics.playback_75_count`, `includes.media.non_public_metrics.playback_100_count` | The number of users who played through each quartile in a video. Requires OAuth 1.0a User Context authentication and media expansion `expansions=attachment.media_keys`. | +### Media metrics (videos) -## Requesting Metrics +| Metric | Type | Field path | +|:-------|:-----|:-----------| +| Views | Public | `public_metrics.view_count` | +| Playback 0% | Non-public | `non_public_metrics.playback_0_count` | +| Playback 25% | Non-public | `non_public_metrics.playback_25_count` | +| Playback 50% | Non-public | `non_public_metrics.playback_50_count` | +| Playback 75% | Non-public | `non_public_metrics.playback_75_count` | +| Playback 100% | Non-public | `non_public_metrics.playback_100_count` | + +--- -### Public Metrics -In the following request, we are requesting public metrics on the Post and on the attached video with the following fields and expansion. Be sure to replace `$BEARER_TOKEN` with your own generated bearer token. +## Requesting metrics -- `tweet.fields=public_metrics` -- `expansions=attachments.media_keys&media.fields=public_metrics` +### Public metrics (any auth) -#### Sample Request ```bash -curl 'https://api.x.com/2/tweets?ids=1204084171334832128&tweet.fields=public_metrics&expansions=attachments.media_keys&media.fields=public_metrics' --header 'Authorization: Bearer $BEARER_TOKEN' +curl "https://api.x.com/2/tweets/1234567890?tweet.fields=public_metrics" \ + -H "Authorization: Bearer $TOKEN" +``` + +Response: + +```json +{ + "data": { + "id": "1234567890", + "text": "Hello world!", + "public_metrics": { + "retweet_count": 50, + "reply_count": 12, + "like_count": 234, + "quote_count": 5 + } + } +} ``` -### Private Metrics (Non-public, Organic Metrics) -The following request asks for non-public metrics with additional details on organic metrics for the Post and attached video. Since these fields are private and not available for public view on X, the request requires [OAuth 2.0](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) or [OAuth 1.0a](/resources/fundamentals/authentication#oauth-1-0a-2) user context authentication. Refer to our [guide](/resources/fundamentals/authentication#creating-a-signature) for generating the OAuth 1.0a signature needed below. +### Private metrics (user context) -- `tweet.fields=non_public_metrics,organic_metrics` -- `expansions=attachments.media_keys&media.fields=non_public_metrics,organic_metrics` +Requires OAuth 1.0a or OAuth 2.0 with user context for posts you own: -#### Sample Request ```bash -curl 'https://api.x.com/2/tweets/1204084171334832128?tweet.fields=non_public_metrics,organic_metrics&media.fields=non_public_metrics,organic_metrics&expansions=attachments.media_keys' --header 'authorization: OAuth oauth_consumer_key="CONSUMER_API_KEY", oauth_nonce="OAUTH_NONCE", oauth_signature="OAUTH_SIGNATURE", oauth_signature_method="HMAC-SHA1", oauth_timestamp="OAUTH_TIMESTAMP", oauth_token="ACCESS_TOKEN", oauth_version="1.0"' +curl "https://api.x.com/2/tweets/1234567890?tweet.fields=non_public_metrics,organic_metrics" \ + -H "Authorization: OAuth oauth_consumer_key=...,oauth_token=..." ``` -#### Sample Response -``` +Response: + +```json { "data": { - "attachments": { - "media_keys": ["13_1204080851740315648"] - }, - "id": "1263145271946551300", + "id": "1234567890", + "text": "Hello world!", "non_public_metrics": { - "impression_count": 956, - "url_link_clicks": 9, - "user_profile_clicks": 34 + "impression_count": 5432, + "url_link_clicks": 89, + "user_profile_clicks": 156 }, "organic_metrics": { - "impression_count": 956, - "like_count": 49, - "reply_count": 2, - "retweet_count": 9, - "url_link_clicks": 9, - "user_profile_clicks": 34 - }, - "text": "test" + "impression_count": 5432, + "like_count": 234, + "reply_count": 12, + "retweet_count": 50, + "url_link_clicks": 89, + "user_profile_clicks": 156 + } + } +} +``` + +--- + +## Video metrics + +For video playback metrics, use the media expansion: + +```bash +curl "https://api.x.com/2/tweets/1234567890?\ +tweet.fields=attachments&\ +expansions=attachments.media_keys&\ +media.fields=public_metrics,non_public_metrics" \ + -H "Authorization: OAuth ..." +``` + +Response: + +```json +{ + "data": { + "id": "1234567890", + "text": "Check out this video!", + "attachments": { + "media_keys": ["13_9876543210"] + } }, "includes": { - "media": [ - { - "media_key": "13_1204080851740315648", - "non_public_metrics": { - "playback_0_count": 0, - "playback_100_count": 1, - "playback_25_count": 2, - "playback_50_count": 1, - "playback_75_count": 1 - }, - "organic_metrics": { - "playback_0_count": 0, - "playback_100_count": 1, - "playback_25_count": 2, - "playback_50_count": 1, - "playback_75_count": 1, - "view_count": 1 - }, - "type": "video" + "media": [{ + "media_key": "13_9876543210", + "type": "video", + "public_metrics": { + "view_count": 12543 + }, + "non_public_metrics": { + "playback_0_count": 12543, + "playback_25_count": 9876, + "playback_50_count": 7654, + "playback_75_count": 5432, + "playback_100_count": 3210 } - ] + }] } } ``` + +--- + +## Organic vs. promoted metrics + +If a post was promoted as an ad, metrics split between organic and promoted views: + +| Context | Description | +|:--------|:------------| +| **Organic** | Metrics from normal timeline views | +| **Promoted** | Metrics from paid ad impressions | +| **Public** | Combined total (organic + promoted) | + +Request both to see the breakdown: + +```bash +tweet.fields=public_metrics,organic_metrics,promoted_metrics +``` + +--- + +## Metric definitions + + +Count of times the post appeared on a user's screen. Not unique—the same user viewing twice counts as two impressions. + + + +Number of reposts (retweets). Does not include quote posts. + + + +Number of quote posts (reposts with comment). These are always organic. + + + +Aggregated across all posts containing the video. A video reposted in multiple posts has one total view count. + + + +Number of unique users who played through each percentage of the video. Useful for understanding drop-off rates. + + +--- + +## Requirements summary + +| Metric field | Authentication required | +|:-------------|:-----------------------| +| `public_metrics` | Bearer Token (any) | +| `non_public_metrics` | User context (owned posts only) | +| `organic_metrics` | User context (owned posts only) | +| `promoted_metrics` | User context (promoted posts only) | + +--- + +## Next steps + + + + Complete field reference. + + + Set up user context auth. + + diff --git a/x-api/fundamentals/pagination.mdx b/x-api/fundamentals/pagination.mdx index fc2b4d105..c515114ff 100644 --- a/x-api/fundamentals/pagination.mdx +++ b/x-api/fundamentals/pagination.mdx @@ -1,52 +1,197 @@ --- title: Pagination -keywords: ["pagination", "pagination tokens", "next token", "previous token", "page through results", "paging", "cursor pagination", "pagination token"] +sidebarTitle: Pagination +description: Navigate through large result sets +keywords: ["pagination", "pagination tokens", "next token", "page through results", "cursor pagination"] --- -## Introduction +When an API response contains more results than can be returned at once, use pagination to retrieve all pages of data. -Pagination is a feature in X API v2 endpoints that return more results than can be returned in a single response. When that happens, the data is returned in a series of "pages." Pagination refers to methods for programmatically requesting all the pages to retrieve the entire result data set. Not all API endpoints support or require pagination, but it is often used when result sets are large. +--- + +## How pagination works + +1. Make your initial request with `max_results` +2. Check the response for a `next_token` in the `meta` object +3. If present, make another request with that token as `pagination_token` +4. Repeat until no `next_token` is returned + +```bash +# Initial request +curl "https://api.x.com/2/users/12345/tweets?max_results=100" \ + -H "Authorization: Bearer $TOKEN" + +# Response includes next_token +# {"data": [...], "meta": {"next_token": "abc123", ...}} + +# Next page +curl "https://api.x.com/2/users/12345/tweets?max_results=100&pagination_token=abc123" \ + -H "Authorization: Bearer $TOKEN" +``` - ### Use cases for pagination +--- + +## Pagination tokens - **To retrieve all results for a request:** Pagination should be used to receive all relevant data related to a specific request and parameters. Pagination is required in cases where there are more matching results than the `max_results` for a request. Looping requests with pagination tokens allows you to retrieve all results. Once a response is returned without a `next_token` value, it can be assumed that all results have been paged through. Pagination should not be used for polling purposes. To get the most recent results since a previous request, review polling with `since_id`. +| Token | Description | +|:------|:------------| +| `next_token` | In response `meta`. Use to get the next page. | +| `previous_token` | In response `meta`. Use to go back a page. | +| `pagination_token` | Request parameter. Set to `next_token` or `previous_token` value. | - **To traverse through the results of a request:** Pagination provides directional options for navigating through results from a request, using `next_token` and `previous_token` values from responses. These tokens can be set as the `pagination_token` in the following request to go to the next or previous page of results. +--- - ### Pagination token definitions +## Response structure + +```json +{ + "data": [ + {"id": "1234", "text": "..."}, + {"id": "1235", "text": "..."} + ], + "meta": { + "result_count": 100, + "next_token": "7140w9gefhslx3", + "previous_token": "77qp89slxjd" + } +} +``` + +When there are no more results, `next_token` is omitted: + +```json +{ + "data": [...], + "meta": { + "result_count": 42, + "previous_token": "77qp89abc" + } +} +``` - - `next_token` - Opaque string returned within the meta object response on endpoints that support pagination. Indicates that more results are available and can be used as the `pagination_token` parameter in the next request to return the next page of results. The last page of results will not have a `next_token` present. - - `previous_token` - Opaque string returned withins the meta object response on endpoints that support pagination. Indicates that there is a previous page of results available and can be set as the `pagination_token` parameter in the next request to return the previous page of results. - - `pagination_token` - Parameter used in pagination requests. Set to the value of `next_token` for the next page of results. Set to the value of `previous_token` for the previous page of results. +--- - ### Fundamentals of pagination +## Pagination parameters - - Endpoints that use pagination will respond to an initial request with the first page of results and provide a `next_token` within the meta object in the JSON response if additional pages of results are available. To receive all results, repeat this process until no `next_token` is included in the response. - - Results are delivered in reverse-chronological order. This is true within individual pages, as well as across multiple pages: - - The first Post in the first response will be the most recent. - - The last Post in the last response will be the oldest. - - The `max_results` request parameter allows you to configure the number of Posts returned per response page. There is a default and maximum `max_results`. - - Every pagination implementation will involve parsing tokens from the response payload, which can be used in subsequent requests. - - In some circumstances, you may get fewer than the `max_results` per page. Do not rely on the results per page always equaling the `max_results` parameter value. - - The best ways to use pagination for complete results are by using loop logic within integration code or by using a [library](/x-api/tools-and-libraries/overview) that supports X API v2. +| Parameter | Description | Default | +|:----------|:------------|:--------| +| `max_results` | Results per page | Endpoint-specific | +| `pagination_token` | Token from previous response | None | - ### Pagination example +Check each endpoint's API reference for specific `max_results` limits. - Here, there are three pages of results because `max_results` is set to `100`, and there are approximately 295 Posts created by user ID `2244994945` (@XDevelopers) between January 1, 2019, at 17:00:00 UTC and December 12 at 00:00:00 UTC. The first Post on the first page (`1337498609819021312`) is the most recent, and the last Post on the third page of results (`1082718487011885056`) is the oldest. +--- - #### Initial request +## Example: Paginating through all results + + + +```python +import requests + +def get_all_tweets(user_id, bearer_token): + url = f"https://api.x.com/2/users/{user_id}/tweets" + headers = {"Authorization": f"Bearer {bearer_token}"} + params = {"max_results": 100} + + all_tweets = [] + + while True: + response = requests.get(url, headers=headers, params=params) + data = response.json() + + if "data" in data: + all_tweets.extend(data["data"]) + + # Check for next page + next_token = data.get("meta", {}).get("next_token") + if not next_token: + break + + params["pagination_token"] = next_token + + return all_tweets +``` + + +```javascript +async function getAllTweets(userId, bearerToken) { + const url = `https://api.x.com/2/users/${userId}/tweets`; + const headers = { Authorization: `Bearer ${bearerToken}` }; + + let allTweets = []; + let paginationToken = null; + + do { + const params = new URLSearchParams({ max_results: 100 }); + if (paginationToken) { + params.set("pagination_token", paginationToken); + } + + const response = await fetch(`${url}?${params}`, { headers }); + const data = await response.json(); + + if (data.data) { + allTweets.push(...data.data); + } + + paginationToken = data.meta?.next_token; + } while (paginationToken); + + return allTweets; +} +``` + + - ```bash - https://api.x.com/2/users/2244994945/tweets?tweet.fields=created_at&max_results=100&start_time=2019-01-01T17:00:00Z&end_time=2020-12-12T01:00:00Z - ``` +--- - -### Sequence Table +## Best practices + + + + Request the maximum allowed `max_results` to minimize API calls. + + + The last page may have fewer results than `max_results`. + + + Save `next_token` if you need to resume pagination later. + + + For new data, use `since_id` instead of paginating repeatedly. + + +--- + +## Result ordering + +Results are returned in **reverse chronological order**: + +- First result on first page = most recent +- Last result on last page = oldest + +This applies within and across pages. + +--- + +## Notes + +- Pagination tokens are opaque strings—don't parse or modify them +- Tokens may expire after some time +- If you get fewer results than `max_results`, more may still exist (continue until no `next_token`) +- Use [SDKs](/x-api/tools-and-libraries/sdks) for automatic pagination handling + +--- -| | **First Request** | **Second Page** | **Third Page** | **Fourth Page** | -|:-------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------| -| **Request Parameters** | - `max_results` = `100` - `tweet.fields` = `created_at` - `start_time` = `2019-01-01T17:00:00Z` - `end_time` = `2020-12-12T01:00:00Z` | - `max_results` = `100` - `tweet.fields` = `created_at` - `start_time` = `2019-01-01T17:00:00Z` - `end_time` = `2020-12-12T01:00:00Z` - `pagination_token` = `7140w` | - `max_results` = `100` - `tweet.fields` = `created_at` - `start_time` = `2019-01-01T17:00:00Z` - `end_time` = `2020-12-12T01:00:00Z` - `pagination_token` = `7140k9` | - `max_results` = `100` - `tweet.fields` = `created_at` - `start_time` = `2019-01-01T17:00:00Z` - `end_time` = `2020-12-12T01:00:00Z` - `pagination_token` = `71408hi` | -| **Response** | ```json { "data": [ { "created_at": "2020-12-11T20:44:52.000Z", "id": "1337498609819021312", "text": "Thanks to everyone who tuned in today..." }, ... , { "created_at": "2020-05-06T17:24:31.000Z", "id": "1258085245091368960", "text": "It’s now easier to understand Tweet impact..." } ], "meta": { "oldest_id": "1258085245091368960", "newest_id": "1337498609819021312", "result_count": 100, "next_token": "7140w" } } ``` | ```json { "data": [ { "created_at": "2020-04-29T17:01:44.000Z", "id": "1255542797765013504", "text": "Our developer community is full of inspiring ideas..." }, ... , { "created_at": "2019-11-21T16:17:23.000Z", "id": "1197549579035496449", "text": "Soon, we'll be releasing tools in..." } ], "meta": { "oldest_id": "1197549579035496449", "newest_id": "1255542797765013504", "result_count": 100, "next_token": "7140k9", "previous_token": "77qp8" } } ``` | ```json { "data": [ { "created_at": "2019-11-21T16:17:23.000Z", "id": "1197549578418941952", "text": "We know that some people who receive a large volume of replies may..." }, ... , { "created_at": "2019-01-08T19:19:37.000Z", "id": "1082718487011885056", "text": "Updates to Grid embeds..." } ], "meta": { "oldest_id": "1082718487011885056", "newest_id": "1197549578418941952", "result_count": 95, "next_token": "71408hi", "previous_token": "77qplte" } } ``` | ```json { "meta": { "result_count": 0, "previous_token": "77qpw8l" } } ``` | -| **Actions to Take for Next Request** | To get the next page, take the `next_token` value directly from the response (`7140w`) and set it as the `pagination_token` for the next request call. | To continue to get all results: take the `next_token` value directly from the response (`7140k9`) and set it as the `pagination_token` for the next request call. To traverse to the previous page: take the `previous_token` value directly from the response (`77qp8`) and set it as the `pagination_token` for the next request call. | To continue to get all results: take the `next_token` value directly from the response (`71408hi`) and set it as the `pagination_token` for the next request call. To traverse to the previous page: take the `previous_token` value directly from the response (`77qplte`) and set it as the `pagination_token` for the next request call. | Note that there is no `next_token`, which indicates that all results have been received. To traverse to the previous page: take the `previous_token` value directly from the response (`77qpw8l`) and set it as the `pagination_token` for the next request call. | +## Next steps + + + Understand request limits when paginating. + + + Libraries with built-in pagination. + + diff --git a/x-api/fundamentals/post-annotations.mdx b/x-api/fundamentals/post-annotations.mdx index 18762ebf7..e9df5ea38 100644 --- a/x-api/fundamentals/post-annotations.mdx +++ b/x-api/fundamentals/post-annotations.mdx @@ -1,279 +1,232 @@ --- -title: "Post Annotations" -keywords: ["annotations", "post annotations", "entity annotations", "context annotations", "NER", "named entity recognition", "topics", "entities", "contextual information"] +title: Post Annotations +sidebarTitle: Annotations +description: Entity and context annotations for semantic analysis +keywords: ["annotations", "post annotations", "entity annotations", "context annotations", "NER", "named entity recognition", "topics"] --- -## Overview -Annotations have been added to the Post object from all v2 endpoints that return a Post object. Post annotations offer a way to understand contextual information about the Post itself. Though 100% of Posts are reviewed, due to the contents of Post text, only a portion are annotated. - -1. **Entity annotations (NER):** Entities include people, places, products, and organizations, and are delivered in the `entity` payload section. They are programmatically assigned based on what is explicitly mentioned (named-entity recognition) in the Post text. - -2. **Context annotations:** Derived from analyzing a Post's text, context annotations include a domain and entity pairing to help discover Posts on topics that may have been previously difficult to surface. We currently use 80+ domains to categorize Posts. A CSV file of available context annotation entities is available in our [Github repository](https://github.com/xdevplatform/twitter-context-annotations). - -## Post Annotation Types - -### Entities -Entity annotations are programmatically defined entities within the `entities` field and are reflected as annotations in the payload. Each annotation has a confidence score and indicates where in the Post text the entities were identified (using `start` and `end` fields). - -The entity annotation types include: -- **Person** - Examples: Barack Obama, Daniel, George W. Bush -- **Place** - Examples: Detroit, Cali, San Francisco -- **Product** - Examples: Mountain Dew, Mozilla Firefox -- **Organization** - Examples: Chicago White Sox, IBM -- **Other** - Examples: Diabetes, Super Bowl 50 - -### Context - -*Last updated: June 2022* - -Context annotations are delivered in the `context_annotations` field of the payload. They are inferred based on semantic analysis of keywords, hashtags, handles, etc., in the Post text and result in domain and/or entity labels. Currently, we use 80+ domains, as shown in the table below. - -| Domain Categories | Domain Codes | -| :------------------------------------------- | :--------------------------------------- | -| 3: TV Shows | 46: Brand Category | -| 4: TV Episodes | 47: Brand | -| 6: Sports Events | 48: Product | -| 10: Person | 54: Musician | -| 11: Sport | 55: Music Genre | -| 12: Sports Team | 56: Actor | -| 13: Place | 58: Entertainment Personality | -| 22: TV Genres | 60: Athlete | -| 23: TV Channels | 65: Interests and Hobbies Vertical | -| 26: Sports League | 66: Interests and Hobbies Category | -| 27: American Football Game | 67: Interests and Hobbies | -| 28: NFL Football Game | 68: Hockey Game | -| 29: Events | 71: Video Game | -| 31: Community | 78: Video Game Publisher | -| 35: Politicians | 79: Video Game Hardware | -| 38: Political Race | 83: Cricket Match | -| 39: Basketball Game | 84: Book | -| 40: Sports Series | 85: Book Genre | -| 43: Soccer Match | 86: Movie | -| 44: Baseball Game | 87: Movie Genre | -| 45: Brand Vertical | 88: Political Body | -| 46: Brand Category | 89: Music Album | -| 47: Brand | 90: Radio Station | -| 48: Product | 91: Podcast | -| 54: Musician | 92: Sports Personality | -| 55: Music Genre | 93: Coach | -| 56: Actor | 94: Journalist | -| 58: Entertainment Personality | 95: TV Channel [Entity Service] | -| 60: Athlete | 109: Reoccurring Trends | -| 65: Interests and Hobbies Vertical | 110: Viral Accounts | -| 66: Interests and Hobbies Category | 114: Concert | -| 67: Interests and Hobbies | 115: Video Game Conference | -| 68: Hockey Game | 116: Video Game Tournament | -| 71: Video Game | 117: Movie Festival | -| 78: Video Game Publisher | 118: Award Show | -| 79: Video Game Hardware | 119: Holiday | -| 83: Cricket Match | 120: Digital Creator | -| 84: Book | 122: Fictional Character | -| 85: Book Genre | 130: Multimedia Franchise | -| 86: Movie | 131: Unified Twitter Taxonomy | -| 87: Movie Genre | 136: Video Game Personality | -| 88: Political Body | 137: eSports Team | -| 89: Music Album | 138: eSports Player | -| 90: Radio Station | 139: Fan Community | -| 91: Podcast | 149: Esports League | -| 92: Sports Personality | 152: Food | -| 93: Coach | 155: Weather | -| 94: Journalist | 156: Cities | -| 95: TV Channel [Entity Service] | 157: Colleges & Universities | -| 109: Reoccurring Trends | 158: Points of Interest | -| 110: Viral Accounts | 159: States | -| 114: Concert | 160: Countries | -| 115: Video Game Conference | 162: Exercise & Fitness | -| 116: Video Game Tournament | 163: Travel | -| 117: Movie Festival | 164: Fields of Study | -| 118: Award Show | 165: Technology | -| 119: Holiday | 166: Stocks | -| 120: Digital Creator | 167: Animals | -| 122: Fictional Character | 171: Local News | -| 130: Multimedia Franchise | 172: Global TV Show | -| 131: Unified Twitter Taxonomy | 173: Google Product Taxonomy | -| 136: Video Game Personality | 174: Digital Assets & Crypto | -| 137: eSports Team | 175: Emergency Events | -| 138: eSports Player | | - -*Note:* Domain 131 (Unified Twitter Taxonomy) refers to X's User Facing Interest Taxonomy. This taxonomy helps to power features on the platform such as [Topics](https://blog.x.com/en_us/topics/product/2020/topics-behind-the-tweets). - -## Requesting Annotations - -### Sample Request +Annotations provide semantic metadata about post content. X analyzes posts to identify entities (people, places, products) and context (topics, domains) to help you understand and filter content. + +--- + +## Annotation types + +### Entity annotations + +Named-entity recognition (NER) identifies specific mentions in post text: + +| Type | Examples | +|:-----|:---------| +| **Person** | Barack Obama, Elon Musk | +| **Place** | San Francisco, Japan | +| **Product** | iPhone, ChatGPT | +| **Organization** | NASA, Google | +| **Other** | Super Bowl, Diabetes | + +Entity annotations include a confidence score and position in the text. + +### Context annotations + +Semantic analysis that classifies posts by topic and domain: + +- **Domain**: Broad category (Sports, Entertainment, Technology) +- **Entity**: Specific topic within the domain (NBA, Marvel Movies, AI) + +Context annotations help filter and categorize posts without relying on keywords. + +--- + +## Requesting annotations + +Add `context_annotations` and `entities` to your `tweet.fields`: + ```bash -curl --location --request GET 'https://api.x.com/2/tweets/1212092628029698048?tweet.fields=context_annotations,entities' --header 'Authorization: Bearer $BEARER_TOKEN' +curl "https://api.x.com/2/tweets/1234567890?tweet.fields=context_annotations,entities" \ + -H "Authorization: Bearer $TOKEN" ``` -### Sample Response +--- -``` +## Response structure + +```json { - "data": { - "context_annotations": [ - { - "domain": { - "id": "119", - "name": "Holiday", - "description": "Holidays like Christmas or Halloween" - }, - "entity": { - "id": "1186637514896920576", - "name": "New Years Eve" - } - }, - { - "domain": { - "id": "119", - "name": "Holiday", - "description": "Holidays like Christmas or Halloween" - }, - "entity": { - "id": "1206982436287963136", - "name": "Happy New Year: It’s finally 2020 everywhere!", - "description": "Catch fireworks and other celebrations as people across the globe enter the new year.\nPhoto via @GettyImages" - } - }, - { - "domain": { - "id": "45", - "name": "Brand Vertical", - "description": "Top level entities that describe a Brand's industry" - } - }, - { - "domain": { - "id": "46", - "name": "Brand Category", - "description": "Categories within Brand Verticals that narrow down the scope of Brands" - }, - "entity": { - "id": "781974596752842752", - "name": "Services" - } - }, - { - "domain": { - "id": "47", - "name": "Brand", - "description": "Brands and Companies" - }, - "entity": { - "id": "10045225402", - "name": "Twitter" - } - } - ], - "entities": { - "annotations": [ - { - "start": 144, - "end": 150, - "probability": 0.626, - "type": "Product", - "normalized_text": "Twitter" - } - ], - "urls": [ - { - "start": 222, - "end": 245, - "url": "https://t.co/yvxdK6aOo2", - "expanded_url": "https://x.com/LovesNandos/status/1211797914437259264/photo/1", - "display_url": "pic.x.com/yvxdK6aOo2" - } - ] + "data": { + "id": "1234567890", + "text": "Just saw the new Marvel movie - it was amazing!", + "entities": { + "annotations": [ + { + "start": 17, + "end": 22, + "probability": 0.9234, + "type": "Organization", + "normalized_text": "Marvel" + } + ] + }, + "context_annotations": [ + { + "domain": { + "id": "86", + "name": "Movie", + "description": "A film" + }, + "entity": { + "id": "1234567890", + "name": "Marvel Cinematic Universe" + } + }, + { + "domain": { + "id": "65", + "name": "Interests and Hobbies Vertical" }, - "id": "1212092628029698048", - "text": "We believe the best future version of our API will come from building it with YOU. Here’s to another great year with everyone who builds on the Twitter platform. We can’t wait to continue working with you in the new year. https://t.co/yvxdK6aOo2" - } + "entity": { + "id": "781974596752842752", + "name": "Entertainment" + } + } + ] + } } ``` -### Sample App -Check out the [Post Entity Extractor on Glitch](https://tweet-entity-extractor.glitch.me/) to easily discover context annotations in Posts and see how this feature works. +--- -## Frequently asked questions +## Entity annotation fields -### Context annotations +| Field | Description | +|:------|:------------| +| `start` | Start position in text | +| `end` | End position in text | +| `probability` | Confidence score (0-1) | +| `type` | Entity type (Person, Place, etc.) | +| `normalized_text` | Standardized entity name | + +--- + +## Context domains + +X uses 80+ domains to categorize posts. Common domains include: + + + +| ID | Domain | +|:---|:-------| +| 3 | TV Shows | +| 4 | TV Episodes | +| 54 | Musician | +| 56 | Actor | +| 86 | Movie | +| 91 | Podcast | + + +| ID | Domain | +|:---|:-------| +| 6 | Sports Events | +| 11 | Sport | +| 12 | Sports Team | +| 26 | Sports League | +| 60 | Athlete | +| 93 | Coach | + + +| ID | Domain | +|:---|:-------| +| 45 | Brand Vertical | +| 46 | Brand Category | +| 47 | Brand | +| 48 | Product | +| 165 | Technology | +| 166 | Stocks | + + +| ID | Domain | +|:---|:-------| +| 10 | Person | +| 13 | Place | +| 29 | Events | +| 35 | Politicians | +| 119 | Holiday | +| 131 | Unified Twitter Taxonomy | + + + + +Domain 131 (Unified Twitter Taxonomy) powers X's Topics feature visible to users on the platform. + + +--- + +## Using annotations in filters + +### Search and filtered stream + +Filter posts by context annotation entity ID: + +```bash +# Posts about a specific entity +context:86.1234567890 + +# Posts in a specific domain +context:86.* +``` + +### Practical examples + +```bash +# Posts about the NBA +query=context:26.852137520 + +# Posts about Apple products +query=context:47.10026792024 + +# Posts about movies +query=context:86.* +``` + +--- + +## Language support + +Annotations are available for multiple languages: + +| Language | Coverage | +|:---------|:---------| +| English | Highest | +| Japanese | High | +| Spanish | High | +| Portuguese | Medium | +| French | Medium | +| Hindi | Medium | + +Coverage varies by domain and market. + +--- + +## Important notes + + +**Not all posts are annotated.** Annotation coverage depends on: +- Language support +- Topic coverage in X's taxonomy +- Semantic richness of the post text + + +- Annotations are not retroactive—only applied when entities are tracked +- The same entity can appear in multiple domains (e.g., a celebrity is both Person and Actor) +- Entity IDs are stable across domains + +--- -The questions below are specific to the context annotations element of Tweet annotations. For more details, please see the [Overview](/x-api/fundamentals/post-annotations) page. - - - Twitter classifies Tweets semantically, meaning that we curate lists of keywords, hashtags, and @handles that are relevant to a given topic. If a Tweet contains the text we’ve specified, it will be labeled appropriately. This differs from a machine learning approach where a model is trained specifically to classify text (in this case, Tweets) and produce a probability score alongside the output/classification. - - - Twitter's annotations are curated by domain experts using research and QA processes that have been refined over the course of several years. The process is supported by custom tooling to scale data tracking as far as we are able to maintain excellent precision and recall. In addition, our data is audited regularly by an internal team, having received a precision score of ~80% for the past several quarters. - - - Team members QA our entities on a daily basis to ensure high precision and recall. Additionally, our work is audited quarterly by an internal team, which manually reviews 10,000 Tweets across all of our domains to calculate a precision score. - - - For some domains, like sports and TV, we rely on automated ingest to build out our graph. In the News domain, we track data around stories published by the Twitter Moments team. Otherwise, the team uses a variety of research methods to identify topics to track that garner a high amount of conversation on the platform. - - - Data tracking begins the moment an entity is published; therefore, we do not annotate Tweets that were published prior to a given entity being tracked. For example, if an upstart brand/company is added to the taxonomy, we will not retroactively annotate Tweets about that brand prior to when the annotation was added. - - - Yes. Language coverage can vary depending on the domain and the market. English and Japanese are included in the majority of the biggest entities. Below, is a list of the languages and main markets that are covered today: - 1. English (US, UK) - 2. Japanese (Japan) - 3. Portuguese (Brazil) - 4. Spanish (Argentina, Mexico, Spain) - 5. Hindi (India) - 6. Arabic (Saudi Arabia) - 7. Turkish (Turkey) - 8. Indonesian (Indonesia) - 9. Russian (Russia) - 10. French (France) - - Coming soon (~H2 2021): - 1. German (Germany) - 2. Tamil (India) - - Below is a table of the top 15 countries ordered by the most coverage of annotated Tweets: - - | Rank | Country code | Country | % of Tweets annotated | - | :--- | :--- | :--- | :--- | - | 1 | IN | India | 41% | - | 2 | VN | Vietnam | 36% | - | 3 | GB | Great Britain | 36% | - | 4 | EC | Ecuador | 35% | - | 5 | PE | Peru | 33% | - | 6 | US | United States | 32% | - | 7 | CA | Canada | 32% | - | 8 | AU | Australia | 31% | - | 9 | JP | Japan | 31% | - | 10 | PH | Philippines | 30% | - | 11 | SG | Singapore | 30% | - | 12 | MY | Malaysia | 30% | - | 13 | MX | Mexico | 30% | - | 14 | GB | Great Britain | 29% | - | 15 | NG | Nigeria | 29% | - - - Tweet annotations consist of the following semantics to annotate a Tweet: - * Accounts - we can annotate tweets from a given handle or mentioning this handle - * Hashtags - * Keywords/phrases - - For customers that are familiar with the filtered steaming APIs such as PowerTrack, the semantics used by annotations are similar in principle to the boolean rules defined to filter a stream of Tweets. If a Tweet matches the underlying semantic conditions, it will be tagged accordingly. - - - The goal is to annotate as many Tweets as possible; however, there are several reasons why some Tweets are not annotated: - * Some Tweets aren't semantically rich enough to be labelled and can't be tagged with our current annotation rules - * Some Tweets aren't topical - * The Tweet is about a very ephemeral topic that's not in our graph - * We don't cover the language/market - * We cover the language/market but we're missing a topic or a specific term/account/hashtag related to a topic we already track - - - An entity can be part of multiple domains. The domain IDs will change but the entity ID remains the same. Donald Glover is a person (domain 10), an actor (domain 56) and a musician (domain 54) but his entity ID is still 875072662527029248. - - - Tracking starts a month prior to the release. For popular blockbusters, like a Marvel movie, we can start tracking them as soon as they start teasing about an upcoming release. - - - No, they do not. - - +## Resources + + + Interactive tool to discover annotations. + + + CSV of available context annotation entities. + + diff --git a/x-api/fundamentals/post-cap.mdx b/x-api/fundamentals/post-cap.mdx index c0a74bc3b..e6553a747 100644 --- a/x-api/fundamentals/post-cap.mdx +++ b/x-api/fundamentals/post-cap.mdx @@ -1,39 +1,144 @@ --- -title: "Post caps" -keywords: ["post caps", "tweet caps", "consumption limits", "monthly limits", "API limits", "rate limits", "tweet consumption", "post consumption"] +title: Usage and Billing +sidebarTitle: Usage & Billing +description: Understanding API usage tracking and pay-per-usage billing +keywords: ["usage", "billing", "pay-per-usage", "API usage", "usage tracking", "post consumption", "API costs"] --- -X API v2 has a Post consumption cap that limits the number of Posts that can be retrieved from specific endpoints on a monthly basis. Post caps apply at the [Project](/resources/fundamentals/projects) level, meaning that any requests to the endpoints listed below using the keys and tokens from [developer Apps](/resources/fundamentals/developer-apps) within that Project will count towards the Project Post cap. +X API v2 uses **pay-per-usage** pricing. You're charged based on actual API consumption, tracked at the app level. -## Limited v2 endpoints +--- + +## How billing works + +| Concept | Description | +|:--------|:------------| +| **Credit-based** | Purchase credits upfront, deducted as you use the API | +| **Per-request pricing** | Different endpoints have different costs | +| **Real-time tracking** | Monitor usage in the Developer Console | +| **No caps** | Use as much as you need—no monthly limits | + +--- -Posts received from any of the following endpoints count towards this monthly Post cap: +## Tracked endpoints -- [Posts Lookup](/x-api/posts/lookup/introduction) -- [Recent search](/x-api/posts/counts/introduction) -- [Full-archive search](/x-api/posts/counts/introduction) -- [Filtered stream](/x-api/posts/filtered-stream) -- [Filtered Stream Webhooks](/x-api/webhooks/stream/introduction) -- [User Post timeline](/x-api/posts/timelines) -- [User mention timeline](/x-api/posts/timelines) -- [Likes lookup - Posts liked by a user](/x-api/posts/likes) -- [Bookmarks lookup - Posts bookmarked by a user](/x-api/posts/bookmarks/introduction) -- [List Posts lookup](/x-api/lists/list-tweets/introduction) -- [Spaces Lookup](/x-api/spaces/lookup/introduction) +Posts retrieved from these endpoints count toward usage: + +| Category | Endpoints | +|:---------|:----------| +| **Post lookup** | [GET /2/tweets](/x-api/posts/lookup/introduction) | +| **Search** | [Recent search](/x-api/posts/search/introduction), [Full-archive search](/x-api/posts/search/introduction) | +| **Streaming** | [Filtered stream](/x-api/posts/filtered-stream/introduction), [Filtered stream webhooks](/x-api/webhooks/stream/introduction) | +| **Timelines** | [User posts](/x-api/posts/timelines/introduction), [User mentions](/x-api/posts/timelines/introduction) | +| **Engagement** | [Liked posts](/x-api/posts/likes/introduction), [Bookmarks](/x-api/posts/bookmarks/introduction) | +| **Lists** | [List posts](/x-api/lists/list-tweets/introduction) | +| **Spaces** | [Spaces lookup](/x-api/spaces/lookup/introduction) | + +--- + +## Deduplication -**Note:** If the same Post is returned from multiple queries during a day, it only counts once against the Post cap; Posts are deduplicated daily. +**Daily deduplication**: If the same post is returned from multiple queries within a day, it only counts once for billing. -## Post cap volumes +This means: + +- Retrieving the same post multiple times in one day = 1 charge +- Retrieving a post again the next day = 1 additional charge +- Different posts in the same request = each counts separately -The Post cap volume varies based on your access level: +--- + +## Monitoring usage + +Track your usage in the [Developer Console](https://console.x.com): + +| Metric | Description | +|:-------|:------------| +| **Total usage** | Posts retrieved in the billing period | +| **By endpoint** | Breakdown by endpoint type | +| **By app** | Usage per developer app | +| **Cost tracking** | Real-time cost calculations | +| **Credit balance** | Remaining prepaid credits | + +--- + +## Managing costs + + + + Configure spending limits in the Developer Console. + + + Get notified before hitting budget thresholds. + + + Cache responses to avoid re-fetching the same posts. + + + Use precise filters to retrieve only needed posts. + + + +--- + +## Pricing details + +For current pricing per endpoint and operation, visit the [Developer Console](https://console.x.com). + +Pricing may vary by: + +- Endpoint type (search vs. lookup vs. stream) +- Operation (read vs. write) +- Data scope (recent vs. full-archive) + +--- + +## Enterprise options + +For high-volume needs with custom pricing: + +- Dedicated support +- Volume discounts +- Custom rate limits +- Complete data access + + + + Discuss custom solutions for your needs. + + + +--- + +## FAQs + + +API requests will fail until you purchase more credits. Set up balance alerts to avoid interruption. + + + +No. Pay only for what you use. You can have months with zero usage and zero cost. + + + +Each unique post delivered through filtered stream counts toward usage, subject to daily deduplication. + + + +No. Only successful responses that return data are billed. + + +--- -| Access Tier | Monthly Post Cap | -|:---------------|:---------------------------------| -| **Free** | 100 Posts per month | -| **Basic** | 15,000 Posts per month | -| **Pro** | 1,000,000 Posts per month | -| **Enterprise**| 50+ million Posts per month | +## Next steps -You can check your usage towards the monthly Post cap by viewing the [main dashboard page](https://developer.x.com/content/developer-twitter/en/portal/dashboard) in the [developer portal](/resources/fundamentals/developer-portal). Under the name of your Project, you’ll see a status bar that shows your current month’s usage in relation to the Post cap, including the total Posts pulled, the percentage of Posts used, and the reset date for your usage. + + + View pricing and purchase credits. + + + Understand request limits (separate from billing). + + diff --git a/x-api/fundamentals/rate-limits.mdx b/x-api/fundamentals/rate-limits.mdx index caf54097a..902ca4bf3 100644 --- a/x-api/fundamentals/rate-limits.mdx +++ b/x-api/fundamentals/rate-limits.mdx @@ -1,586 +1,490 @@ --- -title: Rate limits -keywords: ["rate limits", "API rate limits", "request limits", "rate limiting", "throttling", "15 minute window", "rate limit errors"] +title: X API Rate Limits +sidebarTitle: Rate Limits +description: Per-endpoint rate limits for X API v2 +keywords: ["rate limits", "API rate limits", "request limits", "rate limiting", "throttling", "15 minute window"] --- +Rate limits control the number of requests you can make to each endpoint. Exceeding limits results in a 429 error until the window resets. -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, limits are placed on the number of requests that can be made. These limits help provide the reliable and scalable API that our developer community relies on. - -The maximum number of requests allowed is based on a time interval, typically over a specified period or window of time. The most common interval is fifteen minutes. For example, an endpoint with a limit of 900 requests per 15 minutes allows up to 900 requests in any 15-minute interval. - -Rate limits depend on the authentication method. For instance, if using [OAuth 1.0a User Context](/resources/fundamentals/authentication), each set of users’ [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) has its own rate limit per period. Alternatively, if using [OAuth 2.0 Bearer Token](/resources/fundamentals/authentication#oauth-2-0), your app will have its own separate limit per time period. When these limits are exceeded, an error is returned. - -## Table of contents - -- [X API v2 rate limits](#v2-limits) -- [X API Enterprise rate limits](#v2-limits-enterprise) -- [Rate limits and authentication method](#auth) -- [HTTP headers and response codes](#headers-and-codes) -- [Recovering from rate limits](#recovering) -- [Tips to avoid being rate limited](#tips) - -## X API v2 rate limits - -The following table lists the rate limits of each X API paid plan. These limits are also available in the [developer portal](/resources/fundamentals/developer-portal)'s products section. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EndpointPro Limit Basic Limit Free Limit
Tweets
DELETE /2/tweets/:id50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
17 requests / 24 hours
PER USER
17 requests / 24 hours
PER APP
DELETE /2/users/:id/likes/:tweet_id50 requests / 15 mins
PER USER
100 requests / 24 hours
PER USER
1 requests / 15 mins
PER USER
DELETE /2/users/:id/retweets/:tweet_id50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
GET /2/tweets900 requests / 15 mins
PER USER
450 requests / 15 mins
PER APP
15 requests / 15 mins
PER USER
15 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/tweets/:id900 requests / 15 mins
PER USER
450 requests / 15 mins
PER APP
15 requests / 15 mins
PER USER
15 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/tweets/:id/liking_users75 requests / 15 mins
PER USER
75 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/tweets/:id/quote_tweets75 requests / 15 mins
PER USER
75 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/tweets/:id/retweeted_by75 requests / 15 mins
PER USER
75 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/tweets/counts/all300 requests / 15 mins
PER APP
GET /2/tweets/counts/recent300 requests / 15 mins
PER APP
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER APP
GET /2/tweets/search/all1 requests / second
PER USER
1 requests / second
PER APP
-
GET /2/tweets/search/recent300 requests / 15 mins
PER USER
450 requests / 15 mins
PER APP
60 requests / 15 mins
PER USER
60 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/tweets/search/stream50 requests / 15 mins
PER APP
GET /2/tweets/search/stream/rules450 requests / 15 mins
PER APP
GET /2/users/:id/liked_tweets75 requests / 15 mins
PER USER
75 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/users/:id/mentions300 requests / 15 mins
PER USER
450 requests / 15 mins
PER APP
10 requests / 15 mins
PER USER
15 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/users/:id/timelines/reverse_chronological180 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
GET /2/users/:id/tweets900 requests / 15 mins
PER USER
1500 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
10 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/users/reposts_of_me75 requests / 15 mins
PER USER
75 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
POST /2/tweets100 requests / 15 mins
PER USER
10000 requests / 24 hours
PER APP
100 requests / 24 hours
PER USER
1667 requests / 24 hours
PER APP
17 requests / 24 hours
PER USER
17 requests / 24 hours
PER APP
POST /2/tweets/search/stream/rules100 requests / 15 mins
PER APP
POST /2/users/:id/likes1000 requests / 24 hours
PER USER
200 requests / 24 hours
PER USER
1 requests / 15 mins
PER USER
POST /2/users/:id/retweets50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
PUT /2/tweets/:tweet_id/hidden50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
Users
DELETE /2/users/:source_user_id/following/:target_user_id50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
DELETE /2/users/:source_user_id/muting/:target_user_id50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
GET /2/users900 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
100 requests / 24 hours
PER USER
500 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
GET /2/users/:id900 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
100 requests / 24 hours
PER USER
500 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
GET /2/users/:id/blocking15 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
GET /2/users/:id/muting15 requests / 15 mins
PER USER
100 requests / 24 hours
PER USER
1 requests / 24 hours
PER USER
GET /2/users/by900 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
100 requests / 24 hours
PER USER
500 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
GET /2/users/by/username/:username900 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
100 requests / 24 hours
PER USER
500 requests / 24 hours
PER APP
3 requests / 15 mins
PER USER
3 requests / 15 mins
PER APP
GET /2/users/me75 requests / 15 mins
PER USER
250 requests / 24 hours
PER USER
25 requests / 24 hours
PER USER
GET /2/users/search900 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
POST /2/users/:id/following50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
POST /2/users/:id/muting50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
Spaces
GET /2/spaces300 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/spaces/:id300 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/spaces/:id/buyers300 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/spaces/:id/tweets300 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/spaces/by/creator_ids300 requests / 15 mins
PER USER
1 requests / second
PER APP
5 requests / 15 mins
PER USER
25 requests / second
PER APP
1 requests / second
PER USER
1 requests / 15 mins
PER APP
GET /2/spaces/search300 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
Direct Messages
DELETE /2/dm_events/:id1500 requests / 24 hours
PER USER
4000 requests / 24 hours
PER APP
200 requests / 15 mins
PER USER
2500 requests / 24 hours
PER APP
GET /2/dm_conversations/:dm_conversation_id/dm_events15 requests / 15 mins
PER USER
1 requests / 24 hours
PER USER
GET /2/dm_conversations/with/:participant_id/dm_events15 requests / 15 mins
PER USER
1 requests / 24 hours
PER USER
GET /2/dm_events15 requests / 15 mins
PER USER
1 requests / 24 hours
PER USER
GET /2/dm_events/:id15 requests / 15 mins
PER USER
5 requests / 24 hours
PER USER
POST /2/dm_conversations15 requests / 15 mins
PER USER
1440 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
POST /2/dm_conversations/:dm_conversation_id/messages15 requests / 15 mins
PER USER
1440 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
POST /2/dm_conversations/with/:participant_id/messages1440 requests / 24 hours
PER USER
1440 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
Lists
DELETE /2/lists/:id300 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
DELETE /2/lists/:id/members/:user_id300 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
DELETE /2/users/:id/followed_lists/:list_id50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
DELETE /2/users/:id/pinned_lists/:list_id50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
GET /2/lists/:id75 requests / 15 mins
PER USER
75 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/lists/:id/members900 requests / 15 mins
PER USER
900 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/lists/:id/tweets900 requests / 15 mins
PER USER
900 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
GET /2/users/:id/list_memberships75 requests / 15 mins
PER USER
75 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/users/:id/owned_lists15 requests / 15 mins
PER USER
15 requests / 15 mins
PER APP
100 requests / 24 hours
PER USER
500 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
GET /2/users/:id/pinned_lists15 requests / 15 mins
PER USER
15 requests / 15 mins
PER APP
100 requests / 24 hours
PER USER
500 requests / 24 hours
PER APP
1 requests / 24 hours
PER USER
1 requests / 24 hours
PER APP
POST /2/lists300 requests / 15 mins
PER USER
100 requests / 24 hours
PER USER
1 requests / 24 hours
PER USER
POST /2/lists/:id/members300 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
POST /2/users/:id/followed_lists50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
POST /2/users/:id/pinned_lists50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
PUT /2/lists/:id300 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
Bookmarks
DELETE /2/users/:id/bookmarks/:tweet_id50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
GET /2/users/:id/bookmarks180 requests / 15 mins
PER USER
10 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
GET /2/users/:id/bookmarks/folders50 requests / 15 mins
PER USER
50 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/users/:id/bookmarks/folders/:folder_id50 requests / 15 mins
PER USER
50 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
POST /2/users/:id/bookmarks50 requests / 15 mins
PER USER
5 requests / 15 mins
PER USER
1 requests / 15 mins
PER USER
Compliance
GET /2/compliance/jobs150 requests / 15 mins
PER APP
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER APP
GET /2/compliance/jobs/:job_id150 requests / 15 mins
PER APP
5 requests / 15 mins
PER APP
1 requests / 15 mins
PER APP
POST /2/compliance/jobs150 requests / 15 mins
PER APP
15 requests / 15 mins
PER APP
1 requests / 15 mins
PER APP
Usage
GET /2/usage/tweets50 requests / 15 mins
PER APP
50 requests / 15 mins
PER APP
1 requests / 15 mins
PER APP
Trends
GET /2/trends/by/woeid/:id75 requests / 15 mins
PER APP
15 requests / 15 mins
PER APP
GET /2/users/personalized_trends10 requests / 15 mins
PER USER
200 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
20 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 24 hours
PER APP
Communities
GET /2/communities/:id300 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
GET /2/communities/search300 requests / 15 mins
PER USER
300 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
1 requests / 15 mins
PER USER
1 requests / 15 mins
PER APP
- - - -## Rate limits and authentication method - -Rate limits are set at both the developer App and user access token levels: - -- **[OAuth 2.0 Bearer Token](/resources/fundamentals/authentication/oauth-2-0/application-only): App rate limit** - This method allows you to make a certain number of requests on behalf of your developer App. When using this authentication method, limits are determined by the requests made with a Bearer Token. - - Example: With a limit of 450 requests per 15-minute interval, you can make 450 requests on behalf of your App within that interval. - -- **[OAuth 1.0a User Context](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens): User rate limit** - This method allows requests to be made on behalf of a X user identified by the user Access Token. For example, if retrieving private metrics from Posts, authenticate with user Access Tokens for that user, generated using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). - - Example: With a limit of 900 requests per 15 minutes per user, you can make up to 900 requests per user in that time frame. - -... - -## HTTP headers and response codes - -Use HTTP headers to understand where your application stands within a given rate limit, based on the most recent request made. - -- `x-rate-limit-limit`: rate limit ceiling for the endpoint -- `x-rate-limit-remaining`: remaining requests for the 15-minute window -- `x-rate-limit-reset`: remaining time before the rate limit resets (in UTC epoch seconds) - -### Error Responses -If an application exceeds the rate limit for an endpoint, the API will return a [HTTP 429 “Too Many Requests”](http://tools.ietf.org/html/rfc6585) response with the following error message in the response body: +--- + +## How rate limits work + +| Concept | Description | +|:--------|:------------| +| **Time window** | Usually 15 minutes or 24 hours | +| **Per-user limits** | Apply with OAuth 1.0a or OAuth 2.0 user tokens | +| **Per-app limits** | Apply with Bearer Token (app-only) | +| **Per-endpoint** | Each endpoint has its own limits | + +--- + +## Checking your limits + +Response headers show your current rate limit status: + +``` +x-rate-limit-limit: 900 +x-rate-limit-remaining: 847 +x-rate-limit-reset: 1705420800 +``` + +| Header | Description | +|:-------|:------------| +| `x-rate-limit-limit` | Maximum requests allowed | +| `x-rate-limit-remaining` | Requests remaining in window | +| `x-rate-limit-reset` | Unix timestamp when window resets | + +--- + +## Rate limit tables + +View the rate limit for each endpoint below. You can also see these limits in the [Developer Console](https://console.x.com). + + +Limits are shown per 15 minutes unless otherwise noted (e.g., "/24hrs" or "/sec"). + + +### Posts (25 endpoints) + +#### Tweets lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/tweets` | 3,500/15min | 5,000/15min | +| GET | `/2/tweets/:id` | 450/15min | 900/15min | + +#### Recent search + +| Method | Endpoint | Per App | Per User | Notes | +|:-------|:---------|:--------|:---------|:------| +| GET | `/2/tweets/search/recent` | 450/15min | 300/15min | 10 default, 100 max results; 512 query length | + +#### Full-archive search + +| Method | Endpoint | Per App | Per User | Notes | +|:-------|:---------|:--------|:---------|:------| +| GET | `/2/tweets/search/all` | 1/sec, 300/15min | 1/sec | 10 default, 500 max results; 1024 query length | + +#### Post counts + +| Method | Endpoint | Per App | Per User | Notes | +|:-------|:---------|:--------|:---------|:------| +| GET | `/2/tweets/counts/recent` | 300/15min | — | 512 query length | +| GET | `/2/tweets/counts/all` | 300/15min | — | 1024 query length | + +#### Filtered stream + +| Method | Endpoint | Per App | Per User | Notes | +|:-------|:---------|:--------|:---------|:------| +| GET | `/2/tweets/search/stream` | 50/15min | — | 1 connection; 1000 rules; 1024 rule length; 250 posts/sec | +| GET | `/2/tweets/search/stream/rules` | 450/15min | — | 1 connection; 1000 rules; 1024 rule length | +| POST | `/2/tweets/search/stream/rules` | 100/15min | — | 1 connection; 1000 rules; 1024 rule length | + +#### Manage posts + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/tweets` | 10,000/24hrs | 100/15min | +| DELETE | `/2/tweets/:id` | — | 50/15min | + +#### Timelines + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/:id/tweets` | 10,000/15min | 900/15min | +| GET | `/2/users/:id/mentions` | 450/15min | 300/15min | +| GET | `/2/users/:id/timelines/reverse_chronological` | — | 180/15min | + +#### Likes lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/tweets/:id/liking_users` | 75/15min | 75/15min | +| GET | `/2/users/:id/liked_tweets` | 75/15min | 75/15min | + +#### Manage likes + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/users/:id/likes` | — | 50/15min, 1,000/24hrs | +| DELETE | `/2/users/:id/likes/:tweet_id` | — | 50/15min, 1,000/24hrs | + +#### Retweets lookup + +| Method | Endpoint | Per App | Per User | Notes | +|:-------|:---------|:--------|:---------|:------| +| GET | `/2/tweets/:id/retweeted_by` | 75/15min | 75/15min | — | +| GET | `/2/tweets/:id/quote_tweets` | 75/15min | 75/15min | — | +| GET | `/2/users/reposts_of_me` | — | 75/15min | 100 max results | + +#### Manage retweets + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/users/:id/retweets` | — | 50/15min | +| DELETE | `/2/users/:id/retweets/:tweet_id` | — | 50/15min | + +#### Hide replies + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| PUT | `/2/tweets/:tweet_id/hidden` | — | 50/15min | + +--- + +### Users (14 endpoints) + +#### Users lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users` | 300/15min | 900/15min | +| GET | `/2/users/:id` | 300/15min | 900/15min | +| GET | `/2/users/by` | 300/15min | 900/15min | +| GET | `/2/users/by/username/:username` | 300/15min | 900/15min | +| GET | `/2/users/me` | — | 75/15min | + +#### Search users + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/search` | 300/15min | 900/15min | + +#### Follows lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/:id/following` | 300/15min | 300/15min | +| GET | `/2/users/:id/followers` | 300/15min | 300/15min | + +#### Manage follows + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/users/:id/following` | — | 50/15min | +| DELETE | `/2/users/:source_user_id/following/:target_user_id` | — | 50/15min | + +#### Blocks lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/:id/blocking` | — | 15/15min | + +#### Mutes lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/:id/muting` | — | 15/15min | + +#### Manage mutes + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/users/:id/muting` | — | 50/15min | +| DELETE | `/2/users/:source_user_id/muting/:target_user_id` | — | 50/15min | + +--- + +### Spaces (6 endpoints) + +#### Spaces lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/spaces/:id` | 300/15min | 300/15min | +| GET | `/2/spaces` | 300/15min | 300/15min | +| GET | `/2/spaces/:id/tweets` | 300/15min | 300/15min | +| GET | `/2/spaces/by/creator_ids` | 300/15min, 1/sec | 300/15min, 1/sec | +| GET | `/2/spaces/:id/buyers` | 300/15min | 300/15min | + +#### Search Spaces + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/spaces/search` | 300/15min | 300/15min | + +--- + +### Direct Messages (8 endpoints) + +#### Direct Messages lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/dm_events` | — | 15/15min | +| GET | `/2/dm_events/:id` | — | 15/15min | +| GET | `/2/dm_conversations/:dm_conversation_id/dm_events` | — | 15/15min | +| GET | `/2/dm_conversations/with/:participant_id/dm_events` | — | 15/15min | + +#### Manage Direct Messages + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/dm_conversations` | 1,440/24hrs | 15/15min, 1,440/24hrs | +| POST | `/2/dm_conversations/with/:participant_id/messages` | 1,440/24hrs | 15/15min, 1,440/24hrs | +| POST | `/2/dm_conversations/:dm_conversation_id/messages` | 1,440/24hrs | 15/15min, 1,440/24hrs | +| DELETE | `/2/dm_events/:id` | 4,000/24hrs | 300/15min, 1,500/24hrs | + +--- + +### Lists (14 endpoints) + +#### Lists lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/lists/:id` | 75/15min | 75/15min | +| GET | `/2/users/:id/owned_lists` | 15/15min | 15/15min | + +#### List Tweets lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/lists/:id/tweets` | 900/15min | 900/15min | + +#### List member lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/lists/:id/members` | 900/15min | 900/15min | +| GET | `/2/users/:id/list_memberships` | 75/15min | 75/15min | + +#### Manage Lists + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/lists` | — | 300/15min | +| DELETE | `/2/lists/:id` | — | 300/15min | +| PUT | `/2/lists/:id` | — | 300/15min | + +#### Manage List members + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/lists/:id/members` | — | 300/15min | +| DELETE | `/2/lists/:id/members/:user_id` | — | 300/15min | + +#### Manage List follows + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/users/:id/followed_lists` | — | 50/15min | +| DELETE | `/2/users/:id/followed_lists/:list_id` | — | 50/15min | + +#### Pinned Lists + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/:id/pinned_lists` | 15/15min | 15/15min | +| POST | `/2/users/:id/pinned_lists` | — | 50/15min | +| DELETE | `/2/users/:id/pinned_lists/:list_id` | — | 50/15min | + +--- + +### Bookmarks (5 endpoints) + +#### Bookmarks lookup + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/:id/bookmarks` | — | 180/15min | +| GET | `/2/users/:id/bookmarks/folders` | 50/15min | 50/15min | +| GET | `/2/users/:id/bookmarks/folders/:folder_id` | 50/15min | 50/15min | + +#### Manage Bookmarks + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/users/:id/bookmarks` | — | 50/15min | +| DELETE | `/2/users/:id/bookmarks/:tweet_id` | — | 50/15min | + +--- + +### Compliance (3 endpoints) + +#### Batch compliance + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/compliance/jobs` | 150/15min | — | +| GET | `/2/compliance/jobs/:job_id` | 150/15min | — | +| GET | `/2/compliance/jobs` | 150/15min | — | + +--- + +### Usage (1 endpoint) + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/usage/tweets` | 50/15min | — | + +--- + +### Trends (2 endpoints) + +#### Personalized Trends + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/users/personalized_trends` | 200/24hrs, 200/15min | 100/24hrs, 10/15min | + +#### Trends by WOEID + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/trends/by/woeid/:id` | 75/15min | — | + +--- + +### Communities (2 endpoints) + +| Method | Endpoint | Per App | Per User | Notes | +|:-------|:---------|:--------|:---------|:------| +| GET | `/2/communities/:id` | 300/15min | 300/15min | — | +| GET | `/2/communities/search` | 300/15min | 300/15min | 100 max results | + +--- + +### Analytics (1 endpoint) + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/tweets/analytics` | 300/15min | 300/15min | + +--- + +### Media (8 endpoints) + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| POST | `/2/media/upload` | 50,000/24hrs | 500/15min | +| GET | `/2/media/upload` | 100,000/24hrs | 1,000/15min | +| POST | `/2/media/upload/initialize` | 180,000/24hrs | 1,875/15min | +| POST | `/2/media/upload/:id/append` | 180,000/24hrs | 1,875/15min | +| POST | `/2/media/upload/:id/finalize` | 180,000/24hrs | 1,875/15min | +| POST | `/2/media/metadata` | 50,000/24hrs | 500/15min | +| POST | `/2/media/subtitles` | 10,000/24hrs | 100/15min | +| DELETE | `/2/media/subtitles` | 10,000/24hrs | 100/15min | + +--- + +### Activity & Webhooks + +| Method | Endpoint | Per App | Per User | Notes | +|:-------|:---------|:--------|:---------|:------| +| GET | `/2/activity/stream` | 450/15min | — | 2 connections; 250 posts/sec | +| POST | `/2/activity/subscriptions` | 500/15min | — | — | +| GET | `/2/activity/subscriptions` | 500/15min | — | — | +| PUT | `/2/activity/subscriptions/:subscription_id` | 500/15min | — | — | +| DELETE | `/2/activity/subscriptions/:subscription_id` | 500/15min | — | — | +| POST | `/2/webhooks` | 450/15min | — | — | +| GET | `/2/webhooks` | 450/15min | — | — | +| PUT | `/2/webhooks/:webhook_id` | 450/15min | — | — | +| DELETE | `/2/webhooks/:webhook_id` | 450/15min | — | — | +| POST | `/2/webhooks/replay` | 100/15min | — | — | + +--- + +### Other endpoints + +| Method | Endpoint | Per App | Per User | +|:-------|:---------|:--------|:---------| +| GET | `/2/tweets/sample10/stream` | 100/15min | — | +| GET | `/2/news/:id` | 200/15min | — | +| GET | `/2/news/search` | 200/15min | 200/15min | +| POST | `/2/users/:id/dm/block` | 25/15min, 1,000/24hrs | 10/15min, 400/24hrs | +| POST | `/2/users/:id/dm/unblock` | 25/15min, 1,000/24hrs | 10/15min, 400/24hrs | +| GET | `/2/users/by/username/:username/tweets` | 1,500/15min | 900/15min | +| GET | `/2/users/by/username/:username/mentions` | 450/15min | 180/15min | +| GET | `/2/users/:id/following/spaces` | 300/15min | 300/15min | +| GET | `/2/tweets/:id/retweets` | 75/15min | 75/15min | +| DELETE | `/2/connections/all` | 25/15min | 25/15min | + +--- + +## Handling rate limits + +When you hit a rate limit, you'll receive a 429 response: ```json -{ "errors": [ { "code": 88, "message": "Rate limit exceeded" } ] } +{ + "errors": [{ + "code": 88, + "message": "Rate limit exceeded" + }] +} ``` -## Recovering from a rate limit +### Recovery strategy + +1. Check `x-rate-limit-reset` for when the window resets +2. Wait until that time before retrying +3. Use exponential backoff if needed + +```python +import time + +def make_request_with_backoff(url, headers): + response = requests.get(url, headers=headers) + + if response.status_code == 429: + reset_time = int(response.headers.get('x-rate-limit-reset', 0)) + wait_time = max(reset_time - time.time(), 60) + time.sleep(wait_time) + return make_request_with_backoff(url, headers) + + return response +``` + +--- + +## Best practices + + + + Store results locally to reduce repeated requests. + + + For real-time data, use filtered stream instead of polling. + + + Track remaining requests to avoid hitting limits. + + + Distribute requests across the time window. + + -When these rate limits are exceeded, a 429 'Too many requests' error is returned from the endpoint. As discussed below, when rate limit errors occur, a best practice is to examine HTTP headers that indicate when the limit resets and pause requests until then.
When a "too many requests" or rate-limiting error occurs, the frequency of making requests needs to be slowed down. When a rate limit error is hit, the `x-rate-limit-reset:` HTTP header can be checked to learn when the rate-limiting will reset

. Another common pattern is based on exponential backoff, where the time between requests starts off small (for example, a few seconds), then doubled before each retry. This is continued until a request is successful, or some reasonable maximum time between requests is reached (for example, a few minutes).

Ideally, the client-side is self-aware of existing rate limits and can pause requests until the currently exceeded window expires. If you exceed a 15-minute limit, then waiting a minute or two before retrying makes sense.

Note that beyond these limits on the number of requests, the Standard Basic level of access provides up to 500,000 Posts per month from the recent search and filtered stream endpoints. If you have exceeded the monthly limit on the number of Posts, then it makes more sense for your app to raise a notification and know its enrollment day of the month and hold off requests until that day. +--- -### Tips to avoid being rate limited -The tips below are there to help you code defensively and reduce the possibility of being rate limited. Some application features that you may want to provide are simply impossible in light of rate-limiting, especially around the freshness of results. If real-time information is an aim of your application, look into the filtered and sampled stream endpoints. +## Rate limits vs. billing -#### Caching -Store API responses if expecting frequent usage. Instead of calling the API on every page load, cache the response locally. +Rate limits and billing are separate: -#### Prioritize active users -If your site keeps track of many X users (for example, fetching their current status or statistics about their X usage), consider only requesting data for users who have recently signed into your site. +| Concept | Purpose | +|:--------|:--------| +| **Rate limits** | Control request frequency for system stability | +| **Usage billing** | Charge for data retrieved (pay-per-usage) | -#### Adapt to the search results -If your application monitors a high volume of search terms, query less often for searches that have no results than for those that do. By using a back-off you can keep up to date on queries that are popular but not waste cycles requesting queries that very rarely change. Alternatively, consider using the filtered stream endpoint and filter with your search queries. +You can be within rate limits but still incur usage costs, or hit rate limits without additional cost. + +--- + +## Enterprise rate limits + +Enterprise customers have custom rate limits. Contact your account manager or [apply for Enterprise access](/enterprise/forms/enterprise-api-interest). + +--- -#### Denylist -If an application abuses the rate limits, it will be denied. Denied apps are unable to get a response from the X API. If you or your application has been denied and you think there has been a mistake, you can use our [Platform Support forms](https://support.x.com/forms/platform) to request assistance. Please include the following information: -1. Explain why you think your application was denied. -2. If you are no longer being rate limited, describe in detail how you fixed the problem. +## Next steps + + + Handle 429 and other errors. + + + Learn about access levels and features. + + diff --git a/x-api/fundamentals/response-codes-and-errors.mdx b/x-api/fundamentals/response-codes-and-errors.mdx index e4c19949a..c67925884 100644 --- a/x-api/fundamentals/response-codes-and-errors.mdx +++ b/x-api/fundamentals/response-codes-and-errors.mdx @@ -1,199 +1,223 @@ --- -title: Response codes and errors -sidebarTitle: Response codes -keywords: ["response codes", "error codes", "HTTP status codes", "API errors", "error handling", "debugging", "error messages", "status codes"] +title: Response Codes & Errors +sidebarTitle: Response Codes +description: HTTP status codes and error handling for the X API +keywords: ["response codes", "error codes", "HTTP status codes", "API errors", "error handling", "debugging"] --- import { Button } from '/snippets/button.mdx'; -The X API provides the following response and error codes to help understand and debug in the moment. Use the debugging guide and error index below for additional context. +The X API uses standard HTTP status codes. Successful requests return 2xx codes; errors return 4xx or 5xx codes with details in the response body. -Successful responses are indicated with a 200-series HTTP code and a JSON-based payload containing the object(s) requested, created, modified, or deleted along with an expression of the server's interpretation of your request. - -Error responses are served with a non-200-series HTTP code. Different error codes indicate different reasons for an error. The X API attempts to return appropriate [HTTP status codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) for every request. - -## X API v2 HTTP status codes - -| Code | Text | Description | Troubleshooting tips | -| :--- | :--- | :---------- | :------------------- | -| 200 | OK | The request was successful! | | -| 400 | Bad Request | The request was invalid or cannot be otherwise served. An accompanying error message will explain further. Requests without authentication or with invalid query parameters are considered invalid and will yield this response. | Double check the format of your JSON query. For example, if your rule contains double-quote characters associated with an exact-match or other operator, you may need to escape them using a backslash to distinguish them from the structure of the JSON format. | -| 401 | Unauthorized | There was a problem authenticating your request. This could be due to missing or incorrect authentication credentials. This may also be returned in other undefined circumstances. | Check that you are using the correct [authentication method](https://docs.x.com/resources/fundamentals/authentication/overview) and that your credentials are correct. | -| 403 | Forbidden | The request is understood, but it has been refused or access is not allowed. An accompanying error message will explain why. | Check that your developer account includes access to the endpoint you’re trying to use. You may also need to get your App allowlisted (e.g. Engagement API or Ads API) or sign up for access. | -| 404 | Not Found | The URI requested is invalid or the resource requested, such as a user, does not exist. | Check that you are using valid parameters and the correct URI for the endpoint you’re using. | -| 409 | Connection Exception | Returned when attempting to connect to a filtered stream that has no rules. | Check that you have created at least one rule on the stream you are connecting to. Filtered stream will only return Posts that match an active rule. If there are no rules, the stream will not return any Posts. | -| 429 | Too Many Requests | Returned when a request cannot be served due to the App's [rate limit](/resources/fundamentals/rate-limits) or [Post cap](/x-api/fundamentals/post-cap) having been exhausted. See [Rate Limiting](/resources/fundamentals/rate-limits). | Check the number of requests per timeframe allowed with the endpoint you’re using. Wait for the timeframe to reset. Space out your requests to ensure you don’t hit rate limits or upgrade to the next available data plan. | -| 500 | Internal Server Error | Something is broken. This is usually a temporary error, for example in a high load situation or if an endpoint is temporarily having issues. | Check the X API [status page](/status) or the [developer community forum](https://devcommunity.x.com/) in case others are having similar issues, or simply wait and try again later. | -| 501 | Unimplemented | The X API does not support this endpoint and cannot fulfill the request. | | -| 503 | Service Unavailable | The X servers are up, but overloaded with requests. Try again later. | Check the X API [status page](/status) or the [developer community forum](https://devcommunity.x.com/) in case others are having similar issues, or simply wait and try again later. | - -When an error is incurred during a request, detailed information about the error is returned in the response body to aid in diagnosing the problem. A type field, which is a URI, indicates the nature of the problem, while additional fields provide details about the problem. The type, title, and detail fields will always be returned in these bodies (see table below). Any additional fields, as in the example below, will vary depending on the type of the error. - -```json -{ - "client_id": "101010101", - "required_enrollment": "Standard Basic", - "registration_url": "https://developer.twitter.com/en/account", - "title": "Client Forbidden", - "detail": "This request must be made using an approved developer account that is enrolled in the requested endpoint. Learn more by visiting our documentation.", - "reason": "client-not-enrolled", - "type": "https://api.x.com/2/problems/client-forbidden" -} -``` - -## Partial errors - -In some cases you may see the errors detailed above in a response that returned a 200 status code. In those cases, the endpoint is designed to return the data that it can, while providing detailed errors about what it could not return. +--- -For example, the [Posts lookup endpoint](/x-api/posts/lookup/introduction) allows a X [developer App](/resources/fundamentals/developer-apps#overview) to request more than one ID. If some of those Posts are available, but one of them has been deleted, the available Posts would be returned in the data field of the response. An additional errors field would be returned in the payload, indicating which requested Post(s) could not be returned. The same format is used as full request errors to make diagnosing issues easier. +## HTTP status codes -### Error information +### Success codes -Each problem type indicates the nature of the problem encountered. A full list of problems that you can run into can be found in our [API specification](https://api.x.com/2/openapi.json) as well. +| Code | Meaning | Description | +|:-----|:--------|:------------| +| **200** | OK | Request successful | +| **201** | Created | Resource created (POST requests) | +| **204** | No Content | Success with no response body (DELETE requests) | -The X API attempts to return appropriate [HTTP status codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) for every request. +### Client error codes -| Title | Type | Description | -| :--- | :--- | :--- | -| Generic Problem | `about:blank` | A generic problem with no additional information beyond that provided by the HTTP status code. | -| Invalid Request Problem | `https://api.X.com/2/problems/invalid-request` | A problem that indicates this request is invalid. If your request takes a POST body, ensure the contents is valid JSON and matches the [OpenAPI spec](https://api.x.com/2/openapi.json). | -| Resource Not Found Problem | `https://api.X.com/2/problems/resource-not-found` | A problem that indicates that a given Post, User, etc. does not exist. | -| Resource Unauthorized Problem | `https://api.X.com/2/problems/not-authorized-for-resource` | A problem that indicates you are not allowed to see a particular Post, User, etc. | -| Client Forbidden Problem | `https://api.X.com/2/problems/client-forbidden` | A problem that indicates your client is forbidden from making this request. | -| Disallowed Resource Problem | `https://api.X.com/2/problems/disallowed-resource` | A problem that indicates that the resource requested violates the precepts of this API. | -| Unsupported Authentication Problem | `https://api.X.com/2/problems/unsupported-authentication` | A problem that indicates that the authentication used is not supported. | -| Usage Capped Problem | `https://api.X.com/2/problems/usage-capped` | A problem that indicates that a usage cap has been exceeded. | -| Connection Exception Problem | `https://api.X.com/2/problems/streaming-connection` | A problem that indicates something is wrong with the connection. | -| Client Disconnected Problem | `https://api.X.com/2/problems/client-disconnected` | Your client has gone away. | -| Operational Disconnect Problem | `https://api.X.com/2/problems/operational-disconnect` | You have been disconnected for operational reasons. | -| Rule Cap Problem | `https://api.X.com/2/problems/rule-cap` | You have exceeded the maximum number of rules. | -| Rule Length Problem | `https://api.X.com/2/problems/rule-length` | You have exceeded the maximum number of characters allowed on your query or rule based on your access level. See [access levels](/x-api/getting-started/about-x-api#x-api-access-levels-and-versions). | -| Invalid Rules Problem | `https://api.X.com/2/problems/invalid-rules` | The rule you have submitted is invalid. | -| Duplicate Rules Problem | `https://api.X.com/2/problems/duplicate-rules` | The rule you have submitted is a duplicate. | +| Code | Meaning | Common causes | +|:-----|:--------|:--------------| +| **400** | Bad Request | Invalid JSON, malformed query, missing required parameters | +| **401** | Unauthorized | Invalid or missing authentication credentials | +| **403** | Forbidden | Valid auth but no permission for this resource or action | +| **404** | Not Found | Resource doesn't exist or has been deleted | +| **409** | Conflict | Stream has no rules (filtered stream only) | +| **429** | Too Many Requests | Rate limit or usage cap exceeded | -## Troubleshooting tips +### Server error codes - +| Code | Meaning | What to do | +|:-----|:--------|:-----------| +| **500** | Internal Server Error | Wait and retry; check [status page](/status) | +| **502** | Bad Gateway | Wait and retry | +| **503** | Service Unavailable | X is overloaded; wait and retry | +| **504** | Gateway Timeout | Wait and retry | -When building an application, it's normal to run into errors or unexpected issues from time to time. Below are some tips for how you can debug your code: +--- -Start by breaking down the issue into smaller parts to identify where the problem lies. For example, if you’re integrating a REST or a streaming endpoint, the issue could lie with any of the following items: +## Error response format -- The endpoint requires proper [authentication](/resources/fundamentals/authentication/overview). -- The endpoint requires valid parameters and headers. Any filtering rules are built using the correct operators and proper [syntax](/x-api/enterprise-gnip-2.0/fundamentals/rules-filtering#building-rules-and-queries). -- The endpoint's URI must be correct and, in the case of REST endpoints, the correct HTTP method must be used. -- The data or resource you are trying to access is not accessible to you (for example, private data is only available to authenticated users). -- Your current data package gives you access to certain endpoints only and to specific rate limits. Check out your [developer portal](https://developer.x.com/en/portal/products) for more details. -- Your code uses a third-party library to integrate the endpoint in your code. -- Your code needs to successfully parse the endpoint response. +Error responses include structured details: -Read the accompanying error message. This should give you a good indication of what the problem is. Use the tables in the [error and response codes section](#x-api-http-status-codes) for troubleshooting tips specific to each error code. +```json +{ + "title": "Invalid Request", + "detail": "The 'query' parameter is required.", + "type": "https://api.x.com/2/problems/invalid-request" +} +``` -For REST endpoints, you can use a REST client like [Postman](https://www.postman.com/) or [Insomnia](https://insomnia.rest/) to validate steps (1) to (5) above (review our ["Getting started with Postman"](/tutorials/postman-getting-started) guide). If the request with the REST client returns a 200 success status code, you can assume that the issue lies with your code or the library that you are using; not with the endpoint request itself. +| Field | Description | +|:------|:------------| +| `type` | URI identifying the error type | +| `title` | Short error description | +| `detail` | Specific explanation for this error | -For streaming endpoints, you can use cURL (a command-line tool for getting or sending requests using the URL syntax) to check if the issue lies with the request to the endpoint (steps (1) to (5) above) or with your code itself (steps (6) to (7) above). - +Additional fields may be present depending on the error type. - -- Check that you are using the proper [authentication method](/resources/fundamentals/authentication/overview) required for the endpoint. This can be identified via the endpoint’s API reference page. -- Check that your authentication credentials are correct. You can check or regenerate your App keys and tokens in the [Apps section](https://developer.x.com/en/portal/projects-and-apps) of your developer dashboard (under “Details”). -- Check that you have properly [authorized your OAuth 1.0a request](/resources/fundamentals/authentication/oauth-1-0a/authorizing-a-request) with `oauth_nonce`, `oauth_signature`, and `oauth_timestamp` for your request. -- If the issue persists, consider using an OAuth library, a REST client like [Postman](https://www.postman.com/) or [Insomnia](https://insomnia.rest/), or [xurl](https://github.com/xdevplatform/xurl). +--- -Review our [guide on authentication](/resources/fundamentals/authentication/overview) for additional information on all of the above. - +## Error types + +| Type | Description | +|:-----|:------------| +| `about:blank` | Generic error (see HTTP status code) | +| `.../invalid-request` | Malformed request or invalid parameters | +| `.../resource-not-found` | Post, user, or other resource doesn't exist | +| `.../not-authorized-for-resource` | No access to private/protected content | +| `.../client-forbidden` | App not enrolled or lacks required access | +| `.../usage-capped` | Usage cap exceeded | +| `.../rate-limit-exceeded` | Rate limit exceeded | +| `.../streaming-connection` | Stream connection problem | +| `.../rule-cap` | Too many filtered stream rules | +| `.../invalid-rules` | Rule syntax error | +| `.../duplicate-rules` | Rule already exists | - +--- -A 429 error can deliver if you hit a rate limit for a given endpoint, or if you hit a Post cap. +## Partial errors -If the specific ‘Too many requests’ error is returned, you hit the endpoint's rate limit. In other words, you've exceeded the maximum number of requests allowed for an endpoint per specified time period. +Some requests can partially succeed. A 200 response may include both `data` and `errors`: -Note that rate limits are set per-App and per-user levels. +```json +{ + "data": [ + {"id": "123", "text": "Hello"} + ], + "errors": [ + { + "resource_id": "456", + "resource_type": "tweet", + "title": "Not Found Error", + "detail": "Could not find tweet with id: [456].", + "type": "https://api.x.com/2/problems/resource-not-found" + } + ] +} +``` -- **X App level** indicates the number of requests allowed when using OAuth 2.0 App-Only, where rate limits are determined globally for the entire App. For example, if a method allows for 15 requests per rate limit window, then it allows you to make 15 requests per window on behalf of your X App. This limit is considered completely separately from the user-level limit. Read more in our guide on [OAuth 2.0 App-Only](/resources/fundamentals/authentication/oauth-2-0/application-only). -- **User-context level** indicates the number of requests that can be made per user Access Token when using OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE. For example, if a method allows for 15 requests per rate limit window, then it allows 15 requests per window and per user Access Token. Read more in our guide on how to obtain a user’s Access Tokens with [OAuth 1.0a](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) and [OAuth 2.0](/resources/fundamentals/authentication/oauth-2-0/user-access-token). +This happens when requesting multiple resources and some are unavailable. -Start by checking the rate limits for the endpoint you are using. You can find this information in the endpoint's API reference page and in the new developer portal dashboard. +--- -Review our documentation for additional information on rate limits, including how to use HTTP headers to track where your App is at for a given rate limit, how to recover from a rate limit 429 error code, and tips to avoid being rate limited in the first place: +## Troubleshooting common errors -- [Rate limits for v2 endpoints](/x-api/fundamentals/rate-limits) + +**Check your authentication:** +- Verify you're using the correct authentication method for the endpoint +- Ensure credentials haven't been regenerated +- Check the `Authorization` header format +- For OAuth 1.0a, verify signature calculation -If you've received the specific "Usage cap exceeded: Monthly product cap" error, that means you've hit the Post cap for your access level. We have plenty of details on what these [Post caps](/x-api/fundamentals/post-cap) are on our documentation page. +[Authentication guide →](/resources/fundamentals/authentication/overview) - - -Follow the steps below, if you expected a Post to be returned, but it wasn’t delivered by the endpoint. - -- Check your rule to ensure that you are using the correct [operators](/x-api/enterprise-gnip-2.0/fundamentals/rules-filtering#enterprise-operators) and [syntax](/x-api/enterprise-gnip-2.0/fundamentals/rules-filtering#building-rules-and-queries). Break the rule into smaller clauses to ensure that you are using the correct syntax. -- If the account from which the Post was sent was [protected](https://help.twitter.com/en/safety-and-security/public-and-protected-tweets) at the time the Post was created, the Post won’t be returned - even if the account is public at the time of the request to the endpoint. You can typically check this using [X Advanced Search](https://x.com/search-advanced): if a Post is not surfaced using the X Advanced Search functionality, you should assume that it won’t be returned by the endpoint. - -The following steps apply to streaming endpoints only: - -- Were you connected to the stream when the Post was sent? Remember that the timestamp delivered in the Post object indicates time in UTC. If you experienced a disconnect when the Post was sent, review the [recovery and redundancy features](/x-api/posts/filtered-stream/integrate/recovery-and-redundancy-features#recovery-and-redundancy) available to backfill any missed data. -- Was your rule in place when the Post was sent? Remember that the timestamp delivered in the Post object indicates time in UTC. + +**Check your access:** +- Verify your app has access to this endpoint +- Some endpoints require specific enrollment or approval +- User-context endpoints need appropriate OAuth scopes +- The resource may be private or protected - -You may get one of the following errors when your stream is not keeping up with the speed at which we are delivering data and your App isn't consuming the data from the stream fast enough: - -``` -This stream has been disconnected because your client was unable to keep up with us. -``` + +**Rate limited:** +- Check `x-rate-limit-reset` header for when to retry +- Implement exponential backoff +- Consider caching responses +- Spread requests across the time window -``` -This stream has been disconnected for operational reasons. -``` - -We allow delivery to get behind for a period of time, and we have a temporary staging buffer amount for each stream on our side; but if you don't catch up, we initiate a disconnect to allow you to reconnect at the current point in time. Please note that this may lead to data loss (for data that is within the buffer at the time of the full buffer disconnect). +[Rate limits guide →](/x-api/fundamentals/rate-limits) + -These can occur around large spikes in data. Generally, we recommend using a buffer process for consuming data quickly that is separate from the processing process. + +**Fix your request:** +- Validate JSON syntax +- Check for missing required parameters +- Verify parameter types (strings vs. numbers) +- Escape special characters in queries + -If you are an enterprise customer using v1.1 endpoints, you can find out more about optimizing your App to prevent disconnects like this in our articles on [connection](/x-api/enterprise-gnip-2.0/powertrack-api#disconnections-explained) and on consuming streaming data [here](https://developer.x.com/en/docs/tutorials/consuming-streaming-data) and [here](/x-api/enterprise-gnip-2.0/powertrack-api#planning-for-high-volume-social-data-events). + +**Check these factors:** +- Protected accounts' posts are only visible with authorization +- Deleted posts return 404 +- Some posts are withheld in certain regions +- Verify search query syntax is correct + -There is a range of tools available for retrieving missed Posts due to a disconnect, including the ones listed below. Note that the following tools are only available with v1.1 endpoints at enterprise level of access. + +**Handle reconnection:** +- Implement automatic reconnect with backoff +- Use recovery features for missed data +- Check for full-buffer disconnects (client not consuming fast enough) +- Verify at least one stream rule exists -- [**Redundant Connections**](/x-api/enterprise-gnip-2.0/powertrack-api#recovery-and-redundancy-features) - With multiple connections, consume the stream from multiple servers to prevent missed data when one is disconnected. -- [**Replay**](/x-api/enterprise-gnip-2.0/powertrack-api#recovery-and-redundancy-features) - Recover data from within the last 5 days using a separate stream. -- [**Backfill**](/x-api/enterprise-gnip-2.0/powertrack-api#recovery-and-redundancy-features) - Reconnect within 5 minutes and start from where you left off. -- **Historical PowerTrack** - Recover data from the entire X archive. +[Streaming guide →](/x-api/posts/filtered-stream/integrate/handling-disconnections) -## Get help - -The X community forum is available for you to ask technical questions about the X developer platform. This is a discussions forum where you will find questions by other developers and technical information on a variety of topics related to using the X API. +--- -We encourage you to join the conversation by responding to questions and engaging in conversations on our forum. X employees are also there to provide support. +## Rate limit headers - +Every response includes rate limit information: -### Before you post a question +``` +x-rate-limit-limit: 900 +x-rate-limit-remaining: 847 +x-rate-limit-reset: 1705420800 +``` -- Search the X developer documentation for information relating to your issue -- Search the community forum for similar questions by other developers -- Review the community forum guidelines +| Header | Description | +|:-------|:------------| +| `x-rate-limit-limit` | Max requests in current window | +| `x-rate-limit-remaining` | Requests remaining | +| `x-rate-limit-reset` | Unix timestamp when window resets | -### When you post a question, make sure to include the following information +--- -- A description of the problem -- The API call being made (include headers, if possible) -- The X response returned (include any error messages) -- What you expected to receive instead -- List of steps taken to troubleshoot the issue -- List of steps required to reproduce the issue -- If relevant, the time frame during which an issue occurred -- If relevant, the App ID, Post ID, etc. -- Any relevant code sample or screenshots +## Best practices + + + + Always check HTTP status before parsing the response body. + + + Check for `errors` array even in 200 responses. + + + Use exponential backoff for 429 and 5xx errors. + + + Include request ID and timestamp for debugging. + + -Please only include one topic/question per post. +--- -If you have feature requests or feedback, please submit these through the [X Developer Platform Feedback Form](https://t.co/devfeedback). +## Getting help -For Policy-related issues, such as App suspension, please contact [Policy support](https://help.twitter.com/forms/platform). +When posting questions about errors, include: -For X-related issues, such as login and account support, please use the [X Help Desk](https://help.twitter.com/en/contact-us). +- The API endpoint URL +- Request headers (sanitize credentials) +- Full error response +- What you expected to happen +- Steps you've tried + + + Ask questions and search solutions. + + + Check for known issues. + + diff --git a/x-api/fundamentals/versioning.mdx b/x-api/fundamentals/versioning.mdx index 08c615930..2a21613c8 100644 --- a/x-api/fundamentals/versioning.mdx +++ b/x-api/fundamentals/versioning.mdx @@ -1,62 +1,149 @@ --- -title: "Versioning" -keywords: ["versioning", "API versioning", "API versions", "v2 API", "API migration", "deprecation", "API changes", "version strategy"] ---- - - -

Along with X API v2, we launched a new versioning strategy that enables developers to better understand when to expect changes to X's public APIs, and when they will need to migrate to new versions. 

-

Developers will be notified of deprecations, retirements, changes, and additions to the X API via our communication channels so they can appropriately plan to accommodate these changes in their development roadmap. All changes to the API will be noted in the changelog.

-

The X API currently has three different versions. We strongly encourage users to utilize X API v2 when planning their integration unless we have not released functionality to v2 that is required by your use case. 

-

To learn more about each version, please visit the following pages:

- - -## Versioning Strategy - -

Versioning for the X API will be represented by version numbers declared in the route path for our endpoints:

-

`https://api.x.com/2/tweets`

-

We aim to release major versions of the X API as necessary but no more frequently than every 12 months. A major version will be released when breaking changes are introduced in the API. We will publish migration guides when launching a new major version to help developers migrate over to the new version. 

-

A breaking change requires developers to change their code to maintain existing functionality in their app. Non-breaking changes will be additive and rolled out to the most recent version when ready, requiring no work on a developer’s end unless you would like to take advantage of the new functionality.

-

If a breaking change must be rolled out mid-cycle (for security or privacy reasons), this change will be made to the most recent version.

- -### Breaking changes - -

These changes require developers to change their code to maintain existing functionality of their application.

-
    -
  • Addition of a new required parameter
  • -
  • Removal of an existing endpoint
  • -
  • Removal of any field in the response (either required or optional)
  • -
  • Removal of a query parameter
  • -
  • Restructuring of the input or output format (for example, making a top-level field a sub-field, or changing the location of errors to be inline)
  • -
  • Changing the name or data type of an existing input parameter or output value
  • -
  • Changing the name of a field
  • -
  • Changing the resource name
  • -
  • Changing a response code
  • -
  • Changing error types
  • -
  • Changes to existing authorization scopes
  • -
- -### Non-breaking changes - -
    -
  • Addition of a new endpoint
  • -
  • Addition of a new optional parameter
  • -
  • Addition of a new response field
  • -
  • Changing text in error messages
  • -
  • Availability of new scopes
  • -
  • “Nulling” of fields (changing the value of a to null for privacy/security reasons as an alternative to removing it altogether)
  • -
- -### Deprecation and retirement - -

First of all, here is our definition of what deprecation and retirement mean to the X API:

-
    -
  • Deprecation: The feature is no longer supported by the team. No new functionality will be released around that feature, and if there are any bugs or issues with the product, the chances that we will explore a fix are extremely low. 
  • -
  • Retired: The feature will no longer be accessible.
  • -
-

In most cases, as soon as a new version is released, the previous version will be marked as deprecated. Versions will remain in a deprecated state for a period of time, after which they will be retired. 

-

Please stay informed to learn more about future deprecations and retirements.

+title: Versioning +sidebarTitle: Versioning +description: X API versioning strategy and deprecation policy +keywords: ["versioning", "API versioning", "API versions", "v2 API", "deprecation", "API changes", "migration"] +--- + +The X API uses version numbers in endpoint paths to provide stability while allowing for evolution. Understanding our versioning strategy helps you plan integrations and stay current. + +--- + +## Current versions + +| Version | Status | Description | +|:--------|:-------|:------------| +| **v2** | Current | Modern endpoints, flexible pricing, all new features | +| **v1.1** | Legacy | Limited support, minimal updates | +| **Enterprise** | Available | High-volume access with dedicated support | + + +Use **X API v2** for all new projects. This is where all new features ship. + + +--- + +## Version in URLs + +The version number appears in the endpoint path: + +``` +https://api.x.com/2/tweets + ^ + version +``` + +--- + +## Breaking vs. non-breaking changes + +### Breaking changes (require code updates) + +These changes only occur in major version bumps: + +- Removing an endpoint +- Removing a response field +- Removing a query parameter +- Adding a new required parameter +- Changing a field's data type +- Renaming a field or resource +- Changing response codes or error types +- Modifying authorization scopes + +### Non-breaking changes (additive) + +These can happen at any time without version changes: + +- Adding a new endpoint +- Adding a new optional parameter +- Adding a new response field +- Adding new OAuth scopes +- Changing error message text +- Nulling fields for privacy/security reasons + +--- + +## Release schedule + +| Type | Frequency | Notice | +|:-----|:----------|:-------| +| **Major versions** | No more than annually | Migration guides provided | +| **Non-breaking changes** | Ongoing | Changelog updates | +| **Security patches** | As needed | May be applied to current version | + +--- + +## Deprecation policy + +When we release a new major version: + +1. **Deprecation**: Previous version is marked deprecated +2. **Support period**: Deprecated version continues to work for a defined period +3. **Retirement**: Deprecated version is removed + +### Definitions + +| Status | Meaning | +|:-------|:--------| +| **Active** | Fully supported with new features and fixes | +| **Deprecated** | No new features; critical bugs only; use discouraged | +| **Retired** | No longer accessible | + +--- + +## Staying informed + +Get notified about changes: + + + + All platform changes and updates. + + + Breaking change notices. + + + Platform news and updates. + + + Monthly digest. + + + +--- + +## Migration resources + +When a new version is released, we provide: + +- **Migration guides**: Step-by-step upgrade instructions +- **Endpoint mapping**: v1 to v2 equivalents +- **Data format changes**: Object model differences + + + + Current migration guidance. + + + v1 to v2 endpoint mapping. + + + +--- +## Best practices + + + Start new projects on the latest version. + + + Subscribe to changelog and forum updates. + + + Test in development before production updates. + + + Don't wait until deprecation to upgrade. + + diff --git a/x-api/getting-started/about-x-api.mdx b/x-api/getting-started/about-x-api.mdx index cc8610e39..a61ed2a4a 100644 --- a/x-api/getting-started/about-x-api.mdx +++ b/x-api/getting-started/about-x-api.mdx @@ -1,113 +1,160 @@ --- -title: "About the X API" -keywords: ["X API", "API overview", "API features", "API versions", "API access levels", "API pricing", "v2 API", "API resources", "posts", "users", "spaces"] +title: About the X API +sidebarTitle: About X API +description: Overview of X API capabilities, versions, and resources +keywords: ["X API", "API overview", "API features", "API versions", "API pricing", "v2 API", "API resources"] --- -The X API can be used to programmatically retrieve and analyze X data, as well as build for the conversation on X. +The X API provides programmatic access to X's public conversation. Retrieve posts, analyze trends, build integrations, and create new experiences on the platform. -Over the years, the X API has grown by adding additional levels of access for developers to be able to scale their access to enhance and research the public conversation. +--- -Recently, we released the X API v2. The X API v2 includes a modern foundation, new and advanced features, and quick onboarding to [Basic access](https://developer.x.com/en/portal/products/basic). +## What you can do -The following three tabs explain the different versions and access levels of the X API, what’s new with v2, and which X resources you can retrieve, create, destroy, and adjust using the API. +| Capability | Description | +|:-----------|:------------| +| **Read posts** | Search, look up, and stream posts in real-time | +| **Publish content** | Create posts, replies, and threads | +| **Manage users** | Look up users, manage follows, blocks, and mutes | +| **Analyze data** | Access metrics, trends, and engagement analytics | +| **Build integrations** | Send DMs, manage lists, and interact with Spaces | - - - ## X API access levels and versions - - While the X API v2 is the primary X API, the platform currently supports previous versions (v1.1, Gnip 2.0) as well. We recommend that all users start with v2 as this is where all future innovation will happen. - - The **X API v2** includes a few access levels: - - | | Free | Basic | Pro | Enterprise | - | :---------------------------- | :------------------------------------------------------------- | :-------------------------------------------------------------- | :------------------------------------------------------------ | :---------------------------------------------------------------------------------------------------------- | - | Getting access | [Get Started](https://developer.x.com/en/portal/products/free) | [Get Started](https://developer.x.com/en/portal/products/basic) | [Get Started](https://developer.x.com/en/portal/products/pro) | [Get Started](https://docs.x.com/resources/enterprise/forms/enterprise-api-interest#enterprise-access-form) | - | Price | Free | \$200/month | \$5000/month | - | - | Access to X API v2 | ✔️ | ✔️ | ✔️ | ✔️ | - | Access to standard v1.1 | ✔️ (Limited) | ✔️ (Limited) | ✔️ (Limited) | ✔️ | - | Project limits | 1 Project | 1 Project | 1 Project | - | - | App limits | 1 App per Project | 2 Apps per Project | 3 Apps per Project | - | - | Post caps (per month) | 100 | 15,000 | 1,000,000 | - | - | Enterprise stream APIs | ❌ | ❌ | ❌ | ✔️ | - | Filtered stream API | ❌ | ❌ | ✔️ | ✔️ | - | Access to full-archive search | ❌ | ❌ | ✔️ | ✔️ | +--- - - - ## What's new with v2 +## API versions - The X API v2 represents the largest upgrade of the X API since 2012. With it comes a host of new and advanced features, as well as fast and free access to the API. + + + The current version of the X API with modern features and flexible pricing. + + **Why use v2:** + - Pay-per-usage pricing—no monthly caps + - Modern JSON response format + - Flexible [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) + - Advanced features: annotations, conversation tracking, edit history + - All new endpoints and features + + **Getting started:** + 1. Sign up at [console.x.com](https://console.x.com) + 2. Create an app and get credentials + 3. [Make your first request](/x-api/getting-started/make-your-first-request) + + + The previous version of the X API. Limited support; use v2 for new projects. + + **Still available:** + - Some media upload endpoints + - Legacy streaming (deprecated) + - Some specialized endpoints + + **Migrating to v2:** + See the [migration guide](/x-api/migrate/overview) for endpoint mapping and data format changes. + + + High-volume access for businesses with advanced needs. + + **Features:** + - Complete firehose access + - Historical data backfill + - Dedicated support + - Custom rate limits + - Compliance streams + + [Contact enterprise sales →](/enterprise/forms/enterprise-api-interest) + + - Some of the features that are available with v2 include the following: +--- - ### New endpoints +## Available resources + +The X API provides access to these resource types: + + + + Search, retrieve, create, and delete posts. Access timelines, threads, and quote posts. + + + Look up profiles, manage relationships, and access follower data. + + + Discover live audio conversations and participants. + + + Send and receive private messages between users. + + + Create and manage curated lists of accounts. + + + Access trending topics by location. + + - We have released a set of net-new endpoints to X API v2. You can see a full list of v2 endpoints, including those that are new, on our X API endpoint map guide. +--- - [Visit the X API endpoint map \>](/x-api/migrate/x-api-endpoint-map) +## v2 highlights - ### New and more detailed data objects + +Request only the data you need. Use `fields` parameters to select specific attributes and `expansions` to include related objects. - We've modernized our data objects with a variety off new improvements that will enable you to more easily navigate and parse data. +```bash +curl "https://api.x.com/2/tweets/123?tweet.fields=created_at,public_metrics&expansions=author_id&user.fields=username" \ + -H "Authorization: Bearer $TOKEN" +``` - [Visit the data dictionary \>](/x-api/fundamentals/data-dictionary) +[Learn more about fields →](/x-api/fundamentals/fields) + - ### New parameters to help you retrieve just those objects and fields that you want + +Posts include semantic annotations identifying people, places, products, and topics. Filter streams and searches by topic. - We've added fields and expansions parameters to our data endpoints that allow you to request related objects and fields beyond those fields that return by default. +[Learn more about annotations →](/x-api/fundamentals/post-annotations) + - [Learn how to use fields and expansions \>](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) + +Access public metrics (likes, reposts, replies) and private metrics (impressions, clicks) for your own posts. - ### Advanced metrics +[Learn more about metrics →](/x-api/fundamentals/metrics) + - More easily understand the performance of Posts, users, media, and polls from directly within your payload by requesting both public and private metrics including impressions, video views, user profile, and URL clicks, some of which are separated into an organic and promoted context. + +Reconstruct entire conversation threads using `conversation_id`. Track replies across the full thread. - [Learn more about metrics \>](/x-api/fundamentals/metrics) +[Learn more about conversation tracking →](/x-api/fundamentals/conversation-id) + - ### Filter on and identify which Posts contain different topics + +Access the edit history of posts, including all previous versions and edit metadata. - When using search Posts or filtered stream, you can now filter by topic using our entity and context operators. We've also provided these topics within the Post payload to help with analysis. +[Learn more about edit posts →](/x-api/fundamentals/edit-posts) + - [Learn more about Post annotations \>](/x-api/fundamentals/post-annotations) +--- - ### Filter on and identify which Posts belong to a reply thread +## Pricing - Make it easier to identify a Post as part of a conversation thread when using search Posts, filtered stream, and Post lookup. We've also added the ability to determine whether conversation reply settings have been set for a Post with the Post field `reply_settings`. +X API v2 uses **pay-per-usage** pricing: - [Learn more about conversation tracking \>](/x-api/fundamentals/conversation-id) +| Benefit | Description | +|:--------|:------------| +| **No subscriptions** | Pay only for what you use | +| **No caps** | Use as much as you need | +| **Credit-based** | Purchase credits, deducted per request | +| **Real-time tracking** | Monitor usage in the Developer Console | +| **Deduplication** | Same resource requested twice in 24 hours is only charged once | - ### And so much more... +[View pricing details →](/x-api/getting-started/pricing) - - High confidence spam filtering - - Shortened URLs are fully unwound for easier URL analysis - - Simplified JSON response objects by removing deprecated fields and modernizing labels - - Recovery and redundancy functionality for our streaming endpoints - - Return of 100% of matching public and available Posts in search queries - - Streaming "rules" so you can make changes without dropping connections - - More expressive query language for filtered stream and search - - OpenAPI spec to build new libraries & more transparently track changes - - API support for new features and endpoints more quickly as our platform evolves to meet the needs of developers, researchers, businesses, and people using X - - - ## X API platform resources - - In the API design space, a resource is an entity with associated data, relationships to other resources, and a set of methods that operate on it. For example, a Post is a resource that you can create, delete, or retrieve using a variety of different tools, such as historically searching for them, or retrieving them in real-time. - - The X API provides access to create, delete, receive, or adjust a variety of different resources on the platform including the following: - - | Resource | Description | - | :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | **Posts** | Tap into millions of Posts to understand the public conversation, or create your own to engage with the conversation. Current availability: - X API v2 - Enterprise - Standard v1.1 | - | **Users** | Manage or look up X users to analyze networks, understand your audience, or foster positive online relationships. Current availability: - X API v2 - Enterprise - Standard v1.1 | - | **Spaces** | Look up and search X Spaces and their attendees to help people find interesting and relevant audio conversations. Current availability: - X API v2 | - | **Direct Messages** | Send and receive Direct Messages to triage customer issues, send welcome messages, or create positive human interaction. Current availability: - Standard v1.1 | - | **Lists** | Curate and manage lists of accounts to keep a pulse on industry experts, powerful voices, or organize who you follow. Current availability: - X API v2 - Standard v1.1 | - | **Trends** | Identify geographic trends first to pinpoint industry movement, discover hot topics, or stay ahead of the latest fad. Current availability: - Standard v1.1 | - | **Media** | Upload media objects to share your creative energy, create interactive experiences, or build accessibility tools. Current availability: - Standard v1.1 | - | **Places** | Search for places to understand what's happening in your neighborhood and around the world. Current availability: - Standard v1.1 | - - +--- -### Other X API offerings +## Next steps -- Enterprise APIs (Formerly Gnip 2.0) \ No newline at end of file + + + Sign up and create your first app. + + + Call the API in minutes. + + diff --git a/x-api/getting-started/getting-access.mdx b/x-api/getting-started/getting-access.mdx index 9beee6d0c..aaa10deeb 100644 --- a/x-api/getting-started/getting-access.mdx +++ b/x-api/getting-started/getting-access.mdx @@ -1,47 +1,128 @@ --- -title: How to get access to the X API -sidebarTitle: Getting access -keywords: ["get API access", "sign up", "developer account", "API keys", "access tokens", "OAuth", "API credentials", "developer registration"] +title: Getting Access +sidebarTitle: Getting Access +description: Sign up for API access and get your credentials +keywords: ["get API access", "sign up", "developer account", "API keys", "access tokens", "OAuth", "API credentials"] --- -### Step one: Sign up for a developer account +import { Button } from '/snippets/button.mdx'; -Signing up for a developer account is quick and easy! Just click on the button below, answer a few questions, and you can start exploring and building on the X API v2 using [Basic access](https://developer.x.com/en/portal/products/basic). +Get started with the X API in three steps: sign up, create an app, and save your credentials. -Next you will create a Project and an associated developer App during the onboarding process, which will provide you a set of credentials that you will use to authenticate all requests to the API. + -[Sign up](https://developer.x.com/en/portal/products/basic) +--- -### Step two: Save your App's key and tokens and keep them secure +## Step 1: Create a developer account -Once you have access and have created a Project and App, you will be able to find or generate the following credentials within your developer App: + + + Visit [console.x.com](https://console.x.com) and sign in with your X account. + + + Review and accept the Developer Agreement and Policy. + + + Provide basic information about how you'll use the API. + + -- **[API Key and Secret](https://developer.x.com/resources/fundamentals/authentication/api-key-and-secret):** Essentially the username and password for your App. You will use these to authenticate requests that require [OAuth 1.0a User Context](/resources/fundamentals/authentication), or to generate other tokens such as user Access Tokens or App Access Token. +--- -- **[Access Token and Secret](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens):** In general, Access Tokens represent the user that you are making the request on behalf of. The ones that you can generate via the developer portal represent the user that owns the App. You will use these to authenticate requests that require [OAuth 1.0a User Context](/resources/fundamentals/authentication). If you would like to make requests on behalf of another user, you will need to use the 3-legged OAuth flow for them to authorize you. +## Step 2: Create an app -- **[Client ID and Client Secret](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce):** These credentials are used to obtain a user Access Token with OAuth 2.0 authentication. Similar to OAuth 1.0a, the user Access Tokens are used to authenticate requests that provide private user account information or perform actions on behalf of another account but, with fine-grained scope for greater control over what access the client application has on the user. +After signing up, create an app to get your API credentials: -- **[App only Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only):** You will use this token when making requests to endpoints that responds with information publicly available on X. + + + From the Developer Console dashboard, create a new app. + + + Provide a name, description, and use case for your app. + + + The console will generate your API keys and tokens. + + -Since these keys and tokens do not expire unless regenerated, we suggest that you save them in a secure location, such as a password manager, once you've received your credentials. +--- -> **Please note: Your keys and tokens will only display once in the [developer portal](/resources/fundamentals/developer-portal), so it is important that you store these credentials in your password management system as soon as you generate them.** -> -> If you misplace or forget the keys and tokens, you will need to regenerate them, which creates new credentials and invalidates the old ones. This means that you will have to update any integrations that you may have set up with your prior credentials. -> -> Learn more about our [authentication best practices](/resources/fundamentals/authentication#authentication-best-practices). +## Step 3: Save your credentials -### Step three: Make your first request +You'll receive several credentials depending on your authentication needs: -What's next? Let's make your first request to the API! +| Credential | Purpose | +|:-----------|:--------| +| **API Key & Secret** | Identify your app. Used to generate tokens and sign OAuth 1.0a requests. | +| **Bearer Token** | App-only authentication for reading public data. | +| **Access Token & Secret** | Make requests on behalf of your own account (OAuth 1.0a). | +| **Client ID & Secret** | OAuth 2.0 authentication for user-context requests. | -We have guides, tutorials, tools, and code to help you get started. The following page will be a great place to start, but note that we've also put together an important resources page to help you navigate the broader documentation. + +**Save immediately.** Credentials are only displayed once. Store them in a password manager or secure vault. If you lose them, you'll need to regenerate (which invalidates the old ones). + + +--- -[Make your first request](/x-api/getting-started/make-your-first-request) +## Which credentials do you need? + + + + Use the **Bearer Token** for simple, read-only access to public data. + + ```bash + curl "https://api.x.com/2/users/by/username/xdevelopers" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + Best for: Searching posts, looking up users, reading trends. + + + Use **OAuth 2.0** (recommended) or **OAuth 1.0a** to act on behalf of users. + + OAuth 2.0 offers fine-grained scopes—request only the permissions you need. + + Best for: Posting, liking, following, accessing DMs. + + [OAuth 2.0 guide →](/resources/fundamentals/authentication/oauth-2-0/overview) + + + Use your **Access Token & Secret** to make requests as your own account. + + These tokens represent the account that owns the app. + + Best for: Testing, personal bots, your own automation. + + -## Next step +--- + +## Credential security best practices + + + + Never hardcode credentials in your source code. + + + Add credential files to `.gitignore`. + + + Regenerate credentials periodically as a security measure. + + + Only request the OAuth permissions your app needs. + + + +--- -Find an endpoint to start working with via our [API reference index](/x-api/introduction). +## Next steps -We also have a set of X API [tools and libraries](/x-api/tools-and-libraries/overview) that you can use to speed up your integration. + + + Call the API with your new credentials. + + + Understand OAuth 1.0a and OAuth 2.0. + + diff --git a/x-api/getting-started/important-resources.mdx b/x-api/getting-started/important-resources.mdx index 097248079..c9149af3d 100644 --- a/x-api/getting-started/important-resources.mdx +++ b/x-api/getting-started/important-resources.mdx @@ -1,30 +1,129 @@ --- -title: Important resources -keywords: ["API resources", "documentation", "tutorials", "integration guides", "support", "API updates", "migration", "developer resources"] +title: Important Resources +sidebarTitle: Important Resources +description: Key documentation, tools, and community resources +keywords: ["API resources", "documentation", "tutorials", "support", "API updates", "developer resources"] --- -## Learn about what's possible +Bookmark these essential resources for X API development. -Our use cases, solutions, and product pages that are accessible from the top navigation are a great way to get a high-level overview of what's possible on the platform. +--- + +## Documentation + + + + Complete endpoint documentation with parameters and examples. + + + Object schemas for posts, users, media, and more. + + + OAuth 1.0a and OAuth 2.0 implementation guides. + + + Per-endpoint limits and best practices. + + + +--- + +## Tools + +| Tool | Description | +|:-----|:------------| +| [Developer Console](https://console.x.com) | Manage apps, credentials, and billing | +| [Postman Collection](https://app.getpostman.com/run-collection/9956214-784efcda-ed4c-4491-a4c0-a26470a67400) | Interactive API testing | +| [Python SDK](/xdks/python/overview) | Official Python library | +| [TypeScript SDK](/xdks/typescript/overview) | Official TypeScript library | +| [OpenAPI Spec](https://api.x.com/2/openapi.json) | Machine-readable API specification | + +--- + +## Learning -We have also built out several [tutorials](/tutorials) that help you identify how to use one or many different platform tools to satisfy a variety of different use cases. + + + Step-by-step guides for common use cases. + + + Example apps and code samples. + + + Ideas and inspiration for projects. + + + Upgrade from v1.1 to v2. + + -At the lowest-level of our documentation lives our endpoint and tool docs, which all include introductory content that provides a brief overview of that tool's functionality and typical use case. For example, here is our X API v2 endpoint section for [Search Posts](/x-api/posts/search/introduction). +--- + +## Community & Support + + + + Ask questions and share solutions with the community. + + + FAQs, troubleshooting, and contact options. + + + Official updates and announcements. + + + API-specific news and tips. + + + +--- + +## Stay updated + +| Resource | What you'll get | +|:---------|:----------------| +| [Changelog](/changelog) | All platform changes and updates | +| [Newsletter](/newsletter) | Monthly roundup of news and features | +| [Forum Announcements](https://devcommunity.x.com/c/announcements/22) | Important platform notices | +| [API Status](/status) | Real-time service availability | + + +Follow [@XDevelopers](https://x.com/XDevelopers) and turn on notifications to catch breaking changes and new features. + + +--- -### Integrate an endpoint into your system +## Quick reference -To make your integration with our endpoints seamless, we have built out a set of tools, libraries, and integration guides. The easiest way to integrate is to use our [tools and libraries](/x-api/tools-and-libraries/overview), which automatically handle complex functionality such as [OAuth 1.0a User Context authentication](/resources/fundamentals/authentication#oauth-1-0a-2), pagination, and [rate limit handling](/resources/fundamentals/rate-limits). +### Response structure -If you would rather build a solution from the ground up, our endpoint integration guides describe the functionality and best practices around integrating our endpoints into your system. These guides can be found in the different endpoint and tool documentation, listed as 'integrate' or simply 'guides'. For example, here is an integration guide on [how to build a rule for the Filter stream](/x-api/posts/filtered-stream/integrate/build-a-rule). +All v2 responses follow this structure: -### Troubleshoot an issue +```json +{ + "data": { ... }, // Primary object(s) + "includes": { ... }, // Expanded objects (if requested) + "meta": { ... }, // Pagination info + "errors": [ ... ] // Partial errors (if any) +} +``` -If you are running into an error or have a question, please check out our [support hub](https://developer.x.com/en/support/twitter-api). There, you will find troubleshooting tips, answers to frequently asked questions and other useful resources. +### Common parameters -### Keep up-to-date with the latest releases +| Parameter | Use | +|:----------|:----| +| `tweet.fields` | Request specific post fields | +| `user.fields` | Request specific user fields | +| `expansions` | Include related objects | +| `max_results` | Limit results per page | +| `pagination_token` | Get next/previous page | -We frequently release updates to the platform to unlock new X functionality via the API. In addition to this, we aim to release at least one new major version bump per year, which will include some breaking changes. +### Authentication methods -To receive the latest updates, please make sure to subscribe to our [forum's Announcements category](https://devcommunity.x.com/c/announcements/22), follow [@API](https://x.com/api) and [@XDevelopers](https://x.com/XDevelopers) on X and turn on notifications, and review our different resources via our [stay informed](https://developer.x.com/en/stay-informed) page. +| Method | Use case | +|:-------|:---------| +| Bearer Token | Read-only public data | +| OAuth 2.0 | User actions with fine-grained scopes | +| OAuth 1.0a | User actions (legacy, full permissions) | -In addition to this, we have built out a new [migration hub](/x-api/migrate/overview) to help guide you through the process of updating to the latest version of the API. +[Full authentication guide →](/resources/fundamentals/authentication/overview) diff --git a/x-api/getting-started/make-your-first-request.mdx b/x-api/getting-started/make-your-first-request.mdx index 77cc2f9cd..41253dd8e 100644 --- a/x-api/getting-started/make-your-first-request.mdx +++ b/x-api/getting-started/make-your-first-request.mdx @@ -1,84 +1,231 @@ --- -title: Make your first request to the X API -sidebarTitle: Make your first request -keywords: ["first request", "getting started", "API quickstart", "make API request", "Postman", "cURL", "API example", "sample code", "API tutorial"] +title: Make Your First Request +sidebarTitle: Make Your First Request +description: Get up and running with the X API in minutes +keywords: ["first request", "getting started", "API quickstart", "make API request", "cURL", "API example", "sample code"] --- -This guide will walk you through some steps that you could follow to make your first request. This is a great resource to help you once you've signed up for a X account. +import { Button } from '/snippets/button.mdx'; -If you are interested in using code samples, more technical guides, or a graphical tool like Postman, please consider using the following guides to make your first request: +This guide walks you through making your first X API request. You'll need a [developer account with app credentials](/x-api/getting-started/getting-access) before starting. -- [A quick start guide to making your first request to the post endpoint](/x-api/posts/manage-tweets/quickstart) -- [Getting started with Postman](/tutorials/postman-getting-started) - best for beginners -- [X API v2 code samples](https://github.com/xdevplatform/Twitter-API-v2-sample-code) -- [Post lookup API reference](/x-api/posts/lookup/introduction) +--- -This guide assumes that you have collected your [API key and secret](https://developer.x.com/resources/fundamentals/authentication/api-key-and-secret), [user Access Token and Secret](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens), [App Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only), and have stored them in a secure location. You can learn how to do this by following the steps in the [getting access to the X API](/x-api/getting-started/getting-access) guide. +## Quick start with cURL -### Step 1. Identify which endpoint you would like to use +The fastest way to test the API is with cURL. Let's look up a user: -The X API allows you to perform a variety of different actions using code that you might be able to perform on the X website or mobile application. +```bash +curl "https://api.x.com/2/users/by/username/xdevelopers" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -We have a complete list of the endpoints that are available via the API listed within our [API Reference Index](/x-api/introduction), but we recommend sticking to one of the following for simplicity's sake: +Replace `$BEARER_TOKEN` with your actual Bearer Token. You'll get a response like: -- [Manage Posts > Post a Tweet](/x-api/posts/manage-tweets/introduction) -- [Search Posts](/x-api/posts/search/introduction) -- [User lookup](/x-api/users/lookup/introduction) +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "xdevelopers" + } +} +``` -### Step 2. Choose a tool to make your request +--- -While some requests can be straightforward, others can be tricky to build. That's why the amazing community of developers have developed tools to abstract away some of the complexity. +## Step-by-step guide + + + + In the [Developer Console](https://console.x.com), navigate to your app and copy the Bearer Token. + + + Start with one of these beginner-friendly endpoints: + + | Endpoint | What it does | + |:---------|:-------------| + | [User lookup](/x-api/users/lookup/introduction) | Get user profile by username or ID | + | [Post lookup](/x-api/posts/lookup/introduction) | Get post by ID | + | [Recent search](/x-api/posts/search/introduction) | Search posts from the last 7 days | + + + Use cURL, Postman, or your preferred HTTP client: + + ```bash + # Look up a user by username + curl "https://api.x.com/2/users/by/username/xdevelopers" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + + Responses are JSON. The primary data is in the `data` field: + + ```json + { + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "xdevelopers" + } + } + ``` + + -The following are some recommended tools and details on how to use them: +--- -#### Postman +## Request more data with fields -Postman is a visual tool that you can use to make requests to REST endpoints. We've created some great materials around Postman to help you get started with and explore the different endpoints available via the X API. +By default, endpoints return minimal fields. Use the `fields` parameter to request additional data: -We recommend you read through our ["Getting started with Postman" tutorial](/tutorials/postman-getting-started) to learn how to add your keys and tokens and make your first request. +```bash +curl "https://api.x.com/2/users/by/username/xdevelopers?user.fields=created_at,description,public_metrics" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -We've also produced a quick start guide for each of our X API v2 endpoints, most of which use Postman. You can find these guides in each respective endpoint section, but here is a link to a few: +Response: + +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "xdevelopers", + "created_at": "2013-12-14T04:35:55.000Z", + "description": "The voice of the X Developer Platform", + "public_metrics": { + "followers_count": 570842, + "following_count": 2048, + "tweet_count": 14052, + "listed_count": 1672 + } + } +} +``` -- Quick start: Post a Tweet -- Quick start: Search for Posts -- Quick start: Lookup a user +[Learn more about fields →](/x-api/fundamentals/fields) -Please note that you can't make requests to streaming endpoints using Postman. Please visit the [filtered stream](/x-api/posts/filtered-stream/quickstart) or [1% sampled stream](/x-api/posts/sample-stream) quick start guide to learn how to work with those endpoints. +--- -If you prefer a more simple graphical tool, you should also consider using [Insomnia](https://insomnia.rest). +## More examples -#### Sample code + + +```bash +curl "https://api.x.com/2/tweets/1460323737035677698?tweet.fields=created_at,public_metrics" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + + +```bash +curl "https://api.x.com/2/tweets/search/recent?query=from:xdevelopers&tweet.fields=created_at" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + + +```bash +curl "https://api.x.com/2/users/2244994945/tweets?max_results=5" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + + -If you are interested in using some simple code to make your request, we've put together sample code in multiple different languages for each of our X API v2 endpoints. +--- -You can find the code samples via our Github repo, [X-API-v2-sample-code](https://github.com/xdevplatform/Twitter-API-v2-sample-code), which also contains a README file that you can use to learn how to set up your credentials to properly work with the requests. +## Using code instead of cURL -For example, here is a cURL example for the user lookup endpoint. All you have to do to use this request is replace the $ACCESS_TOKEN and $USERNAME with your [App Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) and X handle. Then, copy and paste this code into your command line tool and press 'Return' or 'Enter'. + + +```python +import requests -```bash -curl "https://api.x.com/2/users/by/username/$USERNAME" -H "Authorization: Bearer $ACCESS_TOKEN" +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/users/by/username/xdevelopers" + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers) + +print(response.json()) +``` + + +```javascript +const bearerToken = "YOUR_BEARER_TOKEN"; +const url = "https://api.x.com/2/users/by/username/xdevelopers"; + +fetch(url, { + headers: { Authorization: `Bearer ${bearerToken}` } +}) + .then(res => res.json()) + .then(data => console.log(data)); ``` + + +For production use, we recommend the official SDKs: + +- [Python SDK](/xdks/python/overview) +- [TypeScript SDK](/xdks/typescript/overview) -#### Libraries +They handle authentication, pagination, and rate limiting automatically. + + -The amazing X Developer community has also built libraries in a variety of different coding languages that can be used to make requests to the X API. +--- -We've built out a ["Tools and libraries" page](/x-api/tools-and-libraries/overview) that lists all of the community libraries that we are aware of. Each library should have a ReadMe file that can be used to learn how to set up the repo on your machine and make your first request. +## Tools for testing -> **Please note:** If you run into any problems, please read through our developer docs for the endpoint that you are making a request to, our [support section](https://developer.x.com/en/support), or reach out to the community on our forums for help. We'll get you to a successful request! + + + Visual API testing with our collection. + + + Examples in multiple languages. + + + Full endpoint documentation. + + -### Step 3. Review the response +--- -Once you've made a successful request, you will receive a payload with metadata related to the request. +## Troubleshooting -If you used an endpoint that utilizes a GET HTTP method, you will receive metadata related to the resource (Post, user, List, Space, etc) that you made the request to in JSON format. Review the different fields that returned and see if you can map the information that you requested to the content on X. + +- Check that your Bearer Token is correct +- Ensure the token hasn't been regenerated +- Verify the `Authorization` header format: `Bearer YOUR_TOKEN` + -If you used an endpoint that utilizes a POST, PUT, or DELETE HTTP method, you performed an action on X. Go to X.com or the mobile app and see if you can track down that action. + +- Your app may not have access to this endpoint +- Some endpoints require user-context authentication (OAuth 1.0a or 2.0) +- Check your app's permissions in the Developer Console + -### Step 4. Adjust your request using parameters + +- You've hit a rate limit +- Check the `x-rate-limit-reset` header for when to retry +- Implement exponential backoff in your code + -Each endpoint has a different set of parameters that you can use to alter your request. For example, you can request additional metadata fields when using GET endpoints with the fields and expansions parameters. You can also experiment with a variety of different filtering tools with endpoints such as [search Posts](/x-api/posts/search/introduction), [Post counts](/x-api/posts/counts/introduction), and [filtered stream](/x-api/posts/filtered-stream/introduction) to help narrow down the data you receive to just those Posts that you are interested in. +[Full error reference →](/x-api/fundamentals/response-codes-and-errors) -You can find a full list of the request parameters and fields in the API Reference for the endpoint that you are working with, and a host of other helpful integration information in our integration guides and fundamentals pages. +--- -You can learn more about all of the educational materials we've made available to you via our [Important resources](/x-api/getting-started/important-resources) guide. +## Next steps + + + + Understand OAuth for user-context requests. + + + Discover what you can build. + + + Faster development with official libraries. + + + Ideas for what to create. + + diff --git a/x-api/getting-started/pricing.mdx b/x-api/getting-started/pricing.mdx new file mode 100644 index 000000000..3f258e293 --- /dev/null +++ b/x-api/getting-started/pricing.mdx @@ -0,0 +1,98 @@ +--- +title: Pricing +sidebarTitle: Pricing +description: Pay-per-usage pricing for the X API +keywords: ["X API pricing", "API pricing", "pay-per-usage", "API credits", "billing", "deduplication", "API costs"] +--- + +import { Button } from '/snippets/button.mdx'; + +The X API uses **pay-per-usage** pricing. No subscriptions, no monthly caps—pay only for what you use. + + + +--- + +## How it works + + + + Purchase credits upfront in the Developer Console. Credits are deducted as you make API requests. + + + Different endpoints have different costs. View current rates in the Developer Console. + + + No contracts, subscriptions, or minimum spend. Start and stop anytime. + + + Monitor usage and costs live in the Developer Console. + + + +--- + +## Deduplication + +All resources are deduplicated within a **24-hour UTC day window**. If you request and are charged for a resource (such as a Post), requesting the same resource again within that window will not incur an additional charge. + +This means: +- Requesting the same Post multiple times in a day counts as one charge +- The deduplication window resets at midnight UTC +- This applies to all billable resources (Posts, users, etc.) + + +Deduplication is a **soft guarantee**. While it occurs in the vast majority of cases, there may be specific edge cases like service outages that result in resources not being deduplicated. + + +--- + +## Credit balance + +Your credit balance is displayed in the Developer Console. Credits are deducted in real-time as you make API requests. + +### Negative balance + +Your credit balance can go slightly negative. If this happens, API requests will be blocked until you add credits to cover the negative balance. + + +Monitor your credit balance regularly to avoid service interruptions. Add credits before your balance reaches zero to ensure uninterrupted API access. + + +--- + +## Monitoring usage + +Track your API usage programmatically with the [Usage endpoint](/x-api/usage/introduction): + +```bash +curl "https://api.x.com/2/usage/tweets" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +This returns daily Post consumption counts, helping you: +- Track consumption against your budget +- Set up alerts when approaching limits +- Identify high-consumption endpoints +- Generate usage reports + +--- + +## Enterprise pricing + +For high-volume access with dedicated support, custom rate limits, and additional features, contact our enterprise sales team. + + + +--- + +## Next steps + + + + Purchase credits and view current pricing. + + + Monitor usage programmatically. + + diff --git a/x-api/introduction.mdx b/x-api/introduction.mdx index 57d592974..1071c2be7 100644 --- a/x-api/introduction.mdx +++ b/x-api/introduction.mdx @@ -1,129 +1,195 @@ --- -title: X API v2 -sidebarTitle: "Introduction" +title: X API +sidebarTitle: Introduction mode: wide -keywords: ["X API", "X API v2", "Twitter API", "API v2", "API pricing", "API tiers", "free tier", "basic tier", "pro tier", "enterprise API", "API endpoints", "REST API", "API documentation"] +description: Programmatic access to X's posts, users, spaces, and more +keywords: ["X API", "X API v2", "Twitter API", "API v2", "API pricing", "pay-per-usage", "API endpoints", "REST API"] --- import { Button } from "/snippets/button.mdx" -import { PricingCard } from "/snippets/pricing-card.mdx" -X API v2 gives you programmatic access to X's global conversation—posts, users, spaces, DMs, lists, trends, media, and more. Build apps, analyze data, and create new experiences with powerful, modern endpoints. +The X API gives you programmatic access to X's public conversation. Read posts, publish content, manage users, and analyze trends—all through modern REST endpoints with flexible pay-per-usage pricing. + + + + Create an app and make your first request in minutes. + + + Explore all available endpoints. + + + Official Python and TypeScript libraries. + + --- -## Key Features +## What you can build - - - Access detailed, structured data for posts, users, media, and more with comprehensive field selection. + + + Search, retrieve, and publish posts. Access timelines, threads, and quote posts. - - Get engagement analytics, real-time metrics, and detailed insights for your data. + + Look up users, manage follows, blocks, and mutes. - - Subscribe to filtered or sampled streams for live updates and real-time data processing. + + Find live audio conversations and their participants. + + + Send and receive private messages. + + + Create and manage curated lists of accounts. + + + Access trending topics by location. --- -## API Tiers & Pricing - -
- Login with X access, - "Basic v2 endpoints access" - ]} - /> - - - - Full-archive search access, - Filtered stream access, - "Priority support" - ]} - /> -
+## Pricing -
-

- Need Enterprise-level access? -

-

- For businesses and scaled commercial projects, we offer custom solutions with dedicated support, - complete streams, backfill capabilities, and custom integrations. -

-
- -
+The X API uses **pay-per-usage** pricing. No subscriptions, no monthly caps—pay only for what you use. + + + + Start small and grow. Costs scale with your actual usage. + + + No contracts or minimum spend. Stop anytime. + + + Monitor usage and costs live in the Developer Console. + + + Purchase credits upfront. Deducted as you use the API. + + + +
+ +
--- -## Get Started Tools +## Key features + + + + ### Rich data objects + + Access detailed, structured data for posts, users, media, and more: + + - **Posts**: Full text, metrics, entities, annotations, conversation threads + - **Users**: Profiles, follower counts, verification status + - **Media**: Images, videos, GIFs with metadata + - **Polls**: Options and vote counts + + Customize responses with [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) to get exactly the data you need. + + + ### Filtered stream + + Get posts delivered in real-time as they're published. Define up to 1,000 filtering rules to receive only matching posts. + + ```bash + # Add a rule + curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Authorization: Bearer $TOKEN" \ + -d '{"add": [{"value": "from:xdevelopers"}]}' + + # Connect to stream + curl "https://api.x.com/2/tweets/search/stream" \ + -H "Authorization: Bearer $TOKEN" + ``` + + [Learn more about filtered stream →](/x-api/posts/filtered-stream/introduction) + + + ### Full-archive search + + Search the complete history of public posts—back to 2006. Build queries with operators for users, keywords, dates, and more. + + ```bash + curl "https://api.x.com/2/tweets/search/all?query=AI%20lang:en" \ + -H "Authorization: Bearer $TOKEN" + ``` + + ### Metrics + + Access engagement metrics including impressions, likes, reposts, replies, and video views. + + [Learn more about search →](/x-api/posts/search/introduction) + + + +--- + +## API versions + +| Version | Status | Description | +|:--------|:-------|:------------| +| **v2** | Current | Modern endpoints, flexible pricing, all new features | +| **v1.1** | Legacy | Limited support, some endpoints still available | +| **Enterprise** | Available | High-volume access with dedicated support | + + +Use **X API v2** for all new projects. It's where all new features and improvements are released. + + +--- + +## Quick start + + + + Sign up at [console.x.com](https://console.x.com) and create an app. + + + Generate your Bearer Token for app-only requests. + + + Try looking up a user: + + ```bash + curl "https://api.x.com/2/users/by/username/xdevelopers" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + - - - Official and community SDKs for fast integration. Support for TypeScript, Java, Python, and more. -

- [Browse libraries](/x-api/tools-and-libraries/overview) + + +--- + +## Tools & libraries + + + + Official Python library with async support. - - Explore endpoints visually and test requests with our comprehensive Postman collection. -

- [Get started with Postman](https://app.getpostman.com/run-collection/9956214-784efcda-ed4c-4491-a4c0-a26470a67400) + + Official TypeScript/JavaScript library. - - Example apps, code samples, and tutorials on GitHub to help you get started quickly. -

- [Get started with our sample code](https://github.com/xdevplatform) + + Interactive API explorer.
+[Browse all libraries →](/x-api/tools-and-libraries/overview) + --- -## Support & Community +## Support -
- - -
\ No newline at end of file + + + Get help from the community and X team. + + + FAQs and troubleshooting guides. + + diff --git a/x-api/lists/list-lookup/integrate.mdx b/x-api/lists/list-lookup/integrate.mdx index 8b0b892ce..1ed7acceb 100644 --- a/x-api/lists/list-lookup/integrate.mdx +++ b/x-api/lists/list-lookup/integrate.mdx @@ -1,91 +1,281 @@ --- -title: Integration guide -sidebarTitle: Integration guide -keywords: ["list lookup integration", "lists integration guide", "list lookup setup", "lists integration"] +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for integrating List lookup +keywords: ["list lookup integration", "lists integration guide", "list lookup implementation"] --- import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and critical concepts that you should know as you integrate the List lookup endpoints into your system. We’ve broken the page into a couple of different sections: +This guide covers the key concepts you need to integrate the List lookup endpoints into your application. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) -* [Pagination](#pagination) +--- + +## Authentication + +List lookup endpoints support multiple authentication methods: + +| Method | Best for | Access to private Lists? | +|:-------|:---------|:-------------------------| +| [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Public List data | No | +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | User-facing apps | Yes (owned/followed) | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy integrations | Yes (owned/followed) | + +### Example request + + + +```bash cURL +curl "https://api.x.com/2/lists/84839422?\ +list.fields=description,member_count,follower_count,private" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get a List by ID +response = client.lists.get( + list_id="84839422", + list_fields=["description", "member_count", "follower_count", "private"] +) +print(response.data) +``` -### Helpful tools +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -#### Postman +const response = await client.lists.get("84839422", { + listFields: ["description", "member_count", "follower_count", "private"], +}); +console.log(response.data); +``` -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  + -#### Code samples +--- + +## Endpoints overview + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/lists/:id`](/x-api/lists/get-list) | Get List by ID | +| GET | [`/2/users/:id/owned_lists`](/x-api/users/get-owned-lists) | Get Lists owned by a user | + +--- -Are you interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +## Fields and expansions + +### Default response + +```json +{ + "data": { + "id": "84839422", + "name": "Tech News" + } +} +``` + +### Available fields + + +| Field | Description | +|:------|:------------| +| `created_at` | List creation timestamp | +| `description` | List description | +| `follower_count` | Number of followers | +| `member_count` | Number of members | +| `owner_id` | Owner's user ID | +| `private` | Whether List is private | + + + +| Field | Description | +|:------|:------------| +| `username` | Owner's @handle | +| `name` | Owner's display name | +| `verified` | Owner's verification status | +| `profile_image_url` | Owner's avatar URL | + + +### Example with expansions + + + +```bash cURL +curl "https://api.x.com/2/lists/84839422?\ +list.fields=description,member_count,follower_count,owner_id&\ +expansions=owner_id&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get List with owner expansion +response = client.lists.get( + list_id="84839422", + list_fields=["description", "member_count", "follower_count", "owner_id"], + expansions=["owner_id"], + user_fields=["username", "verified"] +) + +print(response.data) +print(response.includes) # Contains owner user object +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +const response = await client.lists.get("84839422", { + listFields: ["description", "member_count", "follower_count", "owner_id"], + expansions: ["owner_id"], + userFields: ["username", "verified"], +}); + +console.log(response.data); +console.log(response.includes); // Contains owner user object +``` + + + +### Response with expansion + +```json +{ + "data": { + "id": "84839422", + "name": "Tech News", + "description": "Top tech journalists", + "member_count": 50, + "follower_count": 1250, + "owner_id": "2244994945" + }, + "includes": { + "users": [ + { + "id": "2244994945", + "username": "XDevelopers", + "verified": true + } + ] + } +} +``` + + + Learn more about customizing responses + -#### Third-party libraries +--- -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +## Pagination -### Key concepts +When retrieving owned Lists, results are paginated: -#### Authentication + -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, App only or OAuth 2.0 Authorization Code with PKCE to authenticate your requests to these endpoints. .  +```bash cURL +# First request +curl "https://api.x.com/2/users/123/owned_lists?max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" -[OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-user-context), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). +# Subsequent request with pagination token +curl "https://api.x.com/2/users/123/owned_lists?max_results=100&pagination_token=NEXT_TOKEN" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. +```python Python SDK +from xdk import Client -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +client = Client(bearer_token="YOUR_BEARER_TOKEN") -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +# The SDK handles pagination automatically +all_lists = [] -[App only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) just requires that you pass a [App only Access Token](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) with your request. You can either generate an App only token from directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. +for page in client.lists.get_user_owned_lists(user_id="123", max_results=100): + if page.data: + all_lists.extend(page.data) -#### Developer portal, Projects, and developer Apps +print(f"Found {len(all_lists)} lists") +``` -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -  +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -#### Rate limits +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user.  +async function getAllOwnedLists(userId) { + const allLists = []; -These endpoints are rate limited at both the App-level and the user-level. The app rate limit means that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by using either the API Key and API Secret Key, or the App only Access Token). The user rate limit means that the authenticated user that you are making the request on behalf of can only perform a List lookup a certain number of times across any developer App. + // The SDK handles pagination automatically + const paginator = client.lists.getUserOwnedLists(userId, { maxResults: 100 }); -The chart below shows the rate limits for each endpoint. + for await (const page of paginator) { + if (page.data) { + allLists.push(...page.data); + } + } -| | | | -| :--- | :--- | :--- | -| **Endpoint** | **HTTP method** | **Rate limit** | -| /2/lists/:id | GET | 75 requests per 15 minutes | -| /2/users/:id/owned_lists | GET | 15 requests per 15 minutes | + return allLists; +} -Fields and expansions +// Usage +const lists = await getAllOwnedLists("123"); +console.log(`Found ${lists.length} lists`); +``` -The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called `fields` and `expansions`. The `expansions` parameter allows you to expand objects referenced in the payload. For example, looking up a List by ID allows you to pull the following [expansions](/x-api/fundamentals/expansions): + -* `owner_id` + + Learn more about pagination + +--- -The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. These endpoints deliver List objects primarily. By default, the List object returns the `id`, and `name` fields. To receive additional fields such as `list.created_at` or `list.follower_count`, you will have to specifically request those using a list.fields parameter.  +## Private Lists -We’ve added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). +- Private Lists are only visible to the owner +- You must authenticate as the owner to retrieve private List details +- The `private` field indicates whether a List is private - The chart below shows the field and expansions available for this endpoint group: +--- -| | | | -| :--- | :--- | :--- | -| **Endpoint** | **Fields** | **Expansions** | -| /2/lists/:id | `list.fields`

`user.fields` | `owner_id` | -| /2/users/:id/owned_lists | list.fields

user.fields | owner_id | +## Error handling -#### Pagination +| Status | Error | Solution | +|:-------|:------|:---------| +| 400 | Invalid request | Check List ID format | +| 401 | Unauthorized | Verify authentication | +| 403 | Forbidden | List may be private | +| 404 | Not Found | List doesn't exist | +| 429 | Too Many Requests | Wait and retry | + +--- -Looking up user owned Lists can return a lot of data. To ensure we are returning consistent, high-performing results at any given time, we use pagination. Pagination is a feature in X API v2 endpoints that return more results than can be returned in a single response. When that happens, the data is returned in a series of 'pages'. Learn more about how to [paginate through results.](/x-api/fundamentals/pagination) +## Next steps + + + + Make your first List lookup request + + + Get Posts from a List + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/lists/list-lookup/introduction.mdx b/x-api/lists/list-lookup/introduction.mdx index f1b28b96c..b63af7cf6 100644 --- a/x-api/lists/list-lookup/introduction.mdx +++ b/x-api/lists/list-lookup/introduction.mdx @@ -1,36 +1,80 @@ --- -title: Introduction +title: List Lookup sidebarTitle: Introduction +description: Retrieve List details by ID or get Lists owned by a user keywords: ["list lookup", "get lists", "list by ID", "list information", "list details", "lookup lists", "list API"] --- import { Button } from '/snippets/button.mdx'; -There is a rate limit of 75 requests per 15 minutes when looking up a specified List by ID and a limit of 15 requests per 15 minutes when looking up user-owned Lists. +The List lookup endpoints let you retrieve information about Lists. Look up a specific List by ID or get all Lists owned by a user. -You can use [OAuth 1.0a User Context](/resources/fundamentals/authentication), [App only](https://developer.x.com(/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) to authenticate your requests to these endpoints.  - -**Account setup** +## Overview + + + + Get details for a specific List + + + Get all Lists owned by a user + + + +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/lists/:id`](/x-api/lists/get-list-by-id) | Get List by ID | +| GET | [`/2/users/:id/owned_lists`](/x-api/users/get-owned-lists) | Get Lists owned by a user | + +--- -To access these endpoints, you will need: +## Response fields -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +By default, the response includes `id` and `name`. Request additional fields: + +| Field | Description | +|:------|:------------| +| `description` | List description | +| `owner_id` | Owner's user ID | +| `private` | Whether List is private | +| `follower_count` | Number of followers | +| `member_count` | Number of members | +| `created_at` | List creation date | + +### Example request + +```bash +curl "https://api.x.com/2/lists/1234567890?\ +list.fields=description,owner_id,member_count,follower_count" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +--- + +## Getting started + + +**Prerequisites** -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) -
- - - - -
\ No newline at end of file + + + Make your first List lookup request + + + Key concepts and best practices + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/lists/list-lookup/migrate/overview.mdx b/x-api/lists/list-lookup/migrate/overview.mdx index f34d44c7b..224a76d99 100644 --- a/x-api/lists/list-lookup/migrate/overview.mdx +++ b/x-api/lists/list-lookup/migrate/overview.mdx @@ -34,7 +34,7 @@ The following tables compare the standard v1.1 and X API v2 List endpoints: | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context

App only | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE

App only | | Default request [rate limits](/resources/fundamentals/rate-limits) | 15 requests per 15 min with OAuth 1.0a

15 requests per 15 min with App only | 15 requests per 15 min with OAuth 1.0a

15 requests per 15min with OAuth 2.0

15 requests per 15 min with App only | -To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started](/x-api/getting-started/getting-access) page. diff --git a/x-api/lists/list-lookup/migrate/standard-to-twitter-api-v2.mdx b/x-api/lists/list-lookup/migrate/standard-to-twitter-api-v2.mdx index d8a80a039..ec9bad1d6 100644 --- a/x-api/lists/list-lookup/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/lists/list-lookup/migrate/standard-to-twitter-api-v2.mdx @@ -55,7 +55,7 @@ Depending on your authentication library/package of choice, App only authenticat **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Data objects per request limits** diff --git a/x-api/lists/list-lookup/quickstart.mdx b/x-api/lists/list-lookup/quickstart.mdx index 988dc8dab..41244a775 100644 --- a/x-api/lists/list-lookup/quickstart.mdx +++ b/x-api/lists/list-lookup/quickstart.mdx @@ -1,119 +1,261 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["list lookup quickstart", "get lists quickstart", "list lookup tutorial", "list information quickstart"] +description: Look up Lists by ID or owner +keywords: ["list lookup quickstart", "get list tutorial", "list lookup guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the List lookup endpoint +This guide walks you through looking up List information using the List lookup endpoints. + +**Prerequisites** -This quick start guide will help you make your first request to the List lookup endpoint using Postman. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token + -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to see sample code in different languages. +--- -**Note: **For this example, we will make a request to the _List lookup by ID _endpoint, but you can apply the learnings from this quick start to other lookup requests as well. +## Get a List by ID - -### Prerequisites +Retrieve details for a specific List: -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: + -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. - -### Steps to build a List lookup request +```bash cURL +curl "https://api.x.com/2/lists/1234567890?\ +list.fields=description,owner_id,member_count,follower_count,private,created_at" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -**Step one: Start with a tool or library** +```python Python SDK +from xdk import Client -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -To load the X API v2 Postman collection into your environment, please click on the following button: +# Get a List by ID +response = client.lists.get( + "1234567890", + list_fields=["description", "owner_id", "member_count", "follower_count", "private", "created_at"] +) - +print(f"List: {response.data.name}") +print(f"Members: {response.data.member_count}") +``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Once you have the X API v2 collection loaded in Postman, navigate to the “List” folder, select another folder “List lookup”, and then choose "List by ID". -  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -**Step two: Authenticate your request** +// Get a List by ID +const response = await client.lists.get("1234567890", { + listFields: ["description", "owner_id", "member_count", "follower_count", "private", "created_at"], +}); -To properly make a request to the X API, you need to verify that you have permission. To do this with this endpoint, you must authenticate your request with either [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods. +console.log(`List: ${response.data?.name}`); +console.log(`Members: ${response.data?.member_count}`); +``` -For simplicity's sake, we are going to utilize App only with this request, but if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Lists, you will need to use one of the other authentication methods.  + -To utilize App only, you must add your keys and tokens (specifically the[App only Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token)) to Postman by selecting the environment named “X API v2” (in the top-right corner of Postman), and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +### Response -If you've done this correctly, these variables will automatically be pulled into the request's authorization tab. -  +```json +{ + "data": { + "id": "1234567890", + "name": "Tech News", + "description": "Top tech journalists and publications", + "owner_id": "2244994945", + "private": false, + "member_count": 50, + "follower_count": 1250, + "created_at": "2023-01-15T10:00:00.000Z" + } +} +``` -**Step three: Identify and specify which List you would like to retrieve** +--- -You must specify a List that you would like to receive within the request. You can find the List ID by navigating to x.com and clicking on a List and then looking in the URL. For example, the following URL's List ID is 84839422. +## Get Lists owned by a user -https://x.com/i/lists/84839422 +Retrieve all Lists owned by a specific user: -The target ID can be any valid List ID. In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the id path variable. Be sure not to include any spaces before or after any ID. + -| | | -| :--- | :--- | -| **Key** | **Value** | -| id | 84839422 (The List ID) | +```bash cURL +curl "https://api.x.com/2/users/2244994945/owned_lists?\ +list.fields=description,member_count,follower_count&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` +```python Python SDK +from xdk import Client -**Step four: Identify and specify which fields you would like to retrieve** +client = Client(bearer_token="YOUR_BEARER_TOKEN") -If you click the "Send" button after step three, you will receive the default [List object](/x-api/fundamentals/data-dictionary#list) fields in your response: id, name. +# Get Lists owned by a user with pagination +for page in client.lists.get_owned_lists( + "2244994945", + list_fields=["description", "member_count", "follower_count"], + max_results=100 +): + for lst in page.data: + print(f"{lst.name} - {lst.member_count} members") +``` -If you would like to receive additional fields, you will have to specify those fields in your request with list.fields and/or [expansion](/x-api/fundamentals/expansions) parameters. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -For this exercise, we will request three additional sets of fields from different objects: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -* The additional created_at field in the primary Lists object. +// Get Lists owned by a user with pagination +const paginator = client.lists.getOwnedLists("2244994945", { + listFields: ["description", "member_count", "follower_count"], + maxResults: 100, +}); -* The full [user object](/x-api/fundamentals/data-dictionary#user) using the expansion parameter +for await (const page of paginator) { + page.data?.forEach((lst) => { + console.log(`${lst.name} - ${lst.member_count} members`); + }); +} +``` -* The additional user.created_at field in the associated user object. + +### Response -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +```json +{ + "data": [ + { + "id": "1234567890", + "name": "Tech News", + "description": "Top tech journalists", + "member_count": 50, + "follower_count": 1250 + }, + { + "id": "9876543210", + "name": "Developer Tools", + "description": "Useful tools for developers", + "member_count": 25, + "follower_count": 500 + } + ], + "meta": { + "result_count": 2 + } +} +``` + +--- + +## Include owner information + +Expand the owner's user data: -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| list.fields | created_at | created_at | -| expansions | owner_id | includes.users.id,
includes.users.name,
includes.users.username | -| user.fields | created_at | includes.users.created_at | + -You should now see a similar URL next to the “Send” button: +```bash cURL +curl "https://api.x.com/2/lists/1234567890?\ +list.fields=description,owner_id&\ +expansions=owner_id&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") - `https://api.x.com/2/lists/84839422?list.fields=owner_id&expansions=owner_id&user.fields=created_at` +# Get List with owner info +response = client.lists.get( + "1234567890", + list_fields=["description", "owner_id"], + expansions=["owner_id"], + user_fields=["username", "verified"] +) + +print(f"List: {response.data.name}") +# Owner info is in response.includes.users +``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Step five: Make your request and review your response** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +// Get List with owner info +const response = await client.lists.get("1234567890", { + listFields: ["description", "owner_id"], + expansions: ["owner_id"], + userFields: ["username", "verified"], +}); +console.log(`List: ${response.data?.name}`); +// Owner info is in response.includes?.users ``` + + + +### Response with expansion + +```json { "data": { - "id": "84839422", - "name": "Official Twitter Accounts", - "owner_id": "783214" + "id": "1234567890", + "name": "Tech News", + "description": "Top tech journalists", + "owner_id": "2244994945" }, "includes": { "users": [ { - "name": "Twitter", - "created_at": "2007-02-20T14:35:54.000Z", - "username": "Twitter", - "id": "783214" + "id": "2244994945", + "username": "XDevelopers", + "verified": true } ] } } ``` + +--- + +## Available fields + +| Field | Description | +|:------|:------------| +| `description` | List description | +| `owner_id` | Owner's user ID | +| `private` | Whether List is private | +| `member_count` | Number of members | +| `follower_count` | Number of followers | +| `created_at` | List creation date | + +--- + +## Next steps + + + + Get Posts from a List + + + Get List members + + + Create and update Lists + + + Full endpoint documentation + + diff --git a/x-api/lists/list-members/integrate.mdx b/x-api/lists/list-members/integrate.mdx index b77de816d..b8be7552c 100644 --- a/x-api/lists/list-members/integrate.mdx +++ b/x-api/lists/list-members/integrate.mdx @@ -6,93 +6,80 @@ keywords: ["list members integration", "list members guide", "list members setup import { Button } from '/snippets/button.mdx'; -## Integration guide +This page covers tools and key concepts for integrating the List members endpoints. -This page contains information on several tools and critical concepts that you should know as you integrate the List members endpoints into your system. We’ve broken the page into a couple of different sections: - -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) -* [Pagination](#pagination) +--- -### Helpful tools +## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: -#### Postman +### Postman -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. -#### Code samples +### Code samples -Are you interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). -#### Third-party libraries +### Third-party libraries -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +Take advantage of one of our communities' [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. -### Key concepts +--- -#### Authentication +## Key concepts -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, OAuth 2.0 Authorization Code with PKCE, or App only to authenticate your requests for the Lists **lookup** endpoints. However, you must authenticate with OAuth 1.0a User Context or OAuth 2.0 for the **manage** Lists endpoints.  +### Authentication -[OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). +All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, OAuth 2.0 Authorization Code with PKCE, or App only to authenticate your requests for the Lists **lookup** endpoints. However, you must authenticate with OAuth 1.0a User Context or OAuth 2.0 for the **manage** Lists endpoints. -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use either OAuth 2.0 or App only to authenticate your requests. +[OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use either OAuth 2.0 or App only to authenticate your requests. -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application's scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. -[App only](/resources/fundamentals/authentication#oauth-2-0) just requires that you pass an [App only Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) with your request. You can either generate an App only Access Token directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. +To enable OAuth 2.0 in your App, you must enable it in your's App's authentication settings found in the App settings section of the Developer Console. [App only](/resources/fundamentals/authentication#oauth-2-0) just requires that you pass an [App only Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) with your request. You can either generate an App only Access Token directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. -#### Developer portal, Projects, and developer Apps +### Developer Console, Projects, and developer Apps -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -  +To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -#### Rate limits +### Rate limits -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user.  +Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user. Lookup (GET) endpoints are rate limited at both the App-level and the user-level; while manage (POST/DELETE) endpoints are limited at the user-level. The app rate limit means that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by using either the API Key and API Secret Key, or the App only Access Token). The user rate limit means that the authenticated user that you are making the request on behalf of can only perform a List lookup a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. -| | | | +| Endpoint | HTTP method | Rate limit | | :--- | :--- | :--- | -| **Endpoint** | **HTTP method** | **Rate limit** | | /2/lists/:id/members | GET | 900 requests per 15 minutes | | /2/users/:id/list_memberships | GET | 75 requests per 15 minutes | -| /2/lists/:id/members | POST | 300 requests per 15 minutes | -| /2/lists/:id/members/:user_id | DELETE | 300 requests per 15 minutes | +| /2/lists/:id/members | POST | 300 requests per 15 minutes | +| /2/lists/:id/members/:user_id | DELETE | 300 requests per 15 minutes | -#### Fields and expansions +### Fields and expansions The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called `fields` and `expansions`. The `expansions` parameter allows you to expand objects referenced in the payload. For example, looking up List members allows you to pull the following [expansions](/x-api/fundamentals/expansions): * `pinned_tweet_id` +The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. List members lookup delivers user objects primarily. By default, the user object returns id, name, and username fields. To receive additional fields such as `user.created_at` or `user.description`, you will have to specifically request those using a user.fields parameter. -The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. List members lookup delivers user objects primarily. By default, the user object returns id, name, and username fields. To receive additional fields such as `user.created_at` or `user.description`, you will have to specifically request those using a user.fields parameter.  +We've added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions). -We’ve added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions). +The chart below shows the field and expansions available for each lookup endpoint: - The chart below shows the field and expansions available for each lookup endpoint: - -| | | | +| Endpoint | Fields | Expansions | | :--- | :--- | :--- | -| **Endpoint** | **Fields** | **Expansions** | -| /2/lists/:id/members | `user.fields`

`tweet.fields` | `pinned_tweet_id` | -| /2/users/:id/list_memberships | list.fields

user.fields | owner_id | +| /2/lists/:id/members | `user.fields`, `tweet.fields` | `pinned_tweet_id` | +| /2/users/:id/list_memberships | `list.fields`, `user.fields` | `owner_id` | -#### Pagination +### Pagination Looking up membership/members can return a lot of data. To ensure we are returning consistent, high-performing results at any given time, we use pagination. Pagination is a feature in X API v2 endpoints that return more results than can be returned in a single response. When that happens, the data is returned in a series of 'pages'. Learn more about how to [paginate through results.](/x-api/fundamentals/pagination) - diff --git a/x-api/lists/list-members/introduction.mdx b/x-api/lists/list-members/introduction.mdx index a7b3621ff..79dcf614d 100644 --- a/x-api/lists/list-members/introduction.mdx +++ b/x-api/lists/list-members/introduction.mdx @@ -1,48 +1,91 @@ --- -title: Introduction +title: List Members sidebarTitle: Introduction -keywords: ["list members", "list members lookup", "get list members", "list membership", "list members API", "members lookup"] +description: View, add, and remove members from Lists +keywords: ["list members", "add member", "remove member", "list membership", "list member API"] --- import { Button } from '/snippets/button.mdx'; -### List members lookup +The List Members endpoints let you view List members, add members to your Lists, and remove them. You can also see which Lists a user is a member of. + +## Overview + + + + Get all members of a List + + + Add a user to your List + + + Remove a user from your List + + + See Lists a user is on + + + +--- -Members lookup group has two available endpoints. You are able to retrieve details on members of a specified List and see which Lists a user is a member of. These endpoints can be used to enable people to curate and organize new Lists based on the membership information. +## Endpoints -There is a rate limit of 900 requests per 15 minutes when looking up member details and a limit of 75 requests per 15 minutes when looking up user memberships. +### List members lookup -You can use [OAuth 1.0a User Context](/resources/fundamentals/authentication), [App only](https://developer.x.com/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) to authenticate your requests to this endpoint.  -  +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/lists/:id/members`](/x-api/lists/get-list-members) | Get members of a List | +| GET | [`/2/users/:id/list_memberships`](/x-api/users/get-list-memberships) | Get Lists a user is on | ### Manage List members -The manage List members endpoints allow you to add and remove members to a List on behalf of an authenticated user. For these endpoints, there are two methods available: POST and DELETE. The POST method allows you to add a member to a List, and the DELETE method allows you to remove a member from a List. There is a user rate limit of 300 requests per 15 minutes for both endpoints. +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | [`/2/lists/:id/members`](/x-api/lists/add-list-member) | Add a member | +| DELETE | [`/2/lists/:id/members/:user_id`](/x-api/lists/remove-list-member) | Remove a member | -Note that Lists cannot have more than 5,000 members. +--- -Since you are making requests on behalf of a user with the these endpoints, you must authenticate them with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and use the user Access Tokens associated with a user that has authorized your App, which can be generated using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (OAuth 1.0a) or the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)) (OAuth 2.0). - -**Account setup** +## Example: Get List members -To access these endpoints, you will need: +```bash +curl "https://api.x.com/2/lists/1234567890/members?\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +## Example: Add a member -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +```bash +curl -X POST "https://api.x.com/2/lists/1234567890/members" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"user_id": "9876543210"}' +``` + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) -
- - - - -
\ No newline at end of file + + + + Get members of a List + + + Add and remove members + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/lists/list-members/migrate/list-members-lookup-standard-to-twitter-api-v2.mdx b/x-api/lists/list-members/migrate/list-members-lookup-standard-to-twitter-api-v2.mdx index 92a6c8e5c..85c5d6aeb 100644 --- a/x-api/lists/list-members/migrate/list-members-lookup-standard-to-twitter-api-v2.mdx +++ b/x-api/lists/list-members/migrate/list-members-lookup-standard-to-twitter-api-v2.mdx @@ -55,7 +55,7 @@ Depending on your authentication library/package of choice, App only authenticat **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Data objects per request limits** diff --git a/x-api/lists/list-members/migrate/manage-list-members-standard-to-twitter-api-v2.mdx b/x-api/lists/list-members/migrate/manage-list-members-standard-to-twitter-api-v2.mdx index 0b3859c06..ef3d1984e 100644 --- a/x-api/lists/list-members/migrate/manage-list-members-standard-to-twitter-api-v2.mdx +++ b/x-api/lists/list-members/migrate/manage-list-members-standard-to-twitter-api-v2.mdx @@ -50,7 +50,7 @@ Both endpoint versions support [OAuth 1.0a User Context](https://developer.x.com **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps related to a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps related to a project. **Request parameters** diff --git a/x-api/lists/list-members/migrate/overview.mdx b/x-api/lists/list-members/migrate/overview.mdx index 0ed7c3286..29302e2b7 100644 --- a/x-api/lists/list-members/migrate/overview.mdx +++ b/x-api/lists/list-members/migrate/overview.mdx @@ -65,7 +65,7 @@ The following tables compare the standard v1.1 and X API v2 List endpoints: | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE | | Default request [rate limits](/resources/fundamentals/rate-limits) | None | 300 requests per 15 min (per user) | -To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started](/x-api/getting-started/getting-access) page.
diff --git a/x-api/lists/list-members/quickstart/list-members-lookup.mdx b/x-api/lists/list-members/quickstart/list-members-lookup.mdx index 5acbc97b7..30316a5a7 100644 --- a/x-api/lists/list-members/quickstart/list-members-lookup.mdx +++ b/x-api/lists/list-members/quickstart/list-members-lookup.mdx @@ -1,159 +1,179 @@ --- -title: List members lookup -sidebarTitle: List members lookup +title: List Members Lookup +sidebarTitle: List Members Lookup +description: Get members of a List keywords: ["list members lookup quickstart", "list members lookup", "get list members quickstart", "list membership lookup"] --- import { Button } from '/snippets/button.mdx'; +This guide walks you through retrieving members of a List. + -**Please note:** This guide assumes you have completed the prerequisites from the [quick start overview](/x-api/lists/list-members#getting-started-with-the-list-members-endpoint-group). +**Prerequisites** + +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token -### Steps to build a List members lookup request -**Step one: Choose the List endpoint collection from Postman** +--- -Once you have the X API v2 collection loaded in Postman, navigate to the “List” folder, select another folder “List members”, and then choose "Members lookup". -  +## Get List members -**Step two: Identify and specify which List you would like to retrieve members from** + + + You can find the List ID in the URL when viewing a List: -You must specify a List that you would like to receive members from. You can find the List ID by navigating to x.com and clicking on a List and then looking in the URL. For example, the following URL's List ID is 84839422. + ``` + https://x.com/i/lists/84839422 + └── This is the List ID + ``` + -https://x.com/i/lists/84839422 + -The target ID can be any valid List ID. In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the id path variable. Be sure not to include any spaces before or after any ID. + -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | 84839422 (List ID) | +```bash cURL +curl "https://api.x.com/2/lists/84839422/members?\ +user.fields=created_at,username,verified&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -**Step three: Identify and specify which fields you would like to retrieve** +```python Python SDK +from xdk import Client -If you click the "Send" button after step three, you will receive the default [user object](/x-api/fundamentals/data-dictionary#user) fields in your response: id, name, and username. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -If you would like to receive additional fields beyond id, name, and username, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. +# Get List members with pagination +for page in client.lists.get_members( + "84839422", + user_fields=["created_at", "username", "verified"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Joined: {user.created_at}") +``` -For this exercise, we will request three additional sets of fields from different objects: +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -* The additional user.created_at field in the primary user objects. -* The full [Post object](/x-api/fundamentals/data-dictionary#tweet) using the expansion parameter. -* The additional tweet.created_at field in the associated Post objects. +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get List members with pagination +const paginator = client.lists.getMembers("84839422", { + userFields: ["created_at", "username", "verified"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Joined: ${user.created_at}`); + }); +} +``` -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: + -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| user.fields | created_at | `created_at` | -| expansions | pinned\_tweet\_id | `includes.tweets.id,
includes.tweets.text
` | -| tweet.fields | created_at | `includes.tweets.created_at` | +
-You should now see a similar URL next to the “Send” button: + + ```json + { + "data": [ + { + "id": "1319036828964454402", + "name": "Birdwatch", + "username": "birdwatch", + "created_at": "2020-10-21T22:04:47.000Z", + "verified": true + }, + { + "id": "1065249714214457345", + "name": "Spaces", + "username": "TwitterSpaces", + "created_at": "2018-11-21T14:24:58.000Z", + "verified": true + } + ], + "meta": { + "result_count": 2, + "next_token": "5349804505549807616" + } + } + ``` + +
- `https://api.x.com/2/lists/84839422/members?user.fields=created_at&expansions=pinned_tweet_id&tweet.fields=created_at` +--- +## Include additional data -**Step four: Make your request and review your response** +Use expansions to get related data like pinned Posts: -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: + +```bash cURL +curl "https://api.x.com/2/lists/84839422/members?\ +user.fields=created_at&\ +expansions=pinned_tweet_id&\ +tweet.fields=created_at" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` -{ - "data": [ - { - "pinned_tweet_id": "1353789891348475905", - "id": "1319036828964454402", - "created_at": "2020-10-21T22:04:47.000Z", - "name": "Birdwatch", - "username": "birdwatch" - }, - { - "id": "1244731491088809984", - "created_at": "2020-03-30T21:02:29.000Z", - "name": "Twitter Thailand", - "username": "TwitterThailand" - }, - { - "id": "1194267639100723200", - "created_at": "2019-11-12T14:56:22.000Z", - "name": "Twitter Retweets", - "username": "TwitterRetweets" - }, - { - "id": "1168976680867762177", - "created_at": "2019-09-03T19:59:02.000Z", - "name": "Twitter Able", - "username": "TwitterAble" - }, - { - "pinned_tweet_id": "1451239134798942208", - "id": "1065249714214457345", - "created_at": "2018-11-21T14:24:58.000Z", - "name": "Spaces", - "username": "TwitterSpaces" - }, - { - "id": "1049385226424786944", - "created_at": "2018-10-08T19:45:09.000Z", - "name": "Twitter Miami", - "username": "TwitterMiami" - }, - { - "pinned_tweet_id": "1438533888498876420", - "id": "1004367799588880384", - "created_at": "2018-06-06T14:21:58.000Z", - "name": "Twitter México", - "username": "TwitterMexico" - }, - { - "pinned_tweet_id": "1370178223846297602", - "id": "773578328498372608", - "created_at": "2016-09-07T17:47:00.000Z", - "name": "Twitter Together", - "username": "TwitterTogether" - }, - { - "id": "766296039036948480", - "created_at": "2016-08-18T15:29:47.000Z", - "name": "Moments MENA", - "username": "momentsmena" - }, - { - "id": "738118487122419712", - "created_at": "2016-06-01T21:22:15.000Z", - "name": "Twitter Asians", - "username": "TwitterAsians" - } - ], - "includes": { - "tweets": [ - { - "created_at": "2021-01-25T19:40:36.000Z", - "id": "1353789891348475905", - "text": "Want to help build a new community-driven approach to tackling misleading information? Join us — sign up for Birdwatch! \n\nhttps://t.co/FSsqNznPy1" - }, - { - "created_at": "2021-10-21T17:29:07.000Z", - "id": "1451239134798942208", - "text": "the time has arrived -- we’re now rolling out the ability for everyone on iOS and Android to host a Space\n\nif this is your first time hosting, welcome! here’s a refresher on how https://t.co/cLH8z0bocy" - }, - { - "created_at": "2021-09-16T16:03:00.000Z", - "id": "1438533888498876420", - "text": "Algunos le dicen amor, pero yo le digo:\n\n‌ ∧_∧  \n(。・ω・。)つ━☆・*\n⊂  ノ    ・゜+.\n しーJ   °。+ *´¨)\n         ☆ RECALENTADO ☆ \n#VivaMéxico" - }, - { - "created_at": "2021-03-12T01:01:59.000Z", - "id": "1370178223846297602", - "text": "Still, We Fly\n\n... because no matter how many times 2020 tried to knock us down, we got back up and responded with empathy, agility, innovation, and leadership.\n\nRead more in our 2020 Inclusion & Diversity Annual Report #UntilWeAllBelong" - } - ] - }, - "meta": { - "result_count": 10, - "next_token": "5349804505549807616" - } + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get List members with expansions +for page in client.lists.get_members( + "84839422", + user_fields=["created_at"], + expansions=["pinned_tweet_id"], + tweet_fields=["created_at"] +): + for user in page.data: + print(f"{user.username}") + # Pinned Posts are in page.includes.tweets +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get List members with expansions +const paginator = client.lists.getMembers("84839422", { + userFields: ["created_at"], + expansions: ["pinned_tweet_id"], + tweetFields: ["created_at"], +}); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(user.username); + }); + // Pinned Posts are in page.includes?.tweets } -``` \ No newline at end of file +``` + + + +--- + +## Next steps + + + + Add and remove members + + + Get List details + + + Full endpoint documentation + + diff --git a/x-api/lists/list-members/quickstart/manage-list-members.mdx b/x-api/lists/list-members/quickstart/manage-list-members.mdx index 2a1fd72cd..1f82923e8 100644 --- a/x-api/lists/list-members/quickstart/manage-list-members.mdx +++ b/x-api/lists/list-members/quickstart/manage-list-members.mdx @@ -1,59 +1,159 @@ --- -title: Manage list members -sidebarTitle: Manage list members +title: Manage List Members +sidebarTitle: Manage List Members +description: Add and remove members from a List keywords: ["manage list members quickstart", "add list members quickstart", "remove list members quickstart", "list members management"] --- import { Button } from '/snippets/button.mdx'; -### Manage List members quick start guide +This guide walks you through adding and removing members from a List. -**Please note:** This guide assumes you have completed the prerequisites from the [quick start overview](/x-api/lists/list-members#getting-started-with-the-list-members-endpoint-group). +**Prerequisites** + +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) +- A List that you own -### Steps to build a manage List member request -#### Step one: Choose the List endpoint collection from Postman +--- + +## Add a member to a List + + + + You need the ID of your List and the user ID of the person you want to add. Find user IDs using the [user lookup endpoint](/x-api/users/lookup/introduction). + + + -Once you have the X API v2 collection loaded in Postman, navigate to the “List” folder, select another folder “List members”, and then choose "Add a member". -  + -#### Step two: Specify the user to add +```bash cURL +curl -X POST "https://api.x.com/2/lists/1441162269824405510/members" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"user_id": "2244994945"}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -Manage List member endpoints require two IDs: the ID of the List and the ID of the user to add. You can find the user’s ID using the [users lookup](/x-api/users/lookup/introduction) and pass a username to receive the id field. +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -The target ID can be any valid user ID. In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the id path variable. Navigate to the “Body” tab and ID of the user you wish to add as the value for the user_id parameter. Be sure not to include any spaces before or after any ID. +client = Client(auth=oauth1) -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Parameter type** | -| `id` | The List ID | path | -| user_id | The target user ID you wish to add as a member | body | +# Add a member to a List +response = client.lists.add_member( + list_id="1441162269824405510", + user_id="2244994945" +) -You should now see a similar URL next to the "Send" button: +print(f"Is member: {response.data.is_member}") +``` - `https://api.x.com/2/lists/1441162269824405510/members` +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -Step three: Make your request and review your response +const client = new Client({ oauth1 }); -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +// Add a member to a List +const response = await client.lists.addMember("1441162269824405510", { + userId: "2244994945", +}); +console.log(`Is member: ${response.data?.is_member}`); ``` -{ - "data": { - "is_member": true - } -} + + + + + + + ```json + { + "data": { + "is_member": true + } + } + ``` + + + +--- + +## Remove a member from a List + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/lists/1441162269824405510/members/2244994945" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Remove a member from a List +response = client.lists.remove_member( + list_id="1441162269824405510", + user_id="2244994945" +) + +print(f"Is member: {response.data.is_member}") ``` -If the returned response object contains a true value for the key is_member, you have successfully added the user as a member of the List.  +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -To remove a member from a List, select the “Remove a member” request also found in the “Lists” folder of the X API v2 collection loaded in Postman. This endpoint requires the ID of the List and the user ID of the member you wish to remove. In the “Params” tab, enter the ID of the List as the value for the id column and ID of the user to be removed as the value for the  user_id column.  +const client = new Client({ oauth1 }); -On successful delete request, you will receive a response similar to the following example: +// Remove a member from a List +const response = await client.lists.removeMember( + "1441162269824405510", + "2244994945" +); +console.log(`Is member: ${response.data?.is_member}`); ``` + + + +**Response:** + +```json { "data": { "is_member": false @@ -61,3 +161,28 @@ On successful delete request, you will receive a response similar to the followi } ``` +--- + +## Important notes + + +- You can only manage members of Lists you own +- Adding a user to a List does not require their permission +- Users can see which public Lists they've been added to + + +--- + +## Next steps + + + + Get List members + + + Create and update Lists + + + Full endpoint documentation + + diff --git a/x-api/lists/list-members/quickstart/overview.mdx b/x-api/lists/list-members/quickstart/overview.mdx index 49f854a58..a20a1db17 100644 --- a/x-api/lists/list-members/quickstart/overview.mdx +++ b/x-api/lists/list-members/quickstart/overview.mdx @@ -1,46 +1,94 @@ --- -title: Overview +title: List Members Overview sidebarTitle: Overview +description: Get started with List members endpoints keywords: ["list members quickstart", "list members overview", "list members tutorial", "list membership quickstart"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the List members endpoint group - -This quick overview will help you make your first request to List members endpoints using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +The List members endpoints let you look up members of a List and manage List membership. **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token (for lookups) +- User Access Token (for managing members) -#### Load the X API v2 Postman Collection -To load the X API v2 Postman collection into your workspace, please click on the following button: +--- + +## Available endpoints + + + + Get members of a List + + + Add and remove members + + - +--- + +## Authentication + +| Operation | Authentication | +|:----------|:---------------| +| Look up members | Bearer Token, OAuth 1.0a, or OAuth 2.0 PKCE | +| Add/remove members | OAuth 1.0a or OAuth 2.0 PKCE | + +--- -#### Authenticate your request +## Quick example -To make a successful request to **lookup** endpoints, you can use either [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2), [App only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). However, with **manage** endpoints, you can only authenticate with OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE. + -Regardless, when using Postman, the default authentication keys and tokens will automatically populate in your requests as long as you've set up your environment properly.  +```bash cURL +# Get List members +curl "https://api.x.com/2/lists/84839422/members" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get List members +for page in client.lists.get_members("84839422"): + for user in page.data: + print(f"{user.username}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get List members +const paginator = client.lists.getMembers("84839422"); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(user.username); + }); +} +``` + + + +--- -You can do this by selecting the environment named “X API v2” (in the top-right corner of Postman), and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). These keys include the following: +## Next steps -* API Key (also known as Consumer Key) -* API Secret Key (also known as Consumer Secret) -* OAuth 1.0a user Access Token -* OAuth 1.0a user Access Token Secret -* App only Access Token -* OAuth 2.0 Client Key (only available if you've set up OAuth 2.0 User Authentication settings in your App's settings) -* OAuth 2.0 Client Secret (only available if you've set up OAuth 2.0 User Authentication settings in your App's settings) + + + Get List details + + + Get Posts from a List + + diff --git a/x-api/lists/list-tweets/integrate.mdx b/x-api/lists/list-tweets/integrate.mdx index a6ba49c60..1b6229b70 100644 --- a/x-api/lists/list-tweets/integrate.mdx +++ b/x-api/lists/list-tweets/integrate.mdx @@ -6,84 +6,76 @@ keywords: ["list tweets integration", "list timeline integration", "list tweets import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and critical concepts that you should know as you integrate the List Posts lookup endpoint into your system. We’ve broken the page into a couple of different sections: +This page covers tools and key concepts for integrating the List Posts lookup endpoint. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) -* [Pagination](#pagination) +--- -### Helpful tools +## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: -#### Postman +### Postman + +Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +### Code samples -#### Code samples +Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). -Are you interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +### Third-party libraries -#### Third-party libraries +Take advantage of one of our communities' [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +--- -### Key concepts +## Key concepts -#### Authentication +### Authentication -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, App only, or OAuth 2.0 Authorization Code with PKCE to authenticate your requests to this endpoint.  +All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, App only, or OAuth 2.0 Authorization Code with PKCE to authenticate your requests to this endpoint. [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use either OAuth 2.0 or App only to authenticate your requests. +Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use either OAuth 2.0 or App only to authenticate your requests. -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application's scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +To enable OAuth 2.0 in your App, you must enable it in your's App's authentication settings found in the App settings section of the Developer Console. [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) just requires that you pass an [App only Access Token](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) with your request. You can either generate an App only Access Token directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. -#### Developer portal, Projects, and developer Apps +### Developer Console, Projects, and developer Apps -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -  +To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -#### Rate limits +### Rate limits -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](https://developer.x.com/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user.  +Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](https://developer.x.com/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user. This endpoint is rate limited at both the App-level and the user-level. The app rate limit means that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by using either the API Key and API Secret Key, or the Bearer Token). The user rate limit means that the authenticated user that you are making the request on behalf of can only perform a List Post lookup a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. -| | | | +| Endpoint | HTTP method | Rate limit | | :--- | :--- | :--- | -| **Endpoint** | **HTTP method** | **Rate limit** | | /2/lists/:id/tweets | GET | 900 requests per 15 minutes | -Fields and expansions +### Fields and expansions The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called `fields` and `expansions`. The `expansions` parameter allows you to expand objects referenced in the payload. For example, looking up List Posts allows you to pull the following [expansions](/x-api/fundamentals/expansions): * `author_id` +The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. This endpoint delivers Post objects primarily. By default, the Post object returns the `id`, and `text` fields. To receive additional fields such as `tweet.created_at` or `tweet.lang`, you will have to specifically request those using a fields parameter. -The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. This endpoint delivers Post objects primarily. By default, the Post object returns the `id`, and `text` fields. To receive additional fields such as `tweet.created_at` or `tweet.lang`, you will have to specifically request those using a fields parameter.  - -We’ve added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). +We've added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). - The chart below shows the field and expansions available for the lookup endpoint: +The chart below shows the field and expansions available for the lookup endpoint: -| | | | +| Endpoint | Fields | Expansions | | :--- | :--- | :--- | -| **Endpoint** | **Fields** | **Expansions** | -| /2/lists/:id/tweets | `tweet.fields`

`user.fields` | `author_id` | +| /2/lists/:id/tweets | `tweet.fields`, `user.fields` | `author_id` | -Pagination +### Pagination Looking up List Posts can return a lot of data. To ensure we are returning consistent, high-performing results at any given time, we use pagination. Pagination is a feature in X API v2 endpoints that return more results than can be returned in a single response. When that happens, the data is returned in a series of 'pages'. Learn more about how to [paginate through results.](/x-api/fundamentals/pagination) diff --git a/x-api/lists/list-tweets/introduction.mdx b/x-api/lists/list-tweets/introduction.mdx index c41d1c0a7..6a7bf7516 100644 --- a/x-api/lists/list-tweets/introduction.mdx +++ b/x-api/lists/list-tweets/introduction.mdx @@ -1,36 +1,69 @@ --- -title: Introduction +title: List Posts sidebarTitle: Introduction -keywords: ["list tweets", "list timeline", "get list tweets", "list posts", "list timeline API", "tweets in list"] +description: Retrieve Posts from a List's timeline +keywords: ["list tweets", "list posts", "list timeline", "list feed", "list posts API"] --- import { Button } from '/snippets/button.mdx'; -List Posts lookup has one available endpoint to retrieve Posts from a specified List. With this endpoint, you can build solutions that enable users to customize, organize and prioritize the Posts they see in their timeline. +The List Posts endpoint lets you retrieve Posts from a List's timeline. Get the latest Posts from all members of a List. -There is a rate limit of 900 requests per 15 minutes for this endpoint and the response will support querying the latest 800 Posts for a given List. +## Overview -You can use [OAuth 1.0a User Context](/resources/fundamentals/authentication), [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) to authenticate your requests to this endpoint.  + + + Get Posts from List members + + + Access your curated content feeds + + -**Account setup** +--- + +## Endpoint + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/lists/:id/tweets`](/x-api/lists/get-list-posts) | Get Posts from a List | + +--- + +## Example request + +```bash +curl "https://api.x.com/2/lists/1234567890/tweets?\ +tweet.fields=created_at,author_id,public_metrics&\ +expansions=author_id&\ +user.fields=username&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +--- + +## Getting started -To access these endpoints, you will need: + +**Prerequisites** -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). -
- - - - -
\ No newline at end of file + + + Get Posts from a List + + + Key concepts and best practices + + + Get List details + + + Full endpoint documentation + + diff --git a/x-api/lists/list-tweets/migrate/overview.mdx b/x-api/lists/list-tweets/migrate/overview.mdx index 8f5017638..f7ecdd936 100644 --- a/x-api/lists/list-tweets/migrate/overview.mdx +++ b/x-api/lists/list-tweets/migrate/overview.mdx @@ -23,7 +23,7 @@ The following tables compare the standard v1.1 and X API v2 List endpoints: | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context

App only | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE

App only | | Default request [rate limits](/resources/fundamentals/rate-limits) | 900 requests per 15 min with OAuth 1.0a

900 requests per 15min with App only | 900 requests per 15 min with OAuth 1.0a

900 requests per 15 min with OAuth 2.0

900 requests per 15 min with App only | -To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started](/x-api/getting-started/getting-access) page.
diff --git a/x-api/lists/list-tweets/migrate/standard-to-twitter-api-v2.mdx b/x-api/lists/list-tweets/migrate/standard-to-twitter-api-v2.mdx index 56d7738f4..ed4f31da2 100644 --- a/x-api/lists/list-tweets/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/lists/list-tweets/migrate/standard-to-twitter-api-v2.mdx @@ -8,7 +8,7 @@ import { Button } from '/snippets/button.mdx'; ### List Posts lookup: Standard v1.1 compared to X API v2 -If you have been working with the standard v1.1 [GET lists/statuses](https://developer.x.com/en/docs/twitter-api/v1/accounts-and-users/create-manage-lists/api-reference/get-lists-statuses) endpoint, the goal of this guide is to help you understand the similarities and differences between the standard v1.1 and X API v2 endpoints. +If you have been working with the standard v1.1 [GET lists/statuses](https://developer.x.com/en/docs/twitter-api/v1/accounts-and-users/create-manage-lists/api-reference/get-lists-statuses) endpoint, the goal of this guide is to help you understand the similarities and differences between the standard v1.1 and X API v2 endpoints. * **Similarities** * Authentication methods @@ -33,7 +33,7 @@ Depending on your authentication library/package of choice, App only authenticat | | | | :--- | :--- | | **Standard v1.1** | **X API v2** | -| /1.1/lists/statuses.json

900 requests per 15-minute window with OAuth 1.0a User Context

900 requests per 15-minute window with App only | /2/lists/:id/tweets

900 requests per 15-minute window with OAuth 1.0a User Context

900 requests per 15-minute window with OAuth 2.0 Authorization Code with PKCE

900 requests per 15-minute window with App only | +| /1.1/lists/statuses.json

900 requests per 15-minute window with OAuth 1.0a User Context

900 requests per 15-minute window with App only | /2/lists/:id/tweets

900 requests per 15-minute window with OAuth 1.0a User Context

900 requests per 15-minute window with OAuth 2.0 Authorization Code with PKCE

900 requests per 15-minute window with App only | #### Differences @@ -48,11 +48,11 @@ Depending on your authentication library/package of choice, App only authenticat **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Data objects per request limits** -The standard v1.1 /lists/statuses endpoint allows you to return up to 5000 Posts per request. The new v2 endpoints allow you to return up to 100 Posts per request. By default, 100 user objects will be returned, to change the number of results you will need to pass a query parameter max_results= with a number between 1-100; you can then pass the next_token returned in the response payload to the pagination_token query parameter in your next request. +The standard v1.1 /lists/statuses endpoint allows you to return up to 5000 Posts per request. The new v2 endpoints allow you to return up to 100 Posts per request. By default, 100 user objects will be returned, to change the number of results you will need to pass a query parameter max_results= with a number between 1-100; you can then pass the next_token returned in the response payload to the pagination_token query parameter in your next request. **Response data format** @@ -60,20 +60,15 @@ One of the biggest differences between standard v1.1 and X API v2 endpoint versi For the standard endpoints, you receive many of the response fields by default and then have the option to use parameters to identify which additional fields or sets of fields should return in the payload. -The X API v2 version only delivers the Post id and text fields by default. To request any additional fields or objects, you will need to use the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. Any Post fields that you request from this endpoint will return in the primary Post object. Any expanded object fields will return an includes object within your response. You can then match any expanded objects back to the primary Post object by matching the IDs from the primary object and in expanded objects.  +The X API v2 version only delivers the Post id and text fields by default. To request any additional fields or objects, you will need to use the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. Any Post fields that you request from this endpoint will return in the primary Post object. Any expanded object fields will return an includes object within your response. You can then match any expanded objects back to the primary Post object by matching the IDs from the primary object and in expanded objects. Here are examples of possible Post fields and expansions: * attachments - * author_id - * context_annotations - * created_at - * geo - * lang @@ -82,21 +77,20 @@ Here are examples of possible Post fields and expansions: | **Endpoint** | **Expansion** | | /2/lists/:id/tweets | author_id | -We encourage you to read more about these new parameters in their respective guides, or by reading our guide on [how to use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions).  +We encourage you to read more about these new parameters in their respective guides, or by reading our guide on [how to use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions). -We have also put together a [data format migration guide](/x-api/migrate/data-format-migration) that can help you map standard v1.1 fields to the newer v2 fields. This guide will also provide you the specific expansion and field parameter that you will need to pass with your v2 request to return specific fields.  +We have also put together a [data format migration guide](/x-api/migrate/data-format-migration) that can help you map standard v1.1 fields to the newer v2 fields. This guide will also provide you the specific expansion and field parameter that you will need to pass with your v2 request to return specific fields. In addition to the changes in how you request certain fields, X API v2 is also introducing new JSON designs for the objects returned by the APIs, including [Post](/x-api/fundamentals/data-dictionary#tweet) and [user](/x-api/fundamentals/data-dictionary#user) objects. -* At the JSON root level, the standard endpoints return Post objects in a **statuses** array, while X API v2 returns a **data** array.  +* At the JSON root level, the standard endpoints return Post objects in a **statuses** array, while X API v2 returns a **data** array. -* Instead of referring to Retweeted and Quoted "statuses", X API v2 JSON refers to Retweeted and Quoted Tweets. Many legacy and deprecated fields, such as **contributors** and **user.translator_type** are being removed.  +* Instead of referring to Retweeted and Quoted "statuses", X API v2 JSON refers to Retweeted and Quoted Tweets. Many legacy and deprecated fields, such as **contributors** and **user.translator_type** are being removed. * Instead of using both **favorites** (in Post object) and **favourites** (in user object), X API v2 uses the term **like**. * X is adopting the convention that JSON values with no value (for example, **null**) are not written to the payload. Post and user attributes are only included if they have non-null values. -   - + **Request parameters** @@ -111,6 +105,70 @@ The following standard v1.1 request parameters have equivalents in X API v2: | owner_id | Requested with expansions parameter with value of author_id | | since_id | No equivalent | | max_id | No equivalent | -| include_entities | Requested with tweet.fields parameter with value of entities | +| include_entities | Requested with tweet.fields parameter with value of entities | | include_rts | No equivalent | | count | max_results | + +--- + +## Code examples + +### Get Posts from a List (v2) + + + +```bash cURL +curl "https://api.x.com/2/lists/84839422/tweets?tweet.fields=created_at,public_metrics&max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/lists/84839422/tweets" + +params = { + "tweet.fields": "created_at,public_metrics", + "max_results": 100 +} +headers = {"Authorization": f"Bearer {bearer_token}"} + +response = requests.get(url, headers=headers, params=params) +print(response.json()) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get Posts from a List +for page in client.lists.get_tweets( + "84839422", + tweet_fields=["created_at", "public_metrics"], + max_results=100 +): + for post in page.data: + print(f"{post.created_at}: {post.text[:50]}...") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get Posts from a List +const paginator = client.lists.getTweets("84839422", { + tweetFields: ["created_at", "public_metrics"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); + }); +} +``` + + diff --git a/x-api/lists/list-tweets/quickstart.mdx b/x-api/lists/list-tweets/quickstart.mdx index c45988732..878e0d2f7 100644 --- a/x-api/lists/list-tweets/quickstart.mdx +++ b/x-api/lists/list-tweets/quickstart.mdx @@ -1,122 +1,154 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["list tweets quickstart", "list timeline quickstart", "get list tweets quickstart", "list posts quickstart"] +description: Get Posts from a List timeline +keywords: ["list tweets quickstart", "list timeline quickstart", "get list posts tutorial"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the List Posts lookup endpoint +This guide walks you through retrieving Posts from a List timeline. -This quick start guide will help you make your first request to the List Posts lookup endpoint using Postman. - -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to see sample code in different languages. -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token -### Steps to build a List Posts lookup request - -#### Step one: Start with a tool or library - -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. - -To load the X API v2 Postman collection into your environment, please click on the following button: - - -Once you have the X API v2 collection loaded in Postman, navigate to the “List” folder, select another folder “List Posts”, and then choose "List Posts lookup". -  - -#### Step two: Authenticate your request - -To properly make a request to the X API, you need to verify that you have permission. To do this with this endpoint, you must authenticate your request with either [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods. - -For simplicity's sake, we are going to utilize App only with this request, but if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Posts, you will need to use one of the other authentication methods.  - -To utilize App only, you must add your keys and tokens (specifically the[App only Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token), also known as the App only Bearer Token) to Postman by selecting the environment named “X API v2” (in the top-right corner of Postman), and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). - -If you've done this correctly, these variables will automatically be pulled into the request's authorization tab. -  - -#### Step three: Identify and specify which List you would like to retrieve Posts from - -You must specify a List that you would like to receive within the request. You can find the List ID by navigating to x.com and clicking on a List and then looking in the URL. For example, the following URL's List ID is 84839422. - -https://x.com/i/lists/84839422 - -The target ID can be any valid List ID. In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the id path variable. Be sure not to include any spaces before or after any ID. - -| | | -| :--- | :--- | -| **Key** | **Value** | -| id | 84839422 (The List ID) | +--- -#### Step four: Identify and specify which fields you would like to retrieve + + + You can find a List ID in the URL when viewing a List on x.com: -If you click the "Send" button after step three, you will receive the default [Post object](/x-api/fundamentals/data-dictionary#tweet) fields in your response: idand text. + ``` + https://x.com/i/lists/84839422 + └── This is the List ID + ``` + -If you would like to receive additional fields, you will have to specify those fields in your request with tweet.fields and/or [expansion](/x-api/fundamentals/expansions) parameters. + -For this exercise, we will request three additional sets of fields from different objects: + -* The additional created_at field in the primary Lists object. +```bash cURL +curl "https://api.x.com/2/lists/84839422/tweets?\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id&\ +user.fields=username,verified&\ +max_results=10" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -* The full [user object](/x-api/fundamentals/data-dictionary#user) using the expansion parameter +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get Posts from a List with pagination +for page in client.lists.get_tweets( + "84839422", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id"], + user_fields=["username", "verified"], + max_results=10 +): + for post in page.data: + print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") +``` -* The additional user.created_at field in the associated user object. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +// Get Posts from a List with pagination +const paginator = client.lists.getTweets("84839422", { + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id"], + userFields: ["username", "verified"], + maxResults: 10, +}); -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| tweet.fields | created_at | created_at | -| expansions | author_id | includes.users.id,
includes.users.name,
includes.users.username | -| user.fields | created_at | includes.users.created_at | +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); + }); +} +``` -You should now see a similar URL next to the “Send” button: +
- `https://api.x.com/2/lists/84839422/tweets?expansions=author_id&user.fields=created_at&max_results=1` +
+ + ```json + { + "data": [ + { + "id": "1458172421115101189", + "text": "Check out our latest announcement...", + "author_id": "4172587277", + "created_at": "2024-01-15T10:30:00.000Z", + "public_metrics": { + "retweet_count": 42, + "reply_count": 5, + "like_count": 156, + "quote_count": 3 + }, + "edit_history_tweet_ids": ["1458172421115101189"] + } + ], + "includes": { + "users": [ + { + "id": "4172587277", + "username": "TechNews", + "verified": true + } + ] + }, + "meta": { + "result_count": 1, + "next_token": "7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" + } + } + ``` + -#### Step five: Make your request and review your response + + The SDKs handle pagination automatically. For cURL, use the `next_token` from the response to get more Posts: -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: + ```bash + curl "https://api.x.com/2/lists/84839422/tweets?\ + max_results=10&\ + pagination_token=7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + +
-``` -{ - "data": [ - { - "author_id": "4172587277", - "id": "1458172421115101189", - "text": "A Alemanha registrou nesta semana um recorde de novos casos de Covid-19. Segundo o governo e especialistas em Saúde, pessoas não vacinadas são responsáveis pela situação \nhttps://t.co/4POyaPwMLu" - } - ], - "includes": { - "users": [ - { - "username": "MomentsBrasil", - "name": "Twitter Moments Brasil", - "created_at": "2015-11-12T16:46:02.000Z", - "id": "4172587277" - } - ] - }, - "meta": { - "result_count": 1, - "next_token": "7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" - } -} -``` + +This endpoint returns up to 800 of the most recent Posts from the List. + +--- -**Please note:** The response of this endpoint will support querying the latest 800 Posts for a given List +## Next steps + + + + Get List details + + + Get List members + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/lists/manage-lists/integrate.mdx b/x-api/lists/manage-lists/integrate.mdx index 43e058b55..db12d9da2 100644 --- a/x-api/lists/manage-lists/integrate.mdx +++ b/x-api/lists/manage-lists/integrate.mdx @@ -6,51 +6,47 @@ keywords: ["manage lists integration", "list management integration", "lists int import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and critical concepts that you should know as you integrate the Lists endpoints into your system. We’ve broken the page into a couple of different sections: +This page covers tools and key concepts for integrating the Lists endpoints. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) +--- -### Helpful tools +## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: #### Postman -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. #### Code samples -Are you interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). #### Third-party libraries -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +Take advantage of one of our communities' [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. ### Key concepts #### Authentication -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens.  +All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. These specific endpoints requires the use of [OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize or authenticate your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). Please note that OAuth 1.0a can be tricky to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview) or a tool like Postman to properly authenticate your requests. -  + -#### Developer portal, Projects, and developer Apps +#### Developer Console, Projects, and developer Apps -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -  +To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. + #### Rate limits -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user.  +Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. -These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App.  +These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. @@ -58,4 +54,115 @@ The chart below shows the rate limits for each endpoint. | :--- | :--- | :--- | | **Endpoint** | **HTTP method** | **Rate limit** | | /2/lists | POST | 300 requests per 15 minutes | -| /2/lists/:id | DELETE / PUT | 300 requests per 15 minutes | \ No newline at end of file +| /2/lists/:id | DELETE / PUT | 300 requests per 15 minutes | + +--- + +### Code examples + +#### Create a List + + + +```bash cURL +curl -X POST "https://api.x.com/2/lists" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"name": "My List", "description": "A list of interesting accounts"}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Create a new List +response = client.lists.create( + name="My List", + description="A list of interesting accounts" +) +print(response.data) +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Create a new List +const response = await client.lists.create({ + name: "My List", + description: "A list of interesting accounts", +}); +console.log(response.data); +``` + + + +#### Update a List + + + +```bash cURL +curl -X PUT "https://api.x.com/2/lists/123456789" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"name": "Updated List Name"}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Update a List +response = client.lists.update( + list_id="123456789", + name="Updated List Name" +) +print(response.data) +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Update a List +const response = await client.lists.update("123456789", { + name: "Updated List Name", +}); +console.log(response.data); +``` + + diff --git a/x-api/lists/manage-lists/introduction.mdx b/x-api/lists/manage-lists/introduction.mdx index 5859a5da9..c6a5c441a 100644 --- a/x-api/lists/manage-lists/introduction.mdx +++ b/x-api/lists/manage-lists/introduction.mdx @@ -1,42 +1,87 @@ --- -title: Introduction +title: Manage Lists sidebarTitle: Introduction -keywords: ["manage lists", "list management", "create lists", "update lists", "delete lists", "list API", "lists management"] +description: Create, update, and delete Lists +keywords: ["manage lists", "create list", "delete list", "update list", "list API"] --- import { Button } from '/snippets/button.mdx'; +The Manage Lists endpoints let you create, update, and delete Lists on behalf of authenticated users. -[X Lists](https://help.x.com/en/using-twitter/twitter-lists) allows users to customize, organize and prioritize the Posts they see in their timeline. With the Lists endpoints, you can build solutions that enable people to curate and organize Posts based on preferences, interests, groups, or topics. +## Overview -Since you are making requests on behalf of a user with the manage List endpoints, you must authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and use the user Access Tokens associated with a user that has authorized your App, which can be generated using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (OAuth 1.0a) or the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)) (OAuth 2.0). -  + + + Create a new List + + + Update List name and description + + + Delete a List + + -### Manage Lists +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | [`/2/lists`](/x-api/lists/create-list) | Create a new List | +| PUT | [`/2/lists/:id`](/x-api/lists/update-list) | Update a List | +| DELETE | [`/2/lists/:id`](/x-api/lists/delete-list) | Delete a List | + +--- -The manage List endpoints allow you to create, delete, and update Lists on behalf of an authenticated user. For these endpoints, there are three methods available: POST, DELETE and PUT. The POST method allows you to create a List, the DELETE method allows you to delete a List, and the PUT method allows you to update the metadata of a List. There is a user rate limit of 300 requests per 15 minutes for all three endpoints. +## Example: Create a List -Note that you can create up to 1000 Lists per account. +```bash +curl -X POST "https://api.x.com/2/lists" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Tech News", + "description": "My favorite tech journalists", + "private": false + }' +``` + +## Example response + +```json +{ + "data": { + "id": "1234567890", + "name": "Tech News" + } +} +``` + +--- -**Account setup** +## Getting started -To access these endpoints, you will need: + +**Prerequisites** -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) + -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). -
- - - - -
+ + + Create your first List + + + Key concepts and best practices + + + Add and remove members + + + Full endpoint documentation + + diff --git a/x-api/lists/manage-lists/migrate/overview.mdx b/x-api/lists/manage-lists/migrate/overview.mdx index f6a02fce3..00dd5bdcf 100644 --- a/x-api/lists/manage-lists/migrate/overview.mdx +++ b/x-api/lists/manage-lists/migrate/overview.mdx @@ -42,7 +42,7 @@ The following tables compare the standard v1.1 and X API v2 List endpoints: | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context | | Default request [rate limits](/resources/fundamentals/rate-limits) | None | 300 requests per 15 min (per user) | -To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +To access the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started](/x-api/getting-started/getting-access) page.
diff --git a/x-api/lists/manage-lists/migrate/standard-to-twitter-api-v2.mdx b/x-api/lists/manage-lists/migrate/standard-to-twitter-api-v2.mdx index 3a149f3f1..68b460f36 100644 --- a/x-api/lists/manage-lists/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/lists/manage-lists/migrate/standard-to-twitter-api-v2.mdx @@ -58,7 +58,7 @@ Both endpoint versions support [OAuth 1.0a User Context](https://developer.x.com **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps related to a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps related to a project. **Request parameters** @@ -82,5 +82,138 @@ The following standard v1.1 request parameters have equivalents in X API v2: | slug | No equivalent | -**Please note:** Standard v1.1 parameters are passed as query parameters, whereas the X API v2 parameters are passed as body parameters (for the POST endpoint) or path parameters (for the DELETE and PUT endpoints). +**Please note:** Standard v1.1 parameters are passed as query parameters, whereas the X API v2 parameters are passed as body parameters (for the POST endpoint) or path parameters (for the DELETE and PUT endpoints). + +--- + +## Code examples + +### Create a List (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/lists" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"name": "My List", "description": "A great list"}' +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/lists" +data = {"name": "My List", "description": "A great list"} + +response = requests.post(url, auth=auth, json=data) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Create a List +response = client.lists.create(name="My List", description="A great list") +print(f"Created List: {response.data.id}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Create a List +const response = await client.lists.create({ + name: "My List", + description: "A great list", +}); +console.log(`Created List: ${response.data?.id}`); +``` + + + +### Delete a List (v2) + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/lists/123456789" \ + -H "Authorization: OAuth ..." +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/lists/123456789" +response = requests.delete(url, auth=auth) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Delete a List +response = client.lists.delete("123456789") +print(f"Deleted: {response.data.deleted}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Delete a List +const response = await client.lists.delete("123456789"); +console.log(`Deleted: ${response.data?.deleted}`); +``` + + diff --git a/x-api/lists/manage-lists/quickstart.mdx b/x-api/lists/manage-lists/quickstart.mdx index fc256b841..df34a9df3 100644 --- a/x-api/lists/manage-lists/quickstart.mdx +++ b/x-api/lists/manage-lists/quickstart.mdx @@ -1,91 +1,278 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["manage lists quickstart", "list management quickstart", "create list quickstart", "list quickstart tutorial"] +description: Create, update, and delete Lists +keywords: ["manage lists quickstart", "create list tutorial", "list management guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the manage Lists endpoint group +This guide walks you through creating, updating, and deleting Lists. -This quick overview will help you make your first request to the manage List endpoints using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  - -**Note: **For this example, we will make a request to the _Create a List_ endpoint, but you can apply the learnings from this quick start to other manage requests as well. **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build a manage List request -**Step one: Start with a tool or library** +--- + +## Create a List + + + + Define the List name (required) and optional description and privacy settings: + + ```json + { + "name": "Tech News", + "description": "Top tech journalists and publications", + "private": false + } + ``` + + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. + -To load the X API v2 Postman collection into your environment, please click on the following button: +```bash cURL +curl -X POST "https://api.x.com/2/lists" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Tech News", + "description": "Top tech journalists and publications", + "private": false + }' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Create a new List +response = client.lists.create( + name="Tech News", + description="Top tech journalists and publications", + private=False +) + +print(f"List created: {response.data.id} - {response.data.name}") +``` - -Once you have the X API v2 collection loaded in Postman, navigate to the “List” folder, select another folder “Manage List”, and then choose "Create a List". +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -**Step two: Authenticate your request** +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -To properly make a request to the X API, you need to verify that you have permission to do so. To do this with the manage Posts endpoints, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +const client = new Client({ oauth1 }); -In this example, we are going to use OAuth 1.0a User Context. +// Create a new List +const response = await client.lists.create({ + name: "Tech News", + description: "Top tech journalists and publications", + private: false, +}); -You must add your keys and tokens (and specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret) to Postman. You can do this by selecting the environment named “X API v2” (in the top-right corner of Postman), and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +console.log(`List created: ${response.data?.id} - ${response.data?.name}`); +``` -If you've done this correctly, these variables will automatically be pulled into the request's authorization tab. -  -**Step three: Specify the name for the new List** + -When creating a new List with this endpoint, a name for the List is a required body parameter. Optionally, you can provide a description and specify whether the List is private. + -In Postman, navigate to the “Body” tab and enter the name of the List as the value for the name parameter. Additionally, if you wish to add a description for the List, simply add a new key labeled description in the same fashion as the name, followed by the description of the List as the value. Making a List private will follow the same pattern, but only true or false values are accepted for this parameter.  + + ```json + { + "data": { + "id": "1441162269824405510", + "name": "Tech News" + } + } + ``` -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Parameter type** | -| `name` | Name of the list (required) | body | -| description | Description for the list (optional) | body | -| private | true or false (optional) | body | + Save the `id` to update or delete the List later. + + -You should now see a similar URL next to the "Send" button: +--- - `https://api.x.com/2/lists` +## Update a List -Step four: Make your request and review your response +Modify a List's name, description, or privacy: -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: + +```bash cURL +curl -X PUT "https://api.x.com/2/lists/1441162269824405510" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Tech News & Insights", + "description": "Updated description" + }' ``` -{ - "data": { - "id": "1441162269824405510", - "name": "New list created from Postman" - } -} + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Update a List +response = client.lists.update( + "1441162269824405510", + name="Tech News & Insights", + description="Updated description" +) + +print(f"Updated: {response.data.updated}") ``` -If the returned response object contains an id and the name of your List, you have successfully created the List.  +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -To delete a List, select the “Delete a List” request also found in the “Lists” folder of the X API v2 collection loaded in Postman. This endpoint requires the ID of the List you wish to delete. In the “Params” tab, enter the ID of the List you wish to delete as the value for the id column.  +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -On successful delete request, you will receive a response similar to the following example:  +const client = new Client({ oauth1 }); +// Update a List +const response = await client.lists.update("1441162269824405510", { + name: "Tech News & Insights", + description: "Updated description", +}); + +console.log(`Updated: ${response.data?.updated}`); ``` + + + +**Response:** + +```json { "data": { - "deleted": true + "updated": true } } ``` + +--- + +## Delete a List + + + + You need the ID of the List you want to delete. + + + + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/lists/1441162269824405510" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Delete a List +response = client.lists.delete("1441162269824405510") +print(f"Deleted: {response.data.deleted}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Delete a List +const response = await client.lists.delete("1441162269824405510"); +console.log(`Deleted: ${response.data?.deleted}`); +``` + + + + + + + ```json + { + "data": { + "deleted": true + } + } + ``` + + + + +You can only delete Lists that you own. + + +--- + +## Next steps + + + + Add and remove List members + + + Retrieve List details + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/lists/pinned-lists/integrate.mdx b/x-api/lists/pinned-lists/integrate.mdx index 5b7308953..0efa68b00 100644 --- a/x-api/lists/pinned-lists/integrate.mdx +++ b/x-api/lists/pinned-lists/integrate.mdx @@ -6,75 +6,68 @@ keywords: ["pinned lists integration", "pin lists integration", "pinned lists gu import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and key concepts that you should be aware of as you integrate the mutes endpoints into your system. We’ve broken the page into a couple of different sections: +This page covers tools and key concepts for integrating the pinned Lists endpoints. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) +--- -### Helpful tools +## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: -#### Postman +### Postman + +Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +### Code samples -#### Code samples +Interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). -Interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +### Third-party libraries -#### Third-party libraries +Take advantage of one of our communities' [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +--- -### Key concepts +## Key concepts -#### Authentication +### Authentication -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use OAuth 1.0a User Context to authenticate your requests to this endpoint.  +All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use OAuth 1.0a User Context to authenticate your requests to this endpoint. [OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens). Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman. -#### Developer portal, Projects, and developer Apps +### Developer Console, Projects, and developer Apps -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -  +To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -#### Rate limits +### Rate limits -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user.  +Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user. -These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App.  +These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. -| | | | +| Endpoint | HTTP method | Rate limit | | :--- | :--- | :--- | -| **Endpoint** | **HTTP method** | **Rate limit** | | /2/users/:id/pinned_lists | POST | 50 requests per 15 minutes | -| /2/users/:id/pinned\_lists/:list\_id | DELETE | 50 requests per 15 minutes | +| /2/users/:id/pinned_lists/:list_id | DELETE | 50 requests per 15 minutes | | /2/users/:id/pinned_lists | GET | 15 requests per 15 minutes | -#### Fields and expansions +### Fields and expansions The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called `fields` and `expansions`. The `expansions` parameter allows you to expand objects referenced in the payload. For example, looking up pinned Lists allows you to pull the following [expansions](/x-api/fundamentals/expansions): * `owner_id` +The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. This endpoint delivers user objects primarily. By default, the List object returns the `id`, and `name` fields. To receive additional fields such as `list.created_at` or `list.description`, you will have to specifically request those using a fields parameter. -The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. This endpoint delivers user objects primarily. By default, the List object returns the `id`, and `name` fields. To receive additional fields such as `list.created_at` or `list.description`, you will have to specifically request those using a fields parameter.  - -We’ve added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). +We've added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). - The chart below shows the field and expansions available for the lookup endpoint: +The chart below shows the field and expansions available for the lookup endpoint: -| | | | +| Endpoint | Fields | Expansions | | :--- | :--- | :--- | -| **Endpoint** | **Fields** | **Expansions** | -| /2/users/:id/pinned_lists | `list.fields`

`user.fields` | `owner_id` | +| /2/users/:id/pinned_lists | `list.fields`, `user.fields` | `owner_id` | diff --git a/x-api/lists/pinned-lists/introduction.mdx b/x-api/lists/pinned-lists/introduction.mdx index 9322c963c..cd6dd1cb2 100644 --- a/x-api/lists/pinned-lists/introduction.mdx +++ b/x-api/lists/pinned-lists/introduction.mdx @@ -1,44 +1,80 @@ --- -title: Introduction +title: Pinned Lists sidebarTitle: Introduction -keywords: ["pinned lists", "pin lists", "list pinning", "pinned list lookup", "manage pinned lists", "list management"] +description: View and manage pinned Lists +keywords: ["pinned lists", "pin list", "unpin list", "pinned lists API"] --- import { Button } from '/snippets/button.mdx'; -Pinning lists allows X users to more easily access lists that they've either subscribed to or created themselves. +The Pinned Lists endpoints let you view, pin, and unpin Lists for the authenticated user. Pinned Lists appear prominently in the user's X interface. -Since you are making requests on behalf of a user with the these endpoints, you must authenticate them with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and use the user Access Tokens associated with a user that has authorized your App, which can be generated using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (OAuth 1.0a) or the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)) (OAuth 2.0). +## Overview -### Pinned List lookup + + + Get user's pinned Lists + + + Pin a List + + + Unpin a List + + -Pinned List lookup has one available endpoint that allows you to retrieve an authenticated user's pinned Lists. There is a rate limit of 15 requests per 15 minutes for this endpoint. -  +--- -### Manage pinned Lists +## Endpoints -The manage pinned List endpoints allow you to pin and unpin a List on behalf of an authenticated user. For these endpoints, there are two methods available: POST and DELETE. The POST method allows you to pin a List, and the DELETE method allows you to unpin a List. There is a user rate limit of 50 requests per 15 minutes for both endpoints. - -**Account setup** +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/:id/pinned_lists`](/x-api/users/get-pinned-lists) | Get pinned Lists | +| POST | [`/2/users/:id/pinned_lists`](/x-api/users/pin-list) | Pin a List | +| DELETE | [`/2/users/:id/pinned_lists/:list_id`](/x-api/users/unpin-list) | Unpin a List | + +--- + +## Example: Get pinned Lists + +```bash +curl "https://api.x.com/2/users/123456789/pinned_lists?\ +list.fields=name,description,member_count" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -To access these endpoints, you will need: +## Example: Pin a List -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info)). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +```bash +curl -X POST "https://api.x.com/2/users/123456789/pinned_lists" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"list_id": "9876543210"}' +``` -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -
- - - - -
\ No newline at end of file + + + + Get pinned Lists + + + Pin and unpin Lists + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/lists/pinned-lists/quickstart/manage-pinned-lists.mdx b/x-api/lists/pinned-lists/quickstart/manage-pinned-lists.mdx index eaadf7b73..aebe3c66c 100644 --- a/x-api/lists/pinned-lists/quickstart/manage-pinned-lists.mdx +++ b/x-api/lists/pinned-lists/quickstart/manage-pinned-lists.mdx @@ -1,63 +1,184 @@ --- -title: Manage pinned lists -sidebarTitle: Manage pinned lists +title: Manage Pinned Lists +sidebarTitle: Manage Pinned Lists +description: Pin and unpin Lists keywords: ["manage pinned lists quickstart", "pin lists quickstart", "unpin lists quickstart", "pinned lists management"] --- import { Button } from '/snippets/button.mdx'; +This guide walks you through pinning and unpinning Lists. + -**Please note:** This guide assumes you have completed the prerequisites from the [quick start overview](/x-api/lists/pinned-lists#getting-started-with-the-pinned-list-endpoint-group). +**Prerequisites** + +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build a manage pinned List request -**Step one: Choose the List endpoint collection from Postman** -Once you have the X API v2 collection loaded in Postman, navigate to the “List” folder, select another folder “Pinned Lists”, and then choose "Pin a List". -  +--- -**Step two: Specify the List to pin** +## Pin a List -Manage pinned List endpoints require two IDs: one for the user (the authenticated user to pin a List) and the target List (the List to be pinned). The user’s ID must correspond to the authenticating user’s ID, meaning that you must pass the Access Tokens associated with the user ID when authenticating your request. In this case, you can specify the ID belonging to your user. You can find your ID in two ways: + + + You need your authenticated user's ID and the ID of the List you want to pin. Find List IDs in the URL when viewing a List. + -1. Using the [users lookup](/x-api/users/lookup/introduction) by username endpoint, you can pass a username and receive the id field. -2. Looking at your Access Token, you will find that the numeric part is your user ID. + -The target List ID can be any valid list. In Postman, navigate to the "Params" tab, and enter the user ID into the "Value" column of the id path variable. Navigate to the “Body” tab and ID of the List you wish to pin as the value for the list_id  parameter. Be sure not to include any spaces before or after any ID. + -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Parameter type** | -| `id` | The user ID | path | -| list_id | The target List ID to be pinned | body | +```bash cURL +curl -X POST "https://api.x.com/2/users/2244994945/pinned_lists" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"list_id": "1454155907651158017"}' +``` -You should now see a similar URL next to the "Send" button: +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 - ``` - https://api.x.com/2/users/2244994945/pinned_lists - ``` +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -Step three: Make your request and review your response +client = Client(auth=oauth1) -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +# Pin a List +response = client.lists.pin( + user_id="2244994945", + list_id="1454155907651158017" +) +print(f"Pinned: {response.data.pinned}") ``` -{ - "data": { - "pinned": true - } -} + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Pin a List +const response = await client.lists.pin("2244994945", { + listId: "1454155907651158017", +}); + +console.log(`Pinned: ${response.data?.pinned}`); +``` + + + + + + + ```json + { + "data": { + "pinned": true + } + } + ``` + + + +--- + +## Unpin a List + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/2244994945/pinned_lists/1454155907651158017" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` -You have successfully pinned the target List if the returned response object contains a true value for the key pinned.  +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -To unpin a List, select the Unpin List” request also found in the “Lists” folder of the X API v2 collection loaded in Postman. This endpoint requires the authenticated user ID and the ID of the List to be unpinned. In the “Params” tab, enter the user ID as the value for the id column and ID of the List to be unpinned as the value for the  list_id column.  +client = Client(auth=oauth1) -On successful delete request, you will receive a response similar to the following example: +# Unpin a List +response = client.lists.unpin( + user_id="2244994945", + list_id="1454155907651158017" +) +print(f"Pinned: {response.data.pinned}") ``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Unpin a List +const response = await client.lists.unpin("2244994945", "1454155907651158017"); + +console.log(`Pinned: ${response.data?.pinned}`); +``` + + + +**Response:** + +```json { "data": { "pinned": false } } -``` \ No newline at end of file +``` + +--- + +## Important notes + + +- You can only pin Lists you follow or own +- Pinned Lists appear at the top of your Lists in the X app +- There's a limit on how many Lists you can pin + + +--- + +## Next steps + + + + Get your pinned Lists + + + Get List details + + + Full endpoint documentation + + diff --git a/x-api/lists/pinned-lists/quickstart/overview.mdx b/x-api/lists/pinned-lists/quickstart/overview.mdx index 9a3e47505..a45dc1190 100644 --- a/x-api/lists/pinned-lists/quickstart/overview.mdx +++ b/x-api/lists/pinned-lists/quickstart/overview.mdx @@ -1,49 +1,96 @@ --- -title: Overview +title: Pinned Lists Overview sidebarTitle: Overview +description: Get started with pinned List endpoints keywords: ["pinned lists overview", "pinned lists quickstart", "pin lists overview", "list pinning overview"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the pinned List endpoint group +The pinned Lists endpoints let you look up a user's pinned Lists and manage which Lists are pinned. -This quick overview will help you make your first request to the pinned List endpoints using [Postman](/tutorials/postman-getting-started). - -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to see sample code in different languages. -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -Load the X API v2 Postman collection +--- + +## Available endpoints + + + + Get your pinned Lists + + + Pin and unpin Lists + + -To load the X API v2 Postman collection into your workspace, please click on the following button: +--- + +## Authentication + +| Operation | Authentication | +|:----------|:---------------| +| Look up pinned Lists | OAuth 1.0a or OAuth 2.0 PKCE | +| Pin/unpin Lists | OAuth 1.0a or OAuth 2.0 PKCE | - + +Both lookup and manage operations require user context authentication. App-only (Bearer Token) authentication is not supported. + + +--- +## Quick example -  + -#### Authenticate your request +```bash cURL +# Get pinned Lists +curl "https://api.x.com/2/users/2244994945/pinned_lists" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -To make a successful request to **lookup** endpoints, you can use either [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). However, with **manage** endpoints, you can only authenticate with OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE. +```python Python SDK +from xdk import Client -Regardless, when using Postman, the default authentication keys and tokens will automatically populate in your requests as long as you've set up your environment properly.  +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get pinned Lists +response = client.lists.get_pinned("2244994945") + +for lst in response.data: + print(f"{lst.name}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Get pinned Lists +const response = await client.lists.getPinned("2244994945"); + +response.data?.forEach((lst) => { + console.log(lst.name); +}); +``` + + + +--- -You can do this by selecting the environment named “X API v2” (in the top-right corner of Postman), and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). These keys include the following: +## Next steps -* API Key (also known as Consumer Key) -* API Secret Key (also known as Consumer Secret) -* OAuth 1.0a user Access Token -* OAuth 1.0a user Access Token Secret -* OAuth 2.0 App Access Token -* OAuth 2.0 Client Key (only available if you've set up OAuth 2.0 User Authentication settings in your App's settings) -* OAuth 2.0 Client Secret (only available if you've set up OAuth 2.0 User Authentication settings in your App's settings) + + + Get List details + + + Create and update Lists + + diff --git a/x-api/lists/pinned-lists/quickstart/pinned-list-lookup.mdx b/x-api/lists/pinned-lists/quickstart/pinned-list-lookup.mdx index 8ff7080f2..75b67034a 100644 --- a/x-api/lists/pinned-lists/quickstart/pinned-list-lookup.mdx +++ b/x-api/lists/pinned-lists/quickstart/pinned-list-lookup.mdx @@ -1,88 +1,136 @@ --- -title: Pinned list lookup -sidebarTitle: Pinned list lookup +title: Pinned Lists Lookup +sidebarTitle: Pinned Lists Lookup +description: Get a user's pinned Lists keywords: ["pinned list lookup quickstart", "pinned list lookup", "pinned lists quickstart", "list lookup quickstart"] --- import { Button } from '/snippets/button.mdx'; +This guide walks you through retrieving a user's pinned Lists. + -**Please note:** This guide assumes you have completed the prerequisites from the [quick start overview](/x-api/lists/pinned-lists#getting-started-with-the-pinned-list-endpoint-group). +**Prerequisites** + +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build a pinned List lookup request -**Step one: Choose the List endpoint collection from Postman** +--- + +## Get pinned Lists -Once you have the X API v2 collection loaded in Postman, navigate to the “List” folder, select another folder “Pinned Lists”, and then choose "User's pinned Lists". -  + + + You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). + -**Step two: Identify and specify the user** + -In order to retrieve a user’s pinned List, you must specify their user ID within the request. The user ID must correspond to the authenticating user’s ID, meaning that you must pass the Access Tokens associated with the user ID when authenticating your request. In this example, you can specify the ID belonging to your own user. You can find your ID in two ways: + -1. Using the [users lookup](/x-api/users/lookup/introduction) by username endpoint, you can pass a username and receive the id field. -2. Looking at your Access Token, you will find that the numeric part is your user ID. +```bash cURL +curl "https://api.x.com/2/users/2244994945/pinned_lists?\ +list.fields=follower_count,member_count,owner_id&\ +expansions=owner_id&\ +user.fields=created_at,username" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -In Postman, navigate to the "Params" tab and enter this user ID into the "Value" column of the `id` parameter. +```python Python SDK +from xdk import Client -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | 2244994945 (user ID) | +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -**Step three: Identify and specify which fields you would like to retrieve** +# Get pinned Lists +response = client.lists.get_pinned( + "2244994945", + list_fields=["follower_count", "member_count", "owner_id"], + expansions=["owner_id"], + user_fields=["created_at", "username"] +) -If you click the "Send" button after step three, you will receive the default [List object](/x-api/fundamentals/data-dictionary#list) fields in your response: `id` and `name`. +for lst in response.data: + print(f"{lst.name} - {lst.follower_count} followers") +``` -If you would like to receive additional fields beyond `id` and `name`, you will have to specify those fields in your request with the [`field`](/x-api/fundamentals/fields) and/or [`expansion`](/x-api/fundamentals/expansions) parameters. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -For this exercise, we will request a three additional sets of fields from different objects: +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -* The additional `follower_count` field in the primary List object. -* The full [user object](/x-api/fundamentals/data-dictionary#user) using the expansion parameter. -* The additional  `tweet.created_at` field in the associated user object. +// Get pinned Lists +const response = await client.lists.getPinned("2244994945", { + listFields: ["follower_count", "member_count", "owner_id"], + expansions: ["owner_id"], + userFields: ["created_at", "username"], +}); +response.data?.forEach((lst) => { + console.log(`${lst.name} - ${lst.follower_count} followers`); +}); +``` -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: + -| | | | -| :--- | :--- | :--- | -| Key | Value | Returned fields | -| list.fields | follower_count | `follower_count` | -| expansions | owner_id | `includes.users.id,
includes.users.name,
includes.users.username` | -| user.fields | created_at | `includes.users.created_at` | +
-You should now see a similar URL next to the “Send” button: + + ```json + { + "data": [ + { + "id": "1454155907651158017", + "name": "Tech News", + "follower_count": 150, + "member_count": 25, + "owner_id": "2244994945" + } + ], + "includes": { + "users": [ + { + "id": "2244994945", + "username": "XDevelopers", + "name": "X Developers", + "created_at": "2013-12-14T04:35:55.000Z" + } + ] + }, + "meta": { + "result_count": 1 + } + } + ``` + +
- `https://api.x.com/2/users/2244994945/pinned_lists?expansions=owner_id&list.fields=follower_count&user.fields=created_at` +--- +## Available List fields -**Step four: Make your request and review your response** +| Field | Description | +|:------|:------------| +| `description` | List description | +| `owner_id` | Owner's user ID | +| `private` | Whether List is private | +| `member_count` | Number of members | +| `follower_count` | Number of followers | +| `created_at` | List creation date | -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +--- -``` -{ - "data": [ - { - "follower_count": 0, - "id": "1454155907651158017", - "name": "test list", - "owner_id": "2244994945" - } - ], - "includes": { - "users": [ - { - "username": "TwitterDev", - "id": "2244994945", - "created_at": "2013-12-14T04:35:55.000Z", - "name": "Twitter Dev" - } - ] - }, - "meta": { - "result_count": 1 - } -} -``` \ No newline at end of file +## Next steps + + + + Pin and unpin Lists + + + Get List details + + + Full endpoint documentation + + diff --git a/x-api/media/quickstart/media-upload-chunked.mdx b/x-api/media/quickstart/media-upload-chunked.mdx index e639d9e65..2b76c7761 100644 --- a/x-api/media/quickstart/media-upload-chunked.mdx +++ b/x-api/media/quickstart/media-upload-chunked.mdx @@ -1,214 +1,360 @@ --- -title: Quickstart +title: Chunked Media Upload sidebarTitle: Quickstart +description: Upload videos and large media files using chunked upload keywords: ["chunked upload", "media upload", "chunked media", "video upload", "large file upload", "chunked upload guide"] --- -This guide will help you make your first requests to upload media using the X API v2 media upload endpoint(s). +This guide walks you through uploading videos and large media files using the chunked upload workflow. -For this guide, the chunked `POST /2/media/upload` endpoints are used to upload a video, which requires an adjusted workflow from single image uploads. - -For video or chunked uploads, you must: -1. Initialize the upload using the `INIT` command -1. Upload each chunk of bytes using the `APPEND` command -1. Complete the upload using the `FINALIZE` command +For video or large media uploads, you must: +1. **INIT** — Initialize the upload and get a `media_id` +2. **APPEND** — Upload each chunk of the file +3. **FINALIZE** — Complete the upload +4. **STATUS** — (If needed) Wait for processing to complete -**Note:** See [this sample code](https://github.com/xdevplatform/large-video-upload-python) for an example written in Python. +See [this sample code](https://github.com/xdevplatform/large-video-upload-python) for a complete Python example. -## Authentication +--- + +## Step 1: Initialize upload (INIT) -In order to run the examples provided in this guide you will need to use [OAuth 2](/resources/fundamentals/authentication/oauth-2-0/overview) authentication. +Start the upload session to get a `media_id`: -You can find your `CONSUMER_KEY`, `CONSUMER_SECRET`, `ACCESS_KEY`, `ACCESS_TOKEN` in the App inside of your Project in the [dashboard of the Developer Portal](https://developer.x.com/en/portal/projects-and-apps). + -## Step 1 : POST media/upload (INIT) +```bash cURL +curl -X POST "https://api.x.com/2/media/upload" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: multipart/form-data" \ + -F "command=INIT" \ + -F "media_type=video/mp4" \ + -F "total_bytes=1048576" \ + -F "media_category=amplify_video" +``` -The `INIT` command request is used to initiate a file upload session. It returns a `media_id` which should be used to execute all subsequent requests. The next step after a successful return from `INIT` command is the `APPEND` command. +```python Python SDK +from xdk import Client -See [Best Practices](/x-api/media/quickstart/best-practices) for constraints and requirements on media files. +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -**Example Request** +# Initialize chunked upload +response = client.media.init_upload( + media_type="video/mp4", + total_bytes=1048576, + media_category="amplify_video" +) -```bash -curl --location 'https://api.x.com/2/media/upload' \ - --header 'Authorization: Bearer ' \ - --header 'Content-Type: multipart/form-data' \ - --form 'command=INIT' \ - --form 'media_type=video/mp4' \ - --form 'total_bytes=1024' \ - --form 'media_category=amplify_video' +media_id = response.data.id +print(f"Media ID: {media_id}") ``` - -**Note:** When making raw HTTP requests, the request should be either `multipart/form-data` or `application/x-www-form-urlencoded` POST formats. - +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Initialize chunked upload +const response = await client.media.initUpload({ + mediaType: "video/mp4", + totalBytes: 1048576, + mediaCategory: "amplify_video", +}); -**Example Response** +const mediaId = response.data?.id; +console.log(`Media ID: ${mediaId}`); +``` + + + +**Response:** ```json { - "data": - { - "id":"1880028106020515840", - "media_key":"13_1880028106020515840", - "expires_after_secs":1295999 - } + "data": { + "id": "1880028106020515840", + "media_key": "13_1880028106020515840", + "expires_after_secs": 1295999 + } } ``` -The response provides the media identifiers `id` (string) and `media_key` (string). The entire file must be uploaded before `expires_after_secs` seconds. +--- -## Step 2 : POST media/upload (APPEND) +## Step 2: Upload chunks (APPEND) -The `APPEND` command is used to upload a chunk (consecutive byte range) of the media file. For example, a 3 MB file could be split into 3 chunks of size 1 MB, and uploaded using 3 `APPEND` command requests. After the entire file is uploaded, the next step is to call the `FINALIZE` command. +Upload each chunk of the file. For example, split a 3 MB file into 3 chunks: - -There are a number of advantages of uploading a media file in small chunks: + + +```bash cURL +curl -X POST "https://api.x.com/2/media/upload" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: multipart/form-data" \ + -F "command=APPEND" \ + -F "media_id=1880028106020515840" \ + -F "segment_index=0" \ + -F "media=@/path/to/chunk1.mp4" +``` -- Improved reliability and success rates under low bandwidth network conditions +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Upload chunks +chunk_size = 1024 * 1024 # 1 MB chunks + +with open("video.mp4", "rb") as f: + segment_index = 0 + while True: + chunk = f.read(chunk_size) + if not chunk: + break + + client.media.append_upload( + media_id=media_id, + segment_index=segment_index, + media=chunk + ) + segment_index += 1 + print(f"Uploaded chunk {segment_index}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +import fs from "fs"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Upload chunks +const chunkSize = 1024 * 1024; // 1 MB chunks +const fileBuffer = fs.readFileSync("video.mp4"); + +let segmentIndex = 0; +for (let offset = 0; offset < fileBuffer.length; offset += chunkSize) { + const chunk = fileBuffer.slice(offset, offset + chunkSize); + + await client.media.appendUpload({ + mediaId, + segmentIndex, + media: chunk, + }); + + console.log(`Uploaded chunk ${segmentIndex + 1}`); + segmentIndex++; +} +``` + + + + +**Chunking advantages:** +- Improved reliability on slow networks - Uploads can be paused and resumed -- File chunks can be retried individually -- Ability to tune chunk sizes to match changing network conditions e.g on cellular clients +- Failed chunks can be retried individually -**Example Request** +--- -```bash -curl --location 'https://api.x.com/2/media/upload' \ - --header 'Authorization: Bearer ' \ - --header 'Content-Type: multipart/form-data' \ - --form 'command=APPEND' \ - --form 'media_id=1880028106020515840' \ - --form 'segment_index=0' \ - --form 'media=@/path/to/your/media/file.mp4' +## Step 3: Finalize upload (FINALIZE) + +Complete the upload after all chunks are sent: + + + +```bash cURL +curl -X POST "https://api.x.com/2/media/upload" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: multipart/form-data" \ + -F "command=FINALIZE" \ + -F "media_id=1880028106020515840" ``` -`segment_index` is an ordered index of file chunk. It must be between 0-999 inclusive. The first segment has index 0, second segment has index 1, and so on. Continue uploading the file chunks until all the chunks are uploaded. +```python Python SDK +from xdk import Client - -**Note:** When making raw HTTP requests, the request should be `multipart/form-data` POST format. - +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") - -**Note:** HTTP 2XX will be returned with an empty response body on successful upload. - +# Finalize upload +response = client.media.finalize_upload(media_id=media_id) -## Step 3 : POST media/upload (FINALIZE) +print(f"Processing state: {response.data.processing_info.state}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -The `FINALIZE` command should be called after the entire media file is uploaded using `APPEND` commands. If and (only if) the response of the `FINALIZE` command contains a `processing_info` field, it may also be necessary to use a `STATUS` command and wait for it to return success before proceeding to Post creation. +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -**Example Request** +// Finalize upload +const response = await client.media.finalizeUpload({ mediaId }); -```bash -curl --location --request POST 'https://api.x.com/2/media/upload' \ - --header 'Authorization: Bearer ' \ - --header 'Content-Type: multipart/form-data' \ - --form 'command=FINALIZE' \ - --form 'media_id=1880028106020515840' +console.log(`Processing state: ${response.data?.processing_info?.state}`); ``` - -**Note:** When making raw HTTP requests, the request should be either `multipart/form-data` or `application/x-www-form-urlencoded` POST formats. - + -**Example Response** +**Response:** ```json { - "data": { - "id": "1880028106020515840", - "media_key": "13_1880028106020515840", - "size": 1024, - "expires_after_secs": 86400, - "processing_info": { - "state": "pending", - "check_after_secs": 1 - } + "data": { + "id": "1880028106020515840", + "media_key": "13_1880028106020515840", + "size": 1048576, + "expires_after_secs": 86400, + "processing_info": { + "state": "pending", + "check_after_secs": 1 } + } } ``` -The response provides a media identifier in the `media_id` (64-bit integer) and `media_id_string` (string) fields. Use the `media_id_string` provided in the API response from JavaScript and other languages that cannot accurately represent a long integer. - -The returned `media_id` is only valid for `expires_after_secs` seconds. Any attempt to use mediaId after this time period in other API calls will result in a Bad Request (HTTP 4xx) response. - -If the response contains a `processing_info` field, then use the `STATUS` command to poll for the status of the `FINALIZE` operation. The async finalize approach is used for cases where media processing requires more time. In future, all video and animated GIF processing will only be supported using async finalize. This behavior is enabled if an upload session was [initialized](###INIT) with a `media_category` parameter, and when then media type is either video or animated GIF. - -**Note:** -If a `processing_info` field is NOT returned in the response, then `media_id` is ready for use in other API endpoints. +If `processing_info` is returned, proceed to Step 4 to wait for processing. If not, the media is ready to use. +--- -## **Step 4 : GET media/upload (STATUS)** +## Step 4: Check status (STATUS) -The `STATUS` command is used to periodically poll for updates of media processing operation. After the `STATUS` command response returns `succeeded`, you can move on to the next step which is usually create Post with `media_id`. +If `processing_info` was returned, poll for processing completion: - -**Note:** -The STATUS command should be used **if and (only if)** the response of the `FINALIZE` command or a previous STATUS command contained a `processing_info` field. - + -## **Example Request** +```bash cURL +curl "https://api.x.com/2/media/upload?command=STATUS&media_id=1880028106020515840" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -```bash -curl --location --request GET 'https://api.x.com/2/media/upload?command=STATUS&media_id=1880028106020515840' \ - --header 'Authorization: Bearer ' +```python Python SDK +from xdk import Client +import time + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Wait for processing to complete +while True: + response = client.media.get_status(media_id=media_id) + state = response.data.processing_info.state + + if state == "succeeded": + print("Media ready!") + break + elif state == "failed": + print("Processing failed") + break + else: + check_after = response.data.processing_info.check_after_secs + print(f"Processing... checking again in {check_after}s") + time.sleep(check_after) ``` -## **Example Response** -```json -{ - "data":{ - "id":"1880028106020515840", - "media_key":"13_1880028106020515840", - "processing_info":{ - "state":"uploading" // state transition flow is pending -> in_progress -> [failed|succeeded] - } - } +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Wait for processing to complete +while (true) { + const response = await client.media.getStatus({ mediaId }); + const state = response.data?.processing_info?.state; + + if (state === "succeeded") { + console.log("Media ready!"); + break; + } else if (state === "failed") { + console.log("Processing failed"); + break; + } else { + const checkAfter = response.data?.processing_info?.check_after_secs ?? 1; + console.log(`Processing... checking again in ${checkAfter}s`); + await new Promise((r) => setTimeout(r, checkAfter * 1000)); + } } ``` -The response body contains `processing_info` field which provides information about current state of media processing operation. It contains a `state` field which has transition flow: `pending` -> `in_progress` -> [`failed` | `succeeded`]. You can not use the `media_id` to create Post or other entities before the state field is set to `succeeded`. + -## Step 5 : Post Tweet with Media +**Processing states:** `pending` → `in_progress` → `succeeded` or `failed` + +--- -Creates a Post using the `POST /2/tweets` endpoint, including the `media_id` on behalf of an authenticated user. +## Step 5: Create Post with media -**Example Request** +Once processing is complete, create a Post with the media: -```bash -curl --location 'https://api.x.com/2/tweets' \ ---header 'Content-Type: application/json' \ ---header 'Authorization: Bearer ' ---data '{ - "text": "I made a post with media!", - "media": { - "media_ids": [ - "1880028106020515840" - ] + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Check out this video!", + "media": { + "media_ids": ["1880028106020515840"] } -}' + }' +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") +# Create Post with media +response = client.posts.create( + text="Check out this video!", + media={"media_ids": [media_id]} +) + +print(f"Posted: {response.data.id}") ``` -**Example Response** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -```bash -{ - "data": { - "edit_history_tweet_ids": [ - "1880028106020515840" - ], - "id": "1880028106020515840", - "text": "I made a post with media! https://t.co/DqNyXX" - } -} +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Create Post with media +const response = await client.posts.create({ + text: "Check out this video!", + media: { mediaIds: [mediaId] }, +}); + +console.log(`Posted: ${response.data?.id}`); ``` + + +--- -## Troubleshooting +## Media categories + +| Category | Description | +|:---------|:------------| +| `tweet_image` | Image for a Post | +| `tweet_gif` | Animated GIF for a Post | +| `tweet_video` | Video for a Post | +| `amplify_video` | Amplify video | + +--- -For issues with the Media APIs, browse the [Media API category](https://devcommunity.x.com/c/x-api/media-apis) in the developer forums for an answer. \ No newline at end of file +## Next steps + + + + File constraints and requirements + + + Post with media + + + Full endpoint documentation + + diff --git a/x-api/migrate/overview.mdx b/x-api/migrate/overview.mdx index 2ca4ada61..d737a29fe 100644 --- a/x-api/migrate/overview.mdx +++ b/x-api/migrate/overview.mdx @@ -140,9 +140,9 @@ In order to use v2 endpoints, you will need the following things: - [A developer App](https://developer.x.com/en/apps) created within a [Project](resources/fundamentals/projects) - [Keys and tokens](resources/fundamentals/authentication) from that Project’s developer App -Please note the importance of using keys and tokens from an App within a Project. If you are using keys and tokens from an App outside of a Project, you will not be able to make a request to v2 endpoints. +Please note the importance of using keys and tokens from an App within an App. If you are using keys and tokens from an App outside of a Project, you will not be able to make a request to v2 endpoints. -Once you have a developer account, you can set up all of the above in the [developer portal](https://developer.x.com/en/portal/petition/essential/basic-info). +Once you have a developer account, you can set up all of the above in the [Developer Console](https://developer.x.com/en/portal/petition/essential/basic-info). ### Authentication With the new Twitter API, you’ll use two different authentication patterns, OAuth 1.0a User Context and OAuth 2.0 Bearer Token, to access different endpoints. Each serves a different purpose when making requests to the endpoints: diff --git a/x-api/news/introduction.mdx b/x-api/news/introduction.mdx index 9b5771e3e..91205659a 100644 --- a/x-api/news/introduction.mdx +++ b/x-api/news/introduction.mdx @@ -10,7 +10,7 @@ This endpoint supports app-auth and user-auth authentication for OAuth1 and OAut ## Getting started -To use the endpoint, you need a [bearer token](/fundamentals/authentication/oauth-2-0/application-only) from the [developer portal](https://developer.x.com/en/portal/dashboard). +To use the endpoint, you need a [bearer token](/fundamentals/authentication/oauth-2-0/application-only) from the [Developer Console](https://developer.x.com/en/portal/dashboard). Once you have the bearer token, you can call the news API as shown below: diff --git a/x-api/posts/bookmarks/integrate.mdx b/x-api/posts/bookmarks/integrate.mdx index 50b6e4d58..1ebfeb99e 100644 --- a/x-api/posts/bookmarks/integrate.mdx +++ b/x-api/posts/bookmarks/integrate.mdx @@ -6,12 +6,12 @@ keywords: ["bookmarks integration", "bookmarks guide", "bookmarks integration gu import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and critical concepts that you should know as you integrate the manage Bookmarks endpoints into your system. We’ve broken the page into a couple of different sections: +This page contains information on several tools and critical concepts that you should know as you integrate the manage Bookmarks endpoints into your system. We've broken the page into a couple of different sections: * [Helpful tools](/x-api/users/blocks/integrate#helpful-tools) * Key Concepts * [Authentication](/x-api/users/blocks/integrate#authentication) - * [Developer portal, Projects, and Apps](/x-api/users/blocks/integrate#developer-portal-projects-and-developer-apps) + * [Developer Console, Projects, and Apps](/x-api/users/blocks/integrate#Developer Console-projects-and-developer-apps) * [Rate limits](/x-api/users/blocks/integrate#rate-limits) ### Helpful tools @@ -20,32 +20,108 @@ Before we dive into some key concepts that will help you integrate this endpoint #### Postman -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. #### Code samples -Are you interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). #### Third-party libraries -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +Take advantage of one of our communities' [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. ### Key concepts #### Authentication -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens.  +All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. These specific endpoints require the use of [OAuth 2.0 Authorization Code Flow with PKCE](/resources/fundamentals/authentication/oauth-2-0/authorization-code), which means that you must use a set of keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are requesting on behalf of. If you want to generate a set of Access Tokens for another user, they must authorize or authenticate your App using an [Authorize URL](/resources/fundamentals/authentication#authorize-url). Please note that OAuth 2.0 can be tricky to use. If you are not familiar with this authentication method, we recommend using a [library](/x-api/tools-and-libraries/overview) or a tool like Postman to properly authenticate your requests. -#### Developer portal, Projects, and developer Apps +#### Developer Console, Projects, and developer Apps -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have a [developer account](/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App.  +To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have a [developer account](/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. #### Rate limits -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/resources/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user.  +Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/resources/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. -These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. There is a user rate limit of 180 requests per 15 min window for the GET method. With the GET method of the Bookmarks lookup endpoint you will get back 800 of your most recent Bookmarked Posts. Additionally, there is a user rate limit of 50 requests per 15 minutes for the POST and DELETE methods.  +These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. There is a user rate limit of 180 requests per 15 min window for the GET method. With the GET method of the Bookmarks lookup endpoint you will get back 800 of your most recent Bookmarked Posts. Additionally, there is a user rate limit of 50 requests per 15 minutes for the POST and DELETE methods. + +--- + +### Code examples + +#### Get bookmarks + + + +```bash cURL +curl "https://api.x.com/2/users/123/bookmarks?tweet.fields=created_at,public_metrics" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get user's bookmarks +for page in client.bookmarks.get(user_id="123", tweet_fields=["created_at", "public_metrics"]): + for post in page.data: + print(f"{post.text} - Likes: {post.public_metrics.like_count}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Get user's bookmarks +const paginator = client.bookmarks.get("123", { + tweetFields: ["created_at", "public_metrics"], +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text} - Likes: ${post.public_metrics?.like_count}`); + }); +} +``` + + + +#### Create a bookmark + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123/bookmarks" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1234567890"}' +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Bookmark a Post +response = client.bookmarks.create(user_id="123", tweet_id="1234567890") +print(response.data) +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Bookmark a Post +const response = await client.bookmarks.create("123", { tweetId: "1234567890" }); +console.log(response.data); +``` + + diff --git a/x-api/posts/bookmarks/introduction.mdx b/x-api/posts/bookmarks/introduction.mdx index 913d0886d..ded4aa202 100644 --- a/x-api/posts/bookmarks/introduction.mdx +++ b/x-api/posts/bookmarks/introduction.mdx @@ -1,50 +1,85 @@ --- -title: Introduction +title: Bookmarks sidebarTitle: Introduction -keywords: ["bookmarks", "bookmark tweets", "save tweets", "bookmark posts", "manage bookmarks", "bookmark API"] +description: Save and manage bookmarked Posts for the authenticated user +keywords: ["bookmarks", "saved posts", "bookmark tweet", "save tweet", "bookmark API", "saved tweets"] --- import { Button } from '/snippets/button.mdx'; -### Manage Bookmarks +The Bookmarks endpoints let you view, add, and remove bookmarked Posts for the authenticated user. Bookmarks are private and only visible to the user who created them. -We have two available methods for manage Bookmarks, POST and DELETE. The POST method lets you create Bookmarks. Likewise, the DELETE method allows you to delete a specific Bookmark.  +## Overview -There is a per-user rate limit of 50 requests per 15 minutes for the POST and DELETE methods. + + + Get all bookmarked Posts + + + Bookmark a Post + + + Remove a bookmarked Post + + + Organize bookmarks into folders + + -Since you are making requests on behalf of a user with the manage Bookmarks endpoints, you must authenticate by generating a user Access Token with OAuth 2.0. You can use the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication/oauth-2-0/user-access-token) to do so. To use this endpoint you must pass in the scopes `tweet.read`, `users.read`, and  `bookmark.write`. +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/:id/bookmarks`](/x-api/users/get-bookmarks) | Get user's bookmarks | +| POST | [`/2/users/:id/bookmarks`](/x-api/users/create-bookmark) | Bookmark a Post | +| DELETE | [`/2/users/:id/bookmarks/:tweet_id`](/x-api/users/delete-bookmark) | Remove a bookmark | +| GET | [`/2/users/:id/bookmarks/folders`](/x-api/users/get-bookmark-folders) | Get bookmark folders | +| GET | [`/2/users/:id/bookmarks/folders/:folder_id`](/x-api/users/get-bookmarks-by-folder-id) | Get Posts in a folder | -### Bookmarks lookup +--- + +## Example: Get bookmarks -The Bookmarks lookup endpoint has one method available, GET. This method allows you to get Bookmarks back from yourself or an authenticated account. Pagination tokens will be provided for paging through large sets of results for this endpoint. +```bash +curl "https://api.x.com/2/users/123456789/bookmarks?\ +tweet.fields=created_at,author_id,public_metrics" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -There is a per-user rate limit of 180 requests per 15 min window for the GET method. With this endpoint you will get back 800 of your most recent Bookmarked Posts. +## Example: Add bookmark -Since you are making requests on behalf of a user with the lookup Bookmarks endpoints, you must authenticate by generating a user Access Token with OAuth 2.0. You can use the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication/oauth-2-0/user-access-token) to do so. To use this endpoint you must pass in the scopes `tweet.read`, `users.read`, and `bookmark.read`. +```bash +curl -X POST "https://api.x.com/2/users/123456789/bookmarks" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1234567890"}' +``` -**Account setup** +--- -To access these endpoints, you will need: +## Getting started -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  + +**Prerequisites** +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) or [3-legged OAuth](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) + -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). -
- -
- -
- -
- -
+ + + Get a user's bookmarks + + + Add and remove bookmarks + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/posts/bookmarks/quickstart/bookmarks-lookup.mdx b/x-api/posts/bookmarks/quickstart/bookmarks-lookup.mdx index 8d369304d..b7226ccfb 100644 --- a/x-api/posts/bookmarks/quickstart/bookmarks-lookup.mdx +++ b/x-api/posts/bookmarks/quickstart/bookmarks-lookup.mdx @@ -1,111 +1,193 @@ --- -title: Bookmarks lookup -sidebarTitle: Bookmarks lookup +title: Bookmarks Lookup +sidebarTitle: Bookmarks Lookup +description: Retrieve your bookmarked Posts keywords: ["bookmarks lookup quickstart", "bookmark lookup quickstart", "bookmark lookup tutorial", "bookmark lookup guide"] --- import { Button } from '/snippets/button.mdx'; -### Getting started with the Bookmarks lookup endpoint +This guide walks you through retrieving your bookmarked Posts using the X API. -This quick start guide will help you make your first request to the Bookmarks lookup endpoint using [Postman](/tutorials/postman-getting-started). + +**Prerequisites** -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to see sample code in different languages. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token with `bookmark.read` scope (OAuth 2.0 PKCE) + -#### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +--- -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +## Get your bookmarks -#### Steps to build a Bookmarks lookup request + + + You need your authenticated user's ID. You can find it using the `/2/users/me` endpoint or from the [user lookup endpoint](/x-api/users/lookup/introduction). + -**Step one: Start with a tool or library** + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. + -To load the X API v2 Postman collection into your environment, please click on the following button: +```bash cURL +curl "https://api.x.com/2/users/2244994945/bookmarks?\ +tweet.fields=created_at,public_metrics,author_id&\ +max_results=10" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` - -Once you have the X API v2 collection loaded in Postman, navigate to the “Bookmarks” folder, and select “ Bookmarks lookup”. +```python Python SDK +from xdk import Client -**Step two: Authenticate your request** +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -To make a successful request to this endpoint, you will need to use [OAuth 2.0 Authorization Code Flow with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3). You can generate an access token within Postman.  +# Get bookmarked Posts with pagination +for page in client.bookmarks.get( + "2244994945", + tweet_fields=["created_at", "public_metrics", "author_id"], + max_results=10 +): + for post in page.data: + print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") +``` -If you go to the tab entitled “Authorization” and select “OAuth 2.0”. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -In this tab, be sure to follow these steps: +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -1. Name your token +// Get bookmarked Posts with pagination +const paginator = client.bookmarks.get("2244994945", { + tweetFields: ["created_at", "public_metrics", "author_id"], + maxResults: 10, +}); -2. Select the Grant Type as Authorization Code (with PKCE) +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); + }); +} +``` -3. Update the parameters: + + + + + + ```json + { + "data": [ + { + "id": "1501258597237342208", + "text": "Have you built a project using the X API you'd like to share with the community? We'd love to hear from you!", + "created_at": "2024-01-15T10:30:00.000Z", + "author_id": "2244994945", + "public_metrics": { + "retweet_count": 15, + "reply_count": 8, + "like_count": 89, + "quote_count": 3 + } + }, + { + "id": "1501258542258348032", + "text": "This is just one way developer innovation helps make X a better place...", + "created_at": "2024-01-15T09:15:00.000Z", + "author_id": "2244994945", + "public_metrics": { + "retweet_count": 22, + "reply_count": 5, + "like_count": 156, + "quote_count": 7 + } + } + ], + "meta": { + "result_count": 2, + "next_token": "7140dibdnow9c7btw4539n0vybdnx19ylpayqf16fjt4l" + } + } + ``` + + - **Callback URL** - [https://www.example.com](https://www.example.com/) +--- - This should be matching the callback URL you set in your auth settings page in the Developer Portal. +## Include author information - **Auth URL** - [https://x.com/i/oauth2/authorize](https://x.com/i/oauth2/authorize) +Use expansions to get data about Post authors: - **Access Token URL** - [https://api.x.com/2/oauth2/token](https://api.x.com/2/oauth2/token) + - **Client ID** - Cut and paste OAuth 2.0 client ID from the Developer Portal +```bash cURL +curl "https://api.x.com/2/users/2244994945/bookmarks?\ +tweet.fields=created_at,author_id&\ +expansions=author_id&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` - **Client Secret** - Cut and paste OAuth 2.0 client ID from the Developer Portal. You will need this only if you are using an App type that is a confidential client. +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get bookmarks with author info +for page in client.bookmarks.get( + "2244994945", + tweet_fields=["created_at", "author_id"], + expansions=["author_id"], + user_fields=["username", "verified"] +): + for post in page.data: + print(f"{post.text[:50]}...") + # Author info is in page.includes.users +``` -4. Update the scopes with the following values: post.read users.read bookmark.read +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -5. Populate the field state with “State” +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -6. Click where it says “Generate Token” +// Get bookmarks with author info +const paginator = client.bookmarks.get("2244994945", { + tweetFields: ["created_at", "author_id"], + expansions: ["author_id"], + userFields: ["username", "verified"], +}); -7. Press the save icon to save the folder changes. +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}...`); + }); + // Author info is in page.includes?.users +} +``` + -You may get a message that you are not logged into X. If you get this error, you will need to log in to the X account inside of Postman you are trying to post on behalf of. +--- -**Step three: Specify a user** +## Required scopes -With this endpoint, you must specify the user ID whose followers you would like to receive in the response. For example, the user ID for @XDevelopers is2244994945. In Postman, navigate to the “Params” tab and enter the ID of yourself or an authenticated user as the value for the id parameter. +When using OAuth 2.0 PKCE, your access token must have these scopes: -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | 2244994945 | +| Scope | Description | +|:------|:------------| +| `bookmark.read` | Read bookmarks | +| `tweet.read` | Read Post data | +| `users.read` | Read user data (for expansions) | -**Step four: Make your request and review your response** +--- -Once you have everything set up, hit the "Send" button and you will receive a similar response to the following example response: +## Next steps -``` -{ - "data": [ - { - "id": "1501258597237342208", - "text": "🗣 Have you built a project using the X API you’d like to share with the community? We’d love to hear from you. Share your project with us!" - }, - { - "id": "1501258542258348032", - "text": "🧰🛠 This is just one way developer innovation helps make Twitter a better place. You can find other ready-to-use tools built by our developer community in our Twitter Toolbox here ⬇️ https://t.co/rK0X30JSYU" - }, - { - "id": "1501257716941000709", - "text": "📣Today’s an important day! \nWe’ve partnered with @Jigsaw on the launch of this new tool. This collaboration allows NGOs and nonprofits to build tools that help people stay safe on Twitter by addressing the needs and preferences of the communities they serve. Learn More ⬇️ https://t.co/MmznmgxoWT" - }, - { - "id": "1501686770810900485", - "text": "Join us tomorrow for a continued conversation on customizing timelines and how this might work for developers. And stay tuned for more Spaces coming up next week. 👀 https://t.co/P4JTc14mdC" - }, - { - "id": "1501596763194593285", - "text": "Developer innovation is always important, including in times of crisis. If you're building tools to help connect people, keep them safe, or share information with the world, we're here to support—reply to this Tweet to tell us more about your app." - } - ] -} -``` \ No newline at end of file + + + Add and remove bookmarks + + + Full endpoint documentation + + diff --git a/x-api/posts/bookmarks/quickstart/manage-bookmarks.mdx b/x-api/posts/bookmarks/quickstart/manage-bookmarks.mdx index 21f27c175..da64b3118 100644 --- a/x-api/posts/bookmarks/quickstart/manage-bookmarks.mdx +++ b/x-api/posts/bookmarks/quickstart/manage-bookmarks.mdx @@ -1,116 +1,164 @@ --- -title: Manage bookmarks -sidebarTitle: Manage bookmarks +title: Manage Bookmarks +sidebarTitle: Manage Bookmarks +description: Add and remove bookmarks using the X API keywords: ["manage bookmarks quickstart", "bookmark quickstart", "bookmark tutorial", "bookmark guide", "bookmarks quickstart"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the manage Bookmarks endpoints +This guide walks you through adding and removing bookmarks using the X API. -This quick start guide will help you make your first request to the manage Bookmarks endpoints using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token with `bookmark.write` scope (OAuth 2.0 PKCE) -### Steps to build a manage Bookmarks request +--- -**Step one: Start with a tool or library** +## Add a bookmark -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. + + + You need your authenticated user's ID. You can find it using the `/2/users/me` endpoint or the [user lookup endpoint](/x-api/users/lookup/introduction). + -To load the X API v2 Postman collection into your environment, please click on the following button: + + Find the Post ID in the URL when viewing a Post: - -Once you have the X API v2 collection loaded in Postman, navigate to the “Bookmarks” folder, and select “Create a Bookmark”. + ``` + https://x.com/XDevelopers/status/1460323737035677698 + └── This is the Post ID + ``` + -**Step two: Authenticate your request** + -To make a successful request to this endpoint, you will need to use [OAuth 2.0 Authorization Code Flow with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3). You can generate an access token within Postman.  + -If you go to the tab entitled “Authorization” and select “OAuth 2.0”. +```bash cURL +curl -X POST "https://api.x.com/2/users/2244994945/bookmarks" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1460323737035677698"}' +``` -In this tab, be sure to follow these steps: +```python Python SDK +from xdk import Client -1. Name your token +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -2. Select the Grant Type as Authorization Code (with PKCE) +# Add a bookmark +response = client.bookmarks.create( + user_id="2244994945", + tweet_id="1460323737035677698" +) -3. Update the parameters: +print(f"Bookmarked: {response.data.bookmarked}") +``` - **Callback URL** - [https://www.example.com](https://www.example.com/) +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; - This should be matching the callback URL you set in your auth settings page in the Developer Portal. +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); - **Auth URL** - [https://x.com/i/oauth2/authorize](https://x.com/i/oauth2/authorize) +// Add a bookmark +const response = await client.bookmarks.create("2244994945", { + tweetId: "1460323737035677698", +}); - **Access Token URL** - [https://api.x.com/2/oauth2/token](https://api.x.com/2/oauth2/token) +console.log(`Bookmarked: ${response.data?.bookmarked}`); +``` - **Client ID** \- Cut and paste OAuth 2.0 client ID from the Developer Portal + - **Client Secret **- Cut and paste OAuth 2.0 client ID from the Developer Portal. You will need this only if you are using an App type that is a confidential client. + -4. Update the scopes with the following values: post.read users.read bookmark.write + + ```json + { + "data": { + "bookmarked": true + } + } + ``` + + -5. Populate the field state with “State” +--- -6. Click where it says “Generate Token” +## Remove a bookmark -7. Press the save icon to save the folder changes. +Delete a Post from your bookmarks: + -You may get a message that you are not logged into X. If you get this error, you will need to log in to the X account inside of Postman you are trying to post on behalf of. +```bash cURL +curl -X DELETE "https://api.x.com/2/users/2244994945/bookmarks/1460323737035677698" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -**Step three: Specify a user** +```python Python SDK +from xdk import Client -With this endpoint, you must specify the user ID whose followers you would like to receive in the response. For example, the user ID for @XDevelopers is `2244994945`. +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -In Postman, navigate to the “Params” tab and enter the ID of yourself or an authenticated user as the value for the id parameter. +# Remove a bookmark +response = client.bookmarks.delete( + user_id="2244994945", + tweet_id="1460323737035677698" +) -| | | | -| :--- | :--- | :--- | -| | Key | Value | -| | id | `2244994945` | +print(f"Bookmarked: {response.data.bookmarked}") +``` -**Step four: Specify Specify a Post you want to Bookmark** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -You will want to navigate to the “Body” tab and make sure the Post ID is set to the one you are looking to save to your Bookmarks. The JSON payload should look similar to the one below. +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); - ``` - {"tweet_id": "1460323737035677698"} - ``` +// Remove a bookmark +const response = await client.bookmarks.delete("2244994945", "1460323737035677698"); +console.log(`Bookmarked: ${response.data?.bookmarked}`); +``` -**Step five: Make your request and review your response** + -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +**Response:** -``` +```json { - "data": { - "bookmarked": true - } + "data": { + "bookmarked": false + } } ``` +--- -To delete a Post, select the “Remove a Bookmark” request also found in the “Bookmarks” folder of the X API v2 collection loaded in Postman. You will first want to specify the user ID of the user you are making a request on behalf of as the value for the “`id`” column. This endpoint also requires the ID of the Post you wish to delete. Then, in the “Params” tab, enter the ID of the Post you wish to delete as the value for the “`tweet_id`” column.  +## Required scopes -When you have a successful delete request, you will receive a response similar to the following example:  -``` -{ - "data": { - "bookmarked": false - } -} -``` \ No newline at end of file +When using OAuth 2.0 PKCE, your access token must have these scopes: + +| Scope | Description | +|:------|:------------| +| `bookmark.write` | Add and remove bookmarks | +| `tweet.read` | Read Post data | +| `users.read` | Read user data | + +--- + +## Next steps + + + + Get your bookmarked Posts + + + Full endpoint documentation + + diff --git a/x-api/posts/counts/integrate/build-a-query.mdx b/x-api/posts/counts/integrate/build-a-query.mdx index 7ec499bbc..f244943ec 100644 --- a/x-api/posts/counts/integrate/build-a-query.mdx +++ b/x-api/posts/counts/integrate/build-a-query.mdx @@ -10,7 +10,7 @@ keywords: ["build query counts", "counts query", "query builder counts", "counts Your queries will be limited depending on which [access level](/x-api/getting-started/about-x-api) you are using.  -If you have Pro access, your query can be 512 characters long. +Your query can be 512 characters long for pay-per-use customers, or up to 4,096 characters for Enterprise customers. If you have Enterprise access, please reach out to your account manager.  @@ -19,7 +19,7 @@ If you have Enterprise access, please reach out to your account manager.  While most operators are available to any developer, there are several that are reserved for those that have been approved for Enterprise access. We list which access level each operator is available to in the [list of operators](/x-api/posts/search/integrate/build-a-query) table using the following labels: -* Core operators: Available when using any [Project](/resources/fundamentals/projects). +* Core operators: Available when using any [Project](/resources/fundamentals/developer-apps). * Advanced operators: Available when using a Project with Enterprise access    diff --git a/x-api/posts/counts/integrate/overview.mdx b/x-api/posts/counts/integrate/overview.mdx index 91660923e..0673c7f2e 100644 --- a/x-api/posts/counts/integrate/overview.mdx +++ b/x-api/posts/counts/integrate/overview.mdx @@ -4,83 +4,54 @@ sidebarTitle: Overview keywords: ["post counts integration", "tweet counts integration", "counts overview", "counts integration guide", "tweet volume integration"] --- -## How to integrate with the Posts counts endpoints +This page covers tools and key concepts for integrating the Post counts endpoints. -This page contains information on several tools and key concepts that you should be aware of as you integrate the recent or full-archive Post counts endpoints into your system. We’ve split the page into the following sections: - -* [Helpful tools](#helpful-tools) -* Key concepts - * [Authentication](#authentication) - * [Developer portal, Projects, and Apps](#developer-portal) - * [Rate limits](#rate-limits) - * [Building queries](#queries) - * [Pagination](#pagination) +--- -### Helpful tools +## Helpful tools Before we start to explore some key concepts, we recommend that you use one of the following tools or code samples to start testing the functionality of these endpoints. -#### Code samples +### Code samples -Interested in getting set up with these endpoints with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [GitHub page](https://github.com/xdevplatform/Twitter-API-v2-sample-code), including a [Python client](https://github.com/xdevplatform/search-tweets-python). +Interested in getting set up with these endpoints with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [GitHub page](https://github.com/xdevplatform/Twitter-API-v2-sample-code), including a [Python client](https://github.com/xdevplatform/search-tweets-python). -#### Libraries +### Libraries Take advantage of one of our many [community third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the appropriate version tag. -#### Postman +### Postman -Postman is a great tool that you can use to test out these endpoints. Each Postman request includes all of the given endpoint’s parameters to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) page. -  +Postman is a great tool that you can use to test out these endpoints. Each Postman request includes all of the given endpoint's parameters to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) page. -### Key concepts +--- -#### Authentication +## Key concepts -All X API v2 endpoints require requests to be [authenticated](/resources/fundamentals/authentication) with a set of credentials, also known as keys and tokens. This specific endpoint requires the use of [OAuth 2.0 Bearer Token](/resources/fundamentals/authentication#oauth-2-0), which means that you must pass a [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) to make a successful request. You can either generate a Bearer Token from directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. +### Authentication -#### Developer portal, Projects, and developer Apps +All X API v2 endpoints require requests to be [authenticated](/resources/fundamentals/authentication) with a set of credentials, also known as keys and tokens. This specific endpoint requires the use of [OAuth 2.0 Bearer Token](/resources/fundamentals/authentication#oauth-2-0), which means that you must pass a [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) to make a successful request. You can either generate a Bearer Token from directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. -To work with any X API v2 endpoints, you must have a [developer account](/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. Your keys and tokens within that developer App will work for the recent Post counts endpoints. If you would like to use the full-archive Post counts endpoint, or utilize the advanced operators and longer query length, you will need to have been approved for enterprise access. +### Developer Console, Projects, and developer Apps -Please visit our section on enterprise access to learn more. +To work with any X API v2 endpoints, you must have a [developer account](/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. Your keys and tokens within that developer App will work for the recent Post counts endpoints. If you would like to use the full-archive Post counts endpoint, or utilize the advanced operators and longer query length, you will need to have been approved for enterprise access. -#### Rate limits +Please visit our section on enterprise access to learn more. -Every day, many thousands of developers make requests to the X API. To help manage the volume, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that every developer can make on behalf of an app or on behalf of an authenticated user. +### Rate limits -This endpoint is rate limited at the App-level, meaning that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by the credentials that you are using).  +Every day, many thousands of developers make requests to the X API. To help manage the volume, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that every developer can make on behalf of an app or on behalf of an authenticated user. +This endpoint is rate limited at the App-level, meaning that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by the credentials that you are using). -#### Building queries +### Building queries The central feature of these endpoints is their use of a single query to filter the Posts into the counts that deliver to you. These queries are made up of operators that match on Post and user attributes, such as message keywords, hashtags, and URLs. Operators can be combined into queries with boolean logic and parentheses to help refine the query's matching behavior. You can use our guide on [how to build a query](/x-api/posts/counts/integrate/build-a-query) to learn more. -#### Pagination - -For recent Post counts, there is no next_token returned, which means that regardless of the granularity, you will get  the Post volume for the last 7 days in one API call. - -For full-archive Post counts, you will get data for the last 30 days. For data more than 30 days, you will get a next_token which you can then use to paginate to get the additional data.  - - -### Building queries for Post counts - -The Post counts endpoints accept a single query with a GET request and return a set of historical Post counts that match the query.  Queries are made up of operators that are used to match on a variety of Post attributes.  +### Pagination -#### Table of contents +For recent Post counts, there is no next_token returned, which means that regardless of the granularity, you will get the Post volume for the last 7 days in one API call. -* [Building a query](#build) -* [Query limitations](#limits) -* [Operator availability](#availability) -* [Operator types: standalone and conjunction-required](#types) -* [Boolean operators and grouping](#boolean) -* [Order of operations](#order-of-operations) -* [Punctuation, diacritics, and case sensitivity](#punctuation) -* [Specificity and efficiency](#specificity) -* [Quote Tweet matching behavior](#quote-tweets) -* [Iteratively building a query](#iterative) -* [Adding a query to your request](#adding-a-query) -* [Query examples](#examples) -* [List of operators](#list) +For full-archive Post counts, you will get data for the last 30 days. For data more than 30 days, you will get a next_token which you can then use to paginate to get the additional data. diff --git a/x-api/posts/counts/introduction.mdx b/x-api/posts/counts/introduction.mdx index 82e1c34f0..bf8541db5 100644 --- a/x-api/posts/counts/introduction.mdx +++ b/x-api/posts/counts/introduction.mdx @@ -1,61 +1,167 @@ --- -title: Introduction +title: Post Counts sidebarTitle: Introduction +description: Get Post volume data for queries without retrieving the Posts themselves keywords: ["tweet counts", "post counts", "count endpoint", "volume counts", "search volume", "tweet volume", "count API"] --- import { Button } from '/snippets/button.mdx'; -The v2 Post counts endpoints allow developers to understand and retrieve the volume of data for a given query.  This can be beneficial for a number of reasons, including: +The Post counts endpoints return the volume of Posts matching a query over time, without returning the Posts themselves. Use these endpoints to analyze trends, understand conversation size, and refine queries before searching. + +## Overview + + + + Build trendlines and visualizations showing Post volume over time + + + Estimate result size before running full search queries + + + Identify when conversations peaked around events + + + Understand conversation scale without retrieving all Posts + + -* Understand the Post volume for a keyword to build visualizations, such as trendlines. +--- -* Understanding the time period in which an event or conversation occurred, to ensure your query captures the relevant data -* Understanding how many Posts a search query will return, in order to refine your query, before using the recent search or full-archive search endpoints. - **Please note:** The counts will not always match the result that will be returned from search endpoints because the search endpoints go through additional compliance that the counts endpoints do not go through -* Understanding the size of the conversation around a topic, without actually having to pull the raw data, and put Posts against your monthly [Post cap](/x-api/fundamentals/post-cap).  +## Endpoints -When developing a query, you will be limited to a certain query length and to specific operators based on your [access level](/x-api/getting-started/about-x-api).  +| Endpoint | Description | Access | +|:---------|:------------|:-------| +| GET [`/2/tweets/counts/recent`](/x-api/posts/get-count-of-recent-posts) | Count Posts from last 7 days | All developers | +| GET [`/2/tweets/counts/all`](/x-api/posts/get-count-of-all-posts) | Count Posts from complete archive | Pay-per-use, Enterprise | -* If you are using a Project with Pro access, you can use all available operators, and use queries up to 1024 characters in length.  +--- -* If you are using a Project with Enterprise access, you can use all available operators, and use queries up to 4096 characters in length.  +## Granularity options +Specify how counts are grouped using the `granularity` parameter: -You can also specify the granularity (which can be day, hour, or minute) as well as the time period for which you need the Post counts (using the start_time and end_time parameters). The default time granularity that this endpoint uses is hour, which means if you do not specify the granularity parameter, the endpoint will give you the Post counts per hour, for the last 7 days. - -**Account setup** +| Granularity | Description | Use case | +|:------------|:------------|:---------| +| `minute` | Counts per minute | Real-time monitoring | +| `hour` | Counts per hour (default) | Daily analysis | +| `day` | Counts per day | Long-term trends | + +--- + +## Recent counts + +Count Posts from the **last 7 days**. Available to all developers. + +### Features + +- Counts grouped by minute, hour, or day +- Same query operators as recent search +- 512-character query length -To access these endpoints, you will need: +### Example response -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +```json +{ + "data": [ + { "start": "2024-01-15T00:00:00.000Z", "end": "2024-01-15T01:00:00.000Z", "tweet_count": 1523 }, + { "start": "2024-01-15T01:00:00.000Z", "end": "2024-01-15T02:00:00.000Z", "tweet_count": 1247 }, + { "start": "2024-01-15T02:00:00.000Z", "end": "2024-01-15T03:00:00.000Z", "tweet_count": 892 } + ], + "meta": { + "total_tweet_count": 3662 + } +} +``` -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). + + + Make your first recent counts request + + + Full endpoint documentation + + + +--- + +## Full-archive counts + +Count Posts from the **complete archive** back to 2006. + + +Full-archive counts is available to pay-per-use and Enterprise customers. -### Recent Post counts -The recent Post counts endpoint allows you to programmatically retrieve the numerical count of Posts for a query, over the last seven days. This endpoint is available via to anyone using keys and tokens that are associated with an [App](/resources/fundamentals/developer-apps) within a [Project](/resources/fundamentals/projects) and uses [OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) for authentication. +### Features + +- Count historical Posts from any time period +- All query operators available +- 1,024-character query length (4,096 for Enterprise) -### Full-archive Post counts +### Pagination -The full-archive Post counts endpoint allows you to programmatically retrieve the numerical count of Posts for a query, from the entire archive of public Posts. Currently, this endpoint is only available to those that have been approved for [Academic Research or Enterprise access](/x-api/getting-started/about-x-api) and use the [OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) for authentication. +Results paginate at 31 days per response: +- **Day granularity**: 31 days per page +- **Hour granularity**: 744 hours (31 days) per page +- **Minute granularity**: 44,640 minutes (31 days) per page -One example: You could use the full-archive Post counts endpoint to see the number of Posts for the hashtag #SOSHurricaneHarvey per day between August and September 2017. + + + Make your first full-archive counts request + + + Full endpoint documentation + + + +--- -**Please note:** The counts endpoint paginates at 31 days per response. For example, setting a day granularity, will return the count of results per day for 31 days per page.  Setting an hour granularity, will return the count of results per hour for 744 (31 days x 24 hours) hours per page.  If you do not specify the granularity and time period, this endpoint will give you Post counts for a query per hour, for the last 30 days. +## Query operators + +Post counts use the same query syntax as search endpoints: + +``` +python lang:en -is:retweet +``` + + + Learn query syntax and operators + + +--- + +## Important notes + + +**Counts vs search results** + +Counts may not exactly match search results. Search endpoints apply additional compliance filtering that counts endpoints do not perform. + + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [Bearer Token](/resources/fundamentals/authentication) + -
- - - - -
\ No newline at end of file + + + Count Posts from the last 7 days + + + Count historical Posts + + + Key concepts and best practices + + + Working code examples + + diff --git a/x-api/posts/counts/migrate/enterprise-to-twitter-api-v2.mdx b/x-api/posts/counts/migrate/enterprise-to-twitter-api-v2.mdx index 361f33a5e..49962a48d 100644 --- a/x-api/posts/counts/migrate/enterprise-to-twitter-api-v2.mdx +++ b/x-api/posts/counts/migrate/enterprise-to-twitter-api-v2.mdx @@ -59,7 +59,7 @@ As noted in the pagination section, you can navigate different pages of data usi **App and Project requirement** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with an App.   **Available time periods** @@ -124,7 +124,7 @@ For a complete API reference, please select an endpoint from below. ### Full-archive Post counts -Only available to those with [Pro and Enterprise access](/x-api/getting-started/about-x-api) +Only available to those with [Self-serve and Enterprise access](/x-api/getting-started/about-x-api) | | | | :--- | :--- | diff --git a/x-api/posts/counts/migrate/overview.mdx b/x-api/posts/counts/migrate/overview.mdx index ca345bc9e..9ff58803a 100644 --- a/x-api/posts/counts/migrate/overview.mdx +++ b/x-api/posts/counts/migrate/overview.mdx @@ -8,13 +8,9 @@ keywords: ["counts migration", "tweet counts migration", "v1.1 to v2 counts", "c The v2 Post counts endpoint will eventually replace the [enterprise Search API’s counts endpoint](/x-api/enterprise-gnip-2.0/fundamentals/search-api#counts-requests-post-count). If you have code, apps, or tools that use an older version of a Post counts endpoint and are considering migrating to the newer X API v2 endpoints, then this guide is for you. -This page contains two comparison tables: - -* [Recent Post counts](#recent) -* [Full-archive Post counts](#full-archive) -* [Filtering operator comparison](#operator) +--- -### Recent Post counts comparison +## Recent Post counts comparison The enterprise version of the Post counts endpoints allow you to pull counts for either 30 days or from the full-archive. Therefore, the v2 recent Post counts endpoint, which looks at a 7 day time period, is not a direct replacement for either of the aforementioned endpoints. @@ -38,7 +34,7 @@ The following table compares the various types of recent Post counts endpoints: | Timezone | UTC | UTC | | Request parameters for selecting time period | fromDate
toDate | start_time
end_time | | Request parameters for navigating by Post ID | | since_id
until_id | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/developer-apps) | | ✔ | ### Full-archive Post counts comparison @@ -62,7 +58,7 @@ The following table compares the various types of full-archive search endpoints: | JSON key name for pagination | next | meta.next_token | | Request parameter for pagination | next_token | next_token or pagination_token | | Timezone | UTC | UTC | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) that has [Academic Research access](/x-api/getting-started/about-x-api) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) that has [Academic Research access](/x-api/getting-started/about-x-api) | | ✔ | ### Filtering operator comparison diff --git a/x-api/posts/counts/quickstart/full-archive-tweet-counts.mdx b/x-api/posts/counts/quickstart/full-archive-tweet-counts.mdx index 0df908462..571c2d83b 100644 --- a/x-api/posts/counts/quickstart/full-archive-tweet-counts.mdx +++ b/x-api/posts/counts/quickstart/full-archive-tweet-counts.mdx @@ -1,260 +1,243 @@ --- -title: Search Posts (Full-archive) -sidebarTitle: Search Posts (Full-archive) +title: Full-Archive Post Counts +sidebarTitle: Full-Archive Post Counts +description: Get historical Post volume back to 2006 keywords: ["full archive tweet counts quickstart", "archive counts quickstart", "historical counts quickstart", "full archive counts"] --- import { Button } from '/snippets/button.mdx'; -### Getting started with the full-archive Post counts     endpoint +This guide walks you through getting historical Post counts back to March 2006. -This quick start guide will help you make your first request to the full-archive Post counts endpoint using Postman, a graphical tool that alows you to make HTTP requests. - -If you would like to see sample code in different programming languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  + +Full-archive Post counts requires [Self-serve](/x-api/getting-started/about-x-api) or [Enterprise](/x-api/getting-started/about-x-api) access. + -### Prerequisites +**Prerequisites** + +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with Self-serve or Enterprise access +- Your App's Bearer Token + -The full-archive Post counts endpoint is currently only available to those that have Pro or Enterprise access. In order to use this endpoint, you must apply for Pro or [Enterprise access](https://docs.google.com/forms/d/e/1FAIpQLScO3bczKWO2jFHyVZJUSEGfdyfFaqt2MvmOfl_aJp0KxMqtDA/viewform).  +--- -In addition to being approved for access, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +## Get full-archive Post counts -* Navigate to your [Project](/resources/fundamentals/projects) with Enterprise access in the developer portal and make sure you have an associated [developer App](/resources/fundamentals/developer-apps) within that Project. + + + Use the same query syntax as full-archive search. For example, to count Posts from @XDevelopers: -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. -
-#### Steps to build a full-archive Post counts request + ``` + from:XDevelopers + ``` + -**Step one: Start with a tool or library** + + Specify `start_time` and `end_time` to search specific historical periods: -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. + | Parameter | Format | Example | + |:----------|:-------|:--------| + | `start_time` | ISO 8601 | `2020-01-01T00:00:00Z` | + | `end_time` | ISO 8601 | `2020-12-31T23:59:59Z` | + -To load X API v2 Postman collection into your environment, please click on the following button: + - + -Once you have the X API v2 collection loaded in Postman, navigate to the Post counts > Full-archive Post counts request. +```bash cURL +curl "https://api.x.com/2/tweets/counts/all?\ +query=from%3AXDevelopers&\ +start_time=2020-01-01T00%3A00%3A00Z&\ +end_time=2020-12-31T23%3A59%3A59Z&\ +granularity=day" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -**Step two: Authenticate your request** +```python Python SDK +from xdk import Client -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request with the [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) authentication methods. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -You must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +# Get full-archive Post counts +response = client.posts.count_all( + query="from:XDevelopers", + start_time="2020-01-01T00:00:00Z", + end_time="2020-12-31T23:59:59Z", + granularity="day" +) -This variable will automatically be pulled into the request's authorization tab if you've done this correctly. -  +for bucket in response.data: + print(f"{bucket.start}: {bucket.tweet_count} Posts") -**Step three: Create a query** +print(f"Total: {response.meta.total_tweet_count}") +``` -Each full-archive Post counts request requires a single [query](/x-api/posts/counts/integrate/build-a-query). For this example, we are going to use a query that matches on Posts posted by the @XDevelopers account. For this query we use the from operator and set it to XDevelopers (case insensitive): +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -`from:XDevelopers` +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -In Postman, navigate to the "Params" tab and enter this ID, or a string of Post IDs separated by a comma, into the "Value" column of the `ids` parameter. -  +// Get full-archive Post counts +const response = await client.posts.countAll({ + query: "from:XDevelopers", + startTime: "2020-01-01T00:00:00Z", + endTime: "2020-12-31T23:59:59Z", + granularity: "day", +}); -| Key | Value | Description | -| :--- | :--- | :--- | -| `query` | `from:XDevelopers` | Query to submit to the full-archive Post counts endpoint | +response.data?.forEach((bucket) => { + console.log(`${bucket.start}: ${bucket.tweet_count} Posts`); +}); +console.log(`Total: ${response.meta?.total_tweet_count}`); +``` -Step four (optional): Specify the granularity and time period + + + + + + ```json + { + "data": [ + { + "end": "2020-01-02T00:00:00.000Z", + "start": "2020-01-01T00:00:00.000Z", + "tweet_count": 3 + }, + { + "end": "2020-01-03T00:00:00.000Z", + "start": "2020-01-02T00:00:00.000Z", + "tweet_count": 5 + } + ], + "meta": { + "total_tweet_count": 8 + } + } + ``` + + -If you click the ‘Send’ button after step three, you will get the default full-archive Post counts: by hour for the last 30 days. If you want to get full-archive Post counts by day, you will have to add the granularity parameter with a value of day. If you want Post counts for more than 30 days ago, you will have to specify the start_time and end_time parameters with the desired values.  +--- -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +## Granularity options -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Description** | -| granularity | day | The granularity for the Post counts results. Possible values are day, hour or minute | -| start_time | 2021-05-01T00:00:00Z | The oldest UTC timestamp from which the Posts will be provided | -| end_time | 2021-06-01T00:00:00Z | The oldest UTC timestamp from which the Posts will be provided. | +Control how counts are grouped: +| Granularity | Description | +|:------------|:------------| +| `minute` | Counts per minute | +| `hour` | Counts per hour (default) | +| `day` | Counts per day | -You should now see the following URL next to the "Send" button: +--- - `https://api.x.com/2/tweets/counts/all?query=from%3AXDevelopers&start_time=2021-05-01T00:00:00Z&end_time=2021-06-01T00:00:00Z&granularity=day` +## Paginate through results +For large time ranges, use the `next_token` from the response: -**Step five: Make your request and review your response** + -Once you have everything set up, hit the "Send" button and you will receive a response similar to the following: +```bash cURL +curl "https://api.x.com/2/tweets/counts/all?\ +query=from%3AXDevelopers&\ +start_time=2015-01-01T00%3A00%3A00Z&\ +end_time=2020-12-31T23%3A59%3A59Z&\ +granularity=day&\ +next_token=abc123" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get counts with pagination +next_token = None + +while True: + response = client.posts.count_all( + query="from:XDevelopers", + start_time="2015-01-01T00:00:00Z", + end_time="2020-12-31T23:59:59Z", + granularity="day", + next_token=next_token + ) + + for bucket in response.data: + print(f"{bucket.start}: {bucket.tweet_count}") + + next_token = response.meta.next_token + if not next_token: + break ``` -{ - "data": [ - { - "end": "2021-05-02T00:00:00.000Z", - "start": "2021-05-01T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-03T00:00:00.000Z", - "start": "2021-05-02T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-04T00:00:00.000Z", - "start": "2021-05-03T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-05T00:00:00.000Z", - "start": "2021-05-04T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-06T00:00:00.000Z", - "start": "2021-05-05T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-07T00:00:00.000Z", - "start": "2021-05-06T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-08T00:00:00.000Z", - "start": "2021-05-07T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-09T00:00:00.000Z", - "start": "2021-05-08T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-10T00:00:00.000Z", - "start": "2021-05-09T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-11T00:00:00.000Z", - "start": "2021-05-10T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-12T00:00:00.000Z", - "start": "2021-05-11T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-13T00:00:00.000Z", - "start": "2021-05-12T00:00:00.000Z", - "tweet_count": 6 - }, - { - "end": "2021-05-14T00:00:00.000Z", - "start": "2021-05-13T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-05-15T00:00:00.000Z", - "start": "2021-05-14T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-16T00:00:00.000Z", - "start": "2021-05-15T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-17T00:00:00.000Z", - "start": "2021-05-16T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-18T00:00:00.000Z", - "start": "2021-05-17T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-19T00:00:00.000Z", - "start": "2021-05-18T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-05-20T00:00:00.000Z", - "start": "2021-05-19T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-21T00:00:00.000Z", - "start": "2021-05-20T00:00:00.000Z", - "tweet_count": 8 - }, - { - "end": "2021-05-22T00:00:00.000Z", - "start": "2021-05-21T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-05-23T00:00:00.000Z", - "start": "2021-05-22T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-24T00:00:00.000Z", - "start": "2021-05-23T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-25T00:00:00.000Z", - "start": "2021-05-24T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-26T00:00:00.000Z", - "start": "2021-05-25T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-05-27T00:00:00.000Z", - "start": "2021-05-26T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-05-28T00:00:00.000Z", - "start": "2021-05-27T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-05-29T00:00:00.000Z", - "start": "2021-05-28T00:00:00.000Z", - "tweet_count": 2 - }, - { - "end": "2021-05-30T00:00:00.000Z", - "start": "2021-05-29T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-05-31T00:00:00.000Z", - "start": "2021-05-30T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-06-01T00:00:00.000Z", - "start": "2021-05-31T00:00:00.000Z", - "tweet_count": 0 - } - ], - "meta": { - "total_tweet_count": 22 - } -} + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get counts with pagination +let nextToken = undefined; + +do { + const response = await client.posts.countAll({ + query: "from:XDevelopers", + startTime: "2015-01-01T00:00:00Z", + endTime: "2020-12-31T23:59:59Z", + granularity: "day", + nextToken, + }); + + response.data?.forEach((bucket) => { + console.log(`${bucket.start}: ${bucket.tweet_count}`); + }); + + nextToken = response.meta?.next_token; +} while (nextToken); ``` + + +--- + +## Key differences from recent counts + +| Feature | Recent Counts | Full-Archive Counts | +|:--------|:--------------|:--------------------| +| Time range | Last 7 days | March 2006 to now | +| Access required | All developers | Pay-per-use, Enterprise | +| Default time range | Last 7 days | Last 30 days | -**Step six: Paginate through your results** +--- + +## Common parameters -If the ‘meta’ object in your response also contains next_token, you can pass its value to the next_token query parameter. +| Parameter | Description | Default | +|:----------|:------------|:--------| +| `query` | Search query (required) | — | +| `granularity` | Time bucket size | `hour` | +| `start_time` | Oldest timestamp (ISO 8601) | 30 days ago | +| `end_time` | Newest timestamp (ISO 8601) | Now | +| `next_token` | Pagination token | — | -| Key | Value | Description | -| :--- | :--- | :--- | -| next_token | You will add the next_token that you pull from your previous request's meta object and add it here. | If your latest request does not deliver the remainder of the results, you will receive a next_token in the meta object. You will pull the value of that field and add it as the value of the next_token parameter in your next request, holding all other request parameters constant. | +--- -Once you have the right value for next_token set, hit the "Send" button and you will receive the next page of results. +## Next steps + + + + Get recent Post counts + + + Master query syntax + + + Full endpoint documentation + + diff --git a/x-api/posts/counts/quickstart/recent-tweet-counts.mdx b/x-api/posts/counts/quickstart/recent-tweet-counts.mdx index 7a271c96c..38a0a4efe 100644 --- a/x-api/posts/counts/quickstart/recent-tweet-counts.mdx +++ b/x-api/posts/counts/quickstart/recent-tweet-counts.mdx @@ -1,129 +1,242 @@ --- -title: Search Posts (Recent) -sidebarTitle: Search Posts (Recent) +title: Recent Post Counts +sidebarTitle: Recent Post Counts +description: Get Post volume for the last 7 days keywords: ["recent tweet counts quickstart", "recent counts quickstart", "tweet volume quickstart", "counts quickstart", "recent search counts"] --- import { Button } from '/snippets/button.mdx'; -### Getting started with the recent Post counts endpoint +This guide walks you through getting Post counts (volume) for the last 7 days. -This quick start guide will help you make your first request to the recent Post counts endpoint using Postman, a graphical tool that allows you to send HTTP requests. + +**Prerequisites** -If you would like to see sample code in different programming languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token + - -### Prerequisites +--- -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +## Get recent Post counts -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. - -#### Steps to build a recent Post counts request + + + Use the same query syntax as recent search. For example, to count Posts from @XDevelopers: -**Step one: Start with a tool or library** + ``` + from:XDevelopers + ``` + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. + -To load X API v2 Postman collection into your environment, please click on the following button: + - +```bash cURL +curl "https://api.x.com/2/tweets/counts/recent?\ +query=from%3AXDevelopers&\ +granularity=day" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -Once you have the X API v2 collection loaded in Postman, navigate to the Post counts > Recent Post counts request. +```python Python SDK +from xdk import Client -**Step two: Authenticate your request** +client = Client(bearer_token="YOUR_BEARER_TOKEN") -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request with the [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) authentication methods. +# Get recent Post counts +response = client.posts.count_recent( + query="from:XDevelopers", + granularity="day" +) -You must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +for bucket in response.data: + print(f"{bucket.start}: {bucket.tweet_count} Posts") -This variable will automatically be pulled into the request's authorization tab if you've done this correctly. -  +print(f"Total: {response.meta.total_tweet_count}") +``` -**Step three: Create a query** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Each recent Post counts request requires a single[query](/x-api/posts/counts/integrate/build-a-query). For this example, we are going to use a query that matches on Posts posted by the @XDevelopers account. For this query we use the from: operator and set it to XDevelopers (case insensitive): +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -`from:XDevelopers` +// Get recent Post counts +const response = await client.posts.countRecent({ + query: "from:XDevelopers", + granularity: "day", +}); -In Postman, navigate to the "Params" tab and enter this ID, or a string of Post IDs separated by a comma, into the "Value" column of the `ids` parameter. +response.data?.forEach((bucket) => { + console.log(`${bucket.start}: ${bucket.tweet_count} Posts`); +}); -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Description** | -| `query` | from:XDevelopers | Query to submit to the recent Post counts endpoint | +console.log(`Total: ${response.meta?.total_tweet_count}`); +``` -**Step four (optional): Specify the granularity of the request** + + + + + + ```json + { + "data": [ + { + "end": "2024-01-16T00:00:00.000Z", + "start": "2024-01-15T00:00:00.000Z", + "tweet_count": 5 + }, + { + "end": "2024-01-17T00:00:00.000Z", + "start": "2024-01-16T00:00:00.000Z", + "tweet_count": 3 + }, + { + "end": "2024-01-18T00:00:00.000Z", + "start": "2024-01-17T00:00:00.000Z", + "tweet_count": 8 + } + ], + "meta": { + "total_tweet_count": 16 + } + } + ``` + + -If you click the ‘Send’ button after step three, you will get the default recent Post counts: by hour for the last seven days. If you want to get recent Post counts by day, you will have to add the granularity parameter with a value of day. +--- -In Postman, navigate to the "Params" tab and day into the "Value" column of the granularity parameter. +## Granularity options -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Description** | -| granularity | day | The granularity for the Post counts results. Possible values are day, hour or minute | +Control how counts are grouped: +| Granularity | Description | +|:------------|:------------| +| `minute` | Counts per minute | +| `hour` | Counts per hour (default) | +| `day` | Counts per day | -You should now see the following URL next to the "Send" button: + - `https://api.x.com/2/tweets/counts/recent?query=from%3AXDevelopers&granularity=day` +```bash cURL +# Get hourly counts +curl "https://api.x.com/2/tweets/counts/recent?\ +query=python%20lang%3Aen&\ +granularity=hour" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` +```python Python SDK +from xdk import Client -**Step five: Make your request and review your response** +client = Client(bearer_token="YOUR_BEARER_TOKEN") -Once you have everything set up, hit the "Send" button and you will receive the following response: +# Get hourly counts +response = client.posts.count_recent( + query="python lang:en", + granularity="hour" +) +for bucket in response.data: + print(f"{bucket.start}: {bucket.tweet_count}") ``` -{ - "data": [ - { - "end": "2021-06-16T00:00:00.000Z", - "start": "2021-06-15T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-06-17T00:00:00.000Z", - "start": "2021-06-16T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-06-18T00:00:00.000Z", - "start": "2021-06-17T00:00:00.000Z", - "tweet_count": 2 - }, - { - "end": "2021-06-19T00:00:00.000Z", - "start": "2021-06-18T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-06-20T00:00:00.000Z", - "start": "2021-06-19T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-06-21T00:00:00.000Z", - "start": "2021-06-20T00:00:00.000Z", - "tweet_count": 0 - }, - { - "end": "2021-06-22T00:00:00.000Z", - "start": "2021-06-21T00:00:00.000Z", - "tweet_count": 1 - }, - { - "end": "2021-06-23T00:00:00.000Z", - "start": "2021-06-22T00:00:00.000Z", - "tweet_count": 2 - } - ], - "meta": { - "total_tweet_count": 6 - } -} -``` \ No newline at end of file + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get hourly counts +const response = await client.posts.countRecent({ + query: "python lang:en", + granularity: "hour", +}); + +response.data?.forEach((bucket) => { + console.log(`${bucket.start}: ${bucket.tweet_count}`); +}); +``` + + + +--- + +## Filter by time range + +Limit counts to a specific time period: + + + +```bash cURL +curl "https://api.x.com/2/tweets/counts/recent?\ +query=from%3AXDevelopers&\ +start_time=2024-01-10T00%3A00%3A00Z&\ +end_time=2024-01-15T00%3A00%3A00Z&\ +granularity=day" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get counts for a specific time range +response = client.posts.count_recent( + query="from:XDevelopers", + start_time="2024-01-10T00:00:00Z", + end_time="2024-01-15T00:00:00Z", + granularity="day" +) + +print(f"Total Posts: {response.meta.total_tweet_count}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get counts for a specific time range +const response = await client.posts.countRecent({ + query: "from:XDevelopers", + startTime: "2024-01-10T00:00:00Z", + endTime: "2024-01-15T00:00:00Z", + granularity: "day", +}); + +console.log(`Total Posts: ${response.meta?.total_tweet_count}`); +``` + + + +--- + +## Common parameters + +| Parameter | Description | Default | +|:----------|:------------|:--------| +| `query` | Search query (required) | — | +| `granularity` | Time bucket size | `hour` | +| `start_time` | Oldest timestamp (ISO 8601) | 7 days ago | +| `end_time` | Newest timestamp (ISO 8601) | Now | + +--- + +## Next steps + + + + Get historical Post counts + + + Master query syntax + + + Full endpoint documentation + + diff --git a/x-api/posts/filtered-stream/integrate/build-a-rule.mdx b/x-api/posts/filtered-stream/integrate/build-a-rule.mdx index 45f4a5080..705c58c36 100644 --- a/x-api/posts/filtered-stream/integrate/build-a-rule.mdx +++ b/x-api/posts/filtered-stream/integrate/build-a-rule.mdx @@ -1,203 +1,222 @@ --- title: Build a rule sidebarTitle: Build a rule +description: Learn how to build filtered stream rules using operators keywords: ["build rule", "filter rules", "stream rules", "rule builder", "filtered stream rules", "create rule", "rule guide"] --- -## Building rules for filtered stream +The filtered stream endpoints deliver Posts that match a set of rules applied to the stream. Rules are made up of operators that match on a variety of Post attributes. -The filtered stream endpoints deliver filtered Posts to you that match on a set of rules that are applied to the stream. Rules are made up of operators that are used to match on a variety of Post attributes. +Multiple rules can be applied using the [POST /tweets/search/stream/rules](/x-api/posts/filtered-stream#post-2-tweets-search-stream-rules) endpoint. Once you've added rules and connected using [GET /tweets/search/stream](/x-api/posts/filtered-stream#get-2-tweets-search-stream), only Posts matching your rules will be delivered. You do not need to disconnect to add or remove rules. -Multiple rules can be applied to a stream using the [POST /tweets/search/stream/rules](/x-api/posts/filtered-stream#post-2-tweets-search-stream-rules) endpoint. Once you’ve added rules and connect to your stream using the [GET /tweets/search/stream](/x-api/posts/filtered-stream#get-2-tweets-search-stream) endpoint, only those Posts that match your rules will be delivered through a persistent streaming connection. You do not need to disconnect from your stream to add or remove rules.  - -### Table of contents - -* [Building a rule](#building-a-rule) - * [Rule limitations](#rule-limitations) - * [Operator availability](#operator-availability) - * [Operator types: standalone and conjunction-required](#operator-types-standalone-and-conjunction-required) - * [Boolean operators and grouping](#boolean-operators-and-grouping) - * [Order of operations](#order-of-operations) - * [Punctuation, diacritics, and case sensitivity](#punctuation-diacritics-and-case-sensitivity) - * [Specificity and efficiency](#specificity-and-efficiency) - * [Iteratively building a rule](#iteratively-building-a-rule) - * [Adding and removing rules](#adding-and-removing-rules) - * [Rule examples](#rule-examples) - -### Building a rule +--- -#### Rule limitations +## Rule limitations -Limits on the number of rules will depend on which [access level](/x-api/getting-started/about-x-api) is applied to your [Project](/resources/fundamentals/projects). +Limits on the number of rules depend on your [access level](/x-api/getting-started/about-x-api). See the [filtered stream introduction](/x-api/posts/filtered-stream/introduction) for specific limits. -You can see how these limits apply via the [filtered stream introduction](/x-api/posts/filtered-stream) page. +--- -#### Operator types: standalone and conjunction-required +## Operator types: standalone and conjunction-required **Standalone operators** can be used alone or together with any other operators (including those that require conjunction). -For example, the following rule will work because it uses the `#hashtag` operator, which is standalone: +For example, this rule works because `#hashtag` is a standalone operator: + +``` +#xapiv2 +``` -`#xapiv2` +**Conjunction-required operators** cannot be used by themselves in a rule; they can only be used when at least one standalone operator is included. This is because using these operators alone would match an extremely high volume of Posts. -**Conjunction required** operators cannot be used by themselves in a rule; they can only be used when at least one standalone operator is included in the rule. This is because using these operators alone would be far too general, and would match on an extremely high volume of Posts. +For example, the following rules are **not supported** since they contain only conjunction-required operators: -For example, the following rules are not supported since they contain only conjunction required operators: +``` +has:media +``` -`has:media` +``` +has:links OR is:retweet +``` -`has:links OR is:retweet` +If we add a standalone operator, such as the phrase `"X data"`, the rule works properly: -If we add in a standalone operator, such as the phrase `"X data"`, the rule would then work properly.  +``` +"X data" has:mentions (has:media OR has:links) +``` -`"X data" has:mentions (has:media OR has:links)` +--- -#### Boolean operators and grouping +## Boolean operators and grouping -If you would like to string together multiple operators in a single rule, you have the following tools at your disposal: +String together multiple operators using these tools: -| | | -| :--- | :--- | -| **AND logic** | Successive operators **with a space between them** will result in boolean "AND" logic, meaning that Posts will match only if both conditions are met. For example, `snow day #NoSchool` will match Posts containing the terms snow and day and the hashtag #NoSchool. | -| **OR logic** | Successive operators with OR between them will result in OR logic, meaning that Posts will match if either condition is met. For example, specifying `grumpy OR cat OR #meme` will match any Posts containing at least the terms grumpy or cat, or the hashtag #meme. | -| **NOT logic, negation** | Prepend a dash (-) to a keyword (or any operator) to negate it (NOT). For example, `cat #meme -grumpy` will match Posts containing the hashtag #meme and the term cat, but only if they do not contain the term grumpy. One common rule clause is `-is:retweet`, which will not match on Retweets, thus matching only on original Posts, Quote Tweets, and replies. All operators can be negated, but negated operators cannot be used alone. | -| **Grouping** | You can use parentheses to group operators together. For example, `(grumpy cat) OR (#meme has:images)` will return either Posts containing the terms grumpy and cat, or Posts with images containing the hashtag #meme. Note that ANDs are applied first, then ORs are applied. | +| Operator | Description | Example | +| :--- | :--- | :--- | +| **AND** (space) | Posts must match both conditions | `snow day #NoSchool` matches Posts with "snow" AND "day" AND #NoSchool | +| **OR** | Posts must match either condition | `grumpy OR cat OR #meme` matches Posts with "grumpy" OR "cat" OR #meme | +| **NOT** (dash) | Exclude Posts matching this condition | `cat #meme -grumpy` matches Posts with "cat" and #meme but NOT "grumpy" | +| **Grouping** (parentheses) | Group operators together | `(grumpy cat) OR (#meme has:images)` matches either group | **A note on negations** -All operators can be negated except for `sample:`, and `-is:nullcast` must always be negated. Negated operators cannot be used alone. - -Do not negate a set of operators grouped together in a set of parentheses. Instead, negate each individual operator. - -For example, instead of using `skiing -(snow OR day OR noschool)`, we suggest that you use `skiing -snow -day -noschool`.  +- All operators can be negated except for `sample:` +- The operator `-is:nullcast` must always be negated +- Negated operators cannot be used alone +- Do not negate grouped operators. Instead of `skiing -(snow OR day OR noschool)`, use `skiing -snow -day -noschool` -#### Order of operations +--- -When combining AND and OR functionality, the following order of operations will dictate how your rule is evaluated. +## Order of operations -1. Operators connected by AND logic are combined first -2. Then, operators connected with OR logic are applied +When combining AND and OR: +1. Operators connected by AND logic are combined first +2. Then, operators connected with OR logic are applied +**Examples:** -For example: +| Query | Evaluated as | +| :--- | :--- | +| `apple OR iphone ipad` | `apple OR (iphone ipad)` | +| `ipad iphone OR android` | `(iphone ipad) OR android` | -* `apple OR iphone ipad` would be evaluated as `apple OR (iphone ipad)` -* `ipad iphone OR android` would be evaluated as `(iphone ipad) OR android` +To eliminate uncertainty, use parentheses: +``` +(apple OR iphone) ipad +``` +``` +iphone (ipad OR android) +``` -To eliminate uncertainty and ensure that your rule is evaluated as intended, group terms together with parentheses where appropriate.  +--- -For example: +## Punctuation, diacritics, and case sensitivity -* `(apple OR iphone) ipad` -* `iphone (ipad OR android)` +**Diacritics:** Filtered stream rules with accents only match Posts that also include the accent. For example, `diacrítica` matches _diacrítica_ but **not** _diacritica_. -#### Punctuation, diacritics, and case sensitivity +**Case sensitivity:** All operators are case-insensitive. The rule `cat` matches _cat_, _CAT_, and _Cat_. -If you specify a keyword or hashtag rule with character accents or diacritics, it will match Posts that contain the exact word with proper accents or diacritics, but not those that have the proper letters, but without the accent or diacritic.  + +**Search Posts behaves differently** -For example, rules with the keyword `diacrítica` or hashtag `#cumpleaños` will match Posts that contain _diacrítica_ or _#cumpleaños_ because they include the accent or diacritic. However, these rules will not match Posts that contain _Diacritica_ or _#cumpleanos_ without the tilde í or eñe. +When [building search queries](/x-api/posts/search/integrate/build-a-query), keywords with accents match Posts both with and without the accents. For example, `Diacrítica` matches both _Diacrítica_ and _Diacritica_. + +--- -Characters with accents or diacritics are treated the same as normal characters and are not treated as word boundaries. For example, a rule with the keyword _cumpleaños_ would only match Posts containing the word _cumpleaños_ and would not match Posts containing _cumplea_, _cumplean_, or _os_. +## Quote Tweet matching -All operators are evaluated in a case-insensitive manner. For example, the rule cat will match all of the following: _cat_, _CAT_, _Cat_. +When using filtered stream, operators match on both the Quote Tweet's content **and** the content from the original Post that was quoted. -The [Search Posts](/x-api/posts/search/introduction) matching behavior acts differently from filtered stream. When [building a Search Posts query](/x-api/posts/search/integrate/build-a-query), know that keywords and hashtags that include accents or diacritics will match both the term with the accents and diacritics, as well as with normal characters.  - -For example, Search Posts queries that include a keyword `Diacrítica` or hashtag `#cumpleaños` will match both _Diacrítica_ and _#cumpleaños_, as well as _Diacritica_ or _#cumpleanos_ without the tilde í or eñe. +[Search Posts](/x-api/posts/search/introduction) behaves differently—it only matches on the Quote Tweet's content, not the original Post. -#### Specificity and efficiency +--- -When you start to build your rule, it is important to keep a few things in mind. +## Specificity and efficiency -* Using broad, standalone operators for your rule such as a single keyword or #hashtag is generally not recommended since it will likely match on a massive volume of Posts. Creating a more robust rule will result in a more specific set of matching Posts, and will hopefully reduce the amount of noise in the payload that you will need to sift through to find valuable insights.  - * For example, if your rule was just the keyword `happy` you will likely get anywhere from 200,000 - 300,000 Posts per day. - * Adding more conditional operators narrows your search results, for example `(happy OR happiness) place_country:GB -birthday -is:retweet` -* Writing efficient rules is also beneficial for staying within the characters rule length restriction. The character count includes the entire rule string including spaces and operators. - * For example, the following rule is 59 characters long: `(happy OR happiness) place_country:GB -birthday -is:retweet` + +Using broad operators like a single keyword or hashtag is not recommended—it will match a massive volume of Posts and quickly consume your connection. + -#### Quote Tweet matching behavior +**Tips for building effective rules:** -When using the filtered stream endpoints, operators will match on both the content from the original Post that was quoted, as well as the content included in the Quote Tweet. +1. **Start specific, then broaden** — Create targeted rules that return relevant results +2. **Use multiple operators** — Combine operators to narrow results +3. **Watch your character count** — The entire rule string counts toward the limit -However, please note that the [Search Posts](/x-api/posts/search/introduction) endpoints will not match on the content from the original Post that was quoted, but will match on the Quote Tweet's content. +**Example progression:** -#### Iteratively building a rule +``` +# Too broad - 200,000+ Posts per day +happy -##### Test your rule early and often +# Better - adds language filter and exclusions +(happy OR happiness) lang:en -birthday -is:retweet -Getting a rule to return the "right" results the first time is rare. There is so much on X that may or may not be obvious at first and the rule syntax described above may be hard to match to your desired search. As you build a rule, it is important for you to periodically test it out with the stream endpoint to see what data it returns. You can also test with one of the [Search Post](/x-api/posts/search/introduction) endpoints, assuming the operators that you are using are also available via that endpoint.  +# Even better - 59 characters, more specific +(happy OR happiness) place_country:GB -birthday -is:retweet +``` -For this section, we are going to start with the following rule and adjust it based on the results that we receive during our test:  +--- -`happy OR happiness` +## Iteratively building a rule -##### Use results to narrow the rule +### Step 1: Start with a basic rule -As you test the rule, you should scan the returned Posts to see if they include the data that you are expecting and hoping to receive. Starting with a broad rule and a superset of Post matches allows you to review the result and narrow the rule to filter out undesired results.   +``` +happy OR happiness +``` -When we tested the example rule, we noticed that we were getting Posts in a variety of different languages. In this situation, we want to only receive Posts that are in english, so we’re going to add the `lang:` operator: +### Step 2: Test and narrow based on results -`(happy OR happiness) lang:en` +We noticed Posts in many languages. Add a language filter: -The test delivered a number of Posts wishing people a happy birthday, so we are going to add `-birthday` as a negated keyword operator. We also want to only receive original Posts, so we’ve added the negated `-is:retweet` operator: +``` +(happy OR happiness) lang:en +``` -`(happy OR happiness) lang:en -birthday -is:retweet` +We're getting birthday wishes. Exclude them and Retweets: -##### Adjust for inclusion where needed +``` +(happy OR happiness) lang:en -birthday -is:retweet +``` -If you notice that you are not receiving data that you expect and know that there are existing Posts that should return, you may need to broaden your rule by removing operators that may be filtering out the desired data.  +### Step 3: Broaden for better coverage -For our example, we noticed that there were other Posts in our personal timeline that expressed the emotion that we are looking for and weren’t included in the test results. To ensure we have greater coverage, we are going to add the keywords, `excited` and `elated`. +We want to capture more sentiment. Add related keywords: -`(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet` +``` +(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet +``` -##### Adjust for popular trends/bursts over the time period +### Step 4: Adjust for trends -Trends come and go on X quickly. Maintaining your rule should be an active process. If you plan to use a single rule for a while, we suggest that you periodically check in on the data that you are receiving to see if you need to make any adjustments. +Holiday Posts are appearing. Exclude them: -In our example, we notice that we started to receive some Posts that are wishing people a “happy holidays”. Since we don’t want these Posts included in our results, we are going to add a negated `-holidays` keyword. +``` +(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet -holidays +``` -`(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet -holidays` +--- -#### Adding and removing rules +## Adding and removing rules -You will be using the [POST /2/tweets/search/stream/rules](/x-api/posts/filtered-stream#post-2-tweets-search-stream-rules) endpoint when both adding and deleting rules from your stream. +Use [POST /2/tweets/search/stream/rules](/x-api/posts/filtered-stream#post-2-tweets-search-stream-rules) to add or remove rules. -To add one or more rule to your stream, submit an `add` JSON body with an array that contains the value parameter including the rule, and the optional `tag` parameter including free-form text that you can use to [identify which returned Posts match this rule](/x-api/posts/filtered-stream#matching-returned-posts-to-their-associated-rule).  +### Adding rules -For example, if you were trying to add a set of rules to your stream, your cURL command might look like this: +Submit an `add` JSON body with the `value` (the rule) and optional `tag` (to identify matching Posts): ```bash -curl -X POST 'https://api.x.com/2/tweets/search/stream/rules' \ --H "Content-type: application/json" \ --H "Authorization: Bearer $ACCESS_TOKEN" -d \ -'{ - "add": [ - {"value": "cat has:media", "tag": "cats with media"}, - {"value": "cat has:media -grumpy", "tag": "happy cats with media"}, - {"value": "meme", "tag": "funny things"}, - {"value": "meme has:images"} - ] -}' +curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -d '{ + "add": [ + {"value": "cat has:media", "tag": "cats with media"}, + {"value": "cat has:media -grumpy", "tag": "happy cats with media"}, + {"value": "meme", "tag": "funny things"}, + {"value": "meme has:images"} + ] + }' ``` -Similarly, to remove one or more rules from your stream, submit a `delete` JSON body with the array of that contains the `id` parameter including the rule IDs that you would like to delete. +### Removing rules -For example, if you were trying to remove a set of rules from your stream, your cURL command might look like this: +Submit a `delete` JSON body with the rule IDs to remove: ```bash -curl -X POST 'https://api.x.com/2/tweets/search/stream/rules' \ - -H "Content-type: application/json" \ - -H "Authorization: Bearer $ACCESS_TOKEN" -d \ - '{ +curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -d '{ "delete": { "ids": [ "1165037377523306498", @@ -207,49 +226,69 @@ curl -X POST 'https://api.x.com/2/tweets/search/stream/rules' \ }' ``` -We have sample code in different languages available via our [Github](https://github.com/xdevplatform/Twitter-API-v2-sample-code/tree/master/Filtered-Stream).  +--- -#### Rule examples +## Rule examples -##### Tracking a natural disaster +### Tracking a natural disaster -The following rule matched on Posts coming from weather agencies and gauges that discuss Hurricane Harvey, which hit Houston in 2017. Notice the use of the [matching rules](/x-api/posts/filtered-stream#matching-returned-posts-to-their-associated-rule) tag, and the JSON format that you will need to use when submitting the rule to the [POST /2/tweets/search/stream/rules endpoint](/x-api/posts/filtered-stream#post-2-tweets-search-stream-rules). +Match Posts from weather agencies about Hurricane Harvey: ```json { - "value": "-is:retweet has:geo (from:NWSNHC OR from:NHC_Atlantic OR from:NWSHouston OR from:NWSSanAntonio OR from:USGS_TexasRain OR from:USGS_TexasFlood OR from:JeffLindner1)", - "tag": "theme:info has:geo original from weather agencies and gauges" + "value": "-is:retweet has:geo (from:NWSNHC OR from:NHC_Atlantic OR from:NWSHouston OR from:NWSSanAntonio OR from:USGS_TexasRain OR from:USGS_TexasFlood OR from:JeffLindner1)", + "tag": "Hurricane Harvey - weather agencies with geo" } ``` -##### Reviewing the sentiment of a conversation +### Sentiment analysis for #nowplaying -The next rule could be used to better understand the sentiment of the conversation developing around the hashtag, _#nowplaying_, but only from Posts published within North America. +**Positive sentiment:** ```json { - "value": "#nowplaying (happy OR exciting OR excited OR favorite OR fav OR amazing OR lovely OR incredible) (place_country:US OR place_country:MX OR place_country:CA) -horrible -worst -sucks -bad -disappointing", - "tag": "#nowplaying positive" -}, + "value": "#nowplaying (happy OR exciting OR excited OR favorite OR fav OR amazing OR lovely OR incredible) (place_country:US OR place_country:MX OR place_country:CA) -horrible -worst -sucks -bad -disappointing", + "tag": "#nowplaying positive" +} +``` + +**Negative sentiment:** + +```json { - "value": "#nowplaying (horrible OR worst OR sucks OR bad OR disappointing) (place_country:US OR place_country:MX OR place_country:CA) -happy -exciting -excited -favorite -fav -amazing -lovely -incredible", - "tag": "#nowplaying negative" + "value": "#nowplaying (horrible OR worst OR sucks OR bad OR disappointing) (place_country:US OR place_country:MX OR place_country:CA) -happy -exciting -excited -favorite -fav -amazing -lovely -incredible", + "tag": "#nowplaying negative" } ``` -##### Find Posts that relate to a specific Post annotation +### Using Post annotations -This rule was built to search for original Posts that included an image of a pet that is not a cat, where the language identified in the Post is Japanese. To do this, we used the `context:` operator to take advantage of the [Post annotation](/x-api/fundamentals/post-annotations) functionality. We first used the [Post lookup](/x-api/posts/lookup/introduction) endpoint and the `tweet.fields=context_annotations` fields parameter to identify which domain.entity IDs we need to use in our query: +Find Japanese Posts about pets (not cats) with images using the `context:` operator: -* Posts that relate to cats return `domain` 66 (Interests and Hobbies category) with `entity` 852262932607926273 (Cats).  -* Posts that relate to pets return `domain` 65 (Interests and Hobbies Vertical) with `entity` 852262932607926273 (Pets).  -   +First, use [Post lookup](/x-api/posts/lookup/introduction) with `tweet.fields=context_annotations` to identify domain.entity IDs: -Here is what the rule would look like: +- Cats: `domain` 66, `entity` 852262932607926273 +- Pets: `domain` 65, `entity` 852262932607926273 ```json { - "value": "context:65.852262932607926273 -context:66.852262932607926273 -is:retweet has:images lang:ja", - "tag": "Japanese pets with images - no cats" + "value": "context:65.852262932607926273 -context:66.852262932607926273 -is:retweet has:images lang:ja", + "tag": "Japanese pets with images - no cats" } -``` \ No newline at end of file +``` + +--- + +## Next steps + + + + Complete list of available operators + + + Connect to your stream + + + Code examples in multiple languages + + diff --git a/x-api/posts/filtered-stream/introduction.mdx b/x-api/posts/filtered-stream/introduction.mdx index a3fdd44f0..ee8fac1d4 100644 --- a/x-api/posts/filtered-stream/introduction.mdx +++ b/x-api/posts/filtered-stream/introduction.mdx @@ -1,62 +1,200 @@ --- -title: Introduction +title: Filtered Stream sidebarTitle: Introduction +description: Stream real-time Posts matching your filter rules keywords: ["filtered stream", "streaming API", "real-time stream", "stream tweets", "filter rules", "streaming endpoint", "live tweets", "real-time data", "webhook stream"] --- import { Button } from '/snippets/button.mdx'; -The filtered stream endpoint group enables developers to filter a stream of public Posts. This endpoint group’s functionality includes multiple endpoints that enable you to create and manage rules, and apply those rules to filter a stream of Posts that will return matching public Posts. This endpoint group allows users to listen for specific topics and events, monitor the conversation around competitions, understand how trends develop, and much more. +The Filtered Stream endpoints let you receive real-time Posts that match your filter rules. Create rules using powerful operators, then connect to a persistent stream to receive matching Posts as they're published. + +## Overview + + + + Receive Posts within seconds of publication + + + Add and remove rules without disconnecting + + + Match on keywords, hashtags, users, and more + + + Optionally receive Posts via webhooks + + -Developers can use the REST [rules endpoint](/x-api/posts/filtered-stream#post-2-tweets-search-stream-rules) to add and remove rules to a persistent stream connection without needing to disconnect. These [rules](/x-api/posts/filtered-stream#building-rules-for-filtered-stream) can be created with operators that match on Post attributes such as message keywords, hashtags, and URLs. Operators and rule clauses can be combined with boolean logic and parentheses to help refine the filter’s matching behavior. +--- -Once you've added a set of rules, you can [establish a streaming connection](/x-api/posts/filtered-stream#get-2-tweets-search-stream) which will start to deliver [Post objects](/x-api/fundamentals/data-dictionary#tweet) in JSON format through a persistent HTTP Streaming connection. You will only receive content matching your rules while connected to the stream. +## How it works -Alternatively, you can link the Filtered Stream to a webhook for delivery as webhook events. For more details on using webhooks with Filtered Stream, see the [Filtered Stream Webhooks introduction](/x-api/webhooks/stream/introduction). +1. **Create rules** — Define filter rules using operators +2. **Connect to stream** — Establish a persistent HTTP connection +3. **Receive Posts** — Get matching Posts in real-time -The filtered search endpoint supports edited Posts. This endpoint will deliver edited Posts that match one or more of your filters, along with its edit history, including an array of Post IDs. For Posts with no edit history, this array will hold a single ID. For Posts that have been edited, this array contains multiple IDs, arranged in ascending order reflecting the order of edits, with the most recent version in the last position of the array. To learn more about how Post edits work, see the [Posts edits fundamentals](/x-api/fundamentals/edit-posts) page. +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Create/ │ │ Connect to │ │ Receive │ +│ manage │ → │ streaming │ → │ matching │ +│ rules │ │ endpoint │ │ Posts │ +└─────────────┘ └─────────────┘ └─────────────┘ +``` -Certain aspects of the filtered stream endpoint are limited by [access level](/x-api/getting-started/about-x-api): +--- -**Pro access** +## Endpoints -* 1000 rules per project -* 100 requests per 15 minutes when using the POST /2/tweets/search/stream/rules endpoint to add rules -* Can use all operators when building your rule -* Can build rules up to 1024 characters in length +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/tweets/search/stream`](/x-api/stream/stream-filtered-posts) | Connect to the stream | +| POST | [`/2/tweets/search/stream/rules`](/x-api/stream/update-stream-rules) | Add or delete rules | +| GET | [`/2/tweets/search/stream/rules`](/x-api/stream/get-stream-rules) | List current rules | +--- -**Enterprise access** +## Access levels -* 25,000+ rules per project -* Can use all operators when building your rule -* Can build rules up to 2048 characters in length -* Apply [here](https://developer.x.com/en/products/x-api/enterprise/enterprise-api-interest-form) for Enterprise access +| Feature | Pay-per-use | Enterprise | +|:--------|:------------|:-----------| +| Rules per project | 1,000 | 25,000+ | +| Rule length | 1,024 chars | 2,048 chars | +| Connections | 1 | Multiple | +| All operators | ✓ | ✓ | -The returned Posts from filtered stream count towards the monthly [Post cap](/x-api/fundamentals/post-cap). + + Get higher limits and additional features + - -**Account setup** +--- -To access these endpoints, you will need: +## Building rules -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects). +Rules use the same operators as search queries: -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +``` +(AI OR "machine learning") lang:en -is:retweet +``` + +### Example rules + +| Rule | Matches | +|:-----|:--------| +| `#python` | Posts with #python hashtag | +| `from:elonmusk` | Posts by @elonmusk | +| `"breaking news" has:images` | Posts with phrase and images | +| `(@XDevelopers OR @X) -is:retweet` | Mentions, excluding retweets | + + + Learn rule syntax and operators + + +--- + +## Connecting to the stream + +Establish a persistent HTTP connection to receive Posts: + +```python +import requests + +def stream_posts(bearer_token): + url = "https://api.x.com/2/tweets/search/stream" + headers = {"Authorization": f"Bearer {bearer_token}"} + + response = requests.get(url, headers=headers, stream=True) + + for line in response.iter_lines(): + if line: + print(line.decode("utf-8")) +``` + +### Keep-alive signals + +The stream sends blank lines (`\r\n`) every 20 seconds to maintain the connection. If you don't receive data or a keep-alive for 20 seconds, reconnect. + + + + Reconnect gracefully + + + Process Posts efficiently + + + +--- + +## Webhook delivery + +Instead of maintaining a persistent connection, you can receive Posts via webhooks: + + + Set up webhook delivery for filtered stream + + +--- + +## Post edits + +The stream delivers edited Posts with their edit history. Each edit creates a new Post ID: + +```json +{ + "data": { + "id": "1234567893", + "text": "Hello world! (edited)", + "edit_history_tweet_ids": ["1234567890", "1234567891", "1234567893"] + } +} +``` + + + Learn more about Post edits + + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [Bearer Token](/resources/fundamentals/authentication) -
- - - - -
\ No newline at end of file + + + Connect to the stream in minutes + + + Learn rule syntax + + + All available operators + + + Working code examples + + + +--- + +## Advanced topics + + + + Reconnect gracefully + + + Handle high throughput + + + Build resilient applications + + + Identify which rules matched + + diff --git a/x-api/posts/filtered-stream/migrate/overview.mdx b/x-api/posts/filtered-stream/migrate/overview.mdx index 2f1e04e06..c26912fc0 100644 --- a/x-api/posts/filtered-stream/migrate/overview.mdx +++ b/x-api/posts/filtered-stream/migrate/overview.mdx @@ -4,40 +4,39 @@ sidebarTitle: Overview keywords: ["filtered stream migration", "stream migration", "v1.1 to v2 filtered stream", "filtered stream migration guide", "migrate filtered stream"] --- -## Comparing X API’s filtered stream endpoints +## Comparing X API's filtered stream endpoints -The v2 filtered stream endpoints group is replacing the [standard v1.1 statuses/filter](https://developer.x.com/en/docs/x-api/v1/tweets/filter-realtime/api-reference/post-statuses-filter) and [PowerTrack API](/x-api/enterprise-gnip-2.0/powertrack-api). If you have code, apps, or tools that use an older version of the filtered stream endpoint, and are considering migrating to the newer X API v2 endpoint, then this comparison can help you get started.  +The v2 filtered stream endpoints group is replacing the [standard v1.1 statuses/filter](https://developer.x.com/en/docs/x-api/v1/tweets/filter-realtime/api-reference/post-statuses-filter) and [PowerTrack API](/x-api/enterprise-gnip-2.0/powertrack-api). If you have code, apps, or tools that use an older version of the filtered stream endpoint, and are considering migrating to the newer X API v2 endpoint, then this comparison can help you get started. See our more in depth migration guides for: -[Migrating from Standard v1.1 compared to X API v2](x-api/posts/filtered-stream#standard-v1-1-compared-to-x-api-v2) +[Migrating from Standard v1.1 compared to X API v2](x-api/posts/filtered-stream#standard-v1-1-compared-to-x-api-v2) -[Migrating from PowerTrack API migration to X API v2](/x-api/enterprise-gnip-2.0/powertrack-api#powertrack-operators) +[Migrating from PowerTrack API migration to X API v2](/x-api/enterprise-gnip-2.0/powertrack-api#powertrack-operators) The following table compares the filtered streaming endpoints X offers: -  + | **Description** | **Standard v1.1** | **PowerTrack API** | **X API v2** | | :--- | :--- | :--- | :--- | -| Access | X App | Requires an enterprise contract and account | Requires a developer account ([sign up](https://developer.x.com/en/portal/petition/essential/basic-info)), and [X App](/resources/fundamentals/developer-apps) within a [Project](/resources/fundamentals/projects) | +| Access | X App | Requires an enterprise contract and account | Requires a developer account ([sign up](https://developer.x.com/en/portal/petition/essential/basic-info)), and [X App](/resources/fundamentals/developer-apps) within a [Project](/resources/fundamentals/developer-apps) | | :--- | :--- | :--- | :--- | | Host domain | ******https://stream.x.com****** | ******https://gnip-stream.x.com****** | ******https://api.x.com****** | -| Endpoint path | ******1.1/statuses/filter.json****** | ******/stream/powertrack/accounts/{gnip_account_name}/publishers/twitter/{stream_label}.json******

******/rules/powertrack/accounts/{gnip_account_name}/publishers/twitter/{stream_label}.json ******

******/rules/powertrack/accounts/{gnip_account_name}/publishers/twitter/{stream_label}/validation.json****** | ******/2/tweets/search/stream******

******/2/tweets/search/stream/rules****** | +| Endpoint path | ******1.1/statuses/filter.json****** | ******/stream/powertrack/accounts/{gnip_account_name}/publishers/twitter/{stream_label}.json******

******/rules/powertrack/accounts/{gnip_account_name}/publishers/twitter/{stream_label}.json ******

******/rules/powertrack/accounts/{gnip_account_name}/publishers/twitter/{stream_label}/validation.json****** | ******/2/tweets/search/stream******

******/2/tweets/search/stream/rules****** | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | HTTP Basic Authentication | OAuth 2.0 App-Only | | HTTP methods supported | POST | GET
POST | GET
POST | -| Required parameters | Rule defined on connection as parameter, at least one of:

* ******follow******
* ******track******
* ******locations****** | No required parameters for streaming connection, optional backfill parameter.

Rules managed separately | No required parameters for streaming connection, optional parameters to define response format and add [backfill recovery feature](/x-api/posts/filtered-stream#recovering-missed-data-after-a-disconnection-backfill) for Academic Research access.

Rules managed separately | +| Required parameters | Rule defined on connection as parameter, at least one of:

* ******follow******
* ******track******
* ******locations****** | No required parameters for streaming connection, optional backfill parameter.

Rules managed separately | No required parameters for streaming connection, optional parameters to define response format and add [backfill recovery feature](/x-api/posts/filtered-stream#recovering-missed-data-after-a-disconnection-backfill) for Academic Research access.

Rules managed separately | | Delivery type | Streaming | Streaming

REST (for rules management) | Streaming

REST (for rules management) | -| Default request rate limits | 5 connection attempts per 5 min | 60 requests per min aggregated for both POST and GET requests

/rules:  60 requests per minute, aggregated across all requests to /rules endpoint for the specific stream’s API (POST and GET). | Depends on the endpoint and the [access level](/x-api/getting-started/about-x-api).

[GET /2/tweets/search/stream](/x-api/posts/filtered-stream#get-2-tweets-search-stream):
Pro - 50 requests per 15-minutes per App

[GET /2/tweets/search/stream/rules](/x-api/posts/filtered-stream#get-2-tweets-search-stream-rules):
Pro - 450 requests per 15-minutes per App

\-\-\-

[POST /2/tweets/search/stream/rules](/x-api/posts/adddelete-rules):
Pro - 100 requests per 15 minutes per App | -| Maximum allowed connections | 2 concurrent per authorized user | Supports multiple/redundant connections, determined by contract | Pro access:
1 | +| Default request rate limits | 5 connection attempts per 5 min | 60 requests per min aggregated for both POST and GET requests

/rules: 60 requests per minute, aggregated across all requests to /rules endpoint for the specific stream's API (POST and GET). | Depends on the endpoint. See [rate limits](/x-api/fundamentals/rate-limits) for current limits. | +| Maximum allowed connections | 2 concurrent per authorized user | Supports multiple/redundant connections, determined by contract | Pay-per-use: 1 | | [Recovery and redundancy features](/x-api/posts/filtered-stream#filtered-stream-recovery-and-redundancy-features) | None | Backfill, redundant connections, and the Replay API | | -| [Post caps](/x-api/fundamentals/post-cap) | Limited to 1% of firehose | Determined by contract | There is a monthly, Project-level Post cap applied to all Posts received from this endpoint:

Basic:
10,000 Posts

Pro:
1 million Posts | -| Keep-alive signal/heartbeats | blank lines (\\r\\n or similar) at least every 20 seconds | blank lines (\\r\\n or similar) every 10 seconds | blank lines (\\r\\n or similar) at least every 20 seconds | +| Keep-alive signal/heartbeats | blank lines (\\r\\n or similar) at least every 20 seconds | blank lines (\\r\\n or similar) every 10 seconds | blank lines (\\r\\n or similar) at least every 20 seconds | | Latency | 10 seconds | 2 seconds

At least 10 seconds for URL unwinding enrichment | 10 seconds | -| Maximum allowed rules | 1 rule (within the endpoint connection request) | Determined by contract up to 250,000 | Pro access:
1000 rules | -| Rule filter limitations | One query per connection, up to either:

\- 400 track keywords

\- 5000 follow user IDs

\- 25 location boxes | Up to 2,048 characters per rule | Pro Access: 
1,024 characters per rule | -| [Post JSON format](/x-api/fundamentals/data-dictionary) | Standard v1.1 format | [Native Enriched](/x-api/enterprise-gnip-2.0/fundamentals/data-dictionary#native-enriched-tweet-object) or [Activity Streams]() (selected within the [console](/x-api/enterprise-gnip-2.0/fundamentals/overview)) | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by ******fields****** and ******expansions****** request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). We will be releasing additional data format migration guides for Native Enriched and Activity Streams soon. | +| Maximum allowed rules | 1 rule (within the endpoint connection request) | Determined by contract up to 250,000 | Pay-per-use: 1,000 rules | +| Rule filter limitations | One query per connection, up to either:

\- 400 track keywords

\- 5000 follow user IDs

\- 25 location boxes | Up to 2,048 characters per rule | Pay-per-use: 1,024 characters per rule | +| [Post JSON format](/x-api/fundamentals/data-dictionary) | Standard v1.1 format | [Native Enriched](/x-api/enterprise-gnip-2.0/fundamentals/data-dictionary#native-enriched-tweet-object) or [Activity Streams]() (selected within the [console](/x-api/enterprise-gnip-2.0/fundamentals/overview)) | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by ******fields****** and ******expansions****** request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). We will be releasing additional data format migration guides for Native Enriched and Activity Streams soon. | | Provides Post edit history and metadata | ✔ | ✔ | ✔ | -| Unique Features | Filtering done via query parameters on connection request

No configuration UI | Filtering done via rules created through an independent endpoint

[Enrichment](/x-api/enterprise-gnip-2.0/enterprise-gnip#enrichments) features available in contract

Configuration on console.gnip.com UI | Filtering done via [rules](/x-api/posts/filtered-stream/integrate/build-a-rule) created through an independent endpoint

[Metrics](/x-api/fundamentals/metrics) and URL enrichment features included

Object [fields](/x-api/fundamentals/fields) and  [expansions](/x-api/fundamentals/expansions) specified with request parameters

Post [Annotations](/x-api/fundamentals/post-annotations)

[Conversation ID](/x-api/fundamentals/conversation-id) operator and field

Configuration through [developer portal](/resources/fundamentals/developer-portal) | +| Unique Features | Filtering done via query parameters on connection request

No configuration UI | Filtering done via rules created through an independent endpoint

[Enrichment](/x-api/enterprise-gnip-2.0/enterprise-gnip#enrichments) features available in contract

Configuration on console.gnip.com UI | Filtering done via [rules](/x-api/posts/filtered-stream/integrate/build-a-rule) created through an independent endpoint

[Metrics](/x-api/fundamentals/metrics) and URL enrichment features included

Object [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) specified with request parameters

Post [Annotations](/x-api/fundamentals/post-annotations)

[Conversation ID](/x-api/fundamentals/conversation-id) operator and field

Configuration through [Developer Console](/resources/fundamentals/developer-portal) | **Other migration resources** diff --git a/x-api/posts/filtered-stream/migrate/powertrack-api-migration-to-twitter-api-v2.mdx b/x-api/posts/filtered-stream/migrate/powertrack-api-migration-to-twitter-api-v2.mdx index bc9c05207..daa460e49 100644 --- a/x-api/posts/filtered-stream/migrate/powertrack-api-migration-to-twitter-api-v2.mdx +++ b/x-api/posts/filtered-stream/migrate/powertrack-api-migration-to-twitter-api-v2.mdx @@ -107,19 +107,19 @@ Enterprise access - 25000+ rules (please contact your designated account manager **App and Project requirements for v2 access** -PowerTrack access is granted through a contracted annual subscription for data, and set up through console.gnip.com by your account manager at X.  PowerTrack does not require a X developer App to access.  In order to use the X API v2 filter stream, you must have [signed up for a X developer account](https://developer.x.com/en/portal/petition/essential/basic-info), and a X [developer App](https://developer.x.com/en/portal/projects-and-apps.html) associated with a Project. The developer App and Project setup for X API v2 access is all done through the [developer portal](https://developer.x.com/en/portal/projects-and-apps).   +PowerTrack access is granted through a contracted annual subscription for data, and set up through console.gnip.com by your account manager at X.  PowerTrack does not require a X developer App to access.  In order to use the X API v2 filter stream, you must have [signed up for a X developer account](https://developer.x.com/en/portal/petition/essential/basic-info), and a X [developer App](https://developer.x.com/en/portal/projects-and-apps.html) associated with an App. The developer App and Project setup for X API v2 access is all done through the [Developer Console](https://developer.x.com/en/portal/projects-and-apps).   **Authentication method** The PowerTrack API endpoints use Basic Authentication set up in console.gnip.com. The X API v2 filtered stream endpoints require a X developer App and an [OAuth 2.0 App Access Token](/resources/fundamentals/authentication#oauth-2-0) (also referred to as Application-only or Bearer Authentication). To make requests to the X API v2 version you must use your specific developer App's Access Token to authenticate your requests. -In the process of setting up your developer account, developer App and Project, an App Access Token is created and shared within the dev portal user interface, however, you can generate a new one by navigating to your app's “Keys and tokens” page on the [developer portal](https://developer.x.com/en/portal/projects-and-apps). If you’d like to generate/destroy the App Access Tokens programmatically, see this [OAuth 2.0 App-Only guide](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). +In the process of setting up your developer account, developer App and Project, an App Access Token is created and shared within the dev portal user interface, however, you can generate a new one by navigating to your app's “Keys and tokens” page on the [Developer Console](https://developer.x.com/en/portal/projects-and-apps). If you’d like to generate/destroy the App Access Tokens programmatically, see this [OAuth 2.0 App-Only guide](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). **Usage tracking** PowerTrack usage can be retrieved programatically using the Usage API, or can be seen in console.gnip.com on the [usage tab](/x-api/enterprise-gnip-2.0/fundamentals/overview#usage-tab).  Post consumption across all PowerTrack streams is deduplicated each day and volume consumption is defined within the enterprise contract.  -X API v2 filtered stream usage can be tracked within the developer portal at the Project level. Post consumption is [set at the Project level](/resources/fundamentals/projects) and is shared across several different X API v2 endpoints, including filtered stream, recent search, user Post timeline and user mention timeline.  Currently with Basic Access, the monthly Post consumption limit is 500,000 Posts per month total. +X API v2 filtered stream usage can be tracked within the Developer Console at the Project level. Post consumption is [set at the Project level](/resources/fundamentals/developer-apps) and is shared across several different X API v2 endpoints, including filtered stream, recent search, user Post timeline and user mention timeline.  With pay-per-use pricing, you pay for the Posts you consume. **Multiple streams, redundant conections, backfill and Replay API for recovery** diff --git a/x-api/posts/filtered-stream/migrate/standard-to-twitter-api-v2.mdx b/x-api/posts/filtered-stream/migrate/standard-to-twitter-api-v2.mdx index 057fe82f1..af6985b55 100644 --- a/x-api/posts/filtered-stream/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/posts/filtered-stream/migrate/standard-to-twitter-api-v2.mdx @@ -51,13 +51,13 @@ Both versions provide metadata that describes any edit history. Check out the [f **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with an App. **Authentication method** The standard endpoint supports [OAuth 1.0a User Context](/resources/fundamentals/authentication), whereas the X API v2 filtered stream endpoints supports [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) (also referred to as Application-only authentication). To make requests to the X API v2 version you must use a App Access Token to authenticate your requests. -If you no longer have the App Access Token that was presented to you when you created your Project and app in the developer portal, you can generate a new one by navigating to your app's “Keys and tokens” page on the developer portal. If you’d like to generate an App Access Token programmatically, see this [OAuth 2.0 App-Only guide](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token).  +If you no longer have the App Access Token that was presented to you when you created your App and app in the Developer Console, you can generate a new one by navigating to your app's “Keys and tokens” page on the Developer Console. If you’d like to generate an App Access Token programmatically, see this [OAuth 2.0 App-Only guide](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token).  **Rule volume and persistent stream** @@ -119,6 +119,120 @@ X API v2 introduces new operators in support of two new features:  * **[Conversation IDs](/x-api/fundamentals/conversation-id)** \- As conversations unfold on X, a conservation ID will be available to mark Posts that are part of the conversation. All Posts in the conversation will have their conversation_id set to the Post ID that started it.  * conversation_id: -* **[X Annotations](/x-api/fundamentals/post-annotations)** provide contextual information about Posts, and include entity and context annotations. Entities are comprised of people, places, products and organizations. Contexts are domains, or topics, that surfaced entities are a part of. For example, people mentioned in a Post may have a context that indicates whether they are an athlete, actor, or politician.   - * context: \- matches on Posts that have been annotated with a context of interest.  - * entity: \- matches on Posts that have been annotated with an entity of interest.  +* **[X Annotations](/x-api/fundamentals/post-annotations)** provide contextual information about Posts, and include entity and context annotations. Entities are comprised of people, places, products and organizations. Contexts are domains, or topics, that surfaced entities are a part of. For example, people mentioned in a Post may have a context that indicates whether they are an athlete, actor, or politician. + * context: \- matches on Posts that have been annotated with a context of interest. + * entity: \- matches on Posts that have been annotated with an entity of interest. + +--- + +## Code examples + +### Add a rule to filtered stream (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"add": [{"value": "cat has:images", "tag": "cats with images"}]}' +``` + +```python Python +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/tweets/search/stream/rules" + +headers = { + "Authorization": f"Bearer {bearer_token}", + "Content-Type": "application/json" +} + +data = { + "add": [{"value": "cat has:images", "tag": "cats with images"}] +} + +response = requests.post(url, headers=headers, json=data) +print(response.json()) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Add a rule to filtered stream +response = client.posts.add_stream_rules( + add=[{"value": "cat has:images", "tag": "cats with images"}] +) +print(response.data) +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Add a rule to filtered stream +const response = await client.posts.addStreamRules({ + add: [{ value: "cat has:images", tag: "cats with images" }], +}); +console.log(response.data); +``` + + + +### Connect to filtered stream (v2) + + + +```bash cURL +curl "https://api.x.com/2/tweets/search/stream?tweet.fields=created_at,author_id" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/tweets/search/stream" + +params = {"tweet.fields": "created_at,author_id"} +headers = {"Authorization": f"Bearer {bearer_token}"} + +# Stream Posts in real-time +response = requests.get(url, headers=headers, params=params, stream=True) +for line in response.iter_lines(): + if line: + print(line.decode("utf-8")) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Connect to filtered stream +for post in client.posts.stream( + tweet_fields=["created_at", "author_id"] +): + print(f"{post.data.created_at}: {post.data.text[:50]}...") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Connect to filtered stream +const stream = client.posts.stream({ + tweetFields: ["created_at", "author_id"], +}); + +for await (const post of stream) { + console.log(`${post.data?.created_at}: ${post.data?.text?.slice(0, 50)}...`); +} +``` + + diff --git a/x-api/posts/filtered-stream/quickstart.mdx b/x-api/posts/filtered-stream/quickstart.mdx index f0896f3ac..5f1c4ae5d 100644 --- a/x-api/posts/filtered-stream/quickstart.mdx +++ b/x-api/posts/filtered-stream/quickstart.mdx @@ -1,201 +1,311 @@ --- title: Quickstart sidebarTitle: Quickstart +description: Connect to the filtered stream and receive real-time Posts keywords: ["filtered stream quickstart", "streaming quickstart", "filtered stream tutorial", "stream guide", "streaming example"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the filtered stream endpoints - -This quick start guide will help you make your first request to the filtered stream endpoint group using a cURL request. cURL is a command line tool which allows you to make requests with minimal configuration. - -If you would like to see sample code in different languages, please visit our[X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +This guide walks you through connecting to the filtered stream to receive real-time Posts matching your filter rules. **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- Your App's Bearer Token (found in the Developer Console under "Keys and tokens") -### Steps to build a filtered stream request using cURL +--- + + + + Rules define which Posts to receive. Use operators to match on keywords, hashtags, users, and more. + + **Example rule:** Match Posts containing "cat" with images: + + ``` + cat has:images + ``` + + + Learn rule syntax and operators + + + + + Add your rule using the rules endpoint. Include a `tag` to identify which rule matched each Post: + + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "add": [ + {"value": "cat has:images", "tag": "cats with images"} + ] + }' +``` + +```python Python SDK +from xdk import Client -**Step one: Create a rule** +client = Client(bearer_token="YOUR_BEARER_TOKEN") -Rules are made up of one or many different [operators](/x-api/posts/filtered-stream#building-rules-for-filtered-stream) that are combined using boolean logic and parentheses to help define which Posts will deliver to your stream. In this guide, we will filter the stream to find Posts that contain both the keyword “cat” and images. Here is our rule: +# Add a rule to the filtered stream +response = client.filtered_stream.add_rules( + add=[{"value": "cat has:images", "tag": "cats with images"}] +) -```cat has:images +for rule in response.data: + print(f"Rule added: {rule.id} - {rule.value}") ``` -**Step two: Add a tag to your rule** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -You can add multiple concurrent rules to your stream. When you open your streaming connection, Posts that match any of these rules will flow through the same streaming connection. To ensure that you know which Post matches which rule, you can pass a tag along with your rule creation request. Each Post that matches that rule will then include a tag field within the Post payload noting which rule it matched. For this rule, we are going to assign the following tag: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -```cats with images +// Add a rule to the filtered stream +const response = await client.filteredStream.addRules({ + add: [{ value: "cat has:images", tag: "cats with images" }], +}); + +response.data?.forEach((rule) => { + console.log(`Rule added: ${rule.id} - ${rule.value}`); +}); ``` -**Step three: Add your rule to the stream** + -This endpoint requires you to pass an application/JSON body along with this request that contains your rule and its tag. You will also notice that we have included the rule value and tag within an add object since we are trying to add this rule to the stream. This JSON body will look like this: + **Response:** -```json -{ - "add": [ + ```json { - "value": "cat has:images", - "tag": "cats with images" + "data": [ + { + "id": "1273026480692322304", + "value": "cat has:images", + "tag": "cats with images" + } + ], + "meta": { + "sent": "2024-01-15T10:30:00.000Z", + "summary": { + "created": 1, + "not_created": 0, + "valid": 1, + "invalid": 0 + } + } } - ] -} + ``` + + + + List all active rules to confirm your rule was added: + + + +```bash cURL +curl "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` -Now that you have fully set up this JSON body, you can add that to a cURL request, which will look like the following. This request isn't ready yet, so please hold off on submitting it until a later step. - -```json -curl -X POST 'https://api.x.com/2/tweets/search/stream/rules' \ --H "Content-type: application/json" \ --H "Authorization: Bearer $APP_ACCESS_TOKEN" -d \ -'{ - "add": [ - {"value": "cat has:images", "tag": "cats with images"} - ] -}'` +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get all active rules +response = client.filtered_stream.get_rules() + +for rule in response.data: + print(f"Active rule: {rule.id} - {rule.value}") ``` -**Step four: Authenticate your request** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Since the filtered stream rules endpoints require [OAuth 2.0 App-Only authentication](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), you will need to replace $APP\_ACCESS\_TOKEN in the cURL command from step three with the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) that you generated in the prerequisites.  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -**Step five: Add your rule to the stream** +// Get all active rules +const response = await client.filteredStream.getRules(); -Next step is to execute your cURL request, which will add the rule to your stream. Copy and paste the cURL command into your command line interface and press "return".  +response.data?.forEach((rule) => { + console.log(`Active rule: ${rule.id} - ${rule.value}`); +}); +``` -If your cURL request was successful, you’ll receive a response containing data about your value, tag, and id (serves as a unique identifier for your rule). This response will look similar to this: + -```json -{ - "data": [ - { - "value": "cat has:images", - "tag": "cats with images", - "id": "1273026480692322304" - } - ], - "meta": { - "sent": "2020-06-16T22:55:39.356Z", - "summary": { - "created": 1, - "not_created": 0, - "valid": 1, - "invalid": 0 - } - } -} + + + + Open a persistent connection to receive matching Posts: + + + +```bash cURL +curl "https://api.x.com/2/tweets/search/stream?\ +tweet.fields=created_at,author_id&\ +expansions=author_id&\ +user.fields=username" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` -You can validate that your rule was added successfully by sending the following GET request to the rules endpoint, once again making sure to replace $APP\_ACCESS\_TOKEN with your token. This request will return a full list of all the rules that have been added to your stream. +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") -```bash -curl -X GET 'https://api.x.com/2/tweets/search/stream/rules' \ --H "Authorization: Bearer $APP_ACCESS_TOKEN" +# Connect to the filtered stream +for post in client.filtered_stream.stream( + tweet_fields=["created_at", "author_id"], + expansions=["author_id"], + user_fields=["username"] +): + print(f"New post: {post.data.text}") + print(f"Matching rules: {post.matching_rules}") ``` -If your cURL request was successful, you should receive the following: -  -```json -{ - "data": [{ - "id": "1273028376882589696", - "value": "cat has:images", - "tag": "cats with images" - }], - "meta": { - "sent": "2020-06-16T23:14:06.498Z" - } +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Connect to the filtered stream +const stream = client.filteredStream.stream({ + tweetFields: ["created_at", "author_id"], + expansions: ["author_id"], + userFields: ["username"], +}); + +for await (const post of stream) { + console.log(`New post: ${post.data?.text}`); + console.log(`Matching rules: ${post.matching_rules}`); } ``` -**Step six: Identify and specify which fields you would like to retrieve** + -If you connect to the stream after step five, you will receive the default [Post object](/x-api/fundamentals/data-dictionary#tweet) fields in your response: id , text, and edit\_history\_tweet_ids. If you would like to receive additional fields beyond these, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. + -For this exercise, we will request a three different sets of fields from different objects: + + Matching Posts stream as JSON objects: -1. The additional tweet.created_at field in the primary Post objects. -2. The associated authors’ [user object’s](/x-api/fundamentals/data-dictionary#user) default fields for the returned Posts: id, name, and username -3. The additional  user.created_at field in the associated user objects. -   - -To request these fields, you will need to pass the following in your request: + ```json + { + "data": { + "id": "1234567890", + "text": "Look at this cute cat! 🐱", + "author_id": "9876543210", + "created_at": "2024-01-15T10:35:00.000Z", + "edit_history_tweet_ids": ["1234567890"] + }, + "includes": { + "users": [ + { + "id": "9876543210", + "username": "catperson" + } + ] + }, + "matching_rules": [ + { + "id": "1273026480692322304", + "tag": "cats with images" + } + ] + } + ``` -| | | | -| :--- | :--- | :--- | -| Key | Value | Returned fields | -| tweet.fields | created_at | tweets.created_at | -| expansions | author_id | includes.users.id, includes.users.name, includes.users.username | -| user.fields | created_at | includes.users.created_at | + + The `matching_rules` array shows which rules matched the Post, using the tags you defined. + + -Now that you know this, you can piece together your request URL to connect to the stream, which will look like this: + + Remove rules by their ID: + -```bash -https://api.x.com/2/tweets/search/stream?tweet.fields=created_at&expansions=author_id&user.fields=created_at +```bash cURL +curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "delete": { + "ids": ["1273026480692322304"] + } + }' ``` -**Step seven: Connect to the stream and review your response** +```python Python SDK +from xdk import Client -Now that your rule is in place and you’ve specified which fields you want returned, you’re ready to connect to the stream, which will deliver Post objects that match the rule you’ve submitted. This is what the cURL command looks like once you’ve added the URL from step six into your request: -  +client = Client(bearer_token="YOUR_BEARER_TOKEN") -```bash -curl -X GET -H "Authorization: Bearer $APP_ACCESS_TOKEN" "https://api.x.com/2/tweets/search/stream?tweet.fields=created_at&expansions=author_id&user.fields=created_at" +# Delete rules by ID +response = client.filtered_stream.delete_rules( + delete={"ids": ["1273026480692322304"]} +) +print(f"Deleted: {response.meta.summary.deleted}") ``` -Once again, this request must be authenticated using OAuth 2.0 App-Only, so make sure to replace $APP\_ACCESS\_TOKEN with your credentials before copying and pasting it into your command line tool. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Once you are connected to the filtered stream you will start to receive Posts that match your rules in the following JSON format: -  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -```json -{ - "data": [ - { - "author_id": "2244994945", - "created_at": "2022-09-14T19:00:55.000Z", - "id": "1228393702244134912", - "edit_history_tweet_ids": ["1228393702244134912"], - "text": "What did the developer write in their Valentine’s card?\n \nwhile(true) {\n I = Love(You); \n}" - }, - { - "author_id": "2244994945", - "created_at": "2022-09-12T17:09:56.000Z", - "id": "1227640996038684673", - "edit_history_tweet_ids": ["1227640996038684673"], - "text": "Doctors: Googling stuff online does not make you a doctor\n\nDevelopers: https://t.co/mrju5ypPkb" - }, - { - "author_id": "2244994945", - "created_at": "2022-09-27T20:26:41.000Z", - "id": "1199786642791452673", - "edit_history_tweet_ids": ["1199786642791452673"], - "text": "C#" - } - ], - "includes": { - "users": [ - { - "created_at": "2013-12-14T04:35:55.000Z", - "id": "2244994945", - "name": "Twitter Dev", - "username": "TwitterDev" - } - ] - } -} +// Delete rules by ID +const response = await client.filteredStream.deleteRules({ + delete: { ids: ["1273026480692322304"] }, +}); +console.log(`Deleted: ${response.meta?.summary?.deleted}`); ``` -If you would like to close your connection, you can press Control-C in your command line tool on either Mac or Windows systems to break the connection, or you can also close the window.  \ No newline at end of file + + + + + +--- + +## Managing your connection + + + + The stream sends blank lines (`\r\n`) every 20 seconds. If you don't receive data or a keep-alive for 20 seconds, reconnect. + + + + Press `Ctrl+C` to close the connection, or close your terminal window. + + + + Only one connection per App is allowed. Opening a new connection will close any existing one. + + + +--- + +## Next steps + + + + Learn rule syntax + + + All available operators + + + Reconnect gracefully + + + Full endpoint documentation + + diff --git a/x-api/posts/hide-replies/introduction.mdx b/x-api/posts/hide-replies/introduction.mdx index e47701d39..4a3123dab 100644 --- a/x-api/posts/hide-replies/introduction.mdx +++ b/x-api/posts/hide-replies/introduction.mdx @@ -1,32 +1,86 @@ --- -title: Introduction +title: Hide Replies sidebarTitle: Introduction -keywords: ["hide replies", "hide tweet replies", "moderate replies", "reply moderation", "hide conversation replies"] +description: Hide and unhide replies to Posts you authored +keywords: ["hide replies", "unhide replies", "reply moderation", "hide reply API", "manage replies"] --- import { Button } from '/snippets/button.mdx'; -This endpoint gives you the ability to programmatically hide or unhide replies using criteria you define. Just like the functionality in the main X experience, replies will be hidden from the main conversation but still visible on a separate page. You can use the endpoint to create apps to help people hide replies they don’t find valuable. +The Hide Replies endpoint lets you hide or unhide replies to Posts authored by the authenticated user. Hidden replies are still accessible but require an extra click to view. -The hide replies endpoint uses either [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) authentication. If successful, the endpoint hides a single Reply that was published in a Post conversation that was initiated by an authenticated user. Each conversation supports hiding up to 725 Posts. - -**Account setup** +## Overview + + + + Hide a reply to your Post + + + Unhide a previously hidden reply + + + Moderate discussions on your Posts + + + +--- + +## Endpoint -To access these endpoints, you will need: +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| PUT | [`/2/tweets/:tweet_id/hidden`](/x-api/posts/hide-reply) | Hide or unhide a reply | -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +--- + +## How it works + +Send a PUT request with `hidden: true` to hide a reply, or `hidden: false` to unhide it: + +```json +{ + "hidden": true +} +``` + +--- -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +## Example: Hide a reply + +```bash +curl -X PUT "https://api.x.com/2/tweets/1234567890/hidden" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"hidden": true}' +``` + +## Example response + +```json +{ + "data": { + "hidden": true + } +} +``` + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -
- - - -
\ No newline at end of file + + + + Hide your first reply + + + Full endpoint documentation + + diff --git a/x-api/posts/hide-replies/migrate.mdx b/x-api/posts/hide-replies/migrate.mdx index 1c9da3d06..4f2e8d34c 100644 --- a/x-api/posts/hide-replies/migrate.mdx +++ b/x-api/posts/hide-replies/migrate.mdx @@ -6,14 +6,14 @@ keywords: ["hide replies migration", "hide replies migrate", "migration guide", import { Button } from '/snippets/button.mdx'; -## Comparing X API’s hide replies endpoints +## Comparing X API's hide replies endpoints -The v2 hide replies endpoint is replacing the Labs hide replies endpoint. If you have code, apps, or tools that use the Labs version of this endpoint, and are considering migrating to the newer X API v2 endpoint, then this guide is for you. +The v2 hide replies endpoint is replacing the Labs hide replies endpoint. If you have code, apps, or tools that use the Labs version of this endpoint, and are considering migrating to the newer X API v2 endpoint, then this guide is for you. -In order to use the new X API v2 (including the hide replies endpoint), you will need to [opt in to the new developer portal](https://developer.x.com/en/portal/opt-in), create a [Project](/resources/fundamentals/projects), and add an App to that Project. You can then use the credentials associated with that App to make requests to the hide replies endpoint. Adding the same App that's enrolled for the Labs v2 hide replies endpoint will keep your users authenticated. +In order to use the new X API v2 (including the hide replies endpoint), you will need to [opt in to the new Developer Console](https://developer.x.com/en/portal/opt-in), create a [Project](/resources/fundamentals/developer-apps), and add an App to that Project. You can then use the credentials associated with that App to make requests to the hide replies endpoint. Adding the same App that's enrolled for the Labs v2 hide replies endpoint will keep your users authenticated. The following table compares the differences between Labs and the newer X API v2 endpoint: -  + | **Description** | **Labs v2** | **X API v2** | | :--- | :--- | :--- | @@ -25,8 +25,75 @@ The following table compares the differences between Labs and the newer X API v2 | Can hide replies | ✔︎ | ✔︎ | | Can unhide a previously hidden reply | ✔︎ | ✔︎ | | Can hide or unhide replies multiple times | ✔︎ | ✔︎ | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/developer-apps) | | ✔ | + +--- + +## Code examples + +### Hide a reply (v2) + + + +```bash cURL +curl -X PUT "https://api.x.com/2/tweets/1234567890/hidden" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"hidden": true}' +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/tweets/1234567890/hidden" +response = requests.put(url, auth=auth, json={"hidden": True}) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Hide a reply +response = client.posts.hide_reply("1234567890", hidden=True) +print(f"Hidden: {response.data.hidden}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Hide a reply +const response = await client.posts.hideReply("1234567890", { hidden: true }); +console.log(`Hidden: ${response.data?.hidden}`); +``` + + **Other migration resources** -[X API migration hub](/x-api/migrate/overview) \ No newline at end of file +[X API migration hub](/x-api/migrate/overview) diff --git a/x-api/posts/hide-replies/quickstart.mdx b/x-api/posts/hide-replies/quickstart.mdx index ee20a432c..bc1c0616a 100644 --- a/x-api/posts/hide-replies/quickstart.mdx +++ b/x-api/posts/hide-replies/quickstart.mdx @@ -1,73 +1,187 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["hide replies quickstart", "hide replies tutorial", "hide replies guide", "moderate replies", "reply moderation quickstart"] +description: Hide and unhide replies to your Posts +keywords: ["hide replies quickstart", "hide replies tutorial", "moderate replies guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the hide replies endpoint +This guide walks you through hiding and unhiding replies to Posts in conversations you started. -This quick start guide will help you make your first request to the hide replies endpoint using [Postman](/tutorials/postman-getting-started). - -If you would like to see some code snippets in different languages, please visit the [hide replies API Reference page](/x-api/posts/hide-replies#put-2-tweets-id-hidden).  -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build a PUT /tweets/:id/hidden request -#### Step one: Start with a tool or library +--- + +## Hide a reply + + + + Get the ID of the reply you want to hide. You can only hide replies to conversations started by the authenticated user. + + ``` + https://x.com/user/status/1232720193182412800 + └── This is the Post ID + ``` + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. + -To load X API v2 Postman collection into your environment, please click on the following button: + - -Once you have the X API v2 collection loaded in Postman, navigate to the hide replies endpoint. +```bash cURL +curl -X PUT "https://api.x.com/2/tweets/1232720193182412800/hidden" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"hidden": true}' +``` -#### Step two: Authenticate your request +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -In this example, we are going to use OAuth 1.0a User Context. +client = Client(auth=oauth1) -You must add your keys and tokens – specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret – to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +# Hide a reply +response = client.posts.hide_reply("1232720193182412800", hidden=True) +print(f"Hidden: {response.data.hidden}") +``` -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -#### Step three: Find a Post ID to hide +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -The hide replies endpoint can hide or unhide replies on behalf of an authorized user. Because we are using the Access Tokens related to your user profile in this example, you will be able to hide replies from users who participate in a conversation started by you. Similarly, if you were using Access Tokens that belong to another user that authorized your app, you would be able to moderate replies to any conversations started by that account. +const client = new Client({ oauth1 }); -Ask a friend to reply to a Post (let them know you're testing hide replies) or reply to any of your Posts from a test account. Click on that reply, then copy the numeric part of its URL. That will be the Post ID we will hide. +// Hide a reply +const response = await client.posts.hideReply("1232720193182412800", { + hidden: true, +}); +console.log(`Hidden: ${response.data?.hidden}`); +``` + + + + + + + ```json + { + "data": { + "hidden": true + } + } + ``` + + The reply is now hidden from the main conversation view. Users can still see it by clicking "View hidden replies." + + + +--- -In this case, we will be looking at the following Post, which has the ID `1232720193182412800`: +## Unhide a reply -`https://x.com/TwitterDev/status/1232720193182412800` +To make a hidden reply visible again: -#### Step four: Hide the Post + -In Postman, open the Hide replies folder and select Hide a reply. In the Params tab, paste the Post ID next to the id field (you won't need to replace :id in the URL). Click "Send" and you will see a successful response. +```bash cURL +curl -X PUT "https://api.x.com/2/tweets/1232720193182412800/hidden" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"hidden": false}' +``` - `{"hidden":true}` +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -#### Step five: Unhide the Post +client = Client(auth=oauth1) -Hidden Posts are moved to a separate tab in the X app. To unhide a Post in Postman, open the Hide replies folder and select Unhide a reply. In the Params tab, paste the same Post ID used in the previous step next into the id field. Click "Send" and you will see a successful response. +# Unhide a reply +response = client.posts.hide_reply("1232720193182412800", hidden=False) +print(f"Hidden: {response.data.hidden}") +``` - `{"hidden":false}` +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); +const client = new Client({ oauth1 }); -The hidden field represents the hidden status of the Post. A hidden status of true means the Post is hidden. Similarly, false means the Post is not hidden. +// Unhide a reply +const response = await client.posts.hideReply("1232720193182412800", { + hidden: false, +}); +console.log(`Hidden: ${response.data?.hidden}`); +``` + + + +**Response:** + +```json +{ + "data": { + "hidden": false + } +} +``` + +--- + +## Important notes + + +- You can only hide replies to conversations **you started** +- Hidden replies are still visible via "View hidden replies" +- The reply author is not notified when their reply is hidden + + +--- +## Next steps + + + + Moderate replies based on content + + + Moderate replies as they arrive + + + Full endpoint documentation + + diff --git a/x-api/posts/likes/introduction.mdx b/x-api/posts/likes/introduction.mdx index 832db2154..485770378 100644 --- a/x-api/posts/likes/introduction.mdx +++ b/x-api/posts/likes/introduction.mdx @@ -1,41 +1,99 @@ --- -title: Introduction +title: Likes sidebarTitle: Introduction +description: Like and unlike Posts, and retrieve like information keywords: ["likes", "like lookup", "liked tweets", "liking users", "get likes", "like endpoint", "favorites"] --- import { Button } from '/snippets/button.mdx'; +The Likes endpoints let you like and unlike Posts, see which users liked a Post, and get Posts liked by a user. + +## Overview + + + + Like a Post on behalf of a user + + + Remove a like from a Post + + + See who liked a Post + + + Get Posts a user has liked + + + +--- + +## Endpoints + ### Likes lookup -With endpoints in the Likes lookup group, you can retrieve a list of accounts that have liked a Post, or a list of Posts that an account has liked. These endpoints include: +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/tweets/:id/liking_users`](/x-api/posts/get-liking-users) | Get users who liked a Post | +| GET | [`/2/users/:id/liked_tweets`](/x-api/users/get-liked-posts) | Get Posts liked by a user | + +### Manage likes + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | [`/2/users/:id/likes`](/x-api/users/like-post) | Like a Post | +| DELETE | [`/2/users/:id/likes/:tweet_id`](/x-api/users/unlike-post) | Unlike a Post | -* Posts liked by a user - GET /2/users/:id/liked_tweets -* Users who have liked a Post - GET /2/tweets/:id/liking_users +--- + +## Important notes + + +The liking users endpoint returns a maximum of **100 users** per Post for all time, regardless of the actual number of likes. + + +--- + +## Example: Get liking users -You can authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Bearer Token](/resources/fundamentals/authentication#oauth-2-0). For the liked Posts endpoints, pagination tokens will be provided for paging through large sets of results. +```bash +curl "https://api.x.com/2/tweets/1234567890/liking_users?\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -The liking users endpoint limits you to a total of 100 liking accounts per post for all time.  Additionally, the liked Posts endpoint is also subject to the monthly [Post cap](/x-api/fundamentals/post-cap) applied at the Project level. -  +## Example: Like a Post -### Manage Likes +```bash +curl -X POST "https://api.x.com/2/users/123456789/likes" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1234567890"}' +``` + +--- -The manage Likes endpoints enable you to like or unlike a specified Post on behalf of an authenticated account. For this endpoint group, there are two methods available POST and DELETE. The POST method allows you to like a Post, and the DELETE method will enable you to unlike a Post. +## Getting started -Since you are making requests on behalf of a user, you must authenticate these endpoints with [OAuth 1.0a User Context](/resources/fundamentals/authentication) and use the Access Tokens associated with the user, which can be generated using the [3-legged OAuth flow](/resources/fundamentals/authentication#oauth-1-0a-2)/obtaining-user-access-tokens). You can like a Post from your account or an account of an authenticated user. With both endpoints, there is a user rate limit of 50 requests per 15 minutes per endpoint.  -  + +**Prerequisites** -To access these endpoint, you must have an approved [developer account](/resources/fundamentals/developer-portal). When authenticating, you must use keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + -Learn more about getting access to the X API v2 endpoints in our [getting started](/x-api/getting-started/about-x-api) page. -
- - - -
+ + + Get likes for a Post + + + Like and unlike Posts + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/posts/likes/migrate/likes-lookup-standard-to-twitter-api-v2.mdx b/x-api/posts/likes/migrate/likes-lookup-standard-to-twitter-api-v2.mdx index 0ab762273..98e3ad0d3 100644 --- a/x-api/posts/likes/migrate/likes-lookup-standard-to-twitter-api-v2.mdx +++ b/x-api/posts/likes/migrate/likes-lookup-standard-to-twitter-api-v2.mdx @@ -58,7 +58,7 @@ For the v2 liking users endpoint, you are limited to 100 liking users per Post. **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Request parameters** @@ -82,4 +82,123 @@ In addition to the changes that we made to our new JSON format, we also introduc * [conversation_id](/x-api/fundamentals/conversation-id) * Two new [Post annotations](/x-api/fundamentals/post-annotations) fields, including context and entities -* Several new [metrics](/x-api/fundamentals/metrics) fields  +* Several new [metrics](/x-api/fundamentals/metrics) fields + +--- + +## Code examples + +### Get liked Posts (v2) + + + +```bash cURL +curl "https://api.x.com/2/users/2244994945/liked_tweets?tweet.fields=created_at,public_metrics&max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/users/2244994945/liked_tweets" + +params = { + "tweet.fields": "created_at,public_metrics", + "max_results": 100 +} + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers, params=params) + +print(response.json()) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get user's liked Posts +for page in client.posts.get_liked_posts( + "2244994945", + tweet_fields=["created_at", "public_metrics"], + max_results=100 +): + for post in page.data: + print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get user's liked Posts +const paginator = client.posts.getLikedPosts("2244994945", { + tweetFields: ["created_at", "public_metrics"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); + }); +} +``` + + + +### Get liking users (v2) + + + +```bash cURL +curl "https://api.x.com/2/tweets/1234567890/liking_users?user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/tweets/1234567890/liking_users" + +params = {"user.fields": "username,verified"} + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers, params=params) + +print(response.json()) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get users who liked a Post +response = client.posts.get_liking_users( + "1234567890", + user_fields=["username", "verified"] +) +for user in response.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get users who liked a Post +const response = await client.posts.getLikingUsers("1234567890", { + userFields: ["username", "verified"], +}); + +response.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); +}); +``` + + diff --git a/x-api/posts/likes/migrate/manage-likes-standard-to-twitter-api-v2.mdx b/x-api/posts/likes/migrate/manage-likes-standard-to-twitter-api-v2.mdx index dc30fabe1..10fccc0dc 100644 --- a/x-api/posts/likes/migrate/manage-likes-standard-to-twitter-api-v2.mdx +++ b/x-api/posts/likes/migrate/manage-likes-standard-to-twitter-api-v2.mdx @@ -38,7 +38,7 @@ Both the endpoint versions support [OAuth 1.0a User Context](/resources/fundamen **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with an App. **Request parameters** @@ -52,4 +52,132 @@ The following standard v1.1 request parameters have equivalents in X API v2: Please note that the Standard v1.1 parameters are passed as query parameters, whereas the X API v2 parameters are passed as body parameters for the POST endpoint or path parameters for the DELETE endpoint. -Also, an id of the user liking a Post is not required when using the standard v1.1 endpoints since the [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) passed with [OAuth 1.0a User Context](/resources/fundamentals/authentication) infer which user is initiating the like/unlike.  +Also, an id of the user liking a Post is not required when using the standard v1.1 endpoints since the [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) passed with [OAuth 1.0a User Context](/resources/fundamentals/authentication) infer which user is initiating the like/unlike. + +--- + +## Code examples + +### Like a Post (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/likes" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1234567890"}' +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/likes" +response = requests.post(url, auth=auth, json={"tweet_id": "1234567890"}) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Like a Post +response = client.posts.like(user_id="123456789", tweet_id="1234567890") +print(f"Liked: {response.data.liked}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Like a Post +const response = await client.posts.like("123456789", { tweetId: "1234567890" }); +console.log(`Liked: ${response.data?.liked}`); +``` + + + +### Unlike a Post (v2) + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/likes/1234567890" \ + -H "Authorization: OAuth ..." +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/likes/1234567890" +response = requests.delete(url, auth=auth) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Unlike a Post +response = client.posts.unlike(user_id="123456789", tweet_id="1234567890") +print(f"Liked: {response.data.liked}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Unlike a Post +const response = await client.posts.unlike("123456789", "1234567890"); +console.log(`Liked: ${response.data?.liked}`); +``` + + diff --git a/x-api/posts/likes/migrate/overview.mdx b/x-api/posts/likes/migrate/overview.mdx index e5d86bc5b..d53d0da26 100644 --- a/x-api/posts/likes/migrate/overview.mdx +++ b/x-api/posts/likes/migrate/overview.mdx @@ -11,7 +11,7 @@ These guides will focus on the following areas: * **API request parameters** - The X API v2 endpoint introduces a new set of request parameters. While some parameters will be familiar, especially for those integrating with Labs, there are many important differences such as the introduction of the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. -* **App and Project requirements** \- To access the X API v2, you will need to use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) +* **App and Project requirements** \- To access the X API v2, you will need to use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) #### Likes lookup **Users who have liked a Post** diff --git a/x-api/posts/likes/quickstart/likes-lookup.mdx b/x-api/posts/likes/quickstart/likes-lookup.mdx index f54dab698..598da4fc2 100644 --- a/x-api/posts/likes/quickstart/likes-lookup.mdx +++ b/x-api/posts/likes/quickstart/likes-lookup.mdx @@ -1,194 +1,234 @@ --- -title: Likes lookup -sidebarTitle: Likes lookup +title: Likes Lookup +sidebarTitle: Likes Lookup +description: Get users who liked a Post and Posts liked by a user keywords: ["likes lookup", "likes quickstart", "get likes", "likes tutorial", "likes guide", "likes example"] --- import { Button } from '/snippets/button.mdx'; -### Getting started with the Likes lookup endpoint +This guide walks you through retrieving likes data using the X API. -This quick start guide will help you make your first request to the Likes lookup endpoint using [Postman](/tutorials/postman-getting-started). + +**Prerequisites** -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository. -  +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token (for public data) or User Access Token (for private metrics) + -#### Prerequisites - -For you to be able to complete this guide, you will have need to have a set of [keys and tokens](/resources/fundamentals/authentication), which you can generate by following these steps: - -1. [Apply for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -2. Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. - -3. Navigate to your app's “Keys and tokens” page, and save your API Keys, Access Tokens, and Bearer Token to your password manager. - - -#### Steps to build a Likes lookup request - -**Step one: Start with a tool or library** - -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. - -To load the X API v2 Postman collection into your environment, please click on the following button: - -[![](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/m1_vnext/carat.svg)
Add X API v2 to Postman](https://app.getpostman.com/run-collection/9956214-784efcda-ed4c-4491-a4c0-a26470a67400) - -Once you have the X API v2 collection loaded in Postman, navigate to the "Likes" folder and select "Liking users.”  -  - -**Step two: Authenticate your request** - -To make a successful request to this endpoint, you will need to use either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Bearer Token](/resources/fundamentals/authentication#oauth-2-0) authentication. To do this, you must add the following keys and tokens to Postman by selecting the environment named "X API v2", and adding the following variables to the Initial value and Current value fields: - -* consumer_key with your API Key -* consumer_secret with your API Key Secret -* access_token with your Access Token -* token_secret with your Access Token Secret -   - -**Step three: Specify a Tweet** - -With this endpoint, you must specify the Tweet ID that you want to get liking users for. You can find the ID of a Tweet by navigating to that Tweet on Twitter and pulling the numerical code at the end of the URL. For example, the following URL's Tweet ID is 1354143047324299264. - -https://x.com/TwitterDev/status/1354143047324299264 - -In Postman, navigate to the "Params" tab and enter this username into the "Value" column of the tweet_id path variable (at the bottom of the section), making sure to not include any spaces before or after usernames.  - -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | The Tweet ID you want to get the liking users of | - -**Step four: Identify and specify which fields you would like to retrieve** +--- -If you click the "Send" button after step three, you will receive the default [user object](/x-api/fundamentals/data-dictionary#user) fields in your response: id, name, and username. +## Get users who liked a Post -If you would like to receive additional fields beyond id, name, and username, you will have to specify those fields in your request with the [fields](/x-api/fundamentals/fields) and/or [expansions](/x-api/fundamentals/expansions) parameters. +Retrieve the list of users who liked a specific Post: -For this exercise, we will request three additional sets of fields from different objects: + -1. The additional user.created_at field in the primary user objects. -2. The associated pinned Tweets’ object’s default fields for the returned users: id and text. -3. The additional  tweet.created_at field in the associated Tweet objects. +```bash cURL +curl "https://api.x.com/2/tweets/1354143047324299264/liking_users?\ +user.fields=created_at,username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +```python Python SDK +from xdk import Client -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| user.fields | created_at | user.created_at | -| expansions | pinned\_tweet\_id | tweet.id, tweet.text | -| tweet.fields | created_at | tweet.created_at | +client = Client(bearer_token="YOUR_BEARER_TOKEN") -You should now see the following URL next to the "Send" button: +# Get users who liked a Post with pagination +for page in client.posts.get_liking_users( + "1354143047324299264", + user_fields=["created_at", "username", "verified"] +): + for user in page.data: + print(f"{user.username} - Joined: {user.created_at}") +``` - `https://api.x.com/2/tweets/1354143047324299264/liking_users?user.fields=created_at&expansions=pinned_tweet_id&tweet.fields=created_at` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); +// Get users who liked a Post with pagination +const paginator = client.posts.getLikingUsers("1354143047324299264", { + userFields: ["created_at", "username", "verified"], +}); +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Joined: ${user.created_at}`); + }); +} +``` -**Step five: Make your request and review your response** + -Once you have everything set up, hit the "Send" button and you will receive a similar response to the following example response: +### Response -``` +```json { "data": [ { "created_at": "2008-12-04T18:51:57.000Z", "id": "17874544", "username": "TwitterSupport", - "name": "Twitter Support" + "name": "Twitter Support", + "verified": true }, { "created_at": "2007-02-20T14:35:54.000Z", "id": "783214", "username": "Twitter", - "name": "Twitter" - }, - { - "pinned_tweet_id": "1389270063807598594", - "created_at": "2018-11-21T14:24:58.000Z", - "id": "1065249714214457345", - "username": "TwitterSpaces", - "name": "Spaces" - }, - { - "pinned_tweet_id": "1293595870563381249", - "created_at": "2007-05-23T06:01:13.000Z", - "id": "6253282", - "username": "XAPI", - "name": "X API" + "name": "Twitter", + "verified": true } ], - "includes": { - "tweets": [ - { - "created_at": "2021-05-03T17:26:09.000Z", - "id": "1389270063807598594", - "text": "now, everyone with 600 or more followers can host a Space.\n\nbased on what we've learned, these accounts are likely to have a good experience hosting because of their existing audience. before bringing the ability to create a Space to everyone, we’re focused on a few things. 🧵" - }, - { - "created_at": "2020-08-12T17:11:04.000Z", - "id": "1293595870563381249", - "text": "X API v2: Early Access released\n\nToday we announced Early Access to the first endpoints of the new Twitter API!\n\n#TwitterAPI #EarlyAccess #VersionBump https://t.co/g7v3aeIbtQ" - } - ] + "meta": { + "result_count": 2, + "next_token": "7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" } } ``` -**Step six: Get a user’s liked Tweets** +--- -You might also want to make a request to get a user’s liked Tweets as well. With the Likes lookup endpoint, you can get information about a user’s liked Tweets. To do this navigate to the "Likes" folder and select "Liked Tweets”.  +## Get a user's liked Posts -With this endpoint, you must specify the User ID that you want to get liking users for. You can use the [user lookup](/x-api/users/lookup/introduction) endpoint to get this information. +Retrieve Posts that a specific user has liked: -In Postman, navigate to the "Params" tab and enter this username into the "Value" column of the id path variable (at the bottom of the section), making sure to not include any spaces before or after usernames.  + -| | | -| :--- | :--- | -| **Key** | **Value** | -| id | The user ID you want to get the liked Tweets of | -| max_results | 5 | +```bash cURL +curl "https://api.x.com/2/users/2244994945/liked_tweets?\ +tweet.fields=created_at,public_metrics&\ +max_results=10" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -You can now see a similar URL with your ID instead of TwitterDev’s next to the "Send" button: -  +```python Python SDK +from xdk import Client - `https://api.x.com/2/users/2244994945/liked_tweets?max_results=5` +client = Client(bearer_token="YOUR_BEARER_TOKEN") +# Get a user's liked Posts with pagination +for page in client.users.get_liked_tweets( + "2244994945", + tweet_fields=["created_at", "public_metrics"], + max_results=10 +): + for post in page.data: + print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") +``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); +// Get a user's liked Posts with pagination +const paginator = client.users.getLikedTweets("2244994945", { + tweetFields: ["created_at", "public_metrics"], + maxResults: 10, +}); -Once you have everything set up, hit the "Send" button and you will receive a similar response to the following example response: +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); + }); +} ``` + + + +### Response + +```json { "data": [ { "id": "1362449997430542337", - "text": "Honored to be the first developer to be featured in @TwitterDev's love fest 🥰♥️😍 https://t.co/g8TsPoZsij" - }, - { - "id": "1365416026435854338", - "text": "We're so happy for our Official Partner @Brandwatch and their big news. https://t.co/3DwWBNSq0o https://t.co/bDUGbgPkKO" - }, - { - "id": "1296487407475462144", - "text": "Check out this feature on @TwitterDev to learn more about how we're mining social media data to make sense of this evolving #publichealth crisis https://t.co/sIFLXRSvEX." - }, - { - "id": "1294346980072624128", - "text": "I awake from five years of slumber https://t.co/OEPVyAFcfB" - }, - { - "id": "1283153843367206912", - "text": "@wongmjane Wish we could tell you more, but I’m only a teapot 👀" + "text": "Honored to be the first developer to be featured...", + "created_at": "2021-02-18T17:45:00.000Z", + "public_metrics": { + "retweet_count": 5, + "reply_count": 2, + "like_count": 42, + "quote_count": 1 + } } ], "meta": { - "next_token": "7140dibdnow9c7btw4539n0vybdnx19ylpayqf16fjt4l", - "result_count": 5 + "result_count": 1, + "next_token": "7140dibdnow9c7btw4539n0vybdnx19ylpayqf16fjt4l" } } -``` \ No newline at end of file +``` + +--- + +## Include additional data + +Use expansions to get related data like pinned Posts: + + + +```bash cURL +curl "https://api.x.com/2/tweets/1354143047324299264/liking_users?\ +user.fields=created_at&\ +expansions=pinned_tweet_id&\ +tweet.fields=created_at" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get liking users with pinned Post expansion +for page in client.posts.get_liking_users( + "1354143047324299264", + user_fields=["created_at"], + expansions=["pinned_tweet_id"], + tweet_fields=["created_at"] +): + for user in page.data: + print(f"{user.username}") + # Pinned Posts are in page.includes.tweets +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get liking users with pinned Post expansion +const paginator = client.posts.getLikingUsers("1354143047324299264", { + userFields: ["created_at"], + expansions: ["pinned_tweet_id"], + tweetFields: ["created_at"], +}); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(user.username); + }); + // Pinned Posts are in page.includes?.tweets +} +``` + + + +--- + +## Next steps + + + + Like and unlike Posts + + + Full endpoint documentation + + diff --git a/x-api/posts/likes/quickstart/manage-likes.mdx b/x-api/posts/likes/quickstart/manage-likes.mdx index d9178bcb3..b17cd2a18 100644 --- a/x-api/posts/likes/quickstart/manage-likes.mdx +++ b/x-api/posts/likes/quickstart/manage-likes.mdx @@ -1,89 +1,182 @@ --- title: Manage Likes sidebarTitle: Manage Likes +description: Like and unlike Posts using the X API keywords: ["manage likes", "like posts", "unlike", "likes quickstart", "likes tutorial", "likes guide"] --- import { Button } from '/snippets/button.mdx'; -### Getting started with the manage Likes endpoints +This guide walks you through liking and unliking Posts using the X API. -This quick start guide will help you make your first request to the manage Likes endpoints using [Postman](/tutorials/postman-getting-started). + +**Prerequisites** -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) + +--- + +## Like a Post + + + + You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). + -#### Prerequisites + + Find the Post ID in the URL when viewing a Post: -For you to be able to complete this guide, you will have need to have a set of [keys and tokens](/resources/fundamentals/authentication), which you can generate by following these steps: + ``` + https://x.com/XDevelopers/status/1228393702244134912 + └── This is the Post ID + ``` + -1. [Apply for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -2. Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. + -3. Navigate to your app's “Keys and tokens” page, and save your API Keys, Access Tokens, and Bearer Token to your password manager. + +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/likes" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1228393702244134912"}' +``` -#### Steps to build a manage Likes request +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -**Step one: Start with a tool or library** +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +client = Client(auth=oauth1) -To load the X API v2 Postman collection into your environment, please click on the following button: +# Like a Post +response = client.posts.like( + user_id="123456789", + tweet_id="1228393702244134912" +) -[![](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/m1_vnext/carat.svg)
Add X API v2 to Postman](https://app.getpostman.com/run-collection/9956214-784efcda-ed4c-4491-a4c0-a26470a67400) +print(f"Liked: {response.data.liked}") +``` -Once you have the X API v2 collection loaded in Postman, navigate to the “Likes” folder, and select “Like a Post”. +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -**Step two: Authenticate your request** +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -To make a successful request to this endpoint, you will need to use [OAuth 1.0a User Context](/resources/fundamentals/authentication). To do this, you must add the following keys and tokens to Postman by selecting the environment named “X API v2”, and adding the following variables to the initial value and current value fields: +const client = new Client({ oauth1 }); -* consumer_key with your API Key -* consumer_secret with your API Key Secret -* access_token with your Access Token -* token_secret with your Access Token Secret -   +// Like a Post +const response = await client.posts.like("123456789", { + tweetId: "1228393702244134912", +}); -**Step three: Specify which Post you are going to like** +console.log(`Liked: ${response.data?.liked}`); +``` -Manage Likes endpoints require two IDs: one for the user (the user who wishes to like a Post), and the other representing the  Post ID that the user is trying to like or unlike.  +
-The user’s ID must correspond to the authenticating user’s ID, meaning that you must pass the [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) associated with the user ID when authenticating your request.  In this case, you can specify the ID belonging to your own user. You can find your ID in two ways: +
-1. Using the [user lookup by username](/x-api/users/user-lookup-by-id) endpoint, you can pass a username and receive the id field.  -2. Looking at your Access Token, you will find that the numeric part is your user ID. -   + + ```json + { + "data": { + "liked": true + } + } + ``` + +
-You also must specify a Post that you want to like. You can find the Post ID by navigating to x.com and clicking on a Post and then looking in the URL. For example, the following URL's Post ID is 1228393702244134912. +--- -https://x.com/TwitterDev/status/1228393702244134912 +## Unlike a Post -In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the id path variable. Navigate to the “Body” tab and ID of the Post you wish to like as the value for the tweet_id parameter. Be sure not to include any spaces before or after any ID. +Remove a like from a Post: -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | (your user ID) | -| tweet_id | (the ID of the Post you want to like) | + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/likes/1228393702244134912" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -If you click the "Send" button, you will receive a response object containing the status of the relationship: +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -* If you receive a "liked": true, then the id is successfully liking the tweet_id. -   +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -**Step four: Make your request and review your response** +client = Client(auth=oauth1) -Once you have everything set up, hit the "Send" button and you will receive the following response: +# Unlike a Post +response = client.posts.unlike( + user_id="123456789", + tweet_id="1228393702244134912" +) +print(f"Liked: {response.data.liked}") ``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Unlike a Post +const response = await client.posts.unlike("123456789", "1228393702244134912"); + +console.log(`Liked: ${response.data?.liked}`); +``` + + + +**Response:** + +```json { - "data": { - "liked": true - } + "data": { + "liked": false + } } ``` -If you wish to unlike the same Post you can use the request entitled “Unlike a Post”, which is also found in the “Likes” folder of the X API v2 collection in Postman. The id and tweet_id should be used in the same way as the previous example.  To unlike a Post, you will not have to add this as a JSON body so you will want to make sure that you add in the requisite query params for id and tweet_id. +--- + +## Next steps + + + Get users who liked a Post + + + Full endpoint documentation + + diff --git a/x-api/posts/lookup/integrate.mdx b/x-api/posts/lookup/integrate.mdx index c1ad605c1..7fdb77247 100644 --- a/x-api/posts/lookup/integrate.mdx +++ b/x-api/posts/lookup/integrate.mdx @@ -1,111 +1,283 @@ --- -title: Integration guide -sidebarTitle: Integration guide +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for integrating Post lookup keywords: ["post lookup integration", "lookup integration guide", "lookup implementation", "lookup setup", "tweet lookup integration"] --- import { Button } from "/snippets/button.mdx" -This page contains information on several tools and key concepts to help you integrate the Posts lookup endpoints into your system. We’ve organized the page into a few sections: +This guide covers the key concepts you need to integrate the Post lookup endpoints into your application. -- [Helpful tools](#helpful) -- Key Concepts - - [Authentication](#authentication) - - [Developer portal, Projects, and Apps](#portal) - - [Rate limits](#limits) - - [Fields and expansions](#fields) - - [Post edits](#edits) - - [Edge cases](#edge) - -## Helpful tools - -Before we dive into some key concepts, we recommend familiarizing yourself with the following tools: - -**Postman** -Postman is an excellent tool to test out an endpoint, including every path and body parameter to help you understand what’s available. Check out our [getting started with Postman guide](/tutorials/postman-getting-started) to learn more. - -**Code samples** -Find code samples for your preferred programming language on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +--- -**Third-party libraries** -Utilize community-built [third-party libraries](/resources/tools-and-libraries) compatible with v2 endpoints. +## Authentication +All X API v2 endpoints require authentication. Choose the method that fits your use case: -## Key Concepts +| Method | Best for | Can access private metrics? | +|:-------|:---------|:---------------------------| +| [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Server-to-server, public data | No | +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | User-facing apps | Yes (for authorized user's Posts) | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy integrations | Yes (for authorized user's Posts) | -### Authentication +### App-Only authentication -All X API v2 endpoints require authenticated requests. You can authenticate with either: +For public Post data, use a Bearer Token: -- [OAuth 1.0a User Context](/resources/fundamentals/authentication) using API Keys, Access Tokens, and additional parameters to [create an authorization header](/resources/fundamentals/authentication#authorizing-a-request). -- [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) by passing an [App Access Token](/resources/fundamentals/authentication) with your request. -- [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication) for greater control over app scope and multi-device authorization. + - -OAuth 1.0a can be challenging to implement. If unfamiliar, consider using a [library](/resources/tools-and-libraries) or OAuth 2.0 for requests. For private metrics or Posts, use OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE. - +```bash cURL +curl "https://api.x.com/2/tweets/1234567890" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` +```python Python SDK +from xdk import Client - -**Please note** -> If you are requesting the following fields, OAuth 1.0a User Context or OAuth 2.0 Authorization Code is required: -> -> - `tweet.fields.non_public_metrics` -> - `tweet.fields.promoted_metrics` -> - `tweet.fields.organic_metrics` -> - `media.fields.non_public_metrics` -> - `media.fields.promoted_metrics` -> - `media.fields.organic_metrics` - +client = Client(bearer_token="YOUR_BEARER_TOKEN") +# Get a single Post by ID +response = client.posts.get("1234567890") +print(response.data) +``` -### Developer portal, Projects, and Apps +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -To obtain credentials for X API v2, you need: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -1. An approved [developer account](/resources/fundamentals/developer-portal). -2. A [Project](/resources/fundamentals/projects) within the developer account. -3. A [developer App](/resources/fundamentals/developer-apps) within that Project, where keys and tokens can be found. +const response = await client.posts.get("1234567890"); +console.log(response.data); +``` + +### User Context authentication -### Rate limits +To access private metrics, authenticate on behalf of the Post author: -X API requests are subject to [rate limits](/resources/fundamentals/rate-limits) to manage volume. Limits apply at both the App and user levels: + +The following fields require User Context authentication: +- `tweet.fields.non_public_metrics` +- `tweet.fields.promoted_metrics` +- `tweet.fields.organic_metrics` +- `media.fields.non_public_metrics` +- `media.fields.promoted_metrics` +- `media.fields.organic_metrics` + -- **App-level**: Limits the number of requests made per period by any app. -- **User-level**: Limits how frequently an authenticated user can perform Post lookups across developer Apps. +--- +## Fields and expansions + +The X API v2 returns minimal data by default. Use `fields` and `expansions` to request exactly what you need. + +### Default response + +```json +{ + "data": { + "id": "1234567890", + "text": "Hello world!", + "edit_history_tweet_ids": ["1234567890"] + } +} +``` + +### Available fields + + +| Field | Description | +|:------|:------------| +| `created_at` | Post creation timestamp | +| `author_id` | Author's user ID | +| `public_metrics` | Like, retweet, reply, quote counts | +| `entities` | Hashtags, mentions, URLs, cashtags | +| `attachments` | Media keys, poll IDs | +| `conversation_id` | Thread identifier | +| `context_annotations` | Topic/entity classifications | +| `in_reply_to_user_id` | User being replied to | +| `lang` | Detected language | +| `source` | Posting client | +| `possibly_sensitive` | Sensitive content flag | +| `reply_settings` | Who can reply | + + + +| Field | Description | +|:------|:------------| +| `username` | @handle | +| `name` | Display name | +| `profile_image_url` | Avatar URL | +| `verified` | Verification status | +| `description` | Bio | +| `public_metrics` | Follower/following counts | +| `created_at` | Account creation date | + + + +| Field | Description | +|:------|:------------| +| `url` | Media URL | +| `preview_image_url` | Thumbnail URL | +| `type` | photo, video, animated_gif | +| `duration_ms` | Video duration | +| `height`, `width` | Dimensions | +| `alt_text` | Accessibility text | + + +### Example with fields + + + +```bash cURL +curl "https://api.x.com/2/tweets/1234567890?\ +tweet.fields=created_at,public_metrics,entities&\ +expansions=author_id,attachments.media_keys&\ +user.fields=username,verified&\ +media.fields=url,type" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get a Post with additional fields and expansions +response = client.posts.get( + "1234567890", + tweet_fields=["created_at", "public_metrics", "entities"], + expansions=["author_id", "attachments.media_keys"], + user_fields=["username", "verified"], + media_fields=["url", "type"] +) + +print(response.data) +print(response.includes) # Contains expanded user and media objects +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +const response = await client.posts.get("1234567890", { + tweetFields: ["created_at", "public_metrics", "entities"], + expansions: ["author_id", "attachments.media_keys"], + userFields: ["username", "verified"], + mediaFields: ["url", "type"], +}); + +console.log(response.data); +console.log(response.includes); // Contains expanded user and media objects +``` + + +--- -### Fields and expansions +## Post edits -The X API v2 allows selection of specific data fields using `fields` and `expansions`: +Posts can be edited up to 5 times within 30 minutes of creation. -- **Expansions**: Enable retrieval of additional related objects. Supported expansions include: - - `edit_history_tweet_ids` - - `attachments.poll_ids` - - `attachments.media_keys` - - `author_id` - - `entities.mentions.username` - - `geo.place_id` - - `in_reply_to_user_id` - - `referenced_tweets.id` - - `referenced_tweets.id.author_id` +### How it works -- **Fields**: Specify data fields within objects to return additional data. The Post object defaults to `id`, `text`, and `edit_history_tweet_ids`. Other options, like `tweet.created_at` and `tweet.entities`, must be explicitly requested. +- Each edit creates a new Post ID +- `edit_history_tweet_ids` contains all versions (oldest first) +- The endpoint always returns the most recent version -For more, refer to the fields and expansions guide in the [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). +### Example response +```json +{ + "data": { + "id": "1234567893", + "text": "Hello world! (edited twice)", + "edit_history_tweet_ids": [ + "1234567890", + "1234567891", + "1234567893" + ] + } +} +``` + +Posts retrieved after their 30-minute edit window represent the final version. For real-time use cases, be aware that recently-published Posts may still be edited. + -### Post edits +--- -Eligible Posts can be edited up to five times within 30 minutes of publishing. The Posts lookup endpoint always provides the latest Post version. For near-real-time use cases, be aware of this time window. For more details, see [Edit Posts fundamentals](/x-api/fundamentals/edit-posts). +## Error handling + +### Common errors + +| Status | Error | Solution | +|:-------|:------|:---------| +| 400 | Invalid request | Check parameter formatting | +| 401 | Unauthorized | Verify authentication credentials | +| 403 | Forbidden | Check App permissions | +| 404 | Not Found | Post deleted or doesn't exist | +| 429 | Too Many Requests | Wait and retry (see rate limits) | + +### Deleted or protected Posts + +If a Post is deleted or from a protected account you don't follow: +- Single Post lookup returns `404` +- Multi-Post lookup omits the Post from results with an `errors` array + +```json +{ + "data": [ + { "id": "1234567890", "text": "Available post" } + ], + "errors": [ + { + "resource_id": "1234567891", + "resource_type": "tweet", + "title": "Not Found Error", + "detail": "Could not find tweet with id: [1234567891]." + } + ] +} +``` +--- +## Best practices + + + + Use the multi-Post endpoint to fetch up to 100 Posts at once, reducing API calls. + + + Specify only the fields you need to minimize response size and processing time. + + + Cache Post data locally to reduce repeated requests for the same content. + + + For real-time apps, consider re-fetching Posts after the 30-minute edit window. + + -### Edge cases +--- -- **Promoted metrics**: Requesting promoted metrics for non-promoted Posts returns an empty response. -- **Truncated text**: Post text is truncated for Retweets. To retrieve full text, expand the referenced Post \ No newline at end of file +## Next steps + + + + Complete endpoint documentation + + + All available objects and fields + + + Working code examples + + + Handle errors gracefully + + diff --git a/x-api/posts/lookup/introduction.mdx b/x-api/posts/lookup/introduction.mdx index 74d548c51..7908b5da8 100644 --- a/x-api/posts/lookup/introduction.mdx +++ b/x-api/posts/lookup/introduction.mdx @@ -1,47 +1,80 @@ --- -title: Introduction +title: Post Lookup sidebarTitle: Introduction +description: Retrieve Posts by ID to get details, verify availability, and examine edit history keywords: ["post lookup", "tweet lookup", "get tweet", "tweet by ID", "post by ID", "lookup endpoint", "tweet details", "post details", "edit history"] --- import { Button } from "/snippets/button.mdx" -The Post is one of the primary resources on X. In its simplest form, a Post can contain up to 280 characters and can be posted either publicly or privately, depending on an account’s settings. However, a variety of different objects can also be attached to Post, including media, a place, polls, and URLs. In addition, most Posts can be edited for up to 30 minutes after being created. +The Post lookup endpoints allow you to retrieve one or more Posts by their IDs. Use these endpoints to get up-to-date Post details, verify Post availability, or examine edit history. -While there are a variety of HTTP, selection, and delivery methods for Posts, this group of REST endpoints focuses on returning a Post or group of Posts, specified by a [Post ID](/resources/fundamentals/x-ids). These endpoints can receive up-to-date details on a Post, verify that a Post is available, and examine its edit history. They are also crucial for managing [compliance events](/x-api/enterprise-gnip-2.0/fundamentals/firehouse). +## Overview -The Post lookup endpoint provides edited Post metadata. All Posts created since September 29, 2022, include edit metadata, even if they were never edited. Each edit results in a new Post ID, with a Post's edit history represented by an array of Post IDs, beginning with the original. +Posts are the core content on X. Each Post can contain: +- Up to 280 characters of text +- Attached media (images, videos, GIFs) +- Polls, places, and URLs +- Replies, quotes, and mentions -This endpoint will always return the most recent edit along with its edit history. Any Post collected after its 30-minute edit window has expired will represent its final version. To learn more about Edit Post metadata, check out the [Edit Posts fundamentals](/x-api/fundamentals/edit-posts) page. +Posts can be edited up to 5 times within 30 minutes of creation. Each edit generates a new Post ID, and the edit history is preserved. -These endpoints use the GET HTTP method and return one or many [Posts objects](/x-api/fundamentals/data-dictionary), which include fields such as the Post text, creation timestamp, and lists and metadata of hashtags, mentions, and photos. + + + Retrieve a specific Post by ID + + + Retrieve up to 100 Posts in one request + + + Access the complete edit history of a Post + + + Include author, media, polls, and more + + --- - -### Account setup -To access these endpoints, you will need: +## Use cases -- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -- Authentication using keys and tokens from a [developer App](/resources/fundamentals/developer-apps) within a [Project](/resources/fundamentals/projects). +- **Display Post content** — Show Post details in your application +- **Verify availability** — Check if a Post still exists or was deleted +- **Compliance management** — Track Post changes for compliance events +- **Analytics** — Retrieve engagement metrics for specific Posts + +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/tweets/:id`](/x-api/posts/get-post-by-id) | Retrieve a single Post by ID | +| GET | [`/2/tweets`](/x-api/posts/get-posts-by-ids) | Retrieve multiple Posts by IDs (up to 100) | -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - --- -## Quick Links - -
- - - - -
\ No newline at end of file +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + + + + + Make your first Post lookup request + + + Learn key concepts and best practices + + + See full endpoint documentation + + + Explore code examples + + diff --git a/x-api/posts/lookup/migrate/overview.mdx b/x-api/posts/lookup/migrate/overview.mdx index 99a24c333..e86c95630 100644 --- a/x-api/posts/lookup/migrate/overview.mdx +++ b/x-api/posts/lookup/migrate/overview.mdx @@ -75,7 +75,7 @@ Both versions provide metadata that describes any edit history. Check out the Po #### App and Project Requirements -X API v2 endpoints require credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) for authentication. X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project. +X API v2 endpoints require credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) for authentication. X API v1.1 endpoints can use credentials from Apps or Apps associated with an App. #### Response Data Format @@ -122,7 +122,7 @@ Certain standard v1.1 parameters are **not** supported in X API v2: ### CURL Requests -The following cURL requests show standard v1.1 endpoints and their v2 equivalents. Replace `ACCESS_TOKEN` in the header with your app access token. For v2 endpoints, the token must belong to a [developer App](/resources/fundamentals/developer-apps/overview) within a [Project](/resources/fundamentals/projects). +The following cURL requests show standard v1.1 endpoints and their v2 equivalents. Replace `ACCESS_TOKEN` in the header with your app access token. For v2 endpoints, the token must belong to a [developer App](/resources/fundamentals/developer-apps/overview) within a [Project](/resources/fundamentals/developer-apps). The response payloads from v1.1 will differ from v2. With v2, you can request different fields with the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. diff --git a/x-api/posts/lookup/migrate/standard-to-twitter-api-v2.mdx b/x-api/posts/lookup/migrate/standard-to-twitter-api-v2.mdx index 8ff1a1534..09e171b39 100644 --- a/x-api/posts/lookup/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/posts/lookup/migrate/standard-to-twitter-api-v2.mdx @@ -51,7 +51,7 @@ Both versions provide metadata that describes any edit history. Check out the Po #### App and Project Requirements -X API v2 endpoints require credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) for authentication. X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project. +X API v2 endpoints require credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) for authentication. X API v1.1 endpoints can use credentials from Apps or Apps associated with an App. #### Response Data Format @@ -96,36 +96,151 @@ Certain standard v1.1 parameters are **not** supported in X API v2: | `include_card_uri` | Adds `card_uri` when an ads card is attached. | | `map` | Returns the Post ID and error message for unavailable Posts in X API v2, as opposed to nullified fields in v1.1. | -### CURL Requests +### Code Examples -The following cURL requests show standard v1.1 endpoints and their v2 equivalents. Replace `ACCESS_TOKEN` in the header with your app access token. For v2 endpoints, the token must belong to a [developer App](/resources/fundamentals/developer-apps/overview) within a [Project](/resources/fundamentals/projects/overview). +The following examples show standard v1.1 endpoints and their v2 equivalents. Replace credentials with your actual tokens. For v2 endpoints, the token must belong to a [developer App](/resources/fundamentals/developer-apps/overview) within a [Project](/resources/fundamentals/developer-apps/overview). The response payloads from v1.1 will differ from v2. With v2, you can request different fields with the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. -**Standard v1.1 `GET statuses/lookup` and v2 `GET /tweets` endpoints** +**Multiple Posts lookup: v1.1 `GET statuses/lookup` → v2 `GET /tweets`** -```bash + + +```bash cURL (v1.1) curl --request GET \ --url 'https://api.x.com/1.1/statuses/lookup.json?id=1460323737035677698%2C1460323743339741184' \ --header 'Authorization: Bearer $ACCESS_TOKEN' - ``` - - ``` - curl --request GET \ +``` + +```bash cURL (v2) +curl --request GET \ --url 'https://api.x.com/2/tweets?ids=1460323737035677698%2C1460323743339741184&tweet.fields=created_at&expansions=author_id&user.fields=created_at' \ --header 'Authorization: Bearer $ACCESS_TOKEN' - ``` +``` +```python Python (v2) +import requests -**Standard v1.1 `GET statuses/show/:id` and v2 `GET /tweets/:id` endpoints** +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/tweets" + +params = { + "ids": "1460323737035677698,1460323743339741184", + "tweet.fields": "created_at", + "expansions": "author_id", + "user.fields": "created_at" +} + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers, params=params) + +print(response.json()) ``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get multiple Posts by IDs with fields and expansions +response = client.posts.get_posts( + ids=["1460323737035677698", "1460323743339741184"], + tweet_fields=["created_at"], + expansions=["author_id"], + user_fields=["created_at"] +) + +for post in response.data: + print(f"Post: {post.text}") + print(f"Created at: {post.created_at}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get multiple Posts by IDs with fields and expansions +const response = await client.posts.getPosts({ + ids: ["1460323737035677698", "1460323743339741184"], + tweetFields: ["created_at"], + expansions: ["author_id"], + userFields: ["created_at"], +}); + +response.data?.forEach((post) => { + console.log(`Post: ${post.text}`); + console.log(`Created at: ${post.created_at}`); +}); +``` + + + +**Single Post lookup: v1.1 `GET statuses/show/:id` → v2 `GET /tweets/:id`** + + + +```bash cURL (v1.1) curl --request GET \ --url 'https://api.x.com/1.1/statuses/show.json?id=1460323737035677698' \ --header 'Authorization: Bearer $ACCESS_TOKEN' ``` -``` +```bash cURL (v2) curl --request GET \ --url 'https://api.x.com/2/tweets/1460323737035677698?tweet.fields=created_at&expansions=author_id&user.fields=created_at' \ --header 'Authorization: Bearer $ACCESS_TOKEN' -``` \ No newline at end of file +``` + +```python Python (v2) +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/tweets/1460323737035677698" + +params = { + "tweet.fields": "created_at", + "expansions": "author_id", + "user.fields": "created_at" +} + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers, params=params) + +print(response.json()) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get a single Post by ID +response = client.posts.get( + "1460323737035677698", + tweet_fields=["created_at"], + expansions=["author_id"], + user_fields=["created_at"] +) + +print(f"Post: {response.data.text}") +print(f"Created at: {response.data.created_at}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get a single Post by ID +const response = await client.posts.get("1460323737035677698", { + tweetFields: ["created_at"], + expansions: ["author_id"], + userFields: ["created_at"], +}); + +console.log(`Post: ${response.data?.text}`); +console.log(`Created at: ${response.data?.created_at}`); +``` + + \ No newline at end of file diff --git a/x-api/posts/lookup/quickstart.mdx b/x-api/posts/lookup/quickstart.mdx index 39d3fdddc..50a069d89 100644 --- a/x-api/posts/lookup/quickstart.mdx +++ b/x-api/posts/lookup/quickstart.mdx @@ -1,138 +1,220 @@ --- title: Quickstart sidebarTitle: Quickstart +description: Make your first Post lookup request in minutes keywords: ["post lookup quickstart", "tweet lookup quickstart", "lookup tutorial", "lookup guide", "get tweet quickstart", "lookup example"] --- import { Button } from "/snippets/button.mdx" -## Getting started with the Posts lookup endpoints +This guide walks you through making your first Post lookup request using the X API v2. -This quick start guide will help you make your first request to the Posts lookup endpoints with a set of specified fields using Postman. + +**Prerequisites** -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token (found in the Developer Console under "Keys and tokens") + - -### Prerequisites +--- -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: + + + Every Post has a unique ID. You can find it in the Post's URL: -- [Sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info) and receive approval. -- Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -- Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. - + ``` + https://x.com/XDevelopers/status/1228393702244134912 + └── This is the Post ID + ``` + + -### Steps to build a GET /tweets request + -#### Step one: Start with a tool or library +```bash cURL +curl "https://api.x.com/2/tweets/1228393702244134912" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +```python Python SDK +from xdk import Client -To load the X API v2 Postman collection into your environment, click the button below: +client = Client(bearer_token="YOUR_BEARER_TOKEN") +# Get a single Post by ID +response = client.posts.get("1228393702244134912") +print(response.data) +``` - +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Once you have the X API v2 collection loaded in Postman, navigate to the "Post Lookup > Multiple Posts" request. +const response = await client.posts.get("1228393702244134912"); +console.log(response.data); +``` -#### Step two: Authenticate your request + -To properly make a request to the X API, you need to verify that you have permission. You can authenticate your request with: + -- [OAuth 2.0 App-Only](resources/fundamentals/authentication) -- [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication) -- [OAuth 1.0a User Context](resources/fundamentals/authentication) + + The default response includes the Post's `id`, `text`, and `edit_history_tweet_ids`: -For simplicity's sake, we are going to utilize OAuth 2.0 App-Only with this request, but if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Posts, you will need to use one of the other authentication methods. + ```json + { + "data": { + "id": "1228393702244134912", + "text": "What did the developer write in their Valentine's card?\n\nwhile(true) {\n I = Love(You);\n}", + "edit_history_tweet_ids": ["1228393702244134912"] + } + } + ``` + -To utilize OAuth 2.0 App-Only, you must add your keys and tokens (and specifically the App Access Token, also known as the App-only Bearer Token) to Postman by selecting the environment named “X API v2” (in the top-right corner of Postman), and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). + + Use query parameters to get more data: -If you've done this correctly, these variables will automatically be pulled into the request's authorization tab. + +```bash cURL +curl "https://api.x.com/2/tweets/1228393702244134912?\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client -#### Step three: Identify and specify which Posts you would like to retrieve +client = Client(bearer_token="YOUR_BEARER_TOKEN") -You must specify a Post or a set of Posts that you would like to receive within the request. You can find the Post ID by navigating to X.com and clicking on a Post and then looking in the URL. For example, the following URL's Post ID is `1228393702244134912`. +# Get a Post with additional fields and expansions +response = client.posts.get( + "1228393702244134912", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id"], + user_fields=["username", "verified"] +) -`https://x.com/TwitterDev/status/1228393702244134912` +print(response.data) +print(response.includes) # Contains author user object +``` -In Postman, navigate to the "Params" tab and enter this ID, or a string of Post IDs separated by a comma, into the "Value" column of the ids parameter. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -| Key | Value | -| :---- | :----- | -| `ids` | `1228393702244134912,1227640996038684673,1199786642791452673` | +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); ---- +const response = await client.posts.get("1228393702244134912", { + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id"], + userFields: ["username", "verified"], +}); -#### Step four: Identify and specify which fields you would like to retrieve +console.log(response.data); +console.log(response.includes); // Contains author user object +``` -If you click the "Send" button after step three, you will receive the default [Post object fields](/x-api/fundamentals/data-dictionary) in your response: `id`, `text`, and `edit_history_tweet_ids`. + -For this exercise, we will request a three additional different sets of fields from different objects: +**Response:** -- The additional `tweet.created_at field` in the primary user objects. -- The associated authors’ user object’s default fields for the returned Posts: `id`, `name`, and `username` -- The additional `user.created_at field` in the associated user objects. +```json +{ + "data": { + "id": "1228393702244134912", + "text": "What did the developer write in their Valentine's card?...", + "created_at": "2020-02-14T19:00:55.000Z", + "author_id": "2244994945", + "public_metrics": { + "retweet_count": 156, + "reply_count": 23, + "like_count": 892, + "quote_count": 12 + }, + "edit_history_tweet_ids": ["1228393702244134912"] + }, + "includes": { + "users": [ + { + "id": "2244994945", + "username": "XDevelopers", + "verified": true + } + ] + } +} +``` -In Postman, navigate to the "Params" tab and add the following `key:value` pair to the "Query Params" table: -| Key | Value | Returned fields | -| :-------------- | :------------------- | :---------------------------------------- | -| `tweet.fields` | `created_at` | `tweets.created_at` | -| `expansions` | `author_id` | `includes.users.id`, `includes.users.name`, `includes.users.username` | -| `user.fields` | `created_at` | `includes.users.created_at` | + -You should see this URL next to the "Send" button: + + Retrieve up to 100 Posts in a single request: + + +```bash cURL +curl "https://api.x.com/2/tweets?\ +ids=1228393702244134912,1227640996038684673,1199786642791452673&\ +tweet.fields=created_at,author_id" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` -https://api.x.com/2/tweets?ids=1228393702244134912,1227640996038684673,1199786642791452673&tweet.fields=created_at&expansions=author_id&user.fields=created_at + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get multiple Posts by IDs +response = client.posts.get_posts( + ids=["1228393702244134912", "1227640996038684673", "1199786642791452673"], + tweet_fields=["created_at", "author_id"] +) + +for post in response.data: + print(f"{post.id}: {post.text[:50]}...") ``` -#### Step five: Make your request and review your response -Once you have everything set up, hit the "Send" button and you will receive the following response: +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +const response = await client.posts.getPosts({ + ids: ["1228393702244134912", "1227640996038684673", "1199786642791452673"], + tweetFields: ["created_at", "author_id"], +}); + +response.data?.forEach((post) => { + console.log(`${post.id}: ${post.text?.slice(0, 50)}...`); +}); ``` -{ - "data": [ - { - "edit_history_tweet_ids": [ - "1228393702244134912" - ], - "text": "What did the developer write in their Valentine’s card?\n \nwhile(true) {\n I = Love(You); \n}", - "id": "1228393702244134912", - "created_at": "2020-02-14T19:00:55.000Z", - "author_id": "2244994945" - }, - { - "edit_history_tweet_ids": [ - "1227640996038684673" - ], - "text": "Doctors: Googling stuff online does not make you a doctor\n\nDevelopers: https://t.co/mrju5ypPkb", - "id": "1227640996038684673", - "created_at": "2020-02-12T17:09:56.000Z", - "author_id": "2244994945" - }, - { - "edit_history_tweet_ids": [ - "1199786642791452673" - ], - "text": "C#", - "id": "1199786642791452673", - "created_at": "2019-11-27T20:26:41.000Z", - "author_id": "2244994945" - } - ], - "includes": { - "users": [ - { - "name": "Developers", - "created_at": "2013-12-14T04:35:55.000Z", - "id": "2244994945", - "username": "XDevelopers" - } - ] - } -} -``` \ No newline at end of file + + + + + + +--- + +## Next steps + + + + Learn authentication, rate limits, and best practices + + + Master the fields and expansions system + + + See all available parameters + + + Explore more examples + + diff --git a/x-api/posts/manage-tweets/integrate.mdx b/x-api/posts/manage-tweets/integrate.mdx index d2f6780fb..2453f120b 100644 --- a/x-api/posts/manage-tweets/integrate.mdx +++ b/x-api/posts/manage-tweets/integrate.mdx @@ -6,16 +6,9 @@ keywords: ["manage tweets integration", "tweet creation integration", "post twee import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and key concepts that you should be aware of as you integrate the manag Posts endpoints into your system. We’ve broken the page into a couple of different sections: +This page covers tools and key concepts for integrating the manage Posts endpoints into your system. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Source labels](#source)[](#source)[](#source) -* [Profile settings](#settings) -* [Adding media to a Post](#media) +--- ## Helpful tools @@ -23,58 +16,210 @@ Before we dive into some key concepts that will help you integrate this endpoint ### Postman -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  -  +Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. + ### Code samples -Interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). -  +Interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). + ### Third-party libraries -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. -  +Take advantage of one of our communities' [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. + ## Key concepts ### Authentication -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens.  +All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. These specific endpoints requires the use of [OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize or authenticate your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. +Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application's scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +To enable OAuth 2.0 in your App, you must enable it in your's App's authentication settings found in the App settings section of the Developer Console. -### Developer portal, Projects, and developer Apps +### Developer Console, Projects, and developer Apps -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have a [developer account]/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App.  -  +To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have a [developer account]/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. + ### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](https://developer.x.com/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. There is a user rate limit of 200 requests per 15 minutes for the POST method. The DELETE method has a rate limit of 50 requests for 15 minutes. Additionally, there is a limit of [300 requests per 3 hours](https://blog.x.com/developer/en_us/topics/tools/2018/new-developer-requirements-to-protect-our-platform), including Posts created with either manage Posts or manage Retweets. -  + ### Source labels Your App name and website URL will be shown as the [source label](https://help.x.com/en/using-twitter/how-to-tweet#source-labels) within metadata for any Posts created programmatically by your application. If you change the use case of a X App, be sure to update the use case in these settings in order to ensure you are in compliance with the [Developer Terms](https://developer.x.com/content/developer-twitter/en/developer-terms/agreement-and-policy). -  + ### Profile settings -You can only add a location to Posts if you have geo enabled in your profile settings. If you don’t have geo enabled, you can still add a location parameter in your request body, but it won’t get attached to your Post. The same is also true for tagging users in images. If the user you’re tagging doesn’t have photo-tagging enabled, their names won’t show up in the list of tagged users even though the Post is successfully created.  -  +You can only add a location to Posts if you have geo enabled in your profile settings. If you don't have geo enabled, you can still add a location parameter in your request body, but it won't get attached to your Post. The same is also true for tagging users in images. If the user you're tagging doesn't have photo-tagging enabled, their names won't show up in the list of tagged users even though the Post is successfully created. + ### Adding media to a Post -Currently, isn’t a way to fully upload media using v2 of the X API currently. However, you attach previously uploaded media to a Post. You can use media IDs that have been already [uploaded using the v1 media endpoint](https://developer.x.com/en/docs/twitter-api/v1/media/upload-media/api-reference/post-media-upload) or [X Media Studio](https://media.x.com/en/articles/products/2018/media-studio). These media ids must be your own or that of an authenticated user. +Currently, isn't a way to fully upload media using v2 of the X API currently. However, you attach previously uploaded media to a Post. You can use media IDs that have been already [uploaded using the v1 media endpoint](https://developer.x.com/en/docs/twitter-api/v1/media/upload-media/api-reference/post-media-upload) or [X Media Studio](https://media.x.com/en/articles/products/2018/media-studio). These media ids must be your own or that of an authenticated user. + +--- + +## Code examples + +### Create a Post + + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello world!"}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Create a Post +response = client.posts.create(text="Hello world!") +print(f"Created Post: {response.data.id}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Create a Post +const response = await client.posts.create({ text: "Hello world!" }); +console.log(`Created Post: ${response.data?.id}`); +``` + + + +### Create a reply + + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"text": "This is a reply!", "reply": {"in_reply_to_tweet_id": "1234567890"}}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Create a reply +response = client.posts.create( + text="This is a reply!", + reply={"in_reply_to_tweet_id": "1234567890"} +) +print(f"Created reply: {response.data.id}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Create a reply +const response = await client.posts.create({ + text: "This is a reply!", + reply: { inReplyToTweetId: "1234567890" }, +}); +console.log(`Created reply: ${response.data?.id}`); +``` + + + +### Delete a Post + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/tweets/1234567890" \ + -H "Authorization: OAuth ..." +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Delete a Post +response = client.posts.delete("1234567890") +print(f"Deleted: {response.data.deleted}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Delete a Post +const response = await client.posts.delete("1234567890"); +console.log(`Deleted: ${response.data?.deleted}`); +``` + + **Next steps** diff --git a/x-api/posts/manage-tweets/introduction.mdx b/x-api/posts/manage-tweets/introduction.mdx index 51c9a65c6..7ecf4875c 100644 --- a/x-api/posts/manage-tweets/introduction.mdx +++ b/x-api/posts/manage-tweets/introduction.mdx @@ -1,50 +1,148 @@ --- -title: Introduction +title: Manage Posts sidebarTitle: Introduction -keywords: ["post tweet", "create tweet", "delete tweet", "manage tweets", "tweet creation", "tweet deletion", "post polls", "quote tweets", "reply settings"] +description: Create and delete Posts on behalf of authenticated users +keywords: ["manage tweets", "create tweet", "post tweet", "delete tweet", "manage posts", "tweet API"] --- import { Button } from '/snippets/button.mdx'; -Creating and deleting Posts using the X API is essential for engaging with the public conversation. The new manage Posts endpoints allow you to do just that and much more. +The Manage Posts endpoints let you create and delete Posts on behalf of authenticated users. Build applications that post content, create threads, or manage user Posts. -We have two available methods for managing Posts, POST and DELETE. The POST method lets you post polls, quote Tweets, post with reply settings, post with geo, post with media and tag users, and post to Super Followers, in addition to other features. Likewise, the DELETE method allows you to delete a specific Post. For the POST method, you can pass in [the parameters](/x-api/posts/creation-of-a-post) you are looking for to enable you to further customize your request. +## Overview -The 'delete Post' method has been updated to support edited Posts. When any Post in a chain of Post edits is deleted, all Posts in that edit chain are also deleted. To learn more about Edit Post metadata, check out the [Edit Posts fundamentals](/x-api/fundamentals/edit-posts) page. + + + Publish a new Post + + + Delete an existing Post + + + Reply to another Post + + + Quote another Post + + -There is a user rate limit of 200 requests per 15 minutes for the POST method. The DELETE method has a rate limit of 50 requests per 15 minutes. Additionally, there is a limit of 300 requests per 3 hours, including Posts created with either manage Posts or manage Retweets. +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | [`/2/tweets`](/x-api/posts/create-post) | Create a new Post | +| DELETE | [`/2/tweets/:id`](/x-api/posts/delete-post) | Delete a Post | + +--- + +## Creating Posts + +### Basic Post + +```bash +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello from the API!"}' +``` + +### Reply to a Post + +```bash +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "This is a reply!", + "reply": { + "in_reply_to_tweet_id": "1234567890" + } + }' +``` -Since you are making requests on behalf of a user with the manage Posts endpoints, you must authenticate with either [OAuth 1.0a User Context](https://en.docs/authentication/oauth-1-0a) or [OAuth 2.0 Authorization Code with PKCE](https://en.docs/authentication/oauth-2-0/authorization-code), and use user Access Tokens associated with a user that has authorized your App. To generate this user Access Token with OAuth 1.0a, you can use the [3-legged OAuth flow](https://en.docs/authentication/oauth-2-0/bearer-tokens). To generate a user Access Token with OAuth 2.0, you can use the [Authorization Code with PKCE grant flow](https://en.docs/authentication/oauth-2-0/user-access-token). +### Quote a Post - -**Account setup** +```bash +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Check this out!", + "quote_tweet_id": "1234567890" + }' +``` -To access these endpoints, you will need: +### Post with media -- An approved [developer account](https://en/portal/petition/essential/basic-info). -- To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects). +```bash +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Photo of the day", + "media": { + "media_ids": ["1234567890123456789"] + } + }' +``` + + +Upload media first using the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked), then reference the `media_id` in your Post. + + +### Post with poll + +```bash +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "What is your favorite color?", + "poll": { + "options": ["Red", "Blue", "Green", "Yellow"], + "duration_minutes": 1440 + } + }' +``` + +--- + +## Deleting Posts + +```bash +curl -X DELETE "https://api.x.com/2/tweets/1234567890" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + + +You can only delete Posts authored by the authenticated user. + + +--- -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - +## Getting started - -
- -
- -
- + +**Prerequisites** -## Supporting resources +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) or [3-legged OAuth](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) + -- [Learn how to use Postman](/tutorials/postman-getting-started) -- [Troubleshoot an error](https://en/support/twitter-api) -- [API Reference](/x-api/posts/manage-tweets/introduction) \ No newline at end of file + + + Create your first Post + + + Key concepts and best practices + + + Upload media for Posts + + + Full endpoint documentation + + diff --git a/x-api/posts/manage-tweets/migrate/overview.mdx b/x-api/posts/manage-tweets/migrate/overview.mdx index 61bcac473..cf5131431 100644 --- a/x-api/posts/manage-tweets/migrate/overview.mdx +++ b/x-api/posts/manage-tweets/migrate/overview.mdx @@ -19,7 +19,7 @@ The following tables compare the standard v1.1 and X API v2 manage Posts endpoin | Endpoint path | /1.1/statuses/update.json | /2/tweets | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE | | Default request [rate limits](/x-api/fundamentals/rate-limits) | None

300 requests per 3-hour window per user, per app. Shared with the v1.1 POST Retweets endpoint. | 200 requests per 15 min per user

300 requests per 3-hour window per user, per app. Shared with the v2 POST Retweets endpoint. | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) | | ✔ | ### Delete a Post @@ -30,7 +30,7 @@ The following tables compare the standard v1.1 and X API v2 manage Posts endpoin | Endpoint path | /1.1/statuses/destroy/:id.json | /2/tweets/:id | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE | | Default request [rate limits](/x-api/fundamentals/rate-limits) | None | 50 requests per 15 min per user | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) | | ✔ | ### Other migration resources diff --git a/x-api/posts/manage-tweets/migrate/standard-to-twitter-api-v2.mdx b/x-api/posts/manage-tweets/migrate/standard-to-twitter-api-v2.mdx index d2f974643..08359ce95 100644 --- a/x-api/posts/manage-tweets/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/posts/manage-tweets/migrate/standard-to-twitter-api-v2.mdx @@ -21,7 +21,7 @@ If you have been working with the standard v1.1 [POST statuses/update](https://d **Authentication** -Both the standard v1.1 and X API v2 manage Posts ([POST statuses/update](https://developer.x.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-update) and [POST statuses/destroy/:id](https://developer.x.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-destroy-id)) endpoints use [OAuth 1.0a User Context](https://developer.x.com/content/developer-twitter/resources/fundamentals/authentication). Therefore, if you were previously using one of the standard v1.1 endpoints, you can continue using the same authentication method if you migrate to the X API v2 version.  +Both the standard v1.1 and X API v2 manage Posts ([POST statuses/update](https://developer.x.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-update) and [POST statuses/destroy/:id](https://developer.x.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-destroy-id)) endpoints use [OAuth 1.0a User Context](https://developer.x.com/content/developer-twitter/resources/fundamentals/authentication). Therefore, if you were previously using one of the standard v1.1 endpoints, you can continue using the same authentication method if you migrate to the X API v2 version. ### Differences @@ -40,10 +40,108 @@ Both the standard v1.1 and X API v2 manage Posts ([POST statuses/update](https:/ ### App and Project requirements -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. ### Request parameters The following standard v1.1 request parameters accepted two request query parameters (user_id or screen_name). The X API v2 only accepts the numerical Post ID for the DELETE endpoint, and it must be passed as part of the endpoint path. -For the POST endpoint, additional parameters will need to be passed in via the JSON body of the request. You can learn more about what parameters are available in the [API reference guide](/x-api/posts/manage-tweets/introduction). \ No newline at end of file +For the POST endpoint, additional parameters will need to be passed in via the JSON body of the request. You can learn more about what parameters are available in the [API reference guide](/x-api/posts/manage-tweets/introduction). + +--- + +## Code examples + +### Create a Post (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello world!"}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Create a Post +response = client.posts.create(text="Hello world!") +print(f"Created Post: {response.data.id}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Create a Post +const response = await client.posts.create({ text: "Hello world!" }); +console.log(`Created Post: ${response.data?.id}`); +``` + + + +### Delete a Post (v2) + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/tweets/1234567890" \ + -H "Authorization: OAuth ..." +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Delete a Post +response = client.posts.delete("1234567890") +print(f"Deleted: {response.data.deleted}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Delete a Post +const response = await client.posts.delete("1234567890"); +console.log(`Deleted: ${response.data?.deleted}`); +``` + + diff --git a/x-api/posts/manage-tweets/quickstart.mdx b/x-api/posts/manage-tweets/quickstart.mdx index dc6f9cd28..03312bf1f 100644 --- a/x-api/posts/manage-tweets/quickstart.mdx +++ b/x-api/posts/manage-tweets/quickstart.mdx @@ -1,99 +1,438 @@ --- title: Quickstart sidebarTitle: Quickstart +description: Create and delete Posts using the X API keywords: ["manage tweets quickstart", "post tweet quickstart", "create tweet tutorial", "tweet creation guide", "post tweet example"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the manage Posts endpoints** +This guide walks you through creating and deleting Posts using the X API. -This quick start guide will help you make your first request to the manage Posts endpoints using [Postman](/tutorials/postman-getting-started). + +**Prerequisites** -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to see sample code in different languages. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Tokens (OAuth 1.0a or OAuth 2.0 PKCE) + - -**Prerequisites** +--- -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +## Create a Post -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. - + + + The POST `/2/tweets` endpoint requires a JSON body with at least `text` or `media`: -### Steps to building manage Posts requests + ```json + { + "text": "Hello from the X API!" + } + ``` + -**Step one: Start with a tool or library** + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. + -To load X API v2 Postman collection into your environment, please click on the following button: +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello from the X API!"}' +``` - +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -Once you have the X API v2 collection loaded in Postman, navigate to the “Manage Posts” folder, and select “Create a Post”. -  +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -**Step two: Authenticate your request** +client = Client(auth=oauth1) -To properly make a request to the X API, you need to verify that you have permission to do so. To do this with the manage Posts endpoints, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +# Create a Post +response = client.posts.create(text="Hello from the X API!") +print(f"Created Post: {response.data.id}") +``` -In this example, we are going to use OAuth 1.0a User Context. +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Create a Post +const response = await client.posts.create({ text: "Hello from the X API!" }); +console.log(`Created Post: ${response.data?.id}`); +``` -You must add your keys and tokens (and specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret) to Postman. You can do this by selecting the environment named “X API v2” (in the top-right corner of Postman), and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). + -If you've done this correctly, these variables will automatically be pulled into the request's authorization tab. + -**Step three: Specify the text of the Post** + + A successful response includes the new Post's `id` and `text`: -When creating a new Post with this endpoint, text or media for the Post are the required body parameters. + ```json + { + "data": { + "id": "1445880548472328192", + "text": "Hello from the X API!" + } + } + ``` + + -In Postman, navigate to the “Body” tab and enter the text of the Post as the value for the `text` parameter. Additionally, if you wish to add parameters for items such as polls, replying to a Post ID, or adding reply settings you can also do so here. You can learn more about what’s available in our [API reference guide](/x-api/posts/manage-tweets/introduction). +--- -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Parameter type** | -| `text` | Hello world! | body | +## Advanced examples -**Step four: Identify and specify which fields you would like to retrieve** + + -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: + +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "This is a reply!", + "reply": { + "in_reply_to_tweet_id": "1234567890" + } + }' ``` -{ - "data": { - "id": "1445880548472328192", - "text": "Hello world!" - } -} + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Create a reply +response = client.posts.create( + text="This is a reply!", + reply={"in_reply_to_tweet_id": "1234567890"} +) +print(f"Created reply: {response.data.id}") ``` +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -If the returned response object contains an `id` and the `text` of your Post, you have successfully created a Post. -  +const client = new Client({ oauth1 }); + +// Create a reply +const response = await client.posts.create({ + text: "This is a reply!", + reply: { inReplyToTweetId: "1234567890" }, +}); +console.log(`Created reply: ${response.data?.id}`); +``` -**Step five: Delete your Post** + -To delete a Post, select the “Delete a Post” request also found in the “Manage Posts” folder of the X API v2 collection loaded in Postman. This endpoint requires the ID of the Post you wish to delete. Then, in the “Params” tab, enter the ID of the Post you wish to delete as the value for the `id` column.  + -On successful delete request, you will receive a response similar to the following example: + + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Check this out!", + "quote_tweet_id": "1234567890" + }' ``` -{ - "data": { - "deleted" : true - } -} + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Quote a Post +response = client.posts.create( + text="Check this out!", + quote_tweet_id="1234567890" +) +print(f"Created quote: {response.data.id}") ``` +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Quote a Post +const response = await client.posts.create({ + text: "Check this out!", + quoteTweetId: "1234567890", +}); +console.log(`Created quote: ${response.data?.id}`); +``` + + + + + + + First, upload media using the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked), then reference the `media_id`: + + + +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Photo of the day!", + "media": { + "media_ids": ["1234567890123456789"] + } + }' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Post with media +response = client.posts.create( + text="Photo of the day!", + media={"media_ids": ["1234567890123456789"]} +) +print(f"Created Post with media: {response.data.id}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Post with media +const response = await client.posts.create({ + text: "Photo of the day!", + media: { mediaIds: ["1234567890123456789"] }, +}); +console.log(`Created Post with media: ${response.data?.id}`); +``` + + + + -**Next steps** + -[API Reference](/x-api/posts/manage-tweets#manage-tweets-api-reference-index) + -[Get support](https://developer.x.com/en/support/x-api ) +```bash cURL +curl -X POST "https://api.x.com/2/tweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "What is your favorite color?", + "poll": { + "options": ["Red", "Blue", "Green", "Yellow"], + "duration_minutes": 1440 + } + }' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Post with poll +response = client.posts.create( + text="What is your favorite color?", + poll={"options": ["Red", "Blue", "Green", "Yellow"], "duration_minutes": 1440} +) +print(f"Created poll: {response.data.id}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Post with poll +const response = await client.posts.create({ + text: "What is your favorite color?", + poll: { options: ["Red", "Blue", "Green", "Yellow"], durationMinutes: 1440 }, +}); +console.log(`Created poll: ${response.data?.id}`); +``` + + + + + + +--- + +## Delete a Post + + + + You need the ID of the Post you want to delete. This is returned when you create a Post. + + + + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/tweets/1445880548472328192" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Delete a Post +response = client.posts.delete("1445880548472328192") +print(f"Deleted: {response.data.deleted}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Delete a Post +const response = await client.posts.delete("1445880548472328192"); +console.log(`Deleted: ${response.data?.deleted}`); +``` + + + + + + + ```json + { + "data": { + "deleted": true + } + } + ``` + + + + +You can only delete Posts that you authored. + + +--- -[Sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code "Sample code") \ No newline at end of file +## Next steps + + + + Key concepts and best practices + + + Upload media for Posts + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/posts/quote-tweets/introduction.mdx b/x-api/posts/quote-tweets/introduction.mdx index f67fc8dda..11560af78 100644 --- a/x-api/posts/quote-tweets/introduction.mdx +++ b/x-api/posts/quote-tweets/introduction.mdx @@ -1,45 +1,88 @@ --- -title: Introduction +title: Quote Posts sidebarTitle: Introduction -keywords: ["quote tweets", "quote posts", "quote tweet lookup", "quoted tweets", "quote retweets"] +description: Retrieve Quote Posts for a specific Post +keywords: ["quote tweets", "quote posts", "quotes lookup", "quoted tweets", "quote API"] --- import { Button } from '/snippets/button.mdx'; -### Quote Tweets lookup +The Quote Posts endpoint lets you retrieve Posts that quote a specific Post. See how users are commenting on and sharing content. -Here is a Quote Tweet for a Post from @API: +## Overview - + + + Get all Quote Posts for a Post + + + See how content is being discussed + + -The Quote Tweets lookup endpoint is a REST endpoint that takes a single path parameter to indicate the desired Post (by Post ID).  +--- -Posts are delivered in reverse-chronological order, starting with the most recent. Results are [paginated](/x-api/fundamentals/pagination) up to 100 Posts per page (10 Posts by default). Pagination tokens are provided for paging through large sets of Posts. +## Endpoint -The Quote Tweets endpoint supports [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters, and returns the [new JSON data format](/x-api/fundamentals/data-dictionary). +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/tweets/:id/quote_tweets`](/x-api/posts/get-quoted-posts) | Get Quote Posts for a Post | -This endpoint will always return the most recent edit, along with the edit history. Any Post collected after its 30-minute edit window will represent its final version and will include an array of IDs for all Posts in its edit history. For Posts with no edit history, this array will hold a single ID. +--- -To successfully make a request to this endpoint, you will need to authorize your request with the [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3), or [OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) authentication methods. You must use OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE when requesting non public metrics, promoted metrics or a protected user's timeline. - -**Account setup** +## Example request + +```bash +curl "https://api.x.com/2/tweets/1234567890/quote_tweets?\ +tweet.fields=created_at,author_id,public_metrics&\ +expansions=author_id&\ +user.fields=username" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +## Example response -To access these endpoints, you will need: +```json +{ + "data": [ + { + "id": "9876543210", + "text": "Great point! This is exactly what we need.", + "author_id": "1111111111", + "created_at": "2024-01-15T10:30:00.000Z" + } + ], + "includes": { + "users": [ + { + "id": "1111111111", + "username": "example_user" + } + ] + }, + "meta": { + "result_count": 1 + } +} +``` -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +--- + +## Getting started + + +**Prerequisites** -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) -
- - - -
\ No newline at end of file + + + Get Quote Posts for a Post + + + Full endpoint documentation + + diff --git a/x-api/posts/quote-tweets/quickstart.mdx b/x-api/posts/quote-tweets/quickstart.mdx index 3f84538e4..4a29c9950 100644 --- a/x-api/posts/quote-tweets/quickstart.mdx +++ b/x-api/posts/quote-tweets/quickstart.mdx @@ -1,247 +1,147 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["quote tweets quickstart", "quote tweets tutorial", "quote tweets guide", "quote tweets example", "quote retweets"] +description: Retrieve Quote Posts for a specific Post +keywords: ["quote tweets quickstart", "quote tweets tutorial", "get quotes guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the Quote Tweets lookup endpoint +This guide walks you through retrieving Quote Posts (Posts that quote another Post). -This quick start guide will help you make your first request to the Quote Tweets lookup endpoints with a set of specified fields using [Postman](/tutorials/postman-getting-started). + +**Prerequisites** -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token + -#### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. - -**Steps to build a Quote Tweets lookup request** - -**Step one: Start with a tool or library** - -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. - -To load X API v2 Postman collection into your environment, please click on the following button: - - - -Once you have the X API v2 collection loaded in Postman, navigate to the timeline folder and find the "Quote Tweets by ID" request. - -**Step two: Authenticate your request** - -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request with either [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods. - -For simplicity's sake, we will utilize OAuth 2.0 App-Only with this request, but you will need to use one of the other authentication methods if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Posts.  - -You must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). - -This variable will automatically be pulled into the request's authorization tab if you've done this correctly. - -**Step three: Identify and specify the Post you would like to retrieve the Quote Tweets for** - -You must specify a Post you would like to retrieve Quote Tweets for within the request by passing the  Post ID. - -In this example, the Post ID that we will use is 1409931481552543749. - -In Postman, navigate to the "Params" tab and enter this Post ID into the "Value" column of the id parameter. - -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | 1409931481552543749 | - -**Step four: Identify and specify which fields you would like to retrieve** - -If you click the "Send" button after step three, you will receive the default [Post object](/x-api/fundamentals/data-dictionary#tweet) fields in your response: id,  text, and edit\_history\_tweet_ids. - -If you would like to receive additional fields, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. - -For this exercise, we will request three additional different sets of fields from different objects: - -1. The additional tweet.created_at field in the primary user objects. - -2. The associated authors’ user object’s default fields for the returned Posts: id, name, and username -3. The additional user.created_at field in the associated user objects. -   +--- -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: + + + Get the ID of the Post you want to find quotes for. You can find it in the Post's URL: -| | | | | -| :--- | :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| `tweet.fields` | `created_at` | `tweets.created_at` | -| `expansions` | `author_id` | `includes.users.id`, `includes.users.name`, `includes.users.username` | -| `user.fields` | `created_at` | `includes.users.created_at` | | + ``` + https://x.com/XDevelopers/status/1409931481552543749 + └── This is the Post ID + ``` + -You should now see the following URL next to the "Send" button: + - `https://api.x.com/2/tweets/:id/quote_tweets?expansions=author_id&tweet.fields=created_at&user.fields=created_at` + +```bash cURL +curl "https://api.x.com/2/tweets/1409931481552543749/quote_tweets?\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id&\ +user.fields=username,verified&\ +max_results=10" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -**Please note:** +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get Quote Posts with pagination +for page in client.posts.get_quote_tweets( + "1409931481552543749", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id"], + user_fields=["username", "verified"], + max_results=10 +): + for post in page.data: + print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") +``` -In Postman, the path parameter :id in the URL field will **not** automatically update to the value that you enter into the `id` params field, which is why the above URL includes `:id` and not 1409931481552543749. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Step five: Make your request and review your response** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Once you have everything set up, hit the "Send" button and you will receive the following response: +// Get Quote Posts with pagination +const paginator = client.posts.getQuoteTweets("1409931481552543749", { + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id"], + userFields: ["username", "verified"], + maxResults: 10, +}); -``` -{ - "data": [ - { - "created_at": "2022-02-22T04:31:34.000Z", - "text": "RT @chris_bail: Twitter has created an entire course (with videos, code, and other materials) to help researchers learn how to collect data…", - "author_id": "29757971", - "id": "1495979553889697792" - }, - { - "created_at": "2022-01-26T17:07:43.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "241588187", - "id": "1486385372401737728" - }, - { - "created_at": "2022-01-11T17:28:04.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "24961055", - "id": "1480954678447857669" - }, - { - "created_at": "2022-01-10T20:34:46.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "1441574419789324291", - "id": "1480639272721940486" - }, - { - "created_at": "2021-12-16T22:55:24.000Z", - "text": "RT @chris_bail: Twitter has created an entire course (with videos, code, and other materials) to help researchers learn how to collect data…", - "author_id": "1623598771", - "id": "1471614967207976961" - }, - { - "created_at": "2021-12-13T15:59:55.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "1506401233", - "id": "1470423243513372679" - }, - { - "created_at": "2021-12-10T02:02:45.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "40103034", - "id": "1469125403373568001" - }, - { - "created_at": "2021-12-08T17:27:54.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "1436400156006567936", - "id": "1468633446935318529" - }, - { - "created_at": "2021-09-15T21:40:24.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "81650379", - "id": "1438256410417143809" - }, - { - "created_at": "2021-08-26T16:45:16.000Z", - "text": "RT @suhemparack: Super excited to share our course on Getting started with the #TwitterAPI v2 for academic research\n\nIf you know students w…", - "author_id": "40462535", - "id": "1430934381829492746" - } - ], - "includes": { - "users": [ - { - "username": "j_a_tucker", - "id": "29757971", - "name": "Joshua Tucker", - "created_at": "2009-04-08T16:45:38.000Z" - }, - { - "username": "whimchic", - "id": "241588187", - "name": "whimchic", - "created_at": "2011-01-22T16:51:43.000Z" - }, - { - "username": "mattbiehl", - "id": "24961055", - "name": "Matthias Biehl", - "created_at": "2009-03-17T21:41:27.000Z" - }, - { - "username": "weixinac", - "id": "1441574419789324291", - "name": "J", - "created_at": "2021-09-25T01:25:19.000Z" - }, - { - "username": "RSangeleer", - "id": "1623598771", - "name": "Richard Sangeleer", - "created_at": "2013-07-26T18:25:45.000Z" - }, - { - "username": "Gulnerman", - "id": "1506401233", - "name": "Giz Gulnerman", - "created_at": "2013-06-11T20:13:40.000Z" - }, - { - "username": "efishman123", - "id": "40103034", - "name": "Elishema Fishman", - "created_at": "2009-05-14T22:25:58.000Z" - }, - { - "username": "dtcxwz", - "id": "1436400156006567936", - "name": "Hüseyin Ateş", - "created_at": "2021-09-10T18:44:30.000Z" - }, - { - "username": "brendaberkelaar", - "id": "81650379", - "name": "Dr. Brenda Berkelaar", - "created_at": "2009-10-11T18:09:16.000Z" - }, - { - "username": "misoca", - "id": "40462535", - "name": "Michael Soto", - "created_at": "2009-05-16T13:26:05.000Z" - } - ] - }, - "meta": { - "result_count": 10, - "next_token": "avdjwk0udyx6" - } +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); + }); } ``` -#### Step six: Paginate through your results - -In the previous response, you will find a meta data object at the bottom that includes the following fields: + + + + + + ```json + { + "data": [ + { + "id": "1495979553889697792", + "text": "Great thread on the new API features! https://t.co/...", + "author_id": "29757971", + "created_at": "2022-02-22T04:31:34.000Z", + "public_metrics": { + "retweet_count": 5, + "reply_count": 2, + "like_count": 42, + "quote_count": 1 + }, + "edit_history_tweet_ids": ["1495979553889697792"] + } + ], + "includes": { + "users": [ + { + "id": "29757971", + "username": "developer", + "verified": false + } + ] + }, + "meta": { + "result_count": 1, + "next_token": "avdjwk0udyx6" + } + } + ``` + + + + The SDKs handle pagination automatically. For cURL, use the `next_token` to get more Quote Posts: + + ```bash + curl "https://api.x.com/2/tweets/1409931481552543749/quote_tweets?\ + max_results=10&\ + pagination_token=avdjwk0udyx6" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` + + -* results_count -* next_token - -In step four, because we did not specify a max_results parameter, we will only get 10 Posts by default. To access the additional pages of data, we will be taking the value of the next_token field from our last results and adding that string as the value of the pagination_token parameter on the Postman params page, keeping everything else constant.  - -| | | -| :--- | :--- | -| **Key** | **Value** | -| `pagination_token` | avdjwk0udyx6 | - -Once this is all set up, you can click "Send" again and you should receive the next page of results.  +--- -We have put together a guide on [pagination](/x-api/fundamentals/pagination) to further explain this concept. \ No newline at end of file +## Next steps + + + + Look up Retweets + + + Look up Posts by ID + + + Full endpoint documentation + + diff --git a/x-api/posts/retweets/integrate.mdx b/x-api/posts/retweets/integrate.mdx index efe0ea82c..a67c2adb0 100644 --- a/x-api/posts/retweets/integrate.mdx +++ b/x-api/posts/retweets/integrate.mdx @@ -4,31 +4,26 @@ sidebarTitle: Integration guide keywords: ["retweets integration", "retweets guide", "retweets integration guide", "retweets implementation", "retweets setup"] --- -This page contains information on several tools and key concepts that you should be aware of as you integrate the Retweet endpoints into your system. We’ve broken the page into a couple of different sections: +This page covers tools and key concepts for integrating the Retweet endpoints. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) +--- -### Helpful tools +## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: #### Postman -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. #### Code samples -Interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +Interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). #### Third-party libraries -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. -  +Take advantage of one of our communities' [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. + ### Key concepts @@ -36,7 +31,7 @@ Take advantage of one of our communities’ [third-party libraries](/x-api/tools All X API v2 endpoints require for you to authenticate your requests with a set of credentials, also known as keys and tokens. -You can use either OAuth 1.0a User Context or OAuth 2.0 Bearer Token to authenticate your requests to the Retweets lookup endpoint.  +You can use either OAuth 1.0a User Context or OAuth 2.0 Bearer Token to authenticate your requests to the Retweets lookup endpoint. The manage Retweets endpoints require the use of OAuth 1.0a User Context, which means that you must use a set of API keys and user access tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of access tokens for another user, they must authorize or authenticate your App using the 3-legged OAuth flow. @@ -44,7 +39,7 @@ Please note that OAuth 1.0a can be tricky to use. If you are not familiar with t **Please note** -If you are requesting the following fields, OAuth 1.0a User Context is required:  +If you are requesting the following fields, OAuth 1.0a User Context is required: * tweet.fields.non\_public\_metrics * tweet.fields.promoted_metrics @@ -53,16 +48,16 @@ If you are requesting the following fields, OAuth 1.0a User Context is required: * media.fields.promoted_metrics * media.fields.organic_metrics -#### Developer portal, Projects, and developer Apps +#### Developer Console, Projects, and developer Apps -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App.  -  +To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. + #### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. -The manage Retweets endpoints are limited to 50 requests per 15 min (per user). Additionally, for the POST endpoint, you are limited to 300 requests per 3-hour window (per user, per app).  +The manage Retweets endpoints are limited to 50 requests per 15 min (per user). Additionally, for the POST endpoint, you are limited to 300 requests per 3-hour window (per user, per app). With the Retweets lookup endpoint, you are limited to 75 requests per 15-min window. Additionally, only the 100 most recent Retweeting users will be returned by this endpoint. @@ -72,13 +67,105 @@ The X API v2 allows users to select exactly which data they want to return from * attachments.poll_ids * attachments.media_keys -* author_id, entities.mentions.username +* author_id, entities.mentions.username * geo.place_id * in\_reply\_to\_user\_id, * referenced_tweets.id, * referenced\_tweets.id.author\_id -The fields parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. These endpoints delivers Post objects primarily. By default, the Post object returns the id and text fields. To receive additional fields such as tweet.created_at or tweet.entities, you will have to specifically request those using a fields parameter. Some important fields that you may want to consider using in your integration are our poll data, metrics, Post annotations, and conversation ID fields. +The fields parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. These endpoints delivers Post objects primarily. By default, the Post object returns the id and text fields. To receive additional fields such as tweet.created_at or tweet.entities, you will have to specifically request those using a fields parameter. Some important fields that you may want to consider using in your integration are our poll data, metrics, Post annotations, and conversation ID fields. + +We've added a guide on how to [use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). + +--- + +### Code examples + +#### Get users who retweeted a Post + + + +```bash cURL +curl "https://api.x.com/2/tweets/1234567890/retweeted_by?user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get users who retweeted a Post +response = client.posts.get_retweeted_by( + "1234567890", + user_fields=["username", "verified"] +) +for user in response.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get users who retweeted a Post +const response = await client.posts.getRetweetedBy("1234567890", { + userFields: ["username", "verified"], +}); + +response.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); +}); +``` + + + +#### Retweet a Post + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123/retweets" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1234567890"}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Retweet a Post +response = client.posts.retweet(user_id="123", tweet_id="1234567890") +print(response.data) +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Retweet a Post +const response = await client.posts.retweet("123", { tweetId: "1234567890" }); +console.log(response.data); +``` -We’ve added a guide on how to [use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). + diff --git a/x-api/posts/retweets/introduction.mdx b/x-api/posts/retweets/introduction.mdx index 34fcc370d..26d7ab361 100644 --- a/x-api/posts/retweets/introduction.mdx +++ b/x-api/posts/retweets/introduction.mdx @@ -1,52 +1,92 @@ --- -title: Introduction +title: Retweets sidebarTitle: Introduction -keywords: ["retweets", "retweet lookup", "get retweets", "retweet users", "who retweeted", "retweet endpoint"] +description: Retweet and undo retweets, and retrieve retweet information +keywords: ["retweets", "retweet lookup", "retweeting users", "manage retweets", "retweet API", "reposts"] --- import { Button } from '/snippets/button.mdx'; - -**Account setup** +The Retweets endpoints let you retweet and undo retweets, see who retweeted a Post, and get reposts of your own Posts. -To access these endpoints, you will need: +## Overview -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  + + + Retweet a Post on behalf of a user + + + Remove a retweet + + + See who retweeted a Post + + + Get reposts of your own Posts + + + +--- + +## Endpoints -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - ### Retweets lookup -With the Retweets lookup endpoint, you can retrieve a list of accounts that have Retweeted a Post. For this endpoint, pagination tokens will be provided for paging through large sets of results in batches of up to 100 users.  +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/tweets/:id/retweeted_by`](/x-api/posts/get-reposted-by) | Get users who retweeted a Post | +| GET | [`/2/tweets/:id/quote_tweets`](/x-api/posts/get-quoted-posts) | Get quote Posts of a Post | +| GET | [`/2/users/reposts_of_me`](/x-api/users/get-reposts-of-me) | Get reposts of authenticated user's Posts | + +### Manage retweets -You can authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 App-Only](https://developer.x.com(/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3).  +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | [`/2/users/:id/retweets`](/x-api/users/repost-post) | Retweet a Post | +| DELETE | [`/2/users/:id/retweets/:tweet_id`](/x-api/users/unrepost-post) | Undo a retweet | -### Retweets of Me +--- -The Reposts of Me lookup allows you to see which of your own Posts have been shared by others. +## Example: Get retweeting users -This provides users with insight into which of their Posts are being shared and resonating with their audience, which can help facilitate more targeted interactions and content strategy adjustments. +```bash +curl "https://api.x.com/2/tweets/1234567890/retweeted_by?\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -You can authenticate these endpoints with either either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +## Example: Retweet a Post -#### Manage Retweets +```bash +curl -X POST "https://api.x.com/2/users/123456789/retweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1234567890"}' +``` -The manage Retweets endpoints enable you to Retweet or undo a Retweet of a specified Post on behalf of an authenticated account. For this endpoint group, there are two methods available POST and DELETE. The POST method allows you to Retweet a Post, and the DELETE method will enable you to undo a Retweet of a given Post. +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + -Since you are making requests on behalf of a user, you must authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and utilize the user Access Tokens associated with the user you are making the request on behalf of. You can generate this user Access Token using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (OAuth 1.0a) or using the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)) (OAuth 2.0). You can Retweet a Post from your account or an account of an authenticated user. With both endpoints, there is a user rate limit of 50 requests per 15 minutes per endpoint. - -
- -
- -
- + + + Get retweets for a Post + + + Retweet and undo retweets + + + Get reposts of your Posts + + + Full endpoint documentation + + diff --git a/x-api/posts/retweets/migrate/manage-retweets-standard-to-twitter-api-v2.mdx b/x-api/posts/retweets/migrate/manage-retweets-standard-to-twitter-api-v2.mdx index 6ff9ae902..6a9dcdb3b 100644 --- a/x-api/posts/retweets/migrate/manage-retweets-standard-to-twitter-api-v2.mdx +++ b/x-api/posts/retweets/migrate/manage-retweets-standard-to-twitter-api-v2.mdx @@ -39,7 +39,7 @@ Both the standard v1.1 and X API v2 manage Retweets ([POST statuses/retweet/:id] **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with an App. **Request parameters** @@ -53,4 +53,132 @@ The following standard v1.1 request parameters accepted two request query parame Please note that the Standard v1.1 parameters are passed as query parameters, whereas the X API v2 parameters are passed as body parameters for the POST endpoint or path parameters for the DELETE endpoint. -Also, an id of the user Retweeting a Post is not required when using the standard v1.1 endpoints since the [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) passed with [OAuth 1.0a User Context](/resources/fundamentals/authentication) infer which user is initiating the Retweet/undoing a Retweet.  +Also, an id of the user Retweeting a Post is not required when using the standard v1.1 endpoints since the [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) passed with [OAuth 1.0a User Context](/resources/fundamentals/authentication) infer which user is initiating the Retweet/undoing a Retweet. + +--- + +## Code examples + +### Retweet a Post (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/retweets" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1234567890"}' +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/retweets" +response = requests.post(url, auth=auth, json={"tweet_id": "1234567890"}) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Retweet a Post +response = client.posts.retweet(user_id="123456789", tweet_id="1234567890") +print(f"Retweeted: {response.data.retweeted}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Retweet a Post +const response = await client.posts.retweet("123456789", { tweetId: "1234567890" }); +console.log(`Retweeted: ${response.data?.retweeted}`); +``` + + + +### Undo a Retweet (v2) + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/retweets/1234567890" \ + -H "Authorization: OAuth ..." +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/retweets/1234567890" +response = requests.delete(url, auth=auth) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Undo a Retweet +response = client.posts.unretweet(user_id="123456789", tweet_id="1234567890") +print(f"Retweeted: {response.data.retweeted}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Undo a Retweet +const response = await client.posts.unretweet("123456789", "1234567890"); +console.log(`Retweeted: ${response.data?.retweeted}`); +``` + + diff --git a/x-api/posts/retweets/migrate/overview.mdx b/x-api/posts/retweets/migrate/overview.mdx index 3301f9119..8a570e4b9 100644 --- a/x-api/posts/retweets/migrate/overview.mdx +++ b/x-api/posts/retweets/migrate/overview.mdx @@ -20,7 +20,7 @@ The following tables compare the standard v1.1 and X API v2 Retweets endpoints: | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 2.0 Bearer Token

OAuth 1.0a User Context | | Default request [rate limits](/resources/fundamentals/rate-limits) | 75 requests per 15 min | 75 requests per 15 min (per App)

75 requests per 15 min (per user) | | Data format | Standard v1.1 format | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | ### Manage Retweets @@ -35,7 +35,7 @@ The following tables compare the standard v1.1 and X API v2 undo Retweet endpoin | Endpoint path | /1.1/statuses/retweet/:id.json | /2/users/:id/retweets | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context | | Default request [rate limits](/resources/fundamentals/rate-limits) | None

300 requests per 3-hour window (per user, per app). This is shared with the POST Tweet endpoint | 50 requests per 15 min (per user)

300 requests per 3-hour window (per user, per app). This is shared with the POST Tweet endpoint for manage Posts. | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | #### Undo a Retweet @@ -48,7 +48,7 @@ The following tables compare the standard v1.1 and X API v2 undo Retweet endpoin | Endpoint path | /1.1/statuses/unretweet/:id.json | /2/users/:id/retweets/:source\_tweet\_id | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context | | Default request [rate limits](/resources/fundamentals/rate-limits) | None | 50 requests per 15 min (per user) | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | **Other migration resources** diff --git a/x-api/posts/retweets/migrate/retweets-lookup-standard-to-twitter-api-v2.mdx b/x-api/posts/retweets/migrate/retweets-lookup-standard-to-twitter-api-v2.mdx index 251281374..9fb4816c7 100644 --- a/x-api/posts/retweets/migrate/retweets-lookup-standard-to-twitter-api-v2.mdx +++ b/x-api/posts/retweets/migrate/retweets-lookup-standard-to-twitter-api-v2.mdx @@ -44,7 +44,7 @@ For both v1.1 and v2 GET endpoints the max number of users that will be returned **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Response data format** @@ -78,3 +78,60 @@ We also introduced a new set of fields to the [Post object](/x-api/fundamentals **Request parameters** The following standard v1.1 request parameters accepted two request query parameters (user_id or screen_name). The X API v2 only accepts the numerical user ID, and it must be passed as part of the endpoint path. + +--- + +## Code examples + +### Get users who Retweeted a Post (v2) + + + +```bash cURL +curl "https://api.x.com/2/tweets/1234567890/retweeted_by?user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/tweets/1234567890/retweeted_by" + +params = {"user.fields": "username,verified"} +headers = {"Authorization": f"Bearer {bearer_token}"} + +response = requests.get(url, headers=headers, params=params) +print(response.json()) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get users who Retweeted a Post +response = client.posts.get_retweeted_by( + "1234567890", + user_fields=["username", "verified"] +) +for user in response.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get users who Retweeted a Post +const response = await client.posts.getRetweetedBy("1234567890", { + userFields: ["username", "verified"], +}); + +response.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); +}); +``` + + diff --git a/x-api/posts/retweets/quickstart/manage-retweets.mdx b/x-api/posts/retweets/quickstart/manage-retweets.mdx index ad9866879..4b057f13d 100644 --- a/x-api/posts/retweets/quickstart/manage-retweets.mdx +++ b/x-api/posts/retweets/quickstart/manage-retweets.mdx @@ -1,101 +1,185 @@ --- title: Manage Retweets sidebarTitle: Manage Retweets +description: Retweet and undo Retweets using the X API keywords: ["manage retweets", "retweet posts", "unretweet", "retweet quickstart", "retweet tutorial", "retweet guide"] --- import { Button } from '/snippets/button.mdx'; -### Getting started with the manage Retweets endpoints +This guide walks you through Retweeting and undoing Retweets using the X API. -This quick start guide will help you make your first request to the manage Retweets endpoints using Postman. + +**Prerequisites** -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) + -#### Prerequisites +--- -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +## Retweet a Post -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. + + + You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). + -#### Steps to build a manage Retweets request + + Find the Post ID in the URL when viewing a Post: -**Step one: Start with a tool or library** + ``` + https://x.com/XDevelopers/status/1228393702244134912 + └── This is the Post ID + ``` + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. + -To load the X API v2 Postman collection into your environment, please click on the following button: + - -Once you have the X API v2 collection loaded in Postman, navigate to the “Retweets” folder, and select “Retweet a Post”. -  +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/retweets" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"tweet_id": "1228393702244134912"}' +``` -Step two: Authenticate your request +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -In this example, we are going to use OAuth 1.0a User Context. +client = Client(auth=oauth1) -You must add your keys and tokens – specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret – to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +# Retweet a Post +response = client.posts.retweet( + user_id="123456789", + tweet_id="1228393702244134912" +) -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. +print(f"Retweeted: {response.data.retweeted}") +``` +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -**Step three: Specify which Post you are going to Retweet** +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -Manage Retweets endpoints require two IDs: one for the user who wishes to Retweet or undo a Retweet and the id of the Post. +const client = new Client({ oauth1 }); -The user’s ID must correspond to the authenticating user’s ID, meaning that you must pass the [Access Tokens](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) associated with the user ID when authenticating your request.  In this case, you can specify the ID belonging to your own user. You can find your ID in two ways: +// Retweet a Post +const response = await client.posts.retweet("123456789", { + tweetId: "1228393702244134912", +}); -1. Using the [user lookup by username](/x-api/users/user-lookup-by-username) endpoint, you can pass a username and receive the id field.  -2. Looking at your Access Token, you will find that the numeric part is your user ID. -   +console.log(`Retweeted: ${response.data?.retweeted}`); +``` -You also must specify a Post that you want to Retweet. You can find the Post ID by navigating to X.com and clicking on a Post and then looking in the URL. For example, the following URL's Post ID is 1228393702244134912. + -https://x.com/TwitterDev/status/1228393702244134912 + -In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the id path variable. and enter the ID of the Post you wish to Retweet as the value for thetweet_id parameter. Be sure not to include any spaces before or after any ID. + + ```json + { + "data": { + "retweeted": true + } + } + ``` + + -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | (your user ID) | -| tweet_id | (the ID of the Tweet you want to Retweet) | +--- +## Undo a Retweet -If you click the "Send" button, you will receive a response object containing the status of the relationship: +Remove a Retweet: -* If you receive a "retweeted": true, then the id is successfully Retweeting the tweet_id. -   + -**Step four: Make your request and review your response** +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/retweets/1228393702244134912" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -Once you have everything set up, hit the "Send" button and you will receive the following response: +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -``` -{ - "data": { - "retweeted": true - } -} +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Undo a Retweet +response = client.posts.unretweet( + user_id="123456789", + tweet_id="1228393702244134912" +) + +print(f"Retweeted: {response.data.retweeted}") ``` +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -If you receive a `"retweeted": true` has successfully Retweeted the `tweet_id` +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -If you wish to undo a Retweet by the same user you can use the request entitled “Undo a Retweet”, which is also found in the “Retweets” folder of the X API v2 collection loaded in Postman. The `id` should be your user ID and `source_tweet_id` should be the Post ID to undo the Retweet of. You will not have to add this as a JSON body so you will want to make sure that you add in the requisite query params for id and `source_tweet_id`.  +const client = new Client({ oauth1 }); -On a successful undoing of a Retweet, you will receive a similar response to the following example: +// Undo a Retweet +const response = await client.posts.unretweet("123456789", "1228393702244134912"); +console.log(`Retweeted: ${response.data?.retweeted}`); ``` + + + +**Response:** + +```json { "data": { "retweeted": false } } -``` \ No newline at end of file +``` + +--- + +## Next steps + + + + Get users who Retweeted a Post + + + Get Quote Posts + + + Full endpoint documentation + + diff --git a/x-api/posts/retweets/quickstart/retweets-lookup.mdx b/x-api/posts/retweets/quickstart/retweets-lookup.mdx index c49f48112..b5c11d346 100644 --- a/x-api/posts/retweets/quickstart/retweets-lookup.mdx +++ b/x-api/posts/retweets/quickstart/retweets-lookup.mdx @@ -1,118 +1,175 @@ --- -title: Retweets lookup -sidebarTitle: Retweets lookup +title: Retweets Lookup +sidebarTitle: Retweets Lookup +description: Get users who Retweeted a Post keywords: ["retweets lookup", "retweets quickstart", "get retweets", "retweets tutorial", "retweets guide", "retweets example"] --- import { Button } from '/snippets/button.mdx'; -### Getting started with the Retweets lookup endpoint +This guide walks you through retrieving users who Retweeted a specific Post. -This quick start guide will help you make your first request to the Retweets lookup endpoint using [Postman](/tutorials/postman-getting-started). If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatformTwitter-API-v2-sample-code) GitHub repository. + +**Prerequisites** -#### Prerequisites +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token (for public data) or User Access Token (for private metrics) + -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +--- -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +## Get users who Retweeted a Post -#### Steps to build a Retweets lookup request + + + Get the ID of the Post you want to look up Retweets for: -**Step one: Start with a tool or library** + ``` + https://x.com/XDevelopers/status/1354143047324299264 + └── This is the Post ID + ``` + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. + -To load the X API v2 Postman collection into your environment, please click on the following button: - + -Once you have the X API v2 collection loaded in Postman, navigate to the "Retweets" folder and select "Retweeted by.”  +```bash cURL +curl "https://api.x.com/2/tweets/1354143047324299264/retweeted_by?\ +user.fields=created_at,username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -**Step two: Authenticate your request** +```python Python SDK +from xdk import Client -To properly make a request to the X API, you need to verify that you have permission. To do so, this endpoint requires you to authenticate your request with either [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -For simplicity's sake, we will utilize OAuth 2.0 App-Only with this request, but you will need to use one of the other authentication methods if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Retweets.  +# Get users who Retweeted a Post with pagination +for page in client.posts.get_retweeted_by( + "1354143047324299264", + user_fields=["created_at", "username", "verified"] +): + for user in page.data: + print(f"{user.username} - Joined: {user.created_at}") +``` -To utilize OAuth 2.0 App-Only, you must add your keys and tokens, specifically the[App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -**Step three: Specify a Post** +// Get users who Retweeted a Post with pagination +const paginator = client.posts.getRetweetedBy("1354143047324299264", { + userFields: ["created_at", "username", "verified"], +}); -With this endpoint, you must specify the Post ID that you want to get Retweeting users of.  You can find the ID of a Post by navigating to that Post on X and pulling the numerical code at the end of the URL. For example, the following URL's Post ID is 1354143047324299264. +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Joined: ${user.created_at}`); + }); +} +``` -https://x.com/TwitterDev/status/1354143047324299264 + -In Postman, navigate to the "Params" tab and enter this username into the "Value" column of the id path variable (at the bottom of the section), making sure to not include any spaces before or after usernames.  + -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | The Post ID you want to get the Reweeting users of | + + ```json + { + "data": [ + { + "created_at": "2008-12-04T18:51:57.000Z", + "id": "17874544", + "username": "TwitterSupport", + "name": "Twitter Support", + "verified": true + }, + { + "created_at": "2007-02-20T14:35:54.000Z", + "id": "783214", + "username": "Twitter", + "name": "Twitter", + "verified": true + } + ], + "meta": { + "result_count": 2, + "next_token": "7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" + } + } + ``` + + -**Step four: Identify and specify which fields you would like to retrieve** +--- -If you click the "Send" button after step three, you will receive the default [user object](/x-api/fundamentals/data-dictionary#user) fields in your response: id, name, and username. +## Include additional data -If you would like to receive additional fields beyond id, name, and username, you will have to specify those fields in your request with the [fields](/x-api/fundamentals/fields) and/or [expansions](/x-api/fundamentals/expansions) parameters. +Use expansions to get related data like pinned Posts: -For this exercise, we will request three additional sets of fields from different objects: + -1. The additional user.created_at field in the primary user objects. -2. The associated pinned Posts’ object’s default fields for the returned users: id and text. -3. The additional  tweet.created_at field in the associated Post objects. +```bash cURL +curl "https://api.x.com/2/tweets/1354143047324299264/retweeted_by?\ +user.fields=created_at&\ +expansions=pinned_tweet_id&\ +tweet.fields=created_at" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get Retweeting users with expansions +for page in client.posts.get_retweeted_by( + "1354143047324299264", + user_fields=["created_at"], + expansions=["pinned_tweet_id"], + tweet_fields=["created_at"] +): + for user in page.data: + print(f"{user.username}") + # Pinned Posts are in page.includes.tweets +``` -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| user.fields | created_at | user.created_at | -| expansions | pinned\_tweet\_id | tweet.id, tweet.text | -| tweet.fields | created_at | tweet.created_at | +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -You should now see the following URL next to the "Send" button: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); - `https://api.x.com/2/tweets/1354143047324299264/retweeted_by?user.fields=created_at&expansions=pinned_tweet_id&tweet.fields=created_at` +// Get Retweeting users with expansions +const paginator = client.posts.getRetweetedBy("1354143047324299264", { + userFields: ["created_at"], + expansions: ["pinned_tweet_id"], + tweetFields: ["created_at"], +}); +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(user.username); + }); + // Pinned Posts are in page.includes?.tweets +} +``` -**Step five: Make your request and review your response** + -Once you have everything set up, hit the "Send" button and you will receive a similar response to the following example response: +### Response with expansion -``` +```json { "data": [ - { - "created_at": "2008-12-04T18:51:57.000Z", - "id": "17874544", - "username": "TwitterSupport", - "name": "Twitter Support" - }, - { - "created_at": "2007-02-20T14:35:54.000Z", - "id": "783214", - "username": "Twitter", - "name": "Twitter" - }, { "pinned_tweet_id": "1389270063807598594", "created_at": "2018-11-21T14:24:58.000Z", "id": "1065249714214457345", "username": "TwitterSpaces", "name": "Spaces" - }, - { - "pinned_tweet_id": "1293595870563381249", - "created_at": "2007-05-23T06:01:13.000Z", - "id": "6253282", - "username": "XAPI", - "name": "X API" } ], "includes": { @@ -120,14 +177,25 @@ Once you have everything set up, hit the "Send" button and you will receive a si { "created_at": "2021-05-03T17:26:09.000Z", "id": "1389270063807598594", - "text": "now, everyone with 600 or more followers can host a Space.\n\nbased on what we've learned, these accounts are likely to have a good experience hosting because of their existing audience. before bringing the ability to create a Space to everyone, we’re focused on a few things. 🧵" - }, - { - "created_at": "2020-08-12T17:11:04.000Z", - "id": "1293595870563381249", - "text": "X API v2: Early Access released\n\nToday we announced Early Access to the first endpoints of the new Twitter API!\n\n#TwitterAPI #EarlyAccess #VersionBump https://t.co/g7v3aeIbtQ" + "text": "now, everyone with 600 or more followers can host a Space..." } ] } } -``` \ No newline at end of file +``` + +--- + +## Next steps + + + + Retweet and undo Retweets + + + Get Quote Posts + + + Full endpoint documentation + + diff --git a/x-api/posts/retweets/quickstart/retweets-of-me.mdx b/x-api/posts/retweets/quickstart/retweets-of-me.mdx index a66a7f9df..60df4a927 100644 --- a/x-api/posts/retweets/quickstart/retweets-of-me.mdx +++ b/x-api/posts/retweets/quickstart/retweets-of-me.mdx @@ -1,65 +1,194 @@ --- title: Retweets of Me sidebarTitle: Retweets of Me +description: Get your Posts that have been Retweeted keywords: ["retweets of me", "reposts of me", "my retweets", "retweets quickstart", "retweets tutorial", "retweets guide"] --- import { Button } from '/snippets/button.mdx'; -The Reposts of Me lookup allows you to see which of your own Posts have been shared by others. +This guide walks you through retrieving your Posts that have been Retweeted by others. -This provides users with insight into which of their Posts are being shared and resonating with their audience, which can help facilitate more targeted interactions and content strategy adjustments. + +**Prerequisites** -#### Authentication +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) + -Since you are making requests on behalf of a user, you must authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and utilize the user Access Tokens associated with the user you are making the request on behalf of. You can generate this user Access Token using the [3-legged OAuth flow](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens) (OAuth 1.0a) or using the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) (OAuth 2.0). +--- + +## Why use Retweets of Me? + +The Retweets of Me endpoint helps you: + +- **Track engagement** — See which of your Posts are being shared +- **Understand resonance** — Learn what content resonates with your audience +- **Inform strategy** — Adjust your content strategy based on sharing patterns + +--- -#### Making a request +## Get your Retweeted Posts -Once a user has authenticated with your app, you can call the Retweets of Me API on behalf of user as shown below: + - ```bash - curl 'https://api.x.com/2/users/reposts_of_me' --header 'Authorization: ••••••' - ``` +```bash cURL +curl "https://api.x.com/2/users/reposts_of_me?\ +tweet.fields=created_at,public_metrics&\ +max_results=10" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` +```python Python SDK +from xdk import Client -If the request is successful, you should see the JSON response as shown below: +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") +# Get your Posts that have been Retweeted +for page in client.posts.get_reposts_of_me( + tweet_fields=["created_at", "public_metrics"], + max_results=10 +): + for post in page.data: + print(f"{post.text[:50]}... - Retweets: {post.public_metrics.retweet_count}") ``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Get your Posts that have been Retweeted +const paginator = client.posts.getRepostsOfMe({ + tweetFields: ["created_at", "public_metrics"], + maxResults: 10, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Retweets: ${post.public_metrics?.retweet_count}`); + }); +} +``` + + + +--- + +## Response + +```json { - "data": [ - { - "text": "ever wanted to discover trends.. before they’re trends?\n\nsign up to Premium+ to access Radar\n\nhttps://t.co/7a3JzBb855 https://t.co/rwNogVs0Wi", - "edit_history_tweet_ids": [ - "1848781937210802364" - ], - "id": "1848781937210802364" - }, - { - "text": "want to be mysterious so bad but also love oversharing on x dot com", - "edit_history_tweet_ids": [ - "1847990559081648620" - ], - "id": "1847990559081648620" - }, - { - "text": "post", - "edit_history_tweet_ids": [ - "1847990269125226937" - ], - "id": "1847990269125226937" - }, - { - "text": "posting is just journaling with an audience", - "edit_history_tweet_ids": [ - "1847429017932026009" - ], - "id": "1847429017932026009" - }, - ], - "meta": { - "result_count": 4, - "next_token": "7140dibdnow9c7btw481s8m561gat797rboud5r80xvzm" - } + "data": [ + { + "id": "1848781937210802364", + "text": "ever wanted to discover trends.. before they're trends?...", + "created_at": "2024-01-15T10:30:00.000Z", + "public_metrics": { + "retweet_count": 42, + "reply_count": 5, + "like_count": 156, + "quote_count": 8 + }, + "edit_history_tweet_ids": ["1848781937210802364"] + }, + { + "id": "1847990559081648620", + "text": "posting is just journaling with an audience", + "created_at": "2024-01-14T15:20:00.000Z", + "public_metrics": { + "retweet_count": 28, + "reply_count": 12, + "like_count": 89, + "quote_count": 3 + }, + "edit_history_tweet_ids": ["1847990559081648620"] + } + ], + "meta": { + "result_count": 2, + "next_token": "7140dibdnow9c7btw481s8m561gat797rboud5r80xvzm" + } } -``` \ No newline at end of file +``` + +--- + +## Filter by time range + +Get Retweeted Posts from a specific period: + + + +```bash cURL +curl "https://api.x.com/2/users/reposts_of_me?\ +start_time=2024-01-01T00%3A00%3A00Z&\ +end_time=2024-01-31T23%3A59%3A59Z&\ +tweet.fields=created_at,public_metrics" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get Retweeted Posts from a time range +for page in client.posts.get_reposts_of_me( + start_time="2024-01-01T00:00:00Z", + end_time="2024-01-31T23:59:59Z", + tweet_fields=["created_at", "public_metrics"] +): + for post in page.data: + print(f"{post.created_at}: {post.text[:50]}...") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Get Retweeted Posts from a time range +const paginator = client.posts.getRepostsOfMe({ + startTime: "2024-01-01T00:00:00Z", + endTime: "2024-01-31T23:59:59Z", + tweetFields: ["created_at", "public_metrics"], +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); + }); +} +``` + + + +--- + +## Common parameters + +| Parameter | Description | Default | +|:----------|:------------|:--------| +| `max_results` | Results per page (1-100) | 10 | +| `start_time` | Oldest Post timestamp (ISO 8601) | — | +| `end_time` | Newest Post timestamp (ISO 8601) | — | +| `pagination_token` | Token for next page | — | +| `tweet.fields` | Additional Post fields | `id`, `text` | + +--- + +## Next steps + + + + See who Retweeted a Post + + + Retweet and undo Retweets + + + Full endpoint documentation + + diff --git a/x-api/posts/search/integrate/build-a-query.mdx b/x-api/posts/search/integrate/build-a-query.mdx index 458228eff..396c3c1fe 100644 --- a/x-api/posts/search/integrate/build-a-query.mdx +++ b/x-api/posts/search/integrate/build-a-query.mdx @@ -1,225 +1,285 @@ --- title: Build a query sidebarTitle: Build a query +description: Learn how to build search queries using operators keywords: ["build query", "search query", "query builder", "search operators", "query syntax", "build search query", "query guide"] --- -## Building queries for Search Posts +The search endpoints accept a single query with a GET request and return a set of historical Posts that match the query. Queries are made up of operators that match on a variety of Post attributes. -The search endpoints accept a single query with a GET request and return a set of historical Posts that match the query.  Queries are made up of operators that are used to match on a variety of Post attributes.  - -### Table of contents +--- -* [Building a query](#build) -* [Query limitations](#limits) -* [Operator availability](#availability) -* [Operator types: standalone and conjunction-required](#types) -* [Boolean operators and grouping](#boolean) -* [Order of operations](#order-of-operations) -* [Punctuation, diacritics, and case sensitivity](#punctuation) -* [Specificity and efficiency](#specificity) -* [Quote Tweet matching behavior](#quote-tweets) -* [Iteratively building a query](#iterative) -* [Adding a query to your request](#adding-a-query) -* [Query examples](#examples) -* [List of operators](#list) +## Query limitations +Your queries will be limited depending on which [access level](/x-api/getting-started/about-x-api) you are using: -### Building a query +| Access level | Recent search | Full-archive search | +| :--- | :--- | :--- | +| Self-serve | 512 characters | 1,024 characters | +| Enterprise | 4,096 characters | 4,096 characters | -#### Query limitations +--- -Your queries will be limited depending on which [access level](/x-api/getting-started/about-x-api) you are using.  +## Operator availability -If you have Basic or Pro access, your query can be 512 characters long for recent search endpoint.  +While most operators are available to any developer, some are reserved for certain access levels: -If you have Pro access, your query can be 1,024 characters long for full archive search endpoint.  +* **Core operators:** Available when using any [Project](/resources/fundamentals/developer-apps) +* **Advanced operators:** Available when using a Project with certain access levels -#### Operator availability +See the complete [list of operators](/x-api/posts/search/integrate/operators) for availability details. -While most operators are available to any developer, there are several that are reserved for certain access levels. We list which access level each operator is available to in the [list of operators](/x-api/posts/search/integrate/operators) table using the following labels: +--- -* **Core operators:** Available when using any [Project](/resources/fundamentals/projects). -* **Advanced operators:** Available when using a Project with certain access level +## Operator types: standalone and conjunction-required -#### Operator types: standalone and conjunction-required +**Standalone operators** can be used alone or together with any other operators (including those that require conjunction). -**Standalone operators** can be used alone or together with any other operators (including those that require conjunction). +For example, this query works because `#hashtag` is a standalone operator: -For example, the following query will work because it uses the `#hashtag` operator, which is standalone: +``` +#xapiv2 +``` -`#xapiv2` +**Conjunction-required operators** cannot be used by themselves in a query; they can only be used when at least one standalone operator is included. This is because using these operators alone would match an extremely high volume of Posts. -**Conjunction-required** operators cannot be used by themselves in a query; they can only be used when at least one standalone operator is included in the query. This is because using these operators alone would be far too general, and would match on an extremely high volume of Posts. +For example, the following queries are **not supported** since they contain only conjunction-required operators: -For example, the following queries are not supported since they contain only conjunction-required operators: +``` +has:media +``` -`has:media` +``` +has:links OR is:retweet +``` -`has:links OR is:retweet` +If we add a standalone operator, such as the phrase `"X data"`, the query works properly: -If we add in a standalone operator, such as the phrase `"X data"`, the query would then work properly.  +``` +"X data" has:mentions (has:media OR has:links) +``` -`"X data" has:mentions (has:media OR has:links)` +--- -#### Boolean operators and grouping +## Boolean operators and grouping -If you would like to string together multiple operators in a single query, you have the following tools at your disposal: +String together multiple operators using these tools: -| | | -| :--- | :--- | -| **AND logic** | Successive operators with a space between them will result in boolean "AND" logic, meaning that Posts will match only if both conditions are met. For example, `snow day #NoSchool` will match Posts containing the terms snow and day and the hashtag #NoSchool. | -| **OR logic** | Successive operators with OR between them will result in OR logic, meaning that Posts will match if either condition is met. For example, specifying `grumpy OR cat OR #meme` will match any Posts containing at least the terms grumpy or cat, or the hashtag #meme. | -| **NOT logic, negation** | Prepend a dash (-) to a keyword (or any operator) to negate it (NOT). For example, `cat #meme -grumpy` will match Posts containing the hashtag #meme and the term cat, but only if they do not contain the term grumpy. One common query clause is `-is:retweet`, which will not match on Retweets, thus matching only on original Posts, Quote Tweets, and replies. All operators can be negated, but negated operators cannot be used alone. | -| **Grouping** | You can use parentheses to group operators together. For example, `(grumpy cat) OR (#meme has:images)` will return either Posts containing the terms grumpy and cat, or Posts with images containing the hashtag #meme. Note that ANDs are applied first, then ORs are applied. | +| Operator | Description | Example | +| :--- | :--- | :--- | +| **AND** (space) | Posts must match both conditions | `snow day #NoSchool` matches Posts with "snow" AND "day" AND #NoSchool | +| **OR** | Posts must match either condition | `grumpy OR cat OR #meme` matches Posts with "grumpy" OR "cat" OR #meme | +| **NOT** (dash) | Exclude Posts matching this condition | `cat #meme -grumpy` matches Posts with "cat" and #meme but NOT "grumpy" | +| **Grouping** (parentheses) | Group operators together | `(grumpy cat) OR (#meme has:images)` matches either group | + **A note on negations** -The operators `-is:nullcast` must always be negated. +- The operator `-is:nullcast` must always be negated +- Negated operators cannot be used alone +- Do not negate grouped operators. Instead of `skiing -(snow OR day OR noschool)`, use `skiing -snow -day -noschool` + -Negated operators cannot be used alone. +--- -Do not negate a set of operators grouped together in a set of parentheses. Instead, negate each individual operator. For example, instead of using `skiing -(snow OR day OR noschool)`, we suggest that you use `skiing -snow -day -noschool`.  +## Order of operations -#### Order of operations +When combining AND and OR: -When combining AND and OR functionality, the following order of operations will dictate how your query is evaluated. +1. Operators connected by AND logic are combined first +2. Then, operators connected with OR logic are applied -1. Operators connected by AND logic are combined first -2. Then, operators connected with OR logic are applied +**Examples:** +| Query | Evaluated as | +| :--- | :--- | +| `apple OR iphone ipad` | `apple OR (iphone ipad)` | +| `ipad iphone OR android` | `(iphone ipad) OR android` | -For example: +To eliminate uncertainty, use parentheses: -* `apple OR iphone ipad` would be evaluated as `apple OR (iphone ipad)` -* `ipad iphone OR android` would be evaluated as `(iphone ipad) OR android` +``` +(apple OR iphone) ipad +``` -To eliminate uncertainty and ensure that your query is evaluated as intended, group terms together with parentheses where appropriate.  +``` +iphone (ipad OR android) +``` -For example: +--- + +## Punctuation, diacritics, and case sensitivity -* `(apple OR iphone) ipad` -* `iphone (ipad OR android)` +**Diacritics:** Search queries with accents or diacritics match Posts both with and without the accents. For example, `Diacrítica` matches both _Diacrítica_ and _Diacritica_. -#### Punctuation, diacritics, and case sensitivity +**Case sensitivity:** All operators are case-insensitive. The query `cat` matches _cat_, _CAT_, and _Cat_. -If you specify a keyword or hashtag query with character accents or diacritics, it will match Post text that contains both the term with the accents and diacritics, as well as those terms with normal characters. For example, queries with a keyword `Diacrítica` or hashtag `#cumpleaños` will match _Diacrítica_ or _#cumpleaños_, as well as with _Diacritica_ or _#cumpleanos_ without the tilde í or eñe. + +**Filtered stream behaves differently** -Characters with accents or diacritics are treated the same as normal characters and are not treated as word boundaries. For example, a query with the keyword `cumpleaños` would only match activities containing the word _cumpleaños_ and would not match activities containing _cumplea_, _cumplean_, or _os_. +When [building filtered stream rules](/x-api/posts/filtered-stream/integrate/build-a-rule), keywords with accents only match Posts that also include the accent. For example, `Diacrítica` only matches _Diacrítica_, not _Diacritica_. + -All operators are evaluated in a case-insensitive manner. For example, the query `cat` will match Posts with all of the following: _cat_, _CAT_, _Cat_. +--- -The [filtered stream](/x-api/posts/filtered-stream) matching behavior acts differently from Search Posts. When [building a filtered stream rule](/x-api/posts/filtered-stream#building-rules-for-filtered-stream), know that keywords and hashtags that include accents and diacritics will only match on terms that also include the accent and diacritic, and will not match on terms that use normal characters instead.  +## Quote Tweet matching -For example, filtered stream rules that include a keyword `Diacrítica` or hashtag `#cumpleaños` will only match the terms _Diacrítica_ and _#cumpleaños_, and will not match on _Diacritica_ or _#cumpleanos_ without the tilde í or eñe +When using Search Posts, operators match on the Quote Tweet's content but **not** on the content from the original Post that was quoted. -#### Specificity and efficiency + +[Filtered stream](/x-api/posts/filtered-stream/introduction) behaves differently—it matches on both the Quote Tweet and the original Post's content. + -When you start to build your query, it is important to keep a few things in mind. +--- -* Using broad, standalone operators for your query such as a single keyword or #hashtag is generally not recommended since it will likely match on a massive volume of Posts. Creating a more robust query will result in a more specific set of matching Posts, and will hopefully reduce the amount of noise in the payload that you will need to sift through to find valuable insights.  - * For example, if your query was just the keyword `happy` you will likely get anywhere from 200,000 - 300,000 Posts per day. - * Adding more conditional operators narrows your search results, for example `(happy OR happiness) place_country:GB -birthday -is:retweet` -* Writing efficient queries is also beneficial for staying within the characters query length restriction. The character count includes the entire query string including spaces and operators. - * For example, the following query is 59 characters long: `(happy OR happiness) place_country:GB -birthday -is:retweet` +## Specificity and efficiency -#### Quote Tweet matching behavior + +Using broad operators like a single keyword or hashtag is not recommended—it will match a massive volume of Posts and quickly consume your usage limits. + -When using the Search Posts endpoints, operators will not match on the content from the original Post that was quoted, but will match on the content included in the Quote Tweet. +**Tips for building effective queries:** -However, please note that [filtered stream](/x-api/posts/filtered-stream) will match on both the content from the original P that was quoted and the Quote Tweet's content. +1. **Start specific, then broaden** — Create targeted queries that return relevant results +2. **Use multiple operators** — Combine operators to narrow results +3. **Watch your character count** — The entire query string counts toward the limit -#### Iteratively building a query +**Example progression:** -##### Test your query early and often +``` +# Too broad - 200,000+ Posts per day +happy -Getting a query to return the "right" results the first time is rare. There is so much on X that may or may not be obvious at first and the query syntax described above may be hard to match to your desired search. As you build a query, it is important for you to periodically test it out. +# Better - adds language filter and exclusions +(happy OR happiness) lang:en -birthday -is:retweet -For this section, we are going to start with the following query and adjust it based on the results that we receive during our test:  +# Even better - 59 characters, more specific +(happy OR happiness) place_country:GB -birthday -is:retweet +``` -`happy OR happiness` +--- -##### Use results to narrow the query +## Iteratively building a query -As you test the query, you should scan the returned Posts to see if they include the data that you are expecting and hoping to receive. Starting with a broad query and a superset of Post matches allows you to review the result and narrow the query to filter out undesired results.   +### Step 1: Start with a basic query -When we tested the example query, we noticed that we were getting Po in a variety of different languages. In this situation, we want to only receive Posts that are in english, so we’re going to add the `lang:` operator: +``` +happy OR happiness +``` -`(happy OR happiness) lang:en` +### Step 2: Test and narrow based on results -The test delivered a number of Posts wishing people a happy birthday, so we are going to add `-birthday` as a negated keyword operator. We also want to only receive original Posts, so we’ve added the negated `-is:retweet` operator: +We noticed Posts in many languages. Add a language filter: -`(happy OR happiness) lang:en -birthday -is:retweet` +``` +(happy OR happiness) lang:en +``` -##### Adjust for inclusion where needed +We're getting birthday wishes. Exclude them and Retweets: -If you notice that you are not receiving data that you expect and know that there are existing Posts that should return, you may need to broaden your query by removing operators that may be filtering out the desired data.  +``` +(happy OR happiness) lang:en -birthday -is:retweet +``` -For our example, we noticed that there were other Posts in our personal timeline that expressed the emotion that we are looking for and weren’t included in the test results. To ensure we have greater coverage, we are going to add the keywords, `excited` and `elated`. +### Step 3: Broaden for better coverage -`(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet` +We want to capture more sentiment. Add related keywords: -##### Adjust for popular trends/bursts over the time period +``` +(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet +``` -Trends come and go on X quickly. Maintaining your query should be an active process. If you plan to use a query for a while, we suggest that you periodically check in on the data that you are receiving to see if you need to make any adjustments. +### Step 4: Adjust for trends -In our example, we notice that we started to receive some Posts that are wishing people a “happy holidays”. Since we don’t want these Posts included in our results, we are going to add a negated `-holidays` keyword. +Holiday Posts are appearing. Exclude them: -`(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet -holidays` +``` +(happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet -holidays +``` -### Adding a query to your request +--- -To add your query to your request, you must use the `query` parameter. As with any query parameters, you must make sure to HTTP encode the query that you developed. +## Adding a query to your request -Here is an example of what this might look like using a cURL command, with an additional `tweet.fields` and `max_results` parameter included. If you would like to use this command, please make sure to replace `$BEARER_TOKEN` with your own [Bearer Token](/resources/fundamentals/authentication#oauth-2-0): +Use the `query` parameter and HTTP encode your query: ```bash -curl https://api.x.com/2/tweets/search/recent?query=cat%20has%3Amedia%20-grumpy&tweet.fields=created_at&max_results=100 -H "Authorization: Bearer $BEARER_TOKEN" +curl "https://api.x.com/2/tweets/search/recent?\ +query=cat%20has%3Amedia%20-grumpy&\ +tweet.fields=created_at&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` -### Query examples +--- + +## Query examples -#### Tracking a natural disaster +### Tracking a natural disaster -The following query matched on original Posts coming from weather agencies and gauges that discuss Hurricane Harvey, which hit Houston in 2017. +Match Posts from weather agencies about Hurricane Harvey: -Here is what the query would look like without the HTTP encoding: +**Query:** -`has:geo (from:NWSNHC OR from:NHC\_Atlantic OR from:NWSHouston OR from:NWSSanAntonio OR from:USGS\_TexasRain OR from:USGS_TexasFlood OR from:JeffLindner1) -is:retweet` +``` +has:geo (from:NWSNHC OR from:NHC_Atlantic OR from:NWSHouston OR from:NWSSanAntonio OR from:USGS_TexasRain OR from:USGS_TexasFlood OR from:JeffLindner1) -is:retweet +``` -And here is what the query would look like with the HTTP encoding, the query parameter, and the recent search URI: +**Full request URL:** -`https://api.x.com/2/tweets/search/recent?query=-is%3Aretweet%20has%3Ageo%20(from%3ANWSNHC%20OR%20from%3ANHC\_Atlantic%20OR%20from%3ANWSHouston%20OR%20from%3ANWSSanAntonio%20OR%20from%3AUSGS\_TexasRain%20OR%20from%3AUSGS_TexasFlood%20OR%20from%3AJeffLindner1)` +``` +https://api.x.com/2/tweets/search/recent?query=has%3Ageo%20(from%3ANWSNHC%20OR%20from%3ANHC_Atlantic%20OR%20from%3ANWSHouston%20OR%20from%3ANWSSanAntonio%20OR%20from%3AUSGS_TexasRain%20OR%20from%3AUSGS_TexasFlood%20OR%20from%3AJeffLindner1)%20-is%3Aretweet +``` -#### Reviewing the sentiment of a conversation +### Sentiment analysis for #nowplaying -The next rule could be used to better understand the sentiment of the conversation developing around the hashtag, _#nowplaying_, but scoped to just Posts published within North America. +**Positive sentiment:** -Here is what the two different queries, one for positive and one for negative, would look like without the HTTP encoding: +``` +#nowplaying (happy OR exciting OR excited OR favorite OR fav OR amazing OR lovely OR incredible) (place_country:US OR place_country:MX OR place_country:CA) -horrible -worst -sucks -bad -disappointing +``` -`#nowplaying (happy OR exciting OR excited OR favorite OR fav OR amazing OR lovely OR incredible) (place\_country:US OR place\_country:MX OR place_country:CA) -horrible -worst -sucks -bad -disappointing` +**Negative sentiment:** -`#nowplaying (horrible OR worst OR sucks OR bad OR disappointing) (place\_country:US OR place\_country:MX OR place_country:CA) -happy -exciting -excited -favorite -fav -amazing -lovely -incredible` +``` +#nowplaying (horrible OR worst OR sucks OR bad OR disappointing) (place_country:US OR place_country:MX OR place_country:CA) -happy -exciting -excited -favorite -fav -amazing -lovely -incredible +``` -And here is what the query would look like with the HTTP encoding, the query parameter, and the recent search URI: +### Using Post annotations -`https://api.x.com/2/tweets/search/recent?query=%23nowplaying%20(happy%20OR%20exciting%20OR%20excited%20OR%20favorite%20OR%20fav%20OR%20amazing%20OR%20lovely%20OR%20incredible)%20(place\_country%3AUS%20OR%20place\_country%3AMX%20OR%20place_country%3ACA)%20-horrible%20-worst%20-sucks%20-bad%20-disappointing` +Find Japanese Posts about pets (not cats) with images using the `context:` operator: -`https://api.x.com/2/tweets/search/recent?query=%23nowplaying%20(horrible%20OR%20worst%20OR%20sucks%20OR%20bad%20OR%20disappointing)%20(place\_country%3AUS%20OR%20place\_country%3AMX%20OR%20place_country%3ACA)%20-happy%20-exciting%20-excited%20-favorite%20-fav%20-amazing%20-lovely%20-incredible` +First, use [Post lookup](/x-api/posts/lookup/introduction) with `tweet.fields=context_annotations` to identify domain.entity IDs: -#### Find Posts that relate to a specific Post annotation +- Cats: `domain` 66, `entity` 852262932607926273 +- Pets: `domain` 65, `entity` 852262932607926273 -This rule was built to search for original Posts that included an image of a pet that is not a cat, where the language identified in the Post is Japanese. To do this, we used the `context:` operator to take advantage of the [Post annotation](/x-api/fundamentals/post-annotations) functionality. We first used the [Post lookup](/x-api/posts/lookup/introduction) endpoint and the `tweet.fields=context_annotations` fields parameter to identify which domain.entity IDs we need to use in our query: +**Query:** -* Posts that relate to cats return `domain` 66 (Interests and Hobbies category) with entity 852262932607926273 (Cats).  -* Posts that relate to pets return `domain` 65 (Interests and Hobbies Vertical) with entity 852262932607926273 (Pets).  +``` +context:65.852262932607926273 -context:66.852262932607926273 -is:retweet has:images lang:ja +``` -Here is what the query would look like without the HTTP encoding: +--- -`context:65.852262932607926273 -context:66.852262932607926273 -is:retweet has:images lang:ja` +## Tools -And here is what the query would look like with the HTTP encoding, the query parameter, and the recent search URI: + + Build and test your queries interactively + -`https://api.x.com/2/tweets/search/recent?query=context%3A65.852262932607926273%20-context%3A66.852262932607926273%20-is%3Aretweet%20has%3Aimages%20lang%3Aja` +--- -Try out the [query builder tool](https://developer.x.com/apitools/query?query=) for additional support. \ No newline at end of file +## Next steps + + + + Complete list of available operators + + + Make your first search request + + + Full integration documentation + + diff --git a/x-api/posts/search/integrate/overview.mdx b/x-api/posts/search/integrate/overview.mdx index bc2643a4f..dc4182307 100644 --- a/x-api/posts/search/integrate/overview.mdx +++ b/x-api/posts/search/integrate/overview.mdx @@ -1,146 +1,265 @@ --- -title: Overview +title: Integration Guide sidebarTitle: Overview +description: Key concepts and best practices for integrating Search Posts keywords: ["search integration", "search overview", "search guide", "search implementation", "tweet search integration"] --- -## How to integrate with the Search Posts endpoints +This guide covers the key concepts you need to integrate the Search Posts endpoints into your application. -This page contains information on several tools and key concepts that you should be aware of as you integrate the recent search or full archive search endpoints into your system. We’ve split the page into the following sections: - -* [Helpful tools](#helpful-tools) -* Key concepts - * [Authentication](#authentication) - * [Developer portal, Projects, and Apps](#developer-portal) - * [Rate limits](#rate-limits) - * [Fields and expansions](#fields-expansions) - * [Metrics](#metrics) - * [Building search queries](#queries) - * [Pagination](#pagination) - * [Post caps](#tweet-caps)  - - * [Post edits](#tweet-edits)  - - -### Helpful tools +--- -Before we start to explore some key concepts, we recommend that you use one of the following tools or code samples to start testing the functionality of these endpoints. +## Authentication -#### Code samples +### Recent search -Interested in getting set up with these endpoints with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [GitHub page](https://github.com/xdevplatform/Twitter-API-v2-sample-code), including a [Python client](https://github.com/xdevplatform/search-tweets-python) and a [Ruby client](https://github.com/xdevplatform/search-tweets-ruby). +Recent search supports multiple authentication methods: -#### Libraries +| Method | Use case | +|:-------|:---------| +| [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Public Post data | +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Private metrics | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Private metrics | -Take advantage of one of our many [community third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the appropriate version tag. +### Full-archive search -#### Postman +Full-archive search only supports [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) authentication. -Postman is a great tool that you can use to test out these endpoints. Each Postman request includes all of the given endpoint’s parameters to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) page. -  + +Private metrics (`non_public_metrics`, `organic_metrics`, `promoted_metrics`) are not available with full-archive search because it only supports App-Only authentication. + -### Key concepts +--- -#### Authentication +## Building queries -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, OAuth 2.0 App-Only, or OAuth 2.0 Authorization Code with PKCE to authenticate your requests to the recent search endpoint. You must use OAuth 2.0 App-Only when using the full archive search endpoint. +Queries use operators to match Posts. Combine operators with boolean logic: -[OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) just requires that you pass an [OAuth 2.0 App Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) with your request. You can either generate an App Access Token from directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. +``` +(AI OR "machine learning") lang:en -is:retweet has:links +``` -[OAuth 1.0a User Context](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) requires you to utilize your API Keys, user Access Tokens, and a handful of other parameters to [create an authorization header](https://developer-staging.x.com/resources/fundamentals/authentication/authorizing-a-request), which you will then pass with your request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](https://developer-staging.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens).  +### Query length limits -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. If you would like to request a Post or private metrics from these endpoints, you will need to use a either OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE, which will ensure that you have the proper permissions from the user that owns that content. +| Access level | Recent search | Full-archive search | +|:-------------|:--------------|:--------------------| +| Self-serve | 512 chars | 1,024 chars | +| Enterprise | 4,096 chars | 4,096 chars | -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +### Operator types -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +| Type | Description | Example | +|:-----|:------------|:--------| +| **Standalone** | Can be used alone | `#python`, `from:user` | +| **Conjunction-required** | Must be used with a standalone operator | `has:media`, `is:retweet` | - -**Please note** + + Learn query syntax in detail + -If you are requesting the following [fields](/x-api/fundamentals/fields), OAuth 1.0a User Context or OAuth 2.0 Authorization Code is required:  + + See all available operators + -* tweet.fields.non_public_metrics -* tweet.fields.promoted_metrics -* tweet.fields.organic_metrics -* media.fields.non_public_metrics -* media.fields.promoted_metrics -* media.fields.organic_metrics - -#### -Developer portal, Projects, and developer Apps +--- -To work with any X API v2 endpoints, you must have [signed up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a Project within that account, and created a developer App within that Project. Your keys and tokens within that developer App will work for these search endpoints. +## Fields and expansions + +By default, the response includes only `id`, `text`, and `edit_history_tweet_ids`. Use parameters to request additional data. + +### Example request + + + +```bash cURL +curl "https://api.x.com/2/tweets/search/recent?\ +query=python&\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id,attachments.media_keys&\ +user.fields=username,verified&\ +media.fields=url,type" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Search recent Posts +for page in client.posts.search_recent( + query="python", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id", "attachments.media_keys"], + user_fields=["username", "verified"], + media_fields=["url", "type"], + max_results=100 +): + for post in page.data: + print(f"{post.text} - Likes: {post.public_metrics.like_count}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Search recent Posts +const paginator = client.posts.searchRecent("python", { + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id", "attachments.media_keys"], + userFields: ["username", "verified"], + mediaFields: ["url", "type"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text} - Likes: ${post.public_metrics?.like_count}`); + }); +} +``` + + + +### Available expansions + +| Expansion | Returns | +|:----------|:--------| +| `author_id` | Author's user object | +| `attachments.media_keys` | Attached media objects | +| `attachments.poll_ids` | Attached poll objects | +| `referenced_tweets.id` | Quoted or replied-to Posts | +| `geo.place_id` | Place objects | +| `entities.mentions.username` | Mentioned user objects | + + + Learn more about customizing responses + -You can use keys and tokens from a Project with any [access level](/x-api/getting-started/about-x-api) to make requests to the recent search endpoint. However, you will need to use Project with the Pro or Enterprise access level to make requests to the full archive search endpoint. If you have [Enterprise access](/x-api/getting-started/getting-access), you will have access to additional functionality, including the availability of additional operators and longer query lengths. -  +--- -#### Rate limits +## Pagination -Every day, many thousands of developers make requests to the X API. To help manage the volume, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that every developer can make on behalf of an app or on behalf of an authenticated user. +Search endpoints return results in pages. Use the `next_token` from the response to fetch additional pages. -There are different rate limits applied for these endpoints depending on which authentication method is being used. The app-level rate limits apply to an App making requests using OAuth 2.0 App-Only, whereas the user-level rate limit applies to requests being made on behalf of the specific authorizing user using OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE. These two rate limits are based on the frequency of requests within a 15-minute window. +### How it works -For example, an app using OAuth 2.0 App-Only auth to make requests to the recent search endpoint can make 450 requests (including pagination requests) within a 15-minute timeframe. That same app, within the same 15-minute timeframe and with two different authenticated users (using OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE) can make up to 180 requests (including pagination requests) to the recent search endpoint for each authenticated user. -  +1. Make your initial request with `max_results` +2. Check the `meta` object for `next_token` +3. Include `next_token` in subsequent requests +4. Repeat until no `next_token` is returned -#### Fields and expansions +### Example -The X API v2 allows you to select exactly which data you want returned from the API using [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions). The expansion parameter allows you to expand objects referenced in the payload. For example, this endpoint allows you to request poll, place, media, and other objects using the expansions parameter. + -The fields parameters allows you to select exactly which fields within the different data objects you would like to receive. By default, the primary Post object returned by these endpoints include the id and text fields (in addition to edit_history_tweet_ids for Posts created after that feature was launched). To receive additional fields such as author_id or public_metrics, you will have to specifically request those using the fields parameters. Some important fields that you may want to consider using in your integration are our poll data, [metrics](/x-api/fundamentals/metrics), [Post annotations](/x-api/fundamentals/post-annotations), and [conversation ID](/x-api/fundamentals/conversation-id) fields. +```bash cURL +# First request +curl "https://api.x.com/2/tweets/search/recent?query=python&max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" -We’ve added a guide on how to [use fields and expansions together](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). -  +# Subsequent request with pagination token +curl "https://api.x.com/2/tweets/search/recent?query=python&max_results=100&next_token=NEXT_TOKEN" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -#### Metrics +```python Python SDK +from xdk import Client -The X API v2 endpoints allow you to request metrics directly from the returned objects, assuming you pass the proper [fields](/x-api/fundamentals/fields) with your request. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -There are some limitations with Post metrics that you should be aware of, specifically related to user privacy and the following response fields: +# The SDK handles pagination automatically +all_posts = [] -* tweet.fields.non_public_metrics -* tweet.fields.promoted_metrics -* tweet.fields.organic_metrics -* media.fields.non_public_metrics -* media.fields.promoted_metrics -* media.fields.organic_metrics +for page in client.posts.search_recent(query="python", max_results=100): + if page.data: + all_posts.extend(page.data) +print(f"Found {len(all_posts)} posts") +``` -The noted fields include private metrics data, meaning you must be authorized by the Post's publisher to retrieve this data on their behalf when using the recent search endpoint, meaning you must use [OAuth 1.0a User Context](/resources/fundamentals/authentication). Since you can only use this authentication method using recent search, you will not be able to retrieve these metrics via the full archive search endpoint. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -For example, in order to receive `non_public_metrics` for user ID 1234's Posts, you will need to include access tokens associated with that user in your request. You can have users authorize your app and receive a set of access tokens associated with them by using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -All `non_public_metrics`, `organic_metrics`, and promoted_metrics are only available for Posts created in the last 30 days. This means that when you are requesting the noted fields, the results will automatically adjust to only include Posts from the last 30 days. +async function getAllResults(query) { + const allPosts = []; -If these noted fields are requested, only Posts that are authored by the authenticated user will be returned, all other Posts will receive an error message. -  + // The SDK handles pagination automatically + const paginator = client.posts.searchRecent(query, { maxResults: 100 }); -#### Building search queries + for await (const page of paginator) { + if (page.data) { + allPosts.push(...page.data); + } + } -The central feature of these endpoints is their use of a single search query to filter the Posts that deliver to you. These queries are made up of operators that match on Post and user attributes, such as message keywords, hashtags, and URLs. Operators can be combined into queries with boolean logic and parentheses to help refine the query's matching behavior. + return allPosts; +} -You can use our guide on [how to build a query](/x-api/posts/search/integrate/build-a-query) to learn more. +// Usage +const posts = await getAllResults("python"); +console.log(`Found ${posts.length} posts`); +``` -#### Pagination + -These endpoints utilize pagination so that responses are returned quickly. In cases where there are more results than what can be sent in a single response (up to 100 Posts for recent search and 500 for full-archive search) you will need to paginate. Use the max_results parameter to identify how many results will return per page, and the pagination_token parameter to return the next page of results. You can learn more by reviewing our [pagination guide](/x-api/fundamentals/pagination). + + Learn more about pagination + +--- -#### Post caps +## Post edits -The Search Posts endpoints are limited in the number of Posts that they can return in a given month, regardless of pagination. +Posts can be edited up to 5 times within 30 minutes. The search endpoints always return the most recent version. -Regardless of which search endpoint you use, the Posts returned will count towards the Project-level [Post caps](/x-api/fundamentals/post-cap). Usage is shown in the developer portal, and the 'month' starts on your subscription renewal day shown on the [developer portal dashboard](https://developer.x.com/en/portal/dashboard). +### Considerations -**Post edits** +- `edit_history_tweet_ids` contains all Post IDs (oldest first) +- Posts fetched after the 30-minute window represent the final version +- For near-real-time use cases, recently-published Posts may still be edited -Posts that are eligible for edits can be edited up to five times in the 30 minutes after the original Post was published. The search endpoints will always provide the latest version of the Post. If you only request Posts that were published 30 or more minutes ago, you will always receive the final version of the Post. However, if you have a near-real-time use case, and are querying Posts published within the last thirty minutes, those Posts could have been edited after you received them. These Posts can be rehydrated with search, or the Post Lookup endpoint to confirm their final state. To learn more about how Post edits work, see the [Post edits fundamentals](/x-api/fundamentals/edit-posts) page.   + + Learn more about Post edits + -**Next steps** +--- -[Make your first request to a Search Posts endpoint](/x-api/posts/search/quickstart/recent-search) +## Best practices + + + + Use multiple operators to narrow results and reduce noise. + + + Start broad, then refine based on results. + + + Implement proper pagination for large result sets. + + + Store results locally to avoid repeated requests. + + -[See a full list of parameters, fields, and more in our API Reference pages](/x-api/posts/recent-search) +--- -[Get support or troubleshoot an error](https://developer.x.com/en/support/x-api) +## Next steps + + + + Master query syntax + + + All available operators + + + Handle large result sets + + + Full endpoint documentation + + diff --git a/x-api/posts/search/introduction.mdx b/x-api/posts/search/introduction.mdx index 334222672..20095322e 100644 --- a/x-api/posts/search/introduction.mdx +++ b/x-api/posts/search/introduction.mdx @@ -1,92 +1,168 @@ --- -title: Introduction +title: Search Posts sidebarTitle: Introduction +description: Search for Posts by keyword, hashtag, user, and more with powerful query operators keywords: ["search tweets", "search posts", "recent search", "full archive search", "tweet search", "search API", "search queries", "search operators", "historical search"] --- import { Button } from '/snippets/button.mdx'; -Searching for Posts is an important feature used to surface X conversations about a specific topic or event. While this functionality is present in X, these endpoints provide greater flexibility and power when filtering for and ingesting Posts so you can find relevant data for your research more easily; build out near-real-time ‘listening’ applications; or generally explore, analyze, and/or act upon Posts related to a topic of interest.  +The Search Posts endpoints let you find Posts matching specific criteria using powerful query operators. Search for keywords, hashtags, mentions, URLs, and more. -We offer two endpoints that allow you to search for Posts: Recent search and full-archive search. Both of these REST endpoints share a common design and features, including their use of a single search query to filter for Posts around a specific topic. These search queries are created with a set of operators that match on Post and user attributes, such as message keywords, hashtags, and URLs. Operators can be combined into queries with boolean logic and parentheses to help refine the queries matching behavior.  +## Overview -Both the recent search and the full-archive search endpoints provide edited Post metadata. All objects for Posts created since September 29, 2022, include Post edit metadata, even if the Post was never edited. Each time a Post is edited, a new Post ID is created. A Post's edit history is documented by an array of Post IDs, starting with the original ID. +X offers two search endpoints with different time ranges and access requirements: -These endpoints will always return the most recent edit, along with any edit history. Any Post collected after its 30-minute edit window will represent its final version. To learn more about Edit Post metadata, check out the [Edit Posts fundamentals](/x-api/fundamentals/edit-posts) page. + + + Search Posts from the **last 7 days**. Available to all developers. + + + Search the **complete archive** back to 2006. Available to pay-per-use and Enterprise customers. + + -Once you’ve set up your query and start receiving Posts, these endpoints support navigating the results both by time and Post ID ranges. This is designed to support two common use cases:  - -* **Get historical**: Requests are for a period of interest, with no focus on the real-time nature of the data. A single request is made, and all matching data is delivered using pagination as needed. This is the default mode for Search Posts. -* **Polling** or **listening**: Requests are made in a "any new Posts since my last request?" mode. Requests are made on a continual basis, and typically there is a use case focused on near real-time 'listening' for Posts of interest. - -Many operators and query limits are exclusive to [Enterprise access](/x-api/getting-started/about-x-api), meaning that you must use keys and tokens from an App within a [Project](/resources/fundamentals/projects) with Enterprise access to utilize the additional functionality. You can learn more about this in the endpoint sections below. - -Both the recent search and the full-archive search endpoints returned Posts contribute to the monthly [Post cap](/x-api/fundamentals/post-cap). +--- -**Account setup** +## Use cases -To access these endpoints, you will need: +- **Brand monitoring** — Track mentions of your brand or products +- **Trend analysis** — Analyze conversations around topics or events +- **Research** — Gather data for academic or market research +- **Real-time listening** — Build applications that react to new Posts -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +--- -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +## Endpoints -## Recent search +| Method | Endpoint | Description | Access | +|:-------|:---------|:------------|:-------| +| GET | [`/2/tweets/search/recent`](/x-api/posts/search-recent-posts) | Search Posts from last 7 days | All developers | +| GET | [`/2/tweets/search/all`](/x-api/posts/search-all-posts) | Search complete Post archive | Pay-per-use, Enterprise | -The recent search endpoint allows you to programmatically access filtered public Posts posted over the last week, and is available to all developers who have a developer account and are using keys and tokens from an [App](/resources/fundamentals/developer-apps) within a [Project](/resources/fundamentals/projects). +--- -You can authenticate your requests with [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). However, if you would like to receive private metrics, or a breakdown of organic and promoted metrics within your Post results, you will have to use OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE, and pass user Access Tokens that are associated with the user that published the given content.  +## Query operators + +Build queries using operators that match on Post attributes: + +``` +(AI OR "artificial intelligence") lang:en -is:retweet has:links +``` + + + + | Operator | Example | Description | + |:---------|:--------|:------------| + | Keyword | `python` | Match Posts containing the word | + | Phrase | `"machine learning"` | Match exact phrase | + | Hashtag | `#AI` | Match Posts with hashtag | + | Mention | `@XDevelopers` | Match Posts mentioning user | + + + + | Operator | Example | Description | + |:---------|:--------|:------------| + | `from:` | `from:elonmusk` | Posts by a user | + | `to:` | `to:XDevelopers` | Replies to a user | + | `retweets_of:` | `retweets_of:X` | Retweets of a user's Posts | + + + + | Operator | Example | Description | + |:---------|:--------|:------------| + | `has:images` | `cat has:images` | Posts with images | + | `has:videos` | `has:videos` | Posts with videos | + | `has:links` | `has:links` | Posts with links | + | `has:mentions` | `has:mentions` | Posts with mentions | + | `url:` | `url:github.com` | Posts with specific URL | + + + + | Operator | Example | Description | + |:---------|:--------|:------------| + | `lang:` | `lang:en` | Posts in a language | + | `-is:retweet` | `-is:retweet` | Exclude retweets | + | `-is:reply` | `-is:reply` | Exclude replies | + | `is:verified` | `is:verified` | Posts by verified users | + + + + + See all available operators and their access requirements + -This endpoint can deliver up to 100 Posts per request in reverse-chronological order, and [pagination](/x-api/posts/search/integrate/paginate) tokens are provided for paging through large sets of matching Posts.  +--- -When using a Project with regular access, you can use the basic set of [operators](/x-api/posts/search/integrate/build-a-query) and can make queries up to 512 characters long. When using a Project with Enterprise access, you have access to additional operators. Projects with Enterprise Access can make queries up to 4096 characters long. +## Recent Search -Learn more about [access levels](/x-api/getting-started/about-x-api). +Search Posts from the **last 7 days**. Available to all developers. +### Features -## Full-archive search +- Up to 100 Posts per request +- Pagination for large result sets +- All core query operators +- 512-character query length (4,096 for Enterprise) -The v2 full-archive search endpoint is only available to Projects with [Pro](/x-api/getting-started/about-x-api "Pro") access and [Enterprise](/x-api/getting-started/about-x-api) access. The endpoint allows you to programmatically access public Posts from the complete archive dating back to the first Post in March 2006, based on your search query. + + + Make your first recent search request + + + Full endpoint documentation + + -You can authenticate your requests to this endpoint using [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), and the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) must come from an App that is within a Project that has Pro or Enterprise access. Since you cannot make a request on behalf of other users (OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE) with this endpoint, you will not be able to pull private [metrics](/x-api/fundamentals/metrics).  +--- -This endpoint can deliver up to 500 Posts per request in reverse-chronological order, and [pagination](/x-api/posts/search/integrate/paginate) tokens are provided for paging through large sets of matching Posts. +## Full-Archive Search -**Note:** If requesting [annotations](/x-api/fundamentals/post-annotations) through the tweet.fields parameter, the max_results parameter is currently limited to a max value of 100. This may change in the future, but please be mindful of this limitation. +Search the **complete Post archive** dating back to March 2006. -Since this endpoint is only available to those that have been approved for Pro and Enterprise access, you have access to the full set of search [operators](/x-api/posts/search/integrate/build-a-query) and can make queries up to 1024 characters long. + +Full-archive search is available to pay-per-use and Enterprise customers. + - -
- -
- -
- -
- -
- -
-
+### Features -**Supporting resources** +- Up to 500 Posts per request +- Access to complete Post history +- All query operators available +- 1,024-character query length (4,096 for Enterprise) -[Learn how to use Postman to make requests](/tutorials/postman-getting-started) + + + Make your first full-archive search request + + + Full endpoint documentation + + -[Troubleshoot an error](https://developer.x.com/en/support/x-api) +--- -[Visit the API reference page](/x-api/posts/recent-search) \ No newline at end of file +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + + + + + Learn query syntax and operators + + + Navigate through large result sets + + + Key concepts and best practices + + + Working code examples + + diff --git a/x-api/posts/search/migrate/enterprise-to-twitter-api-v2.mdx b/x-api/posts/search/migrate/enterprise-to-twitter-api-v2.mdx index 2a9f504ab..90fbd67cf 100644 --- a/x-api/posts/search/migrate/enterprise-to-twitter-api-v2.mdx +++ b/x-api/posts/search/migrate/enterprise-to-twitter-api-v2.mdx @@ -53,7 +53,7 @@ Both versions provide metadata that describes any edit history. Check out the [s **App and Project requirement** -The X API v2 endpoints require that you use credentials from a Project when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project. +The X API v2 endpoints require that you use credentials from a Project when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with an App.   **Available time periods** diff --git a/x-api/posts/search/migrate/overview.mdx b/x-api/posts/search/migrate/overview.mdx index 7afd850cd..07916c784 100644 --- a/x-api/posts/search/migrate/overview.mdx +++ b/x-api/posts/search/migrate/overview.mdx @@ -4,17 +4,13 @@ sidebarTitle: Overview keywords: ["search migration", "search migrate", "v1.1 to v2 search", "search migration guide", "migrate search", "enterprise to v2"] --- -## Comparing X API’s Search Posts endpoints +## Comparing X API's Search Posts endpoints -The v2 Search Tweets endpoint will eventually replace the [standard v1.1 search/posts](/x-api/posts/search/introduction) endpoint and [enterprise Search API](/x-api/enterprise-gnip-2.0/fundamentals/search-api). If you have code, apps, or tools that use an older version of a X search endpoint and are considering migrating to the newer X API v2 endpoints, then this guide is for you.  +The v2 Search Tweets endpoint will eventually replace the [standard v1.1 search/posts](/x-api/posts/search/introduction) endpoint and [enterprise Search API](/x-api/enterprise-gnip-2.0/fundamentals/search-api). If you have code, apps, or tools that use an older version of a X search endpoint and are considering migrating to the newer X API v2 endpoints, then this guide is for you. -This page contains three comparison tables: - -* [Recent search](#recent) -* [Full-archive search](#full-archive) -* [Filtering operator comparison](#operators) +--- -### Recent search comparison +## Recent search comparison The following table compares the various types of recent search endpoints: @@ -26,10 +22,10 @@ The following table compares the various types of recent search endpoints: | Timestamp format | YYYYMMDD | YYYY-MM-DDTHH:mm:ssZ
[ISO 8601 / RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6) | | Returns Posts that are no older than | 7 days | 7 days | | HTTP methods supported | GET | GET | -| Default request rate limits | 180 requests per 15 min with OAuth 1.0a User Context

450 requests per 15 min with OAuth 2.0 App-Only | **Basic:**

60 requests per 15 min with OAuth 2.0 App-Only

60 requests per 15 min with OAuth 1.0a User Context

60 requests per 15 min with OAuth 2.0 Authorization Code with PKCE

**Pro:**

450 requests per 15 min with OAuth 2.0 App-Only

180 requests per 15 min with OAuth 1.0a User Context

180 requests per 15 min with OAuth 2.0 Authorization Code with PKCE | +| Default request rate limits | 180 requests per 15 min with OAuth 1.0a User Context

450 requests per 15 min with OAuth 2.0 App-Only | **Self-serve:**

60 requests per 15 min with OAuth 2.0 App-Only

60 requests per 15 min with OAuth 1.0a User Context

60 requests per 15 min with OAuth 2.0 Authorization Code with PKCE | | Offers fully unwound URLs | | ✔ | | Maximum Posts per response (default) | 100 (15) | 100 (10) | -| Post JSON format | Standard v1.1 format | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | +| Post JSON format | Standard v1.1 format | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | | Supports selecting which [fields](/x-api/fundamentals/fields) return in the payload | | ✔ | | Supports requesting and receiving [annotations](/x-api/fundamentals/post-annotations) | | ✔ | | Supports requesting specific [metrics](/x-api/fundamentals/metrics) within Post object | | ✔ | @@ -41,9 +37,9 @@ The following table compares the various types of recent search endpoints: | Time resolution of time-based requests | day | second | | Timezone | UTC | UTC | | Request parameters for navigating by time | until | start_time
end_time | -| Request parameters for navigating by Post ID | since_id 
max_id | since_id 
until_id | +| Request parameters for navigating by Post ID | since_id
max_id | since_id
until_id | | Request parameter for pagination | Provides URL-encoded query | next_token | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) | | ✔ | ### Full-archive search comparison @@ -53,14 +49,14 @@ The following table compares the various types of full-archive search endpoints: | :--- | :--- | :--- | | Host domain | https://gnip-api.x.com | https://api.x.com | | Endpoint path | /search/fullarchive/accounts/:account_name/:label | /2/tweets/search/all | -| [Authentication](/resources/fundamentals/authentication) | Basic auth | OAuth 2.0 App-Only | +| [Authentication](/resources/fundamentals/authentication) | Basic auth | OAuth 2.0 App-Only | | Timestamp format | YYYYMMDDHHMM | YYYY-MM-DDTHH:mm:ssZ
[ISO 8601 / RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6) | | Returns Posts that are no older than | The full archive since March 2006 | The full archive since March 2006 | | HTTP methods supported | GET
POST | GET | -| Default request rate limits | The per minute rate limit will vary by partner as specified in your contract. 

20 requests per sec with Basic auth | 300 requests per 15 min with OAuth 2.0 App-Only

1 requests per 1 sec with OAuth 2.0 App-Only | +| Default request rate limits | The per minute rate limit will vary by partner as specified in your contract.

20 requests per sec with Basic auth | 300 requests per 15 min with OAuth 2.0 App-Only

1 requests per 1 sec with OAuth 2.0 App-Only | | Offers fully unwound URLs | ✔ | ✔ | | Posts per response | Maximum: 500
Default: 100 | Maximum: 500
Default: 10 | -| Post JSON format | [Native Enriched or Activity Streams](/x-api/enterprise-gnip-2.0/fundamentals/data-dictionary) format | [X API v2](/x-api/fundamentals/data-dictionary) format (determined by fields and expansions request parameters) | +| Post JSON format | [Native Enriched or Activity Streams](/x-api/enterprise-gnip-2.0/fundamentals/data-dictionary) format | [X API v2](/x-api/fundamentals/data-dictionary) format (determined by fields and expansions request parameters) | | Supports selecting which [fields](/x-api/fundamentals/fields) return in the payload | | ✔ | | Supports requesting and receiving [annotations](/x-api/fundamentals/post-annotations) | | ✔ | | Supports requesting specific [metrics](/x-api/fundamentals/metrics) within Post object | | ✔ | @@ -72,13 +68,13 @@ The following table compares the various types of full-archive search endpoints: | Timezone | UTC | UTC | | Supports navigating archive by Post ID | | ✔ | | Request parameters for navigation by time | fromDate
toDate | start_time
end_time | -| Request parameters for navigating by Post ID | | since_id 
until_id | +| Request parameters for navigating by Post ID | | since_id
until_id | | Request parameter for pagination | next_token | next_token | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) that has Academic Research access | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) that has Academic Research access | | ✔ | **Filtering operator comparison** -The four different versions (standard, enterprise, and v2) of search Posts differ in which operators are available, and also have varying levels of operator availability within each version, which are explained below.  +The four different versions (standard, enterprise, and v2) of search Posts differ in which operators are available, and also have varying levels of operator availability within each version, which are explained below. Enterprise @@ -86,10 +82,8 @@ Enterprise X API v2 -* **Free: **Available when using any Project -* **Basic:** Available when using any Project -* **Pro:** Available when using a Project  -* **Enterprise:** Available when using a Project  +* **Self-serve:** Available when using any Project +* **Enterprise:** Available when using a Project You can learn more about each of these sets of operators in their respective guides: @@ -100,37 +94,37 @@ Now that we understand the different operator levels within X API v2, here is th | Search operator | Standard | Enterprise | v2 | | :--- | :--- | :--- | :--- | -| keyword | Available
q:keyword | Available | Basic & Pro | -| emoji | Available
q:😄 | Available | Basic & Pro | -| “exact phrase” | Available | Available | Basic & Pro | -| # | Available | Available | Basic & Pro | -| $ | Available | Available | Pro | -| @ | Available | Available | Basic & Pro | -| from: | Available | Available | Basic & Pro | -| to: | Available | Available | Basic & Pro | -| url: | Available | Available | Basic & Pro | -| retweets_of: | | Available | Basic & Pro | -| context: | | | Basic & Pro | -| entity: | | | Basic & Pro - Only available with recent search | -| conversation_id: | | | Basic | -| place: | | Available | Pro | -| place_country: | | Available | Pro | -| point_radius: | geocode parameter | Available | Pro | -| bounding_box: | | Available | Pro | -| is:retweet | filter:retweets | Available | Basic & Pro | -| is:reply | | Available | Basic & Pro | -| is:quote | | Available | Basic & Pro | -| is:verified | | Available | Basic & Pro | -| -is:nullcast | | Available | Pro | -| has:hashtags | | Available | Basic & Pro | -| has:cashtags | | Available | Pro | -| has:links | filter:links | Available | Basic & Pro | -| has:mentions | | Available | Basic & Pro | -| has:media | filter:media | Available | Basic & Pro | -| has:images | filter:images, filter:twimg | Available | Basic & Pro | -| has:videos | filter:videos
filter:native_video | Available | Basic & Pro | -| has:geo | | Available | Pro | -| lang: | lang - can be used as an operator or a parameter | Available | Basic & Pro | +| keyword | Available
q:keyword | Available | Available | +| emoji | Available
q:😄 | Available | Available | +| "exact phrase" | Available | Available | Available | +| # | Available | Available | Available | +| $ | Available | Available | Available | +| @ | Available | Available | Available | +| from: | Available | Available | Available | +| to: | Available | Available | Available | +| url: | Available | Available | Available | +| retweets_of: | | Available | Available | +| context: | | | Available | +| entity: | | | Self-serve - Only available with recent search | +| conversation_id: | | | Available | +| place: | | Available | Available | +| place_country: | | Available | Available | +| point_radius: | geocode parameter | Available | Available | +| bounding_box: | | Available | Available | +| is:retweet | filter:retweets | Available | Available | +| is:reply | | Available | Available | +| is:quote | | Available | Available | +| is:verified | | Available | Available | +| -is:nullcast | | Available | Available | +| has:hashtags | | Available | Available | +| has:cashtags | | Available | Available | +| has:links | filter:links | Available | Available | +| has:mentions | | Available | Available | +| has:media | filter:media | Available | Available | +| has:images | filter:images, filter:twimg | Available | Available | +| has:videos | filter:videos
filter:native_video | Available | Available | +| has:geo | | Available | Available | +| lang: | lang - can be used as an operator or a parameter | Available | Available | | has:profile_geo | | Available | | | profile_country | | Available | | | profile_locality | | Available | | @@ -140,7 +134,7 @@ Now that we understand the different operator levels within X API v2, here is th | :) | Available | | | | ? | Available | | | | filter:periscope | Available | | | -| list: | Available | | Pro | +| list: | Available | | Available | | filter:replies | Available | | | | filter:pro_video | Available | | | | filter:social | Available | | | diff --git a/x-api/posts/search/migrate/standard-to-twitter-api-v2.mdx b/x-api/posts/search/migrate/standard-to-twitter-api-v2.mdx index 7a358ec54..4dd97955f 100644 --- a/x-api/posts/search/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/posts/search/migrate/standard-to-twitter-api-v2.mdx @@ -51,7 +51,7 @@ Both versions provide metadata that describes any edit history. Check out the [s **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a Project.  +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with an App.  **Response data format** @@ -100,7 +100,7 @@ There are also a set of standard search Posts request parameters **not** suppor | **Standard v1.1 parameter** | **Details** | | :--- | :--- | -| geocode | Search Posts at the Basic Access level does not support geo operators. | +| geocode | Search Posts supports geo operators for location-based queries. | | locale | With standard search, this was used to specify the language of the query but never fully implemented. | | lang | Search Posts endpoints provide a lang query operator for matching on languages of interest. | | include_entities | Post entities are always included. | @@ -160,25 +160,79 @@ These two rules have very different matching behavior. For the month of October **The following section displays cURL requests for the standard v1.1 endpoint and its equivalent endpoint in v2.** -The requests are made using [OAuth 2.0 App-Only](https://developer.x.com(/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token). In order to run the following cURL requests, you will need to replace ACCESS_TOKEN in the authorization header with your app access token. For v2 endpoints, your app access token must belong to a [developer App](/resources/fundamentals/developer-apps/overview) within a [Project](/resources/fundamentals/projects/overview).  +The requests are made using [OAuth 2.0 App-Only](https://developer.x.com(/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token). In order to run the following cURL requests, you will need to replace ACCESS_TOKEN in the authorization header with your app access token. For v2 endpoints, your app access token must belong to a [developer App](/resources/fundamentals/developer-apps/overview) within a [Project](/resources/fundamentals/developer-apps/overview).  The response payload returned by the v1.1 endpoint will be different from the response payload returned by the v2 endpoint. With v2, the response will include the default fields, as well as any other fields requested with the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. You can use these parameters to request a different set of fields to be returned. -**Standard v1.1 [GET search/tweets](https://developer.x.com/en/docs/twitter-api/v1/tweets/search/api-reference/get-search-tweets) and v2 [GET tweets/search/recent](/x-api/posts/recent-search) endpoints** +**Standard v1.1 `GET search/tweets` → v2 `GET tweets/search/recent`** - ```bash - curl --request GET \ - --url 'https://api.x.com/1.1/search/tweets.json?q=from%3ATwitterDev%20-is%3Aretweet&count=100' \ - --header 'Authorization: Bearer $ACCESS_TOKEN' - ``` + +```bash cURL (v1.1) +curl --request GET \ + --url 'https://api.x.com/1.1/search/tweets.json?q=from%3AXDevelopers%20-is%3Aretweet&count=100' \ + --header 'Authorization: Bearer $ACCESS_TOKEN' +``` - ```bash - curl --request GET \ - --url 'https://api.x.com/2/tweets/search/recent?query=from%3ATwitterDev%20-is%3Aretweet&tweet.fields=created_at%2Cconversation_id%2Centities&max_results=100' \ +```bash cURL (v2) +curl --request GET \ + --url 'https://api.x.com/2/tweets/search/recent?query=from%3AXDevelopers%20-is%3Aretweet&tweet.fields=created_at%2Cconversation_id%2Centities&max_results=100' \ --header 'Authorization: Bearer $ACCESS_TOKEN' - ``` +``` + +```python Python (v2) +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/tweets/search/recent" + +params = { + "query": "from:XDevelopers -is:retweet", + "tweet.fields": "created_at,conversation_id,entities", + "max_results": 100 +} + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers, params=params) + +for post in response.json()["data"]: + print(f"{post['created_at']}: {post['text'][:50]}...") +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Search recent Posts - SDK handles pagination automatically +for page in client.posts.search_recent( + query="from:XDevelopers -is:retweet", + tweet_fields=["created_at", "conversation_id", "entities"], + max_results=100 +): + for post in page.data: + print(f"{post.created_at}: {post.text[:50]}...") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Search recent Posts - SDK handles pagination automatically +const paginator = client.posts.searchRecent("from:XDevelopers -is:retweet", { + tweetFields: ["created_at", "conversation_id", "entities"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); + }); +} +``` + **Next steps** diff --git a/x-api/posts/search/quickstart/full-archive-search.mdx b/x-api/posts/search/quickstart/full-archive-search.mdx index 6c8d3d923..881085751 100644 --- a/x-api/posts/search/quickstart/full-archive-search.mdx +++ b/x-api/posts/search/quickstart/full-archive-search.mdx @@ -1,150 +1,258 @@ --- -title: Full-archive Search -sidebarTitle: Full-archive Search +title: Full-Archive Search Quickstart +sidebarTitle: Full-Archive Search +description: Search the complete Post archive back to 2006 keywords: ["full archive search", "archive search quickstart", "historical search", "full archive tutorial", "archive search guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the full-archive search endpoint +This guide walks you through making your first full-archive search request to find Posts from the complete X archive, dating back to March 2006. -This quick start guide will help you make your first request to the full-archive search endpoint with a set of specified fields using Postman. - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  + +Full-archive search requires [Self-serve](/x-api/getting-started/about-x-api) or [Enterprise](/x-api/getting-started/about-x-api) access. [Upgrade your access](https://developer.x.com/en/portal/products) to use this endpoint. + -### Prerequisites - -The full-archive search endpoint is currently available as part of the Pro and Enterprise access only. In order to use this endpoint, you must upgrade to Pro access or  [apply for Enterprise access level](https://docs.google.com/forms/d/e/1FAIpQLScO3bczKWO2jFHyVZJUSEGfdyfFaqt2MvmOfl_aJp0KxMqtDA/viewform). - -In addition to being approved for access, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* Navigate to your [Project](/resources/fundamentals/projects) with Enterprise or Pro access in the developer portal and make sure you have an associated [developer App](/resources/fundamentals/developer-apps) within that Project. - -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- Your App's Bearer Token (found in the Developer Console under "Keys and tokens") -### Steps to build a full-archive search request +--- -**Step one: Start with a tool or library** +## Step 1: Build a query -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +Full-archive search supports all query operators. Build queries the same way as recent search: -To load X API v2 Postman collection into your environment, please click on the following button: +``` +from:XDevelopers lang:en +``` - + +Full-archive search supports queries up to 1,024 characters (4,096 for Enterprise). + -Once you have the X API v2 collection loaded in Postman, navigate to the Search Posts > Full-archive search request. +--- -**Step two: Authenticate your request** +## Step 2: Set a time range -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request with the [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) authentication methods. +By default, results return Posts from the last 30 days. Use `start_time` and `end_time` to search specific periods: -You must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +| Parameter | Format | Example | +|:----------|:-------|:--------| +| `start_time` | ISO 8601 | `2020-01-01T00:00:00Z` | +| `end_time` | ISO 8601 | `2020-12-31T23:59:59Z` | -This variable will automatically be pulled into the request's authorization tab if you've done this correctly. -  +--- -**Step three: Create a search query** +## Step 3: Make a request -Each full-archive search query requires a single search query. For this example, we are going to use a query that matches on Posts posted by the @XDevelopers account. For this query we use the from operator and set it to XDevelopers (case insensitive): + -`from:XDevelopers` +```bash cURL +curl "https://api.x.com/2/tweets/search/all?\ +query=from%3AXDevelopers&\ +start_time=2020-01-01T00%3A00%3A00Z&\ +end_time=2020-12-31T23%3A59%3A59Z&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -In Postman, navigate to the "Params" tab and enter this ID, or a string of Post IDs separated by a comma, into the "Value" column of the `ids` parameter. -  +```python Python SDK +from xdk import Client -| Key | Value | Description | -| :--- | :--- | :--- | -| `query` | `from:XDevelopers` | Search query to submit to the full-archive search endpoint | +client = Client(bearer_token="YOUR_BEARER_TOKEN") +# Full-archive search with pagination +for page in client.posts.search_all( + query="from:XDevelopers", + start_time="2020-01-01T00:00:00Z", + end_time="2020-12-31T23:59:59Z", + max_results=100 +): + for post in page.data: + print(post.text) +``` -Step four: Identify and specify which fields you would like to retrieve +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -If you click the "Send" button after step three, you will receive the default [Post object](/x-api/fundamentals/data-dictionary#tweet) fields in your response: `id` ,`text`, and `edit_history_tweet_ids`. Note that if you receive Posts from before editing was supported, the `edit_history_tweet_ids` field will not be provided. No history backfill was performed for this field.  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -If you would like to receive additional fields beyond these default fields, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. +// Full-archive search with pagination +const paginator = client.posts.searchAll({ + query: "from:XDevelopers", + startTime: "2020-01-01T00:00:00Z", + endTime: "2020-12-31T23:59:59Z", + maxResults: 100, +}); -For this exercise, we will request a four different sets of fields from different objects: +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(post.text); + }); +} +``` -1. The default Post object fields -2. The additional tweet.created_at field in the primary user objects -3. The associated authors’ [user object’s](/x-api/fundamentals/data-dictionary#user) default fields for the returned Posts -4. The additional user.description field in the associated user objects -   + -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +--- -| Key | Value | Returned fields | -| :--- | :--- | :--- | -| `tweet.fields` | `created_at` | `tweets.created_at` | -| `expansions` | `author_id` | includes.users.id, includes.users.name, includes.users.username | -| user.fields | description | includes.users.description | +## Step 4: Review the response +```json +{ + "data": [ + { + "id": "1271111223220809728", + "text": "Tune in tonight and watch as @jessicagarson takes us through...", + "edit_history_tweet_ids": ["1271111223220809728"] + }, + { + "id": "1270799243071062016", + "text": "As we work towards building the new Twitter API...", + "edit_history_tweet_ids": ["1270799243071062016"] + } + ], + "meta": { + "newest_id": "1271111223220809728", + "oldest_id": "1270799243071062016", + "result_count": 2 + } +} +``` + + +Posts created before the edit feature was introduced (September 2022) won't include `edit_history_tweet_ids`. + -You should now see the following URL next to the "Send" button: +--- - `https://api.x.com/2/tweets/search/all?query=from:XDevelopers&tweet.fields=created_at&expansions=author_id&user.fields=created_at` +## Step 5: Add fields and expansions - -**Please note** +Request additional data with query parameters: -By default, only 10 most recent Posts will be returned. If you want more than 10 Posts per request, you can use the max_results parameter and set it to a maximum of 500 Posts per request. Similarly,  by default Posts from the last 30 days will be returned. If you want to get Posts that are older than 30 days, you can use the start_time and end_time parameters in your API call. - -**Step five: Make your request and review your response** + -Once you have everything set up, hit the "Send" button and you will receive a response similar to the following: +```bash cURL +curl "https://api.x.com/2/tweets/search/all?\ +query=from%3AXDevelopers&\ +start_time=2020-01-01T00%3A00%3A00Z&\ +end_time=2020-12-31T23%3A59%3A59Z&\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id&\ +user.fields=username,description&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Search with fields and expansions +for page in client.posts.search_all( + query="from:XDevelopers", + start_time="2020-01-01T00:00:00Z", + end_time="2020-12-31T23:59:59Z", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id"], + user_fields=["username", "description"], + max_results=100 +): + for post in page.data: + print(f"{post.created_at}: {post.text[:50]}...") ``` -{ - "data": [ - { - "author_id": "2244994945", - "created_at": "2020-06-11T16:05:06.000Z", - "id": "1271111223220809728", - "text": "Tune in tonight and watch as @jessicagarson takes us through running your favorite Python package in R. 🍿\n\nLearn how to use two powerful programming languages for data science together, and see a live example that uses the recent search endpoint from Twitter’s Developer Labs. https://t.co/v178oUZNuj" - }, - { - "author_id": "2244994945", - "created_at": "2020-06-10T19:25:24.000Z", - "id": "1270799243071062016", - "text": "As we work towards building the new Twitter API, we’ve extended the deprecation timeline for several Labs v1 endpoints. Learn more 📖 https://t.co/rRWaJYJgKk" - }, - { - "author_id": "2244994945", - "created_at": "2020-06-09T18:08:47.000Z", - "id": "1270417572001976322", - "text": "Annotations help you learn more about a Tweet — they can even help you find topics of interest. 🔬\n\nIn this tutorial, @suhemparack shows us how find Tweets related to COVID-19 using annotations + the filtered stream endpoint.\n\nLearn how you can, too. ⤵️\nhttps://t.co/qwVOgw0zSV" - } - ], - "includes": { - "users": [ - { - "description": "The voice of Twitter's #DevRel team, and your official source for updates, news, & events about Twitter's API. \n\n#BlackLivesMatter", - "id": "2244994945", - "name": "Twitter Dev", - "username": "TwitterDev" - } - ] - }, - "meta": { - "newest_id": "1271111223220809728", - "oldest_id": "1270417572001976322", - "result_count": 3 - } + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Search with fields and expansions +const paginator = client.posts.searchAll({ + query: "from:XDevelopers", + startTime: "2020-01-01T00:00:00Z", + endTime: "2020-12-31T23:59:59Z", + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id"], + userFields: ["username", "description"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); + }); } ``` + +--- -In this example, we used a very simple query. If you would like to see more detailed guides, please visit the resources listed below.  +## Step 6: Paginate through results -**Next steps** +The SDKs handle pagination automatically. For cURL, use the `next_token` from the response: + +```bash +curl "https://api.x.com/2/tweets/search/all?\ +query=from%3AXDevelopers&\ +max_results=500&\ +next_token=b26v89c19zqg8o3fo7gesq314yb9l2l4ptqy" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -[Customize your request using the API Reference](/x-api/posts/full-archive-search) + + Learn more about navigating large result sets + -[See a full list of query operators](/x-api/posts/search/integrate/build-a-query "See a full list of query operators") +--- + +## Key differences from recent search + +| Feature | Recent Search | Full-Archive Search | +|:--------|:--------------|:--------------------| +| Time range | Last 7 days | March 2006 to now | +| Access required | All developers | Pay-per-use, Enterprise | +| Max results per request | 100 | 500 | +| Query length | 512 chars | 1,024 chars | +| Rate limit | 450 / 15 min | 300 / 15 min, 1 / sec | +| Authentication | App-Only, User Context | App-Only only | + +--- + +## Common parameters + +| Parameter | Description | Default | +|:----------|:------------|:--------| +| `query` | Search query (required) | — | +| `max_results` | Posts per page (10-500) | 10 | +| `start_time` | Oldest Post timestamp | 30 days ago | +| `end_time` | Newest Post timestamp | Now | +| `next_token` | Pagination token | — | +| `tweet.fields` | Additional Post fields | `id`, `text` | +| `expansions` | Related objects to include | — | + +--- -[Use sample code for these endpoints](https://github.com/xdevplatform/Twitter-API-v2-sample-code "Use sample code for these endpoints") \ No newline at end of file +## Next steps + + + + Master query syntax and operators + + + See all available operators + + + Handle large result sets + + + Full endpoint documentation + + diff --git a/x-api/posts/search/quickstart/recent-search.mdx b/x-api/posts/search/quickstart/recent-search.mdx index d159279b0..f2ad4c247 100644 --- a/x-api/posts/search/quickstart/recent-search.mdx +++ b/x-api/posts/search/quickstart/recent-search.mdx @@ -1,139 +1,275 @@ --- -title: Recent Search +title: Recent Search Quickstart sidebarTitle: Recent Search +description: Make your first recent search request in minutes keywords: ["recent search", "search quickstart", "recent search tutorial", "search tweets", "recent search guide", "search example"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the recent search endpoint - -This quick start guide will help you make your first request to the recent search endpoint with a set of specified fields using Postman. - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +This guide walks you through making your first recent search request to find Posts from the last 7 days. -### **Prerequisites** - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token (found in the Developer Console under "Keys and tokens") -### Steps to build a recent search request - -**Step one: Start with a tool or library** - -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +--- -To load X API v2 Postman collection into your environment, please click on the following button: + + + Search queries use operators to match Posts. Start with a simple keyword: - + ``` + python + ``` -Once you have the X API v2 collection loaded in Postman, navigate to the Search Posts > Recent search request. + Or combine multiple operators: -**Step two: Authenticate your request** + ``` + python lang:en -is:retweet + ``` -To properly make a request to the X API, you need to verify that you have permission. To do this with this endpoint, you must authenticate your request with either [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods. + This matches Posts containing "python" in English, excluding retweets. -For simplicity's sake, we will utilize OAuth 2.0 App-Only with this request, but you will need to use one of the other authentication methods if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Posts.  + + See the [full operator reference](/x-api/posts/search/integrate/operators) for all available options. + + -To utilize OAuth 2.0 App-Only, you must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). + -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  + -**Step three: Create a search query** +```bash cURL +curl "https://api.x.com/2/tweets/search/recent?query=python%20lang%3Aen%20-is%3Aretweet" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -Each recent search query requires a single [search query](/x-api/posts/search/integrate/build-a-query). For this example, we are going to use a query that matches on Posts posted by the @XDevelopers account. For this query we use the from operator and set it to XDevelopers (case insensitive): +```python Python SDK +from xdk import Client -`from:XDevelopers` +client = Client(bearer_token="YOUR_BEARER_TOKEN") -In Postman, navigate to the "Params" tab and enter this ID, or a string of Post IDs separated by a comma, into the "Value" column of the `ids` parameter. +# Search recent Posts +for page in client.posts.search_recent( + query="python lang:en -is:retweet" +): + for post in page.data: + print(post.text) +``` -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Description** | -| `query` | from:XDevelopers | Search query to submit to the recent search endpoint | +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Step four: Identify and specify which fields you would like to retrieve** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -If you click the "Send" button after step three, you will receive the default [Post object](/x-api/fundamentals/data-dictionary#tweet) fields in your response: id , text, and  `edit_history_tweet_ids`. If you would like to receive additional fields beyond id , text, and `edit_history_tweet_ids`, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. +// Search recent Posts +const paginator = client.posts.searchRecent({ + query: "python lang:en -is:retweet", +}); -For this exercise, we will request a four different sets of fields from different objects: +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(post.text); + }); +} +``` -1. The default Post object fields. -2. The additional tweet.created_at field in the primary user objects. -3. The associated authors’ user object’s default fields for the returned Posts. -4. The additional  user.description field in the associated user objects. + + + + + + The default response includes `id`, `text`, and `edit_history_tweet_ids`: + + ```json + { + "data": [ + { + "id": "1234567890123456789", + "text": "Just started learning Python and loving it!", + "edit_history_tweet_ids": ["1234567890123456789"] + }, + { + "id": "1234567890123456788", + "text": "Python tip: use list comprehensions for cleaner code", + "edit_history_tweet_ids": ["1234567890123456788"] + } + ], + "meta": { + "newest_id": "1234567890123456789", + "oldest_id": "1234567890123456788", + "result_count": 2 + } + } + ``` + + + + Request additional data with query parameters: + + + +```bash cURL +curl "https://api.x.com/2/tweets/search/recent?\ +query=python%20lang%3Aen%20-is%3Aretweet&\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id&\ +user.fields=username,verified&\ +max_results=10" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Search with fields and expansions +for page in client.posts.search_recent( + query="python lang:en -is:retweet", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id"], + user_fields=["username", "verified"], + max_results=10 +): + for post in page.data: + print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") +``` -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| `tweet.fields` | `created_at` | `tweets.created_at` | -| `expansions` | `author_id` | `includes.users.id`, `includes.users.name`, `includes.users.username` | -| `user.fields` | `description` | `includes.users.description` | +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -You should now see the following URL next to the "Send" button: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -`https://api.x.com/2/tweets/search/recent?query=from:XDevelopers&tweet.fields=created_at&expansions=author_id&user.fields=created_at` +// Search with fields and expansions +const paginator = client.posts.searchRecent({ + query: "python lang:en -is:retweet", + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id"], + userFields: ["username", "verified"], + maxResults: 10, +}); +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); + }); +} +``` -**Step five: Make your request and review your response** + -Once you have everything set up, hit the "Send" button and you will receive the following response: +**Response:** -``` +```json { - "data": [ - { - "author_id": "2244994945", - "created_at": "2020-06-11T16:05:06.000Z", - "id": "1271111223220809728", - "text": "Tune in tonight and watch as @jessicagarson takes us through running your favorite Python package in R. 🍿\n\nLearn how to use two powerful programming languages for data science together, and see a live example that uses the recent search endpoint from Twitter’s Developer Labs. https://t.co/v178oUZNuj" - }, - { - "author_id": "2244994945", - "created_at": "2020-06-10T19:25:24.000Z", - "id": "1270799243071062016", - "text": "As we work towards building the new Twitter API, we’ve extended the deprecation timeline for several Labs v1 endpoints. Learn more 📖 https://t.co/rRWaJYJgKk" - }, - { - "author_id": "2244994945", - "created_at": "2020-06-09T18:08:47.000Z", - "id": "1270417572001976322", - "text": "Annotations help you learn more about a Tweet — they can even help you find topics of interest. 🔬\n\nIn this tutorial, @suhemparack shows us how find Tweets related to COVID-19 using annotations + the filtered stream endpoint.\n\nLearn how you can, too. ⤵️\nhttps://t.co/qwVOgw0zSV" - } - ], - "includes": { - "users": [ - { - "description": "The voice of Twitter's #DevRel team, and your official source for updates, news, & events about Twitter's API. \n\n#BlackLivesMatter", - "id": "2244994945", - "name": "X Developers", - "username": "XDevelopers" - } - ] - }, - "meta": { - "newest_id": "1271111223220809728", - "oldest_id": "1270417572001976322", - "result_count": 3 - } + "data": [ + { + "id": "1234567890123456789", + "text": "Just started learning Python and loving it!", + "created_at": "2024-01-15T10:30:00.000Z", + "author_id": "9876543210", + "public_metrics": { + "retweet_count": 5, + "reply_count": 2, + "like_count": 42, + "quote_count": 1 + }, + "edit_history_tweet_ids": ["1234567890123456789"] + } + ], + "includes": { + "users": [ + { + "id": "9876543210", + "username": "pythondev", + "verified": false + } + ] + }, + "meta": { + "newest_id": "1234567890123456789", + "oldest_id": "1234567890123456789", + "result_count": 1 + } } ``` + -**Next steps** + + The SDKs handle pagination automatically. For cURL, use the `next_token` from the response: -[Customize your request using the API Reference](/x-api/posts/recent-search) + ```bash + curl "https://api.x.com/2/tweets/search/recent?\ + query=python&\ + max_results=100&\ + next_token=b26v89c19zqg8o3fo7gesq314yb9l2l4ptqy" \ + -H "Authorization: Bearer $BEARER_TOKEN" + ``` -[See a full list of query operators](/x-api/posts/search/integrate/build-a-query "See a full list of query operators") + + Learn more about navigating large result sets + + + + +--- + +## Example queries + + + + ``` + from:XDevelopers + ``` + + + + ``` + #Python -is:retweet + ``` + + + + ``` + "machine learning" has:images lang:en + ``` + + + + ``` + @elonmusk -is:retweet -is:reply + ``` + + + + ``` + url:github.com lang:en + ``` + + + +--- -[Use sample code for these endpoints](https://github.com/xdevplatform/Twitter-API-v2-sample-code "Use sample code for these endpoints") \ No newline at end of file +## Next steps + + + + Master query syntax and operators + + + See all available operators + + + Search the complete Post archive + + + Full endpoint documentation + + diff --git a/x-api/posts/timelines/integrate.mdx b/x-api/posts/timelines/integrate.mdx index 6dc744e13..eb77297ba 100644 --- a/x-api/posts/timelines/integrate.mdx +++ b/x-api/posts/timelines/integrate.mdx @@ -1,159 +1,338 @@ --- -title: Integration guide -sidebarTitle: Integration guide +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for integrating Timelines endpoints keywords: ["timeline integration", "timeline guide", "timeline implementation", "home timeline integration", "user timeline integration"] --- -## How to integrate with the Timelines endpoints +This guide covers the key concepts you need to integrate the Timelines endpoints into your application. -This page contains information on several tools and key concepts that you should be aware of as you integrate the timelines endpoints into your system. We’ve split the page into the following sections: +--- -* [Helpful tools](#helpful-tools) -* Key Concepts - * [Authentication](#authentication) - * [Developer portal, Projects, and Apps](#portal) - * [Rate limits](#rate-limits) - * [Fields and expansions](#fields) - * [Post metrics](/x-api/posts/timelines#timelines-integration-guide) - * [Pagination](#pagination) - * [Filtering results](#filtering) - * [Post caps and volume of Posts returned](#caps) - * [Edit Posts](#edits) - * [Edge cases](#edge)[](#edge) +## Authentication -### Helpful tools +### Endpoint requirements -Before we explore some key concepts, we recommend that you use one of the following tools or code samples to start testing the functionality of these endpoints. +| Endpoint | App-Only | User Context | +|:---------|:---------|:-------------| +| User Posts timeline | ✓ | ✓ | +| User mentions timeline | ✓ | ✓ | +| Home timeline | — | ✓ (required) | -**Code samples** +### Private metrics -Interested in getting set up with these endpoints with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [GitHub page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +To access private metrics, you must authenticate on behalf of the Post author: -**Libraries** + +These fields require User Context authentication: +- `tweet.fields.non_public_metrics` +- `tweet.fields.promoted_metrics` +- `tweet.fields.organic_metrics` +- `media.fields.non_public_metrics` +- `media.fields.promoted_metrics` +- `media.fields.organic_metrics` + -Take advantage of one of our many [community third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +--- -**Postman** +## Fields and expansions + +By default, responses include only `id`, `text`, and `edit_history_tweet_ids`. Request additional data: + +### Example request + + + +```bash cURL +curl "https://api.x.com/2/users/123/tweets?\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id,attachments.media_keys&\ +user.fields=username,verified&\ +media.fields=url,type" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get user's Posts timeline +for page in client.posts.get_user_posts( + user_id="123", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id", "attachments.media_keys"], + user_fields=["username", "verified"], + media_fields=["url", "type"], + max_results=100 +): + for post in page.data: + print(f"{post.text} - {post.public_metrics}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get user's Posts timeline with pagination +const paginator = client.posts.getUserPosts("123", { + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id", "attachments.media_keys"], + userFields: ["username", "verified"], + mediaFields: ["url", "type"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text} - ${JSON.stringify(post.public_metrics)}`); + }); +} +``` + + + +### Key fields + +| Field | Description | +|:------|:------------| +| `created_at` | Post creation timestamp | +| `public_metrics` | Engagement counts | +| `conversation_id` | Thread identifier | +| `context_annotations` | Topic classifications | +| `entities` | Hashtags, mentions, URLs | + + + Learn more about customizing responses + -Postman is a great tool that you can use to test out these endpoints. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) page. +--- -### Key concepts +## Pagination -**Authentication** +Timelines return up to 100 Posts per request. Use pagination for larger result sets. -All X API v2 endpoints require requests to be [authenticated](/resources/fundamentals/authentication) with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE to authenticate requests to these endpoints. You can use OAuth 2.0 App-Only for user Posts timeline and user mentions timeline. +### How it works -[OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2) requires you to utilize your API Keys, user Access Tokens, and a handful of other parameters to [create an authorization header](https://developer-staging.x.com/resources/fundamentals/authentication/authorizing-a-request), which you will then pass with your request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](https://developer-staging.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens).  +1. Make initial request with `max_results` +2. Get `next_token` from the `meta` object +3. Include `pagination_token` in next request +4. Repeat until no `next_token` is returned -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. If you would like to request a Post or private metrics from these endpoints, you will need to use a either OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE, which will ensure that you have the proper permissions from the user that owns that content. +### Example -[OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) just requires that you pass an [OAuth 2.0 App Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) with your request. You can either generate an App Access Token from directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. You can use this for user Posts timeline or user mention timeline. + -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +```bash cURL +# First request +curl "https://api.x.com/2/users/123/tweets?max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +# Subsequent request with pagination token +curl "https://api.x.com/2/users/123/tweets?max_results=100&pagination_token=NEXT_TOKEN" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` - -**Please note** +```python Python SDK +from xdk import Client -If you are requesting the following [fields](/x-api/fundamentals/fields), OAuth 1.0a User Context or OAuth 2.0 Authorization Code is required:  +client = Client(bearer_token="YOUR_BEARER_TOKEN") -* `tweet.fields.non_public_metrics` -* `tweet.fields.promoted_metrics` -* `tweet.fields.organic_metrics` -* `media.fields.non_public_metrics` -* `media.fields.promoted_metrics` -* `media.fields.organic_metrics` - -**[](/x-api/fundamentals/pagination)Developer portal, Projects, and developer Apps** +# The SDK handles pagination automatically +all_posts = [] -To work with any X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. Your keys and tokens within that developer App will work for these timelines endpoints. +for page in client.posts.get_user_posts(user_id="123", max_results=100): + if page.data: + all_posts.extend(page.data) +print(f"Found {len(all_posts)} posts") +``` -**Rate limits** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Every day, many thousands of developers make requests to the X API. To help manage the volume, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that every developer can make on behalf of an app or on behalf of an authenticated user.  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -There are different rate limits applied for these endpoints depending on which authentication method is being used. The app-level rate limits apply to an App making requests using OAuth 2.0 App-Only, whereas the user-level rate limit applies to requests being made on behalf of the specific authorizing user using OAuth 1.0a User Context. These two rate limits are based on the frequency of requests within a 15-minute window. +async function getAllTimelinePosts(userId) { + const allPosts = []; -For example, an app using OAuth 2.0 App-Only auth for both of these timelines endpoints, can make 1500 requests (including pagination requests) to the user Post timeline, and 450 requests (including pagination requests) to the user mention timeline within a 15-minute timeframe.  That same app, within the same 15-minute timeframe, with two different authorized users (using OAuth 1.0a User Context) can make 900 requests (including pagination requests) to the user Post timeline, and 180 requests (including pagination requests) to the user mention timeline for each authenticated user.  + // The SDK handles pagination automatically with async iteration + const paginator = client.posts.getUserPosts(userId, { maxResults: 100 }); -Reverse chronological home timeline has a per-user rate limit of 180 requests per a 15 min window. With this endpoint you can return every Post created on a timeline over the last 7 days as well as the most recent 800 regardless of creation date. + for await (const page of paginator) { + if (page.data) { + allPosts.push(...page.data); + } + } -**Fields and expansions** + return allPosts; +} -The X API v2 allows you to select exactly which data you want returned from the API using [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions). The expansion parameter allows you to expand objects referenced in the payload. For example, this endpoint allows you to request poll, place, media, and other objects using the expansions parameter. +// Usage +const posts = await getAllTimelinePosts("123"); +console.log(`Found ${posts.length} posts`); +``` -The fields parameter allows you to select exactly which fields within the different data objects you would like to receive. By default, the primary Post object returned by these endpoints include the id and text fields. To receive additional fields such as author_id or public_metrics, you will have to specifically request those using the fields parameters. Some important fields that you may want to consider using in your integration are our poll data, [metrics](/x-api/fundamentals/metrics), [Post annotations](/x-api/fundamentals/post-annotations), and [conversation ID](/x-api/fundamentals/conversation-id) fields. + -We’ve added a guide on [how to use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). + + Learn more about pagination + +--- -**Post metrics** +## Filtering results -The X API v2 endpoints allow you to request Post metrics directly from the returned Post object, assuming you pass the proper fields with your request. +### Time-based filtering -There are some limitations with Post metrics that you should be aware of, specifically related to user privacy and the following response fields: +| Parameter | Description | +|:----------|:------------| +| `start_time` | Oldest Post timestamp (ISO 8601) | +| `end_time` | Newest Post timestamp (ISO 8601) | +| `since_id` | Return Posts after this ID | +| `until_id` | Return Posts before this ID | -* `tweet.fields.non_public_metrics` -* `tweet.fields.promoted_metrics` -* `tweet.fields.organic_metrics` -* `media.fields.non_public_metrics` -* `media.fields.promoted_metrics` -* `media.fields.organic_metrics` +### Exclude parameter +Remove specific Post types from results: -The noted fields include private metrics data, meaning you must be authorized by the Post's publisher to retrieve this data on their behalf when using the user Post timeline endpoint, meaning you must use [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code Flow with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3). + -For example, in order to receive `non_public_metrics` for user ID 1234's user Post timeline you will need to include access tokens associated with that user in your request. You can have users authorize your app and receive a set of access tokens associated with them by using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow).  +```bash cURL +curl "https://api.x.com/2/users/123/tweets?exclude=retweets,replies" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -If you are using user mention timeline, the noted fields will not be available unless the mentioning author has authorized your App to access their private metrics data and you are using that user’s access tokens when making the request with OAuth 1.0a User Context. +```python Python SDK +from xdk import Client -All `non_public_metrics`, organic_metrics, and promoted_metrics are only available for Posts created in the last 30 days. This means that when you are requesting the noted fields, the results will automatically adjust to only include Posts from the last 30 days. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -If these noted fields are requested, only Posts that are authored by the authenticated user will be returned, all other Posts will receive an error message. +# Exclude retweets and replies +for page in client.posts.get_user_posts( + user_id="123", + exclude=["retweets", "replies"] +): + for post in page.data: + print(post.text) +``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Pagination** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -These endpoints utilize pagination so that responses are returned quickly. In cases where there are more results than what can be sent in a single response (up to 100 Posts for the timelines endpoints) you will need to paginate. Use the max_results parameter to identify how many results will return per page, and the pagination_token parameter return the next page of results. You can learn more by reviewing our [pagination guide](/x-api/fundamentals/pagination). +// Exclude retweets and replies +const paginator = client.posts.getUserPosts("123", { + exclude: ["retweets", "replies"], +}); +for await (const page of paginator) { + page.data?.forEach((post) => console.log(post.text)); +} +``` -**Filtering results** + -These endpoints include several parameters that you can use to filter results. Using start_date and end_date, you can narrow down results to a specific timeframe. If you’d rather use POst IDs to select a specific set of Posts, you can use the since_id and until_id. The user Posts timeline also has an exclude parameter that can remove Retweets and Replies from your results.  +| Value | Effect | +|:------|:-------| +| `retweets` | Exclude retweets | +| `replies` | Exclude replies | +--- -**Post caps and volume of Posts returned** +## Volume limits -The user Post timeline and user mention timeline endpoints are limited in the number of Posts that they can return in a given month. The reverse chronological home timeline endpoint is not subject to this limitation. +Each timeline has maximum retrieval limits: -Regardless of which timelines endpoint you use, the Posts returned will count towards the Project-level [Post caps](/x-api/fundamentals/post-cap). Usage is shown in the developer portal, and the 'month' starts on your subscription renewal day shown on the [developer portal dashboard](https://developer.x.com/en/portal/dashboard).  +| Endpoint | Maximum Posts | +|:---------|:--------------| +| User Posts timeline | 3,200 most recent | +| User Posts (exclude=replies) | 800 most recent | +| User mentions timeline | 800 most recent | +| Home timeline | 3,200 or 7 days | -The user Post timeline endpoint will only return the most recent 3200 Posts posted to a user’s timeline. Setting start_time and end_time to a time period that includes Posts beyond the 3200 most recent, you will receive a successful response, but no Posts. + +Requesting Posts beyond these limits returns a successful response with no data. + -It is also important to note that, if you pass the excludes=replies with your user Post timeline requests, only the most recent 800 Posts will be returned. +--- + +## Post edits -The user mention timeline endpoint will only return the most recent 800 Post mentions. +Posts can be edited up to 5 times within 30 minutes. Timeline endpoints always return the most recent version. -The reverse chronological home timeline endpoint returns the last 3200 Posts. +### Considerations +- Posts older than 30 minutes represent their final version +- Near-real-time use cases should account for potential edits +- Use Post lookup to verify final state if needed -**Post edits** + + Learn more about Post edits + + +--- -Posts that are eligible for edits can be edited up to five times in the 30 minutes after the original Post was published. The search endpoints will always provide the latest version of the Post. If you only request Posts that were published 30 or more minutes ago, you will always receive the final version of the Post. However, if you have a near-real-time use case, and are querying Posts published within the last thirty minutes, those Posts could have been edited after you received them. These Posts can be rehydrated with search, or the Pos Lookup endpoint to confirm their final state. To learn more about how Post edits work, see the [Edit Posts fundamentals](/x-api/fundamentals/edit-posts) page.   +## Post metrics -**Edge cases** +### Public metrics -* When requesting non-public metrics on the User Post timeline endpoint for Posts that are older than 30 days, you may see a next_token in the response with a result count of 0. To avoid encountering this issue, ensure that the timeframe requested with the non\_public\_metrics parameter is within the most recent 30 days. Additionally, the max_results minimum value should be 10. These may help to avoid this scenario, but this could still occur. -* Requesting promoted metrics for Posts that were not promoted returns an empty response, instead of Post data. Our team is currently working on fixing this issue. +Available for all Posts with App-Only or User Context authentication: -* For a Retweet that contains Post body text greater than 140 characters in length, the text field will be truncated instead of returning the full Post text. The short term workaround is to expand the referenced Post and retrieve the full text from the expansion. This is a bug that we will fix in the future. +```json +{ + "public_metrics": { + "retweet_count": 156, + "reply_count": 23, + "like_count": 892, + "quote_count": 12 + } +} +``` -**Next steps** +### Private metrics -[Make your first request to a Timelines endpoint]/x-api/posts/timelines#getting-started-with-reverse-chronological-home-timeline "Make your first request to a Timelines endpoint") +Requires User Context authentication from the Post author: -[See a full list of parameters, fields, and more in our API Reference pages](/x-api/posts/timelines#api-reference-index) +- Only available for Posts from the last 30 days +- Only returned for Posts authored by the authenticated user +- Returns error for other users' Posts + +--- + +## Edge cases + + +When requesting non-public metrics for Posts older than 30 days, you may receive a `next_token` with `result_count: 0`. To avoid this: +- Keep requests within the last 30 days +- Use `max_results` of at least 10 + + + +Requesting promoted metrics for Posts that weren't promoted returns an empty response. This is a known issue. + + + +For Retweets with text over 140 characters, the text field is truncated. Use the `referenced_tweets.id` expansion to get the full text. + + +--- -[Get support or troubleshoot an error](https://developer.x.com/en/support/x-api) \ No newline at end of file +## Next steps + + + + Get a user's home feed + + + Get mentions for a user + + + Full endpoint documentation + + + Handle large result sets + + diff --git a/x-api/posts/timelines/introduction.mdx b/x-api/posts/timelines/introduction.mdx index 5bb03c706..d6a3c4128 100644 --- a/x-api/posts/timelines/introduction.mdx +++ b/x-api/posts/timelines/introduction.mdx @@ -1,79 +1,157 @@ --- -title: Introduction +title: Timelines sidebarTitle: Introduction -description: "The X API v2 has three timelines endpoints - reverse chronological home timeline, user Post timeline, and user mention timeline. See below for more details." -keywords: ["timelines", "home timeline", "user timeline", "mention timeline", "tweet timeline", "reverse chronological", "timeline API"] +description: Retrieve Posts from user timelines and home feeds +keywords: ["timelines", "user timeline", "home timeline", "mentions", "user posts", "timeline API", "reverse chronological"] --- import { Button } from '/snippets/button.mdx'; -These three timelines endpoints support edited Posts. These endpoints will always return the most recent edit, along with the edit history. Any Post collected after its 30-minute edit window will represent its final version. Edit metadata includes an array of IDs for all Posts in its history. For Posts with no edit history, this array will hold a single ID. For Posts that have been edited, this array contains multiple IDs, arranged in ascending order reflecting the order of edits, with the most recent version in the last position of the array. To learn more about how Post edits work, see the [Edit Posts fundamentals](/x-api/fundamentals/edit-posts) page. +The Timelines endpoints let you retrieve Posts from user timelines, mention feeds, and home feeds. Get a user's posted content, see what Posts mention them, or view their personalized home timeline. + +## Overview + + + + Get Posts authored by a user + + + Get Posts mentioning a user + + + Get personalized home feed + + + Posts in time order + + -## Reverse chronological home timeline +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/:id/tweets`](/x-api/users/get-posts) | Get Posts by a user | +| GET | [`/2/users/:id/mentions`](/x-api/users/get-mentions) | Get Posts mentioning a user | +| GET | [`/2/users/:id/timelines/reverse_chronological`](/x-api/users/get-timeline) | Get home timeline | + +--- -This endpoint enables you to retrieve the most recent Posts, Retweets, and replies posted by the authenticated user and the accounts they follow.  +## User Posts timeline -Since you are making requests on behalf of a user, you must authenticate these endpoints using an [OAuth 2.0 Authorization Code Flow with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) or [OAuth 1.0a User Context](/resources/fundamentals/authentication). This endpoint has a per-user rate limit of 180 requests per 15-minute window. This endpoint can return every Post created on a timeline over the last 7 days as well as the most recent 800 regardless of creation date. +Get the most recent Posts authored by a specific user. -## User Post timeline +### Features -The user Post timeline endpoint provides access to Posts published by a specific X account.  Retrieving a user's Posts allows you to build experiences such as showcasing a timeline in a user interface, analyzing a user's Posts to better understand their content, or creating engagement workflows with their Posts programmatically. This endpoint gives you access to a single X account's most recent Posts, Retweets, replies, and Quote Tweets, similar to what may be seen on a user's profile timeline. +- Up to 3,200 most recent Posts +- Filter out replies and retweets +- Pagination support +- Historical access with time-based filtering -Here is a user timeline for @XDevelopers: + + + Full endpoint documentation + + -The user Post timeline endpoint is a REST endpoint that receives a single path parameter to indicate the desired user (by user ID). The endpoint can return the 3,200 most recent Posts, Retweets, replies, and Quote Tweets posted by the user. +--- + +## User mentions timeline -Posts are delivered in reverse-chronological order, starting with the most recent. Results are [paginated](/x-api/fundamentals/pagination) up to 100 Posts per page. Pagination tokens are provided for paging through large sets of Posts. The Post IDs of the newest and the oldest Posts included in the given page are also provided as metadata, which can also be used for polling timelines for recent Posts. The user Post timeline also supports the ability to specify start\_time and end\_time parameters to receive Posts that were created within a certain window of time.  +Get Posts that mention a specific user. -The user Post timeline endpoint supports [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters, and returns the [new JSON data format](/x-api/fundamentals/data-dictionary). +### Features -To successfully make a request to this endpoint, you will need to authorize your request with the [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3), or [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) authentication methods. You must use OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE when requesting nonpublic metrics, promoted metrics or a protected user's timeline.  +- Up to 800 most recent mentions +- Includes replies and quote Posts +- Pagination support -The user Post timeline endpoint is designed to support two common usage patterns:  + + + Get mentions for a user + + + Full endpoint documentation + + -* "Get a user’s historical Posts": Requests made to user Post timeline in order to receive Posts authored by the user of interest in chronological order over a specific recent timeframe. The timeframe can be set using the start\_time and end\_time and paginating through the full results.  In some cases, a user’s entire history of Posts can be retrieved if the user has only authored up to 3,200 Posts in their account. Posts included will depend on the public availability and the authentication that is used for the requests. +--- -* "Polling for new Posts": Requests made to user Post timeline on a continual basis, to retrieve new Posts authored by a specific user. The last Post ID received can be set as a parameter for any new requests since the last Post. +## Reverse chronological home timeline +Get the authenticated user's home timeline in reverse chronological order. -## User mention timeline +### Features -The user mention timeline endpoint allows you to request Posts mentioning a specific X user, for example, if a X account mentioned @XDevelopers within a Post. This will also include replies to Posts by the user requested. Retrieving a user's mentions allows you to build experiences such as quickly discovering who is replying to a users' Posts, mentioning or to create engagement workflows with their Posts programmatically. The endpoint allows you to request to a single user's most recent mentions and replies, similar to what may be seen in a user's [notifications for mentions](https://x.com/notifications/mentions) on X. +- Posts from followed accounts +- Most recent 3,200 Posts (or 7 days) +- Excludes algorithmic ranking +- Requires user authentication -The user mention timeline is a REST endpoint that receives a single path parameter to indicate the desired user (by user ID). The endpoint can return the 800 most recent mentions for that user. + +This endpoint requires [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). + -Posts are delivered in reverse-chronological order, starting with the most recent. Results are [paginated](/x-api/fundamentals/pagination) in up to 100 Posts per page. Pagination tokens are provided for paging through large sets of Posts. The Post IDs of the newest and the oldest Posts included in the given page are also provided as metadata, which can also be used for polling timelines for recent Posts, or for navigating through the timeline similar to the v1.1 [mentions_timeline](https://developer.x.com/en/docs/twitter-api/v1/tweets/timelines/api-reference/get-statuses-mentions_timeline) endpoint. The endpoint also supports the ability to specify start\_time and end\_time parameters to receive Posts that were created within a certain window of time.  + + + Get a user's home timeline + + + Full endpoint documentation + + -To successfully make a request to this endpoint, you will need to authorize your request with the [OAuth 1.0a User Context](/resources/fundamentals/authentication), [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3), or [OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) authentication methods. You must use OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE when requesting non public metrics, promoted metrics or a protected user's timeline. +--- -The user mention timeline endpoint supports [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters, and returns the [new JSON data format](/x-api/fundamentals/data-dictionary). +## Filtering options - -**Account setup** +### Exclude parameter -To access these endpoints, you will need: +Filter out specific Post types: -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +| Value | Effect | +|:------|:-------| +| `retweets` | Exclude retweets | +| `replies` | Exclude replies | -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - -
- - - -
+```bash +curl "https://api.x.com/2/users/123/tweets?exclude=retweets,replies" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -**Supporting resources** +### Time-based filtering -[Learn how to use Postman to make requests](/tutorials/postman-getting-started) +| Parameter | Description | +|:----------|:------------| +| `start_time` | Oldest Post timestamp (ISO 8601) | +| `end_time` | Newest Post timestamp (ISO 8601) | +| `since_id` | Return Posts after this ID | +| `until_id` | Return Posts before this ID | -[Troubleshoot an error](https://developer.x.com/en/support/x-api) +--- -[Visit the API reference page for this endpoint]((/x-api/posts/tweets-lookup#curl-requests "Visit the API reference page for this endpoint") \ No newline at end of file +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + + + + + Get a user's home feed + + + Get mentions for a user + + + Key concepts and best practices + + + Working code examples + + diff --git a/x-api/posts/timelines/migrate/overview.mdx b/x-api/posts/timelines/migrate/overview.mdx index 7afd60ed1..ed07937ea 100644 --- a/x-api/posts/timelines/migrate/overview.mdx +++ b/x-api/posts/timelines/migrate/overview.mdx @@ -6,7 +6,7 @@ keywords: ["timeline migration", "timeline migrate", "v1.1 to v2 timeline", "tim ## Comparing X API's timelines endpoints -The v2 reverse chronological timeline, user Posts timeline, and user mention timeline endpoints replace the [v1.1 statuses/home_timeine,](https://developer.x.com/en/docs/twitter-api/v1/tweets/timelines/api-reference/get-statuses-home_timeline) [v1.1 statuses/user_timeline](https://developer.x.com/en/docs/twitter-api/v1/tweets/timelines/api-reference/get-statuses-user_timeline.html), and [v1.1 statuses/mentions_timeline](https://developer.x.com/en/docs/twitter-api/v1/tweets/timelines/api-reference/get-statuses-mentions_timeline.html) endpoints respectively. If you have code, apps, or tools that use an older version of this endpoint and are considering migrating to the newer X API v2 endpoint, then this guide is for you.  For a more in-depth migration guide see [Standard v1.1 migration to X API v2](/x-api/posts/timelines/migrate/standard-to-twitter-api-v2). +The v2 reverse chronological timeline, user Posts timeline, and user mention timeline endpoints replace the [v1.1 statuses/home_timeine,](https://developer.x.com/en/docs/twitter-api/v1/tweets/timelines/api-reference/get-statuses-home_timeline) [v1.1 statuses/user_timeline](https://developer.x.com/en/docs/twitter-api/v1/tweets/timelines/api-reference/get-statuses-user_timeline.html), and [v1.1 statuses/mentions_timeline](https://developer.x.com/en/docs/twitter-api/v1/tweets/timelines/api-reference/get-statuses-mentions_timeline.html) endpoints respectively. If you have code, apps, or tools that use an older version of this endpoint and are considering migrating to the newer X API v2 endpoint, then this guide is for you. For a more in-depth migration guide see [Standard v1.1 migration to X API v2](/x-api/posts/timelines/migrate/standard-to-twitter-api-v2). This page contains three comparison tables: @@ -27,20 +27,20 @@ The following tables compare the standard v1.1 and X API v2 home timeline endpoi | Endpoint paths | `/1.1/statuses/home_timeline.json` | `/2/users/:id/timelines/reverse_chronological` | | Required parameters | `user_id` or `screen_name` | User ID set as path parameter :id | | Authentication | OAuth 1.0a User Context | OAuth 1.0a User Context

[OAuth 2.0 Authorization Code Flow with PKCE](/resources/fundamentals/authenticationoauth-2-0/authorization-code) | -| Request rate limits/Volume limits | 15 requests per 15-minute with OAuth 1.0a User Context

Request cap: 100,000 within a 24 hour period. | 180 requests per 15-minute window 

[Post cap](/x-api/fundamentals/post-cap):

500,000 when using Essential access

2 million when using Elevated access

10 million when using Academic Research access | +| Request rate limits | 15 requests per 15-minute with OAuth 1.0a User Context

Request cap: 100,000 within a 24 hour period. | 180 requests per 15-minute window | | Default Posts per response | 15 | 100 | | Maximum Posts per response | 800 | This endpoint returns every Post created on a timeline over the last 7 days as well as the most recent 800 regardless of creation date. | | Provides Post edit history | ✔ | ✔ | | Historical Posts available | The most recent 800 Posts, including Retweets | The most recent 3,200 Posts, including Retweets | -| Timeline navigation options | since_id (exclusive) used for update polling

`max_id` (inclusive) | `start_time`

end_time

`since_id`(exclusive) used for update polling 

`until_id` (exclusive) | +| Timeline navigation options | since_id (exclusive) used for update polling

`max_id` (inclusive) | `start_time`

end_time

`since_id`(exclusive) used for update polling

`until_id` (exclusive) | | Optional parameters for results refinement | `count`

`exclude_replies`

`include_rts`

`trim_user`

`tweet_mode`

`since_id`

`max_id` | `max_results`

`exclude`(retweets,replies)

`tweet.fields`

`user.fields`

`place.fields`

`media.fields`

`poll.fields`

`expansions`

`start_time`

`end_time`

`since_id`

`until_id` | | Supports requesting and receiving [annotations](/x-api/fundamentals/post-annotations) | N/A | If annotations are included in tweet.fields, results will be annotated with inferred annotation data based on the Post text, such as 'Music Genre' and 'Folk Music' or 'Musician' and 'Dolly Parton' | -| Supports requesting and receiving specific Post [metrics](/x-api/fundamentals/metrics) | N/A | If annotations are included in `tweet.fields`, results will be annotated with public_metrics per Post including `retweet_count`, `reply_count`, `quote_count` and `like_count`, `non_public_metrics` , including `impression_count`, `user_profile_clicks`, `url_link_clicks`.

Additional media metrics such as view_count and video playback metrics.

Additional organic\_metrics and promoted\_metrics available with User Context for promoted Posts. | +| Supports requesting and receiving specific Post [metrics](/x-api/fundamentals/metrics) | N/A | If annotations are included in `tweet.fields`, results will be annotated with public_metrics per Post including `retweet_count`, `reply_count`, `quote_count` and `like_count`, `non_public_metrics` , including `impression_count`, `user_profile_clicks`, `url_link_clicks`.

Additional media metrics such as view_count and video playback metrics.

Additional organic\_metrics and promoted\_metrics available with User Context for promoted Posts. | | Supports requesting and receiving [conversation_id](/x-api/fundamentals/conversation-id) | N/A | Returns a conversation_id field where the value represents the first published Post in a reply thread to help you track conversations. | | Post JSON format | [Standard v1.1 data format](https://developer.x.com/en/docs/twitter-api/v1/data-dictionary/overview.html) | [X API v2](/x-api/fundamentals/data-dictionary) format (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | | Results order | Reverse chronological | Reverse chronological | | Results pagination | N/A must use navigation by Post ID | Results can be reviewed moving forward or backward using a pagination_token | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) | | ✔ | ### User Post timeline @@ -55,23 +55,23 @@ The following tables compare the standard v1.1 and X API v2 user Post timeline e | Endpoint paths | /1.1/statuses/user_timeline.json | /2/users/:id/tweets | | Required parameters | user_id or screen_name | User ID set as path parameter :id | | Authentication | OAuth 1.0a User Context

OAuth 2.0 App-Only | OAuth 1.0a User Context

OAuth 2.0 App-Only

OAuth 2.0 Authorization Code with PKCE | -| Request rate limits/Volume limits | 900 requests per 15 min with OAuth 1.0a User Context

1500 requests per 15 min with OAuth 2.0 App-Only

Request cap: 100,000 within a 24 hour period. | 900 requests per 15-minute window with OAuth 1.0a User Context

1500 requests per 15-minute window with OAuth 2.0 App-Only

[Post cap](/x-api/fundamentals/post-cap):
500,000 when using Essential access
2 million when using Elevated access
10 million when using Academic Research access | +| Request rate limits | 900 requests per 15 min with OAuth 1.0a User Context

1500 requests per 15 min with OAuth 2.0 App-Only

Request cap: 100,000 within a 24 hour period. | 900 requests per 15-minute window with OAuth 1.0a User Context

1500 requests per 15-minute window with OAuth 2.0 App-Only | | Default Posts per response | 15 | 10 | | Maximum Posts per response | 200 | 100 | | Historical Posts available | The most recent 3,200 Posts, including Retweets | The most recent 3,200 Posts, including Retweets | -| Timeline navigation options | since_id (exclusive) used for update polling

max_id (inclusive) | start_time

end_time

since_id (exclusive) used for update polling 

until_id (exclusive) | +| Timeline navigation options | since_id (exclusive) used for update polling

max_id (inclusive) | start_time

end_time

since_id (exclusive) used for update polling

until_id (exclusive) | | Optional parameters for results refinement | count
exclude_replies
include_rts
trim_user
tweet_mode
since_id
max_id | max_results
exclude(retweets,replies)
tweet.fields
user.fields
place.fields
media.fields
poll.fields
expansions
start_time
end_time
since_id
until_id | -| Supports requesting and receiving [annotations](/x-api/fundamentals/post-annotations) | N/A | Returns Post results with inferred annotation data based on the Post text, such as 'Music Genre' and 'Folk Music' or 'Musician' and 'Dolly Parton' | -| Supports requesting and receiving specific Post [metrics](/x-api/fundamentals/metrics) | N/A | Returns Post results with available public_metrics per Post including retweet_count, reply_count, quote_count and like_count.

Available with OAuth1.0a User Context:
Additional non\_public\_metrics , including impression_count, user\_profile\_clicks, url\_link\_clicks.

Additional media metrics such as view_count and video playback metrics.

Additional organic_metrics and promoted_metrics available with OAuth 1.0a User Context for promoted Posts. | -| Supports requesting and receiving [conversation_id](/x-api/fundamentals/conversation-id) | N/A | Returns a conversation_id field where the value represents the first published Post in a reply thread to help you track conversations. | +| Supports requesting and receiving [annotations](/x-api/fundamentals/post-annotations) | N/A | Returns Post results with inferred annotation data based on the Post text, such as 'Music Genre' and 'Folk Music' or 'Musician' and 'Dolly Parton' | +| Supports requesting and receiving specific Post [metrics](/x-api/fundamentals/metrics) | N/A | Returns Post results with available public_metrics per Post including retweet_count, reply_count, quote_count and like_count.

Available with OAuth1.0a User Context:
Additional non\_public\_metrics , including impression_count, user\_profile\_clicks, url\_link\_clicks.

Additional media metrics such as view_count and video playback metrics.

Additional organic_metrics and promoted_metrics available with OAuth 1.0a User Context for promoted Posts. | +| Supports requesting and receiving [conversation_id](/x-api/fundamentals/conversation-id) | N/A | Returns a conversation_id field where the value represents the first published Post in a reply thread to help you track conversations. | | Post JSON format | [Standard v1.1 data format](/x-api/fundamentals/data-dictionary) | [X API v2](/x-api/fundamentals/data-dictionary) format (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | | Results order | Reverse chronological | Reverse chronological | | Results pagination | N/A must use navigation by Post ID | Results can be reviewed moving forward or backward using a pagination_token | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) | | ✔ | | Provides Post edit history | ✔ | ✔ | -  + ### User mention timeline @@ -84,25 +84,25 @@ The following tables compare the standard v1.1 and X API v2 user mention timelin | HTTP methods supported | GET | GET | | Host domain | https://api.x.com | https://api.x.com | | Endpoint paths | /1.1/statuses/mentions_timeline.json | /2/users/:id/mentions | -| Required parameters | no required parameters | User ID set as path parameter :id | +| Required parameters | no required parameters | User ID set as path parameter :id | | Authentication | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 App-Only

OAuth 2.0 Authorization Code with PKCE | -| Default request rate limits | 75 requests per 15 min with OAuth 1.0a User Context

100,000 request cap within a 24 hour period. | 180 requests per 15-minute window with OAuth 1.0a User Context

450 requests per 15-minute window with OAuth 2.0 App-Only

[Post cap](/x-api/fundamentals/post-cap):

500,000 when using Essential access
2 million when using Elevated access
10 million when using Academic Research access | +| Default request rate limits | 75 requests per 15 min with OAuth 1.0a User Context

100,000 request cap within a 24 hour period. | 180 requests per 15-minute window with OAuth 1.0a User Context

450 requests per 15-minute window with OAuth 2.0 App-Only | | Default Posts per response | 15 | 10 | | Maximum Posts per response | 200 | 100 | | Historical Posts available | The most recent 800 Posts | The most recent 800 Posts | -| Timeline navigation options | since_id (exclusive) used for update polling

max_id (inclusive) | start_time

end_time

since_id (exclusive) used for update polling

until_id (exclusive) | +| Timeline navigation options | since_id (exclusive) used for update polling

max_id (inclusive) | start_time

end_time

since_id (exclusive) used for update polling

until_id (exclusive) | | Optional parameters for results refinement | count
trim_user
include_entities
tweet_mode
since_id
max_id | max_results
tweet.fields
user.fields
place.fields
media.fields
poll.fields
expansions
start_time
end_time
since_id
until_id | -| Supports requesting and receiving [annotations](/x-api/fundamentals/post-annotations) | N/A | Returns Posts results with inferred anotation data based on the Post text, such as 'Music Genre' and 'Folk Music' or 'Musician' and 'Dolly Parton' | -| Supports requesting and receiving specific Post [metrics](/x-api/fundamentals/metrics) | N/A | Returns Post results with available public_metrics per Post including retweet_count, reply_count, quote_count and like_count.

Available with OAuth 1.0a User Context:
Additional non\_public\_metrics , including impression_count, user\_profile\_clicks, url\_link\_clicks. 

Additional media metrics such as view_count and video playback metrics.

Additional organic_metrics and promoted_metrics available with OAuth 1.0a User Context for promoted Posts | -| Supports requesting and receiving [conversation_id](/x-api/fundamentals/conversation-id) | N/A | Returns a conversation_id field where the value represents the first published Post in a reply thread to help you track conversations. | -| Post JSON format | [Standard v1.1 data format](/x-api/fundamentals/data-dictionary) | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | +| Supports requesting and receiving [annotations](/x-api/fundamentals/post-annotations) | N/A | Returns Posts results with inferred anotation data based on the Post text, such as 'Music Genre' and 'Folk Music' or 'Musician' and 'Dolly Parton' | +| Supports requesting and receiving specific Post [metrics](/x-api/fundamentals/metrics) | N/A | Returns Post results with available public_metrics per Post including retweet_count, reply_count, quote_count and like_count.

Available with OAuth 1.0a User Context:
Additional non\_public\_metrics , including impression_count, user\_profile\_clicks, url\_link\_clicks.

Additional media metrics such as view_count and video playback metrics.

Additional organic_metrics and promoted_metrics available with OAuth 1.0a User Context for promoted Posts | +| Supports requesting and receiving [conversation_id](/x-api/fundamentals/conversation-id) | N/A | Returns a conversation_id field where the value represents the first published Post in a reply thread to help you track conversations. | +| Post JSON format | [Standard v1.1 data format](/x-api/fundamentals/data-dictionary) | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | | Results order | Reverse chronological | Reverse chronological | | Request parameters for pagination | N/A must use navigation by Post ID | Results can be reviewed moving forward or backward using pagination_token | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [Project](/resources/fundamentals/developer-apps) | | ✔ | | Provides Post edit history | ✔ | ✔ | **Other migration resources** [Post lookup: Standard v1.1 to X API v2](/x-api/posts/lookup/migrate/standard-to-twitter-api-v2 "Post lookup: Standard v1.1 to X API v2") -[X API migration hub](/x-api/migrate/overview) \ No newline at end of file +[X API migration hub](/x-api/migrate/overview) diff --git a/x-api/posts/timelines/migrate/standard-to-twitter-api-v2.mdx b/x-api/posts/timelines/migrate/standard-to-twitter-api-v2.mdx index 0a0ca279a..aa8412f5f 100644 --- a/x-api/posts/timelines/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/posts/timelines/migrate/standard-to-twitter-api-v2.mdx @@ -24,14 +24,13 @@ If you have been working with the v1.1 timelines endpoints (statuses/user\_timel * Ability to exclude replies (user Post timeline only) * Ability to exclude Retweets (user Post timeline only) * **Differences** - * New Authentication capability:  + * New Authentication capability: * OAuth 2.0 App-Only (user mention timeline) * OAuth 2.0 Authorization Code Flow with PKCE (reverse chronological home timeline, user Post timeline and user mentions timeline) * Access requirements: X API v2 App and Project requirements * Rate limits (user mention timeline and reverse chronological home timeline) - * Different Request limit & Volume limit * Additional pagination method - * Different max_results (count) per response + * Different max_results (count) per response * Response data format * Request parameters * Customized data format based on request parameters, including v2 fields and expansions @@ -51,7 +50,7 @@ The v1.1 statuses/mentions_timeline and the X API v2 user mention timeline endpo **Support for Post edit history and metadata** -Both versions provide metadata that describes any edit history. Check out the [filtered stream API References](/x-api/posts/filtered-stream#api-reference-index) and the [Edit Posts fundamentals page](/x-api/fundamentals/edit-posts) for more details.  +Both versions provide metadata that describes any edit history. Check out the [filtered stream API References](/x-api/posts/filtered-stream#api-reference-index) and the [Edit Posts fundamentals page](/x-api/fundamentals/edit-posts) for more details. **Rate Limits** @@ -66,7 +65,7 @@ Both versions allow polling for recent results using the since_id. **Traversing timelines by Post IDs** -Both endpoints have the capability to traverse through timelines using Post ID 'timestamps' based on the way Post IDs are constructed.  The functionality is generally  the same except for the following: +Both endpoints have the capability to traverse through timelines using Post ID 'timestamps' based on the way Post IDs are constructed. The functionality is generally the same except for the following: | | | | :--- | :--- | @@ -79,17 +78,17 @@ Both endpoints have the capability to traverse through timelines using Post ID ' | :--- | :--- | | **Standard timelines v1.1** | **timelines v2** | | Response filtering parameters:

* include_rts
* exclude_replies | Response filtering parameters:

* exclude=retweets,replies | -| Example 

https://api.x.com/1.1/statuses/user\_timeline.json?user\_id=2244994945&include\_rts=0&&exclude\_replies=1 | Example:

https://api.x.com/2/users/2244994945/tweets?max_results=100&exclude=retweets,replies | -| Notes:

For user_timeline:

* Using include_rts=0 does not change the possible historical Post limit of the most recent 3200 | Notes:

For user Posts timeline:

* Using exclude=retweets does not change the possible historical Post limit of the most recent  3200 
* Using exclude=replies reduces the possible historical Post limit to the most recent 800 replies | +| Example

https://api.x.com/1.1/statuses/user\_timeline.json?user\_id=2244994945&include\_rts=0&&exclude\_replies=1 | Example:

https://api.x.com/2/users/2244994945/tweets?max_results=100&exclude=retweets,replies | +| Notes:

For user_timeline:

* Using include_rts=0 does not change the possible historical Post limit of the most recent 3200 | Notes:

For user Posts timeline:

* Using exclude=retweets does not change the possible historical Post limit of the most recent 3200
* Using exclude=replies reduces the possible historical Post limit to the most recent 800 replies | ### Differences **Authentication** -**The v1.1 statuses/mentions_timeline endpoint only supports [OAuth 1.0a User Context](https://developer-staging.x.com/resources/fundamentals/authentication).  The X API v2 user mention timeline endpoint supports both [OAuth 1.0a User Context](/resources/fundamentals/authentication),[OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only), and [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authenticationoauth-2-0/authorization-code "This method allows an authorized app to act on behalf of the user, as the user. It is typically used to access or post public information for a specific user, and it us useful when your app needs to be aware of the relationship between a user and what this endpoint returns. Click to learn how to authenticate with OAuth 2.0 Authorization Code with PKCE.") ** +**The v1.1 statuses/mentions_timeline endpoint only supports [OAuth 1.0a User Context](https://developer-staging.x.com/resources/fundamentals/authentication). The X API v2 user mention timeline endpoint supports both [OAuth 1.0a User Context](/resources/fundamentals/authentication),[OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only), and [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authenticationoauth-2-0/authorization-code "This method allows an authorized app to act on behalf of the user, as the user. It is typically used to access or post public information for a specific user, and it us useful when your app needs to be aware of the relationship between a user and what this endpoint returns. Click to learn how to authenticate with OAuth 2.0 Authorization Code with PKCE.") ** If you would like to take advantage of the ability to access private or promoted metrics using the X API v2 user Post timeline endpoint, you will need to use OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE, and pass the user access tokens related to the user who posted the Post for which you would like to access metrics. -  + **Endpoint URLs** @@ -107,66 +106,146 @@ Note that the X API v2 timelines endpoints require a path parameter of :id for t **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project.  -  +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. + **Rate Limits** | | | | :--- | :--- | -| **mentions_timeline:**

75 requests per 15 min with OAuth 1.0a User Context | **user mention timeline: **

180 requests per 15-minute window with OAuth 1.0a User Context
450 requests per 15-minute window with OAuth 2.0 Bearer Token | +| **mentions_timeline:**

75 requests per 15 min with OAuth 1.0a User Context | **user mention timeline: **

180 requests per 15-minute window with OAuth 1.0a User Context
450 requests per 15-minute window with OAuth 2.0 Bearer Token | | **home_timelime:**

15 requests per 15 minutes

Up to 800 Posts are obtainable on the home timeline | **reverse chronological home timeline:**

180 requests per 15 minutes

You can return every Post created on a timeline over the last 7 days as well as the most recent 800 regardless of creation date. | -**Request limits** - -| | | -| :--- | :--- | -| **Standard timelines v1.1** | **timelines v2** | -| Daily request limit

* 100,000 request cap within a 24 hour period. | No daily request limits, only limited by consumption volume. | - -**Consumption volume limits** - -| | | -| :--- | :--- | -| **Standard timelines v1.1** | **timelines v2** | -| Consumption limited only by request limits

* 100,000 request cap within a 24 hour period. | Consumption limited by only Project-level monthly [Post cap](/x-api/fundamentals/post-cap) (across multiple v2 endpoints) based on [access level](/x-api/getting-started/about-x-api).

* 500,000 Posts per month with Essential access.
* 2 million Posts per month with Elevated access
* 10 million Posts per month with Academic Research access 

Reverse chronological home timeline is not subject to the monthly Post cap. | - **Request parameters** | | | | :--- | :--- | | **Standard timelines v1.1** | **timelines v2** | | Required: user_id or screen_name | Required: The specific user ID is specified in the path parameter | -| Optional:

count \- sets the maximum results returned per request

exclude_replies \- removes replies from the results

Include_rts \- when set to 0 removes retweets from the results

trim_user \- removes rehydrated user objects from the results

tweet_mode \- sets the data format returned for the results, set to extended for Posts longer than 140

since_id \- sets the earliest Post ID in result (exclusive)

max_id \- sets the latest Post ID in result (inclusive) | Optional:

max_results \- sets the maximum results returned per request

exclude=retweets,replies \- removes Retweets or replies from the results

tweet.fields \- sets the Post object fields to return

user.fields \- sets the User object fields to return

place.fields \- sets the place object fields to return

media.fields \- sets the media object fields to return

poll.fields \- sets the poll object fields to return

expansions - sets the expanded fields and data to return

start_time \- sets the earliest created_at time for the results

end_time \- sets the latest created_at time for the results

since_id \- sets the earliest Post ID for the  results (exclusive)

until_id \- sets the latest Post ID in result (exclusive) | +| Optional:

count \- sets the maximum results returned per request

exclude_replies \- removes replies from the results

Include_rts \- when set to 0 removes retweets from the results

trim_user \- removes rehydrated user objects from the results

tweet_mode \- sets the data format returned for the results, set to extended for Posts longer than 140

since_id \- sets the earliest Post ID in result (exclusive)

max_id \- sets the latest Post ID in result (inclusive) | Optional:

max_results \- sets the maximum results returned per request

exclude=retweets,replies \- removes Retweets or replies from the results

tweet.fields \- sets the Post object fields to return

user.fields \- sets the User object fields to return

place.fields \- sets the place object fields to return

media.fields \- sets the media object fields to return

poll.fields \- sets the poll object fields to return

expansions - sets the expanded fields and data to return

start_time \- sets the earliest created_at time for the results

end_time \- sets the latest created_at time for the results

since_id \- sets the earliest Post ID for the results (exclusive)

until_id \- sets the latest Post ID in result (exclusive) | **Response data format** | | | | :--- | :--- | | **Standard search v1.1** | **Search Posts v2** | -| \[
    tweet object,
    tweet object
  \] |
"data": \[id,text,id,text\],
"meta":
"oldest_id": "1337085692623646724",
"newest_id": "1334183616172019713",
"previous_token": "77qpymm88g5h9vqkluldpw91lr0qzfz1sqydh841iz48k",
"result_count": 10,
"next_token": "7140dibdnow9c7btw3w29gqolns6a1ipl3kzeae41vsxk"

| +| \[
tweet object,
tweet object
\] |
"data": \[id,text,id,text\],
"meta":
"oldest_id": "1337085692623646724",
"newest_id": "1334183616172019713",
"previous_token": "77qpymm88g5h9vqkluldpw91lr0qzfz1sqydh841iz48k",
"result_count": 10,
"next_token": "7140dibdnow9c7btw3w29gqolns6a1ipl3kzeae41vsxk"

| **X API v2 JSON format** -X API v2 is introducing new JSON designs for the objects returned by the APIs, including [Post](/x-api/fundamentals/data-dictionary#tweet) and [user](/x-api/fundamentals/data-dictionary#user) objects. You can learn more about the X API v2 format, how to use fields and expansions by visiting our [guide](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions), and by reading through our broader [data dictionary](/x-api/fundamentals/data-dictionary) +X API v2 is introducing new JSON designs for the objects returned by the APIs, including [Post](/x-api/fundamentals/data-dictionary#tweet) and [user](/x-api/fundamentals/data-dictionary#user) objects. You can learn more about the X API v2 format, how to use fields and expansions by visiting our [guide](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions), and by reading through our broader [data dictionary](/x-api/fundamentals/data-dictionary) -* At the JSON root level, the standard endpoints return Post objects in a statuses array, while X API v2 returns a data array.  -* Instead of referring to Retweeted and Quoted "statuses", X API v2 JSON refers to Retweeted and Quoted Tweets. Many legacy and deprecated fields, such as contributors and user.translator_type are being removed.  -* Instead of using both favorites (in Post object) and favorites (in user object), X API v2 uses the term like.  +* At the JSON root level, the standard endpoints return Post objects in a statuses array, while X API v2 returns a data array. +* Instead of referring to Retweeted and Quoted "statuses", X API v2 JSON refers to Retweeted and Quoted Tweets. Many legacy and deprecated fields, such as contributors and user.translator_type are being removed. +* Instead of using both favorites (in Post object) and favorites (in user object), X API v2 uses the term like. * X is adopting the convention that JSON values with no value (for example, null) are not written to the payload. Post and user attributes are only included if they have a non-null value. -   + -One of the biggest differences between standard v1.1 and X API v2 endpoint versions is how you select which fields return in your payload. For the standard endpoints, there are several parameters that you could use to identify which fields or sets of fields would return in the payload, while the X API v2 version simplifies these different parameters into [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions).  +One of the biggest differences between standard v1.1 and X API v2 endpoint versions is how you select which fields return in your payload. For the standard endpoints, there are several parameters that you could use to identify which fields or sets of fields would return in the payload, while the X API v2 version simplifies these different parameters into [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions). -* fields: X API v2 endpoints enable you to select which fields are provided in your payload. For example, Post, user, Media, Place, and Poll objects all have a list of fields that can be returned (or not).  -* expansions: Used to expand the complementary objects referenced in Post JSON payloads. For example, all Retweets and Replies reference other Posts. By setting expansions=referenced_tweets.id, these other Post objects are expanded according to the tweet.fields setting.  Other objects such as users, polls, and media can be expanded.  +* fields: X API v2 endpoints enable you to select which fields are provided in your payload. For example, Post, user, Media, Place, and Poll objects all have a list of fields that can be returned (or not). +* expansions: Used to expand the complementary objects referenced in Post JSON payloads. For example, all Retweets and Replies reference other Posts. By setting expansions=referenced_tweets.id, these other Post objects are expanded according to the tweet.fields setting. Other objects such as users, polls, and media can be expanded. * conversation_id * Two new [annotations](/x-api/fundamentals/post-annotations) fields, including context and entities -* Several new [metrics](/x-api/fundamentals/metrics) fields  -   +* Several new [metrics](/x-api/fundamentals/metrics) fields + + +We have put together a [data format migration guide](/x-api/migrate/data-format-migration#migrating-from-standard-v1-1s-data-format-to-v2) which can help you map standard v1.1 fields to the newer v2 fields. This guide will also provide you the specific expansion and field parameter that you will need to pass with your v2 request to return specific fields. + +--- + +## Code examples + +### User Posts timeline (v2) + + + +```bash cURL +curl "https://api.x.com/2/users/2244994945/tweets?max_results=100&tweet.fields=created_at,public_metrics&exclude=retweets,replies" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get user's Posts timeline +for page in client.posts.get_user_posts( + "2244994945", + tweet_fields=["created_at", "public_metrics"], + exclude=["retweets", "replies"], + max_results=100 +): + for post in page.data: + print(f"{post.created_at}: {post.text[:50]}...") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get user's Posts timeline +const paginator = client.posts.getUserPosts("2244994945", { + tweetFields: ["created_at", "public_metrics"], + exclude: ["retweets", "replies"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); + }); +} +``` + + + +### User mentions timeline (v2) + + + +```bash cURL +curl "https://api.x.com/2/users/2244994945/mentions?max_results=100&tweet.fields=created_at,author_id" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get user's mentions +for page in client.posts.get_user_mentions( + "2244994945", + tweet_fields=["created_at", "author_id"], + max_results=100 +): + for post in page.data: + print(f"Mentioned by {post.author_id}: {post.text[:50]}...") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get user's mentions +const paginator = client.posts.getUserMentions("2244994945", { + tweetFields: ["created_at", "author_id"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`Mentioned by ${post.author_id}: ${post.text?.slice(0, 50)}...`); + }); +} +``` -We have put together a [data format migration guide](/x-api/migrate/data-format-migration#migrating-from-standard-v1-1s-data-format-to-v2) which can help you map standard v1.1 fields to the newer v2 fields. This guide will also provide you the specific expansion and field parameter that you will need to pass with your v2 request to return specific fields.  + **Next steps** @@ -174,4 +253,4 @@ We have put together a [data format migration guide](/x-api/migrate/data-format- [Review the API references for v2 Post lookup](/x-api/posts/lookup/migrate/overview "Review the API references for v2 Post lookup") -[Check out our sample code for the timelines endpoints](https://github.com/xdevplatform/Twitter-API-v2-sample-code "Check out our sample code for the timelines endpoints") \ No newline at end of file +[Check out our sample code for the timelines endpoints](https://github.com/xdevplatform/Twitter-API-v2-sample-code "Check out our sample code for the timelines endpoints") diff --git a/x-api/posts/timelines/quickstart/reverse-chron-quickstart.mdx b/x-api/posts/timelines/quickstart/reverse-chron-quickstart.mdx index 8f9081e83..f9a902a6f 100644 --- a/x-api/posts/timelines/quickstart/reverse-chron-quickstart.mdx +++ b/x-api/posts/timelines/quickstart/reverse-chron-quickstart.mdx @@ -1,179 +1,232 @@ --- -title: Reverse Chronological -sidebarTitle: Reverse Chronological +title: Home Timeline Quickstart +sidebarTitle: Home Timeline +description: Get a user's home timeline in reverse chronological order keywords: ["home timeline", "reverse chronological", "home timeline quickstart", "timeline quickstart", "home timeline tutorial"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with reverse chronological home timeline - -This quick start guide will help you make your first request to one of the timelines endpoints with a set of specified fields using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  +This guide walks you through making your first request to the reverse chronological home timeline endpoint. **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Tokens (this endpoint requires user authentication) -**Steps to build a reverse chronological home timeline request** -_For this example, we will make a request to the user Post timeline by ID endpoint, but you can apply the learnings from this quick start to user mention timelines requests as well. _ +--- + +## Step 1: Get the user ID + +You need the user ID for the account whose home timeline you want to retrieve. Find it using the username lookup endpoint: + + + +```bash cURL +curl "https://api.x.com/2/users/by/username/XDevelopers" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +response = client.users.get_by_username("XDevelopers") +print(f"User ID: {response.data.id}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Step one: Start with a tool or library** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +const response = await client.users.getByUsername("XDevelopers"); +console.log(`User ID: ${response.data?.id}`); +``` -To load X API v2 Postman collection into your environment, please click on the following button: - + -Once you have the X API v2 collection loaded in Postman, navigate to the "Timelines" folder and find the "Reverse chronological home timeline" request. +The response includes the user ID: -**Step two: Authenticate your request** +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers" + } +} +``` -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request with either [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods.  +--- -You must add your keys and tokens to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +## Step 2: Request the home timeline -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. +Make a GET request with the user ID and User Access Token: -**Step three: Identify and specify which user from which you would like to retrieve their home timeline for** + -You must specify a user you would like to retrieve recent Posts for within the request. In this example, we will be passing a single user ID. +```bash cURL +curl "https://api.x.com/2/users/2244994945/timelines/reverse_chronological" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -User IDs are simply the numerical value that represents an account handle that you can find within an account's profile URL. For example, the following account’s username is `XDevelopers`. +```python Python SDK +from xdk import Client -`https://x.com/XDevelopers` +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -To convert this username to the user ID, you will have to use the [users lookup endpoint](/x-api/users/lookup/introduction) with the username and find the numerical user ID in the payload. In the case of @XDevelopers, the user ID is 2244994945. +# Get home timeline with pagination +for page in client.posts.get_home_timeline("2244994945"): + for post in page.data: + print(post.text) +``` -In Postman, navigate to the "Params" tab and enter this user ID into the "Value" column of the id parameter. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | 2244994945 | +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -**Step four: Identify and specify which fields you would like to retrieve** +// Get home timeline with pagination +const paginator = client.posts.getHomeTimeline("2244994945"); -If you click the "Send" button after step three, you will receive the default [Post object](/x-api/fundamentals/data-dictionary#tweet) fields in your response: id, text, and edit\_history\_tweet_ids. +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(post.text); + }); +} +``` -If you would like to receive additional fields, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. + -For this exercise, we will request three additional different sets of fields from different objects: +--- -1. The additional tweet.created_at field in the primary user objects. +## Step 3: Review the response + +```json +{ + "data": [ + { + "id": "1524796546306478083", + "text": "Today marks the launch of Devs in the Details...", + "edit_history_tweet_ids": ["1524796546306478083"] + }, + { + "id": "1524468552404668416", + "text": "Join us tomorrow for a discussion about bots...", + "edit_history_tweet_ids": ["1524468552404668416"] + } + ], + "meta": { + "result_count": 2, + "newest_id": "1524796546306478083", + "oldest_id": "1524468552404668416", + "next_token": "7140dibdnow9c7btw421dyz6jism75z99gyxd8egarsc4" + } +} +``` -2. The associated authors’ user object’s default fields for the returned Posts: id, name, and username -3. The additional user.created_at field in the associated user objects. -   +--- -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +## Step 4: Add fields and expansions -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| `tweet.fields` | `created_at` | `tweets.created_at` | -| `expansions` | `author_id` | `includes.users.id`, `includes.users.name`, `includes.users.username` | -| `user.fields` | `created_at` | `includes.users.created_at` | -| max_results | 5 | | +Request additional data with query parameters: + -You should now see the following URL next to the "Send" button: +```bash cURL +curl "https://api.x.com/2/users/2244994945/timelines/reverse_chronological?\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id&\ +user.fields=username,verified&\ +max_results=10" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` - `https://api.x.com/2/users/:id/timelines/reverse_chronological?tweet.fields=created_at&expansions=author_id&user.fields=created_at&max_results=5` +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get home timeline with fields and expansions +for page in client.posts.get_home_timeline( + "2244994945", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id"], + user_fields=["username", "verified"], + max_results=10 +): + for post in page.data: + print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") +``` - -**Please note:** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -In Postman, the path parameter :id in the URL field will **not** automatically update to the value that you enter into the `id` params field, which is why the above URL includes `:id` and not 2244994945. - -**Step five: Make your request and review your response** +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -Once you have everything set up, hit the "Send" button and you will receive the following response: +// Get home timeline with fields and expansions +const paginator = client.posts.getHomeTimeline("2244994945", { + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id"], + userFields: ["username", "verified"], + maxResults: 10, +}); -```{ - "data": [ - { - "created_at": "2022-05-12T17:00:00.000Z", - "text": "Today marks the launch of Devs in the Details, a technical video series made for developers by developers building with the Twitter API. 🚀\n\nIn this premiere episode, @jessicagarson walks us through how she built @FactualCat #WelcomeToOurTechTalk\n⬇️\n\nhttps://t.co/nGa8JTQVBJ", - "author_id": "2244994945", - "id": "1524796546306478083" - }, - { - "created_at": "2022-05-11T19:16:40.000Z", - "text": "📢 Join @jessicagarson @alanbenlee and @i_am_daniele tomorrow, May 12 | 5:30 ET / 2:30pm PT as they discuss the future of bots https://t.co/sQ2bIO1fz6", - "author_id": "2244994945", - "id": "1524468552404668416" - }, - { - "created_at": "2022-05-09T20:12:01.000Z", - "text": "Do you make bots with the Twitter API? 🤖\n\nJoin @jessicagarson @alanbenlee and @iamdaniele on Thursday, May 12 | 5:30 ET / 2:30pm PT as they discuss the future of bots and answer any questions you might have. 🎙📆⬇️\n\nhttps://t.co/2uVt7hCcdG", - "author_id": "2244994945", - "id": "1523757705436958720" - }, - { - "created_at": "2022-05-06T18:19:54.000Z", - "text": "If you’d like to apply, or would like to nominate someone else for the program, please feel free to fill out the following form:\n\nhttps://t.co/LUuWj24HLu", - "author_id": "2244994945", - "id": "1522642324781633536" - }, - { - "created_at": "2022-05-06T18:19:53.000Z", - "text": "We’ve gone into more detail on each Insider in our forum post. \n\nJoin us in congratulating the new additions! 🥳\n\nhttps://t.co/0r5maYEjPJ", - "author_id": "2244994945", - "id": "1522642323535847424" - } - ], - "includes": { - "users": [ - { - "created_at": "2013-12-14T04:35:55.000Z", - "name": "Twitter Dev", - "username": "TwitterDev", - "id": "2244994945" - } - ] - }, - "meta": { - "result_count": 5, - "newest_id": "1524796546306478083", - "oldest_id": "1522642323535847424", - "next_token": "7140dibdnow9c7btw421dyz6jism75z99gyxd8egarsc4" - } +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); + }); } ``` + -**Step six: Paginate through your results** +--- -In the previous response, you will find a meta data object at the bottom that includes the following fields: +## Step 5: Paginate through results -* oldest_id -* newest_id -* results_count -* next_token -* previous_token +The SDKs handle pagination automatically. For cURL, use the `next_token` from the response to get more results: -In step four, we passed a max_results value of 5, meaning that each page will only include up to five results. To access the additional pages of data, we will be taking the value of the next_token field from our last results and adding that string as the value of the pagination_token parameter on the Postman params page, keeping everything else constant.  +```bash +curl "https://api.x.com/2/users/2244994945/timelines/reverse_chronological?\ +max_results=10&\ +pagination_token=7140dibdnow9c7btw421dyz6jism75z99gyxd8egarsc4" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -| | | -| :--- | :--- | -| **Key** | **Value** | -| `pagination_token` | t3buvdr5pujq9g7bggsnf3ep2ha28 | +--- -Once this is all set up, you can click "Send" again and you should receive the next page of results.  +## Common parameters -We have put together a guide on [pagination](/x-api/fundamentals/pagination) to further explain this concept.  +| Parameter | Description | Default | +|:----------|:------------|:--------| +| `max_results` | Results per page (1-100) | 10 | +| `start_time` | Oldest Post timestamp (ISO 8601) | — | +| `end_time` | Newest Post timestamp (ISO 8601) | — | +| `since_id` | Return Posts after this ID | — | +| `until_id` | Return Posts before this ID | — | +| `exclude` | Exclude `retweets`, `replies`, or both | — | -**Next steps** -[Customize your request using the API Reference](/x-api/posts/timelines#api-reference-index) +--- -[Reach out to the community for help](https://devcommunity.x.com "Reach out to the community for help") +## Next steps + + + + Get Posts mentioning a user + + + Key concepts and best practices + + + Full endpoint documentation + + + Navigate large result sets + + diff --git a/x-api/posts/timelines/quickstart/user-mention-quickstart.mdx b/x-api/posts/timelines/quickstart/user-mention-quickstart.mdx index d15a14d91..50d079dd0 100644 --- a/x-api/posts/timelines/quickstart/user-mention-quickstart.mdx +++ b/x-api/posts/timelines/quickstart/user-mention-quickstart.mdx @@ -1,187 +1,250 @@ --- -title: User Mentions +title: User Mentions Timeline sidebarTitle: User Mentions +description: Get Posts that mention a specific user keywords: ["user mentions", "mentions timeline", "mentions quickstart", "mentions tutorial", "mention timeline", "get mentions"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the user Post and mention timeline endpoints +This guide walks you through retrieving Posts that mention a specific user. -This quick start guide will help you make your first request to the user Post timeline endpoint with a set of specified fields using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token (for public data) or User Access Token (for private metrics) -**Steps to build a timelines request** - -_For this example, we will make a request to the user Post timeline by ID endpoint, but you can apply the learnings from this quick start to user mention timelines requests as well. _ -**Step one: Start with a tool or library** - -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +--- -To load X API v2 Postman collection into your environment, please click on the following button: +## Get user mentions + + + Find the user ID using the [user lookup endpoint](/x-api/users/lookup/introduction). For example, @XDevelopers has user ID `2244994945`. + + - + -Once you have the X API v2 collection loaded in Postman, navigate to the timeline folder and find the "User Post timeline by ID" request. -  +```bash cURL +curl "https://api.x.com/2/users/2244994945/mentions?\ +tweet.fields=created_at,public_metrics,author_id&\ +expansions=author_id&\ +user.fields=username,verified&\ +max_results=10" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -**Step two: Authenticate your request** +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get mentions timeline with pagination +for page in client.posts.get_user_mentions( + "2244994945", + tweet_fields=["created_at", "public_metrics", "author_id"], + expansions=["author_id"], + user_fields=["username", "verified"], + max_results=10 +): + for post in page.data: + print(f"@{post.author_id}: {post.text[:50]}...") +``` -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request with either [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -For simplicity's sake, we will utilize OAuth 2.0 App-Only with this request, but you will need to use one of the other authentication methods if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Posts.  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -You must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +// Get mentions timeline with pagination +const paginator = client.posts.getUserMentions("2244994945", { + tweetFields: ["created_at", "public_metrics", "author_id"], + expansions: ["author_id"], + userFields: ["username", "verified"], + maxResults: 10, +}); -This variable will automatically be pulled into the request's authorization tab if you've done this correctly. -  +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`@${post.author_id}: ${post.text?.slice(0, 50)}...`); + }); +} +``` -**Step three: Identify and specify which user from which you would like to retrieve Posts** + -You must specify a user you would like to retrieve recent Posts for within the request. In this example, we will be passing a single user ID. + -User IDs are simply the numerical value that represents an account handle that you can find within an account's profile URL. For example, the following account’s username is `XDevelopers`. + + ```json + { + "data": [ + { + "id": "1301573587187331074", + "text": "Hey @XDevelopers, love the new API!", + "author_id": "1234567890", + "created_at": "2024-01-15T10:30:00.000Z", + "public_metrics": { + "retweet_count": 5, + "reply_count": 2, + "like_count": 42, + "quote_count": 1 + } + } + ], + "includes": { + "users": [ + { + "id": "1234567890", + "username": "developer", + "name": "Dev Person", + "verified": false + } + ] + }, + "meta": { + "newest_id": "1301573587187331074", + "oldest_id": "1301573587187331074", + "result_count": 1, + "next_token": "t3buvdr5pujq9g7bggsnf3ep2ha28" + } + } + ``` + + -`https://x.com/XDevelopers` +--- -To convert this username to the user ID, you will have to use the [users lookup endpoint](/x-api/users/lookup/introduction) with the username and find the numerical user ID in the payload. In the case of @XDevelopers, the user ID is 2244994945. +## Filter mentions -In Postman, navigate to the "Params" tab and enter this user ID into the "Value" column of the id parameter. +### Exclude replies -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | 2244994945 | +Get only original Posts that mention the user: -**Step four: Identify and specify which fields you would like to retrieve** + -If you click the "Send" button after step three, you will receive the default [Post object](/x-api/fundamentals/data-dictionary#tweet) fields in your response: id and text. +```bash cURL +curl "https://api.x.com/2/users/2244994945/mentions?\ +exclude=replies&\ +max_results=10" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -If you would like to receive additional fields beyond id and text, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. +```python Python SDK +from xdk import Client -For this exercise, we will request a three additional different sets of fields from different objects: +client = Client(bearer_token="YOUR_BEARER_TOKEN") -1. The additional tweet.created_at field in the primary user objects. +# Get mentions excluding replies +for page in client.posts.get_user_mentions( + "2244994945", + exclude=["replies"], + max_results=10 +): + for post in page.data: + print(post.text) +``` -2. The associated authors’ user object’s default fields for the returned Posts: id, name, and username -3. The additional  user.created_at field in the associated user objects. -   +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| `tweet.fields` | `created_at` | `tweets.created_at` | -| `expansions` | `author_id` | `includes.users.id`, `includes.users.name`, `includes.users.username` | -| `user.fields` | `created_at` | `includes.users.created_at` | -| max_results | 5 | | +// Get mentions excluding replies +const paginator = client.posts.getUserMentions("2244994945", { + exclude: ["replies"], + maxResults: 10, +}); +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(post.text); + }); +} +``` -You should now see the following URL next to the "Send" button: + - `https://api.x.com/2/users/:id/tweets?tweet.fields=created_at&expansions=author_id&user.fields=created_at&max_results=5` +### Get mentions in a time range - -**Please note:** + -In Postman, the path parameter :id in the URL field will **not** automatically update to the value that you enter into the `id` params field, which is why the above URL includes `:id` and not 2244994945. +```bash cURL +curl "https://api.x.com/2/users/2244994945/mentions?\ +start_time=2024-01-01T00%3A00%3A00Z&\ +end_time=2024-01-31T23%3A59%3A59Z" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` - -**Step five: Make your request and review your response** +```python Python SDK +from xdk import Client -Once you have everything set up, hit the "Send" button and you will receive the following response: +client = Client(bearer_token="YOUR_BEARER_TOKEN") -```{ - "data": [ - { - "author_id": "2244994945", - "created_at": "2020-09-03T17:31:39.000Z", - "id": "1301573587187331074", - "text": "Starting today, you can see your monthly Tweet usage for the v2 API in the developer portal. ✨📊\n\nThis tracks how many Tweets you’ve received from filtered stream and recent search. Learn more here: https://t.co/nfJHkFRQcZ https://t.co/vFXmoj3qaA" - }, - { - "author_id": "2244994945", - "created_at": "2020-09-03T15:43:00.000Z", - "id": "1301546240887398401", - "text": "RT @snowman: So, we built a live golf leaderboard on the X API with Python, Flask, Postgres, and Heroku.\n\nSend a 'leaderboard' Direct…" - }, - { - "author_id": "2244994945", - "created_at": "2020-09-01T20:07:50.000Z", - "id": "1300888112948752389", - "text": "⛳ Why do golfers carry an extra shirt? In case they get a hole in one.\n\nNow that we have your attention, learn how @snowman and @johnd built a real-time golf leaderboard using the #TwitterAPI. 📖\n\nhttps://t.co/rRKeKmaRrN" - }, - { - "author_id": "2244994945", - "created_at": "2020-08-28T23:14:22.000Z", - "id": "1299485505478963200", - "text": "RT @jessicagarson: Posted my first tutorial on @ThePracticalDev on using v2 of the Twitter API! You will learn how to explore a user’s Twee…" - }, - { - "author_id": "2244994945", - "created_at": "2020-08-21T19:10:05.000Z", - "id": "1296887316556980230", - "text": "See how @PennMedCDH are using Twitter data to understand the COVID-19 health crisis 📊\n\nhttps://t.co/1tdA8uDWes" - } - ], - "includes": { - "users": [ - { - "created_at": "2013-12-14T04:35:55.000Z", - "id": "2244994945", - "name": "Twitter Dev", - "username": "TwitterDev" - } - ] - }, - "meta": { - "newest_id": "1301573587187331074", - "next_token": "t3buvdr5pujq9g7bggsnf3ep2ha28", - "oldest_id": "1296887316556980230", - "previous_token": "t3equkmcd2zffvags2nkj0nhlrn78", - "result_count": 5 - } -} +# Get mentions in a time range +for page in client.posts.get_user_mentions( + "2244994945", + start_time="2024-01-01T00:00:00Z", + end_time="2024-01-31T23:59:59Z" +): + for post in page.data: + print(f"{post.created_at}: {post.text[:50]}...") ``` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Step six: Paginate through your results** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -In the previous response, you will find a meta data object at the bottom that includes the following fields: +// Get mentions in a time range +const paginator = client.posts.getUserMentions("2244994945", { + startTime: "2024-01-01T00:00:00Z", + endTime: "2024-01-31T23:59:59Z", +}); -* oldest_id -* newest_id -* results_count -* next_token -* previous_token +for await (const page of paginator) { + page.data?.forEach((post) => { + console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); + }); +} +``` -In step four, we passed a max_results value of 5, meaning that each page will only include up to five results. To access the additional pages of data, we will be taking the value of the next_token field from our last results and adding that string as the value of the pagination_token parameter on the Postman params page, keeping everything else constant.  + -| | | -| :--- | :--- | -| **Key** | **Value** | -| `pagination_token` | t3buvdr5pujq9g7bggsnf3ep2ha28 | +--- -Once this is all set up, you can click "Send" again and you should receive the next page of results.  +## Common parameters -We have put together a guide on [pagination](/x-api/fundamentals/pagination) to further explain this concept.  +| Parameter | Description | Default | +|:----------|:------------|:--------| +| `max_results` | Results per page (1-100) | 10 | +| `start_time` | Oldest Post timestamp (ISO 8601) | — | +| `end_time` | Newest Post timestamp (ISO 8601) | — | +| `since_id` | Return Posts after this ID | — | +| `until_id` | Return Posts before this ID | — | +| `exclude` | Exclude `retweets`, `replies`, or both | — | +| `pagination_token` | Token for next page | — | -**Next steps** -[Customize your request using the API Reference](/x-api/posts/timelines#api-reference-index) +--- -[Reach out to the community for help](https://devcommunity.x.com "Reach out to the community for help") +## Next steps + + + + Get user's home timeline + + + Key concepts and best practices + + + Full endpoint documentation + + + Navigate large result sets + + diff --git a/x-api/posts/volume-streams/introduction.mdx b/x-api/posts/volume-streams/introduction.mdx index 8b484b227..ae6018f52 100644 --- a/x-api/posts/volume-streams/introduction.mdx +++ b/x-api/posts/volume-streams/introduction.mdx @@ -23,7 +23,7 @@ _To use these APIs, you must first set up an account with our enterprise sales t To access these endpoints, you will need: * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access).
diff --git a/x-api/powerstream/introduction.mdx b/x-api/powerstream/introduction.mdx index 5e139aa9d..20126cbe7 100644 --- a/x-api/powerstream/introduction.mdx +++ b/x-api/powerstream/introduction.mdx @@ -21,10 +21,6 @@ If you're interested in accessing Powerstream or learning more about our Enterpr We'll be happy to discuss how Powerstream can support your needs.
-## Authentication - -Powerstream API endpoints use OAuth 2.0 Bearer Token. Include in `Authorization: Bearer ` header and you can start using these endpoints. - ## Quick Start This section showcases how to quickly get started with the PowerStream endpoints using Python with the `requests` library. Install it via `pip install requests`. All examples use OAuth 2.0 Bearer Token authentication. Replace `YOUR_BEARER_TOKEN` with your actual token (store it securely, e.g., via `os.getenv('BEARER_TOKEN')`). diff --git a/x-api/spaces/lookup/introduction.mdx b/x-api/spaces/lookup/introduction.mdx index 2acd7a744..49b19b422 100644 --- a/x-api/spaces/lookup/introduction.mdx +++ b/x-api/spaces/lookup/introduction.mdx @@ -1,34 +1,101 @@ --- -title: Introduction +title: Spaces Lookup sidebarTitle: Introduction +description: Retrieve Space details by ID or find Spaces by their creators keywords: ["spaces lookup", "get spaces", "space by ID", "space information", "space details", "lookup spaces", "spaces API"] --- import { Button } from '/snippets/button.mdx'; -The lookup by ID endpoints return data you can use to build experiences to display Spaces information to your users, or return details about one or more Spaces. You can lookup one or more Spaces in the same request. +The Spaces lookup endpoints let you retrieve information about live or scheduled Spaces. Look up Spaces by their ID or find Spaces created by specific users. -You can also lookup Spaces by their creator ID. This endpoint is useful to lookup Spaces from users in a list, or to determine if multiple recurring hosts are live now or have an upcoming Space scheduled. Up to 100 users can be looked up in a single request. +## Overview -**Account setup** + + + Get details for a specific Space + + + Look up multiple Spaces at once + + + Find Spaces by their hosts + + + Get users who purchased tickets + + -To access these endpoints, you will need: +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/spaces/:id`](/x-api/spaces/get-space-by-id) | Get Space by ID | +| GET | [`/2/spaces`](/x-api/spaces/get-spaces-by-ids) | Get Spaces by IDs | +| GET | [`/2/spaces/by/creator_ids`](/x-api/spaces/get-spaces-by-creator-ids) | Get Spaces by creator user IDs | +| GET | [`/2/spaces/:id/buyers`](/x-api/spaces/get-space-ticket-buyers) | Get ticket buyers for a Space | +| GET | [`/2/spaces/:id/tweets`](/x-api/spaces/get-space-posts) | Get Posts shared in a Space | + +--- + +## Response fields + +By default, the response includes `id` and `state`. Request additional fields: + +| Field | Description | +|:------|:------------| +| `title` | Space title | +| `host_ids` | Host user IDs | +| `speaker_ids` | Speaker user IDs | +| `participant_count` | Number of participants | +| `scheduled_start` | Scheduled start time | +| `started_at` | Actual start time | +| `ended_at` | End time | +| `is_ticketed` | Whether Space has tickets | + +### Space states + +| State | Description | +|:------|:------------| +| `live` | Currently active | +| `scheduled` | Scheduled for future | +| `ended` | Has ended | + +--- + +## Example request + +```bash +curl "https://api.x.com/2/spaces/1DXxyRYNejbKM?\ +space.fields=title,host_ids,participant_count,scheduled_start,state" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +--- + +## Getting started + + +**Prerequisites** -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) + -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). -
- - - - -
\ No newline at end of file + + + Make your first Spaces lookup request + + + Find Spaces by keyword + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/spaces/lookup/quickstart.mdx b/x-api/spaces/lookup/quickstart.mdx index dea40c0cf..e991eb6ae 100644 --- a/x-api/spaces/lookup/quickstart.mdx +++ b/x-api/spaces/lookup/quickstart.mdx @@ -1,117 +1,288 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["spaces lookup quickstart", "get spaces quickstart", "space lookup tutorial", "spaces quickstart guide"] +description: Retrieve Space details by ID or creator +keywords: ["spaces quickstart", "space lookup tutorial", "spaces guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the Spaces lookup endpoints +This guide walks you through retrieving Space information using the Spaces lookup endpoints. -This quick start guide will help you make your first request to one of the Spaces lookup endpoints with a set of specified fields using Postman. - -If you would like to see sample code in different programming languages, please visit our [X API v2 sample code GitHub](https://github.com/xdevplatform/Twitter-API-v2-sample-code) repository. -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token -#### Steps to build a Spaces lookup request -_For this example, we will make a request to the user Spaces lookup by creator ID endpoint, but you can apply the learnings from this quick start to other lookup requests as well. _ +--- + +## Get a Space by ID + +Retrieve details for a specific Space: + + + +```bash cURL +curl "https://api.x.com/2/spaces/1DXxyRYNejbKM?\ +space.fields=title,host_ids,participant_count,scheduled_start,state,created_at" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get a Space by ID +response = client.spaces.get( + "1DXxyRYNejbKM", + space_fields=["title", "host_ids", "participant_count", "scheduled_start", "state", "created_at"] +) + +print(f"Space: {response.data.title}") +print(f"State: {response.data.state}") +print(f"Participants: {response.data.participant_count}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get a Space by ID +const response = await client.spaces.get("1DXxyRYNejbKM", { + spaceFields: ["title", "host_ids", "participant_count", "scheduled_start", "state", "created_at"], +}); + +console.log(`Space: ${response.data?.title}`); +console.log(`State: ${response.data?.state}`); +console.log(`Participants: ${response.data?.participant_count}`); +``` + + + +### Response + +```json +{ + "data": { + "id": "1DXxyRYNejbKM", + "state": "live", + "title": "Discussing AI and the Future", + "host_ids": ["2244994945"], + "participant_count": 245, + "created_at": "2024-01-15T09:00:00.000Z" + } +} +``` + +--- + +## Get multiple Spaces + +Look up multiple Spaces at once: + + + +```bash cURL +curl "https://api.x.com/2/spaces?\ +ids=1DXxyRYNejbKM,1YqJDqWYNQDGW&\ +space.fields=title,state,participant_count" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client -**Step one: Start with a tool or library** +client = Client(bearer_token="YOUR_BEARER_TOKEN") -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +# Get multiple Spaces +response = client.spaces.get_spaces( + ids=["1DXxyRYNejbKM", "1YqJDqWYNQDGW"], + space_fields=["title", "state", "participant_count"] +) -To load X API v2 Postman collection into your environment, please click on the following button: +for space in response.data: + print(f"{space.title} - {space.state}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get multiple Spaces +const response = await client.spaces.getSpaces({ + ids: ["1DXxyRYNejbKM", "1YqJDqWYNQDGW"], + spaceFields: ["title", "state", "participant_count"], +}); + +response.data?.forEach((space) => { + console.log(`${space.title} - ${space.state}`); +}); +``` + + + +--- + +## Get Spaces by creator - -Once you have the X API v2 collection loaded in Postman, navigate to the Spaces folder and find the "Lookup Spaces created by one or more users" request. -  +Find Spaces hosted by specific users: -**Step two: Authenticate your request** + -To properly make a request to the X API, you need to verify that you have permission. To do so, this endpoint requires you to authenticate your request with either [OAuth 2.0 App-Only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) authentication methods. +```bash cURL +curl "https://api.x.com/2/spaces/by/creator_ids?\ +user_ids=2244994945,783214&\ +space.fields=title,state,scheduled_start" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -For simplicity's sake, we will utilize OAuth 2.0 App-Only with this request, but you will need to use one of the other authentication methods if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Spaces from a private user.  +```python Python SDK +from xdk import Client -To utilize OAuth 2.0 App-Only, you must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +client = Client(bearer_token="YOUR_BEARER_TOKEN") -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +# Get Spaces by creator +response = client.spaces.get_by_creator_ids( + user_ids=["2244994945", "783214"], + space_fields=["title", "state", "scheduled_start"] +) -**Step three: Identify and specify which user from which you would like to retrieve Posts** +for space in response.data: + print(f"{space.title} - {space.state}") +``` -You must specify a user you would like to retrieve live or upcoming Spaces for within the request. In this example, we will be passing a single user ID. +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -User IDs are simply the numerical value that represents an account handle that you can find within an account's profile URL. For example, the following account’s username is XDevelopers. +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -https://x.com/XDevelopers +// Get Spaces by creator +const response = await client.spaces.getByCreatorIds({ + userIds: ["2244994945", "783214"], + spaceFields: ["title", "state", "scheduled_start"], +}); -To convert this username to the user ID, you will have to use the user lookup endpoint with the username and find the numerical user ID in the payload. In the case of @XDevelopers, the user ID is 2244994945. +response.data?.forEach((space) => { + console.log(`${space.title} - ${space.state}`); +}); +``` -In Postman, navigate to the "Params" tab and enter this user ID into the "Value" column of the id parameter. + -| | | -| :--- | :--- | -| **Key** | **Value** | -| id | 2244994945 | +--- -**Step four: Identify and specify which fields you would like to retrieve** +## Include host information -If you click the "Send" button after step three, you will receive an id, which is the only [Space object](/x-api/fundamentals/data-dictionary#space) field returned by default in your response. +Expand host user data: -If you would like to receive additional fields, you will have to specify them in your request with the space.fields or expansions parameters. + -For this exercise, we will requested three additional sets of fields from different objects: +```bash cURL +curl "https://api.x.com/2/spaces/1DXxyRYNejbKM?\ +space.fields=title,host_ids,state&\ +expansions=host_ids&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -* The additional title field in the primary Spaces object. -* The full [user object](/x-api/fundamentals/data-dictionary#user) of the specified creator ID -* The additional user.created_at field in the associated user object. +```python Python SDK +from xdk import Client -In Postman, navigate to the “Params” tab and add the following key:value pair to the “Query Params” table: +client = Client(bearer_token="YOUR_BEARER_TOKEN") -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| space.fields | title | creator_id | -| expansions | creator_id | includes.users.id, includes.users.name, includes.users.username | -| user.fields | created_at | includes.users.created_at | +# Get Space with host info +response = client.spaces.get( + "1DXxyRYNejbKM", + space_fields=["title", "host_ids", "state"], + expansions=["host_ids"], + user_fields=["username", "verified"] +) -You should now see the following URL next to the “Send” button: +print(f"Space: {response.data.title}") +# Host info is in response.includes.users +``` -https://api.x.com/2/spaces/by/creator\_ids?user\_ids=2244994945&space.fields=creator\_id&expansions=creator\_id&user.fields=created_at +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Step five: Make your request and review your response** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Once you have everything set up, hit the “Send” button and you will receive the following response: +// Get Space with host info +const response = await client.spaces.get("1DXxyRYNejbKM", { + spaceFields: ["title", "host_ids", "state"], + expansions: ["host_ids"], + userFields: ["username", "verified"], +}); +console.log(`Space: ${response.data?.title}`); +// Host info is in response.includes?.users ``` + + + +### Response with expansion + +```json { - "data": [ - { - "creator_id": "2244994945", - "id": "1zqKVXPQhvZJB", - "title": "Hello world 👋", - "state": "Running" - }, - "includes": { - "users": [ - { - "created_at": "2013-12-14T04:35:55.000Z", - "name": "Twitter Dev", - "id": "2244994945", - "username": "TwitterDev" - } - ] - } -] + "data": { + "id": "1DXxyRYNejbKM", + "state": "live", + "title": "Discussing AI and the Future", + "host_ids": ["2244994945"] + }, + "includes": { + "users": [ + { + "id": "2244994945", + "username": "XDevelopers", + "verified": true + } + ] + } } -``` \ No newline at end of file +``` + +--- + +## Space states + +| State | Description | +|:------|:------------| +| `live` | Currently active | +| `scheduled` | Scheduled for future | +| `ended` | Has ended | + +--- + +## Available fields + +| Field | Description | +|:------|:------------| +| `title` | Space title | +| `host_ids` | Host user IDs | +| `speaker_ids` | Speaker user IDs | +| `participant_count` | Current participants | +| `scheduled_start` | Scheduled start time | +| `started_at` | Actual start time | +| `ended_at` | End time | +| `is_ticketed` | Whether Space has tickets | +| `state` | Current state | + +--- + +## Next steps + + + + Find Spaces by keyword + + + Full endpoint documentation + + diff --git a/x-api/spaces/search/introduction.mdx b/x-api/spaces/search/introduction.mdx index 3d4ae314f..2c0fa4189 100644 --- a/x-api/spaces/search/introduction.mdx +++ b/x-api/spaces/search/introduction.mdx @@ -1,35 +1,95 @@ --- -title: Introduction +title: Spaces Search sidebarTitle: Introduction -keywords: ["spaces search", "search spaces", "find spaces", "space search", "spaces discovery", "search spaces API"] +description: Search for live or scheduled Spaces by keyword +keywords: ["spaces search", "search spaces", "find spaces", "spaces API", "audio spaces search"] --- import { Button } from '/snippets/button.mdx'; -This endpoint allows you to search Spaces by their title, and to filter results by status. This endpoint is useful to discover live or upcoming Spaces based on keywords, mentioned users or hashtags in their title. +The Spaces Search endpoint lets you search for live or scheduled Spaces by keyword. Find Spaces about topics of interest. -The endpoint accepts one or more keywords as a query. Its results can be filtered by the status of a Space (live or scheduled). By default, a request will return both live and scheduled Spaces that match the specified query. - -**Account setup** +## Overview + + + + Search Spaces by title + + + Find live and upcoming Spaces + + + +--- + +## Endpoint + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/spaces/search`](/x-api/spaces/search-spaces) | Search for Spaces | + +--- + +## Parameters + +| Parameter | Description | +|:----------|:------------| +| `query` | Search query (required) | +| `state` | Filter by `live` or `scheduled` | +| `space.fields` | Additional Space fields | +| `expansions` | Related objects to include | -To access these endpoints, you will need: +--- + +## Example request + +```bash +curl "https://api.x.com/2/spaces/search?\ +query=AI&\ +state=live&\ +space.fields=title,host_ids,participant_count" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +## Example response + +```json +{ + "data": [ + { + "id": "1DXxyRYNejbKM", + "state": "live", + "title": "Discussing AI and the Future", + "host_ids": ["1234567890"], + "participant_count": 245 + } + ], + "meta": { + "result_count": 1 + } +} +``` -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +--- + +## Getting started -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) -
- - - - -
+ + + + Search for your first Space + + + Look up Spaces by ID + + + Full endpoint documentation + + diff --git a/x-api/spaces/search/quickstart.mdx b/x-api/spaces/search/quickstart.mdx index 29f58751f..933ac2701 100644 --- a/x-api/spaces/search/quickstart.mdx +++ b/x-api/spaces/search/quickstart.mdx @@ -1,110 +1,261 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["spaces search quickstart", "search spaces quickstart", "spaces search tutorial", "spaces search guide"] +description: Search for Spaces by keyword +keywords: ["spaces search quickstart", "search spaces tutorial", "find spaces guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the search Spaces endpoint +This guide walks you through searching for Spaces by keyword. -This quick start guide will help you make your first request to the search Spaces endpoint with a set of specified fields using Postman. - -If you would like to see sample code in different programming languages, please visit our [X API v2 sample code GitHub repository](https://github.com/xdevplatform/Twitter-API-v2-sample-code). **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token -### Steps to build a search Spaces request +--- + +## Search for Spaces + +Search for Spaces matching a keyword: + + -**Step one: Start with a tool or library** +```bash cURL +curl "https://api.x.com/2/spaces/search?\ +query=AI&\ +space.fields=title,host_ids,participant_count,state&\ +state=live" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +```python Python SDK +from xdk import Client -To load X API v2 Postman collection into your environment, please click on the following button: +client = Client(bearer_token="YOUR_BEARER_TOKEN") - +# Search for Spaces +response = client.spaces.search( + query="AI", + space_fields=["title", "host_ids", "participant_count", "state"], + state="live" +) -Once you have the X API v2 collection loaded in Postman, navigate to the Spaces folder and find the "Search Spaces" request. -  +for space in response.data: + print(f"{space.title} - {space.participant_count} participants") +``` -**Step two: Authenticate your request** +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -To properly make a request to the X API, you need to verify that you have permission. To do so, this endpoint requires you to authenticate your request with either [OAuth 2.0 App-Only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) authentication methods. +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -For simplicity's sake, we will utilize OAuth 2.0 App-Only with this request, but you will need to use one of the other authentication methods if you'd like to request private [metrics](/x-api/fundamentals/metrics) or Spaces from a private user.  +// Search for Spaces +const response = await client.spaces.search({ + query: "AI", + spaceFields: ["title", "host_ids", "participant_count", "state"], + state: "live", +}); -To utilize OAuth 2.0 App-Only, you must add your keys and tokens, specifically the [App Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) (also known as the App-only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +response.data?.forEach((space) => { + console.log(`${space.title} - ${space.participant_count} participants`); +}); +``` -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  + -**Step three: create a search query** +### Response -This endpoint accepts text as a search query. Unlike other search endpoints, it does not accept operators, grouping, and logical operators. For this exercise, we will use “hello” as a simple query. +```json +{ + "data": [ + { + "id": "1DXxyRYNejbKM", + "state": "live", + "title": "Discussing AI and the Future", + "host_ids": ["2244994945"], + "participant_count": 245 + }, + { + "id": "1YqJDqWYNQDGW", + "state": "live", + "title": "AI in Healthcare", + "host_ids": ["783214"], + "participant_count": 89 + } + ], + "meta": { + "result_count": 2 + } +} +``` -In Postman, navigate to the "Params" tab and enter this user ID into the "Value" column of the id parameter. +--- -| | | -| :--- | :--- | -| **Key** | **Value** | -| query | hello | +## Filter by state -**Step four: Identify and specify which fields you would like to retrieve** +Search only live or scheduled Spaces: -If you click the "Send" button after step three, you will receive the ID of the Spaces and its state, which are the only Space object fields returned by default in your response. +### Live Spaces only -If you would like to receive additional fields, you will have to specify them in your request with the space.fields or expansions parameters. + -For this exercise, we will requested three additional sets of fields from different objects: +```bash cURL +curl "https://api.x.com/2/spaces/search?query=tech&state=live" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -* The additional title field in the primary Spaces object. -* The full user object of the specified creator ID -* The additional user.created_at field in the associated user object. +```python Python SDK +from xdk import Client -In Postman, navigate to the “Params” tab and add the following key:value pair to the “Query Params” table: +client = Client(bearer_token="YOUR_BEARER_TOKEN") -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| space.fields | title | creator_id | -| expansions | creator_id | includes.users.id, includes.users.name, includes.users.username | -| user.fields | created_at | includes.users.created_at | +# Search live Spaces only +response = client.spaces.search(query="tech", state="live") -You should now see the following URL next to the “Send” button: +for space in response.data: + print(f"LIVE: {space.title}") +``` -https://api.x.com/2/spaces/search?query=hello&space.fields=creator\_id&expansions=creator\_id&user.fields=created_at +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -**Step five: Make your request and review your response** +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Once you have everything set up, hit the “Send” button and you will receive the following response: +// Search live Spaces only +const response = await client.spaces.search({ + query: "tech", + state: "live", +}); -```{ - "data": [ - { - "creator_id": "2244994945", - "id": "1zqKVXPQhvZJB", - "title": "Hello world 👋", - "state": "Running" - }, - "includes": { - "users": [ - { - "created_at": "2013-12-14T04:35:55.000Z", - "name": "Twitter Dev", - "id": "2244994945", - "username": "TwitterDev" - } - ] - } -] -} -``` \ No newline at end of file +response.data?.forEach((space) => { + console.log(`LIVE: ${space.title}`); +}); +``` + + + +### Scheduled Spaces only + + + +```bash cURL +curl "https://api.x.com/2/spaces/search?query=tech&state=scheduled" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Search scheduled Spaces only +response = client.spaces.search(query="tech", state="scheduled") + +for space in response.data: + print(f"SCHEDULED: {space.title}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Search scheduled Spaces only +const response = await client.spaces.search({ + query: "tech", + state: "scheduled", +}); + +response.data?.forEach((space) => { + console.log(`SCHEDULED: ${space.title}`); +}); +``` + + + +--- + +## Include host information + +Expand host user data: + + + +```bash cURL +curl "https://api.x.com/2/spaces/search?\ +query=AI&\ +space.fields=title,host_ids,state&\ +expansions=host_ids&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Search with host info +response = client.spaces.search( + query="AI", + space_fields=["title", "host_ids", "state"], + expansions=["host_ids"], + user_fields=["username", "verified"] +) + +for space in response.data: + print(f"{space.title}") +# Host info is in response.includes.users +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Search with host info +const response = await client.spaces.search({ + query: "AI", + spaceFields: ["title", "host_ids", "state"], + expansions: ["host_ids"], + userFields: ["username", "verified"], +}); + +response.data?.forEach((space) => { + console.log(space.title); +}); +// Host info is in response.includes?.users +``` + + + +--- + +## Common parameters + +| Parameter | Description | +|:----------|:------------| +| `query` | Search query (required) | +| `state` | Filter: `live`, `scheduled`, or `all` | +| `max_results` | Results to return (1-100) | +| `space.fields` | Space fields to include | +| `expansions` | Related objects to include | +| `user.fields` | User fields to include | + +--- + +## Next steps + + + + Look up Spaces by ID + + + Full endpoint documentation + + diff --git a/x-api/tools-and-libraries/overview.mdx b/x-api/tools-and-libraries/overview.mdx index 292bdb795..915d08c24 100644 --- a/x-api/tools-and-libraries/overview.mdx +++ b/x-api/tools-and-libraries/overview.mdx @@ -1,150 +1,131 @@ --- -title: "Overview" -keywords: ["tools", "libraries", "SDK", "client libraries", "API tools", "developer tools", "code libraries", "X API tools"] +title: Tools & Libraries +sidebarTitle: Overview +description: Official and community libraries for the X API +keywords: ["tools", "libraries", "SDK", "client libraries", "API tools", "developer tools", "code libraries"] --- -## X-built v2 tools and libraries +import { Button } from '/snippets/button.mdx'; -X maintains a set of official libraries and SDKs, listed here. - -We also include a list of [community-supported libraries](#community-libraries) lower on this page. - -[Explore XDev code on GitHub](https://github.com/xdevplatform) -[Find X samples on Glitch](https://glitch.com/@twitter) -[Find X samples on Replit](https://replit.com/@twitter) +Speed up your development with official SDKs, community libraries, and developer tools. --- -## Community tools and libraries for v2 - -The libraries listed here have been built by members of the developer community. Note that they may be at different stages of API coverage. - -If you've built your own X API library or useful tool, please [let us know](https://devcommunity.x.com/c/libraries-and-sdks/63), and we'll add it to this list to help others to find it. We also have some [version badges](https://twbadges.glitch.me/) you can borrow, to use in your own README files. - -Looking for inspiration? You can browse and search in the [X](https://github.com/topics/twitter?o=desc&s=updated) and [X-api-v2](https://github.com/topics/twitter-api-v2?o=desc&s=updated) topics on GitHub to find helpful code examples from other developers. - -**Jump to:** [C# / .NET](#csharp), [Dart / Flutter](#dart), [Go](#go), [Java](#java), [JavaScript (Node.JS) / TypeScript](#nodejs), [Kotlin](#kotlin), [PHP](#php), [PowerShell](#powershell), [Python](#python), [R](#r), [Ruby](#ruby), [Rust](#rust), [Swift](#swift) - -### C# / .NET - -- [**CoreTweet**](https://github.com/CoreTweet/CoreTweet) Yet Another .NET X Library -- [**LinqToTwitter**](https://github.com/JoeMayo/LinqToTwitter) LINQ Provider for the X API -- [**SocialOpinion**](https://github.com/jamiemaguiredotnet/SocialOpinion-Public) APIs written in C# that connect to the X API -- [**Tweetinvi**](https://github.com/linvi/tweetinvi) an intuitive X C# library -- [**TwitterSharp**](https://github.com/Xwilarg/TwitterSharp) C# wrapper around X API V2 - -### Go - -- [**ctw**](https://github.com/0dayfall/ctw) a library for the X API -- [**go-twitter**](https://github.com/g8rswimmer/go-twitter) a Go library for X v2 API integration. -- [**gotwi**](https://github.com/michimani/gotwi) a library for the X API v2 in Go -- [**gotwtr**](https://github.com/sivchari/gotwtr) a library for the X API -- [**twitter-stream**](https://github.com/Fallenstedt/twitter-stream) a Go wrapper for X's V2 Filtered Stream API -- [**twitter**](https://github.com/creachadair/twitter) a Go client for the X API - -### java - -- [**twittered**](https://github.com/redouane59/twittered) X API client for Java developers -- [**twitter4j-v2**](https://github.com/takke/twitter4j-v2) a simple wrapper for X API v2 that is designed to be used with Twitter4J -- [**twitter-compliance**](https://github.com/UCL/twitter-compliance) multi-module Jakarta EE application for syncing compliance events from X -- [**JTW**](https://github.com/uakihir0/jtw) X V2 API client library for Java - -### JavaScript (Node.JS) / TypeScript - -- [**node-twitter-api-v2**](https://github.com/PLhery/node-twitter-api-v2) strongly typed, full-featured, light, versatile yet powerful X API client for Node.js -- [**twitter.js**](https://github.com/twitterjs/twitter.js) an object-oriented Node.js and TypeScript library for interacting with X API v2 -- [**twitter-types**](https://github.com/twitterjs/twitter-types) type definitions for the X API -- [**twitter-v2**](https://github.com/HunterLarco/twitter-v2) An asynchronous client library for the X APIs -- [**tweet-json-to-html**](https://github.com/wdl/tweet-json-to-html) converts X API v2 Post JSON objects into HTML format +## Official SDKs -### Kotlin +X provides official SDKs for TypeScript and Python with full X API v2 support: -- [**KTweet**](https://github.com/ChromasIV/KTweet) a Kotlin library that allows you to consume the X API v2. -- [**Tweedle**](https://github.com/tyczj/Tweedle) a Kotlin-based Android library around the X v2 API -- [**TwitterApiKit**](https://github.com/kojofosu/TwitterApiKit) saves you time creating data objects to access X's API v2. This library is supported on Java, Kotlin, and Android + + + Async support, type hints, and automatic token refresh. Perfect for data analysis and automation. + + + Full type safety and ESM support. Works in Node.js and modern bundlers. + + -### PHP - -- [**bird-elephant**](https://github.com/danieldevine/bird-elephant) PHP client library for the X API v2 endpoints -- [**twifer**](https://github.com/ferrysyahrinal/twifer) Simple PHP Library for X API Standard v1.1 & X API v2 -- [**twitter-api-v2-php**](https://github.com/noweh/twitter-api-v2-php) PHP package providing easy and fast access to X API V2 -- [**twitteroauth**](https://github.com/abraham/twitteroauth) PHP library for use with the X API -- [**twitter-ultimate-php**](https://github.com/utxo-one/twitter-ultimate-php) PHP Wrapper for the X v2 API -- [**Twitter Stream API**](https://github.com/redwebcreation/twitter-stream-api) consume the X Stream API v2 in real-time - -### PowerShell - -- [**BluebirdPS**](https://github.com/thedavecarroll/BluebirdPS) a X Automation Client for PowerShell 7. Post, Retweet, send Direct Messages, manage lists, and more - -### Python - -- [**tweepy**](https://github.com/tweepy/tweepy) X for Python -- [**twarc**](https://twarc-project.readthedocs.io/en/latest/twarc2_en_us/) a command line tool and Python library for collecting JSON data via the X API, with a command (twarc2) for working with the v2 API -- [**python-twitter**](https://github.com/sns-sdks/python-twitter) a simple Python wrapper for X API v2 -- [**TwitterAPI**](https://github.com/geduldig/TwitterAPI) minimal Python wrapper for X's APIs -- [**twitterati**](https://github.com/JeannieDaniel/twitterati) Wrapper for X Developer API V2 -- [**twitter-stream.py**](https://github.com/twitivity/twitter-stream.py) a Python API client for X API v2 -- [**twitivity**](https://github.com/twitivity/twitivity) Account Activity API client library for Python -- [**PyTweet**](https://github.com/TheFarGG/PyTweet) a synchronous Python wrapper for the X API -- [**tweetkit**](https://github.com/ysenarath/tweetkit) a Python Client for the X API for Academic Research -- [**tweetple**](https://github.com/dapivei/tweetple) a wrapper to stream information from the Full-Archive Search Endpoint, for Academic Research -- [**2wttr**](https://github.com/simonlindgren/2wttr) get Posts from the v2 X API, for Academic Research +--- -### R +## Official tools -- [**academictwitteR**](https://github.com/cjbarrie/academictwitteR) R package to query the X Academic Research Product Track v2 API endpoint -- [**RTwitterV2**](https://github.com/MaelKubli/RTwitterV2) R functions for X's v2 API +| Tool | Description | +|:-----|:------------| +| [Postman Collection](https://app.getpostman.com/run-collection/9956214-784efcda-ed4c-4491-a4c0-a26470a67400) | Interactive API testing for all v2 endpoints | +| [OpenAPI Spec](https://api.x.com/2/openapi.json) | Machine-readable API specification | +| [twitter-text](https://github.com/twitter/twitter-text) | Parse and validate post text, count characters | -### Ruby +--- -- [**omniauth-twitter2**](https://github.com/unasuke/omniauth-twitter2) OmniAuth strategy for authenticating with X OAuth2 -- [**tweetkit**](https://github.com/julianfssen/tweetkit) X v2 API client for Ruby -- [**twitter_oauth2**](https://github.com/nov/twitter_oauth2) X OAuth 2.0 Client Library in Ruby +## Community libraries + +These community-maintained libraries support X API v2. Check each library's documentation for current API coverage. + + + +| Library | Description | +|:--------|:------------| +| [tweepy](https://github.com/tweepy/tweepy) | Popular Python library with v2 support | +| [twarc](https://twarc-project.readthedocs.io/) | CLI and library for data collection | +| [python-twitter](https://github.com/sns-sdks/python-twitter) | Simple Python wrapper | +| [TwitterAPI](https://github.com/geduldig/TwitterAPI) | Minimal Python wrapper | + + +| Library | Description | +|:--------|:------------| +| [node-twitter-api-v2](https://github.com/PLhery/node-twitter-api-v2) | Strongly-typed, full-featured Node.js client | +| [twitter.js](https://github.com/twitterjs/twitter.js) | Object-oriented Node.js library | +| [twitter-v2](https://github.com/HunterLarco/twitter-v2) | Async client library | + + +| Library | Description | +|:--------|:------------| +| [go-twitter](https://github.com/g8rswimmer/go-twitter) | Go library for v2 API | +| [gotwi](https://github.com/michimani/gotwi) | Go wrapper for v2 | +| [twitter-stream](https://github.com/Fallenstedt/twitter-stream) | Filtered stream wrapper | + + +| Library | Description | +|:--------|:------------| +| [twittered](https://github.com/redouane59/twittered) | Java client for v2 | +| [twitter4j-v2](https://github.com/takke/twitter4j-v2) | Twitter4J v2 wrapper | +| [KTweet](https://github.com/ChromasIV/KTweet) | Kotlin v2 library | +| [Tweedle](https://github.com/tyczj/Tweedle) | Kotlin Android library | + + +| Library | Description | +|:--------|:------------| +| [twitter-api-v2-php](https://github.com/noweh/twitter-api-v2-php) | PHP v2 client | +| [bird-elephant](https://github.com/danieldevine/bird-elephant) | PHP v2 library | +| [twitteroauth](https://github.com/abraham/twitteroauth) | Popular OAuth library | + + +| Library | Description | +|:--------|:------------| +| [tweetkit](https://github.com/julianfssen/tweetkit) | Ruby v2 client | +| [twitter_oauth2](https://github.com/nov/twitter_oauth2) | OAuth 2.0 library | +| [omniauth-twitter2](https://github.com/unasuke/omniauth-twitter2) | OmniAuth strategy | + + +| Language | Library | +|:---------|:--------| +| **C#/.NET** | [Tweetinvi](https://github.com/linvi/tweetinvi), [LinqToTwitter](https://github.com/JoeMayo/LinqToTwitter) | +| **Rust** | [twitter-v2](https://github.com/jpopesculian/twitter-v2-rs) | +| **Swift** | [Twift](https://github.com/daneden/Twift/), [TwitterAPIKit](https://github.com/mironal/TwitterAPIKit) | +| **R** | [academictwitteR](https://github.com/cjbarrie/academictwitteR) | +| **PowerShell** | [BluebirdPS](https://github.com/thedavecarroll/BluebirdPS) | + + + + +Community libraries are not maintained by X. Check their repositories for support and current status. + -### Rust +--- -- [**twitter-v2**](https://github.com/jpopesculian/twitter-v2-rs) Rust bindings for X API v2 +## Code samples -### Swift +Find examples on GitHub: -- [**Twift**](https://github.com/daneden/Twift/) An async Swift library for the X v2 API -- [**TwitterAPIKit**](https://github.com/mironal/TwitterAPIKit) Swift library for the X API v1 and v2 +- [X API v2 Sample Code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) — Examples in Python, JavaScript, Ruby, and more +- [X Developer GitHub](https://github.com/xdevplatform) — Official repos and tools +- [Glitch Examples](https://glitch.com/@twitter) — Interactive, remixable apps +- [Replit Examples](https://replit.com/@twitter) — Browser-based coding --- -## Official v1.1 tools and libraries - -The X teams maintain a set of official libraries and SDKs, listed here.\ -We also include a list of [community-supported libraries](#community-libraries) lower on this page. - -| Language | Clients | SDKs / Libraries | Tools | -| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------- | -| JavaScript / Node.js | -- | -- | [Autohook](https://www.npmjs.com/package/twitter-autohook) - Get started with the **Premium v1.1** Account Activity API | -| Python | [search-tweets-python](https://github.com/xdevplatform/search-tweets-python) - A client supporting **v2**, **Premium v1.1**, and **Enterprise** search | -- | -- | -| Ruby | [search-tweets-ruby](https://github.com/sferik/twitter) - A client supporting **v2**, **Premium v1.1**, and **Enterprise** search | -- | -- | +## Building a library? --- +If you've built an X API library, share it with the community: -## Additional official resources +1. Post in the [Libraries & SDKs forum](https://devcommunity.x.com/c/libraries-and-sdks/63) +2. We may add it to this page! -The tools below can also be useful when working with the X API.\ -Looking for even more code? You can find examples on our [GitHub](https://github.com/xdevplatform) and on [Glitch](https://glitch.com/@twitter). - -| Tool / Library | Description | -| :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| twemoji | Twitter’s free, open source emoji character set, including a JavaScript library for cross-platform support. | -| twitter-text | A collection of libraries to standardize parsing and tokenization of Tweet text. Available for Java, JavaScript, Objective-C & Ruby. [Learn more about counting characters in Tweets.](#) | -| OpenAPI specification | Use this specification to exercise the **v2** API with tools like [Postman](#) or [Insomnia](#). | -| twurl | A command-line tool (CLI) for interacting with the Twitter API, including OAuth authentication. Requires a Ruby runtime. | -| Postman collection | Explore the Postman collection to work with X API endpoints for testing and development. | +Use our [version badges](https://twbadges.glitch.me/) in your README to show API compatibility. --- -## Community tools and libraries - -These are some of the many community-supported libraries that cover the X API across several programming languages and platforms. Note that these resources may not all have been tested by the Twitter team. - -The libraries listed here should implement most features of the Standard API **v1.1**, unless otherwise noted—check with the authors for details and additional support.\ -If you have built a library that supports X API **v2**, please let us know about it via our [community forums](https://devcommunity.x.com/c/libraries-and-sdks/63) for possible addition to this page. You can also use the forums to report any changes to these listings. +## Getting help -If you're missing a library or tool for your favorite programming language, let us know via the [feedback platform](https://twitterdevfeedback.uservoice.com/), where you can also vote for ideas or get inspired to build and submit something new. \ No newline at end of file + + Get help from the community. + diff --git a/x-api/tools-and-libraries/sdks.mdx b/x-api/tools-and-libraries/sdks.mdx index dd0a11a88..3329f6e54 100644 --- a/x-api/tools-and-libraries/sdks.mdx +++ b/x-api/tools-and-libraries/sdks.mdx @@ -1,148 +1,218 @@ --- -title: SDKs -keywords: ["SDK", "software development kit", "client SDK", "API SDK", "developer SDK", "SDK libraries", "official SDK"] +title: Official SDKs +sidebarTitle: SDKs +description: Official X API SDKs for TypeScript and Python +keywords: ["SDK", "software development kit", "client SDK", "API SDK", "official SDK"] --- -## Introduction +import { Button } from '/snippets/button.mdx'; -A software development kit (SDK) is a set of software tools and programs tailored for a specific platform or API. The purpose of an SDK is to build or extend functionality of applications by providing libraries or codebases that developers can use within their applications, efficiently and with minimal coding. This significantly speeds up the development process, saving time, money, and effort. +X provides official SDKs for TypeScript and Python. These libraries handle authentication, pagination, and provide full type safety. -> The X Developer Platform now offers 2 official SDKs for those who develop in TypeScript/Javascript and Java. These will allow developers to build more effectively by eliminating the need to manually program the complexities around the X API v2, utilizing the pre-built functions for all available v2 endpoints as well as simplifying the authentication process. As these are built and maintained by the Developer Platform team, they will always be up to date with future releases to the X API v2 + + + Async support, type hints, comprehensive v2 coverage. + + + Full TypeScript types, ESM support, works with Node.js. + + -Since these SDKs wrap the X API, you must have a [developer account](https://developer.x.com/en/portal/dashboard) to authenticate requests using the credentials from a [developer App](/resources/fundamentals/developer-apps), located within a [Project](/resources/fundamentals/projects). +--- + +## Why use the official SDKs? + +| Benefit | Description | +|:--------|:------------| +| **Always up-to-date** | Maintained by X, updated with new endpoints | +| **Type safety** | Full type definitions for all objects and methods | +| **Built-in auth** | OAuth 2.0 and OAuth 1.0a support | +| **Automatic pagination** | Iterate through results without manual token handling | + +--- -#### Installing +## Quick start - -There are a few ways to install the Java package (requires Java 1.8+) -- Maven users - add this dependency to your project's POM file: -```xml - - com.twitter - twitter-api-java-sdk - 1.1.4 - -``` -- Grade users - add this dependency to your project's build file: -`implementation "com.twitter:twitter-api-java-sdk:1.1.4"` + +### Installation -- Others - first generate the JAR by running the following command -`mvn clean package` -Then manually install the following JARs: +```bash +pip install xdk +``` -`target/twitter-api-java-sdk-1.1.4.jar` +### Basic usage -`target/lib/*.jar` - - -This package supports Node.js 14+, to install, run the following command in the directory of the Node project: -`npm install twitter-api-sdk` - - +```python +from xdk import Client -#### Client basics +client = Client(bearer_token="YOUR_BEARER_TOKEN") -Import the classes (Java) and package (TypeScript) at the top of a working file to gain access to the authentication and library clients. In order to use the methods from the library client, authentication credentials must be passed, this could either be Bearer Token (App-only) or client id/client secret if authenticating with OAuth 2.0 user context. +# Search for posts (returns an iterator) +for page in client.posts.search_recent(query="api", max_results=10): + if page.data and len(page.data) > 0: + first_post = page.data[0] + print(first_post.text) + break +``` -Here are examples of how this would look: + + + +### Installation - - -```java -// Import classes: -import com.twitter.clientlib.ApiClient; -import com.twitter.clientlib.ApiException; -import com.twitter.clientlib.Configuration; -import com.twitter.clientlib.auth.*; -import com.twitter.clientlib.model.*; -import com.twitter.clientlib.TwitterCredentialsBearer; -import com.twitter.clientlib.api.TwitterApi; - -// Instantiate library client -TwitterApi apiInstance = new TwitterApi(); - -// Instantiate auth credentials (App-only example) -TwitterCredentialsBearer credentials = new TwitterCredentialsBearer(System.getenv("APP-ONLY-ACCESS-TOKEN")); - -// Pass credentials to library client -apiInstance.setTwitterCredentials(credentials); +```bash +npm install @xdevplatform/xdk ``` - - +### Basic usage + ```typescript -//Import package -import { Client, auth } from "twitter-api-sdk"; - -// Initialize auth client first -const authClient = new auth.OAuth2User({ - client_id: process.env.CLIENT_ID as string, - client_secret: process.env.CLIENT_SECRET as string, - callback: "YOUR-CALLBACK", - scopes: ["tweet.read", "users.read", "offline.access"], -}); +import { Client } from '@xdevplatform/xdk'; + +const client = new Client({ bearerToken: 'YOUR_BEARER_TOKEN' }); - // Pass auth credentials to the library client - const twitterClient = new Client(authClient); +// Look up a user +const userResponse = await client.users.getByUsername('XDevelopers'); +console.log(userResponse.data?.username); ``` - + + + -#### Authentication Flow +--- -If you are using the application only option to authenticate the SDKs, you will only need to provide the token and the library client will be ready to use the endpoint methods right away. Keep in mind, application only tokens cannot be used on endpoints that require user context authentication. +## Authentication -OAuth 2.0 user context authentication requires a few extra steps after creating the auth client. +Both SDKs support multiple authentication methods: -- Generate authorization URL -- Authorize the application from the authorization URL -- Redirects to callback (this should be matching the callback URL set in the auth settings page in the Developer Portal). -- Parse code verifier to exchange for access token + + +Simplest option for reading public data. + +**Python:** +```python +from xdk import Client -The SDKs provide methods on the auth client that simplifies these steps. For a full example of how to make a request authenticating with OAuth 2.0 user context, check out the GitHub repositories. +client = Client(bearer_token="YOUR_BEARER_TOKEN") +``` -- [TypeScript](https://github.com/xdevplatform/twitter-api-typescript-sdk/blob/main/examples/oauth2-callback_pkce_s256.ts) +**TypeScript:** +```typescript +import { Client } from '@xdevplatform/xdk'; -- [Java](https://github.com/xdevplatform/twitter-api-java-sdk/blob/main/examples/src/main/java/com/twitter/clientlib/auth/OAuth20GetAccessToken.java) +const client = new Client({ bearerToken: 'YOUR_BEARER_TOKEN' }); +``` + + +For actions on behalf of users (posting, following, etc.). + +**Python:** +```python +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +auth = OAuth2PKCEAuth( + client_id="YOUR_CLIENT_ID", + redirect_uri="YOUR_CALLBACK_URL", + scope="tweet.read users.read offline.access" +) + +# Get authorization URL +auth_url = auth.get_authorization_url() + +# After user authorizes, exchange code for tokens +tokens = auth.fetch_token(authorization_response=callback_url) +client = Client(bearer_token=tokens["access_token"]) +``` -#### Endpoint methods +**TypeScript:** +```typescript +import { Client, OAuth2, generateCodeVerifier, generateCodeChallenge } from '@xdevplatform/xdk'; -The methods provided within the library client are clearly named to correspond with every endpoint and all parameters are passed in as arguments. -Here is an example of Post lookup by ID: +const oauth2 = new OAuth2({ + clientId: 'YOUR_CLIENT_ID', + clientSecret: 'YOUR_CLIENT_SECRET', + redirectUri: 'https://your-app.com/callback', + scope: ['tweet.read', 'users.read', 'offline.access'], +}); - - -```java -String id = "1511757922354663425"; // String | A single Tweet ID. -Set expansions = new HashSet<>(Arrays.asList("author_id")); // Set | A comma separated list of fields to expand. -Set tweetFields = new HashSet<>(Arrays.asList("created_at", "lang", "context_annotations")); // Set | A comma separated list of Tweet fields to display. -Set userFields = new HashSet<>(Arrays.asList("created_at", "description", "name")); // Set | A comma separated list of User fields to display. - -try { - SingleTweetLookupResponse result = apiInstance.tweets().findTweetById(id, expansions, tweetFields, userFields, null, null, null); - System.out.println(result); -} catch (ApiException e) { - System.err.println("Exception when calling TweetsApi#findTweetById"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); -} +const codeVerifier = generateCodeVerifier(); +const codeChallenge = await generateCodeChallenge(codeVerifier); +oauth2.setPkceParameters(codeVerifier, codeChallenge); +const authUrl = await oauth2.getAuthorizationUrl('state'); +// After authorization, exchange code +const tokens = await oauth2.exchangeCode(authCode, codeVerifier); +const client = new Client({ accessToken: tokens.access_token }); ``` - - + + +For legacy applications or specific use cases. + +**Python:** +```python +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) +``` + +**TypeScript:** ```typescript -const lookupTweetById = await client.tweets.findTweetById( - // Tweet ID - "1511757922354663425", - { - // Optional parameters - expansions: ["author_id"], - "user.fields": ["created_at", "description", "name"], - } -); +import { Client, OAuth1 } from '@xdevplatform/xdk'; + +const oauth1 = new OAuth1({ + apiKey: 'YOUR_API_KEY', + apiSecret: 'YOUR_API_SECRET', + accessToken: 'YOUR_ACCESS_TOKEN', + accessTokenSecret: 'YOUR_ACCESS_TOKEN_SECRET' +}); + +const client = new Client({ oauth1: oauth1 }); ``` - + + +--- + +## Available methods + +The SDKs provide methods for all X API v2 endpoints: + +| Category | Python | TypeScript | +|:---------|:-------|:-----------| +| **Posts** | `client.posts.search_recent()` | `client.posts.search()` | +| **Users** | `client.users.get_me()` | `client.users.getMe()` | +| **Spaces** | `client.spaces.get()` | `client.spaces.findSpaceById()` | +| **Lists** | `client.lists.get()` | `client.lists.getList()` | +| **DMs** | `client.direct_messages.get()` | `client.directMessages.lookup()` | + +See the full SDK documentation for complete method reference. + +--- + +## Resources + + + + Complete Python documentation. + + + Complete TypeScript documentation. + + + Source code and issues. + + + Source code and issues. + + diff --git a/x-api/trends/personalized-trends/introduction.mdx b/x-api/trends/personalized-trends/introduction.mdx index c93e899f8..b22392700 100644 --- a/x-api/trends/personalized-trends/introduction.mdx +++ b/x-api/trends/personalized-trends/introduction.mdx @@ -1,77 +1,79 @@ --- -title: Introduction +title: Personalized Trends sidebarTitle: Introduction -keywords: ["personalized trends", "user trends", "personal trends", "trending for you", "personalized trending", "user-specific trends"] +description: Get trending topics personalized for the authenticated user +keywords: ["personalized trends", "trends API", "trending topics", "user trends", "for you trends"] --- import { Button } from '/snippets/button.mdx'; -The Personalized Trends endpoint allows you to get personalized trends for an authenticating user account. - -**Note:** Personalized trends will only be returned by the endpoint for user accounts that are subscribed to X Premium. - - -**Account setup** +The Personalized Trends endpoint returns trending topics tailored to the authenticated user, based on their location and interests. -To access this endpoint, you will need: +## Overview -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  - -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - -### Getting started + + + Trends tailored to the user + + + Based on user's location + + + Current trending topics + + -#### Authentication +--- -You can authenticate this endpoint with either [OAuth 1.0a User Context](/resources/fundamentals/authentication), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). -  +## Endpoint -#### Making a request +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | `/2/users/personalized_trends` | Get personalized trends | -You can call the Personalized Trends endpoint as shown below: +--- - `curl 'https://api.x.com/2/users/personalized_trends' --header 'Authorization: ••••••'` +## Example request +```bash +curl "https://api.x.com/2/users/personalized_trends" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -If the request is successful, you should see the JSON response as shown below: +## Example response -``` +```json { - "data": [ - { - "category": "Bitcoin", - "post_count": "42K posts", - "trend_name": "MicroStrategy's $42 Billion Bitcoin Investment", - "trending_since": "5 hours ago" - }, - { - "category": "Music", - "post_count": "2K posts", - "trend_name": "Drake's 'No Face' Music Video Release", - "trending_since": "Trending now" - }, - { - "category": "Weather", - "post_count": "1.2K posts", - "trend_name": "Super Typhoon Leon Impacts Taiwan and Batanes", - "trending_since": "Trending now" - } - ] + "data": [ + { + "trend_name": "#AI", + "tweet_count": 125000 + }, + { + "trend_name": "Machine Learning", + "tweet_count": 85000 + } + ] } ``` -**Note:** If the authenticated user of the request is not subscribed to X Premium, they will receive the following response instead: +--- -``` -{ - "data": [ - { - "category": "Unknown", - "post_count": "Unknown", - "trending_since": "" - } - ] -} -``` +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) + + + + Get trends for a specific location + + + Full endpoint documentation + + diff --git a/x-api/trends/trends-by-woeid/introduction.mdx b/x-api/trends/trends-by-woeid/introduction.mdx index 19cf5179c..c53b57646 100644 --- a/x-api/trends/trends-by-woeid/introduction.mdx +++ b/x-api/trends/trends-by-woeid/introduction.mdx @@ -1,83 +1,94 @@ --- -title: Introduction +title: Trends by WOEID sidebarTitle: Introduction -keywords: ["trends by WOEID", "trending topics", "location trends", "WOEID trends", "trending topics API", "trends API"] +description: Get trending topics for a specific geographic location +keywords: ["trends by WOEID", "location trends", "geographic trends", "regional trends", "woeid API"] --- import { Button } from '/snippets/button.mdx'; -The Trends lookup endpoint allow developers to get the Trends for a location, specified using the where-on-earth id (WOEID). - -**Note**: WOEID is a legacy identifier created by Yahoo and has been deprecated. X API uses the numeric value to identify town and country trend locations. Reference our legacy [blog post](https://blog.x.com/engineering/en_us/a/2010/woeids-in-twitters-trends.html), or [archived data](https://archive.org/details/geoplanet_data_7.10.0.zip0.) - -The `tweet_count` for the last 24 hours is also returned for many trends if this is available. +The Trends by WOEID endpoint returns trending topics for a specific geographic location, identified by a Yahoo! Where On Earth ID (WOEID). -This endpoint supports app-auth authentication and has a rate limit of 75 requests per 15-minute window. +## Overview -### Getting started + + + Trends for any supported location + + + Countries, cities, and regions + + + Current trending topics + + -To use this endpoint, you need a [bearer token](https://developer.x.com(/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) from the [developer portal](https://developer.x.com/en/portal/dashboard). Once you have the bearer token,  you can call the usage API as shown below: +--- - ``` - curl 'https://api.x.com/2/trends/by/woeid/26062' --header 'Authorization: Bearer XXXXX' - ``` +## Endpoint -If the request is successful, you should see the JSON response as shown below: +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/trends/by/woeid/:id`](/x-api/trends/get-trends-by-woeid) | Get trends for a WOEID | + +--- + +## Common WOEIDs + +| Location | WOEID | +|:---------|:------| +| Worldwide | 1 | +| United States | 23424977 | +| United Kingdom | 23424975 | +| Japan | 23424856 | +| New York | 2459115 | +| Los Angeles | 2442047 | +| London | 44418 | +| Tokyo | 1118370 | + +--- +## Example request + +```bash +curl "https://api.x.com/2/trends/by/woeid/1" \ + -H "Authorization: Bearer $BEARER_TOKEN" ``` + +## Example response + +```json { - "data": [ - { - "trend_name": "Europe", - "tweet_count": 232408 - }, - { - "trend_name": "Isak", - "tweet_count": 2956 - }, - { - "trend_name": "RNLI", - "tweet_count": 2484 - }, - { - "trend_name": "Toon", - "tweet_count": 11447 - }, - { - "trend_name": "St James", - "tweet_count": 5565 - }, - { - "trend_name": "Manning", - "tweet_count": 10077 - }, - { - "trend_name": "Copenhagen", - "tweet_count": 35272 - }, - { - "trend_name": "Coventry", - "tweet_count": 3662 - ] + "data": [ + { + "trend_name": "#AI", + "tweet_count": 250000 + }, + { + "trend_name": "Breaking News", + "tweet_count": 180000 + } + ] } ``` -**Account setup** - -To access these endpoints, you will need: - -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +--- -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +## Getting started + +**Prerequisites** -
- - -
+- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [Bearer Token](/resources/fundamentals/authentication) +
+ + + Get trends for the authenticated user + + + Full endpoint documentation + + diff --git a/x-api/usage/introduction.mdx b/x-api/usage/introduction.mdx index 7badd96cc..723ca250e 100644 --- a/x-api/usage/introduction.mdx +++ b/x-api/usage/introduction.mdx @@ -1,55 +1,111 @@ --- -title: Introduction +title: Usage sidebarTitle: Introduction -keywords: ["usage API", "API usage", "usage monitoring", "usage tracking", "project usage", "billing usage", "consumption tracking"] +description: Monitor your API usage and track Post consumption +keywords: ["usage API", "API usage", "usage tracking", "post usage", "consumption monitoring"] --- import { Button } from '/snippets/button.mdx'; -The Usage API in the X API v2 allows developers to programmatically retrieve their project usage. Using this endpoint, developers can keep track and monitor the number of Posts they have pulled for a given billing cycle. +The Usage endpoint lets you monitor your API usage, including the number of Posts consumed. Track your usage programmatically to manage costs and stay within limits. -Developers can use the GET endpoint to get the daily project usage for upto the last 90 days. The usage can also be aggregated per client app connected to your project. +## Overview -There is a app rate limit of 50 requests per 15 minutes for this GET endpoint. + + + Track Posts consumed + + + View usage by day + + + Monitor usage across your App + + -### Getting started +--- -To use this endpoint, you need a [bearer token](https://developer.x.com/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) from the [developer portal](https://developer.x.com/en/portal/dashboard). Once you have the bearer token,  you can call the usage API as shown below: +## Endpoint - ``` - curl 'https://api.x.com/2/usage/tweets' --header 'Authorization: Bearer XXXXX' - ``` +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/usage/tweets`](/x-api/usage/get-usage) | Get Post usage data | +--- -If the request is successful, you should see the JSON response as shown below: +## Response data -``` +The response includes daily Post consumption counts: + +```json { - "data": { - "cap_reset_day": 10, - "project_cap": "5000000000", - "project_id": "1369785403853424", - "project_usage": "43435" - } + "data": { + "daily_project_usage": [ + { + "date": "2024-01-15", + "usage": [ + { + "app_id": "12345678", + "tweets_consumed": 15420 + } + ] + } + ], + "project_id": "1234567890", + "project_cap": 10000000 + } } ``` - -**Account setup** +### Fields + +| Field | Description | +|:------|:------------| +| `daily_project_usage` | Array of daily usage data | +| `date` | Date in YYYY-MM-DD format | +| `app_id` | Your App's ID | +| `tweets_consumed` | Posts consumed that day | +| `project_cap` | Your monthly Post limit | + +--- + +## Example request + +```bash +curl "https://api.x.com/2/usage/tweets" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +--- + +## Use cases + +- **Cost monitoring** — Track consumption against your plan limits +- **Alerting** — Set up alerts when approaching limits +- **Optimization** — Identify high-consumption endpoints +- **Reporting** — Generate usage reports -To access these endpoints, you will need: + +Resources are deduplicated within a 24-hour UTC window, so requesting the same resource multiple times in a day only counts as one charge. [Learn more about pricing →](/x-api/getting-started/pricing) + -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +--- + +## Getting started -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [Bearer Token](/resources/fundamentals/authentication) -
- - - -
+ + + + Full endpoint documentation + + + View usage in the dashboard + + diff --git a/x-api/users/blocks/integrate.mdx b/x-api/users/blocks/integrate.mdx index 367f718dc..0b19ee42b 100644 --- a/x-api/users/blocks/integrate.mdx +++ b/x-api/users/blocks/integrate.mdx @@ -1,74 +1,194 @@ --- -title: Integration guide -sidebarTitle: Integration guide -keywords: ["blocks integration", "block users integration", "blocks guide", "block integration guide", "blocks setup", "block users setup"] +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for integrating blocks endpoints +keywords: ["blocks integration", "block users integration", "blocks guide"] --- import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and key concepts that you should be aware of as you integrate the blocks endpoints into your system. We’ve broken the page into a couple of different sections: +This guide covers the key concepts you need to integrate the blocks endpoints into your application. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) -* [Pagination](#pagination) +--- + +## Authentication + +Blocks endpoints require user authentication: + +| Method | Description | +|:-------|:------------| +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended for new applications | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | + + +App-Only authentication is not supported. You must authenticate on behalf of a user. + + +### Required scopes (OAuth 2.0) + +| Scope | Required for | +|:------|:-------------| +| `block.read` | Retrieving blocked accounts | +| `block.write` | Blocking and unblocking accounts | +| `users.read` | Required with block scopes | + +--- + +## Endpoints overview -### Helpful tools +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | `/2/users/:id/blocking` | Get list of blocked accounts | +| POST | `/2/users/:id/blocking` | Block an account | +| DELETE | `/2/users/:source_user_id/blocking/:target_user_id` | Unblock an account | + +--- -Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: +## Fields and expansions + +### Default response + +```json +{ + "data": [ + { + "id": "1234567890", + "name": "Example User", + "username": "example" + } + ] +} +``` + +### Available fields + + +| Field | Description | +|:------|:------------| +| `created_at` | Account creation date | +| `description` | User bio | +| `profile_image_url` | Avatar URL | +| `public_metrics` | Follower/following counts | +| `verified` | Verification status | + + + +| Expansion | Description | +|:----------|:------------| +| `pinned_tweet_id` | User's pinned Post | + -#### Postman +--- -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +## What happens when you block + + + + - See your Posts (unless logged out) + - Follow you + - Send you DMs + - Add you to Lists + - Tag you in photos + + + - See their Posts + - Follow them + - Send them DMs + + + + +When you block someone who follows you, they are automatically unfollowed. + -#### Code samples +--- -Interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +## Pagination -#### Third-party libraries +For users with large block lists, results are paginated: -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. + -### Key concepts +```bash cURL +# First request +curl "https://api.x.com/2/users/123/blocking?max_results=100" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" -#### Authentication +# Subsequent request with pagination token +curl "https://api.x.com/2/users/123/blocking?max_results=100&pagination_token=NEXT_TOKEN" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE to authenticate your requests to these endpoints.  +```python Python SDK +from xdk import Client -[OAuth 1.0a User Context](/resources/fundamentals/authentication) requires you to utilize your API Keys, user Access Tokens, and a handful of other parameters to [create an authorization header](/resources/fundamentals/authentication#authorizing-a-request), which you will then pass with your request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](https://developer-staging.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens).  +# Use OAuth 2.0 user access token +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. +# The SDK handles pagination automatically +all_blocked = [] -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-3) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +for page in client.users.get_blocking(user_id="123", max_results=100): + if page.data: + all_blocked.extend(page.data) -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +print(f"Blocked {len(all_blocked)} users") +``` -#### Developer portal, Projects, and developer Apps +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -  +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -#### Rate limits +async function getAllBlockedUsers(userId) { + const allBlocked = []; -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user.  + // The SDK handles pagination automatically + const paginator = client.users.getBlocking(userId, { maxResults: 100 }); -These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. There is a user rate limit of 50 requests per 15 minutes per endpoint with both POST and DELETE methods. However, with the GET method, the rate limit is only 15 requests per 15 minutes. -  + for await (const page of paginator) { + if (page.data) { + allBlocked.push(...page.data); + } + } -#### Fields and expansions + return allBlocked; +} -The X API v2 GET endpoints allow users to select exactly which data they want to return from the API using a set of tools called fields and expansions. The expansions parameter allows you to expand objects referenced in the payload. For example, this endpoint allows you to pull the following [expansions](/x-api/fundamentals/expansions): +// Usage +const blocked = await getAllBlockedUsers("123"); +console.log(`Blocked ${blocked.length} users`); +``` -* pinned\_tweet\_id + +--- -The **fields** parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. These endpoints delivers Post objects primarily. By default, the Post object returns the **id** and **text** fields. To receive additional fields such as **tweet.created_at** or **tweet.entities**, you will have to specifically request those using a **fields** parameter. Some important fields that you may want to consider using in your integration are our poll data, metrics, Post annotations, and conversation ID fields. +## Error handling -We’ve added a guide on how to [use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). +| Status | Error | Solution | +|:-------|:------|:---------| +| 400 | Invalid request | Check user ID format | +| 401 | Unauthorized | Verify access token | +| 403 | Forbidden | Check scopes and permissions | +| 404 | Not Found | User doesn't exist | +| 429 | Too Many Requests | Wait and retry | -#### Pagination +--- -Blocks lookup can return a lot of data. To ensure we don’t return to many results at any given time, we use pagination. Learn more about how to [paginate through results.](/x-api/fundamentals/pagination) \ No newline at end of file +## Next steps + + + + Make your first blocks request + + + Mute users instead of blocking + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/users/blocks/introduction.mdx b/x-api/users/blocks/introduction.mdx index b2b900b6d..af03a1384 100644 --- a/x-api/users/blocks/introduction.mdx +++ b/x-api/users/blocks/introduction.mdx @@ -1,38 +1,77 @@ --- -title: Introduction +title: Blocks sidebarTitle: Introduction -keywords: ["blocks", "block users", "blocked accounts", "block lookup", "manage blocks", "user blocking"] +description: Block and unblock users, and retrieve blocked user lists +keywords: ["blocks", "block user", "unblock", "blocked users", "block API"] --- import { Button } from '/snippets/button.mdx'; -### Blocks lookup +The Blocks endpoints let you block and unblock users, and retrieve the list of users blocked by the authenticated user. -The blocks lookup GET endpoint allows you to see which accounts you’ve blocked on behalf of an authorized user. This endpoint has a rate limit of 15 requests per 15 minutes per user.  +## Overview -Since you are making requests for private information with blocks lookup, and on behalf of a user with manage blocks, you must authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and use the user Access Tokens associated with a user that has authorized your App, which can be generated using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (OAuth 1.0a) or the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)) (OAuth 2.0). - -**Account setup** + + + Block a user + + + Unblock a user + + + Get your blocked user list + + + +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/:id/blocking`](/x-api/users/get-blocking) | Get blocked users | +| POST | [`/2/users/:id/blocking`](/x-api/users/block-dms) | Block a user | +| DELETE | `/2/users/:source_user_id/blocking/:target_user_id` | Unblock a user | + +--- -To access these endpoints, you will need: +## Example: Get blocked users -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +```bash +curl "https://api.x.com/2/users/123456789/blocking?\ +user.fields=username,description" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +## Example: Block a user + +```bash +curl -X POST "https://api.x.com/2/users/123456789/blocking" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "9876543210"}' +``` + +--- + +## Getting started + + +**Prerequisites** -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) -
- - - - -
\ No newline at end of file + + + Get started with blocks + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/users/blocks/migrate.mdx b/x-api/users/blocks/migrate.mdx index 3c1425742..24f95a4b4 100644 --- a/x-api/users/blocks/migrate.mdx +++ b/x-api/users/blocks/migrate.mdx @@ -46,7 +46,7 @@ The standard v1.1 endpoints allow you to return up to 5000 users per request. Th **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Response data format** @@ -80,3 +80,151 @@ We also introduced a new set of fields to the [Post object](/x-api/fundamentals **Request parameters** The following standard v1.1 request parameters accepted two request query parameters (user_id or screen_name). The X API v2 only accepts the numerical user ID, and it must be passed as part of the endpoint path. + +--- + +## Code examples + +### Get blocked users (v2) + + + +```bash cURL +curl "https://api.x.com/2/users/123456789/blocking?user.fields=username,verified&max_results=100" \ + -H "Authorization: OAuth ..." +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/blocking" +params = {"user.fields": "username,verified", "max_results": 100} + +response = requests.get(url, auth=auth, params=params) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Get blocked users with pagination +for page in client.users.get_blocking( + "123456789", + user_fields=["username", "verified"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Get blocked users with pagination +const paginator = client.users.getBlocking("123456789", { + userFields: ["username", "verified"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); + }); +} +``` + + + +### Block a user (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/blocking" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "2244994945"}' +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/blocking" +response = requests.post(url, auth=auth, json={"target_user_id": "2244994945"}) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Block a user +response = client.users.block( + source_user_id="123456789", + target_user_id="2244994945" +) +print(f"Blocking: {response.data.blocking}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Block a user +const response = await client.users.block("123456789", { + targetUserId: "2244994945", +}); +console.log(`Blocking: ${response.data?.blocking}`); +``` + + diff --git a/x-api/users/blocks/quickstart.mdx b/x-api/users/blocks/quickstart.mdx index d98db7e87..31e91fe47 100644 --- a/x-api/users/blocks/quickstart.mdx +++ b/x-api/users/blocks/quickstart.mdx @@ -1,148 +1,257 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["blocks quickstart", "block users quickstart", "blocks tutorial", "block guide", "blocks quickstart guide"] +description: Block and unblock users, and retrieve your block list +keywords: ["blocks quickstart", "block users quickstart", "blocks tutorial", "block guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the blocks lookup endpoint +This guide walks you through blocking and unblocking users, and retrieving your block list. -This quick start guide will help you make your first request to the blocks lookup endpoint using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository. -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build a blocks lookup request -#### Step one: Start with a tool or library +--- -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. +## Get blocked users -To load the X API v2 Postman collection into your environment, please click on the following button: + + + You need your authenticated user's ID to retrieve your block list. You can get it from the `/2/users/me` endpoint or use the ID from your tokens. + - -Once you have the X API v2 collection loaded in Postman, navigate to the “Blocks” folder, and select “Blocks Lookup”. -  + -#### Step two: Authenticate your request + -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +```bash cURL +curl "https://api.x.com/2/users/123456789/blocking?\ +user.fields=username,verified,created_at&\ +max_results=100" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -In this example, we are going to use OAuth 1.0a User Context. +```python Python SDK +from xdk import Client -You must add your keys and tokens – specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret – to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +# Get blocked users with pagination +for page in client.users.get_blocking( + "123456789", + user_fields=["username", "verified", "created_at"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Created: {user.created_at}") +``` -#### Step three: Specify a user +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -With this endpoint, you must specify your user ID or the user ID of an authenticated user to see who you have blocked. +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -In Postman, navigate to the "Params" tab and enter this username into the "Value" column of the id path variable (at the bottom of the section), making sure to not include any spaces before or after usernames. -  +// Get blocked users with pagination +const paginator = client.users.getBlocking("123456789", { + userFields: ["username", "verified", "created_at"], + maxResults: 100, +}); -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | (your user ID) | -| max_results | 5 | +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Created: ${user.created_at}`); + }); +} +``` -#### Step four: Identify and specify which fields you would like to retrieve + -If you click the "Send" button after step three, you will receive the default [user object](/x-api/fundamentals/data-dictionary#user) fields in your response: id, name, and username. + -If you would like to receive additional fields beyond id, name, and username, you will have to specify those fields in your request with the [fields](/x-api/fundamentals/fields) and/or [expansions](/x-api/fundamentals/expansions) parameters. + + ```json + { + "data": [ + { + "id": "17874544", + "name": "Example User", + "username": "example_user", + "verified": false, + "created_at": "2008-12-04T18:51:57.000Z" + } + ], + "meta": { + "result_count": 1, + "next_token": "abc123" + } + } + ``` + + -For this exercise, we will request three additional sets of fields from different objects: +--- -1. The additional user.created_at field in the primary user objects. -2. The associated pinned Posts’ object’s default fields for the returned users: id and text. -3. The additional  tweet.created_at field in the associated Post objects. +## Block a user -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: + + + Get the user ID of the account you want to block. + -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| user.fields | created_at | user.created_at | -| expansions | pinned\_tweet\_id | tweet.id, tweet.text | -| tweet.fields | created_at | includes.tweets.created_at | + -You should now see a similar URL with your own user ID instead of TwitterDev’s URL next to the "Send" button: + - ``` - https://api.x.com/2/users/2244994945/blocking?user.fields=created_at&expansions=pinned_tweet_id&tweet.fields=created_at - ``` +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/blocking" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "9876543210"}' +``` -#### Step five: Make your request and review your response +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Block a user +response = client.users.block( + source_user_id="123456789", + target_user_id="9876543210" +) +print(f"Blocking: {response.data.blocking}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -Once you have everything set up, hit the "Send" button and you will receive a similar response to the following example response: +const client = new Client({ oauth1 }); +// Block a user +const response = await client.users.block("123456789", { + targetUserId: "9876543210", +}); +console.log(`Blocking: ${response.data?.blocking}`); ``` + + + + + + + ```json { - "data": [ - { - "created_at": "2008-12-04T18:51:57.000Z", - "id": "17874544", - "username": "TwitterSupport", - "name": "Twitter Support" - }, - { - "created_at": "2007-02-20T14:35:54.000Z", - "id": "783214", - "username": "Twitter", - "name": "Twitter" - }, - { - "pinned_tweet_id": "1389270063807598594", - "created_at": "2018-11-21T14:24:58.000Z", - "id": "1065249714214457345", - "username": "TwitterSpaces", - "name": "Spaces" - }, - { - "pinned_tweet_id": "1293595870563381249", - "created_at": "2007-05-23T06:01:13.000Z", - "id": "6253282", - "username": "XAPI", - "name": "X API" - } - ], - "includes": { - "tweets": [ - { - "created_at": "2021-05-03T17:26:09.000Z", - "id": "1389270063807598594", - "text": "now, everyone with 600 or more followers can host a Space.\n\nbased on what we've learned, these accounts are likely to have a good experience hosting because of their existing audience. before bringing the ability to create a Space to everyone, we’re focused on a few things. 🧵" - }, - { - "created_at": "2020-08-12T17:11:04.000Z", - "id": "1293595870563381249", - "text": "X API v2: Early Access released\n\nToday we announced Early Access to the first endpoints of the new Twitter API!\n\n#TwitterAPI #EarlyAccess #VersionBump https://t.co/g7v3aeIbtQ" + "data": { + "blocking": true } - ] - } - ``` + } + ``` + + + +--- + +## Unblock a user + + + + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/blocking/9876543210" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Unblock a user +response = client.users.unblock( + source_user_id="123456789", + target_user_id="9876543210" +) +print(f"Blocking: {response.data.blocking}") +``` -#### Step six: Paginate through your results +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -You may notice that there is a meta object located at the bottom of the response. If you received a next_token, this signals that there is another page of results that we can retrieve. To pull the next page of results, you will pull the value of the next_token field and add it to the request as the value to an additional pagination_token parameter. -  +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -| | | -| :--- | :--- | -| **Key** | **Value** | -| pagination_token | 1D3PU6DRII9HEZZZ | +const client = new Client({ oauth1 }); + +// Unblock a user +const response = await client.users.unblock("123456789", "9876543210"); +console.log(`Blocking: ${response.data?.blocking}`); +``` + + + + + + + ```json + { + "data": { + "blocking": false + } + } + ``` + + + +--- -If you send the request after adding this additional parameter, the next five results will be delivered with the subsequent payload since we specified max_results as 5 in step three. You can continue to repeat this process until all results have been returned, but you can also use the max_results parameter to request up to 1000 users per request, so you don’t have to paginate through results quite as much. +## Next steps + + + + Mute users instead of blocking + + + Manage follows + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/users/follows/introduction.mdx b/x-api/users/follows/introduction.mdx index ac7af1d2d..28bae61fe 100644 --- a/x-api/users/follows/introduction.mdx +++ b/x-api/users/follows/introduction.mdx @@ -1,50 +1,88 @@ --- -title: Introduction +title: Follows sidebarTitle: Introduction -keywords: ["follows", "followers", "following", "follow relationships", "network analysis", "user connections", "follow lookup", "manage follows"] +description: Manage follows and retrieve follower/following information +keywords: ["follows", "followers", "following", "follow API", "unfollow", "follower lookup"] --- import { Button } from '/snippets/button.mdx'; -We offer two sets of endpoint groups to help you lookup, create, and delete follow relationships: follows lookup and manage follows. -  +The Follows endpoints let you follow and unfollow users, and retrieve follower and following lists for any user. -### Follows lookup +## Overview + + + + Follow a user on behalf of the authenticated user + + + Unfollow a user + + + Get a user's followers + + + Get who a user follows + + + +--- -Only available to those with [Enterprise access](/x-api/getting-started/about-x-api) +## Endpoints -The follows lookup endpoints enable you to explore and analyze relationships between users, which is sometimes called network analysis. Specifically, there are two REST endpoints that return [user objects](/x-api/fundamentals/data-dictionary#user) representing who a specified user is following, or who is following a specified user. +### Follows lookup -You can authenticate this endpoint with either [OAuth 1.0a User Context](/resources/fundamentals/authentication), [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). You can request up to 1,000 users per request, and pagination tokens will be provided for paging through large sets of results. -  +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/:id/followers`](/x-api/users/get-followers) | Get a user's followers | +| GET | [`/2/users/:id/following`](/x-api/users/get-following) | Get who a user follows | ### Manage follows -The manage follows endpoints enable you to follow or unfollow users. +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| POST | [`/2/users/:id/following`](/x-api/users/follow-user) | Follow a user | +| DELETE | [`/2/users/:source_user_id/following/:target_user_id`](/x-api/users/unfollow-user) | Unfollow a user | + +--- -Since you are making requests on behalf of a user, you must authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and utilize the user Access Tokens associated with the user you are making the request on behalf of. You can generate this user Access Token using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (OAuth 1.0a) or using the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)) (OAuth 2.0). +## Example: Get followers -You are limited to 400 follow actions per day on behalf of each authenticated user, and will be limited to 1,000 actions per day per App across all of your authenticated users. For example, if you have five authenticated users, you can follow 400 users per day (per user limit) with two of those users for a total of 800 actions, and will have to split the remaining 200 actions (per app) amongst the remaining three users. This limit does not apply to the unfollow endpoint, which has a separate limit of 500 actions per day (per app). - -**Account setup** +```bash +curl "https://api.x.com/2/users/2244994945/followers?\ +user.fields=username,verified,public_metrics" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -To access these endpoints, you will need: +## Example: Follow a user -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +```bash +curl -X POST "https://api.x.com/2/users/123456789/following" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "2244994945"}' +``` + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). -
- - - - -
\ No newline at end of file + + + + Get started with follows + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/users/follows/migrate/overview.mdx b/x-api/users/follows/migrate/overview.mdx index 7dc655c76..d53a8e53c 100644 --- a/x-api/users/follows/migrate/overview.mdx +++ b/x-api/users/follows/migrate/overview.mdx @@ -29,7 +29,7 @@ The following tables compare the various types of follows lookup endpoints: | Supports the Post [annotations](/x-api/fundamentals/post-annotations) fields | | ✔ | | Supports requesting new [metrics](/x-api/fundamentals/metrics) fields | | ✔ | | Supports the [conversation_id](/x-api/fundamentals/conversation-id) field | | ✔ | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/developer-apps) | | ✔ | #### Manage follows @@ -48,7 +48,7 @@ The following tables compare the standard v1.1 and X API v2 create follow endpoi | Default request [rate limits](/resources/fundamentals/rate-limits) | 50 requests per 15 min | 50 requests per 15 min | | Maximum daily operations per users | 400 | 400 | | Maximum daily operations per app | 1000 | 1000 | -| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | #### Unfollow a user @@ -62,7 +62,7 @@ The following tables compare the standard v1.1 and X API v2 delete follow endpoi | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE | | Default request [rate limits](/resources/fundamentals/rate-limits) | 15 requests per 15 min (per user) | 50 requests per 15 min (per user) | | Maximum daily operations per app | None | 500 | -| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | **Other migration resources** diff --git a/x-api/users/follows/migrate/standard-to-twitter-api-v2.mdx b/x-api/users/follows/migrate/standard-to-twitter-api-v2.mdx index faa3910c3..e0bb58be5 100644 --- a/x-api/users/follows/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/users/follows/migrate/standard-to-twitter-api-v2.mdx @@ -18,13 +18,13 @@ If you have been working with the standard v1.1 [POST friendships/create](https: * HTTP methods * Request parameters -   + #### Similarities **OAuth 1.0a User Context authentication method** -Both the endpoint versions support [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2). Therefore, if you were previously using one of the standard v1.1 manage follows endpoints, you can continue using the same authentication method if you migrate to the X API v2 version.  +Both the endpoint versions support [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2). Therefore, if you were previously using one of the standard v1.1 manage follows endpoints, you can continue using the same authentication method if you migrate to the X API v2 version. #### Differences @@ -39,11 +39,11 @@ Both the endpoint versions support [OAuth 1.0a User Context](/resources/fundamen * POST https://api.x.com/2/users/:id/following (follow a user) * DELETE https://api.x.com/2/users/:source\_user\_id/following/:target\_user\_id - (unfollow a user)  + (unfollow a user) **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Request parameters** @@ -58,5 +58,140 @@ The following standard v1.1 request parameters have equivalents in X API v2: Please note that the Standard v1.1 parameters are passed as query parameters, whereas the X API v2 parameters are passed as body parameters (for the POST endpoint) or path parameters (for the DELETE endpoint). -Also, the v2 id and source\_user\_id are not required when using the standard v1.1 endpoints since the Access Tokens passed with OAuth 1.0a User Context inferred which user was initiating the follow/unfollow.  +Also, the v2 id and source\_user\_id are not required when using the standard v1.1 endpoints since the Access Tokens passed with OAuth 1.0a User Context inferred which user was initiating the follow/unfollow. +--- + +## Code examples + +### Follow a user (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/following" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "2244994945"}' +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/following" +response = requests.post(url, auth=auth, json={"target_user_id": "2244994945"}) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Follow a user +response = client.users.follow( + source_user_id="123456789", + target_user_id="2244994945" +) +print(f"Following: {response.data.following}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Follow a user +const response = await client.users.follow("123456789", { + targetUserId: "2244994945", +}); +console.log(`Following: ${response.data?.following}`); +``` + + + +### Unfollow a user (v2) + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/following/2244994945" \ + -H "Authorization: OAuth ..." +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/following/2244994945" +response = requests.delete(url, auth=auth) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Unfollow a user +response = client.users.unfollow( + source_user_id="123456789", + target_user_id="2244994945" +) +print(f"Following: {response.data.following}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Unfollow a user +const response = await client.users.unfollow("123456789", "2244994945"); +console.log(`Following: ${response.data?.following}`); +``` + + diff --git a/x-api/users/follows/quickstart.mdx b/x-api/users/follows/quickstart.mdx index 280546991..447abe24b 100644 --- a/x-api/users/follows/quickstart.mdx +++ b/x-api/users/follows/quickstart.mdx @@ -1,85 +1,308 @@ --- title: Quickstart sidebarTitle: Quickstart -keywords: ["follows quickstart", "follow quickstart", "manage follows quickstart", "follow tutorial", "follow guide", "follow relationships quickstart"] +description: Get followers and following lists, and manage follows +keywords: ["follows quickstart", "followers quickstart", "following tutorial", "follows guide"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the manage follows endpoints +This guide walks you through retrieving followers and following lists, and managing follows. -This quick start guide will help you make your first request to the manage follows endpoints using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token (for lookups) +- User Access Token (for managing follows) -### Steps to build a manage follows request -#### Step one: Start with a tool or library +--- -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +## Get a user's followers -To load the X API v2 Postman collection into your environment, please click on the following button: +Retrieve the list of users following a specific user: - -Once you have the X API v2 collection loaded in Postman, navigate to the “Follows” folder, and select “Follow a user ID”. -  + -#### Step two: Authenticate your request +```bash cURL +curl "https://api.x.com/2/users/2244994945/followers?\ +user.fields=username,verified,public_metrics&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +```python Python SDK +from xdk import Client -In this example, we are going to use OAuth 1.0a User Context. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -You must add your keys and tokens – specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret – to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +# Get a user's followers with pagination +for page in client.users.get_followers( + "2244994945", + user_fields=["username", "verified", "public_metrics"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Followers: {user.public_metrics.followers_count}") +``` -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -#### Step three: Specify who is going to follow whom +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Manage follows endpoints take two IDs: one for the source user (the user who wishes to follow or unfollow another user) and the target user (the user that will be followed or unfollowed). The source user’s ID must correspond to the user ID of the authenticating user. In this case, you can specify the ID belonging to your own user. You can find your ID in two ways: +// Get a user's followers with pagination +const paginator = client.users.getFollowers("2244994945", { + userFields: ["username", "verified", "public_metrics"], + maxResults: 100, +}); -1. Using the [user lookup by username](/x-api/users/user-lookup-by-id) endpoint, you can pass a username and receive the id field.  -2. Looking at your Access Token, you will find that the numeric part is your user ID. -   +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Followers: ${user.public_metrics?.followers_count}`); + }); +} +``` -The target ID can be any valid user ID. For example the user ID for @XDevelopers is 2244994945. + -In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the id path variable. Navigate to the “Body” tab and and 2244994945 (the user ID for @XDevelopers) as the value for the target\_user\_id parameter. Making sure to not include any spaces before or after any ID. +### Response -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | (your user ID) | -| target\_user\_id | 2244994945 | +```json +{ + "data": [ + { + "id": "1234567890", + "name": "Developer", + "username": "dev_user", + "verified": false, + "public_metrics": { + "followers_count": 500, + "following_count": 200, + "tweet_count": 1500 + } + } + ], + "meta": { + "result_count": 1, + "next_token": "abc123" + } +} +``` +--- -If you click the "Send" button, you will receive a response object containing the status of the relationship: +## Get who a user follows -* If you receive a "following": true, then the id is successfully following the target\_user\_id. -* If you receive a "pending": true, then the target\_user\_id is protected and must accept your follow request. +Retrieve the list of users that a specific user follows: -#### -Step four: Make your request and review your response + -Once you have everything set up, hit the "Send" button and you will receive the following response: -```{ - "data": { - "following": true, - "pending_follow": false - } +```bash cURL +curl "https://api.x.com/2/users/2244994945/following?\ +user.fields=username,verified&\ +max_results=100" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get users that a user follows +for page in client.users.get_following( + "2244994945", + user_fields=["username", "verified"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get users that a user follows +const paginator = client.users.getFollowing("2244994945", { + userFields: ["username", "verified"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); + }); } ``` + + +--- + +## Follow a user + +Follow a user on behalf of the authenticated user: + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/following" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "2244994945"}' +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Follow a user +response = client.users.follow( + source_user_id="123456789", + target_user_id="2244994945" +) +print(f"Following: {response.data.following}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Follow a user +const response = await client.users.follow("123456789", { + targetUserId: "2244994945", +}); +console.log(`Following: ${response.data?.following}`); +``` + + + +### Response + +```json +{ + "data": { + "following": true, + "pending_follow": false + } +} +``` + + +If the target account is protected, `pending_follow` will be `true` until the follow request is approved. + + +--- + +## Unfollow a user + +Unfollow a user on behalf of the authenticated user: + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/following/2244994945" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Unfollow a user +response = client.users.unfollow( + source_user_id="123456789", + target_user_id="2244994945" +) +print(f"Following: {response.data.following}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Unfollow a user +const response = await client.users.unfollow("123456789", "2244994945"); +console.log(`Following: ${response.data?.following}`); +``` + + + +### Response + +```json +{ + "data": { + "following": false + } +} +``` + +--- + +## Common parameters + +| Parameter | Description | +|:----------|:------------| +| `max_results` | Results per page (1-1000, default 100) | +| `pagination_token` | Token for next page | +| `user.fields` | Additional user fields | +| `expansions` | Related objects to include | + +--- + +## Next steps -Similarly, if you were trying to unfollow a user, you would use the "Unfollow a user ID" request within the same Postman collection. However, both the source\_user\_id and target\_user\_id parameters should be passed as path variables using the unfollow endpoint.  + + + Look up user profiles + + + Block and unblock users + + + Mute and unmute users + + + Full endpoint documentation + + diff --git a/x-api/users/lookup/integrate.mdx b/x-api/users/lookup/integrate.mdx index 6eb7e303f..25427fd3d 100644 --- a/x-api/users/lookup/integrate.mdx +++ b/x-api/users/lookup/integrate.mdx @@ -1,96 +1,369 @@ --- -title: Integration guide -sidebarTitle: Integration guide -keywords: ["user lookup integration", "users integration guide", "user lookup setup", "users integration", "user lookup implementation"] +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for integrating User lookup +keywords: ["user lookup integration", "users integration guide", "user lookup implementation"] --- import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and key concepts that you should be aware of as you integrate the users lookup endpoints into your system. We’ve broken the page into a couple of different sections: +This guide covers the key concepts you need to integrate the User lookup endpoints into your application. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) -* [Edge cases](#edge) +--- -### Helpful tools +## Authentication -Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: +All X API v2 endpoints require authentication. Choose the method that fits your use case: -#### Postman +| Method | Best for | Can access private metrics? | +|:-------|:---------|:---------------------------| +| [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Server-to-server, public data | No | +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | User-facing apps | Yes (for authorized user's data) | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy integrations | Yes (for authorized user's data) | -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +### App-Only authentication -#### Code samples +For public user data, use a Bearer Token: -Interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). + -#### Third-party libraries +```bash cURL +curl "https://api.x.com/2/users/by/username/XDevelopers" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +```python Python SDK +from xdk import Client -### Key concepts +client = Client(bearer_token="YOUR_BEARER_TOKEN") -#### Authentication +# Get user by username +response = client.users.get_by_username("XDevelopers") +print(response.data) +``` -All X API v2 endpoints require requests to be [authenticated](/resources/fundamentals/authentication) with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, App only, or OAuth 2.0 Authorization Code with PKCE to authenticate requests to these endpoints.  +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -[OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2) requires you to utilize your API Keys, user Access Tokens, and a handful of other parameters to [create an authorization header](/resources/fundamentals/authentication#authorizing-a-request), which you will then pass with your request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](https://developer-staging.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens).  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. If you would like to request a Post or private metrics from these endpoints, you will need to use a either OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE, which will ensure that you have the proper permissions from the user that owns that content. -  +const response = await client.users.getByUsername("XDevelopers"); +console.log(response.data); +``` -[App only](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) just requires that you pass an [App only Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) with your request. You can either generate an App only Access Token from directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. + -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +### User Context authentication -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +Required for the authenticated user endpoint (`/2/users/me`): -**Please note** + -If you are requesting the following fields, OAuth 1.0a User Context or OAuth 2.0 Authorization Code is required:  +```bash cURL +curl "https://api.x.com/2/users/me" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -* tweet.fields.non\_public\_metrics -* tweet.fields.promoted_metrics -* tweet.fields.organic_metrics +```python Python SDK +from xdk import Client -#### Developer portal, Projects, and developer Apps +# Using OAuth 2.0 user access token +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App.  -  +# Get authenticated user's profile +response = client.users.get_me() +print(response.data) +``` -#### Rate limits +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user.  +// Using OAuth 2.0 user access token +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -The user lookup endpoints are rate limited at both the app-level and the user-level. However, the authenticated user lookup endpoint is rate limited at the user-level +const response = await client.users.getMe(); +console.log(response.data); +``` -The app-level rate limit means that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by the keys and tokens that you are using. The user-level rate limit means that the authenticated user that you are making the request on behalf of can only perform a certain number of times across any developer App. + -The chart below shows the rate limits for each endpoint. + +The `/2/users/me` endpoint only works with User Context authentication. App-Only tokens will return an error. + -| | | | -| :--- | :--- | :--- | -| **Endpoint** | **HTTP method** | **Rate limit / Level** | -| /2/users | GET | 900 requests per 15 minutes / App and User | -| /2/users/:id | GET | 900 requests per 15 minutes / App and User | -| /2/users/by | GET | 900 requests per 15 minutes / App and User | -| /2/users/by/username/:username | GET | 900 requests per 15 minutes / App and User | -| /2/users/me | GET | 75 requests per 15 minutes / User | +--- -#### Fields and expansions +## Fields and expansions + +The X API v2 returns minimal data by default. Use `fields` and `expansions` to request exactly what you need. + +### Default response + +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers" + } +} +``` + +### Available fields + + +| Field | Description | +|:------|:------------| +| `created_at` | Account creation timestamp | +| `description` | User bio | +| `entities` | Parsed URLs in bio | +| `location` | User-defined location | +| `pinned_tweet_id` | Pinned Post ID | +| `profile_image_url` | Avatar URL | +| `protected` | Whether account is protected | +| `public_metrics` | Follower/following counts | +| `url` | Website URL | +| `verified` | Verification status | +| `withheld` | Withholding information | + + + +| Field | Description | +|:------|:------------| +| `created_at` | Post creation timestamp | +| `text` | Post content | +| `public_metrics` | Engagement counts | +| `entities` | Hashtags, mentions, URLs | + + +### Example with fields + + + +```bash cURL +curl "https://api.x.com/2/users/by/username/XDevelopers?\ +user.fields=created_at,description,public_metrics,verified&\ +expansions=pinned_tweet_id&\ +tweet.fields=created_at,public_metrics" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get user with additional fields and expansions +response = client.users.get_by_username( + "XDevelopers", + user_fields=["created_at", "description", "public_metrics", "verified"], + expansions=["pinned_tweet_id"], + tweet_fields=["created_at", "public_metrics"] +) + +print(response.data) +print(response.includes) # Contains expanded pinned tweet +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +const response = await client.users.getByUsername("XDevelopers", { + userFields: ["created_at", "description", "public_metrics", "verified"], + expansions: ["pinned_tweet_id"], + tweetFields: ["created_at", "public_metrics"], +}); + +console.log(response.data); +console.log(response.includes); // Contains expanded pinned tweet +``` + + + +### Response with expansions + +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers", + "created_at": "2013-12-14T04:35:55.000Z", + "verified": true, + "pinned_tweet_id": "1234567890", + "public_metrics": { + "followers_count": 583423, + "following_count": 2048, + "tweet_count": 14052 + } + }, + "includes": { + "tweets": [ + { + "id": "1234567890", + "text": "Welcome to the X Developer Platform!", + "created_at": "2024-01-15T10:00:00.000Z" + } + ] + } +} +``` + + + Learn more about customizing responses + -The X API v2 allows users to select exactly which data they want to return from the API using a set of tools called fields and expansions. The expansion parameter allows you to expand objects referenced in the payload. For example, this endpoint allows you to use the pinned\_tweet\_id expansion. +--- -The fields parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. These endpoints delivers user objects primarily. By default, the user object returns the id, name, and username fields. To receive additional fields such as user.created_at or user.location, you will have to specifically request those using a fields parameter. Some important fields that you may want to consider using in your integration are our Post poll data, metrics, annotations, and conversation ID fields. +## Batch lookups + +Look up multiple users in a single request: + + + +```bash cURL (by IDs) +curl "https://api.x.com/2/users?ids=2244994945,783214,6253282&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```bash cURL (by usernames) +curl "https://api.x.com/2/users/by?usernames=XDevelopers,X,XAPI&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get multiple users by IDs +response = client.users.get_users( + ids=["2244994945", "783214", "6253282"], + user_fields=["username", "verified"] +) +for user in response.data: + print(f"{user.username}: {user.verified}") + +# Get multiple users by usernames +response = client.users.get_users_by_usernames( + usernames=["XDevelopers", "X", "XAPI"], + user_fields=["username", "verified"] +) +for user in response.data: + print(f"{user.username}: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get multiple users by IDs +const byIds = await client.users.getUsers({ + ids: ["2244994945", "783214", "6253282"], + userFields: ["username", "verified"], +}); +byIds.data.forEach((user) => { + console.log(`${user.username}: ${user.verified}`); +}); + +// Get multiple users by usernames +const byUsernames = await client.users.getUsersByUsernames({ + usernames: ["XDevelopers", "X", "XAPI"], + userFields: ["username", "verified"], +}); +byUsernames.data.forEach((user) => { + console.log(`${user.username}: ${user.verified}`); +}); +``` + + + + +Batch requests are limited to 100 users. Use multiple requests for larger datasets. + -We’ve added a guide on how to [use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) in our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). +--- +## Error handling + +### Common errors + +| Status | Error | Solution | +|:-------|:------|:---------| +| 400 | Invalid request | Check parameter formatting | +| 401 | Unauthorized | Verify authentication credentials | +| 403 | Forbidden | Check App permissions | +| 404 | Not Found | User doesn't exist or was suspended | +| 429 | Too Many Requests | Wait and retry (see rate limits) | + +### Suspended or deleted users + +If a user is suspended or deleted: +- Single user lookup returns `404` +- Multi-user lookup omits the user from results with an `errors` array + +```json +{ + "data": [ + { "id": "2244994945", "username": "XDevelopers" } + ], + "errors": [ + { + "resource_id": "1234567890", + "resource_type": "user", + "title": "Not Found Error", + "detail": "Could not find user with id: [1234567890]." + } + ] +} +``` + +### Protected users + +For protected accounts you don't follow: +- Basic info (id, name, username) is available +- Protected content (pinned Post) may be restricted +- `protected: true` indicates the account status -#### Edge cases +--- + +## Best practices + + + + Use multi-user endpoints to fetch up to 100 users at once, reducing API calls. + + + Specify only the fields you need to minimize response size. + + + Cache user profiles locally to reduce repeated requests. + + + Check for partial errors in batch responses. + + -* Post text is truncated for Retweets. The short term workaround is to expand the referenced Post and retrieve the full text from the expansion. This is a bug that we will fix in the future. +--- +## Next steps + + + + Complete endpoint documentation + + + All available objects and fields + + + Working code examples + + + Handle errors gracefully + + diff --git a/x-api/users/lookup/introduction.mdx b/x-api/users/lookup/introduction.mdx index e65787990..8a20805fe 100644 --- a/x-api/users/lookup/introduction.mdx +++ b/x-api/users/lookup/introduction.mdx @@ -1,43 +1,97 @@ --- -title: Introduction +title: User Lookup sidebarTitle: Introduction +description: Retrieve user profiles by ID, username, or for the authenticated user keywords: ["user lookup", "get user", "user by ID", "user by username", "user profile", "user information", "authenticated user", "user details"] --- import { Button } from '/snippets/button.mdx'; -You can authenticate your request to all users lookup endpoints other than the authenticated user lookup with [OAuth 1.0a User Context](/resources/fundamentals/authentication), [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), or [OAuth 2.0 Authorization code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). However, if you would like to access private metrics or data from private users, you will need to utilize OAuth 1.0a User Context or OAuth 2.0 Authorization Code with PKCE, and pass the authenticated users' Access Token with your request.     +The User lookup endpoints let you retrieve profile information for one or more users. Look up users by their ID, username, or get details for the currently authenticated user. -This endpoint is commonly used to receive up-to-date details on a user, to verify that a user exists, or to update your stored details following a compliance event. +## Overview -### Authenticated user lookup + + + Look up users by their unique user ID + + + Look up users by their @handle + + + Retrieve up to 100 users per request + + + Get details for the current user + + -If authenticating on behalf of other users, it is critical to be able to see who you can make a request on behalf of. +--- -This endpoint requires you to use [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). It returns information about the authorized user, meaning the user that is associated with the user Access Tokens that you pass with the request. +## Endpoints -You can access this endpoint via the GET method. There is a user rate limit of 75 requests per 15 minutes for this endpoint. - -**Account setup** +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/:id`](/x-api/users/get-user-by-id) | Get user by ID | +| GET | [`/2/users`](/x-api/users/get-users-by-ids) | Get users by IDs (up to 100) | +| GET | [`/2/users/by/username/:username`](/x-api/users/get-user-by-username) | Get user by username | +| GET | [`/2/users/by`](/x-api/users/get-users-by-usernames) | Get users by usernames (up to 100) | +| GET | [`/2/users/me`](/x-api/users/get-my-user) | Get authenticated user | + +--- + +## Example request + +```bash +curl "https://api.x.com/2/users/by/username/XDevelopers?\ +user.fields=created_at,description,public_metrics,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -To access these endpoints, you will need: +## Example response -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers", + "created_at": "2013-12-14T04:35:55.000Z", + "description": "The voice of the X developer community", + "verified": true, + "public_metrics": { + "followers_count": 583423, + "following_count": 2048, + "tweet_count": 14052, + "listed_count": 1672 + } + } +} +``` -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) -
- - - - -
\ No newline at end of file + + + + Look up users by ID or username + + + Get the current user's profile + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/users/lookup/migrate/overview.mdx b/x-api/users/lookup/migrate/overview.mdx index 43c4799a1..7a5bcb4e1 100644 --- a/x-api/users/lookup/migrate/overview.mdx +++ b/x-api/users/lookup/migrate/overview.mdx @@ -28,7 +28,7 @@ The following tables compare the various types of users lookup endpoints: | Supports the [annotations](/x-api/fundamentals/post-annotations) fields (on pinned Post) | | ✔ | | Supports requesting new [metrics](/x-api/fundamentals/metrics) fields (on pinned Post) | | ✔ | | Supports the [conversation_id](/x-api/fundamentals/conversation-id) field (on pinned Post) | | ✔ | -| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/projects) | | ✔ | +| Requires the use of credentials from a [developer App](/resources/fundamentals/developer-apps) associated with a [project](/resources/fundamentals/developer-apps) | | ✔ | **Other migration resources** diff --git a/x-api/users/lookup/migrate/standard-to-twitter-api-v2.mdx b/x-api/users/lookup/migrate/standard-to-twitter-api-v2.mdx index 5deed8099..cb42ebddb 100644 --- a/x-api/users/lookup/migrate/standard-to-twitter-api-v2.mdx +++ b/x-api/users/lookup/migrate/standard-to-twitter-api-v2.mdx @@ -23,14 +23,14 @@ If you have been working with the standard v1.1 GET users/show and GET users/loo **OAuth 1.0a User Context authentication method** -The standard endpoint supports [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2), while the new X API v2 users lookup endpoints supports both OAuth 1.0a User Context and [App only](/resources/fundamentals/authentication#oauth-2-0). Therefore, if you were previously using one of the standard v1.1 users lookup endpoints, you can continue using the same authentication method if you migrate to the X API v2 version.  +The standard endpoint supports [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2), while the new X API v2 users lookup endpoints supports both OAuth 1.0a User Context and [App only](/resources/fundamentals/authentication#oauth-2-0). Therefore, if you were previously using one of the standard v1.1 users lookup endpoints, you can continue using the same authentication method if you migrate to the X API v2 version. -Depending on your authentication library/package of choice, App only authentication is probably the easiest way to get started and can be set with a simple request header. To learn how to generate an App only Access Token, see [this App only guide](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only).  +Depending on your authentication library/package of choice, App only authentication is probably the easiest way to get started and can be set with a simple request header. To learn how to generate an App only Access Token, see [this App only guide](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). **Users per request limits** -The standard v1.1 GET users/lookup endpoint allows you to specify 100 users per request. This also goes for the GET /users and GET /users/by endpoints. To specify a full 100 users, you will need to pass the ids (GET /users) parameter or the username (GET /users/by) parameter as a query parameter, and include the list of user IDs/usernames in a comma-separated list.  -  +The standard v1.1 GET users/lookup endpoint allows you to specify 100 users per request. This also goes for the GET /users and GET /users/by endpoints. To specify a full 100 users, you will need to pass the ids (GET /users) parameter or the username (GET /users/by) parameter as a query parameter, and include the list of user IDs/usernames in a comma-separated list. + #### Differences @@ -47,35 +47,35 @@ The standard v1.1 GET users/lookup endpoint allows you to specify 100 users per **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project.  +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated to a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Response data format** One of the biggest differences between standard v1.1 and X API v2 endpoint versions is how you select which fields return in your payload. -For the standard endpoints, you receive many of the response fields by default, and then have the option to use parameters to identify which fields or sets of fields should return in the payload. +For the standard endpoints, you receive many of the response fields by default, and then have the option to use parameters to identify which fields or sets of fields should return in the payload. -The X API v2 version only delivers the user id , name, and username fields by default. To request any additional fields or objects, you wil need to use the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. Any user fields that you request from this endpoint will return in the primary user object. Any expanded Post object and fields will return in an includes object within your response. You can then match any expanded objects back to the user object by matching the IDs located in both the user and the expanded Post object.  +The X API v2 version only delivers the user id , name, and username fields by default. To request any additional fields or objects, you wil need to use the [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters. Any user fields that you request from this endpoint will return in the primary user object. Any expanded Post object and fields will return in an includes object within your response. You can then match any expanded objects back to the user object by matching the IDs located in both the user and the expanded Post object. -We encourage you to read more about these new parameters in their respective guides, or by reading our guide on [how to use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions).  +We encourage you to read more about these new parameters in their respective guides, or by reading our guide on [how to use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions). -We have also put together a [data format migration guide](/x-api/migrate/data-format-migration#migrating-from-standard-v1-1s-data-format-to-v2) which can help you map standard v1.1 fields to the newer v2 fields. This guide will also provide you the specific expansion and field parameter that you will need to pass with your v2 request to return specific fields.  -  +We have also put together a [data format migration guide](/x-api/migrate/data-format-migration#migrating-from-standard-v1-1s-data-format-to-v2) which can help you map standard v1.1 fields to the newer v2 fields. This guide will also provide you the specific expansion and field parameter that you will need to pass with your v2 request to return specific fields. + -In addition to the changes in how you request certain fields, X API v2 is also introducing new JSON designs for the objects returned by the APIs, including [Post](/x-api/fundamentals/data-dictionary#tweet) and [user](/x-api/fundamentals/data-dictionary#user) objects. +In addition to the changes in how you request certain fields, X API v2 is also introducing new JSON designs for the objects returned by the APIs, including [Post](/x-api/fundamentals/data-dictionary#tweet) and [user](/x-api/fundamentals/data-dictionary#user) objects. -* At the JSON root level, the standard endpoints return Post objects in a statuses array, while X API v2 returns a data array.  -* Instead of referring to Retweeted and Quoted "statuses", X API v2 JSON refers to Retweeted and Quoted Tweets. Many legacy and deprecated fields, such as contributors and user.translator_type are being removed.  -* Instead of using both favorites (in Post object) and favourites (in user object), X API v2 uses the term like.  -* X is adopting the convention that JSON values with no value (for example, null) are not written to the payload. Post and user attributes are only included if they have a non-null values.  -   +* At the JSON root level, the standard endpoints return Post objects in a statuses array, while X API v2 returns a data array. +* Instead of referring to Retweeted and Quoted "statuses", X API v2 JSON refers to Retweeted and Quoted Tweets. Many legacy and deprecated fields, such as contributors and user.translator_type are being removed. +* Instead of using both favorites (in Post object) and favourites (in user object), X API v2 uses the term like. +* X is adopting the convention that JSON values with no value (for example, null) are not written to the payload. Post and user attributes are only included if they have a non-null values. + -We also introduced a new set of fields to the [Post object](/x-api/fundamentals/data-dictionary#tweet) including the following: +We also introduced a new set of fields to the [Post object](/x-api/fundamentals/data-dictionary#tweet) including the following: -* A [conversation_id](/x-api/fundamentals/conversation-id) field -* Two new [annotations](/x-api/fundamentals/post-annotations) fields, including context and entities -* Several new [metrics](/x-api/fundamentals/metrics) fields  +* A [conversation_id](/x-api/fundamentals/conversation-id) field +* Two new [annotations](/x-api/fundamentals/post-annotations) fields, including context and entities +* Several new [metrics](/x-api/fundamentals/metrics) fields * A new reply_setting field, which shows you who can reply to a given Post **Request parameters** @@ -88,8 +88,142 @@ The following standard v1.1 request parameters have equivalents in X API v2: | user_id | ids | | screen_name | username | -There are also a set of standard users lookup request parameters **not** supported in X API v2: +There are also a set of standard users lookup request parameters **not** supported in X API v2: | Standard | Comment | | :--- | :--- | -| include_entities | This parameter is used to remove the entities node from the Post payload.  It has been replaced with the additive fields and expansions functionality. | +| include_entities | This parameter is used to remove the entities node from the Post payload. It has been replaced with the additive fields and expansions functionality. | + +--- + +### Code Examples + +The following examples show standard v1.1 endpoints and their v2 equivalents. + +**Single user lookup: v1.1 `GET users/show` → v2 `GET /users/by/username/:username`** + + + +```bash cURL (v1.1) +curl --request GET \ + --url 'https://api.x.com/1.1/users/show.json?screen_name=XDevelopers' \ + --header 'Authorization: Bearer $ACCESS_TOKEN' +``` + +```bash cURL (v2) +curl --request GET \ + --url 'https://api.x.com/2/users/by/username/XDevelopers?user.fields=created_at,description,public_metrics' \ + --header 'Authorization: Bearer $ACCESS_TOKEN' +``` + +```python Python (v2) +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/users/by/username/XDevelopers" + +params = { + "user.fields": "created_at,description,public_metrics" +} + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers, params=params) + +print(response.json()) +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get user by username with additional fields +response = client.users.get_by_username( + "XDevelopers", + user_fields=["created_at", "description", "public_metrics"] +) + +print(f"Name: {response.data.name}") +print(f"Followers: {response.data.public_metrics.followers_count}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +const response = await client.users.getByUsername("XDevelopers", { + userFields: ["created_at", "description", "public_metrics"], +}); + +console.log(`Name: ${response.data?.name}`); +console.log(`Followers: ${response.data?.public_metrics?.followers_count}`); +``` + + + +**Multiple users lookup: v1.1 `GET users/lookup` → v2 `GET /users/by`** + + + +```bash cURL (v1.1) +curl --request GET \ + --url 'https://api.x.com/1.1/users/lookup.json?screen_name=XDevelopers,X,XAPI' \ + --header 'Authorization: Bearer $ACCESS_TOKEN' +``` + +```bash cURL (v2) +curl --request GET \ + --url 'https://api.x.com/2/users/by?usernames=XDevelopers,X,XAPI&user.fields=created_at,public_metrics' \ + --header 'Authorization: Bearer $ACCESS_TOKEN' +``` + +```python Python (v2) +import requests + +bearer_token = "YOUR_BEARER_TOKEN" +url = "https://api.x.com/2/users/by" + +params = { + "usernames": "XDevelopers,X,XAPI", + "user.fields": "created_at,public_metrics" +} + +headers = {"Authorization": f"Bearer {bearer_token}"} +response = requests.get(url, headers=headers, params=params) + +for user in response.json()["data"]: + print(f"{user['username']}: {user['public_metrics']['followers_count']} followers") +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get multiple users by usernames +response = client.users.get_users_by_usernames( + usernames=["XDevelopers", "X", "XAPI"], + user_fields=["created_at", "public_metrics"] +) + +for user in response.data: + print(f"{user.username}: {user.public_metrics.followers_count} followers") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +const response = await client.users.getUsersByUsernames({ + usernames: ["XDevelopers", "X", "XAPI"], + userFields: ["created_at", "public_metrics"], +}); + +response.data?.forEach((user) => { + console.log(`${user.username}: ${user.public_metrics?.followers_count} followers`); +}); +``` + + diff --git a/x-api/users/lookup/quickstart/authenticated-lookup.mdx b/x-api/users/lookup/quickstart/authenticated-lookup.mdx index 5c5952104..d5897ebb0 100644 --- a/x-api/users/lookup/quickstart/authenticated-lookup.mdx +++ b/x-api/users/lookup/quickstart/authenticated-lookup.mdx @@ -1,75 +1,215 @@ --- -title: Authenticated user lookup -sidebarTitle: Authenticated user lookup -keywords: ["authenticated user lookup quickstart", "get authenticated user quickstart", "me endpoint quickstart", "authenticated user tutorial"] +title: Authenticated User Quickstart +sidebarTitle: Authenticated User +description: Get the currently authenticated user's profile +keywords: ["authenticated user", "me endpoint", "current user", "authenticated lookup"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the Authenticated User Lookup endpoint +This guide walks you through retrieving the currently authenticated user's profile using the `/me` endpoint. -This quick start guide will help you make your first request to the authenticated user lookup endpoint using Postman. - -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to see sample code in different languages. -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build an authenticated user lookup request -#### Step one: Start with a tool or library +--- + +## Get the authenticated user + +Make a request to the `/me` endpoint with a User Access Token: -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. + -To load the X API v2 Postman collection into your environment, please click on the following button: +```bash cURL +curl "https://api.x.com/2/users/me?\ +user.fields=created_at,description,verified,public_metrics,profile_image_url" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` - -Once you have the X API v2 collection loaded in Postman, navigate to the “Authenticated User Lookup” folder, and select “Lookup an Authenticated User”. +```python Python SDK +from xdk import Client -#### Step two: Authenticate your request +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +# Get the authenticated user +response = client.users.get_me( + user_fields=["created_at", "description", "verified", "public_metrics", "profile_image_url"] +) -In this example, we are going to use OAuth 1.0a User Context. +print(f"Username: {response.data.username}") +print(f"ID: {response.data.id}") +print(f"Followers: {response.data.public_metrics.followers_count}") +``` -You must add your keys and tokens – specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret – to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -##### Step three: Determine which user fields you want to retrieve +// Get the authenticated user +const response = await client.users.getMe({ + userFields: ["created_at", "description", "verified", "public_metrics", "profile_image_url"], +}); -If you click the "Send" button after step three, you will receive the default [user object](/x-api/fundamentals/data-dictionary#user) fields in your response: id, name, and username. +console.log(`Username: ${response.data?.username}`); +console.log(`ID: ${response.data?.id}`); +console.log(`Followers: ${response.data?.public_metrics?.followers_count}`); +``` -If you would like to receive additional fields beyond id, name, and username, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. + -For this exercise, we will request three additional sets of fields from different objects: +--- -1. The additional user.created_at field in the primary user objects. +## Response + +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers", + "created_at": "2013-12-14T04:35:55.000Z", + "description": "The voice of the X developer community", + "verified": true, + "profile_image_url": "https://pbs.twimg.com/profile_images/...", + "public_metrics": { + "followers_count": 583423, + "following_count": 2048, + "tweet_count": 14052, + "listed_count": 1672 + } + } +} +``` -2. The associated pinned Posts’ object’s default fields for the returned users: id and text. +--- -3. The additional tweet.created_at field in the associated Post objects. +## Use case +The `/me` endpoint is essential when: -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +- **Verifying authentication** — Confirm the user is properly authenticated +- **Getting the user ID** — Retrieve the authenticated user's ID for other API calls +- **Personalizing experiences** — Display the user's profile in your app +- **On behalf of requests** — Know who you're making requests for -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| user.fields | created_at | user.created_at | -| expansions | pinned\_tweet\_id | includes.tweets.id,
includes.tweets.text | -| tweet.fields | created\_at, author\_id | includes.tweets.created\_at, includes\_tweets.author_id | +--- -You should now see a similar URL next to the “Send” button: +## Include pinned Post + +Request the user's pinned Post: + + + +```bash cURL +curl "https://api.x.com/2/users/me?\ +user.fields=pinned_tweet_id&\ +expansions=pinned_tweet_id&\ +tweet.fields=created_at,text" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get authenticated user with pinned Post +response = client.users.get_me( + user_fields=["pinned_tweet_id"], + expansions=["pinned_tweet_id"], + tweet_fields=["created_at", "text"] +) + +print(f"Username: {response.data.username}") +# Pinned Post is in response.includes.tweets +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +// Get authenticated user with pinned Post +const response = await client.users.getMe({ + userFields: ["pinned_tweet_id"], + expansions: ["pinned_tweet_id"], + tweetFields: ["created_at", "text"], +}); + +console.log(`Username: ${response.data?.username}`); +// Pinned Post is in response.includes?.tweets +``` + + + +### Response with expansion + +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers", + "pinned_tweet_id": "1234567890" + }, + "includes": { + "tweets": [ + { + "id": "1234567890", + "text": "Welcome to my profile!", + "created_at": "2024-01-01T00:00:00.000Z" + } + ] + } +} +``` + +--- + +## Available fields + +| Field | Description | +|:------|:------------| +| `created_at` | Account creation date | +| `description` | User bio | +| `profile_image_url` | Avatar URL | +| `verified` | Verification status | +| `public_metrics` | Follower/following counts | +| `location` | User-defined location | +| `url` | User's website | +| `protected` | Protected account status | +| `pinned_tweet_id` | Pinned Post ID | + +--- + +## Authentication requirement + + +The `/me` endpoint requires User Context authentication. App-Only (Bearer Token) authentication is not supported. + + +Use either: +- [OAuth 1.0a User Context](/resources/fundamentals/authentication) +- [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) + +--- - ``` - https://api.x.com/2/users/me?user.fields=created_at&expansions=pinned_tweet_id&tweet.fields=author_id,created_at - ``` \ No newline at end of file +## Next steps + + + + Look up other users + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/users/lookup/quickstart/user-lookup.mdx b/x-api/users/lookup/quickstart/user-lookup.mdx index af881ce85..aaa52ffe4 100644 --- a/x-api/users/lookup/quickstart/user-lookup.mdx +++ b/x-api/users/lookup/quickstart/user-lookup.mdx @@ -1,130 +1,275 @@ --- -title: User lookup -sidebarTitle: User lookup -keywords: ["user lookup quickstart", "get user quickstart", "user lookup tutorial", "user lookup guide", "user lookup example"] +title: User Lookup Quickstart +sidebarTitle: User Lookup +description: Look up users by ID or username +keywords: ["user lookup quickstart", "get user tutorial", "user by ID guide", "user by username"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the users lookup endpoints +This guide walks you through looking up users by their ID or username. -This quick start guide will help you make your first request to the users lookup endpoints with a set of specified fields using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository.  -### Prerequisites - -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: +**Prerequisites** -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- Your App's Bearer Token -### Steps to build a users lookup request -#### Step one: Start with a tool or library +--- + +## Look up by ID + +### Single user + + + +```bash cURL +curl "https://api.x.com/2/users/2244994945?\ +user.fields=created_at,description,verified,public_metrics" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_BEARER_TOKEN") + +# Get user by ID +response = client.users.get( + "2244994945", + user_fields=["created_at", "description", "verified", "public_metrics"] +) + +print(f"Name: {response.data.name}") +print(f"Username: {response.data.username}") +print(f"Followers: {response.data.public_metrics.followers_count}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get user by ID +const response = await client.users.get("2244994945", { + userFields: ["created_at", "description", "verified", "public_metrics"], +}); + +console.log(`Name: ${response.data?.name}`); +console.log(`Username: ${response.data?.username}`); +console.log(`Followers: ${response.data?.public_metrics?.followers_count}`); +``` + + + +### Response + +```json +{ + "data": { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers", + "created_at": "2013-12-14T04:35:55.000Z", + "description": "The voice of the X developer community", + "verified": true, + "public_metrics": { + "followers_count": 583423, + "following_count": 2048, + "tweet_count": 14052, + "listed_count": 1672 + } + } +} +``` + +### Multiple users + +Look up up to 100 users at once: + + + +```bash cURL +curl "https://api.x.com/2/users?\ +ids=2244994945,783214,6253282&\ +user.fields=username,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` + +```python Python SDK +from xdk import Client -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we are going to use the Postman tool here to simplify the process. +client = Client(bearer_token="YOUR_BEARER_TOKEN") -To load the X API v2 Postman collection into your environment, please click on the following button: +# Get multiple users by ID +response = client.users.get_users( + ids=["2244994945", "783214", "6253282"], + user_fields=["username", "verified"] +) - +for user in response.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); + +// Get multiple users by ID +const response = await client.users.getUsers({ + ids: ["2244994945", "783214", "6253282"], + userFields: ["username", "verified"], +}); -Once you have the X API v2 collection loaded in Postman, navigate to the GET /users/by endpoint. -  +response.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); +}); +``` + + + +--- -#### Step two: Authenticate your request +## Look up by username -To properly make a request to the X API, you need to verify that you have permission. To do so, this endpoint requires you to authenticate your request with either [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token), [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), or [OAuth 1.0a User Context](/resources/fundamentals/authentication) authentication methods. +### Single user -For simplicity's sake, we will utilize App only with this request, but you will need to use one of the other authentication methods if you'd like to request private [metrics](/x-api/fundamentals/metrics) or users.  + -To utilize App only, you must add your keys and tokens, specifically the[App Access Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) (also known as the App only Bearer Token) to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +```bash cURL +curl "https://api.x.com/2/users/by/username/XDevelopers?\ +user.fields=created_at,description,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +```python Python SDK +from xdk import Client -#### Step three: Identify and specify which user(s) you would like to retrieve +client = Client(bearer_token="YOUR_BEARER_TOKEN") -You must specify a user or a set of users that you would like to receive within the request. Depending on which user endpoint you use, you can pass either a user ID or a username. In this situation, we are going to use the [GET /users/by endpoint](/x-api/users/user-lookup-by-usernames) which allows you to pass multiple usernames in a single request (rather than the [single-ID](/x-api/users/user-lookup-by-id), [multi-ID](/x-api/users/user-lookup-by-ids), and [single-username](/x-api/users/user-lookup-by-username) endpoints) and pass a set of usernames using the usernames query parameter. +# Get user by username +response = client.users.get_by_username( + "XDevelopers", + user_fields=["created_at", "description", "verified"] +) -Usernames are simply the account handle that you can find within an account's profile URL. For example, the following account’s username is XDevelopers. +print(f"ID: {response.data.id}") +print(f"Name: {response.data.name}") +``` -`https://x.com/XDevelopers` +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -In Postman, navigate to the "Params" tab and enter this username, or a string of usernames separated by a comma, into the "Value" column of the username parameter, making sure to not include any spaces between usernames and commas.  +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); -| | | -| :--- | :--- | -| **Key** | **Value** | -| `username` | XDeveloeprs,API,adsapi | +// Get user by username +const response = await client.users.getByUsername("XDevelopers", { + userFields: ["created_at", "description", "verified"], +}); -#### Step four: Identify and specify which fields you would like to retrieve +console.log(`ID: ${response.data?.id}`); +console.log(`Name: ${response.data?.name}`); +``` -If you click the "Send" button after step three, you will receive the default [user object](/x-api/fundamentals/data-dictionary#user) fields in your response: id, name, and username. + -If you would like to receive additional fields beyond id, name, and username, you will have to specify those fields in your request with the [field](/x-api/fundamentals/fields) and/or [expansion](/x-api/fundamentals/expansions) parameters. +### Multiple users -For this exercise, we will request a three additional sets of fields from different objects: + -1. The additional user.created_at field in the primary user objects. -2. The associated pinned Posts’ object’s default fields for the returned users: id and text. -3. The additional  tweet.created_at field in the associated Post objects. -   +```bash cURL +curl "https://api.x.com/2/users/by?\ +usernames=XDevelopers,X,elonmusk&\ +user.fields=created_at,verified" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +```python Python SDK +from xdk import Client -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| `user.fields` | `created_at` | `user.created_at` | -| `expansions` | `author_id` | tweet.id, tweet.text | -| `tweet.fields` | `created_at` | `includes.users.created_at` | +client = Client(bearer_token="YOUR_BEARER_TOKEN") -You should now see the following URL next to the "Send" button: +# Get multiple users by username +response = client.users.get_users_by_usernames( + usernames=["XDevelopers", "X", "elonmusk"], + user_fields=["created_at", "verified"] +) - ``` - https://api.x.com/2/users/by?usernames=twitterdev,twitterapi,adsapi&user.fields=created_at&expansions=pinned_tweet_id&tweet.fields=author_id,created_at +for user in response.data: + print(f"{user.username} - {user.created_at}") ``` -#### Step five: Make your request and review your response +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -Once you have everything set up, hit the "Send" button and you will receive the following response: +const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); +// Get multiple users by username +const response = await client.users.getUsersByUsernames({ + usernames: ["XDevelopers", "X", "elonmusk"], + userFields: ["created_at", "verified"], +}); + +response.data?.forEach((user) => { + console.log(`${user.username} - ${user.created_at}`); +}); ``` + + + +--- + +## Available fields + +| Field | Description | +|:------|:------------| +| `created_at` | Account creation date | +| `description` | User bio | +| `profile_image_url` | Avatar URL | +| `verified` | Verification status | +| `public_metrics` | Follower/following counts | +| `location` | User-defined location | +| `url` | User's website | +| `protected` | Protected account status | +| `pinned_tweet_id` | Pinned Post ID | + +--- + +## Handle errors + +### User not found + +```json { - "data": [ - { - "created_at": "2013-12-14T04:35:55.000Z", - "id": "2244994945", - "name": "Twitter Dev", - "pinned_tweet_id": "1255542774432063488", - "username": "TwitterDev" - }, + "errors": [ { - "created_at": "2007-05-23T06:01:13.000Z", - "id": "6253282", - "name": "Twitter API", - "username": "TwitterAPI" - }, - { - "created_at": "2013-02-27T20:01:12.000Z", - "id": "1225933934", - "name": "Twitter Ads API", - "username": "AdsAPI" + "resource_type": "user", + "title": "Not Found Error", + "detail": "Could not find user with username: [nonexistent_user]." } - ], - "includes": { - "tweets": [ - { - "author_id": "2244994945", - "created_at": "2020-04-29T17:01:38.000Z", - "id": "1255542774432063488", - "text": "During these unprecedented times, what’s happening on Twitter can help the world better understand & respond to the pandemic. \n\nWe're launching a free COVID-19 stream endpoint so qualified devs & researchers can study the public conversation in real-time. https://t.co/BPqMcQzhId" - } - ] - } + ] } -``` \ No newline at end of file +``` + +### Protected user + +Protected users' data is still returned, but you won't be able to access their Posts unless you follow them. + +--- + +## Next steps + + + + Get the current user + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/users/mutes/integrate.mdx b/x-api/users/mutes/integrate.mdx index 36a0baa6b..73d0a8caf 100644 --- a/x-api/users/mutes/integrate.mdx +++ b/x-api/users/mutes/integrate.mdx @@ -1,79 +1,237 @@ --- -title: Integration guide -sidebarTitle: Integration guide -keywords: ["mutes integration", "mute users integration", "mutes guide", "mute integration guide", "mutes setup", "mute users setup"] +title: Integration Guide +sidebarTitle: Integration Guide +description: Key concepts and best practices for integrating mutes endpoints +keywords: ["mutes integration", "mute users integration", "mutes guide"] --- import { Button } from '/snippets/button.mdx'; -This page contains information on several tools and key concepts that you should be aware of as you integrate the mutes endpoints into your system. We’ve broken the page into a couple of different sections: +This guide covers the key concepts you need to integrate the mutes endpoints into your application. -* [Helpful tools](#helpful) -* Key Concepts -* [Authentication](#authentication) -* [Developer portal, Projects, and Apps](#portal) -* [Rate limits](#limits) -* [Fields and expansions](#fields) -* [Pagination](#pagination) +--- + +## Authentication + +Mutes endpoints require user authentication to access private mute lists: + +| Method | Description | +|:-------|:------------| +| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended for new applications | +| [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | + + +App-Only authentication is not supported. You must authenticate on behalf of a user. + -### Helpful tools +### Required scopes (OAuth 2.0) -Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: +| Scope | Required for | +|:------|:-------------| +| `mute.read` | Retrieving muted accounts | +| `mute.write` | Muting and unmuting accounts | +| `users.read` | Required with mute scopes | -#### Postman +--- -Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page.  +## Endpoints overview -#### Code samples +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | `/2/users/:id/muting` | Get list of muted accounts | +| POST | `/2/users/:id/muting` | Mute an account | +| DELETE | `/2/users/:source_user_id/muting/:target_user_id` | Unmute an account | -Interested in getting set up with this endpoint with some code in your preferred coding language? We’ve got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). +--- -#### Third-party libraries +## Fields and expansions + +### Default response + +```json +{ + "data": [ + { + "id": "1234567890", + "name": "Example User", + "username": "example" + } + ] +} +``` + +### Available fields + + +| Field | Description | +|:------|:------------| +| `created_at` | Account creation date | +| `description` | User bio | +| `profile_image_url` | Avatar URL | +| `public_metrics` | Follower/following counts | +| `verified` | Verification status | + + + +| Expansion | Description | +|:----------|:------------| +| `pinned_tweet_id` | User's pinned Post | + + +### Example with fields + + + +```bash cURL +curl "https://api.x.com/2/users/123456789/muting?\ +user.fields=username,verified,created_at&\ +max_results=100" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get muted users with additional fields +for page in client.users.get_muting( + user_id="123456789", + user_fields=["username", "verified", "created_at"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; + +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); + +const paginator = client.users.getMuting("123456789", { + userFields: ["username", "verified", "created_at"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); + }); +} +``` + + -Take advantage of one of our communities’ [third-party libraries](/x-api/tools-and-libraries/overview) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. +--- -### Key concepts +## Pagination -#### Authentication +For users with large mute lists, results are paginated: -All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, or OAuth 2.0 Authorization Code with PKCE to authenticate your requests to these endpoints.  + -[OAuth 1.0a User Context](/resources/fundamentals/authentication) requires you to utilize your API Keys, user Access Tokens, and a handful of other parameters to [create an authorization header](/resources/fundamentals/authentication#authorizing-a-request), which you will then pass with your request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](https://developer-staging.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens).  +```bash cURL +# First request +curl "https://api.x.com/2/users/123/muting?max_results=100" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" -Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/x-api/tools-and-libraries/overview), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. +# Subsequent request with pagination token +curl "https://api.x.com/2/users/123/muting?max_results=100&pagination_token=NEXT_TOKEN" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -[OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  +```python Python SDK +from xdk import Client -To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the developer portal. +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -#### Developer portal, Projects, and developer Apps +# The SDK handles pagination automatically +all_muted = [] -To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/projects) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. -  +for page in client.users.get_muting(user_id="123", max_results=100): + if page.data: + all_muted.extend(page.data) -#### Rate limits +print(f"Muted {len(all_muted)} users") +``` -Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user.  +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); -There is a user rate limit of 50 requests per 15 minutes per endpoint with both POST and DELETE methods. However, with the GET method, the rate limit is only 15 requests per 15 minutes. -  +async function getAllMutedUsers(userId) { + const allMuted = []; -#### Fields and expansions + // The SDK handles pagination automatically + const paginator = client.users.getMuting(userId, { maxResults: 100 }); -The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called fields and expansions. The expansions parameter allows you to expand objects referenced in the payload. For example, this endpoint allows you to pull the following [expansions](/x-api/fundamentals/expansions): + for await (const page of paginator) { + if (page.data) { + allMuted.push(...page.data); + } + } -* pinned\_tweet\_id + return allMuted; +} +// Usage +const muted = await getAllMutedUsers("123"); +console.log(`Muted ${muted.length} users`); +``` -The **fields** parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. This endpoint delivers User objects primarily. By default, the User object returns the **id** , **name**  and **username** fields. To receive additional fields such as **user.created_at** or **user.entities**, you will have to specifically request those using a **fields** parameter.  + -We’ve added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). + + Learn more about pagination + + +--- -#### Pagination +## Behavior differences + +### Muting vs Blocking + +| Feature | Mute | Block | +|:--------|:-----|:------| +| See their Posts | No (hidden) | No | +| They see your Posts | Yes | No | +| They follow you | Yes (can follow) | No (removed) | +| They can DM you | Yes | No | +| Notification sent | No | No | + + +Muting is private — the muted user is not notified and cannot tell they've been muted. + + +--- + +## Error handling + +| Status | Error | Solution | +|:-------|:------|:---------| +| 400 | Invalid request | Check user ID format | +| 401 | Unauthorized | Verify access token | +| 403 | Forbidden | Check scopes and permissions | +| 404 | Not Found | User doesn't exist | +| 429 | Too Many Requests | Wait and retry | + +--- -Mutes lookup can return a lot of data. To ensure we are returning consistent, high-performing results at any given time, we use pagination. Pagination is a feature in X API v2 endpoints that return more results than can be returned in a single response. When that happens, the data is returned in a series of 'pages'. Learn more about how to [paginate through results.](/x-api/fundamentals/pagination) - -**Please note:** If a user mutes from [X](https://x.com/), there is a limit of 200 requests per 15 minutes. - +## Next steps + + + + Make your first mutes request + + + Block users instead of muting + + + Full endpoint documentation + + + Working code examples + + diff --git a/x-api/users/mutes/introduction.mdx b/x-api/users/mutes/introduction.mdx index 310e2f714..1d7f348b3 100644 --- a/x-api/users/mutes/introduction.mdx +++ b/x-api/users/mutes/introduction.mdx @@ -1,44 +1,80 @@ --- -title: Introduction +title: Mutes sidebarTitle: Introduction -keywords: ["mutes", "mute users", "muted accounts", "mute lookup", "manage mutes", "user muting"] +description: Mute and unmute users, and retrieve muted user lists +keywords: ["mutes", "mute user", "unmute", "muted users", "mute API"] --- import { Button } from '/snippets/button.mdx'; -Muting an account allows you to remove an account's Posts from your timeline without unfollowing or blocking that account. Muted accounts will not know that you've muted them and you can unmute them at any time. With manage mutes endpoints, developers can create safer experiences for people on X. One example of how to build with manage mutes is an application that allows you to mute accounts that might Post about specific topics for a specified length of time. With the mutes lookup endpoint, you can see who you or an authenticated user has muted. This can be useful to determine how you interact with the muted accounts.  +The Mutes endpoints let you mute and unmute users, and retrieve the list of users muted by the authenticated user. -Since you are making requests for private information with mute lookup, and on behalf of a user with manage mutes, you must authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and use the user Access Tokens associated with a user that has authorized your App, which can be generated using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) (OAuth 1.0a) or the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#how-to-connect-to-endpoints-using-oauth-2-0-authorization-code-flow-with-pkce)) (OAuth 2.0). +## Overview -### Mutes lookup + + + Mute a user + + + Unmute a user + + + Get your muted user list + + -The mutes lookup endpoint allows you to see which accounts the authenticated user has muted. This endpoint has a rate limit of 15 requests per 15 minutes per user. +--- + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/:id/muting`](/x-api/users/get-muting) | Get muted users | +| POST | [`/2/users/:id/muting`](/x-api/users/mute-user) | Mute a user | +| DELETE | [`/2/users/:source_user_id/muting/:target_user_id`](/x-api/users/unmute-user) | Unmute a user | + +--- + +## Example: Get muted users -### Manage mutes +```bash +curl "https://api.x.com/2/users/123456789/muting?\ +user.fields=username,description" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -The manage mute endpoints enable you to mute or unmute a specified account on behalf of an authenticated user. For these endpoints, there are two methods available: POST and DELETE. The POST method allows you to mute an account, and the DELETE method allows you to unmute an account. There is a user rate limit of 50 requests per 15 minutes for both the POST and DELETE endpoints. +## Example: Mute a user -**Please note: **If a user mutes from [X](https://x.com/), there is a limit of 200 requests per 15 minutes. +```bash +curl -X POST "https://api.x.com/2/users/123456789/muting" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "9876543210"}' +``` + +--- -**Account setup** +## Getting started -To access these endpoints, you will need: + +**Prerequisites** -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) + -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). -
- - - - -
\ No newline at end of file + + + Get your muted users + + + Mute and unmute users + + + Key concepts and best practices + + + Full endpoint documentation + + diff --git a/x-api/users/mutes/migrate/manage-mutes-standard-to-twitter-api-v2.mdx b/x-api/users/mutes/migrate/manage-mutes-standard-to-twitter-api-v2.mdx index 14e5d00fa..b0cfbeb91 100644 --- a/x-api/users/mutes/migrate/manage-mutes-standard-to-twitter-api-v2.mdx +++ b/x-api/users/mutes/migrate/manage-mutes-standard-to-twitter-api-v2.mdx @@ -17,7 +17,7 @@ If you have been working with the standard v1.1 [POST mutes/users/create](https: * App and Project requirements * HTTP methods * Request parameters -   + #### Similarities @@ -38,11 +38,11 @@ Both the endpoint versions support [OAuth 1.0a User Context](https://developer.x * POST https://api.x.com/2/users/:id/muting (mute a user) * DELETE https://api.x.com/2/users/:source\_user\_id/muting/:target\_user\_id - (unmute a user)  + (unmute a user) **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Request parameters** @@ -56,4 +56,140 @@ The following standard v1.1 request parameters have equivalents in X API v2: Please note that the Standard v1.1 parameters are passed as query parameters, whereas the X API v2 parameters are passed as body parameters (for the POST endpoint) or path parameters (for the DELETE endpoint). -Also, an id of the user muting a target user is not required when using the standard v1.1 endpoints since the access tokens passed with [OAuth 1.0a User Context](/resources/fundamentals/authentication) inferred which user was initiating the mute/unmute.  +Also, an id of the user muting a target user is not required when using the standard v1.1 endpoints since the access tokens passed with [OAuth 1.0a User Context](/resources/fundamentals/authentication) inferred which user was initiating the mute/unmute. + +--- + +## Code examples + +### Mute a user (v2) + + + +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/muting" \ + -H "Authorization: OAuth ..." \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "2244994945"}' +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/muting" +response = requests.post(url, auth=auth, json={"target_user_id": "2244994945"}) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Mute a user +response = client.users.mute( + source_user_id="123456789", + target_user_id="2244994945" +) +print(f"Muting: {response.data.muting}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Mute a user +const response = await client.users.mute("123456789", { + targetUserId: "2244994945", +}); +console.log(`Muting: ${response.data?.muting}`); +``` + + + +### Unmute a user (v2) + + + +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/muting/2244994945" \ + -H "Authorization: OAuth ..." +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/muting/2244994945" +response = requests.delete(url, auth=auth) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Unmute a user +response = client.users.unmute( + source_user_id="123456789", + target_user_id="2244994945" +) +print(f"Muting: {response.data.muting}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Unmute a user +const response = await client.users.unmute("123456789", "2244994945"); +console.log(`Muting: ${response.data?.muting}`); +``` + + diff --git a/x-api/users/mutes/migrate/mutes-lookup-standard-to-twitter-api-v2.mdx b/x-api/users/mutes/migrate/mutes-lookup-standard-to-twitter-api-v2.mdx index 4200aa07c..a31e8f6da 100644 --- a/x-api/users/mutes/migrate/mutes-lookup-standard-to-twitter-api-v2.mdx +++ b/x-api/users/mutes/migrate/mutes-lookup-standard-to-twitter-api-v2.mdx @@ -47,7 +47,7 @@ The standard v1.1 endpoints allow you to return up to 5000 users per request. Th **App and Project requirements** -The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from standalone Apps or Apps associated with a project. +The X API v2 endpoints require that you use credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) when authenticating your requests. All X API v1.1 endpoints can use credentials from Apps or Apps associated with a project. **Response data format** @@ -93,3 +93,83 @@ There are also a set of standard v1.1 Mutes lookup request parameters **not** s | Standard | Comment | | :--- | :--- | | include_entities | This parameter is used to remove the entities node from the Post payload. It has been replaced with additive fields and expansions functionality. | + +--- + +## Code examples + +### Get muted users (v2) + + + +```bash cURL +curl "https://api.x.com/2/users/123456789/muting?user.fields=username,verified&max_results=100" \ + -H "Authorization: OAuth ..." +``` + +```python Python +# Requires OAuth 1.0a User Context authentication +import requests +from requests_oauthlib import OAuth1 + +auth = OAuth1( + "API_KEY", "API_SECRET", + "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET" +) + +url = "https://api.x.com/2/users/123456789/muting" +params = {"user.fields": "username,verified", "max_results": 100} + +response = requests.get(url, auth=auth, params=params) +print(response.json()) +``` + +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 + +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) + +client = Client(auth=oauth1) + +# Get muted users with pagination +for page in client.users.get_muting( + "123456789", + user_fields=["username", "verified"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Verified: {user.verified}") +``` + +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Get muted users with pagination +const paginator = client.users.getMuting("123456789", { + userFields: ["username", "verified"], + maxResults: 100, +}); + +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Verified: ${user.verified}`); + }); +} +``` + + diff --git a/x-api/users/mutes/migrate/overview.mdx b/x-api/users/mutes/migrate/overview.mdx index af60aeba8..121b7692f 100644 --- a/x-api/users/mutes/migrate/overview.mdx +++ b/x-api/users/mutes/migrate/overview.mdx @@ -23,7 +23,7 @@ The following tables compare the standard v1.1 and X API v2 mute endpoints: | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE | | Default request [rate limits](/resources/fundamentals/rate-limits) | 15 requests per 15 min (per user) | 15 requests per 15 min (per user) | | Data formats | Standard v1.1 format | [X API v2 format](/x-api/fundamentals/data-dictionary) (determined by fields and expansions request parameters, not backward-compatible with v1.1 formats)

To learn more about how to migrate from the Standard v1.1 format to the X API v2 format, please visit our [data formats migration guide](/x-api/migrate/data-format-migration). | -| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | #### Manage mutes @@ -40,7 +40,7 @@ The following tables compare the standard v1.1 and X API v2 mute endpoints: | Endpoint path | /1.1/mutes/users/create.json | /2/users/:id/muting | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE | | Default request [rate limits](/resources/fundamentals/rate-limits) | 50 requests per 15 min | 50 requests per 15 min | -| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | **Unmute a user** @@ -53,7 +53,7 @@ The following tables compare the standard v1.1 and X API v2 unmute endpoints: | Endpoint path | /1.1/mutes/users/destroy.json | /2/users/:source\_user\_id/muting/:target\_user\_id | | [Authentication](/resources/fundamentals/authentication) | OAuth 1.0a User Context | OAuth 1.0a User Context

OAuth 2.0 Authorization Code with PKCE | | Default request [rate limits](/resources/fundamentals/rate-limits) | 50 requests per 15 min | 50 requests per 15 min | -| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/projects) | | ✔️ | +| Requires use of credentials from a [developer App](/resources/fundamentals/developer-apps) that is associated with a [Project](/resources/fundamentals/developer-apps) | | ✔️ | **Other migration resources** diff --git a/x-api/users/mutes/quickstart/manage-mutes-quickstart.mdx b/x-api/users/mutes/quickstart/manage-mutes-quickstart.mdx index 590958691..bb6e07ea2 100644 --- a/x-api/users/mutes/quickstart/manage-mutes-quickstart.mdx +++ b/x-api/users/mutes/quickstart/manage-mutes-quickstart.mdx @@ -1,81 +1,192 @@ --- -title: Manage mutes -sidebarTitle: Manage mutes +title: Manage Mutes +sidebarTitle: Manage Mutes +description: Mute and unmute users using the X API keywords: ["manage mutes quickstart", "mute users quickstart", "manage mutes tutorial", "mute guide", "mutes quickstart"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the manage mutes endpoints +This guide walks you through muting and unmuting users using the X API. -This quick start guide will help you make your first request to the manage mutes endpoints using [Postman](/tutorials/postman-getting-started). - -If you would like to see sample code in different languages, please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository. **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build a manage mutes request -#### Step one: Start with a tool or library +--- + +## Mute a user + + + + You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). + + + + Find the user ID of the account you want to mute using the [user lookup endpoint](/x-api/users/lookup/introduction). + + + -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. + -To load the X API v2 Postman collection into your environment, please click on the following button: +```bash cURL +curl -X POST "https://api.x.com/2/users/123456789/muting" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"target_user_id": "9876543210"}' +``` - -Once you have the X API v2 collection loaded in Postman, navigate to the “Mutes” folder, and select “Mute a user’s ID”. -  +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 -#### Step two: Authenticate your request +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +client = Client(auth=oauth1) -In this example, we are going to use OAuth 1.0a User Context. +# Mute a user +response = client.users.mute( + source_user_id="123456789", + target_user_id="9876543210" +) -You must add your keys and tokens – specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret – to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +print(f"Muting: {response.data.muting}") +``` -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; -#### Step three: Specify who is going to mute whom +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); -Manage mutes endpoints require two IDs: one for the user (the user who wishes to mute or unmute another user) and the target user (the user that will be muted or unmuted). The user’s ID must correspond to the authenticating user’s ID, meaning that you must pass the Access Tokens associated with the user ID when authenticating your request. In this case, you can specify the ID belonging to your own user. You can find your ID in two ways: +const client = new Client({ oauth1 }); + +// Mute a user +const response = await client.users.mute("123456789", { + targetUserId: "9876543210", +}); + +console.log(`Muting: ${response.data?.muting}`); +``` + + + + + + + ```json + { + "data": { + "muting": true + } + } + ``` + + + +--- -1. Using the [user lookup](/x-api/users/lookup/introduction) by username endpoint, you can pass a username and receive the id field.  -2. Looking at your Access Token, you will find that the numeric part is your user ID. -   +## Unmute a user -The target ID can be any valid user ID. In Postman, navigate to the "Params" tab, and enter your ID into the "Value" column of the `id` path variable. Navigate to the “Body” tab and ID of the user you wish to mute as the value for the target\_user\_id parameter. Be sure not to include any spaces before or after any ID. +Remove a mute from a user: -| | | -| :--- | :--- | -| **Key** | **Value** | -| `id` | authenticated user ID | -| target\_user\_id | the user ID you wish to mute | + -#### Step four: Make your request and review your response +```bash cURL +curl -X DELETE "https://api.x.com/2/users/123456789/muting/9876543210" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +```python Python SDK +from xdk import Client +from xdk.oauth1_auth import OAuth1 - ``` - { "data": { "muting": true } } - ``` +oauth1 = OAuth1( + api_key="YOUR_API_KEY", + api_secret="YOUR_API_SECRET", + access_token="YOUR_ACCESS_TOKEN", + access_token_secret="YOUR_ACCESS_TOKEN_SECRET" +) +client = Client(auth=oauth1) -If you receive a "muting": true, then the id is successfully muting the target\_user\_id +# Unmute a user +response = client.users.unmute( + source_user_id="123456789", + target_user_id="9876543210" +) -To unmute the same user you can use the request entitled “Unmute a user ID”, which is also found in the “Mutes” folder of the X API v2 collection loaded in Postman. The source\_user\_id should be your user ID and target\_user\_id should be the user ID to unmute. You will not have to add this as a JSON body so you will want to make sure that you add in the requisite query params for source\_user\_id and target\_user\_id. +print(f"Muting: {response.data.muting}") +``` -On a successful unmute, you will receive a similar response to the following example: +```javascript JavaScript SDK +import { Client, OAuth1 } from "@xdevplatform/xdk"; + +const oauth1 = new OAuth1({ + apiKey: "YOUR_API_KEY", + apiSecret: "YOUR_API_SECRET", + accessToken: "YOUR_ACCESS_TOKEN", + accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", +}); + +const client = new Client({ oauth1 }); + +// Unmute a user +const response = await client.users.unmute("123456789", "9876543210"); + +console.log(`Muting: ${response.data?.muting}`); +``` + + + +**Response:** + +```json +{ + "data": { + "muting": false + } +} +``` + +--- + +## Mute vs Block + +| Feature | Mute | Block | +|:--------|:-----|:------| +| See their Posts | No | No | +| They see your Posts | Yes | No | +| They can follow you | Yes | No | +| They can DM you | Yes | No | +| They know | No | Yes | + +--- - ``` - { "data": { "muting": false } } - ``` +## Next steps + + + + Get your muted users + + + Block users instead + + + Full endpoint documentation + + diff --git a/x-api/users/mutes/quickstart/mutes-lookup.mdx b/x-api/users/mutes/quickstart/mutes-lookup.mdx index 9582e3eb8..45ef2460f 100644 --- a/x-api/users/mutes/quickstart/mutes-lookup.mdx +++ b/x-api/users/mutes/quickstart/mutes-lookup.mdx @@ -1,103 +1,165 @@ --- -title: Mutes lookup -sidebarTitle: Mutes lookup +title: Mutes Lookup +sidebarTitle: Mutes Lookup +description: Get the list of users you have muted keywords: ["mutes lookup quickstart", "mute lookup quickstart", "mutes lookup tutorial", "mute lookup guide", "mutes quickstart"] --- import { Button } from '/snippets/button.mdx'; -## Getting started with the mutes lookup endpoint +This guide walks you through retrieving your muted users list using the X API. -This quick start guide will help you make your first request to the mutes lookup endpoint using Postman. - -Please visit our [X API v2 sample code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) GitHub repository if you want to see sample code in different languages. **Prerequisites** -To complete this guide, you will need to have a set of [keys and tokens](/resources/fundamentals/authentication) to authenticate your request. You can generate these keys and tokens by following these steps: - -* [Sign up for a developer account](https://developer.x.com/en/apply-for-access) and receive approval. -* Create a [Project](/resources/fundamentals/projects) and an associated [developer App](/resources/fundamentals/developer-apps) in the developer portal. -* Navigate to your App's “Keys and tokens” page to generate the required credentials. Make sure to save all credentials in a secure location. +Before you begin, you'll need: +- A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App +- User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) -### Steps to build a mutes lookup request -#### Step one: Start with a tool or library +--- -There are several different tools, code examples, and libraries that you can use to make a request to this endpoint, but we will use the Postman tool here to simplify the process. +## Get your muted users -To load the X API v2 Postman collection into your environment, please click on the following button: + + + You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). + - -Once you have the X API v2 collection loaded in Postman, navigate to the “Mutes” folder, and select “Mutes lookup”. -  + -#### Step two: Authenticate your request + -To properly make a request to the X API, you need to verify that you have permission. To do so with this endpoint, you must authenticate your request using either [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). +```bash cURL +curl "https://api.x.com/2/users/123456789/muting?\ +user.fields=created_at,username,verified&\ +max_results=100" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -In this example, we are going to use OAuth 1.0a User Context. +```python Python SDK +from xdk import Client -You must add your keys and tokens – specifically your API Key, API Secret Key, OAuth 1.0a user Access Token, and OAuth 1.0a user Access Token Secret – to Postman. You can do this by selecting the environment named “X API v2” in the top-right corner of Postman and adding your keys and tokens to the "initial value" and "current value" fields (by clicking the eye icon next to the environment dropdown). +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") -These variables will automatically be pulled into the request's authorization tab if you've done this correctly. -  +# Get muted users with pagination +for page in client.users.get_muting( + "123456789", + user_fields=["created_at", "username", "verified"], + max_results=100 +): + for user in page.data: + print(f"{user.username} - Muted") +``` -#### Step three: Specify a user +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -With this endpoint, you must specify your user ID or the ID of an authenticated user to see who you or the authenticated user has muted. +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); +// Get muted users with pagination +const paginator = client.users.getMuting("123456789", { + userFields: ["created_at", "username", "verified"], + maxResults: 100, +}); -In Postman, navigate to the "Params" tab and enter the authenticated user ID into the "Value" column of the id under “Path Variables” (at the bottom of the section), making sure to not include any spaces before or after ID. +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(`${user.username} - Muted`); + }); +} +``` -Above the “Path Variables” section, you’ll notice there are optional “Query Params” to add. For this example, we will check the variable max_results and add a value of 5. + -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Parameter Type** | -| `id` | (your user ID) | Path | -| max_results | 5 | Query | + -#### Step four: Identify and specify which fields you would like to retrieve + + ```json + { + "data": [ + { + "id": "2244994945", + "name": "X Developers", + "username": "XDevelopers", + "created_at": "2013-12-14T04:35:55.000Z", + "verified": true + } + ], + "meta": { + "result_count": 1, + "next_token": "1710819323648428707" + } + } + ``` + + -If you click the "Send" button after step three, you will receive the default [user object](/x-api/fundamentals/data-dictionary#user) fields in your response: id, name, and username. +--- -If you want to receive additional fields beyond id, name, and username, you will have to specify those fields in your request with the [fields](/x-api/fundamentals/fields) and/or [expansions](/x-api/fundamentals/expansions) parameters. +## Include additional data -For this exercise, we will request three additional sets of fields from different objects: +Use expansions to get related data like pinned Posts: -1. The additional user.created_at field in the primary user objects. -2. The associated pinned Posts’ object’s default fields for the returned users: id and text. -3. The additional  tweet.created_at field in the associated Post objects. + -In Postman, navigate to the "Params" tab and add the following key:value pair to the "Query Params" table: +```bash cURL +curl "https://api.x.com/2/users/123456789/muting?\ +user.fields=created_at&\ +expansions=pinned_tweet_id&\ +tweet.fields=created_at" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` -| | | | -| :--- | :--- | :--- | -| **Key** | **Value** | **Returned fields** | -| user.fields | created_at | user.created_at | -| expansions | pinned\_tweet\_id | tweet.id, tweet.text | -| tweet.fields | created_at | includes.tweets.created_at | +```python Python SDK +from xdk import Client + +client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") + +# Get muted users with expansions +for page in client.users.get_muting( + "123456789", + user_fields=["created_at"], + expansions=["pinned_tweet_id"], + tweet_fields=["created_at"] +): + for user in page.data: + print(f"{user.username}") + # Pinned Posts are in page.includes.tweets +``` + +```javascript JavaScript SDK +import { Client } from "@xdevplatform/xdk"; -You should now see a similar URL with your own user ID instead of the example ID URL next to the "Send" button: +const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); - `https://api.x.com/2/users/1324848235714736129/muting?user.fields=created_at&expansions=pinned_tweet_id&tweet.fields=created_at&max_results=5` +// Get muted users with expansions +const paginator = client.users.getMuting("123456789", { + userFields: ["created_at"], + expansions: ["pinned_tweet_id"], + tweetFields: ["created_at"], +}); +for await (const page of paginator) { + page.data?.forEach((user) => { + console.log(user.username); + }); + // Pinned Posts are in page.includes?.tweets +} +``` -#### Step five: Make your request and review your response + -Once you have everything set up, hit the "Send" button, and you will receive a similar response to the following example response: +### Response with expansion -``` +```json { "data": [ { - "username": "TwitterDev", + "username": "XDevelopers", "created_at": "2013-12-14T04:35:55.000Z", "id": "2244994945", - "name": "Twitter Dev", + "name": "X Developers", "pinned_tweet_id": "1430984356139470849" } ], @@ -106,7 +168,7 @@ Once you have everything set up, hit the "Send" button, and you will receive a s { "created_at": "2021-08-26T20:03:51.000Z", "id": "1430984356139470849", - "text": "Help us build a better Twitter Developer Platform!\n \nTake the annual developer survey >>> https://t.co/9yTbEKlJHH https://t.co/fYIwKPzqua" + "text": "Help us build a better X Developer Platform!..." } ] }, @@ -116,14 +178,28 @@ Once you have everything set up, hit the "Send" button, and you will receive a s } ``` -#### Step six: Paginate through your results +--- -You may notice that there is a meta object located at the bottom of the response. If you received a next_token, this signals that there is another page of results that we can retrieve. To pull the next page of results, you will pull the value of the next_token field and add it to the request as the value to an additional pagination_token query parameter. -  +## Paginate through results + +The SDKs handle pagination automatically. For cURL, use the `next_token` from the response: + +```bash +curl "https://api.x.com/2/users/123456789/muting?\ +max_results=100&\ +pagination_token=1710819323648428707" \ + -H "Authorization: Bearer $USER_ACCESS_TOKEN" +``` + +--- -| | | -| :--- | :--- | -| **Key** | **Value** | -| pagination_token | 1710819323648428707 | +## Next steps -If you send the request after adding this additional parameter, the next five results will be delivered with the subsequent payload since we specified max_results as 5 in step three. You can continue to repeat this process until all results have been returned, but you can also use the max_results parameter to request up to 1000 users per request, so you don’t have to paginate through results quite as much. + + + Mute and unmute users + + + Full endpoint documentation + + diff --git a/x-api/users/search/introduction.mdx b/x-api/users/search/introduction.mdx index fe5f1aa26..74beb2b15 100644 --- a/x-api/users/search/introduction.mdx +++ b/x-api/users/search/introduction.mdx @@ -1,34 +1,85 @@ --- -title: Introduction +title: User Search sidebarTitle: Introduction -keywords: ["user search", "search users", "find users", "user discovery", "user search API", "search accounts"] +description: Search for users by keyword +keywords: ["user search", "search users", "find users", "user search API", "people search"] --- import { Button } from '/snippets/button.mdx'; -Available to developers with Pro access and higher +The User Search endpoint lets you search for users by keyword. Find users by name, username, or content in their bio. -The Users Search endpoint provides a simple, relevance-based search interface to public user accounts on X. Try querying by topical interest, full name, company name, location, or other criteria. +## Overview -This endpoint currently supports [User Auth](/resources/fundamentals/authentication) . By default, you get 100 Users per request and you can specify up to 1000 Users per request. It has a limit of 10,000 requests per 24 hours (in addition to the 300 requests per 15 minutes) - -**Account setup** + + + Search by name, username, or bio + + + Find relevant accounts + + + +--- + +## Endpoint + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/users/search`](/x-api/users/search-users) | Search for users | + +--- + +## Example request -To access these endpoints, you will need: +```bash +curl "https://api.x.com/2/users/search?\ +query=python%20developer&\ +user.fields=description,verified,public_metrics" \ + -H "Authorization: Bearer $BEARER_TOKEN" +``` -* An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +## Example response -Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). +```json +{ + "data": [ + { + "id": "1234567890", + "name": "Python Developer", + "username": "pythondev", + "description": "Building cool things with Python", + "verified": false, + "public_metrics": { + "followers_count": 5000, + "following_count": 200, + "tweet_count": 1500 + } + } + ], + "meta": { + "result_count": 1 + } +} +``` + +--- + +## Getting started + + +**Prerequisites** + +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- Your App's [keys and tokens](/resources/fundamentals/authentication) -
- - - -
+ + + + Look up users by ID or username + + + Full endpoint documentation + + diff --git a/x-api/webhooks/introduction.mdx b/x-api/webhooks/introduction.mdx index 200530def..3c75a3023 100644 --- a/x-api/webhooks/introduction.mdx +++ b/x-api/webhooks/introduction.mdx @@ -1,370 +1,113 @@ --- -title: V2 Webhooks API +title: Webhooks sidebarTitle: Introduction -keywords: ["webhooks", "webhook API", "webhook events", "real-time webhooks", "webhook notifications", "webhook subscriptions", "webhook management"] +description: Receive real-time data via webhook delivery +keywords: ["webhooks", "webhook API", "real-time webhooks", "webhook delivery", "event webhooks"] --- -## Overview +import { Button } from '/snippets/button.mdx'; -The V2 Webhooks API enables developers to receive real-time event notifications from X accounts via webhook-based JSON messages. These APIs allow you to register and manage webhooks, develop consumer applications to process events, and ensure secure communication through challenge-response checks (CRC) and signature headers. +Webhooks enable real-time data delivery to your server. Instead of polling for updates, receive data as events occur. -## Feature Summary +## Overview -| Tier | Pricing | Number of webhooks | -| :---: | :---: | :---: | -| Self-Serve Pro | $5000/mo | 1 | -| Enterprise | [Contact sales](/resources/enterprise/forms/enterprise-api-interest#enterprise-access-form) | 5+ | + + + Receive events instantly + + + Data sent to your server + + + No polling required + + + Retry and recovery support + + -## Products That Support Webhooks - -These are the products that currently support delivering events via webhook: - -- [Account Activity API (AAA)](/x-api/account-activity/introduction) -- [Filtered Stream](/x-api/webhooks/stream/introduction) - -## Manage Webhooks - -The Account Activity API provides you webhook-based JSON messages any time there are events associated with X accounts subscribed to your service. X delivers those activities to your registered webhook. In the following steps, you will learn how to manage webhooks and subscribed users. - -You will learn how to register, view, and remove, both webhooks and subscribed users. We'll be using simple cURL commands to make requests to the various API endpoints. cURL is a command-line tool for getting or sending requests using the URL syntax. - -There are several details that need attention before you can start receiving webhook events in your event consumer application. As described below, you will need to create an X app, obtain Account Activity API access, and develop a web app that consumes webhook events.  - -### 1\. Develop A Webhook Consumer App - -In order to register a new webhook associated with your X app, you’ll need to develop, deploy and host a web app that receives X webhook events, and responds to our security requests. - -*Before you get started, we recommend checking out our [sample apps](https://github.com/xdevplatform/account-activity-dashboard-enterprise/tree/master)\!* - -* Create a web app with a publicly accessible HTTPS URL that will act as the webhook endpoint to receive events.  - * The URI *path* is completely up to you. This example would be valid: [https://mydomain.com/service/listen](https://mydomain.com/service/listen) - * If you are listening for webhooks from a variety of sources, a common pattern is: [https://mydomain.com/webhook/twitter](https://mydomain.com/webhook/twitter) - * Note that the specified URL cannot include a port specification ([https://mydomain.com:5000/NoWorkie](https://mydomain.com:5000/NoWorkie)). -* A first step is writing code that receives a X Challenge Response Check (CRC) GET request and responds with a properly formatted JSON response.  -* Register your webhook URL. You will make a POST request to a /2/webhooks endpoint with the URL in the json body. When you make this request X will send a CRC request to your web app. -* When a webhook is successfully registered, the response will include a webhook id. This webhook id is needed later when making requests to products that support webhooks.  -* X will send POST requests containing events to the URL you registered. These events will be encoded in JSON. See [here](/x-api/account-activity/introduction#account-activity-data-object-structure) for example webhook JSON payloads. - -#### Optional: Use xurl for testing - -For testing purposes, the xurl tool now supports temporary webhooks\! Install the latest version of the [`xurl` project](https://github.com/xdevplatform/xurl) from GitHub, configure your authorization, then run: - -``` -xurl webhook start -``` - -This will generate a temporary public webhook URL, automatically handle all CRC checks, and log any incoming subscription events. It’s a great way to verify your setup before deploying. Example output: - -``` -Starting webhook server with ngrok... -Enter your ngrok authtoken (leave empty to try NGROK_AUTHTOKEN env var): - -Attempting to use NGROK_AUTHTOKEN environment variable for ngrok authentication. -Configuring ngrok to forward to local port: 8080 -Ngrok tunnel established! - Forwarding URL: https://.ngrok-free.app -> localhost:8080 - -Use this URL for your X API webhook registration: https://.ngrok-free.app/webhook - -Starting local HTTP server to handle requests from ngrok tunnel (forwarded from https://.ngrok-free.app)... -``` - -**Important Notes** - -* When registering your webhook URL, your web app needs to use your app’s consumer secret for the encryption of the CRC check. - -* All incoming Direct Messages will be delivered via webhooks. All Direct Messages sent via [POST /2/dm\_conversations/with/:participant\_id/messages](/x-api/direct-messages/send-a-new-message-to-a-user) will also be delivered via webhooks. This is so your web app can be aware of Direct Messages sent via a different client. - -* If you have more than one web app sharing the same webhook URL and the same user mapped to each app, the same event will be sent to your webhook multiple times (once per web app). - -* In some cases, your webhook may receive duplicate events. Your webhook app should be tolerant of this and dedupe by event ID. - -* See example code: - - * [Account Activity API Setup](https://github.com/m-rosinsky/XWebhookTest), a node web app that displays webhook events using the enterprise tier of the Account Activity API. - -### 2\. Securing Webhooks - -X's webhook-based APIs provide two methods for confirming the security of your webhook server: - -1. The challenge-response checks enable X to confirm the ownership of the web app receiving webhook events.  -2. The signature header in each POST request enables you to confirm that X is the source of the incoming webhooks. - -See the CRC Check section for implementation requirements. - -### 3\. The Webhooks API - -Webhooks are managed through the Webhook Management API. All endpoints require OAuth2 App Only Bearer Token authentication. - -#### [Adding a webhook](/x-api/webhooks/create-webhook-config) - -**POST /2/webhooks** - -**Description:** - -Create a webhook configuration. Your publicly accessible `https` callback URL must be passed in the JSON body. -Let’s begin with registering a new webhook URL for the given application context. - -**Authentication:** - -OAuth2 App Only Bearer Token - -* **Bearer token** `` e.g. `AAAAAAAAAAAA0%2EUifi76ZC9Ub0wn...` - -**Parameters (JSON Body):** - -``` -{ - "url": "" -} -``` - -* **URL** `` e.g. `https://yourdomain.com/webhooks/twitter/` - -**Request:** Copy the following cURL request into your command line after making changes to the following: - -```` - ``` - curl --request POST --url 'https://api.twitter.com/2/webhooks?url=' --header 'authorization: Bearer ' - --data '{ -"url": "https://yourdomain.com/webhooks/twitter" - }' - ``` -```` - -**Responses:** - -**Success (200 OK)** +--- -A successful response indicates the webhook was created and the initial CRC check passed. +## Webhook types -``` -{ - "data": { - "id": "", - "url": "", - "valid": true, - "created_at": "YYYY-mm-DDTHH:MM:ss.000Z" - } -} -``` +| Type | Description | +|:-----|:------------| +| [Filtered Stream Webhooks](/x-api/webhooks/stream/introduction) | Receive filtered stream Posts via webhook | +| [Account Activity API](/x-api/account-activity/introduction) | Receive account activity events | -**Failure (400 Bad Request)** +--- -Failures return a standard error object. +## How webhooks work ``` -{ - "errors": [ - { - "message": ":
" - } - ], - "title": "Invalid Request", - "detail": "One or more parameters to your request was invalid.", - "type": "https://api.twitter.com/2/problems/invalid-request" -} +┌──────────┐ ┌──────────┐ ┌──────────┐ +│ X Event │ → │ X Server │ → │ Your │ +│ Occurs │ │ │ │ Webhook │ +└──────────┘ └──────────┘ └──────────┘ ``` -Common reasons for failure include: - -| Reason | Description | -| :---- | :---- | -| `CrcValidationFailed` | The callback URL did not respond correctly to the CRC check (e.g., timed out, wrong response). | -| `UrlValidationFailed` | The callback URL provided does not meet requirements (e.g., not `https`, invalid format). | -| `DuplicateUrlFailed` | A webhook is already registered by your application for the provided callback URL. | -| `WebhookLimitExceeded` | Your application has reached the maximum number of allowed webhook configurations. | - -#### [Viewing a webhook](/x-api/webhooks/get-a-list-of-webhook-configs-associated-with-a-client-app) - -**GET /2/webhooks** - -**Description:** Retrieve all webhook configurations associated with your application (developer account). +1. **Event occurs** — A user posts, sends a DM, etc. +2. **X sends request** — POST request to your webhook URL +3. **You process** — Your server handles the event +4. **Respond 200** — Return 200 OK to acknowledge -**Authentication:** - -OAuth2 App Only Bearer Token - -* **Bearer token** `` e.g. `AAAAAAAAAAAA0%2EUifi76ZC9Ub0wn...` +--- -* **URL** `` e.g. `https://yourdomain.com/webhooks/twitter/` +## Webhook requirements -**Request:** Run the following command to retrieve all registered webhook URLs and their statuses for the given application. +| Requirement | Description | +|:------------|:------------| +| **HTTPS** | Webhook URL must use HTTPS | +| **Public** | URL must be publicly accessible | +| **Fast response** | Respond within 10 seconds | +| **200 OK** | Return 200 status to acknowledge | -Copy the following cURL request into your command line after making changes to the following: +--- -```` - ``` - curl --request GET --url 'https://api.twitter.com/2/webhooks' --header 'authorization: Bearer ' - ``` -```` +## Security -**Success (200 OK)** +### Challenge-Response Check (CRC) -Returns a list of webhook configurations. The list will be empty if no webhooks are configured. +X validates your webhook by sending a CRC request. Respond with an HMAC-SHA256 hash: -*Example (with one webhook configured):* +```python +import hmac +import hashlib +import base64 -``` -{ - "data": [ - { - "created_at": "YYYY-mm-DDTHH:MM:ss.000Z", - "id": "", - "url": "", - "valid": true +def handle_crc(crc_token, consumer_secret): + sha256_hash = hmac.new( + consumer_secret.encode(), + crc_token.encode(), + hashlib.sha256 + ).digest() + + return { + "response_token": "sha256=" + base64.b64encode(sha256_hash).decode() } - ], - "meta": { - "result_count": 1 - } -} -``` - -*Example (with no webhooks configured):* - -``` -{ - "data": [], - "meta": { - "result_count": 0 - } -} -``` - -#### [Removing a webhook](/x-api/webhooks/delete-webhook-config) - -DELETE /2/webhooks/:webhook\_id - -**Description:** Delete a webhook configuration using its specific `webhook_id`. The ID can be obtained from the `POST /2/webhooks` creation response or the `GET /2/webhooks` listing response. - -**Authentication:** - -OAuth2 App Only Bearer Token - -* **Bearer token** `` e.g. `AAAAAAAAAAAA0%2EUifi76ZC9Ub0wn...` - -**Path Parameters:** - -| Parameter | Description | -| :---- | :---- | -| `webhook_id` | The ID of the webhook to delete. | - -**Request:** Run the following command to remove the webhook from the provided application's configuration. - -Copy the following cURL request into your command line after making changes to the following: - -```` - ``` - curl --request DELETE --url 'https://api.twitter.com/2/webhooks/:WEBHOOK_ID' --header 'authorization: Bearer ' - ``` - - **Responses:** -```` - -**Success (200 OK)** - -Returns a json response with "deleted" status true if successfully deleted. - -*Example (with successful deletion of webhook):* - ``` -{ - "data": { - "deleted": true - } -} -``` - -**Failure (400 Bad Request)** - -| Reason | Description | -| :---- | :---- | -| `WebhookIdInvalid` | The provided `webhook_id` was not found or is not associated with your app. | -#### [Validate and Reenable webhook](/x-api/webhooks/webhook-crc-check) +### Signature verification -**PUT /2/webhooks/:webhook\_id** +Verify webhook authenticity using the `x-twitter-webhooks-signature` header. -**Description:** Triggers the challenge response check (CRC) for the given webhook's URL. If the check is successful, returns 200 and reenables the webhook by setting its status to `valid`. - -**Authentication:** - -OAuth2 App Only Bearer Token - -* **Bearer token** `` e.g. `AAAAAAAAAAAA0%2EUifi76ZC9Ub0wn...` - -**Path Parameters:** - -| Parameter | Description | -| :---- | :---- | -| `webhook_id` | The ID of the webhook to validate. | - -**Request:** Run the following command to validate the webhook from the provided application's configuration. - -Copy the following cURL request into your command line after making changes to the following: - -```` - ``` - curl --request PUT --url 'https://api.twitter.com/2/webhooks/:WEBHOOK_ID' --header 'authorization: Bearer ' - ``` - - **Responses:** -```` - -**Success (200 OK)** - -A 200 OK response indicates that the CRC check request was *initiated*. It does **not** guarantee that the CRC check passed. The `valid` field in the response reflects the status *after* the check attempt. If the CRC check succeeds, the webhook's status will be updated to valid. You can verify the current status using `GET /2/webhooks`. - -``` -{ - "data": { - "valid": true // Indicates the status after the CRC check attempt completes - } -} -``` - -**Failure (400 Bad Request)** - -| Reason | Description | -| :---- | :---- | -| `WebhookIdInvalid` | The provided `webhook_id` was not found or is not associated with your app. | -| `CrcValidationFailed` | The callback URL did not respond correctly to the CRC check request. | - -### 4\. The CRC Check - -The Challenge Response Check (CRC) is how X validates that the callback URL you provided is valid and that you control it. - -**When CRC is triggered:** - -* On initial webhook registration (`POST /2/webhooks`) -* Hourly by X to validate -* Manually via `PUT /2/webhooks/:id` - -Example: - -``` -GET https://your-webhook-url.com/webhook?crc_token=challenge_string -``` - -Example JSON Response Body: - -``` -{ - "response_token": "sha256=" -} -``` +--- -**How to Build the Response:** +## Getting started -* Use crc\_token as the message -* Use the app’s **consumer secret** as the key -* Create an HMAC SHA-256 hash -* Base64 encode the result + +**Prerequisites** -## Sample Apps +- An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) +- A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console +- A publicly accessible HTTPS endpoint + -* [Simple webhook server](https://github.com/m-rosinsky/XWebhookTest/blob/main/app.py) - * A single python script that shows you how to respond to the CRC check and accept POST events. -* [Account Activity API sample dashboard](https://github.com/xdevplatform/account-activity-dashboard-enterprise/tree/master) - * A web app written with [bun.sh](https://bun.sh) that allows you to manage webhooks, subscriptions, and receive live events directly in the app. + + + Receive filtered Posts via webhook + + + Receive account events via webhook + + diff --git a/x-api/webhooks/stream/introduction.mdx b/x-api/webhooks/stream/introduction.mdx index dcc9e2a37..d1ea23326 100644 --- a/x-api/webhooks/stream/introduction.mdx +++ b/x-api/webhooks/stream/introduction.mdx @@ -38,7 +38,7 @@ The returned Posts from filtered stream count towards the monthly [Post cap](/x To access these endpoints, you will need: * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). -* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects).  +* To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). diff --git a/x-api/what-to-build.mdx b/x-api/what-to-build.mdx index b95ea4407..0f4c9fe67 100644 --- a/x-api/what-to-build.mdx +++ b/x-api/what-to-build.mdx @@ -1,81 +1,187 @@ --- -title: What to build -keywords: ["what to build", "use cases", "build ideas", "developer ideas", "API use cases", "what can I build", "project ideas"] +title: What to Build +sidebarTitle: What to Build +description: Ideas and inspiration for building with the X API +keywords: ["what to build", "use cases", "build ideas", "project ideas", "API use cases"] --- -## Introduction +The X API opens up possibilities across moderation, analytics, community building, and more. Here are ideas to inspire your next project. -Our developer community has unique skills, experiences, and perspectives that can fill gaps, solve problems, and seed new innovation on X well beyond what we at X can do on our own. We encourage you to build tools and products that make X better, healthier, and extend the public conversation. In particular, we want to see innovation in the following categories: - -- [Moderate conversations for health and safety](#moderate) -- [Enable creation and personal expression](#enable) -- [Measure and analyze what's happening](#measure) -- [Improve community experiences](#improve) -- [Curate and recommend content](#curate) -- [Impact the greater good](#impact) - -### Moderate conversations for health and safety - -We want everyone to feel comfortable, safe, and excited to participate in the conversation on X. We've worked hard to offer tools that give people more control over their experience, like Post reply settings, blocks, and mutes. However, there will always be more work to do, and we want to encourage you to build complementary or additive solutions that help improve the health and safety of the platform. Check out some of our resources below to help you get started: - -Relevant endpoints: +--- -- [Follows](/x-api/users/follows/introduction) -- [Blocks](/x-api/users/blocks/introduction) -- [Mutes](/x-api/users/mutes/introduction) -- [Hide replies](/x-api/posts/hide-replies/introduction) -- [Manage Posts](/x-api/posts/manage-tweets/introduction) (Post reply settings) +## Build for safety & moderation -### Enable creation and personal expression +Help create healthier conversations on X. -X is where people come to share their perspective, ideas, and passions. It's the convergence of individuals and brands, founders and athletes, celebrities and CEOs. Developers have an opportunity to build creative solutions that allow people on X to expand their reach, express themselves in new ways, and connect with like minded communities. As creators and innovators themselves, developers can also build things that broaden how people experience X like helpful bots, gamification, or cross-posted content. Below are a few ideas to seed your innovation: + + + Build tools to help users manage replies, filter content, and protect their experience. + + + Detect and flag abusive content, provide bulk blocking tools. + + -Relevant endpoints: +**Relevant endpoints:** +- [Blocks](/x-api/users/blocks/introduction) — Block and unblock users +- [Mutes](/x-api/users/mutes/introduction) — Mute accounts and keywords +- [Hide replies](/x-api/posts/hide-replies/introduction) — Hide unwanted replies +- [Manage posts](/x-api/posts/manage-tweets/introduction) — Control reply settings -- [Manage Posts](/x-api/posts/manage-tweets/introduction) -- [Spaces](/x-api/spaces/introduction) -- [Retweets](/x-api/posts/retweets/introduction) +--- -### Measure and analyze "what's happening" +## Build for creators + +Help people express themselves and grow their audience. + + + + Schedule posts, threads, and content calendars. + + + Sync content across platforms and formats. + + + Help users compose and publish long-form threads. + + + Create helpful bots that add value to the conversation. + + + +**Relevant endpoints:** +- [Manage posts](/x-api/posts/manage-tweets/introduction) — Create and delete posts +- [Media upload](/x-api/media/quickstart/media-upload-chunked) — Upload images and videos +- [Spaces](/x-api/spaces/lookup/introduction) — Discover audio conversations -Many people come to X because of the opportunity it presents to reach new or existing audiences, amplify their voice, or have an impact. Developers can help these creators, artists, businesses, and local advocates measure, analyze, or understand the impact of their content. Additionally, developers can build new solutions that help people derive insights from the public conversation about trends, their audience, and so much more. We've put together a few resources below to help you get started: +--- -Relevant endpoints/functionality: +## Build for analytics + +Help users understand their impact and audience. + + + + Visualize engagement metrics, growth trends, and reach. + + + Analyze followers, engagement patterns, and demographics. + + + Track hashtags, topics, and conversation volume. + + + Compare accounts, benchmark performance. + + + +**Relevant endpoints:** +- [Metrics](/x-api/fundamentals/metrics) — Engagement and performance data +- [Search posts](/x-api/posts/search/introduction) — Historical and recent search +- [Post counts](/x-api/posts/counts/introduction) — Volume analytics +- [Annotations](/x-api/fundamentals/post-annotations) — Topic classification -- [Object model](/x-api/fundamentals/data-dictionary) (Posts, Users, Spaces, etc) -- [Advanced metrics](/x-api/fundamentals/metrics) -- [Post annotations](/x-api/fundamentals/post-annotations) -- [Search Posts](/x-api/posts/search/introduction) -- [Posts counts](/x-api/posts/counts/introduction) -- [Filtered stream](/x-api/posts/filtered-stream/introduction) -- [Sampled stream](/x-api/posts/volume-streams/introduction) +--- -### Improve community experiences +## Build for communities + +Help people connect and organize around shared interests. + + + + Moderation, member management, and engagement tools. + + + Organize Spaces, live events, and group activities. + + + Aggregate and curate content by topic or interest. + + + Build tools for specific languages and regions. + + + +**Relevant endpoints:** +- [Lists](/x-api/lists/list-lookup/introduction) — Curated account lists +- [Spaces](/x-api/spaces/lookup/introduction) — Audio conversations +- [Follows](/x-api/users/follows/introduction) — Relationship management -As new communities grow and convene on X, we want developers to use their expertise to help serve the unique needs of these communities. X is home of the global conversation, and we want everyone to be able to participate regardless of their language, timezone, cultural differences, or other localized needs. Additionally, we want growing communities around topics like finance or gaming to have tools built for their unique experiences and needs. +--- -### Curate and recommend content +## Build for research + +Analyze public conversations and extract insights. + + + + Study public discourse, information spread, and trends. + + + Source stories, verify information, track breaking news. + + + Monitor brand sentiment, industry trends, and competitors. + + + Train models, analyze networks, study behavior patterns. + + + +**Relevant endpoints:** +- [Full-archive search](/x-api/posts/search/introduction) — Search posts back to 2006 +- [Filtered stream](/x-api/posts/filtered-stream/introduction) — Real-time matching posts +- [Post annotations](/x-api/fundamentals/post-annotations) — Entity recognition +- [Conversation ID](/x-api/fundamentals/conversation-id) — Thread reconstruction -Developers have always come up with creative ways to connect people on X to content they're interested in. Whether it's apps, bots, or other tools that help people have more curated or customized experiences, this is an opportunity for developers to connect people to the content they care most about. Get started: +--- -Relevant endpoints: +## Build for good + +Use the API to make a positive impact. + + + + Monitor emergencies, coordinate aid, spread awareness. + + + Build tools that make X more accessible to everyone. + + + Create learning tools, teaching resources, and tutorials. + + + Help people stay informed and participate in democracy. + + -- [Spaces](/x-api/spaces/introduction) -- [Manage Posts](/x-api/posts/manage-tweets/introduction) -- [Lists](/x-api/lists/manage-lists/introduction) +--- -### Impact the greater good +## Getting started + +Ready to build? Here's your path forward: + + + + [Sign up for a developer account](/x-api/getting-started/getting-access) and create an app. + + + [Make your first request](/x-api/getting-started/make-your-first-request) and explore the API. + + + Pick an [SDK or library](/x-api/tools-and-libraries/overview) for your language. + + + Start small, iterate, and share what you've built! + + -We've seen so many ways that developers have used X to help make the world a better place. From groundbreaking research, to helpful bots, to other non-commercial innovation, developers never cease to amaze us. Below are just a few of the resources to help you get started: +--- -Relevant endpoints: +## Share your work -- [Manage Posts](/x-api/posts/manage-tweets/introduction) -- [Conversation ID](/x-api/fundamentals/conversation-id) -- [Post annotations](/x-api/fundamentals/post-annotations) -- [Search Posts](/x-api/posts/search/introduction) -- [Post counts](/x-api/posts/counts/introduction) -- [Filtered stream](/x-api/posts/filtered-stream/introduction) -- [Sampled stream](/x-api/posts/volume-streams/introduction) +Built something with the X API? We'd love to see it: -Explore [different access levels](/x-api/getting-started/about-x-api) +- Share in the [Developer Forum](https://devcommunity.x.com) +- Tag [@XDevelopers](https://x.com/XDevelopers) on X +- Submit to our [Success Stories](/success-stories) diff --git a/xdks/python/authentication.mdx b/xdks/python/authentication.mdx index 7f7fb1cac..e71a6f2aa 100644 --- a/xdks/python/authentication.mdx +++ b/xdks/python/authentication.mdx @@ -9,7 +9,7 @@ The X API requires authentication for all endpoints. The XDK supports three auth - **Bearer Token**: Use this for read-only access for endpoints that support app-auth (e.g., searching Post's, streaming endpoints). - **OAuth 2.0 PKCE**: Secure authentication for scope-based, user-authorized access (e.g. getting authenticated user's Post non_public metrics) - **OAuth 1.0a**: Legacy authentication for user-specific operations (e.g., posting on behalf of a user, managing lists) -Obtain credentials from the [X Developer Portal](https://developer.x.com/en/portal/dashboard). You'll need an approved developer account and an app with appropriate permissions (e.g., Read + Write). +Obtain credentials from the [X Developer Console](https://developer.x.com/en/portal/dashboard). You'll need an approved developer account and an app with appropriate permissions (e.g., Read + Write). ## Creating a Client All authentication flows create a `Client` instance: ```python @@ -18,7 +18,7 @@ from xdk import Client ### 1. Bearer Token (App-Only) For read-only operations without user context. **Steps**: -1. In the Developer Portal, generate a Bearer Token for your app. +1. In the Developer Console, generate a Bearer Token for your app. 2. Pass it to the `Client`. **Example**: ```python @@ -37,7 +37,7 @@ for page in client.posts.search_recent(query="python", max_results=10): ### 2. OAuth 2.0 with PKCE (User Context) This example shows how to use OAuth 2.0 with Proof Key for Code Exchange (PKCE). Use this for user-specific access (e.g. posting on behalf of a user), uploading media for a user etc.). **Steps**: -1. In the developer portal, register your app with a redirect URI (e.g., `http://localhost:8080/callback`). +1. In the Developer Console, register your app with a redirect URI (e.g., `http://localhost:8080/callback`). 2. Get Client ID (no secret needed for PKCE). 3. Initiate the flow, direct user to auth URL and handle callback. **Example** (using a web server for callback): @@ -80,7 +80,7 @@ client = Client(bearer_token=tokens["access_token"]) ### 3. OAuth 1.0a (User Context) For legacy applications or specific use cases that require OAuth 1.0a authentication: **Steps**: -1. In the Developer Portal, get your API Key and API Secret. +1. In the Developer Console, get your API Key and API Secret. 2. If you already have access tokens, use them directly. Otherwise, complete the OAuth 1.0a flow to obtain them. 3. Create an OAuth1 instance and pass it to the Client. **Example** (with existing access tokens): diff --git a/xdks/python/quickstart.mdx b/xdks/python/quickstart.mdx index 537b51e74..387923ecf 100644 --- a/xdks/python/quickstart.mdx +++ b/xdks/python/quickstart.mdx @@ -8,7 +8,7 @@ This example showcases how to quickly search for Posts using the XDK using Beare pip install xdk ``` ## Step 2: Get Your Bearer Token -1. Log in to the [X Developer Portal](https://developer.x.com/en/portal/dashboard). +1. Log in to the [X Developer Console](https://developer.x.com/en/portal/dashboard). 2. Create or select an app. 3. Under "Keys and Tokens," generate a Bearer Token (app-only auth). ## Step 3: Write and Run Your First Script From b7e4cc09cdcd8f21feb10e2cd54e8e99a389df53 Mon Sep 17 00:00:00 2001 From: Taylor Caldwell Date: Tue, 20 Jan 2026 12:59:32 -0800 Subject: [PATCH 2/4] Add usage auto-recharge and spending limit docs --- x-api/getting-started/pricing.mdx | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/x-api/getting-started/pricing.mdx b/x-api/getting-started/pricing.mdx index 3f258e293..bb8dc2363 100644 --- a/x-api/getting-started/pricing.mdx +++ b/x-api/getting-started/pricing.mdx @@ -2,7 +2,7 @@ title: Pricing sidebarTitle: Pricing description: Pay-per-usage pricing for the X API -keywords: ["X API pricing", "API pricing", "pay-per-usage", "API credits", "billing", "deduplication", "API costs"] +keywords: ["X API pricing", "API pricing", "pay-per-usage", "API credits", "billing", "deduplication", "API costs", "auto-recharge", "spending limit", "spend cap"] --- import { Button } from '/snippets/button.mdx'; @@ -59,6 +59,34 @@ Your credit balance can go slightly negative. If this happens, API requests will Monitor your credit balance regularly to avoid service interruptions. Add credits before your balance reaches zero to ensure uninterrupted API access. +### Auto-recharge + +Enable auto-recharge to automatically top up your credit balance and avoid service interruptions. Configure this in the Developer Console: + +| Setting | Description | +|:--------|:------------| +| **Recharge amount** | The amount to add when auto-recharge triggers (e.g., $25) | +| **Trigger threshold** | Auto-recharge activates when your balance falls below this amount (e.g., $5) | + + +Auto-recharge requires a saved payment method set as your default. You can cancel anytime in the Developer Console or by contacting support. + + +--- + +## Spending limits + +Set a maximum amount you can spend per billing cycle to control costs. When the limit is reached, API requests will be blocked until the next billing cycle. + +| Option | Description | +|:-------|:------------| +| **Spending limit** | Set a specific dollar amount as your maximum spend per billing cycle | +| **Unlimited spending** | No cap—your account continues billing without limits | + + +Use spending limits to prevent unexpected charges, especially during development and testing. + + --- ## Monitoring usage From 2c55c3220b2c9daaa9010004d461e3742123d08a Mon Sep 17 00:00:00 2001 From: tcaldwell-x Date: Tue, 20 Jan 2026 13:21:10 -0800 Subject: [PATCH 3/4] Documentation edits made through Mintlify web editor --- x-api/getting-started/pricing.mdx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/x-api/getting-started/pricing.mdx b/x-api/getting-started/pricing.mdx index bb8dc2363..d3db82632 100644 --- a/x-api/getting-started/pricing.mdx +++ b/x-api/getting-started/pricing.mdx @@ -51,12 +51,10 @@ Deduplication is a **soft guarantee**. While it occurs in the vast majority of c Your credit balance is displayed in the Developer Console. Credits are deducted in real-time as you make API requests. -### Negative balance - -Your credit balance can go slightly negative. If this happens, API requests will be blocked until you add credits to cover the negative balance. - Monitor your credit balance regularly to avoid service interruptions. Add credits before your balance reaches zero to ensure uninterrupted API access. + +_**Note:** It is possible for an account credit balance to go slightly negative. In this case, API requests will be blocked until you add credits to cover the negative balance._ ### Auto-recharge @@ -74,14 +72,13 @@ Auto-recharge requires a saved payment method set as your default. You can cance --- -## Spending limits +### Spending limits Set a maximum amount you can spend per billing cycle to control costs. When the limit is reached, API requests will be blocked until the next billing cycle. | Option | Description | |:-------|:------------| | **Spending limit** | Set a specific dollar amount as your maximum spend per billing cycle | -| **Unlimited spending** | No cap—your account continues billing without limits | Use spending limits to prevent unexpected charges, especially during development and testing. From f0af3bacaab8797f53e7fe5d75a26d1a732264da Mon Sep 17 00:00:00 2001 From: Taylor Caldwell Date: Tue, 20 Jan 2026 14:09:43 -0800 Subject: [PATCH 4/4] . --- images/customer-directory/buzzmonitor.png | Bin 5278 -> 25603 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/customer-directory/buzzmonitor.png b/images/customer-directory/buzzmonitor.png index 3cf80e51a54f6cb28f76facb5ad5b9d1647085a0..45fc215eb8ce3e561cb6db6bc79503f2a6af47ca 100644 GIT binary patch literal 25603 zcmce81yr3&@+T17gM<)*T`ag?+yewBK#*X;xwyN#dvFb&;O-JUxVu|$2n3h?NZ!1e zH*aRo?)mRoLO8d-{#8|Xmvnc11;O&N;%LY(kfETU&?MfxR)B(nwgUc>Ai@Jb=c1l# zK|vu!LX=ePRAr>O^(@U9bo4EC4H%rv0Tl4XE8t|Mqi1ShN1|(B1hL=)9W}OsNFe%r zAQg5QCK)Rc17pY=7aIdb7g;4e7gIeheUJb@GOrUiz`)$VPKU(F+|0t3+ldeKi!V3u z`SCF$h~yW<&Xf-%^eB)-RYsmf#L~uqgq;CQugA>BNy5R!zzk+%VrQZwVPRtCVq|7w zWMZag=HzB#Q)X}xHx8nnWfPRuc znCt79|Ao1}jxz(WY>dEjMrH=4e}&pX4F5S03)??d_7VKp^N42rSGd03pNy^SZOnd+ zSznLQz|6qhz{1WJz%&2G1ERR)jh$^FdOBvt1`s1-JBS6uLeJR1mJdY#k1_l!$PuD% zXZ&96ti`n3%Yk*tnV48CW=(-~WjfhS=F!8Q54soD9tPK&&i(cF0J`h}t<@0jweB zIz|SJRu)Eo_K8@U|C%r#=x-*;O+smA$MbI{Noj3wX~zTD^}7%7D<*m-R^VF+a4J@IZYD4T zD<{|czf=Cj!G3qMviu$Pr@Q^``qStBj{HYl`C}#YtssBsfu8xlga5J8|9iBoy}7P| zjiuo)>9&8c{SRFKfwKda#a_n@h?l>^!EgEhL3_5wI?R8tV`BmSv4IVkxWGDkEP9+A ztjzj`dMrA6T)Hg!ta@CW%q)6b9IS?apNlQT#o!MnEUe6IY=1>O+A_DqW2llgurRVS z{s)qi^S|L#9%JqwFjnxt!SwaG|A@K2$^V-|^tjC}^&y53AlUu}K5m5cOl?}Hl z9Qw?<%)k#8c4l1;W_>pOKgaY-y&x7gX7F!me^V%kg_H9a`ky_&RR{vK_nXb1O8fUs z>tECa?1}$bLmDAhSuJB$F4{ zk4jGTDV)Y};qe(CZu?JY7*GXLZwnPUxiI1^tNO)VhN7l@{!!KQ6C0`J1Gcai&IRB*0E}z zLv3EJ3ZU~a2-Y9)%s^O~Mey#UB>h528OZRquCeVTtZpQ@w4QJ1js0v->yXWV@t$VX zvQVRE`%bDvB?eP6r z`&0Hlbxu?(G+R~-YHPiXu`T<@BTjXhO&94lTQ+YTWlp91b*ZBv`|+3TdMvk|8KqXW zU;OJ-{gAkf6~g5SJDby$UGnW@R7%%j34?le(NCPRq4P=AX7x0FK3(2k9^`Itob4$7 z^2z#V0n4V<_^8gqNo9z`4EwWroxBz05{=+2M-6TsYt>rPkCI7BSZ+%d2BEC|)zezE zDjL>s=s8!e-&474!y-emgP~LXu}<#&aA}oc9haPqydn^0&8C!^FP2+5el}!Vzj|RniypIu;!s3vM%Rt$MD`Z}MG7oW?)W)YC<_M@?_L`VO8e z>y<)Zm2BqFMmpt5@!Rd|N0-RtRjQFR<30#C#Eq+$S{U$Hl!#gNkZalm1rnAp_0Woc zq?n#V&mP~BEZXFBd}Bi>H6N*c%tXtmrqekK3FW+yc9d z`MNxEUWrFDb6_I9pPyo@B*f8Y>CiwLowD;;f0#Cu)74?)VN2fVMcUTb<#>M57vloD z9?j{2XoMt-msT_b=CvW;$cEw-i|fyYK6cZL_=T(FulGFk*`>DSm)x~3L>3%B?Aq~0 z57}cAMn}6?sSxL1WLK+MY57%*N_kq)C_+?ri=0h~xDst~W&p^P%grzKP&B>+eoqNjfT+DsA znJ4HKcVU;c7M0pug%mV>VYD>eT&qjGE_`wt;NhAYF3(Mx&!HYR?U1;azE6&71stAh zBRfC4Lm-~6+0(Pax(a-RzTNyM7Dr4^MiLiC5oP?*gkkL=eZrYslsF6 z>GU<#DA(|N^}A@b*(*bwmaAh@E8jGDdd77sm}8l~pgmDDa@7(^DWY#v?4YQmp;Yr) zbX#BsUCiXyy%bxW>^uqdUbd+7*|Ghd7#9lsUG8_wliSk z<<3UbvWZ>m!Nj4v!kt*p2j!KrCuwD+LaG^tZ!X(oJA2(T7-PJ=Qp>WjGY9k1sw&mP z%u?QoeNls*qO$$H)_L{xmqpPxwiJni*}a-H>zYpp9tu#8&f(1k)30Twai&tB081UnXuV}QPS^LQt+*d{iOF&Xo*%f)ZJ zXTuEdXeKCEZtWXA74(m!Y%+NqQt}7s$E~I9T+DUL7B^!9Thw2a-teGS)JcM*(lTL#^h6SLd9VsCr?N+X2gh5KhmXjg%)sTXEd; za9jt$d`{1O`x4sTP-*vC zZ)R<`%eJG@f`_hFo@f+4>I%*ejfN}M<-uIjTTgeU1#hR7Nd^Nb$J!lAN9_f@(i5dS zrc}WQ0VZPEGCugcT8-;FH&?Xn-OyE77V}0k_61(Ew;fR8jmn;0k?vAUmT%ZrCA_$i ze9zIs1AaUU%A6t;4jkn+UVf>Ggp<5mM)lLP5NqtUwroEDYue%P{1 z7eSS@ZmlC@xl%HvPLcH}%v#XrKK-Z7A_zo@1Zi$RViUE?b*_GFQdT?A=NUh}WcVa- zwbCNPn2Gs;@AkMur=hTrTH5m@RGSUb^h5mkJ&9Vi$vai$@Yj>j+cGzd zEtj8wgTE{+X__p`l0CWY_7H9pO5coAEdQZp!V`2(s8f)4JtX6*)cwG8i$dPXGv=i6 z_D8?Wfal(HX~NEUe!rp|AMTvfx0p)f5b+TpsxU`%YW(Ov z@>y=1s&&ffL>^~q%8WF<@@OvkqY`{AVaB}zKM}15ZN=KO(;Tig##a&L6pQBNIF8)o zjUOII(Gy5`>xb`;l=}pT$b?*~I>nACScoIbCf*#os6OG_{hE?9{{*6)5xp^e@o}RE zYS%ZTeQ$hvgS+RZW01bmt&R$>=2OJNsrHyRg*OlE3r1H3-P!|x^_~=pc$qh4&hKOv zVYSu0)@ci7e=;}CkWi-0hQ~DRX#{==w{A4MoDs=EoRwCoHM{S8DKQu)|n5KF{xf#qG60Ia9X>aYJ)hr<529momE?%2KB% zH`&dcZE?o&3X#&oG4sWll^HRYCV-A$P$Hny>|dR(7xg(o7_T%GmSN`QvMnUXXzPa@ zH?L{6Bh2TluCmU<90`op{5(g8@HtzQ<^_z&E22goxxUva%eGm2Vu5ci9dR&aNL2Si z(hjAlu?8xGvse%h@X$CMZ@n4g0*DBvg^SwPG>ii?Eo%EbTSRIaF=E z4A(o7bTg_%Bk*nw-6p$C6xEX{-PX>k+bsB3w~<6$)^Xn(qLHIIGmw}BT3c8zif}bS zQf}M3Za-#9nwQ_2GxOp)3x=$rg0=0wX{5GcN-Ip!_Y%wo^~rX`u$160tctC~d@-!z zYrfQEnvg9huE$wh%h3Fxl+MV$f?@*GxfnD^;KOaOUQCNt<{TzwZ-IeLH$Lxi=EFWA zrlgKaMTwUFt$y6;ld;yK_e`n*h_cMWI?QkhhOpbLGC=qMntxIfs%I$+eAyuk3SmJh zdY8szx$hv-nLjZlfl=!U08Z%l@nD4P5hOmDjxS&u3Nd?Ew*?BiUg>Dpe?*&9D&T8F zlH1eAFoPNF^05HJ2Kb|^_HSW2rCekveWBy?R&9j74PKv76VbSlPa)ROU!ZIXs~Y8Uh)PFyVYfzyeJo;e0)u72 z+rk^%^@hX!;G#Ofb|zyEE>)9vqlb@Eb!~RCnH&#Lw-6Vpap@>U;Img7-_l z28z%6O9sp#q*~fZt#UbYP}a8ccM=K49zd2rZxJl*zqq#{9go&-s9MN71Z{gcjK#FT z@Y`bwhH@jiL6-?l?a?NgKhrQ4d?Ve}XtGB#fzW|O&Yu~L*A$8tE@2UMjrj8ka5MQP zKEeF*I?a9l=Ct30Q-wH27%GKov!izu>4}#mclNf75SQM;eJODpMgAmd98_|)Xv?dL zbm6-<$ZutS+D)SlBGtfXiMCV^9zhn;kCjeO*Z63^XnYQ2`Dj|{LO=NMMs8joEO?|9 z<+`peCuE+#i*o!H#bu<*X6)<`<6>r)T+kSKghB6Xk)M0nS>E9AXUhKAm=zGghPq0|L9UydcG zjwZ3WS;pqGu_oTVhLMHT#z(}mbk{>_&$~Ts87^(hX5*Ut$>nU>EFBUhi{?f9M6f~jDd_U4NModcXtw~`Vc zv^?*Ae&~y#(7B&(y|=eo59dhc-J@5pp@VXQw$Nq;H@Whj5%M?`NmQCW++ARlL3glB zSwkNobM&2`I~XCCH2si_?Uq`V3A;!PG?4^XfP>^imYD+bW7S(-raU3PN5gKWZo* z<4`}b$K`7Gkf-ZWvt#0tX=(G1A0_)OH-{+hhYforOm|rr?ysahw|SPCT^9n-2*S`} zbk}$e>&VUum4(Hr-(}mZ27K`tp=+8Woc`>E@uNni?f+mqqwYY!w@Q=3*Kf18_YL); z`L?oFY)YUH8@B>%ktG&ZPvw0u7?v7pgv11q_Fh#DkoNC z+8M=JL{Fc`iL>rKeSX@PsXZKR;qeoXG^x`y0He#1M{NK7-0&E#L=cqxDB|mk= z7IZX6I+nBfM>CJ_q7u?tBqvwmZ%hu=$-&5_I@0UyyI2Co1Vt^Hx{OO+cMRcpkQJWN zQvsUt=*EPpQo;eLBTBlOb{;LfdVAuXU8uK>uhuTf!TjNqB;lYVPgp;*ox;9pN`;+_ z8}BzfElHCQ++|+5@NZ#*wNF`NoXykB+*j(;8={r4N*9ol#U(qa6QtY{d~F$zYWS0; ziQ_tbw|Odp()(#NuY-6@HhDsX_lw zVkXKacab%+{5K{`%2kzb8)i#k=VTI_(FsKwtizlsVoXKWi2x7(-qGp`K?k^2_>ZE|82 zkGDeA7L6O!bxCT23B+7JvhR>(!d{FXj|W%Tl|Yv>`=K`o{xEx=oIr$L<6Yq2a(y_> zE-9nHqs#M2ai{*ZkGQeo+P3Cg_hNtLShLxe9EZL5PQL~2H9c1?LTre0;P5V;Bs22y zI(u;_6HSo!n{gnqBvLD5R$~xA88IVUO+Pz`KuxcRkNdvY)5sA*km3vVO?BbQDWPh) zQ4I^aTW%f0OH_mun5n;fc}-_}zLeV~Cq%VQidakQX%F^SYmVT-uBVG#RqZ`kQ%u$J z$>4?t|D1^Hax+Dea!-BhPh10gew6O8c0p^|PbGTri0H%H4>;>#AyP;I(edGMeKN`w z3*cwLGGJZS^eV2#y}&_Btx?kOJ?(_fk8C@UaRksY#_iI_wT| zle6lw5*f{@N7>O39b%Ky9WXfX4~aEWuU|pMli(RK?~S?lbI2EDVZulr3m0&zZ@<6{ zE}9c7Teont*EqkMUjA0EvTD8+u|f$iCE}yE`&1^mD>{Ms++T&mK!41hv0s2=oETi| zatsW5;AdL8`9VKhcY=_zDBX?ZIu;8DfH2m5HWrLg-d>%InB`W7fzTgW6%O$jW6ilg1MYN?XVw~0e&iji!QDx8?Hw-0+ zuZUVzk`JFiQ#cW?;Lxo-K|pZF=)g_QQ2fZXkBB>;doV&?6}wRr7H&hl0;Bl+2BNZM z)=PzrgzwNn=Jd^oQE5z`2C#XB=pT2N`x`ODt8dHGK2&$w!-AQg?{qI3YFN0>!1wJT zz6>YKG9U1TPBAq$iQal}QP!1l;*}9;#gzBCKw%h8N+7~$kkr4P*QtU9Zv?pT*zk-6 zDwyxZwpm#FQ63KVXfy$c3==y;IdP4kSNYQS~6(*E03;*Ip z#R#F)ZkeM*e?HUHwEQ!Gcg4ynO=Kc%MUOv|s>Be1uak##Os9XZ3_<+HK40!thjvze z01L2}CLn##`k59Vf|>!LNPbNfi{dA|0scl9+Z{j&so&p*qbY=t0B(%n^IqFN-U|6C zy5a5Hwo%NaP1OZWe}(}1pA^lvT2R%{DRC=L0!rR=z0U3pV-3amx{p0i#WPkOy>5L) z$l(Yncn*vyL}-Vd?Z`K34K#9{a~?p`G-@?tAaMF(RG$L#RRu21=o$I~yyZ}`orX13 zSV;Esa{N5sh4L-s;K*%%GPIAc1(CEQ*zRf^dcq{J@O+W|tdd8_TsqV;XjTgIY#!$l z`l0-qx+M%@{Y1DOO>rMHmozEvjQNOWqp;?WxsL9-$?Fa!HHVUIY|S)Q+S^$QR4c&( z#v}THZmgHCz$Qy4Odg11XKv^IFFhIM?0l zO9!|n6)}hfJ}Ov)#_CPQH4u}D*&jTp?9&$9Obh zTKq$yu`-g@TTT=<@D6?bTS;$FGFIcd>C+dV1H7=BOh)Dc)Qy6x2KPm6K0E5oTS4v< zhLsx;cM-##P{>#a}OtCs7+F{Z#$Ww_Zut-(trFrbxLe_fs!9o6|3 z37jfNz?Mr*nf3Kt1A0_QTJAsJRcg2%R6_?nz1^S^K&~ZH0`n=H*1BG4^i|B()0&QOKw22p{7k(h=qULgTkVSZezWwt9w(}WQ^dN1&vGGmm=TZyWitx%|O{!UO2KLs}Qv+^Ma5Jc4trlcTj3@E6_(@0_dw3b?tlhkCmQUqJB~U!}nM9-HS{a6| zsR{kqy)x3h8}hZ!dCUy8g2ou=M*E2;-*Y^e@DxE2XxIuuQ) z-dm|FcFt5a_k`a5)`8i|t^Q>aj$rd~!=P9JHk}e|o=7Cx^teM}ca0aZ);n`|P7tS_ zZSpA=Zk6tbm+=c%41E^visYkyX>>?%k?USQWXO>?BuxQJfWf)y#Z-RiCG;xrrf?iV z&-E8HI(#Va%wzo6IfMXuV(+o(2zs~`i5T!u57;n+g>}%gz`OC#N>*B)3+KkTdyf^l z1Zv->h~d7Y31*Ls8t{FP^FgB(G=2pRkPw33h!Cny8dhGpZQJWPk5lr&n@#rqV1^=` zLMka9u>cce6dmz!?2e^j72278DHn%FmOuuE^t0WCT%8}jMhJNQV($Cjv7&5o{6p`%|BC+}<09gc|0Qj<75{eq>#;=+)G^fKQL4LgT#3Yl*o`>xaJQ zHLvNn_UHQqx477>k!7*QdU;`DyL&cVezYv+nwx>{^#oI$>1k)r8ITmRZNPWhm_R%d zQvD#EL+<@n&}NJ6cnqsDv9yvvK-{EXEPq8mH(Gl;ERkV~p3%WUIV&{b;*OixC!|(3 zFc28__Z`Ql`nq+)*``v`FU}KDFj$J>1jTGlBqFLv3o5e+Te%VG3p zR9yPpn81TDT+oN<9mAomswXb7!XJ4CEycu39WA(;>l~A8UsgAhE8(E7|G`dXekQ2Z z(zfNTE)UO(oom)Zs|k4=V2W>zt=z57H~1y9ML%f9jqsPFRhkou%M;*{gEiAK)IVly zv%At2hCkTlzQ7X{>{)>m>`Q9P8>9`1-@=P>RyIUa*PN3mHOQm(rLAPBfEb~mr1IIZ zQ3On6VPG+*QC79#L;x3bYv|~`?aVC?PD6ZL_Acj6g4}g(ul66*xWw zKZjN{*k}Vaj>Xkl>lI}iHW-b+m1CAGS~-5*z3iQd&{Z}c`gRm=-d!Q8Y^jsh5`2){ zqq*ATS)(#N86^U2mtjccXvCB=h9pu?7P!+t{!FpC@W1`p$6*y9=@U~>Z*%<|+^AC@ zYJOAO5|rIX)W(JmIxOA&At_4(s*tQlf&ulGz%Gp@r?qjf#{Qjb`Uv#xe7ralXU_(~l< z=k~%NHU}MRP5`}EXaGWmV9#Fq|}> zGF39mqytvfcy9=#tR$B?KhJgG>WWcr60m#BJP ze{Oth&4O(q)`#z#WT^830en5S0{OHsp1c;uj@H&2M3S$u7jr*8s&Q>XPlqZ>KH47ml}}K zV#^)67{tH^Mj{Oh>sCX9r$~F{-1~zM=OPkA0yE&ew>Z$vBiN#y!|RJ=ZX4xRBsX6!7Av4B3m=BN6R{T5Im4sEwv48nl4jXO^y8LSGOLbguS& z_lNUf!#6}ah@FpGtnC=aBsh#XD>6Yc(M_lY*An~jYcw9MPY&iCo1mnLk_wCZKB~Ir z!<@QnWJmaz4}_mU1V()SK4L{x-LLHM1Exb^P=+a`bsJLF7fNK{FvJ)p6n zNEe(|^}@zd&sbQyQX~^1u2JG|s4`hR`LS{4I>`l`7Pc<*J@yz&7R?=mxoG3geH7*I zma<=P*OVbUHLDXuT9n{0fEFyR4O{s{wdTEIMkm-_dQgkUSKWWb-lAN`i9|#s15n|O zU(d54UL+ce(&r7KJkB}x8>0L3ixD4Yq=^qv-CxyE=lIrMf)m72dDl?}AT#j5(a#)8 zKP0ve(U(JW6GmbHM~|b%iW#No6OY37QzFJwHhWw8qz8uK8M#U(lgg`rJTVF4x(TIe zPj_H`M+6z^W6s+;-9A>cv~m2_9hD+XBT-Sz*oavfeH-7*S;))m2m3E{LeuO>)koxE z0BKl)EDVqZr%7R4$BpXGGBB}Wl;Gf0IYtV{`lCCz8nyKQP|6saM~Jx^?02{jI&-> z!4X^kmWA)owiYCvmt0kSwM!U(#ec?1zojdFrVCb2f5|L1c0+7RYode(0U;^Ke}B^#XNs$qqkw85^3Q9He$I=m__Vw^Qa{;^~K3<8cj6>flJto)mEx6t7% z@c`clCb}Xg+esi*@7B|KX54bA3idqwPhbJd{`E#ex)W<2e8+##?4G=AZAFyhMq@2p z`;bFQ@u2pJz;dp+tNMeRxqTk~^P6|Y;ggD&G4I#&Ei*+$1c3QmV3(!xe3-|ea^+p6 z%b6H@M$@FneAnzBE_#|i$d1kPQ+OfXf|0FUL!=Kd0I2|)wHYtD+wfUDv3~iAQ93rR zsfvhd*1k3n{Pvpeu%Yx6<04q{MMBH6(x~ShaAR2V9B?tAyCfgFwHkJMW6aSF#Ut|d z2371`f<3c(B?Oy`N7o|tj6xtcFaR33FB6kk<-O3RWe%lE9q^8hmO zlF=U@>R!7X;3H=?cozEtKFABp&8y9S(t#V^$QzBzX8if*dYKfr?}RaP`xs9hMJyV8 z_Nl|_)mtvLwM1pmfqcTHp#16HvBO+*D@^@Q93Hto>On2Rsi5jBqXUKm-9%sCrGOKp z^qJ8=ay6A7#aDK2$hC0a?vFLKqDOXe<;@Gv;bZ12QbV|oLNv4<68wSiOm}H!z~5N^ zU0465>(fA*!{WBENj{Ee&hMm?Ynmh4>Xx|1Gj%e7#X|_us9O3C7o~-&BCN}^CH4|x zXw}6~Q$rrKcvhN#k9ViS4`}`e^FVdmFhq`@2-2h4v(bxutGm5sW@kpo=h2DfGMt=fxAHF z*ccD?%ZYSKpdGD)c}$T&b(SH?;>)hy&GGwojA?wujh<2aTthEj(TFb+>t`YhTV7G| zn6#pmcb8n@qu4mW*wncCKsRr!~M20GyaW&yb;*K&kjD!jXO6KI4dN1B1>BqTg@;~~GyOoz%F@BI4qw&}Q16*0-aPZ3 zQZ%QjOJR{CK97Jdm8E?qF1Vc&qQ`oS#h*Ap-6q_QmD=gxeYzYxTQ^afwT6fjofJ}P z)MFBLx>{_Dd~f)Jo4mH~1DkIn!cWn2xK*0@0$4_ZuOKvO7pZm_5eq3ZZY!6vC#Awu z@`S)eZL)DTxXZ`sgnk^` zHzJddV$j~R@fzwJO&yGM;(kX{Mi1WYlsw*DNjA0TWH`r3ra7C!&(ON`iQ4|u$MSd| zhh74ILGQR}y4t$t7q)Tm){!Q@e?K7rNVx0x;<*$|`RWpk$ZuMEUtPF#Ck(hVW8L-6 zKvG=GWq8G9&d!~g(&EkIO!uwVlcmD*`?Hnd7e)+&86D>uwMxUy&04crtg5OiKk)vFj_W@0yur;d@2T|n(}dPriWx|L znh)RkOvl;50P8Sn8LR)M^hmeI5%4Oz{cLC47lGJ~skDiRRVk(cU7XN9j&y;*vgs-B z{pEUqSAM#S$z@7O-CBqbtRrk5S?~o=!VX^wysICXn|mRc)^RNzZ(r!$y2xJzqqOd0uj2S2_VQ1()YQfdRga+f~2zvncUHKw#6iSV|eJYdXyaB|(My zlWu|z8#6m->cx|0Y z*9t&e8ns0utd09!*))G1++Pr zJXihkK8eC~;U2<10Oj7O)H_n2ZoGL`fV8n&tb|@22uM6)qoRFfiRj2WWHFy(Yab=6 z!eBcAET5W!N6UICsBV?#sfvF_J8VAy{0;RHihBYWYr~2Q!@U+NDUt0S$ z0Y!a=uE(z<$Q`K+cy;twByv!c4t&wM-vEwAg{SRS&Kh{qfgwPrqU~NyDjC2a)>vdHJE1(a8t@`qWEQ4wGDif>%+m13BC z_~=`^f0);pd1mAuf(D1qwSn1=l+XKcd+MQhC)>KscNCps*`j7;L6RE1`WAZq$cKmZ zBLUS{F>?NgtKkb*O?DM%g1VB9S)&YdLsU-Fw#tV{AY0t*F$`qCpX~#NWN7$U-13+( z79o}n^{;$k)dSxvr?OtwHEBdgQ$fpnM-1hi9O%Q`2z~_ZBFJbX3DWCCBW* zV^wUQ4FSrnIZ9LU)BVlQ7_?aALz6&!q#>-fJ9uGJj9GJT9mIec9 z-bkoVvj-?ZNIkFS`#Jp@*m}gFM)%Kw^OM`nT>7;s2sj^+c^Nz00DDY}ySdyQ`%_>K zdMe1)=uVmmyWCQff1iv9FhnFtUTguVEw$$rJ!Kx>5y4^(0`EFswbzy&6t+_OcwzCv z1R2&c0@U-<=M&o4LV`#{(61~1=fgSc5&*{Y1@RS3h+s2GCRv=ztiqg}sNhq1SP)`? z7cie95O6kw`RU_@?9^;VeUSgF`4m>E4ValS0K@Ti5fX%J(yEC&SNzpPgrRwy4STZwh~jfQ80=ob5YU9B9!@Elhdn(ggb4JEg}a$)f~d-ZU^FULbi!K=^cj{FukD zCMF=^aRqRpfsqy90mAnTq#y#nnn5I)Fpp~iP-P-cpa8;uR^(P?{AvO!fd-VM_G>=7 zaDedtuhV-oB_&>+!5$#E&D~ehpr~tlgbkU0r@M}h1xbDtV^&xmG4;Wbg%C6=OScFL z#)&tKPK?Mkv67l}OHgV9UL8!TBKQ{d1{jgMP8VVwX;CdMdhdK-!H*?LkDKw+d%fi@ ztUsd^r10rF4WrKEjJh{SzadI&0nNJ;n)17o;>O^|7`18o@=+d%BKxq;WbqY}SRXA) ziZ_|9I3w+tazPGd_gv8*TjOW8@XBQOUDVEa!adjqqGuLtM;Q_8;YBlv5j$c0JTAZN zp+^XgG{39Ba96$vaqe(;mG8Gw-{KcSEC$jh9!mF|J z_xj_sAc6!PHKg_>uL_4S{ZZmNNqDk(I&ME{!=Lc8*DIAi9iim9%)vb?0qqQG%jgN5 zulb;@Bjx;pWRt0agKb5{Wa3qy43Wp6sg8XYph07TT}%6I6*JawoK^jg|L?q05b>AySlZ)(F;E|i3GiP zR+jT{!MQ`^SwN0nk4m;&o?9GE<|fN%XYRu(z*w=R6a91e6N4Ele!SjhSonLgycd;3 z!|=~-nF;l&>Q@9lS#T%|3263OIBC}PN~vN9%dY6f_|-UkW#qSrCx1pnTJ9VurMoYU zG&cC0S7f3VmvilYdl8HGgcxhMN{TXQmD9_`nl6zW2}>=A^ORF+N0_LUc`AHaRdRm* zeQo>^6JYvBOeqb*0&MW_vPa~p!asb{{Lbz_Z%lEJ+V?#|!j%Jg>h7)XuwXCxeH=Mn zTEM80(ycrO43)Yzw(}EE!xkyx96^0e!>p>ehpp!7H;otNj0@5|8fxC)ki;1~u&-|v z?$yBVb`FZvZRGSD5fCLI=E_9T6Y`qj;F!elx-523gYk!s{uuWSR;(T+MM~ewrx8_p z)Sdenemp3nVk|*MR!nUSz0e+pv>{ACMcz(lBE|&)-Wx1fm?GkJI+ux-FZXIN;RkLUDN@MmkDp7tbj#}$my2Hc zm=VfrkK2ILV;%lAoXbMu!`Yw*6zW+IA~!l#IY~Inz;O1fDKFo}HY1a758iXanxIS= zt%MZw@7#o)1ep?bn269U*%l@-PtAhZjkpSmmFsL_GO*8Nk*DIRa!&IuBwbbyF=@j% zxdsjrTE5N_yJN$XjhMkoEg`Tc0nU6CwpTEBfoAqnn zHp~z<@iL7F&+FJ`dnqtG)>w(q)9=JUn7-?fYGy7as5+o)eoAK5I`+)Bt*EK@VxU}|ftON4n_a%VcSjQ&D+bH2>J)Gw+PnKUE%;|AN= z$BUknQO?(C2uWt|*XW;rHkF6H4hHN}0D69P(RyAT6=rGHzy3G^-L}!*3xZ`!g?w1| z=Afc7XM2c^Y_LLXfq%qs*AEo#ue;NkT8?cQYnX*?oA)%#l0ogb)t**WOfTz)_l$i0 z-j&6UR#H2=S6t`(hZW;g#aDIVBfOVNRoL${5pl$zu#h6)r-|rHe0Z% z9u{rPX@oy!$$mT4=_0$2U-NXUxiHni`s-j5wq0xu=xD5aS>1Q~bycquE)Ao8cBN(H z)?336{Q0i1hFbVBYtMYU)~{E2vKpNOZ{RNid*0`j!=Z%;0(C*qM|c{$(y+SW#aP7E zq2$){HY3m{vS7*e7V z8kbU=<-=Qdb?++X)r@6P#mS3Y;8y_inO=C7OJTWX<|EYfYmZbF8}NT}{)73wX9Pd8c~KZ?r%8 zeA8Xty3MY6v!s7gD%YMDi&D~Z@P7IkjmkjVvZslFb)0NOn zEX8NNQxQ3A`w{a`dQeN-{J$YKH7$-5(D)}ADBp#>uLz@@35y!EFg zH$ZKX%c`;!Qmf-u9L1gCG|_XcuLEmxnJqHH*J0bXR00z@LjleM9__h9@X#%-dbf1J z!*lNxc;tML-=@(#{~FC9Rv=4#w}VBK_0;l9P+r(x{vvg5|2RT>LxA;AvSs9Ty@}Zr zL!QUPa0$pP#d#V{rJEadpb@TbKi*=eVGm4B6Gl3ntJ5eh21pTul=Wr_(I7*eVQ6P}m}k-M=akQHJzuz|9LO5m z?uA}+@n~82698467!11nuC4l7E}b9$MICxXFiuwrbd~UmRL?0?SQ1A$CZmMId&3J( zaV86Crw^1Skho8482Y98h6n5CXI>8ter)G6o2tEXXlxjXG+?OXX2@Gac6d&pUg8?= zBxAAF*IGCCf)<8?EK@~RGC-2h71N8wekJL|qA4XsSh)~WHcj3u?V8j9M1)-NPL67* zd4C^suRL6XC876@Ls@-eKK%JMew4KbLqt5Qc0rgAAsXDB73zNXtJ_JV?4@KC*c!k! zFTtb=fm(?2&#$f(Jvg%z+Jor*XNO$Zk-AVVBs5w~vWqktMIGiLiD714Qkh}zb?;Uu zkh+$T8+K6M#cZlx#*yj1M-DA2jCvU6tb9-Mu#o&QVal;@pe9b*xEs5mDLSS7dV^)@ zp}-jL=jkp<(x$g5D|*1AcLF=Wl3d%wBFO7PJ!RBUlya5igIHUklj_Kpm>a|PLg^Bj zK(l+2nvNnj=`y#a9M|4u((}91HXV0OK5WH-=9;f8^G3?S@XLndVJ$yaxa;?`?7Mlw z@7?S!p8hzxiDV;oMobAp2BX}br>=v|cAlp6(D3C2VWIkF@VtflUcXqMA^WYP@uy`lMCP z1$E^-(Mh1QdHC_bW^=imh=AK#C@?#q zeiHIe_tgu63JjA#jJQhXW;O8BI$ct0dO=R>$a`i|DmHDo6Vu8YJe;vAf-J_BCb%73 z_pZCCBGr|&9bIJ&X_xQIytPq|;!-NYsblk8x~pF1wyES@llEZkICF9>xaAWE7`ax@ z0pq(eTI*tluR2al8bX}j{g3<~N6_T%3Im@UMp5nAn1I1Y9L zeoyvd!|nQ+b?YEZ+WKb{Kd5X|4~1g+m4-oUZ-0M{Pc_C%UyMR7G30;rO?B$?h8F_c z2^9=C%n{Xx-9yYaUa>v40hI)yEUVp4nzZ`o!73%XhFdFF&C6(GV~KI@+cPQ?0ofM$ zpUs|xzEw5K?yDKJP*31Ju^%VZp{qE$6g#!nQ?~wJ?VaUcR9)2f0V!!@kOl!M0coT` zX^D%_3zP-{=`M+(JEUt!X{2$6?h+XkQEFfqYAAsrq~ktwJ#X&M^ZX0X%k!Dp=j^@D z+H1%9uHRvgYG65dR?lwqTVfk#G=_d+qUg0m>oCo=@Fz}%sK*mjB=bGM3EP`W41pG3 zclAd1sBsmNr146{epCt{vu46b)B_@`Dn+gpj^`LW*Ezb@zeZan-EYUNziCD|%R$`~ zhV6ERt-JMTIahT$eI#_EUbsZ}K>wQ3`2O`gU+0!~+Q;Y(1yCQC6_{+)P3M(ISS)>Y zYN)JlYEdB=_<;P{!lhsp-QxIPb;w{^ss9-o=a2U1>rMRbM#OAe zVFsCuiB&&~c1Gwy-h+nr$_fz(oV)8Wr=y{7ySCdsHnwP$F5v`=j%T;fU-9&&Cv$~Gzsb!2N{7zE}1txl3$TFjAaS-WWm1>=t59kf{lh@ zjefXaq0qL?q=?xMaJ&*2J2O$f-N{eEDSVVc3#NMG!>`iyi$}D?8W_e07i#HhLwx8l zj7h4v%yWAr@24*eVfI|R2UqC%t$Vm&7QB$Vw+KS*kYX)y6_=^5fENj}6n7Y6 zy3dJZPMWqm2CYMAtn($UU3Z%&*5AZQP8Z87wTX@hs5cH#BC|!Q(iHoF#5H zvdc)*Pz&aOHY%8LlI+;ONn&?AKGqg9&fgB7EjKMW>rLsXCtqVZ)Uy1(%PgVuQ@wlE zNjp7W=?>dpZ00*GsyGLP*-i9Ou>EiZ$wj12lM+G1gO1cz8e7h`R~i;vW`!;Rz7`O( zg(2T3BVV>N#7>@zQRBkDc}YMQ>6{GWwRh&b) zz8`AVcc$6;JK6!rBHU%BsCKu58v0cf+_W3Jz{rRppHSA#9x9BPQU)Gki68&R<(}(5 z{QDn?F~0F09A1+{uU>|Y?jE}`JXZR5qbf)D^I$9OQdLz8MQ!ff3}sY-?WCQ}^wUMY z9rIL^B$_?beCJuxLa?zpyNR+WU#=Nt)`*`Os!lIGzON7HA~t&0xBT%qh1?m`Icm89 zPh4HmuSir1*g-R6OOHzm@SNy4Cy~5}xX$I|+D&VthiEes0aSl+<;9O-<8JUqY|9&5 zBWt?Z&)>wL>~VoKot1(aXz)#5a7rTolrc3uJ>DhnO`rSp+bF8o=WV0=5rG4T?Ux(9 zeXnIgphzRpvO&41JkoD`tpYZMW5M?YCOe{64e zWa@A+3_S933w#%6>Y{;%l* z@(v!vyCn%9o6B{V%-@$dAn$ZQB$)Y(gjmu{=@FPbXpoxNZ_O76?+`9}NXvAn!TIs- zsR4_Oz8?<5rNnweqe3G=QcqobFv^KTh?SSNC#sna&+uTr9a2Slk@ER`UWOt-g~3gn z0khFJ#StD&aN%)MyMll$({yUXxo9_`SJ@kd@&(!&cAN6$8+T@voNjEgI!4%|oA|z} zq>~qYLG2-m6MrQWZ3;vewpzFx$^VT5^`qhKZ5U z{nTU2;b%}k>~$9lFQXANw#Ldel^uTmzVn`!h*T@#1ib6^bd z6IV#S#>b5}W@H1;U0ElNosF!W+3~tYL41m1yVX`(-;{edIUrkN!oPFN1r~zq0}M0o zoYMj4BIqef1*NS^2&(ZZ8&VAUQkER-R+w)G3UdUuVdmtozQ6~FALpr{i+DsY>T(QL zHt1?AbluIENm^)ZlY8bL-~N^GSzY)hy?JSAME!8yZlKHI*{t__{@0EAv*IaW^cP_o zzpN9wI}?LWp|5o&cl>y-0o&*r$8kEQ#y>AZPx}7}nT!!J9ggbGd9B5X1UYiXRjK&A zTWGmh-TNiEV{?@1^Fb%iQ6PEYI;gDUfT@Fhr6X^sfcvohN^3eB%pMmfR{BMvf%p|U zZZ~PU;}6F{Rce$=G>u2T6(?&ic->&pE>@`TST^I8`mNVE{SzLzz)4oA;ZtYM2il!n zbC85hHtxYluNPz{j0X~_5lC*G%@78dj;ABmKlu}H5eH!!XCyY*adwg*36y%bF&fA3 z7E1;ag7s8*esQC>@-yWEb1HJ9eD6=62vNz+n&ua#7>(FzXEoVBLx!+!k5q8S6`kli zTxNfJ-6bz2A?*(wr%M2dC4_I_Prj#c_f!;(Gm(&< zhV1eytTE6b%>aUsb!a9zc8YY3NVx1lBNbru%bA1Ua3vpd+AN1y*!{xN=nW|EgLZ2}&Yz~O)i1*L5dPf~ z(vFZa#o&m?4^ft%o>S9X0~Lz#zqhp`kN?2`g9j`HWoxW%nM2xXXi-z887PS!^lTtk zj9bHrLR^d87lH?YoiRGe%!1@aU1|?n9yY!vkMH{kY_t^C^Oa;j#)MKIrmQTjCj8N0 z2Bv|9AtdN!RbPPM$)r@1Z@5lM$bx}?hv?rDAua1k)^3DR0*9~)wr8m^9SaInDALB$ zKJS-|FowK&kZytTumndUSZ(6!J?GnXQmDhHO3m5UYJ*}54AXBh}e#2;Mqi4)04oMlCtM=6iar7<{q|g&% zc3oMxk`C^~FN-O+*OjL3zC;4sSV0%^JuL-12xZX?tDJ}!E#-J4as11}JvR&iqQOml zUac{h4H06f{HY9ih%3jc*xl>&3ao0WhkZ0C$A{~l+M}ei*zdvMcZ}t1b+k0j$;}1t z>E1t$tInTv=zKY-fACbVhFi5*5-8A+v{&5C8y$Kv!i~%qCGjgq(LT9H;`+9XvY%pj z@p8yxrFmtD()uauStW38;RphZgmFoDL69=+-KVk7V#3y}kIZiL+ni?N+7ldLA$${l z_I__7J9R0%kia%gBCZeTEH>|2aG`pS7T4=J4JW4QYQ1Mm?Bg=m7 znZ}L8EB&wkE0NKl)()kil3n4F4sOTaytX*khVq(7d*Jibqv?E_EFU-u@itnn0r$Ru zbw5y8%ITzfXwf^ww(*3=iDBQono6V2!@F~By>nBl)97UCHLfqc3Le{o_1g=oKmlJ^ z(y2f^&VdF)imXA|ZzLV74{utf#-}lX9!pt2let)jymf^bji7U0qf)>pPh+D6C+Mlk z4FHn$JN8f2BwEF@+5Yvnm&j^?^f$9~*^++`%d|K-5ledSOE@2%M55Am;B)dj;=Kim zNF1aN2My)$+I~=3&kz;$pT;cve^C7e>xpJu3iejUpYN*{&rky|@ULLmO~Rv4zqG`W zt2QPKAGD20R^2q-R=!{Im~WEu)cXlnQxuVgK-B1&1TUTsHl9)7%)3{i8cHX8;eXTC zEu3T!9(q-}7x8}YT7@xvFUR7agE|<+C6z^3==!^k=G(FbyB5^t{ zMX6Btr0T>Tq4caNs;?N*+U{f}t!jqeVtkc>6Ezm>$cSOm>2#(p4t13wgx+z2A+Gfv(l4)JJj zzcvHv-VLlV&D~72uR2}uI}obo=S^eHMVjbbiRKHxy|e`vA48S2SVxI7K(I?oUd|hAc}NQ^mHXzcK@T|^$KN4onH7&zI-4Q5_~#u<>BWC zTlD-Ucr-vNG^Vf4k~!un*(n2fwQAy z8dimeHSiC6iyb^o{}bllL5{E#%`QL-;i%T)<|Ty`3TGaCcLP^e(m$&+fsH3UL-M+~ zxE$^K^~%0=KDw?O+WN{X^VW^Yl{+Gm`Y&)Iyah4UlH2k zJTDq>eK@*NU0IkmYaR%1`=|xz%+(4QH!4+V=W+yPDm9@lFXwDha+kH3=Xh>=3Vav7 zwu<@e{8B*h&OjIVV{zElr-Z$UKN~l3gt{ME)!_U?L`b!b0*a77((Mnq+M~0K5wWEz zKy@zPxHcsFdVAvo%6L(S{iFB8*OF!Hvnf@ibDp^s9y++XQasA(4sw5V{}FLNdkpgf z<_}vX0Jt1ZD=sOS*D)6s0njG0p>ycpF;#?$w}&amJI&-XMREbn)!&w%*-hl20Nw{) z+X)C&pvuy9MSFwS$5Q)+z!%@TJQZ62aS0Sn(YZ=r2vy2%ednamB(SJ#Yt$k3alhEe zCX)I=K=lQM3_Kw3x&7NfM8xWEiDh3*y{A+0O+XW~+T|!%`=vJ_GytH3FNKovqPK6EM*+!d_iry}dkMi;h-GbU({?1l zUyRMY-#kJ^P!bMJQc~@sZm$lWA)Hr-;_S?%F!)p}s3~0~tTSDq=(JNwQFouW+;{Kx1VVb))uY!mHH)zge zP>%yeD&_ZUT31%^4~3dfzrDh7uAZDOK=3q9a6REd)yvZvY6G^w!08FHAh{b=IBg2u z<>$hLVxokC*!y_D6YrO*CWM2p*Vwvi3?h+-$$t1izNA=h=73UaCH=(;0C9;E(X*nk zgbSy@GLUi?lr0RG*dyN~PV(w;*v2hdfxlX>CgTG=vQ{W-xG$3=I{7#Ifi=1=I*xExORhM=1W z$AnB&IE_22^M2Ryq#WI9;L%r}{D@_MV}Te|$llkv$kDTkoq)%^E$PPU6!OMBYbT2n8$M7UUl3zY0K zPr;^)hhCE;(juqlXq-Q)@+LTnRH#eOz2Xs&;#VlWH##;4DfQZkaghUvD7zXvK)6da zf929cF9U#RzG~AnnK$+EMGjKPPG~P(-=6i-C7Rm;Yze`VbZ{s&G-!k0Lc~z#AaMCg zyBn*-MNlb1B59z?S(z1bKFCHw(dBu2u}Ml1sX99%YMpei(i9M0Q3Z=JSq1_sFKLPN zA_HaZf>IkXEjSm8n+o<7CGqtdq&3OF1o@l&X)pok{$zr68C;CBpLcxX>=tkgVtD7@ z!MD@Ie49--eIK5>--&&~~3jY+x(+!gafUu2dCRZwLFsLTY{u9P) z^A^a%bdD_YDx=3GRZ9s7i4rw^HXMGuD`)8u;s+$zRtSN#wBvFjm>TZ6_jENV1qtuj zT>d~Jk(*%=ZSIAT<&pJ%JCiUM!$-@vr?C8pae&xz#V;TVw{CN-g}==3T7ndDCwJBV zG;&w?9Kl!CV;U7flK2xY!D#N(&k)9-fdza9cG($$Nz_@JTsbyWF=}9&(9}}=I38lM zUi}n*3i86np42@|G_()>H>+|Ua<*GM2-k&j+lM&N(}$;+6zP8>u$x%sg#L>U<3mB???3z zro4IuJQ*VM;KVD_dj!{5@~u?!@Y9$9PP_1TQeTk0xpxOZyOBVLSqi*TLDZA%E#xyk zqzL+FuBM@#oOty@j8b&h(?z%94TZb+5Jz;2O;b)!^NDEvQh(J=!-KDU_%gaIw}>Vy zkF4qUorwt+GYu^R`lpKg&Ir1GsG1pNhd`F)&ruQZVMB-}zQyIw> zukACF#N7vp9-l5>qn|~%1+6iS=rRp6*;5BkreEQO5K;VjiaF~6I5%{$jPD;~vA*I) zCwfNw6a5dolX`@cW}dp)m4{P(d}PQ~!V;0n^Qog30mR9cL}Jrqa+%TY`3?-a_8`*Y z!uDaR7p-syoYOcM@?x6&njBKQ*>wZObF&>Blktvtlymz+c%m-NxwoOO$5SS1nzyT< zE~Azm9$0}}%*tYoQ2z14mxQC0gplMl-)0RTh-FzDNf9fSX4jiQOMD@{CeqyIxe zwFAqj?1bVOqyNgrVz-YRD4#F?zaSPn`@cKq{7$tQG)~I*X&3wL4oy{EmHHQ!;r|24 C+O`Y; literal 5278 zcmcIoc{tSF+n>>(2tBeCSsEph8DlrhASC-TvM&wB&S+-rN+^S(P1(lINQ))ex8%u^ zOla&&b|Hx@S>hd@o*r-S^Sj=^e&4yyx$p0NKA-zO=RWs2*Y%w!Lw!vaCLSgL0KlSk zQQep(OKDB_AOo#`wXPRL6F|JN=6OIq<-`o_r4eJU<)o(vI7`zH0vLhpfCIY_+7AHa z0nqR10DwJ^_m}Pl6#0pvqhVkGS`Xl)H5^cKPv@ZNkwjMj=qHy%le_A<-R^H`=<1L0 z@I(`^I6*@kR!|Be2Y~?qk`j_II7|XAAukA%-BaWMfCK5kf98}<_sef-`hj1%?rwzm z__7L`IOKiN0uKN{gmxRyKS$XY000qPP0R`Adb)52tS1ERh_%N+{5`#QSpX$}I8F7$ z5YU4Do*rI!xIY5Chk(=cT{9FcxCbG)Bf#c*hJtEX97a$UA_0*ABbfvR1(k4)PH1oYo%w8j3K z-~VFyqrLwliQww=A7br}{70Ij!>>@@J~)rPL^wJ?F&-FCj28h<{Aq4!G3T-}xC5l_n+5+*PCll1$@zcMZUVamzwC+-{A zN2FyBu7<w)~YLRNzQj{41k9@f#-DL@@fz#w-^UK}PNE+KF7cgg=n--r2~ zO{+;qGy(lLUq&1z`I9dp4VRP=mym)>Nc{A&AG^=b?ykX&F?g(p&u*z*@^U30X=(d6 z^Y1J*tfw~)gWtk-*5XJZ~o7;kWvzR4-~d5escGZ z|IW4ef4KX{_i-H^;CLTw0|;*KY-pmac>^kfv=BPm^0^S9~k1ZM?dohoq!tgC>zS+t(_E?b2GxWW74t3kQS# zoZMRcIQwOTRjJXyN3q$8N_go<6{=v7)DC(qYM4XkI*7mRz8cgcowIe?@AH*Myhxlu zPd}mX+t9J;=?mFP%9E4dM5mYXJSgJ%S8T}7J}ZAuoWd^|OuAb&ZnW81&-lz)O|Q)N z#n&o7eG%gB#pH2>zO%w99v@@dkNx7SyMS(BJW`%8hq!HgW-8j>i z6k_Z=@uXE0M8lEPN@4W(qL^sigJGgw<#u#R?E=DDR%5)1Z!NwSFL zWB(2h2+3KH#G8e@m@;imq)WC9B?L~RnXCDS?1kzv>q)2N!_+nJB{*oZ@+p*eZh`9{ ztAN%dx@7B6lgf{e5dAT9?`?+eFG?kvzJ{UU&~8UFOmHpzL~QA&wvV?J=U-ALh$DM9e0a`y2FSyqvFl# zyd!VI@p2ZOx@qr7;Czt(XDFKh$*-?V9;;Jd zv0}v%Yi(vV4jBk9pr35daA3qYSBa`xZaCOM7Wo1c$4Bd-52du68^(Oim~R44<=$MG z9k*zXz?>%5Hx~wrxh}=O|B=VbXA6D!DY4)@`WBbvkBDsdlUB=k&Ju%Ho1A*1ua<3E5CNg~vTH00mJ~({ri?_0i z`2A8DPU36DufAjU+;4MF&ED5o`N8vPJ9}n3BRyaqq^@0dL|ShBfO2e{^h~->Ol_f| za;H@w^?)YJbSGp1>La?qLf{W)y@^!|TPWy2MD}XC9*4hpp=yyx7fm&IT6z0wvO9Nt zdH$P?GuEu%*ge1VznHJJYF1{u(uBORlqa~!c#1EG!zX8>3y~E1rGrjHps&L5)Me4l zIOc`(6TKS^>jZha(r~ep+e*=)u0XbM-pj0U_|F~IJ@OOR1?t(`?OEwou1YB#x`Gg4 zATKsAO7D!L*-y=HnplH6YZDyU$lJc z)a>p#94!hLB0f3`e3Q7vr0`ITuY~9#&nPXbaf444?3%j`q&954=m}_#i6XM(Rx49q zGkz6z8EBlBWg@-l*BzTbWT}+cs4XGlcE(7Ir~`S-vj*}6wAGMeCNjY@#_}u^=HltF z1v*^0d&cq9o5rQ$++RYA(kt?zs5;Fie&ME0ryz!x7LJxpje=!U4MIW3w+?U9huknw z^^^-28pVA&Fs{<0-PdX@FIL_A(8N8E@l)!p55oyM#oNqh3~;tzW)_Tj6$L%EJ;-( z;urgGMn#{DD>?E)Se}vV>0JrFc#*>ghx<{f7M`+98J@2zmuh&L*6k z=Xh8Gi&ud?kixP>Uq7Q#1Vh=Bv@j#1hPrsBa`dW^-KUzrH03rv)7!zD1Mi80@hpxb zOzo9J%uChwDhIeb9+8Xi#VmB z@=6)QGQ9>PjN2oO>fXqrsJ>9ihEWawlTUQB(~P#IZifP++!x2@I9?u-c_fvg=yCSJ zHt`jgx6Gd@N%qM@YF_*&4m0`l$*iNqMH0N1Usd1pE3NdHGCdKfIx-!r7{!``7Z?sYNpUnzs89;_XFx81j3xI z+8xx=;(gCq!zyDCCH{f;y#Dj)A=YTw9FJq%vW%X96^nN!(A-OkB~TiJXDu&XuO?5o zk_|{-VD|R#`Pg-aV9sTRg~*lc4*6rC2WX3tUPsbe4Uvnzr0F^5^B*I(*r7P5oF$(V zSG%WT@_4J`ZQssrpZ)CJ|2CuKYqTz$s24_Gn4z2bc9dhtw0=fH;B22c&lcT8dh`o9 zwVbjCqz$Y$=rizzk++~Vn^XpAWh_%REEPpD#ToHtV=FFLKM)!^$fV*|f4P@~3?R!j z8jF+_P1>}o+^cC*ZaJfuyiEU{Ef`Q29;5;+K2p>In&SK3E45{dtR1O<$F^(M7Y%@Y zk2UiaS8PSi`GeM(Q$%sIcW$3ieNe*y0qP;W4yU(8>q*+?ikQAzhF9?rIPRi`L8$cm zEZL#9<5rinYla;&?+#Vp8GxzkGG@8 zJt3+%^!EzSbH)Uzc_vay$T&C8!S5SqupI-dYOjpKSI<0ftKle*{v2sNmliq55{jB$ zzZ4M?W=EYIpJn*YH4%!G`Sy`|pu3)bJataxd>W7^UAU6_6t`5Zi=sDz<=#w5Ey~;l z{Ln=Um5n$Y%L=W&5GW^2oHUNwP@rFB<{i|81GPO<3oWdcj#ms^=5AM7Igw*Is4Yum zu%sSP#h-I}vLo!mUwlhO#5lKk)#?~4w~9b-0$HIgJipK&F}B2Fg&f2tiRrI-Tk=6= zF1svm>ZKHj>yH0To&ro|t8-dYh3K5}p@x^F4Al5UTDN~5vF8Ic1~<`c(ZEkdCySqw zlR6h8jF5ikZms!x7##qgiIxyBU@(^FEu~ZFnlxm4=poHQ0Cj+?S#gnq?00>sspQdd zZ;mZ)+Z(Y7;2Lm;->BkguTPpk3`S48T#rpKT|}3&$KDJ_&#&a-4kVt?PI5`1FltQN zlZ#lwozsPq+z|!Wx4p$Z4!Ik=+pM3sK>R`Ys##n9ob4DMx6jH^My zVcgrQQJqhZT_I%+s6rtR-6nF?WxUS7kCDJ*AWJhN#&MPYS~`O;S+&zBm0;k+VAjat zPwW~xOR#U2xwz8q^N))q>=}{6NlSw-YlA2|)nnM0`WOvm*2pfmk!bn;7ItgaRB7?o zr@hY}4oy)dKVSW0P#{x7r+=|1`r5>u*oO}5*~}L|@hp&0WaN;( zb0Z!t>@v@hGdXfpMSlZk;?RGj>a~`HjLkB|gZXtAM36FX9Z8ihS-rVnI)w;H@^kG| zyIQ1Kdof3tLr$TKJ%MdOr6>#3RsY8rm+}1A5L&RA>ouW${RvfU4xHbmBKqN((<#Vo zKueKu5~!o@q-me-w(Z3?v)Y)F$Y{%}+A;^{(oX*XtX{WK>9BM_4Gjo7$7gX!#Y|tj z+xSMeGKB^AnQYJU7}YM%245nsYWHguyBfAEuhyJcF5QNIKG^BW?jp|N_()as14H~# zv6$qkCo4z}N!4Sqb}>uaH-;eiTED1|W1CNf3w-+`p4Bc{H8%lkQc0U=YJPa$hG=&@ z7nwT75%dytL{cF~#Mx*7=Xu{jM5cQ}>i$*DtEcB4tmRqjS;c53qw*S`(0`6}dbkle zW#Lxcv677>*ior=ngbWu+7U)09o5<~r`-f{Ca1Wp#rTWD2i(b}D-GV^SaniacF*Q>jqh~-sR$21WF?gvrOm;+-8P<(pEqWS*gd~Ao7Jf#W@8YWYaG(DdUh_= zr>3CyTc&fVMr;Xh$@h%|;)Nr2*UuhCNBB)!y`j__p>*^@^)pmBI(zta<1^rDTom}| z@Z`?HoZ`ep(e@cmk(u7n9il-<5tqKcy2rH}Q~`(+StZvjJ==j%XsscHX!Zy1_)egy zU2|WqXnO^x$U<+bvK%tFPRC8P+9Lp!UVPS&cEkQ)1jYCqotF0Pj_NJf4K1ge3cj@=A{Rlt|6S-DmWhC*e;a(K?Xt6OTFOVKvMZ33{wi|Lg}T**#Q*X~ i->=WgEoHH<%F1?65ie#hUElqSO-n;xJ^%dWu>S!o>GN#>