diff --git a/changelog.mdx b/changelog.mdx index b096829de..5bab3a0dc 100644 --- a/changelog.mdx +++ b/changelog.mdx @@ -923,9 +923,9 @@ Today, we are announcing that advertisers and publishers are now able to add sub To read more about the new subtitles endpoints, please review their respective API reference pages: - * [POST media/subtitles-create](https://developer.x.com/en/docs/x-api/v1/media/upload-media/api-reference/post-media-subtitles-create) + * [POST media/subtitles-create](/x-api/media/create-media-subtitles) - * [POST media/subtitles-delete](https://developer.x.com/en/docs/x-api/v1/media/upload-media/api-reference/post-media-subtitles-delete) + * [POST media/subtitles-delete](/x-api/media/delete-media-subtitles) diff --git a/developer-terms.mdx b/developer-terms.mdx index 7668cfaf8..b5a41067a 100644 --- a/developer-terms.mdx +++ b/developer-terms.mdx @@ -79,4 +79,15 @@ Developer use of X materials and content is subject to and governed by our Devel + +
+ +
+
\ No newline at end of file diff --git a/developer-terms/display-requirements.mdx b/developer-terms/display-requirements.mdx new file mode 100644 index 000000000..903db33eb --- /dev/null +++ b/developer-terms/display-requirements.mdx @@ -0,0 +1,127 @@ +--- +title: "Display requirements: Posts" +keywords: ["developer agreement", "display requirements"] +--- + +## General principles and Post anatomy + +Posts are one of our most visible brand elements, so it’s important that they are presented correctly. You should comply with the display requirements below when you display posts, timelines, and other X content. + +If you follow these guidelines merely to display a post, you may not need to contact X for any additional display or trademark permissions. However, you may still want to submit your proposed use and context for X review. (Note that, in some cases, permission from the original content creator may still be necessary, as X does not provide permission to use third party/user content.) + +The following general principles apply to all display mediums. Please note, medium-specific requirements do apply, and are are outlined in the sections below. + +The image below shows all the main components of a post. + +![Post Anatomy](/images/post_anatomy.png) + +### Do + +- Display real, unmodified posts from real accounts. +- Follow X’s [Terms of Service](https://x.com/tos?lang=en), the [X Brand Assets and Guidelines](https://about.x.com/en_us/company/brand-resources.html), and (if applicable) the [Developer Agreement and Developer Policy](/developer-terms/agreement). +- Display the X logo. + +### Don't + +- Use X content to promote any product or service (e.g., by displaying X content in advertising or otherwise displaying X content to imply endorsement of any product or service) without explicit permission from the user. +- Use X content to imply sponsorship by, endorsement from, or a false association with X. +- Include buttons or icons from other social platforms. +- Modify post text. +- Use mock ups of posts that don’t exist on the platform. + +--- + +## Online display / Mobile, web, and beyond + +We’re committed to bringing the ease and power of X to everyone. To ensure every post is displayed optimally, we strongly encourage using [embedded posts and/or embedded timelines](https://publish.x.com). When you use these products to publish X content, posts and timelines are automatically rendered correctly, including advanced features like photos, video playback, edited posts, and fetching of up-to-date X data without OAuth. If it is not possible for you to use our embedding features, you must follow the requirements below when displaying posts and timelines online. + +![Post Display](/images/post_display.png) + +### Post author + +- The post author’s profile picture, @username, and display name must always be displayed and link to the user’s X profile. +- The post author’s @username must always be displayed with the “@” symbol. +- The post author’s avatar must be positioned to the left of the author’s name and @username—except for posts in languages that read right to left, in which case the author’s avatar must be positioned to the right of the author’s display name and @username. + +### Post text + +- The post text must be displayed on a line below the author’s display name and @username, and may not be altered or modified. +- On touch devices, the white space around the post text and post author must link to the post’s permalink. +- Post entities within the post text must be properly linked to their appropriate home on X. For example: + - User @mentions must link to the mentioned user’s profile. + - Hashtags must link to a X search with the hashtag as the query. + - Links in post text must be displayed using the display_url field in the URL entities API response, and link to the original t.co url field. + +### Timestamp + +- The post timestamp must be displayed and link to the post’s permalink. + +### Post Actions + +- All post Action icons (including reply, repost, and like) must always be visible for the user to interact with the post. These actions must be implemented using [Web Intents](/x-for-websites/web-intents/overview) or with the authenticated X API. In lieu of post Actions, “View on X” may be shown next to the timestamp, linking the user to the post permalink. +- No other social or third party actions may be attached to a post. (e.g., subscribe, comment, like). +- If the post being displayed is a repost, the display name of the user who reposted it and the repost icon must be displayed above or under the post text. e.g., “reposted by Jane Doe”. The display name must link to the profile of the user who reposted it. +- Follow [guidelines for displaying replies](/developer-terms/reply-requirements). + +### Post edits + +- X allows people to edit their posts up to 5 times during the first 30 minutes after posting the original post. When using [embedded posts](/x-for-websites/embedded-posts/overview), the display will appear slightly different depending on whether the post was edited before or after it was embedded. +- When a post is embedded first, and then edited, you must display those edits as they occur on the X platform: +- If you are displaying a previous version of an edited post, you must note below the timestamp that there is a new version of the post, and to the right of that note, provide a link which expands the post’s edit history. +- You must then display the full edit history of the post. + +When a post has been edited first, and then embedded, you must note in the timestamp that the post was edited, and the timestamp must also link to the live post on X. + +### Branding + +- The official X logo must always be reasonably visible and displayed on the upper-right corner of an individual post or directly attached to the timeline (e.g., top of the timeline). The logo should be the same height “x” as in the image. See X’s [brand resources page](https://about.x.com/en_us/company/brand-resources.html) for brand guidelines and assets. + +### Mobile deep linking + +- Deep links from native mobile apps must direct to the native X application. If the native X application is not installed on the device, the link must direct to X.com via a web browser. + +### Timelines + +- All timelines must allow users to view the details of an individual post (e.g., by linking the post timestamp or by linking the entire post area). + +If you plan any advertising near posts, please ensure it complies with the [Developer Policy](/developer-terms/policy). + +If for any reason you cannot comply with one or more of these rules, please contact us directly via our [Policy Support form](https://help.x.com/forms/platform) before displaying X content. For questions or further information about use of the X name and trademarks, please contact trademarks@x.com. + +--- + +## Broadcast display / 15 minutes of fame + +We welcome and encourage the use of X in broadcast media. Our requirements ensure that X users receive proper attribution for their content, and help provide the best experience for your audience. + +![Post Broadcast](/images/post_broadcast.png) + +### Do + +- Show the user’s full name, @username, post text, and profile picture. +- Include the X logo in close proximity to the posts for as long as they appear in your broadcast. Make sure that the X logo is sized similarly to the size in the above image, relative to the content. +- Use the full text of the post. +- When displaying images, the post text must also be included along with the user’s display name, @username, and the X logo. + +### Don't + +- Display X content in advertising or to imply endorsement of any product or service without explicit permission from the user. +- Delete, obscure, or alter the post content or identification of the user (with the exception of removing hyperlinks). +- Exclude the timestamp. +- Do not use X marks, in whole or in part, in the title of your production without first reviewing their use with X by contacting us at trademarks@X.com. + +--- + +## Verbal or voice over + +### Posts + +When reading posts on air with no graphic display of the post, you do not need to display the X logo but you do need to verbally attribute it to X. Read the post as originally written, without edits. + +### Usernames + +Make sure to include a reference to X when mentioning X usernames. For example, “Follow us on X, at-username” or “You can follow Lady Gaga on X, at-ladygaga” + +### Hashtags + +Make sure to include a reference to X when mentioning X hashtags. For example, “Use the hashtag ‘election2016’ on X” or “The hashtag for this broadcast on X is bbcaq”. \ No newline at end of file diff --git a/developer-terms/reply-requirements.mdx b/developer-terms/reply-requirements.mdx new file mode 100644 index 000000000..74a0be65c --- /dev/null +++ b/developer-terms/reply-requirements.mdx @@ -0,0 +1,20 @@ +--- +title: "Display requirements: Replies" +keywords: ["developer agreement", "display requirements", "reply requirements"] +--- + +## Reply + +Please follow the following guidelines for displaying replies. + +### For a default reply: + +![Post Reply](/images/post_reply.png) + +### For a reply with social context: + +![Post Reply Social](/images/post_reply_social.png) + +### For a Quote: + +![Post Reply Quote](/images/post_reply_quote.png) \ No newline at end of file diff --git a/docs.json b/docs.json index 83b6c02d0..618062f0a 100644 --- a/docs.json +++ b/docs.json @@ -149,7 +149,16 @@ "x-api/fundamentals/rate-limits", "x-api/fundamentals/post-cap", "x-api/fundamentals/edit-posts", - "x-api/fundamentals/response-codes-and-errors" + "x-api/fundamentals/response-codes-and-errors", + { + "group": "Streaming", + "pages": [ + "x-api/fundamentals/consuming-streaming-data", + "x-api/fundamentals/handling-disconnections", + "x-api/fundamentals/high-volume-capacity", + "x-api/fundamentals/recovery-and-redundancy" + ] + } ] } ] @@ -226,11 +235,11 @@ "pages": [ "x-api/posts/filtered-stream/integrate/build-a-rule", "x-api/posts/filtered-stream/integrate/operators", - "x-api/posts/filtered-stream/integrate/consuming-streaming-data", - "x-api/posts/filtered-stream/integrate/handling-disconnections", - "x-api/posts/filtered-stream/integrate/handling-high-volume-capacity", "x-api/posts/filtered-stream/integrate/matching-returned-tweets", - "x-api/posts/filtered-stream/integrate/recovery-and-redundancy-features" + "x-api/fundamentals/consuming-streaming-data", + "x-api/fundamentals/handling-disconnections", + "x-api/fundamentals/high-volume-capacity", + "x-api/fundamentals/recovery-and-redundancy" ] } ] @@ -798,6 +807,9 @@ { "group": "Stream Connections", "pages": [ + "x-api/connections/get-connection-history", + "x-api/connections/terminate-connections-by-endpoint", + "x-api/connections/terminate-multiple-connections", "x-api/connections/terminate-all-connections" ] }, @@ -832,16 +844,6 @@ ] }, - { - "group": "Enterprise v2", - "pages": [ - { - "group": "Engagement Metrics", - "pages": [ - "x-api/posts/get-post-analytics", - "x-api/media/get-media-analytics" - ] - }, { "group": "Webhooks", "pages": [ @@ -853,6 +855,16 @@ "x-api/webhooks/create-replay-job-for-webhook" ] }, + { + "group": "Enterprise v2", + "pages": [ + { + "group": "Engagement Metrics", + "pages": [ + "x-api/posts/get-post-analytics", + "x-api/media/get-media-analytics" + ] + }, { "group": "Account Activity", "pages": [ @@ -885,6 +897,7 @@ "group": "Powerstream", "pages": [ "x-api/powerstream/introduction", + "x-api/powerstream/operators", "x-api/powerstream/handling-disconnections", "x-api/powerstream/recovery-and-redundancy" ] diff --git a/forms/billing-support.mdx b/forms/billing-support.mdx index 51a8486af..59f3d677f 100644 --- a/forms/billing-support.mdx +++ b/forms/billing-support.mdx @@ -9,4 +9,4 @@ import { Form } from '/snippets/form.mdx'; Get billing support for Basic, Pro, and Enterprise API subscriptions. -
\ No newline at end of file + \ No newline at end of file diff --git a/forms/enterprise-api-interest.mdx b/forms/enterprise-api-interest.mdx index ef4b778e7..058aba0c1 100644 --- a/forms/enterprise-api-interest.mdx +++ b/forms/enterprise-api-interest.mdx @@ -6,4 +6,4 @@ keywords: ["enterprise access", "enterprise API", "apply for enterprise", "enter import { Form } from '/snippets/form.mdx'; - \ No newline at end of file + \ No newline at end of file diff --git a/forms/government-end-user-request.mdx b/forms/government-end-user-request.mdx index 1615816c2..35ceac50d 100644 --- a/forms/government-end-user-request.mdx +++ b/forms/government-end-user-request.mdx @@ -6,4 +6,4 @@ keywords: ["government", "government access", "government form", "public sector" import { Form } from '/snippets/form.mdx'; - \ No newline at end of file + \ No newline at end of file diff --git a/fundamentals/authentication/oauth-2-0/authorization-code.mdx b/fundamentals/authentication/oauth-2-0/authorization-code.mdx index 284c0377a..a567583f6 100644 --- a/fundamentals/authentication/oauth-2-0/authorization-code.mdx +++ b/fundamentals/authentication/oauth-2-0/authorization-code.mdx @@ -71,6 +71,8 @@ Scopes allow you to set granular access for your App so that your App only has t | block.write | Block and unblock accounts for you. | | bookmark.read | Get Bookmarked Tweets from an authenticated user. | | bookmark.write | Bookmark and remove Bookmarks from Tweets. | +| dm.read | All the Direct Messages you can view, including Direct Messages from protected accounts. | +| dm.write | Send and manage Direct Messages for you. | | media.write | Upload media. | #### Rate limits diff --git a/images/post_anatomy.png b/images/post_anatomy.png new file mode 100644 index 000000000..95204c8f4 Binary files /dev/null and b/images/post_anatomy.png differ diff --git a/images/post_broadcast.png b/images/post_broadcast.png new file mode 100644 index 000000000..8273cffcb Binary files /dev/null and b/images/post_broadcast.png differ diff --git a/images/post_display.png b/images/post_display.png new file mode 100644 index 000000000..ff21d1836 Binary files /dev/null and b/images/post_display.png differ diff --git a/images/post_reply.png b/images/post_reply.png new file mode 100644 index 000000000..88cc64a82 Binary files /dev/null and b/images/post_reply.png differ diff --git a/images/post_reply_quote.png b/images/post_reply_quote.png new file mode 100644 index 000000000..8d48ab1bc Binary files /dev/null and b/images/post_reply_quote.png differ diff --git a/images/post_reply_social.png b/images/post_reply_social.png new file mode 100644 index 000000000..41b071cd0 Binary files /dev/null and b/images/post_reply_social.png differ diff --git a/incidents.mdx b/incidents.mdx index 8f75da5c6..3c5f7ddb5 100644 --- a/incidents.mdx +++ b/incidents.mdx @@ -5,6 +5,18 @@ keywords: ["incidents", "incident history", "outages", "service issues", "API in ## January 2026 + +Incident has been resolved. | **January 29, 04:00 UTC - 04:45 UTC** + + +Incident has been resolved. | **January 25, 17:00 UTC - 19:00 UTC** + + +Incident has been resolved. | **January 24, 16:48 UTC - 20:30 UTC** + + +Incident has been resolved. | **January 23, 19:25 UTC - 20:30 UTC** + Incident has been resolved. | **January 22, 17:30 UTC - 17:45 UTC** diff --git a/newsletter.mdx b/newsletter.mdx index 2f7eed5d4..b622559b4 100644 --- a/newsletter.mdx +++ b/newsletter.mdx @@ -10,4 +10,4 @@ import { Form } from '/snippets/form.mdx'; Sign up for emails about the latest news, product updates, and events from the X Developer team. - \ No newline at end of file + \ No newline at end of file diff --git a/openapi.json b/openapi.json index e1892710f..be4bdeb28 100644 --- a/openapi.json +++ b/openapi.json @@ -2,7 +2,7 @@ "openapi" : "3.0.0", "info" : { "description" : "X API v2 available endpoints", - "version" : "2.157", + "version" : "2.158", "title" : "X API v2", "termsOfService" : "https://developer.x.com/en/developer-terms/agreement-and-policy.html", "contact" : { @@ -505,7 +505,31 @@ "url" : "https://docs.x.com/x-api/activity/get-x-activity-subscriptions" }, "operationId" : "getActivitySubscriptions", - "parameters" : [ ], + "parameters" : [ + { + "name" : "max_results", + "in" : "query", + "description" : "The maximum number of results to return per page.", + "required" : false, + "schema" : { + "type" : "integer", + "minimum" : 1, + "maximum" : 100, + "format" : "int32" + }, + "style" : "form" + }, + { + "name" : "pagination_token", + "in" : "query", + "description" : "This parameter is used to get the next 'page' of results.", + "required" : false, + "schema" : { + "$ref" : "#/components/schemas/PaginationToken32" + }, + "style" : "form" + } + ], "responses" : { "200" : { "description" : "The request has succeeded.", @@ -538,6 +562,15 @@ "security" : [ { "BearerToken" : [ ] + }, + { + "OAuth2UserToken" : [ + "dm.read", + "tweet.read" + ] + }, + { + "UserToken" : [ ] } ], "tags" : [ @@ -710,6 +743,257 @@ } } }, + "/2/chat/conversations" : { + "get" : { + "security" : [ + { + "OAuth2UserToken" : [ + "dm.read", + "users.read" + ] + }, + { + "UserToken" : [ ] + } + ], + "tags" : [ + "Chat" + ], + "summary" : "Get Chat Conversations", + "description" : "Retrieves a list of Chat conversations for the authenticated user's inbox.", + "externalDocs" : { + "url" : "https://developer.x.com/" + }, + "operationId" : "getChatConversations", + "parameters" : [ + { + "name" : "max_results", + "in" : "query", + "description" : "Maximum number of conversations to return.", + "required" : false, + "schema" : { + "type" : "integer", + "minimum" : 1, + "maximum" : 100, + "format" : "int32", + "default" : 10 + }, + "style" : "form" + }, + { + "name" : "pagination_token", + "in" : "query", + "description" : "Token for pagination to retrieve the next page of results.", + "required" : false, + "schema" : { + "type" : "string" + }, + "style" : "form" + }, + { + "$ref" : "#/components/parameters/ChatConversationFieldsParameter" + }, + { + "$ref" : "#/components/parameters/ChatConversationExpansionsParameter" + }, + { + "$ref" : "#/components/parameters/UserFieldsParameter" + } + ], + "responses" : { + "200" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ChatGetConversationsResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/2/chat/conversations/{conversation_id}" : { + "get" : { + "security" : [ + { + "OAuth2UserToken" : [ + "dm.read", + "tweet.read", + "users.read" + ] + }, + { + "UserToken" : [ ] + } + ], + "tags" : [ + "Chat" + ], + "summary" : "Get Chat Conversation", + "description" : "Retrieves messages and key change events for a specific Chat conversation with pagination support.", + "externalDocs" : { + "url" : "https://developer.x.com/" + }, + "operationId" : "getChatConversation", + "parameters" : [ + { + "name" : "conversation_id", + "in" : "path", + "description" : "The Chat conversation ID.", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/ChatConversationId" + }, + "style" : "simple" + }, + { + "name" : "max_results", + "in" : "query", + "description" : "Maximum number of message events to return.", + "required" : false, + "schema" : { + "type" : "integer", + "minimum" : 1, + "maximum" : 100, + "format" : "int32", + "default" : 10 + }, + "style" : "form" + }, + { + "name" : "pagination_token", + "in" : "query", + "description" : "Token for pagination to retrieve the next page of results.", + "required" : false, + "schema" : { + "type" : "string" + }, + "style" : "form" + }, + { + "$ref" : "#/components/parameters/ChatMessageEventFieldsParameter" + } + ], + "responses" : { + "200" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ChatGetConversationResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/2/chat/conversations/{conversation_id}/messages" : { + "post" : { + "security" : [ + { + "OAuth2UserToken" : [ + "dm.write", + "tweet.read", + "users.read" + ] + }, + { + "UserToken" : [ ] + } + ], + "tags" : [ + "Chat" + ], + "summary" : "Send Chat Message", + "description" : "Sends an encrypted message to a specific Chat conversation.", + "externalDocs" : { + "url" : "https://developer.x.com/" + }, + "operationId" : "sendChatMessage", + "parameters" : [ + { + "name" : "conversation_id", + "in" : "path", + "description" : "The Chat conversation ID.", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/ChatConversationId" + }, + "style" : "simple" + } + ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ChatSendMessageRequest" + } + } + }, + "required" : true + }, + "responses" : { + "201" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ChatSendMessageResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + } + }, "/2/communities/search" : { "get" : { "security" : [ @@ -1078,6 +1362,57 @@ } }, "/2/connections" : { + "delete" : { + "security" : [ + { + "BearerToken" : [ ] + } + ], + "tags" : [ + "Connections" + ], + "summary" : "Terminate multiple connections", + "description" : "Terminates multiple streaming connections by their UUIDs for the authenticated application.", + "operationId" : "deleteConnectionsByUuids", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/KillConnectionsByUuidsRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/KillConnectionsByUuidsResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + }, "get" : { "security" : [ { @@ -1118,7 +1453,7 @@ "items" : { "type" : "string", "enum" : [ - "filter_stream", + "filtered_stream", "sample_stream", "sample10_stream", "firehose_stream", @@ -1172,7 +1507,53 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/Get2ConnectionsResponse" + "$ref" : "#/components/schemas/Get2ConnectionsResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/2/connections/all" : { + "delete" : { + "security" : [ + { + "BearerToken" : [ ] + } + ], + "tags" : [ + "Connections" + ], + "summary" : "Terminate all connections", + "description" : "Terminates all active streaming connections for the authenticated application.", + "externalDocs" : { + "url" : "https://docs.x.com/x-api/connections/terminate-all-connections" + }, + "operationId" : "deleteAllConnections", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/KillAllConnectionsResponse" } } } @@ -1195,7 +1576,7 @@ } } }, - "/2/connections/all" : { + "/2/connections/{endpoint_id}" : { "delete" : { "security" : [ { @@ -1205,20 +1586,44 @@ "tags" : [ "Connections" ], - "summary" : "Terminate all connections", - "description" : "Terminates all active streaming connections for the authenticated application.", - "externalDocs" : { - "url" : "https://docs.x.com/x-api/connections/terminate-all-connections" - }, - "operationId" : "deleteAllConnections", - "parameters" : [ ], + "summary" : "Terminate connections by endpoint", + "description" : "Terminates all streaming connections for a specific endpoint ID for the authenticated application.", + "operationId" : "deleteConnectionsByEndpoint", + "parameters" : [ + { + "name" : "endpoint_id", + "in" : "path", + "description" : "The endpoint ID to terminate connections for.", + "required" : true, + "schema" : { + "type" : "string", + "enum" : [ + "filtered_stream", + "sample_stream", + "sample10_stream", + "firehose_stream", + "tweets_compliance_stream", + "users_compliance_stream", + "tweet_label_stream", + "firehose_stream_lang_en", + "firehose_stream_lang_ja", + "firehose_stream_lang_ko", + "firehose_stream_lang_pt", + "likes_firehose_stream", + "likes_sample10_stream", + "likes_compliance_stream" + ] + }, + "style" : "simple" + } + ], "responses" : { "200" : { "description" : "The request has succeeded.", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/KillAllConnectionsResponse" + "$ref" : "#/components/schemas/KillConnectionsByEndpointResponse" } } } @@ -4474,7 +4879,7 @@ { "name" : "post_selection", "in" : "query", - "description" : "The selection of posts to return. Valid values are 'feed_size: small' and 'feed_size: large'. Default is 'feed_size: small', only top AI writers have access to large size feed.", + "description" : "The selection of posts to return. Valid values are 'feed_size: small', 'feed_size: large', and 'feed_size: xl'. Default is 'feed_size: small', only top AI writers have access to large and xl size feeds.", "required" : false, "schema" : { "type" : "string" @@ -9062,6 +9467,105 @@ } } }, + "/2/users/{id}/affiliates" : { + "get" : { + "security" : [ + { + "BearerToken" : [ ] + }, + { + "OAuth2UserToken" : [ + "tweet.read", + "users.read" + ] + }, + { + "UserToken" : [ ] + } + ], + "tags" : [ + "Users" + ], + "summary" : "Get affiliates", + "description" : "Retrieves a list of Users who are affiliated with a specific organization User by their ID.", + "externalDocs" : { + "url" : "https://developer.twitter.com/en/docs/twitter-api/users/affiliates/api-reference/get-users-id-affiliates" + }, + "operationId" : "getUsersAffiliates", + "parameters" : [ + { + "name" : "id", + "in" : "path", + "description" : "The ID of the User to lookup.", + "required" : true, + "example" : "2244994945", + "schema" : { + "$ref" : "#/components/schemas/UserId" + }, + "style" : "simple" + }, + { + "name" : "max_results", + "in" : "query", + "description" : "The maximum number of results.", + "required" : false, + "schema" : { + "type" : "integer", + "minimum" : 1, + "maximum" : 1000, + "format" : "int32" + }, + "style" : "form" + }, + { + "name" : "pagination_token", + "in" : "query", + "description" : "This parameter is used to get a specified 'page' of results.", + "required" : false, + "schema" : { + "$ref" : "#/components/schemas/PaginationTokenLong" + }, + "style" : "form" + }, + { + "$ref" : "#/components/parameters/UserFieldsParameter" + }, + { + "$ref" : "#/components/parameters/UserExpansionsParameter" + }, + { + "$ref" : "#/components/parameters/TweetFieldsParameter" + } + ], + "responses" : { + "200" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Get2UsersIdAffiliatesResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + } + }, "/2/users/{id}/blocking" : { "get" : { "security" : [ @@ -11164,31 +11668,171 @@ { "name" : "id", "in" : "path", - "description" : "The ID of the authenticated source User for whom to return results.", - "required" : true, - "schema" : { - "$ref" : "#/components/schemas/UserIdMatchesAuthenticatedUser" - }, - "style" : "simple" - }, - { - "name" : "list_id", - "in" : "path", - "description" : "The ID of the List to unpin.", + "description" : "The ID of the authenticated source User for whom to return results.", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/UserIdMatchesAuthenticatedUser" + }, + "style" : "simple" + }, + { + "name" : "list_id", + "in" : "path", + "description" : "The ID of the List to unpin.", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/ListId" + }, + "style" : "simple" + } + ], + "responses" : { + "200" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ListUnpinResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/2/users/{id}/public_keys" : { + "get" : { + "security" : [ + { + "OAuth2UserToken" : [ + "dm.read", + "tweet.read", + "users.read" + ] + }, + { + "UserToken" : [ ] + } + ], + "tags" : [ + "Chat" + ], + "summary" : "Get user public keys", + "description" : "Returns the public keys and Juicebox configuration for the specified user.", + "externalDocs" : { + "url" : "https://developer.x.com/" + }, + "operationId" : "getUserPublicKeys", + "parameters" : [ + { + "name" : "id", + "in" : "path", + "description" : "The ID of the User to lookup.", + "required" : true, + "example" : "2244994945", + "schema" : { + "$ref" : "#/components/schemas/UserId" + }, + "style" : "simple" + }, + { + "$ref" : "#/components/parameters/PublicKeyFieldsParameter" + } + ], + "responses" : { + "200" : { + "description" : "The request has succeeded.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Get2UsersIdPublicKeysResponse" + } + } + } + }, + "default" : { + "description" : "The request has failed.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + }, + "application/problem+json" : { + "schema" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + } + } + }, + "post" : { + "security" : [ + { + "OAuth2UserToken" : [ + "dm.write", + "tweet.read", + "users.read" + ] + }, + { + "UserToken" : [ ] + } + ], + "tags" : [ + "Chat" + ], + "summary" : "Add public key", + "description" : "Registers a user's public key for X Chat encryption.", + "externalDocs" : { + "url" : "https://developer.x.com/" + }, + "operationId" : "addUserPublicKey", + "parameters" : [ + { + "name" : "id", + "in" : "path", + "description" : "The ID of the requesting user.", "required" : true, "schema" : { - "$ref" : "#/components/schemas/ListId" + "$ref" : "#/components/schemas/UserId" }, "style" : "simple" } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ChatAddPublicKeyRequest" + } + } + }, + "required" : true + }, "responses" : { "200" : { "description" : "The request has succeeded.", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ListUnpinResponse" + "$ref" : "#/components/schemas/ChatAddPublicKeyResponse" } } } @@ -12204,6 +12848,14 @@ "url" : "https://developer.twitter.com/en/docs/twitter-api/lists" } }, + { + "name" : "Marketplace", + "description" : "Endpoints related to marketplace handles", + "externalDocs" : { + "description" : "Handle marketplace availability", + "url" : "https://docs.x.com/x-api/marketplace/handles/availability" + } + }, { "name" : "Media", "description" : "Endpoints related to Media", @@ -12362,9 +13014,11 @@ "follow.follow" : "#/components/schemas/FollowActivityResponsePayload", "follow.unfollow" : "#/components/schemas/FollowActivityResponsePayload", "news.new" : "#/components/schemas/NewsActivityResponsePayload", + "profile.update.affiliate_badge" : "#/components/schemas/ProfileUpdateActivityResponsePayload", "profile.update.banner_picture" : "#/components/schemas/ProfileUpdateActivityResponsePayload", "profile.update.bio" : "#/components/schemas/ProfileUpdateActivityResponsePayload", "profile.update.geo" : "#/components/schemas/ProfileUpdateActivityResponsePayload", + "profile.update.handle" : "#/components/schemas/ProfileUpdateActivityResponsePayload", "profile.update.profile_picture" : "#/components/schemas/ProfileUpdateActivityResponsePayload", "profile.update.screenname" : "#/components/schemas/ProfileUpdateActivityResponsePayload", "profile.update.url" : "#/components/schemas/ProfileUpdateActivityResponsePayload", @@ -12425,9 +13079,15 @@ "profile.update.geo", "profile.update.url", "profile.update.verified_badge", + "profile.update.affiliate_badge", + "profile.update.handle", "news.new", "follow.follow", "follow.unfollow", + "spaces.start", + "spaces.end", + "chat.received", + "chat.sent", "ProfileBioUpdate", "ProfilePictureUpdate", "ProfileBannerPictureUpdate", @@ -12549,9 +13209,13 @@ "meta" : { "type" : "object", "properties" : { - "total_subscriptions" : { + "next_token" : { + "type" : "string", + "description" : "Token to retrieve the next page of results." + }, + "result_count" : { "type" : "integer", - "description" : "Number of active subscriptions.", + "description" : "Number of active subscriptions returned in response.", "format" : "int32" } } @@ -12780,24 +13444,335 @@ } } }, - "BookmarkFolderId" : { + "BookmarkFolderId" : { + "type" : "string", + "description" : "The unique identifier of this Bookmark folder.", + "pattern" : "^[0-9]{1,19}$", + "example" : "1146654567674912769" + }, + "BookmarkFolderPostsResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "id" : { + "$ref" : "#/components/schemas/TweetId" + } + } + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + }, + "meta" : { + "type" : "object", + "properties" : { + "next_token" : { + "$ref" : "#/components/schemas/NextToken" + } + } + } + } + }, + "BookmarkFoldersResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "id" : { + "$ref" : "#/components/schemas/BookmarkFolderId" + }, + "name" : { + "type" : "string" + } + } + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + }, + "meta" : { + "type" : "object", + "properties" : { + "next_token" : { + "$ref" : "#/components/schemas/NextToken" + } + } + } + } + }, + "BookmarkMutationResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "object", + "properties" : { + "bookmarked" : { + "type" : "boolean" + } + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "CashtagEntity" : { + "allOf" : [ + { + "$ref" : "#/components/schemas/EntityIndicesInclusiveExclusive" + }, + { + "$ref" : "#/components/schemas/CashtagFields" + } + ] + }, + "CashtagFields" : { + "type" : "object", + "description" : "Represent the portion of text recognized as a Cashtag, and its start and end position within the text.", + "required" : [ + "tag" + ], + "properties" : { + "tag" : { + "type" : "string", + "example" : "TWTR" + } + } + }, + "ChatAddPublicKeyRequest" : { + "type" : "object", + "required" : [ + "public_key", + "version" + ], + "properties" : { + "generate_version" : { + "type" : "boolean", + "description" : "When true, the server generates a new version." + }, + "public_key" : { + "type" : "object", + "description" : "Public key registration payload.", + "properties" : { + "identity_public_key_signature" : { + "type" : "string", + "description" : "Signature over the identity public key." + }, + "public_key" : { + "type" : "string", + "description" : "Identity public key (base64 encoded)." + }, + "public_key_fingerprint" : { + "type" : "string", + "description" : "Fingerprint of the identity public key." + }, + "registration_method" : { + "type" : "string", + "description" : "Registration method for the public key." + }, + "signing_public_key" : { + "type" : "string", + "description" : "Signing public key (base64 encoded)." + }, + "signing_public_key_signature" : { + "type" : "string", + "description" : "Signature over the signing public key." + } + } + }, + "version" : { + "type" : "string", + "description" : "Public key version." + } + } + }, + "ChatAddPublicKeyResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "object", + "properties" : { + "error_code" : { + "type" : "string", + "description" : "Error code when key registration fails." + }, + "token_map" : { + "type" : "object", + "description" : "Key store token map for key recovery.", + "properties" : { + "key_store_token_map_json" : { + "type" : "string", + "description" : "Raw JSON for key recovery configuration." + }, + "max_guess_count" : { + "type" : "integer", + "description" : "Maximum guess count for key recovery." + }, + "realm_state_string" : { + "type" : "string", + "description" : "Serialized realm state for key recovery." + }, + "recover_threshold" : { + "type" : "integer", + "description" : "Threshold required to recover the key." + }, + "register_threshold" : { + "type" : "integer", + "description" : "Threshold required to register the key." + }, + "token_map" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "key" : { + "type" : "string", + "description" : "Token map entry key." + }, + "value" : { + "type" : "object", + "properties" : { + "address" : { + "type" : "string", + "description" : "Key store realm address." + }, + "public_key" : { + "type" : "string", + "description" : "Realm public key." + }, + "token" : { + "type" : "string", + "description" : "JWT auth token for the realm." + } + } + } + } + } + } + } + }, + "version" : { + "type" : "string", + "description" : "Public key version." + } + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "ChatConversation" : { + "type" : "object", + "description" : "A Chat conversation resource representing either a direct or group conversation.", + "required" : [ + "id" + ], + "properties" : { + "admin_ids" : { + "type" : "array", + "description" : "User IDs of group admins. Only present for group conversations.", + "items" : { + "type" : "string" + } + }, + "created_at" : { + "type" : "string", + "description" : "ISO 8601 timestamp when the group was created. Only present for group conversations." + }, + "group_avatar_url" : { + "type" : "string", + "description" : "URL for the group avatar. Only present for group conversations." + }, + "group_name" : { + "type" : "string", + "description" : "Encrypted group name. Only present for group conversations." + }, + "id" : { + "type" : "string", + "description" : "The unique identifier for this conversation." + }, + "is_muted" : { + "type" : "boolean", + "description" : "Whether notifications are muted for this conversation." + }, + "member_ids" : { + "type" : "array", + "description" : "User IDs of group members. Only present for group conversations.", + "items" : { + "type" : "string" + } + }, + "message_ttl_msec" : { + "type" : "string", + "description" : "Message time-to-live in milliseconds." + }, + "participant_ids" : { + "type" : "array", + "description" : "Array of user IDs who are participants in this conversation.", + "items" : { + "type" : "string" + } + }, + "screen_capture_blocking_enabled" : { + "type" : "boolean", + "description" : "Whether screen capture blocking is enabled for this conversation." + }, + "screen_capture_detection_enabled" : { + "type" : "boolean", + "description" : "Whether screen capture detection is enabled for this conversation." + }, + "type" : { + "type" : "string", + "description" : "The type of conversation: 'direct' or 'group'.", + "enum" : [ + "direct", + "group" + ] + }, + "updated_at" : { + "type" : "string", + "description" : "ISO 8601 timestamp when the group was last updated. Only present for group conversations." + } + } + }, + "ChatConversationId" : { "type" : "string", - "description" : "The unique identifier of this Bookmark folder.", - "pattern" : "^[0-9]{1,19}$", - "example" : "1146654567674912769" + "description" : "Unique identifier of an Chat conversation. Format is either two user IDs separated by a hyphen (e.g., '1215441834412953600-1603419180975409153') for direct conversations, or 'g' followed by a numeric ID (e.g., 'g1234567890123456789') for group conversations.", + "pattern" : "^([0-9]{1,19}-[0-9]{1,19}|g[0-9]{1,19})$", + "example" : "1215441834412953600-1603419180975409153" }, - "BookmarkFolderPostsResponse" : { + "ChatGetConversationResponse" : { "type" : "object", "properties" : { "data" : { "type" : "array", + "description" : "List of message events in the conversation.", "items" : { - "type" : "object", - "properties" : { - "id" : { - "$ref" : "#/components/schemas/TweetId" - } - } + "$ref" : "#/components/schemas/ChatMessageEvent" } }, "errors" : { @@ -12810,28 +13785,37 @@ "meta" : { "type" : "object", "properties" : { + "has_more" : { + "type" : "boolean", + "description" : "Whether there are more messages to fetch." + }, + "missing_conversation_key_change_events" : { + "type" : "array", + "description" : "List of conversation key change events needed for decryption.", + "items" : { + "type" : "string" + } + }, "next_token" : { - "$ref" : "#/components/schemas/NextToken" + "type" : "string", + "description" : "Token to retrieve the next page of results." + }, + "result_count" : { + "type" : "integer", + "description" : "The number of message events returned." } } } } }, - "BookmarkFoldersResponse" : { + "ChatGetConversationsResponse" : { "type" : "object", "properties" : { "data" : { "type" : "array", + "description" : "List of conversations in the user's inbox.", "items" : { - "type" : "object", - "properties" : { - "id" : { - "$ref" : "#/components/schemas/BookmarkFolderId" - }, - "name" : { - "type" : "string" - } - } + "$ref" : "#/components/schemas/ChatConversation" } }, "errors" : { @@ -12841,56 +13825,176 @@ "$ref" : "#/components/schemas/Problem" } }, + "includes" : { + "type" : "object", + "description" : "Expanded objects requested via expansions parameter.", + "properties" : { + "users" : { + "type" : "array", + "description" : "User objects for expanded user IDs.", + "items" : { + "type" : "object" + } + } + } + }, "meta" : { "type" : "object", "properties" : { + "has_message_requests" : { + "type" : "boolean", + "description" : "Whether the user has pending message requests." + }, "next_token" : { - "$ref" : "#/components/schemas/NextToken" + "type" : "string", + "description" : "Token to retrieve the next page of results." + }, + "result_count" : { + "type" : "integer", + "description" : "The number of conversations returned." } } } } }, - "BookmarkMutationResponse" : { + "ChatMessageEvent" : { "type" : "object", + "description" : "An Chat message event with extracted envelope fields and the original encoded event.", + "required" : [ + "encoded_event" + ], "properties" : { - "data" : { - "type" : "object", - "properties" : { - "bookmarked" : { - "type" : "boolean" - } - } + "conversation_id" : { + "type" : "string", + "description" : "The conversation ID this message belongs to." }, - "errors" : { + "conversation_token" : { + "type" : "string", + "description" : "The conversation token for this message." + }, + "created_at_msec" : { + "type" : "string", + "description" : "The creation timestamp in milliseconds." + }, + "encoded_event" : { + "type" : "string", + "description" : "Base64-encoded MessageEvent for client decoding." + }, + "id" : { + "type" : "string", + "description" : "The unique identifier for this message event (message_id)." + }, + "is_trusted" : { + "type" : "boolean", + "description" : "Whether the message is from a trusted source." + }, + "message_event_signature" : { + "$ref" : "#/components/schemas/ChatMessageEventSignature" + }, + "previous_sequence_id" : { + "type" : "string", + "description" : "The sequence ID of the previous message." + }, + "sender_id" : { + "type" : "string", + "description" : "The user ID of the message sender." + }, + "sequence_id" : { + "type" : "string", + "description" : "The sequence identifier for ordering messages." + } + } + }, + "ChatMessageEventSignature" : { + "type" : "object", + "description" : "Message event signature for verification.", + "properties" : { + "message_signing_key_info_list" : { "type" : "array", - "minItems" : 1, + "description" : "List of signing key information for message verification.", "items" : { - "$ref" : "#/components/schemas/Problem" + "$ref" : "#/components/schemas/ChatMessageSigningKeyInfo" } + }, + "public_key_version" : { + "type" : "string", + "description" : "The version of the public key used for signing." + }, + "signature" : { + "type" : "string", + "description" : "The signature of the message event." + }, + "signature_version" : { + "type" : "string", + "description" : "The version of the signature algorithm." + }, + "signing_public_key" : { + "type" : "string", + "description" : "The public key used for signing." } } }, - "CashtagEntity" : { - "allOf" : [ - { - "$ref" : "#/components/schemas/EntityIndicesInclusiveExclusive" + "ChatMessageSigningKeyInfo" : { + "type" : "object", + "description" : "Signing key information for message verification.", + "properties" : { + "member_id" : { + "type" : "string", + "description" : "The member ID associated with this signing key." }, - { - "$ref" : "#/components/schemas/CashtagFields" + "public_key_version" : { + "type" : "string", + "description" : "The version of the public key." + }, + "signing_public_key" : { + "type" : "string", + "description" : "The signing public key." } - ] + } }, - "CashtagFields" : { + "ChatSendMessageRequest" : { "type" : "object", - "description" : "Represent the portion of text recognized as a Cashtag, and its start and end position within the text.", "required" : [ - "tag" + "message_id", + "encoded_message_create_event" ], "properties" : { - "tag" : { + "conversation_token" : { "type" : "string", - "example" : "TWTR" + "description" : "Optional conversation token." + }, + "encoded_message_create_event" : { + "type" : "string", + "description" : "Base64-encoded Thrift MessageCreateEvent containing encrypted message contents." + }, + "encoded_message_event_signature" : { + "type" : "string", + "description" : "Base64-encoded Thrift MessageEventSignature for message verification." + }, + "message_id" : { + "type" : "string", + "description" : "Unique identifier for this message." + } + } + }, + "ChatSendMessageResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "object", + "properties" : { + "encoded_message_event" : { + "type" : "string", + "description" : "Base64-encoded response message event." + } + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } } } }, @@ -14215,6 +15319,69 @@ } ] }, + "Get2ChatConversationsConversationIdResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/ChatMessageEvent" + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + }, + "meta" : { + "type" : "object", + "properties" : { + "next_token" : { + "$ref" : "#/components/schemas/NextToken" + }, + "result_count" : { + "$ref" : "#/components/schemas/ResultCount" + } + } + } + } + }, + "Get2ChatConversationsResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/ChatConversation" + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + }, + "includes" : { + "$ref" : "#/components/schemas/Expansions" + }, + "meta" : { + "type" : "object", + "properties" : { + "next_token" : { + "$ref" : "#/components/schemas/NextToken" + }, + "result_count" : { + "$ref" : "#/components/schemas/ResultCount" + } + } + } + } + }, "Get2CommunitiesIdResponse" : { "type" : "object", "properties" : { @@ -14738,6 +15905,21 @@ } } }, + "Get2MarketplaceHandlesHandleAvailabilityResponse" : { + "type" : "object", + "properties" : { + "data" : { + "$ref" : "#/components/schemas/MarketplaceHandleAvailability" + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, "Get2MediaAnalyticsResponse" : { "type" : "object", "properties" : { @@ -15643,6 +16825,42 @@ } } }, + "Get2UsersIdAffiliatesResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/User" + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + }, + "includes" : { + "$ref" : "#/components/schemas/Expansions" + }, + "meta" : { + "type" : "object", + "properties" : { + "next_token" : { + "$ref" : "#/components/schemas/NextToken" + }, + "previous_token" : { + "$ref" : "#/components/schemas/PreviousToken" + }, + "result_count" : { + "$ref" : "#/components/schemas/ResultCount" + } + } + } + } + }, "Get2UsersIdBlockingResponse" : { "type" : "object", "properties" : { @@ -16039,6 +17257,25 @@ } } }, + "Get2UsersIdPublicKeysResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/PublicKey" + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, "Get2UsersIdResponse" : { "type" : "object", "properties" : { @@ -16387,8 +17624,125 @@ "data" : { "type" : "object", "properties" : { - "killed_connections" : { - "type" : "boolean" + "failed_kills" : { + "type" : "integer" + }, + "results" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "error_message" : { + "type" : "string" + }, + "success" : { + "type" : "boolean" + }, + "uuid" : { + "type" : "string" + } + } + } + }, + "successful_kills" : { + "type" : "integer" + } + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "KillConnectionsByEndpointResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "object", + "properties" : { + "failed_kills" : { + "type" : "integer" + }, + "results" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "error_message" : { + "type" : "string" + }, + "success" : { + "type" : "boolean" + }, + "uuid" : { + "type" : "string" + } + } + } + }, + "successful_kills" : { + "type" : "integer" + } + } + }, + "errors" : { + "type" : "array", + "minItems" : 1, + "items" : { + "$ref" : "#/components/schemas/Problem" + } + } + } + }, + "KillConnectionsByUuidsRequest" : { + "type" : "object", + "required" : [ + "uuids" + ], + "properties" : { + "uuids" : { + "type" : "array", + "description" : "Array of connection UUIDs to terminate", + "minItems" : 1, + "maxItems" : 100, + "items" : { + "type" : "string" + } + } + } + }, + "KillConnectionsByUuidsResponse" : { + "type" : "object", + "properties" : { + "data" : { + "type" : "object", + "properties" : { + "failed_kills" : { + "type" : "integer" + }, + "results" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "error_message" : { + "type" : "string" + }, + "success" : { + "type" : "boolean" + }, + "uuid" : { + "type" : "string" + } + } + } + }, + "successful_kills" : { + "type" : "integer" } } }, @@ -16753,6 +18107,24 @@ } } }, + "MarketplaceHandleAvailability" : { + "type" : "object", + "required" : [ + "availability_state" + ], + "properties" : { + "availability_state" : { + "type" : "string", + "description" : "Availability state of the handle.", + "example" : "available" + }, + "redirect_url" : { + "type" : "string", + "description" : "Redirect URL for marketplace handle search.", + "example" : "https://handles.x.com/search/jack" + } + } + }, "Media" : { "type" : "object", "required" : [ @@ -17123,10 +18495,6 @@ "properties" : { "data" : { "type" : "object", - "required" : [ - "id", - "media_key" - ], "properties" : { "expires_after_secs" : { "type" : "integer", @@ -18363,6 +19731,59 @@ }, "additionalProperties" : false }, + "PublicKey" : { + "type" : "object", + "description" : "Public key information for Chat encryption", + "properties" : { + "public_key" : { + "type" : "string", + "description" : "Identity public key (base64 encoded)." + }, + "signing_public_key" : { + "type" : "string", + "description" : "Signing public key (base64 encoded)." + }, + "token_map" : { + "type" : "object", + "description" : "Juicebox configuration.", + "properties" : { + "key_store_token_map_json" : { + "type" : "string", + "description" : "Raw JSON for Juicebox SDK." + }, + "max_guess_count" : { + "type" : "integer", + "description" : "Maximum guess count for Juicebox." + }, + "realms" : { + "type" : "array", + "description" : "List of Juicebox realms.", + "items" : { + "type" : "object", + "properties" : { + "address" : { + "type" : "string", + "description" : "Realm URL." + }, + "realm_id" : { + "type" : "string", + "description" : "Realm identifier." + }, + "token" : { + "type" : "string", + "description" : "JWT auth token for realm." + } + } + } + } + } + }, + "version" : { + "type" : "string", + "description" : "Public key version." + } + } + }, "ReplayJobCreateResponse" : { "type" : "object", "description" : "Confirmation that the replay job request was accepted.", @@ -21792,6 +23213,118 @@ "explode" : false, "style" : "form" }, + "ChatConversationExpansionsParameter" : { + "name" : "expansions", + "in" : "query", + "description" : "A comma separated list of fields to expand.", + "schema" : { + "type" : "array", + "description" : "The list of fields you can expand for a [ChatConversation](#ChatConversation) object. If the field has an ID, it can be expanded into a full object.", + "minItems" : 1, + "uniqueItems" : true, + "items" : { + "type" : "string", + "enum" : [ + "admin_ids", + "member_ids", + "participant_ids" + ] + }, + "example" : [ + "admin_ids", + "member_ids", + "participant_ids" + ] + }, + "explode" : false, + "style" : "form" + }, + "ChatConversationFieldsParameter" : { + "name" : "chat_conversation.fields", + "in" : "query", + "description" : "A comma separated list of ChatConversation fields to display.", + "required" : false, + "schema" : { + "type" : "array", + "description" : "The fields available for a ChatConversation object.", + "minItems" : 1, + "uniqueItems" : true, + "items" : { + "type" : "string", + "enum" : [ + "admin_ids", + "created_at", + "group_avatar_url", + "group_name", + "id", + "is_muted", + "member_ids", + "message_ttl_msec", + "participant_ids", + "screen_capture_blocking_enabled", + "screen_capture_detection_enabled", + "type", + "updated_at" + ] + }, + "example" : [ + "admin_ids", + "created_at", + "group_avatar_url", + "group_name", + "id", + "is_muted", + "member_ids", + "message_ttl_msec", + "participant_ids", + "screen_capture_blocking_enabled", + "screen_capture_detection_enabled", + "type", + "updated_at" + ] + }, + "explode" : false, + "style" : "form" + }, + "ChatMessageEventFieldsParameter" : { + "name" : "chat_message_event.fields", + "in" : "query", + "description" : "A comma separated list of ChatMessageEvent fields to display.", + "required" : false, + "schema" : { + "type" : "array", + "description" : "The fields available for a ChatMessageEvent object.", + "minItems" : 1, + "uniqueItems" : true, + "items" : { + "type" : "string", + "enum" : [ + "conversation_id", + "conversation_token", + "created_at_msec", + "encoded_event", + "id", + "is_trusted", + "message_event_signature", + "previous_id", + "sender_id" + ] + }, + "example" : [ + "conversation_id", + "conversation_token", + "created_at_msec", + "encoded_event", + "id", + "is_trusted", + "message_event_signature", + "previous_id", + "sender_id" + ] + }, + "explode" : false, + "style" : "form" + }, "CommunityFieldsParameter" : { "name" : "community.fields", "in" : "query", @@ -22183,6 +23716,31 @@ "explode" : false, "style" : "form" }, + "MarketplaceHandleAvailabilityFieldsParameter" : { + "name" : "marketplace_handle_availability.fields", + "in" : "query", + "description" : "A comma separated list of MarketplaceHandleAvailability fields to display.", + "required" : false, + "schema" : { + "type" : "array", + "description" : "The fields available for a MarketplaceHandleAvailability object.", + "minItems" : 1, + "uniqueItems" : true, + "items" : { + "type" : "string", + "enum" : [ + "availability_state", + "redirect_url" + ] + }, + "example" : [ + "availability_state", + "redirect_url" + ] + }, + "explode" : false, + "style" : "form" + }, "MediaAnalyticsFieldsParameter" : { "name" : "media_analytics.fields", "in" : "query", @@ -22603,6 +24161,35 @@ "explode" : false, "style" : "form" }, + "PublicKeyFieldsParameter" : { + "name" : "public_key.fields", + "in" : "query", + "description" : "A comma separated list of PublicKey fields to display.", + "required" : false, + "schema" : { + "type" : "array", + "description" : "The fields available for a PublicKey object.", + "minItems" : 1, + "uniqueItems" : true, + "items" : { + "type" : "string", + "enum" : [ + "juicebox_config", + "public_key", + "signing_public_key", + "version" + ] + }, + "example" : [ + "juicebox_config", + "public_key", + "signing_public_key", + "version" + ] + }, + "explode" : false, + "style" : "form" + }, "RulesCountFieldsParameter" : { "name" : "rules_count.fields", "in" : "query", diff --git a/overview.mdx b/overview.mdx index b6d3f7e13..306841a70 100644 --- a/overview.mdx +++ b/overview.mdx @@ -9,7 +9,7 @@ keywords: ["X API", "Twitter API", "developer platform", "API documentation", "X import { Button } from '/snippets/button.mdx'; - +
- X logo + Grok logo
- Streamline your development workflow with the official SDKs of the X API! + Pay only for what you use. Plus, earn free xAI API credits when you purchase X API credits!

- +
@@ -70,24 +70,20 @@ Build, analyze, and innovate with X's real-time global data. Access posts, users ## Pricing -The X API uses **pay-per-usage** pricing. No monthly subscriptions—pay only for what you use. +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. + + Flexible, credit-based pricing. Scale up or down based on your needs with no commitments or minimum spend. - - Lower barriers for small projects. No tier jumps or commitments. + + Dedicated support, complete streams, and custom integrations for high-volume needs. -
- -
- -For enterprise needs with dedicated support, complete streams, and custom integrations: - - + +Earn free [xAI API](https://docs.x.ai) credits when you purchase X API credits—up to 20% back based on your spend. [Learn more](/x-api/getting-started/pricing#free-xai-api-credits) + --- diff --git a/status.mdx b/status.mdx index 9f36fd0c2..856b37cc0 100644 --- a/status.mdx +++ b/status.mdx @@ -4,9 +4,13 @@ keywords: ["status", "API status", "system status", "uptime", "service status", --- - All systems are operational. + All systems are operational. + + + + Normal diff --git a/style.css b/style.css index 262596e21..32ea02437 100644 --- a/style.css +++ b/style.css @@ -162,4 +162,4 @@ td[data-label="Free Limit"] strong { .dark #usage-slider::-moz-range-thumb { background: #0B0C0E; -} \ No newline at end of file +} diff --git a/x-ads-api/campaign-management.mdx b/x-ads-api/campaign-management.mdx index a4900666d..fc9cafc2a 100644 --- a/x-ads-api/campaign-management.mdx +++ b/x-ads-api/campaign-management.mdx @@ -903,7 +903,7 @@ The following guide outlines the steps required to set up a PREROLL_VIEWS campai #### Endpoints Required -* [Chunked media upload](https://developer.x.com/en/docs/x-api/v1/media/upload-media/uploading-media/chunked-media-upload) (for video upload) +* [Chunked media upload](/x-api/media/quickstart/media-upload-chunked) (for video upload) * [POST accounts/:account\_id/media\_library](/x-ads-api/creatives#media-library) (for video association to ads account) * [POST accounts/:account_id/campaigns](/x-ads-api/campaign-management#post-accounts-account-id-campaigns) (create campaign) * [GET content_categories](/x-ads-api/campaign-management#content-categories) (to get the mapping of content categories to IAB categories) @@ -924,7 +924,7 @@ Uploading the video involves 2 steps: #### Upload the video media -First, using the [Chunked media upload](https://developer.x.com/en/docs/x-api/v1/media/upload-media/uploading-media/chunked-media-upload) endpoint, you will upload the video to X for processing. You must pass the `media_category=amplify_video` on the initial `INIT` using this endpoint. You’ll upload the video in chunks. Once the `STATUS` returns a `state` of `succeeded` you may continue with the next steps. More on the uploading of media using the chunked endpoint can be found in our [Promoted Video Overview](/x-ads-api/creatives#promoted-video). +First, using the [Chunked media upload](/x-api/media/quickstart/media-upload-chunked) endpoint, you will upload the video to X for processing. You must pass the `media_category=amplify_video` on the initial `INIT` using this endpoint. You’ll upload the video in chunks. Once the `STATUS` returns a `state` of `succeeded` you may continue with the next steps. More on the uploading of media using the chunked endpoint can be found in our [Promoted Video Overview](/x-ads-api/creatives#promoted-video). #### Add the video to the ads account diff --git a/x-ads-api/creatives.mdx b/x-ads-api/creatives.mdx index 77cc23225..21b41b5f0 100644 --- a/x-ads-api/creatives.mdx +++ b/x-ads-api/creatives.mdx @@ -56,7 +56,7 @@ The Ads API supports Promoted Video in [Tweets](/x-ads-api/creatives#tweets-2) a - [Video App Download](https://devcommunity.x.com/t/ads-api-version-11/168814) - [Video Conversation](/x-ads-api/creatives#video-conversation-cards) -First, upload the video using the [POST media/upload (chunked)](https://developer.x.com/en/docs/media/upload-media/api-reference/post-media-upload-init.html) endpoint. Using the `media_id`, associate the video with an ads account using the POST accounts/:account\_id/videos endpoint. The video's `id`, sometimes referred to as the `media_key`, will be used in subsequent requests. This is a string that begins with an int, is followed by an underscore, and ends with a long value. As an example, see: `13_875943225764098048`. +First, upload the video using the [POST media/upload (chunked)](/x-api/media/initialize-media-upload) endpoint. Using the `media_id`, associate the video with an ads account using the POST accounts/:account\_id/videos endpoint. The video's `id`, sometimes referred to as the `media_key`, will be used in subsequent requests. This is a string that begins with an int, is followed by an underscore, and ends with a long value. As an example, see: `13_875943225764098048`. #### Promoted Video in Tweets @@ -64,7 +64,7 @@ To create a Tweet, use the [POST accounts/:account\_id/tweet](/x-ads-api/creativ #### Promoted Video in Cards -Video App Download and Video Conversation cards support the ability to add a poster images. Upload an image to use in these cards using the [POST media/upload](https://developer.x.com/en/docs/media/upload-media/api-reference/post-media-upload.html) endpoint. +Video App Download and Video Conversation cards support the ability to add a poster images. Upload an image to use in these cards using the [POST media/upload](/x-api/media/upload-media) endpoint. Create the card using one of the following endpoints: @@ -78,11 +78,11 @@ Finally, create the Tweet using the POST accounts/:account\_id/tweet endpoint. C #### General Information -For detailed guidance on video uploading through the API, please see the [Video Upload Guide](https://developer.x.com/en/docs/media/upload-media/uploading-media.html). +For detailed guidance on video uploading through the API, please see the [Video Upload Guide](/x-api/media/quickstart/media-upload-chunked). Videos can also be promoted as pre-roll assets. See the [Video Views Pre-roll Objective Guide](/x-ads-api/campaign-management#video-views-preroll-objective) for a detailed explanation. -- (As of 2015-10-22) When uploading videos to be used in promoted content, the `media_category` parameter must be set with a value of `amplify_video` for all `INIT` command requests to the [POST media/upload (chunked)](https://developer.x.com/en/docs/media/upload-media/api-reference/post-media-upload-init.html) endpoint. Using this new param ensures that the video is asynchronously pre-processed and prepared for use in promoted content. The `STATUS` command can be used to check completion of asynchronous processing after video upload. +- (As of 2015-10-22) When uploading videos to be used in promoted content, the `media_category` parameter must be set with a value of `amplify_video` for all `INIT` command requests to the [POST media/upload (chunked)](/x-api/media/initialize-media-upload) endpoint. Using this new param ensures that the video is asynchronously pre-processed and prepared for use in promoted content. The `STATUS` command can be used to check completion of asynchronous processing after video upload. - The maximum promoted video length currently allowed is 10 mins with a file size of 500MB or less. - Uploaded video should be either mp4 or mov. - Uploaded video generally processes quickly, but processing times can vary depending on video length and file size. @@ -278,12 +278,12 @@ The Media Library endpoints provide the ability to manage images, GIFs, and vide #### API Endpoints -- [POST media/upload](https://developer.x.com/content/developer-twitter/en/docs/media/upload-media/api-reference/post-media-upload#post-media-upload) or [POST media/upload (chunked)](https://developer.x.com/content/developer-twitter/en/docs/media/upload-media/api-reference/post-media-upload-init#post-media-upload-init) (upload media) +- [POST media/upload](/x-api/media/upload-media) or [POST media/upload (chunked)](/x-api/media/initialize-media-upload) (upload media) - [POST accounts/:account\_id/media\_library](https://developer.x.com/x-ads-api/creatives#get-accounts-account-id-media-library#post-accounts-account-id-media-library) (add media to the Media Library) #### Adding to the Library -Adding media to the library is a two-step process. First, upload the asset using either the [POST media/upload](https://developer.x.com/content/developer-twitter/en/docs/media/upload-media/api-reference/post-media-upload#post-media-upload) endpoint or the [POST media/upload (chunked)](https://developer.x.com/content/developer-twitter/en/docs/media/upload-media/api-reference/post-media-upload-init#post-media-upload-init) set of endpoints. (See the [Chunked media upload](https://developer.x.com/content/developer-twitter/en/docs/media/upload-media/uploading-media/chunked-media-upload) guide for details on our multi-part upload process.) +Adding media to the library is a two-step process. First, upload the asset using either the [POST media/upload](/x-api/media/upload-media) endpoint or the [POST media/upload (chunked)](/x-api/media/initialize-media-upload) set of endpoints. (See the [Chunked media upload](/x-api/media/quickstart/media-upload-chunked) guide for details on our multi-part upload process.) ``` twurl -X POST -H upload.x.com "/1.1/media/upload.json?additional_owners=756201191646691328" --file latte.jpeg --file-field "media" @@ -1109,7 +1109,7 @@ The following table shows the identifier types currently available in each image Follow the provided [instructions to install ExifTool](https://exiftool.org/install.html). There are also software packages offered by [Homebrew](https://formulae.brew.sh/) to further simplify the installation by providing the [exiftool install command](https://formulae.brew.sh/formula/exiftool) for macOS and Linux. Confirm your tool is properly installed by entering exiftool -ver in the command line to return the tool’s version number. Learn more about ExifTool command parameters in [ExifTool documentation](https://exiftool.org/). -Creative partners can assign metadata tags on new or existing creative assets with their X app\_id to the contributor XMP tag, and date tag. The creative assets will follow the existing size restrictions when [Uploading Media](https://developer.x.com/en/docs/twitter-api/v1/media/upload-media/overview).  +Creative partners can assign metadata tags on new or existing creative assets with their X app\_id to the contributor XMP tag, and date tag. The creative assets will follow the existing size restrictions when [Uploading Media](/x-api/media/upload-media).  **Note:** X's use of the contributor XMP tag ensures metadata captures values for campaigns on X exclusively. diff --git a/x-api/activity/introduction.mdx b/x-api/activity/introduction.mdx index c73dea29e..66531538c 100644 --- a/x-api/activity/introduction.mdx +++ b/x-api/activity/introduction.mdx @@ -1,103 +1,148 @@ --- -title: Activity Stream +title: Introduction sidebarTitle: Introduction -description: Stream real-time account activity events -keywords: ["activity stream", "real-time activity", "account activity", "activity API", "event stream"] +keywords: ["activity API", "X Activity API", "activity events", "profile updates", "user activity", "real-time events", "activity stream"] --- import { Button } from '/snippets/button.mdx'; -The Activity Stream endpoint provides real-time delivery of account activity events for subscribed users. Receive events when users post, like, follow, receive DMs, and more. - -## Overview - - - - Events delivered instantly - - - Posts, likes, follows, DMs, and more - - - Subscribe to user activity - - - Events delivered to your server - - +The X Activity API (XAA) endpoint group allows developers to tap in to activity events happening on the X Platform. ---- +A developer can subscribe to events they are interested in such as `profile.update.bio`, `profile.update.profile_picture` etc. and filter for the User ID whose events they want. The matching events for that User ID will be delivered to your app with sub-second latency. -## Event types + +The X Activity API is available as an open beta. While in open beta, please expect potential bugs, breaking changes, or incomplete features. -| 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 | +Your feedback is invaluable—report issues via our [developer community forums](https://devcommunity.x.com/). + ---- +## Delivery Mechanisms -## Endpoints +The X Activity API currently supports the following delivery mechanisms to send events to your app: -| Method | Endpoint | Description | -|:-------|:---------|:------------| -| GET | [`/2/activity/stream`](/x-api/activity/activity-stream) | Connect to activity stream | -| POST | [`/2/activity/subscriptions`](/x-api/activity/create-x-activity-subscription) | Create a subscription | -| GET | [`/2/activity/subscriptions`](/x-api/activity/get-x-activity-subscriptions) | List subscriptions | -| PUT | [`/2/activity/subscriptions/:id`](/x-api/activity/update-x-activity-subscription) | Update a subscription | -| DELETE | [`/2/activity/subscriptions/:id`](/x-api/activity/deletes-x-activity-subscription) | Delete a subscription | +- Persistent HTTP stream +- [Webhook](/x-api/webhooks/introduction) ---- +## Supported Event Types -## How it works +Currently, X Activity API supports the following event types, organized by category: -1. **Create subscription** — Subscribe to a user's activity -2. **Connect to stream** — Establish persistent connection -3. **Receive events** — Get real-time activity events -4. **Process events** — Handle events in your application +### Profile Events ---- +Profile events are triggered when a user makes changes to their profile information. -## Example: Connect to stream +| Event Name | Description | Filters | +| --- | --- | --- | +| `profile.update.bio` | Fired when a user updates their profile bio | `user_id` | +| `profile.update.profile_picture` | Fired when a user updates their profile picture | `user_id` | +| `profile.update.banner_picture` | Fired when a user updates their profile banner | `user_id` | +| `profile.update.screenname` | Fired when a user updates their display name | `user_id` | +| `profile.update.geo` | Fired when a user updates their profile location | `user_id` | +| `profile.update.url` | Fired when a user updates their profile website URL | `user_id` | +| `profile.update.verified_badge` | Fired when a user updates their verified badge | `user_id` | +| `profile.update.affiliate_badge` | Fired when a user updates their affiliate badge | `user_id` | -```python -import requests +### Follow Events -def stream_activity(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")) -``` +Follow events are triggered when a user follows or unfollows another user. ---- +| Event Name | Description | Filters | +| --- | --- | --- | +| `follow.follow` | Fired when a user follows another user | `user_id` | +| `follow.unfollow` | Fired when a user unfollows another user | `user_id` | + +### Spaces Events + +Spaces events are triggered when a user starts or ends a Space. + +| Event Name | Description | Filters | +| --- | --- | --- | +| `spaces.start` | Fired when a user starts a Space | `user_id` | +| `spaces.end` | Fired when a user ends a Space | `user_id` | + +### Chat Events + +Chat events are triggered when a user sends or receives a direct message. + +| Event Name | Description | Filters | +| --- | --- | --- | +| `chat.received` | Fired when a user receives a direct message | `user_id` | +| `chat.sent` | Fired when a user sends a direct message | `user_id` | + +### News Events + +News events provide updates on trending topics and headlines curated by Grok. + +| Event Name | Description | Filters | +| --- | --- | --- | +| `news.new` | New grok-curated trends and headlines | `keyword` | + + +**Enterprise Only:** The `news.new` event is only available to Enterprise and Partner tier accounts at this time. + + +In future releases, XAA will expand to support additional event types including social interactions, content engagement, monetization features, and more. We will continue to update our docs when new event types become available. -## Getting started + +**Note:** XAA does not deliver posts. For real-time post delivery, see our [Filtered Stream](/x-api/posts/filtered-stream/introduction) endpoint, which allows developers to filter for and stream posts in real-time. + + +## Event Privacy and Authentication + +The X Activity API distinguishes between **public events** and **private events** as at parity with the X app as explained below. + +### Public Events + +Public events are activities that a public user account perform publicly that are visible to all X users. These events are visible to all users on the X platform and don't require OAuth authentication from the user in order to view. + +**Current public events:** +- Profile updates (bio, picture, banner, location, URL, username changes) + +For these public events, you can create subscriptions by specifying the user ID in your filter and receive them via XAA. + +### Private Events + +Private events are activities that require explicit user consent through OAuth authentication. A User has to authenticate via X and give explicit permission to a developer app to access these events. + +**Authentication requirements for private events:** +- The user must authenticate your application via OAuth 2.0 +- Your application must obtain appropriate OAuth scopes +- The user must explicitly grant permission for your app to access these events +- Subscriptions for private events can only be created for users who have authorized your application + +## Subscription Limits + +The X Activity API has different subscription limits based on your account tier: + +| Package Tier | Maximum Subscriptions | +| --- | --- | +| Self-serve | 1000 | +| Enterprise | 50,000 | +| Partner | 100,000 | + +## Endpoints + +| Method | Endpoint | Description | +|:-------|:---------|:------------| +| GET | [`/2/activity/stream`](/x-api/activity/activity-stream) | Connect to activity stream | +| POST | [`/2/activity/subscriptions`](/x-api/activity/create-x-activity-subscription) | Create a subscription | +| GET | [`/2/activity/subscriptions`](/x-api/activity/get-x-activity-subscriptions) | List subscriptions | +| PUT | [`/2/activity/subscriptions/:id`](/x-api/activity/update-x-activity-subscription) | Update a subscription | +| DELETE | [`/2/activity/subscriptions/:id`](/x-api/activity/deletes-x-activity-subscription) | Delete a subscription | -**Prerequisites** +**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). -- 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) +Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access). - - - 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 32abb85dd..9328149a5 100644 --- a/x-api/activity/quickstart.mdx +++ b/x-api/activity/quickstart.mdx @@ -1,184 +1,294 @@ --- title: Quickstart sidebarTitle: Quickstart -description: Set up activity stream subscriptions and receive events -keywords: ["activity quickstart", "activity stream tutorial", "subscription guide"] +keywords: ["activity API quickstart", "X Activity API quickstart", "activity quickstart", "activity tutorial", "activity guide"] --- import { Button } from '/snippets/button.mdx'; -This guide walks you through setting up activity stream subscriptions to receive real-time account activity events. +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 **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 +- Your App's [Bearer Token](/resources/fundamentals/authentication) --- - - - 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" - } - payload = { - "user_id": "2244994945", - "event_types": ["tweet_create_events", "favorite_events", "follow_events"] - } +## Getting User IDs - response = requests.post(url, headers=headers, json=payload) - print(response.json()) - ``` - - +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: - **Response:** +**Look up a user's ID by username:** - ```json - { - "data": { - "id": "1234567890", - "user_id": "2244994945", - "event_types": ["tweet_create_events", "favorite_events", "follow_events"], - "created_at": "2024-01-15T10:00:00.000Z" - } +```bash +curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" "https://api.x.com/2/users/by/username/xdevelopers" +``` + +**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" } - ``` - - - - 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 +} +``` + +--- + +## 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 `profile.update.bio`. + +We'll pass X Developer's user ID: `2244994945`, and an optional tag: + +```json +{ + "event_type": "profile.update.bio", + "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": "profile.update.bio", + "filter": { + "user_id": "2244994945" + }, + "tag": "Xdevelopers' bio updates" + }' +``` + +Upon successful request, your subscription will be created: + +```json +{ + "data":[ { - "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" - } - ] + "created_at":"2025-10-09T16:35:08.000Z", + "event_type":"profile.update.bio", + "filter":{ + "user_id":"2244994945" + }, + "subscription_id":"1976325569252868096", + "tag": "Xdevelopers' bio updates", + "updated_at":"2025-10-09T16:35:08.000Z" } - ``` - - + ], + "meta": { + "total_subscriptions": 1 + } +} +``` --- -## Available event types +## 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: -| 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 | +```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": "profile.update.bio", + "tag": "Xdevelopers' bio updates", + "payload": { + "before": "Mars & Cars", + "after": "Mars, Cars & AI" + } + } +} +``` --- -## 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" - ``` - - +## 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": "profile.update.bio", + "filter": { + "user_id": "123456789" + }, + "tag": "my bio updates", + "webhook_id": "1976325569252868099" + }' +``` + + +- 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": "profile.update.bio", + "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": [ + { + "subscription_id": "1976325569252868096", + "event_type": "profile.update.bio", + "filter": { + "user_id": "123456789" + }, + "created_at": "2025-10-09T16:35:08.000Z", + "updated_at": "2025-10-10T03:50:59.000Z" + }, + { + "subscription_id": "1976325569252868097", + "event_type": "profile.update.profile_picture", + "filter": { + "user_id": "987654321" + }, + "created_at": "2025-10-08T14:35:08.000Z", + "updated_at": "2025-10-08T14:35:08.000Z" + } + ], + "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. + +```bash +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": "profile.update.bio", + "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 + } +} +``` --- ## Next steps - - Webhook-based alternative - Full endpoint documentation + + Set up webhook delivery + diff --git a/x-api/chat/add-public-key.mdx b/x-api/chat/add-public-key.mdx new file mode 100644 index 000000000..223225bec --- /dev/null +++ b/x-api/chat/add-public-key.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /2/users/{id}/public_keys +--- \ No newline at end of file diff --git a/x-api/chat/get-chat-conversation.mdx b/x-api/chat/get-chat-conversation.mdx new file mode 100644 index 000000000..2d9be686d --- /dev/null +++ b/x-api/chat/get-chat-conversation.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /2/chat/conversations/{conversation_id} +--- \ No newline at end of file diff --git a/x-api/chat/get-chat-conversations.mdx b/x-api/chat/get-chat-conversations.mdx new file mode 100644 index 000000000..a7a127f69 --- /dev/null +++ b/x-api/chat/get-chat-conversations.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /2/chat/conversations +--- \ No newline at end of file diff --git a/x-api/chat/get-user-public-keys.mdx b/x-api/chat/get-user-public-keys.mdx new file mode 100644 index 000000000..804e1cbc7 --- /dev/null +++ b/x-api/chat/get-user-public-keys.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /2/users/{id}/public_keys +--- \ No newline at end of file diff --git a/x-api/chat/send-chat-message.mdx b/x-api/chat/send-chat-message.mdx new file mode 100644 index 000000000..004043586 --- /dev/null +++ b/x-api/chat/send-chat-message.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /2/chat/conversations/{conversation_id}/messages +--- \ No newline at end of file diff --git a/x-api/compliance/streams/introduction.mdx b/x-api/compliance/streams/introduction.mdx index 458508625..2c6f117dd 100644 --- a/x-api/compliance/streams/introduction.mdx +++ b/x-api/compliance/streams/introduction.mdx @@ -6,28 +6,28 @@ keywords: ["compliance streams", "compliance events", "compliance API", "complia 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 edits a Post, it is critical for both X and our developers to honor that person’s expectations and intent. +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 edits a Post, it is critical for both X and our developers to honor that person's expectations and intent. -Real-time streams of compliance events provide developers the tools to maintain X data in compliance with the[X Developer Agreement and Policy](https://developer.x.com/en/developer-terms/policy).  +Near real-time streams of compliance events provide developers the tools to maintain X data in compliance with the [X Developer Agreement and Policy](https://developer.x.com/en/developer-terms/policy). -There are two c_ompliance event_ streams, one for Post_ compliance_ events, and one for _User compliance_ events. These streams are available with Enterprise access and are designed to help partners that ingest high volumes of data 'listen' for compliance events such as Post edit events. +There are two compliance event streams, one for _Post compliance_ events, and one for _User compliance_ events. These streams are available with Enterprise access and are designed to help partners that ingest high volumes of data 'listen' for compliance events such as Post edit events. -These streams provide the following events:  +These streams provide the following events: -**Post compliance stream: ** +**Post compliance stream:** -* delete \- indicates that the Post was deleted. +* **delete** \- indicates that the Post was deleted. -* tweet_edit \- indicates a Post has been edited and provides the ID of the updated Post.  +* **tweet_edi** \- indicates a Post has been edited and provides the ID of the updated Post. -* withheld \- indicates that the Post has been withheld from one or more countries.  +* **withheld** \- indicates that the Post has been withheld from one or more countries. -* drop \- indicates that the Post should be removed from public view. +* **drop** \- indicates that the Post should be removed from public view. -* undrop \- indicates that the Post may be displayed again and treated as public. +* **undrop** \- indicates that the Post may be displayed again and treated as public. -**User compliance stream: ** +**User compliance stream:** * **user_delete** \- indicates that the User account was deleted @@ -59,3 +59,22 @@ Learn more about getting access to the X API v2 endpoints in our [getting start Try with API Explorer + +--- + +## Streaming fundamentals + + + + Best practices for streaming clients + + + Reconnect gracefully + + + Handle high throughput + + + Build resilient applications + + diff --git a/x-api/connections/terminate-connections-by-endpoint.mdx b/x-api/connections/terminate-connections-by-endpoint.mdx new file mode 100644 index 000000000..3b36dadfc --- /dev/null +++ b/x-api/connections/terminate-connections-by-endpoint.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /2/connections/{endpoint_id} +--- \ No newline at end of file diff --git a/x-api/connections/terminate-multiple-connections.mdx b/x-api/connections/terminate-multiple-connections.mdx new file mode 100644 index 000000000..5e2e0e1a2 --- /dev/null +++ b/x-api/connections/terminate-multiple-connections.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /2/connections +--- \ No newline at end of file diff --git a/x-api/fundamentals/consuming-streaming-data.mdx b/x-api/fundamentals/consuming-streaming-data.mdx new file mode 100644 index 000000000..9648fa71d --- /dev/null +++ b/x-api/fundamentals/consuming-streaming-data.mdx @@ -0,0 +1,187 @@ +--- +title: Consuming streaming data +sidebarTitle: Consuming streaming data +description: Best practices for building clients that consume X streaming APIs +keywords: ["consuming streaming data", "streaming data", "process stream", "handle stream", "stream processing", "stream consumption", "streaming API"] +--- + +Learn how to build robust clients that consume data from X streaming endpoints. + +## Streaming endpoints overview + +X streaming endpoints are categorized by volume: + +| Category | Endpoints | Description | +|:---------|:----------|:------------| +| **High-volume streams** | **Firehose**, [Volume Streams](/x-api/posts/volume-streams/introduction) (1% and 10% sampled) | Deliver large volumes of Post data without filtering. Designed for comprehensive coverage of platform activity. | +| **Lower-volume streams** | [Filtered Stream](/x-api/posts/filtered-stream/introduction) | Allow you to specify keywords or criteria to receive only matching Posts. Ideal for targeted monitoring. | +| **Low-latency streams** | [Powerstream](/x-api/powerstream/introduction) | Optimized for speed with minimal delay. Best for use cases requiring real-time data delivery. | + +## Data delivery and latency + +X API streaming endpoints prioritize **data hydration and delivery**. To ensure you receive hydrated Post data with all metadata, these streams have a **P99 latency of approximately 6-7 seconds**. + +### Delivery guarantees + +| Metric | Value | +|:-------|:------| +| Retry strategy | Exponential back-off | +| P99 latency | ~6-7 seconds | + +### High-volume streams + +For high-volume streams like Firehose and Sample (1% and 10%) streams, **99% of all Posts are delivered within 1 minute** of their creation time. This guarantee is based on the high-volume, continuous nature of the full data feed. + +### Lower-volume streams + +For streams with potentially lower-volume like Filtered Stream, delivery times may vary based on the specificity of your filters and the resulting volume of matching Posts. + + +If your use case requires lower latency, consider [Powerstream](/x-api/powerstream/introduction), which is optimized for speed and delivers data with minimal delay. + + + +Latency and data delivery may be negatively impacted during outages. Refer to the [status page](https://docs.x.com/status) for updates when issues occur. + + +--- + +## Client design + +When building a solution with streaming endpoints, your client needs to: + +1. **Establish an HTTPS streaming connection** to the streaming endpoint +2. **Handle low data volumes** — Maintain the connection, detecting Post objects and keep-alive signals +3. **Handle high data volumes** — Decouple stream ingestion from processing using asynchronous processes, and ensure client-side buffers are flushed regularly +4. **Manage volume tracking** on the client side +5. **Detect disconnections** and reconnect automatically + +For endpoints with rules (like Filtered Stream and Powerstream), your client should also asynchronously send requests to manage rules without disconnecting from the stream. + +--- + +## Connecting to a streaming endpoint + +Establishing a connection to X API streaming endpoints means making a very long-lived HTTP request and parsing the response incrementally. Conceptually, you can think of it as downloading an infinitely long file over HTTP. + +Once a connection is established, the X server will deliver Post events through the connection as long as it remains open. + +```python +import requests + +def connect_to_stream(url, bearer_token): + headers = {"Authorization": f"Bearer {bearer_token}"} + + response = requests.get(url, headers=headers, stream=True) + + for line in response.iter_lines(): + if line: + # Process the Post + print(line.decode("utf-8")) +``` + +--- + +## Consuming data + +JSON objects from the stream may have fields in any order, and not all fields will be present in all circumstances. Posts are not delivered in sorted order, and duplicate messages may occur. Over time, new message types may be added to the stream. + +Your client must tolerate: + +- Fields appearing in any order +- Unexpected or missing fields +- Non-sorted Posts +- Duplicate messages +- New message types appearing at any time + +--- + +## Buffering + +Streaming endpoints send data as quickly as it becomes available, which can result in high volumes. If the X server cannot write new data to the stream (for example, if your client is not reading fast enough), it will buffer content on its end. However, when this buffer is full, the connection will be dropped and buffered Posts will be lost. + +One way to identify when your app is falling behind is to compare the timestamp of received Posts with the current time and track this over time. + +To minimize stream backups: + +- **Read the stream quickly** — Don't do processing work as you read. Hand activities to another thread/process/data store for asynchronous processing +- **Ensure sufficient bandwidth** — Your data center needs inbound bandwidth for large sustained volumes as well as spikes (5-10x normal volume) + +--- + +## Responding to system messages + +### Keep-alive signals + +At least every 20 seconds, the stream sends a keep-alive signal (heartbeat) in the form of a `\r\n` carriage return through the open connection. This prevents your client from timing out. Your client should be tolerant of these characters. + +If your client implements a read timeout on your HTTP library, it can rely on the HTTP protocol to throw an event if no data is read within this period. It's recommended to wrap HTTP methods with error/event handlers to detect these timeouts and trigger a reconnect. + +### Error messages + +Streaming endpoints may deliver in-stream error messages. Your client should be tolerant of changing message payloads. + +Example error message format: + +```json +{ + "errors": [{ + "title": "operational-disconnect", + "disconnect_type": "UpstreamOperationalDisconnect", + "detail": "This stream has been disconnected upstream for operational reasons.", + "type": "https://api.x.com/2/problems/operational-disconnect" + }] +} +``` + + +Error messages indicating a force disconnect due to a full buffer may never reach your client if the backup prevents delivery. Your app should not depend solely on these messages to initiate reconnection. + + +--- + +## Usage tracking + +Monitor your stream data volumes for unexpected deviations. A significant decrease in volume may indicate an issue other than disconnection — the stream would still receive keep-alive signals and some data, but reduced Post volume should prompt investigation. + +To create monitoring: + +1. Track the number of Posts expected in a set time period +2. If volume falls below a threshold and doesn't recover, initiate alerts +3. Also monitor for large increases, especially when modifying rules or during events that spike Post activity + + +Posts delivered through streaming endpoints count towards your monthly Post volume. Track and adjust consumption to optimize usage. If volume is high, consider adding a `sample:` operator to rules to reduce matching from 100% to `sample:50` or `sample:25`. + + +--- + +## Multi-threaded processing + +Building a multi-threaded application is key for handling high-volume streams. A best practice: + +1. **Stream thread** — A lightweight thread that establishes the connection and writes received JSON to a memory structure or buffered stream reader +2. **Processing thread(s)** — Separate threads that consume from the buffer and do the heavy lifting: parsing JSON, preparing database writes, or other application logic + +This design allows your service to scale efficiently as incoming Post volumes change. + +```mermaid actions={false} +flowchart LR + A["Stream Connection
(lightweight)"] --> B["Memory Buffer
(FIFO)"] --> C["Processing Thread(s)
(heavy work)"] +``` + +--- + +## Next steps + + + + Reconnect gracefully when connections drop + + + Handle high throughput streams + + + Build resilient streaming applications + + diff --git a/x-api/fundamentals/conversation-id.mdx b/x-api/fundamentals/conversation-id.mdx index fd518add0..5694bf899 100644 --- a/x-api/fundamentals/conversation-id.mdx +++ b/x-api/fundamentals/conversation-id.mdx @@ -13,12 +13,12 @@ Every reply on X belongs to a conversation thread. The `conversation_id` field l 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 +```mermaid actions={false} +flowchart TD + A["Original post (ID: 1234567890)
← conversation_id for all replies"] --> B["Reply 1 (ID: 1234567891)
conversation_id: 1234567890"] + A --> C["Reply 2 (ID: 1234567892)
conversation_id: 1234567890"] + B --> D["Reply to Reply 1
conversation_id: 1234567890"] + C --> E["Reply to Reply 2
conversation_id: 1234567890"] ``` No matter how deep the thread goes, all posts share the same `conversation_id`. diff --git a/x-api/fundamentals/handling-disconnections.mdx b/x-api/fundamentals/handling-disconnections.mdx new file mode 100644 index 000000000..75c7af96f --- /dev/null +++ b/x-api/fundamentals/handling-disconnections.mdx @@ -0,0 +1,160 @@ +--- +title: Handling disconnections +sidebarTitle: Handling disconnections +description: Best practices for detecting and recovering from streaming disconnections +keywords: ["streaming disconnections", "handle disconnections", "reconnect stream", "stream reconnection", "disconnection handling", "stream errors"] +--- + +Learn how to handle disconnections when using X streaming endpoints including [Filtered Stream](/x-api/posts/filtered-stream/introduction), [Volume Streams](/x-api/posts/volume-streams/introduction), [Powerstream](/x-api/powerstream/introduction), and [Compliance Streams](/x-api/compliance/streams/introduction). + +## What is a disconnection? + +Establishing a connection to streaming APIs means making a very long-lived HTTPS request and parsing the response incrementally. When connecting to a streaming endpoint, you should form an HTTPS request and consume the resulting stream for as long as practical. + +X servers will hold the connection open indefinitely, barring: + +- Server-side errors +- Excessive client-side lag +- Network issues +- Routine server maintenance +- Duplicate logins + +With streaming connections, disconnections **will** occur and should be expected. Your application must include reconnection logic. + +--- + +## Why connections disconnect + +Possible reasons for disconnections: + +| Reason | Description | +|:-------|:------------| +| Authentication error | Wrong token or incorrect authentication method | +| Server restart | Code deploy on X side — should be expected and designed around | +| Client too slow | Your client isn't keeping up with volume. The server-side message queue grows too large and the connection closes | +| Quota exceeded | Your account exceeded daily/monthly Post quota | +| Too many connections | You have too many active redundant connections | +| Sudden read stop | The rate of Posts being read drops suddenly | +| Network issues | Connectivity problems between server and client | +| Server maintenance | Temporary server-side issue or scheduled maintenance (check the [status page](https://api.twitterstat.us/)) | + +--- + +## Common disconnection errors + +### Operational disconnect + +```json +{ + "errors": [{ + "title": "operational-disconnect", + "disconnect_type": "UpstreamOperationalDisconnect", + "detail": "This stream has been disconnected upstream for operational reasons.", + "type": "https://api.x.com/2/problems/operational-disconnect" + }] +} +``` + +### Too many connections + +```json +{ + "title": "ConnectionException", + "detail": "This stream is currently at the maximum allowed connection limit.", + "connection_issue": "TooManyConnections", + "type": "https://api.x.com/2/problems/streaming-connection" +} +``` + +--- + +## Detecting disconnections + +The streaming endpoints provide a 20-second keep-alive heartbeat (a newline character). Use this signal to detect disconnections: + +1. Your code should detect when fresh content and the heartbeat stop arriving +2. If no data is received for 20 seconds, trigger reconnection logic +3. Some HTTP clients allow you to specify a read timeout — set this to 20 seconds + +--- + +## Reconnection strategy + +Once an established connection drops, attempt to reconnect immediately. If the reconnect fails, use backoff strategies based on the error type: + +### TCP/IP network errors + +Back off **linearly**. These problems are generally temporary. + +- Increase delay by 250ms each attempt +- Maximum delay: 16 seconds + +### HTTP errors + +Back off **exponentially** for HTTP errors where reconnecting is appropriate. + +- Start with 5 second wait +- Double each attempt +- Maximum delay: 320 seconds + +### Rate limit errors (HTTP 429) + +Back off **exponentially** with longer initial wait. + +- Start with 1 minute wait +- Double each attempt +- Each 429 received increases the wait time until rate limiting expires + +--- + +## Rate limits and usage + +Streaming connection responses return three headers to help you understand limits: + +| Header | Description | +|:-------|:------------| +| `x-rate-limit-limit` | Number of allotted requests during the 15-minute window | +| `x-rate-limit-remaining` | Requests made so far in the 15-minute window | +| `x-rate-limit-reset` | UNIX timestamp when the window resets | + +To track how many Posts have been delivered, implement metering logic on the client side so consumption can be measured and paused if needed. + +--- + +## Reconnection best practices + +### Use a FIFO queue architecture + +Your stream client should insert incoming Posts into a first-in, first-out (FIFO) queue or similar memory structure. A separate process/thread should consume Posts from that queue for parsing and storage. This design scales efficiently when Post volumes change dramatically. + +### Test backoff strategies + +Test your backoff implementation using invalid authorization credentials and examine reconnect attempts. A good implementation will not receive any 429 responses. + +### Issue alerts for multiple reconnects + +If your client reaches its upper threshold of time between reconnects, send notifications so you can triage connection issues. + +### Handle DNS changes + +Ensure your client process honors DNS Time To Live (TTL). Some stacks cache resolved addresses for the process duration and won't pick up DNS changes within the TTL. Aggressive caching leads to service disruptions as X shifts load between IP addresses. + +### Set User-Agent header + +Include your client's version in the `User-Agent` HTTP header. This is critical for diagnosing issues on X's end. If your environment precludes setting `User-Agent`, use the `x-user-agent` header instead. + +--- + +## Next steps + + + + Recover missed data after disconnections + + + Build robust streaming clients + + + Handle high throughput streams + + diff --git a/x-api/fundamentals/high-volume-capacity.mdx b/x-api/fundamentals/high-volume-capacity.mdx new file mode 100644 index 000000000..bccc71c1d --- /dev/null +++ b/x-api/fundamentals/high-volume-capacity.mdx @@ -0,0 +1,148 @@ +--- +title: Handling high-volume capacity +sidebarTitle: High volume capacity +description: Best practices for handling high-volume streaming data events +keywords: ["high volume", "capacity handling", "stream capacity", "volume management", "high throughput", "scaling stream"] +--- + +Learn how to prepare for high-volume events when using X streaming endpoints including [Filtered Stream](/x-api/posts/filtered-stream/introduction), [Volume Streams](/x-api/posts/volume-streams/introduction), [Powerstream](/x-api/powerstream/introduction), and [Compliance Streams](/x-api/compliance/streams/introduction). + +## Planning for high-volume events + +Major national and global events are often accompanied by dramatic spikes in user activity across social media. Sometimes these events are known in advance: + +- Super Bowl +- Political elections +- New Year's celebrations worldwide + +Other times, spikes are unexpected: + +- Natural disasters +- Unplanned political events +- Pop culture moments +- Health emergencies + +These bursts may be short-lived (seconds) or sustained over several minutes. Proper preparation helps your applications handle these spikes. + +--- + +## Review your stream rules + +Before high-volume events: + +- Certain keywords can skyrocket during events (e.g., brand mentions when a brand sponsors a major sporting event) +- Remove unnecessary or overly generic rules that may generate excessive activity volumes +- Communicate with stakeholders prior to known high-volume events to help them plan appropriately + +--- + +## Stress test your application + +Anticipate that burst volumes may reach **5-10x average daily consumption levels**. Depending on your rules, the increase may be much higher. + +Test your application with simulated high volumes to identify bottlenecks before real events occur. + +--- + +## Understand delivery caps + +Flow and delivery caps are based on your access level: + +| Access Level | Delivery Cap | +|:-------------|:-------------| +| Academic | 250 Posts/second | +| Enterprise | Posts/second set at access level | + +--- + +## Stay connected + +With streams, staying connected is essential to avoid missing data. + +Your client application should: + +1. **Detect disconnects** immediately +2. **Retry connections** using appropriate backoff strategies +3. **Use exponential backoff** if reconnect attempts fail + +See [Handling disconnections](/x-api/fundamentals/handling-disconnections) for detailed reconnection strategies. + +--- + +## Build client-side buffering + +Building a multi-threaded application is key for handling high-volume streams: + +### Recommended architecture + +1. **Stream thread** (lightweight) + - Establishes the streaming connection + - Writes received JSON to a memory structure or buffered stream reader + - Handles incoming data only — no processing + +2. **Memory buffer** + - Grows and shrinks as needed + - Acts as a shock absorber for volume spikes + +3. **Processing thread(s)** (heavy lifting) + - Consumes from the buffer + - Parses JSON + - Prepares database writes + - Handles application logic + +```mermaid actions={false} +flowchart LR + A["Stream Connection
(lightweight)"] --> B["Memory Buffer
(expandable)"] --> C["Processing Thread(s)
(scalable)"] +``` + +--- + +## Consider time zones + +Global events happen in global time zones. Events may occur: + +- After business hours +- Over weekends +- During holidays + +Ensure your team is prepared for spikes outside normal business hours. Consider: + +- Automated alerting systems +- On-call rotations +- Automated scaling responses + +--- + +## Monitoring recommendations + +1. **Track Post volume** in real-time +2. **Set up alerts** for volume thresholds (both increases and decreases) +3. **Monitor buffer sizes** to detect when your processing is falling behind +4. **Compare Post timestamps** to current time to identify lag + +--- + +## Emergency measures + +Implement safeguards for extreme circumstances: + +- **Automated alerts** when volume passes preset thresholds +- **Automated rule deletion** for rules bringing in excessive data +- **Stream disconnection** in extreme circumstances to protect your systems +- **Sampling operators** — add `sample:` to rules to reduce matching volume + +--- + +## Next steps + + + + Build robust streaming clients + + + Reconnect gracefully + + + Build resilient applications + + diff --git a/x-api/fundamentals/recovery-and-redundancy.mdx b/x-api/fundamentals/recovery-and-redundancy.mdx new file mode 100644 index 000000000..44005386d --- /dev/null +++ b/x-api/fundamentals/recovery-and-redundancy.mdx @@ -0,0 +1,161 @@ +--- +title: Recovery and redundancy +sidebarTitle: Recovery and redundancy +description: Features for recovering missed data and building resilient streaming applications +keywords: ["recovery", "redundancy", "stream recovery", "reconnect", "fault tolerance", "stream redundancy", "error recovery", "backfill"] +--- + +Learn how to maximize connection time and recover missed data when using X streaming endpoints including [Filtered Stream](/x-api/posts/filtered-stream/introduction), [Volume Streams](/x-api/posts/volume-streams/introduction), [Powerstream](/x-api/powerstream/introduction), and [Compliance Streams](/x-api/compliance/streams/introduction). + +## Overview + +When consuming streaming data, maximizing connection time and receiving all matched data is a fundamental goal. This requires: + +- Taking advantage of redundant connections +- Automatically detecting disconnections +- Reconnecting quickly +- Having a plan for recovering lost data + +--- + +## Redundant connections + +A redundant connection allows you to establish more than one simultaneous connection to a stream. This provides redundancy by connecting with two separate consumers, receiving the same data through both connections. + +Benefits: +- Hot failover if one stream disconnects +- Protection if your primary server fails +- Continuous data delivery during reconnection + +### How to use + +Simply connect to the same stream URL with a second client. Data will be sent through both connections. + + +Redundant connections are available for Enterprise access. Filtered Stream allows up to two redundant connections for Enterprise projects. Check your specific endpoint documentation for connection limits. + + +--- + +## Backfill + +After detecting a disconnection, your system should track how long the disconnection lasted to determine the appropriate recovery method. + +### For disconnections of 5 minutes or less + +Use the **backfill parameter** when reconnecting to receive Posts matched during the disconnection period. + +| Endpoint | Parameter | Example | +|:---------|:----------|:--------| +| Filtered Stream | `backfill_minutes` | `?backfill_minutes=5` | +| Powerstream | `backfillMinutes` | `?backfillMinutes=5` | + +Example request: + +```bash +curl 'https://api.x.com/2/tweets/search/stream?backfill_minutes=5' \ + -H "Authorization: Bearer $ACCESS_TOKEN" +``` + + +**Important considerations:** +- Older Posts are generally delivered first, before newly matched Posts +- Posts are **not** deduplicated — if you were disconnected for 90 seconds but request 2 minutes of backfill, you'll receive 30 seconds of duplicate Posts +- Your system should be tolerant of duplicates +- Backfill is available with Enterprise access + + +--- + +## Recovery + +For disconnections lasting **longer than 5 minutes**, use the Recovery feature to replay missed data from within the last 24 hours. + +### How Recovery works + +1. Make a connection request with `start_time` and `end_time` parameters +2. Recovery re-streams the specified time period +3. Once complete, the connection disconnects + +### Parameters + +| Parameter | Type | Description | +|:----------|:-----|:------------| +| `start_time` | ISO 8601 date | Start time to recover from (UTC) | +| `end_time` | ISO 8601 date | End time to recover to (UTC) | + +### Example request + +**Filtered Stream:** + +```bash +curl 'https://api.x.com/2/tweets/search/stream?start_time=2022-07-12T15:10:00Z&end_time=2022-07-12T15:20:00Z' \ + -H "Authorization: Bearer $ACCESS_TOKEN" +``` + +**Powerstream:** + +```bash +curl 'https://api.x.com/2/powerstream?startTime=2022-07-12T15:10:00Z&endTime=2022-07-12T15:20:00Z' \ + -H "Authorization: Bearer $ACCESS_TOKEN" +``` + + +**Recovery limits:** +- Available for Enterprise access +- Recovery window: up to 24 hours in the past +- Filtered Stream allows 2 concurrent recovery jobs + + +--- + +## Alternative recovery: Search + +If you don't have access to backfill or recovery features, or if the disconnection exceeded 24 hours, you can use the [Search Posts endpoint](/x-api/posts/search/introduction) to request missed data. + + +**Matching differences:** +The Search Posts endpoint does not include the `sample:`, `bio:`, `bio_name:`, or `bio_location:` operators, and has certain differences in matching behavior with accents and diacritics. This means you may not fully recover all Posts that would have been received via streaming endpoints. + + +--- + +## Recovery decision tree + +```mermaid actions={false} +flowchart TD + A[Disconnection detected] --> B{How long was the disconnection?} + B -->|5 minutes or less| C[Use backfill parameter] + B -->|5 minutes to 24 hours| D[Use Recovery feature] + B -->|More than 24 hours| E[Use Search Posts endpoint] +``` + +--- + +## Best practices + +1. **Track disconnection duration** — Your system should note when disconnections occur and how long they last + +2. **Implement automatic recovery** — Based on disconnection duration, automatically choose the appropriate recovery method + +3. **Handle duplicates** — Both backfill and recovery may deliver duplicate Posts; implement deduplication logic + +4. **Use redundant connections** — Prevent data loss by maintaining multiple connections when available + +5. **Monitor recovery jobs** — Track the status and completion of recovery operations + +--- + +## Next steps + + + + Detect and handle disconnections + + + Build robust streaming clients + + + Handle high throughput streams + + diff --git a/x-api/getting-started/pricing.mdx b/x-api/getting-started/pricing.mdx index d3db82632..2616bc3de 100644 --- a/x-api/getting-started/pricing.mdx +++ b/x-api/getting-started/pricing.mdx @@ -2,14 +2,14 @@ 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", "auto-recharge", "spending limit", "spend cap"] +keywords: ["X API pricing", "API pricing", "pay-per-usage", "API credits", "billing", "deduplication", "API costs", "auto-recharge", "spending limit", "spend cap", "xAI credits", "free credits", "Grok API"] --- 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. - + --- @@ -30,6 +30,14 @@ The X API uses **pay-per-usage** pricing. No subscriptions, no monthly caps—pa
+ +Earn free [xAI API](https://docs.x.ai) credits when you purchase X API credits—up to 20% back based on your spend. [Learn more](#free-xai-api-credits) + + + +If you are on a legacy subscription package (Basic or Pro), you can opt in to Pay-per-use pricing directly from the [Developer Console](https://console.x.com). If you'd like to switch back to your legacy plan at any time, you can do so from the settings page within the Developer Console. + + --- ## Deduplication @@ -86,6 +94,52 @@ Use spending limits to prevent unexpected charges, especially during development --- +## Free xAI API Credits + +When you purchase X API credits, you can earn free [xAI API](https://docs.x.ai) credits based on your cumulative spend during a billing cycle. + + +To receive free xAI credits, you must link your xAI team to your X developer account. You can do this by visiting your account settings in the [developer console](https://console.x.com). + + +### How it works + +Your cumulative spend is tracked throughout each billing cycle. As you cross spending thresholds, you unlock higher reward rates. When a new billing cycle starts, your cumulative spend resets to $0. + +| Cumulative spend | Rate | +|:-----------------|:-----| +| $0 – $199 | 0% | +| $200 – $499 | 10% | +| $500 – $999 | 15% | +| $1,000+ | 20% | + + +The rate applies to your **entire cumulative balance**, but you only receive the delta—what's newly owed minus what was already credited. + + +### Example + +Suppose you make several purchases throughout a billing cycle: + +| Purchase | Rate | Total owed | Already credited | You receive | +|:---------|:-----|:-----------|:-----------------|:------------| +| $100 | 0% | $0 | $0 | **$0** | +| $100 | 10% | $20 | $0 | **$20** | +| $150 | 10% | $35 | $20 | **$15** | +| $150 | 15% | $75 | $35 | **$40** | +| $250 | 15% | $112.50 | $75 | **$37.50** | +| $250 | 20% | $200 | $112.50 | **$87.50** | +| | | | | | +| **$1,000** | | | | **$200** | + +This is the same amount you'd receive from a single $1,000 purchase—the order and size of purchases doesn't affect your total rewards. + + +View your xAI credit balance and manage your account at [console.x.ai](https://console.x.ai). For more details on xAI API billing, see the [xAI billing documentation](https://docs.x.ai/docs/key-information/billing). + + +--- + ## Monitoring usage Track your API usage programmatically with the [Usage endpoint](/x-api/usage/introduction): diff --git a/x-api/introduction.mdx b/x-api/introduction.mdx index 56095e579..e0236ac0b 100644 --- a/x-api/introduction.mdx +++ b/x-api/introduction.mdx @@ -68,6 +68,10 @@ The X API uses **pay-per-usage** pricing. No subscriptions, no monthly caps—pa
+ +Earn free [xAI API](https://docs.x.ai) credits when you purchase X API credits—up to 20% back based on your spend. [Learn more](/x-api/getting-started/pricing#free-xai-api-credits) + +
@@ -90,10 +94,10 @@ The X API uses **pay-per-usage** pricing. No subscriptions, no monthly caps—pa 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. + Get posts delivered in near real-time as they're published. Define up to 1,000 filtering rules to receive only matching posts. ```bash # Add a rule diff --git a/x-api/marketplace/get-marketplace-handle-availability.mdx b/x-api/marketplace/get-marketplace-handle-availability.mdx new file mode 100644 index 000000000..618c8e1e7 --- /dev/null +++ b/x-api/marketplace/get-marketplace-handle-availability.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /2/marketplace/handles/{handle}/availability +--- \ No newline at end of file diff --git a/x-api/media/v1/upload-media.mdx b/x-api/media/v1/upload-media.mdx deleted file mode 100644 index 0a92fbf8e..000000000 --- a/x-api/media/v1/upload-media.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Upload media -keywords: ["upload media", "media upload v1", "upload images", "upload videos", "media upload API", "upload media v1.1"] ---- - -## Overview - -A media object represents a single photo, video or animated GIF. Media objects are used by many endpoints within the Twitter API, and may be included in Tweets, Direct Messages, user profiles, advertising creatives and elsewhere. Each media object may have multiple display or playback variants, with different resolutions or formats. - -## Media types & size restrictions - -Size restrictions for uploading via API - -- Image 5 MB -- GIF 15 MB -- Video 512 MB (when using media_category=amplify) - -## Creation - -Objects such as Tweets, Direct Messages, user profile pictures, hosted Ads cards, etc. can contain one or more media objects. These top-level objects are collectively known as entities. The relevant entity creation API (e.g. POST statuses/update) can be passed one or more media objects using a unique media_id. - -An entity which contains media object(s) can be created by following these steps: - -1. Upload the media file(s) using either the recommended chunked upload (images/GIF/video), or the older simple upload (images only). -2. Receive a media_id from step 1. This step may be repeated multiple times with different media if the entity allows multiple media_id parameters to be passed in. -3. Create the entity by calling the appropriate endpoint, including the media_id and other required parameters. For example, attach a media_id to a Tweet using the POST statuses/update endpoint. - -## Retrieving - -Please refer to the Media Object in the Tweet data dictionary. - -## Right sidebar content -## Guides - -### Media Best Practices - -### Chunked media upload - -## API Reference - -### POST media/upload (INIT) - -### POST media/upload (APPEND) - -### GET media/upload (STATUS) - -### POST media/upload (FINALIZE) - -### POST media/upload - -### POST media/metadata/create - -### POST media/subtitles/delete - -### POST media/subtitles/create \ No newline at end of file diff --git a/x-api/posts/filtered-stream/integrate/consuming-streaming-data.mdx b/x-api/posts/filtered-stream/integrate/consuming-streaming-data.mdx deleted file mode 100644 index 1861a1127..000000000 --- a/x-api/posts/filtered-stream/integrate/consuming-streaming-data.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Consuming streaming data -sidebarTitle: Consuming streaming data -keywords: ["consuming streaming data", "streaming data", "process stream", "handle stream", "stream processing", "stream consumption"] ---- - -### Building a client to consume streaming data - -When using a streaming endpoint, there are some general best practices to consider in order to optimize usage.   -  - -#### Client design - -When building a solution with the filter stream endpoint, you will need a client that can do the following: - -1. Establish an HTTPS streaming connection to the filter stream endpoint. -2. Asynchronously send POST requests to the filter stream rules endpoint to add and delete rules from the stream. -3. Handle low data volumes – Maintain the streaming connection, detecting Post objects and keep-alive signals -4. Handle high data volumes – de-couple stream ingestion from additional processing using asynchronous processes, and ensure client side buffers are flushed regularly. -5. Manage volume consumption tracking on the client side. -6. Detect stream disconnections, evaluate and reconnect to the stream automatically. -   - -#### Connecting to a streaming endpoint - -Establishing a connection to X API v2 streaming endpoints means making a very long lived HTTP request, and parsing the response incrementally. Conceptually, you can think of it as downloading an infinitely long file over HTTP.  Once a connection has been established, the X server will deliver Post events through the connection as long as the connection is open. -  - -#### Consuming data - -Note that the individual fields of JSON objects are not ordered, and not all fields will be present in all circumstances. Similarly, separate activities are not delivered in sorted order, and duplicate messages may be encountered. Keep in mind that over time, new message types may be added and sent through the stream. - -Thus, your client must tolerate: - -* Fields appearing in any order -* Unexpected or missing fields -* Non-sorted Posts -* Duplicate messages -* New arbitrary message types coming down the stream at any time - -In addition to relevant Post data and requested field parameters, the following kinds of messages may be delivered on a stream connection. Note that this list may not be comprehensive—additional objects may be introduced into streams. Ensure that your parser is tolerant of unexpected message formats. -  - -#### Buffering  - -The streaming endpoints will send data to you as quickly as it becomes available, which can result in high volumes in many cases. If the X server cannot write new data to the stream right away (for example if your client is not reading fast enough, see [handling disconnections](/x-api/posts/filtered-stream#what-is-a-disconnection) for more), it will buffer the content on its end to allow your client to catch up. However, when this buffer is full, a forced disconnect will be initiated to drop the connection, and the buffered Posts will be dropped and not resent. See below for more details. - -One way to identify times where your app is falling behind is to compare the timestamp of the Posts being received with the current time, and track this over time. - -Although stream backups cannot ever be completely eliminated due to potential latency and hiccups over the public internet, they can be largely eliminated through proper configuration of your app. To minimize the occurrence of backups: - -* Ensure that your client is reading the stream fast enough. Typically you should not do any real processing work as you read the stream. Read the stream and hand the activity to another thread/process/data store to do your processing asynchronously. -* Ensure that your data center has inbound bandwidth sufficient to accomodate large sustained data volumes as well as significantly larger spikes (e.g. 5-10x normal volume). For filtered stream, the volume and corresponding bandwidth required on your end are wholly dependent on what Posts your rules are matching. -   - -#### Usage tracking and rule management - -As developers expectations around what “normal” data volume should be for their streams, we do not have a general recommendation for a specific percentage decrease/increase or period of time.  - -Consider monitoring your stream data volumes for unexpected deviations. A data volume decrease may be symptomatic of a different issue than a stream disconnection. In such a situation, a stream would still be receiving the keep-alive signal and probably some new activity data. However, a significantly decreased number of Posts should lead you to investigate whether there is anything causing the decrease in inbound data volume to your application or network, check the [status page](https://api.twitterstat.us/) for any related notices. - -To create such monitoring, you could track the number of new Posts you expect to see in a set amount of time. If a stream’s data volume falls far enough below the specified threshold, and does not recover within a set period of time, then alerts and notifications should be initiated. You may also want to monitor for a large increase in data volume, particularly if you are in the process of modifying rules in a filtered stream, or if an event occurs that produces a spike in Post activity. - -It's important to note that Posts delivered through filtered stream do count towards the total monthly Post volume, and you should track and adjust consumption in order to optimize.  If volume is high, consider adding a sample: operator to each of your rules to reduce from 100% matching to sample:50 or sample:25 when necessary. - -Additionally, we encourage you to implement measures within your app that will alert your team if the volume passes a pre-set threshold, and to possibly introduce other measures such as automated deletion of rules that are bringing in too much data, or disconnecting from the stream completely in extreme circumstances. -  - -#### Responding to system messages - -Keep-alive signals -At least every 20 seconds, the stream will send a keep-alive signal, or heartbeat in the form of an \\r\\n carriage return through the open connection to prevent your client from timing out. Your client application should be tolerant of the \\r\\n characters in the stream. - -If your client properly implements a read timeout on your HTTP library, your app will be able to rely on the HTTP protocol and your HTTP library to throw an event if no data is read within this period, and you will not need to explicitly monitor for the \\r\\n character. - -This event will typically be an exception being thrown or some other event depending on the HTTP library used. It is highly recommended to wrap your HTTP methods with error/event handlers to detect these timeouts. On timeout, your application should attempt to reconnect. - -Error messages -The v2 streaming endpoints may also deliver in stream error messages. Provided below is the basic format of these messages, along with some examples. Please note that the messages delivered could change, with new message being introduced. Client applications need to be tolerant of changing system message payloads. - -Note that error messages will link to the documentation describing how to solve the problem. - -Message format: - -```{ - "errors": [{ - "title": "operational-disconnect", - "disconnect_type": "UpstreamOperationalDisconnect", - "detail": "This stream has been disconnected upstream for operational reasons.", - "type": "https://api.x.com/2/problems/operational-disconnect" - }] -} -``` - -Note that error messages indicating a force disconnect for a full buffer may never get to your client, if the backup which caused the force disconnect prevents it from getting through. Accordingly, your app should not depend on these messages to initiate a reconnect. - diff --git a/x-api/posts/filtered-stream/integrate/handling-disconnections.mdx b/x-api/posts/filtered-stream/integrate/handling-disconnections.mdx deleted file mode 100644 index 275773e89..000000000 --- a/x-api/posts/filtered-stream/integrate/handling-disconnections.mdx +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: Handling disconnections -sidebarTitle: Handling disconnections -keywords: ["streaming disconnections", "handle disconnections", "reconnect stream", "stream reconnection", "disconnection handling", "stream errors"] ---- - -### What is a disconnection? - -Establishing a connection to the streaming APIs means making a very long lived HTTPS request, and parsing the response incrementally. When connecting to the filtered stream endpoint, you should form a HTTPS request and consume the resulting stream for as long as is practical. Our servers will hold the connection open indefinitely, barring server-side error, excessive client-side lag, network issues, routine server maintenance, or duplicate logins. With connections to streaming endpoints, it is likely, and should be expected, that disconnections will take place and reconnection logic built. -  - -#### Why a streaming connection might be disconnected - -Your stream can disconnect for a number of reasons. Inspect the error message returned by the stream to understand the reason for the failure. Possible reasons for disconnections are as follows: - -* An authentication error (such as a wrong token or a wrong authentication method being used). -* A streaming server is restarted on the X side. This is usually related to a code deploy and should be generally expected and designed around. -* Your client is not keeping up with the volume of Posts the stream is delivering or is reading data too slowly. Every streaming connection is backed by a queue of messages to be sent to the client. If this queue grows too large over time, the connection will be closed. -* Your account exceeded your daily/monthly quota of Posts. -* You have too many active redundant connections. -* A client stops reading data suddenly. If the rate of Posts being read off of the stream drops suddenly, the connection will be closed. -* Possible networking issues between server and client -* A temporary server side issue, scheduled maintenance and updates. (Check the [status page](https://api.twitterstat.us/)) -   - -#### Common disconnection errors include:  - -```{ - "errors": [{ - "title": "operational-disconnect", - "disconnect_type": "UpstreamOperationalDisconnect", - "detail": "This stream has been disconnected upstream for operational reasons.", - "type": "https://api.x.com/2/problems/operational-disconnect" - }] -} -``` - -``` -{ - "title": "ConnectionException", - "detail": "This stream is currently at the maximum allowed connection limit.", - "connection_issue": "TooManyConnections", - "type": "https://api.x.com/2/problems/streaming-connection" -} -``` - - -#### Anticipating disconnects and reconnecting - -When streaming Posts, the goal is to stay connected for as long as possible, recognizing that disconnects may occur. The endpoint provides a 20-second keep alive heartbeat (it will look like a new line character). Use this signal to detect if you’re being disconnected. - -1. Your code should detect when fresh content and the heartbeat stop arriving. -2. If that happens, your code should trigger a reconnection logic. Some clients and languages allow you to specify a read timeout, which you can set to 20 seconds. -3. Your service should detect these disconnections and reconnect as soon as possible. - - -Once an established connection drops, attempt to reconnect immediately. If the reconnect fails, slow down your reconnect attempts according to the type of error experienced: - -* Back off linearly for TCP/IP level network errors. These problems are generally temporary and tend to clear quickly. Increase the delay in reconnects by 250ms each attempt, up to 16 seconds. -* Back off exponentially for HTTP errors for which reconnecting would be appropriate. Start with a 5 second wait, doubling each attempt, up to 320 seconds. -* Back off exponentially for HTTP 429 errors Rate limit exceeded. Start with a 1 minute wait and double each attempt. Note that every HTTP 429 received increases the time you must wait until rate limiting will no longer be in effect for your account. -   - -#### Recovering lost data - -If you do experience a disconnect, there are some different strategies that you can use to ensure that you receive all of the data that you might have missed. We've documented some key steps that you can take to recover missed data on our integration guide on [recovering data](/x-api/posts/filtered-stream/integrate/recovery-and-redundancy-features).  -  - -#### Rate limits and usage - -To check connection limits response will return three headers. This is useful to understand how many times you can use the rule endpoint, and how many reconnections attempts are allowed for the streaming endpoint. - -* x-rate-limit-limit indicates the number of allotted requests your client is allowed to make during the 15-minute window. - -* x-rate-limit-remaining indicates the number of requests made so far in the 15-minute window. - -* x-rate-limit-reset is a UNIX timestamp indicating when the 15-minute window will restart, resetting x-rate-limit-remaining to 0. - - -The filter stream endpoint does not currently report usage data. To check how many Posts have been delivered, your code can implement a metering logic, so that consumption can be measured and paused if needed.  - -Your code that hosts the client side of the stream simply inserts incoming Posts into a first in, first out (FIFO) queue, or a similar memory structure; a separate process/thread should consume Posts from that queue to parse and prepare content for storage. With this design, you can implement a service that can scale efficiently in case incoming Post volumes changes dramatically. Conceptually, you can think of it as downloading an infinitely long file over HTTP. - -#### Reconnection best practices - -**Test backoff strategies** - -A good way to test a backoff implementation is to use invalid authorization credentials and examine the reconnect attempts. A good implementation will not get any 429 responses. - -**Issue alerts for multiple reconnects** - -If a client reaches its upper threshold of its time between reconnects, it should send you notifications so you can triage the issues affecting your connection. - -**Handle DNS changes** - -Test that your client process honors the DNS Time To live (TTL). Some stacks will cache a resolved address for the duration of the process and will not pick up DNS changes within the prescribed TTL. Such aggressive caching will lead to service disruptions on your client as X shifts load between IP addresses. - -**User Agent** - -Ensure your user-agent HTTP header includes the client’s version. This will be critical in diagnosing issues on X's end. If your environment precludes setting the user-agent field, then set an x-user-agent header. diff --git a/x-api/posts/filtered-stream/integrate/handling-high-volume-capacity.mdx b/x-api/posts/filtered-stream/integrate/handling-high-volume-capacity.mdx deleted file mode 100644 index ff274699d..000000000 --- a/x-api/posts/filtered-stream/integrate/handling-high-volume-capacity.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Handling high-volume capacity -sidebarTitle: Handling high-volume capacity -keywords: ["high volume", "capacity handling", "stream capacity", "volume management", "high throughput", "scaling stream"] ---- ---- - -### How to plan for high-volume social data events - -Major national and global events are often accompanied by dramatic spikes in user activity across social media platforms. Sometimes these events are known about in advance, like the Super Bowl, political elections, and New Year’s celebrations around the world. Other times, the spikes in volume are due to unexpected happenings such as natural disasters, unplanned political events, pop culture moments, or health pandemics like COVID-19. - -These bursts of user activity may sometimes be short-lived (measured in seconds), or they may even be sustained over several minutes’ time. No matter their origin, it is important to consider the impact that they can have on applications consuming data from X. Here are some best practices that will help your team prepare for high-volume social data events. - -#### Review your current filtered stream rules - -* Certain keywords can skyrocket during high volume events, such as brand mentions when a brand sponsors a major sporting event. -* Be careful to avoid any unnecessary or overly generic rules that may generate unnecessary activity volumes. -* Consider communicating with your clients prior to known high-volume events to help them plan appropriately. -   - -#### Stress test your application - -Anticipate that burst volumes may reach 5-10x average daily consumption levels. Depending on your rule set, the increase may be much higher. - -#### Understand delivery caps for connections - -Flow and delivery caps are based on levels of access. This results in a static volume of delivered results for streams. - -* **Academic**: 250 Posts/second -* **Enterprise**: Posts/second is set at access level - -#### Optimize to stay connected - -With streams, staying connected is essential to avoid missing data. Your client application should be able to detect a disconnect and have logic to immediately retry its connection, using an exponential backoff if the reconnect attempt fails. -  - -#### Add built-in buffering on your end - -Building a multi-threaded application is a key strategy for handling high-volume streams. At a high-level, a best practice for managing data streams is to have a separate thread/process that establishes the streaming connection and then writes received JSON activities to a memory structure or a buffered stream reader. This ‘light-weight’ stream processing thread is responsible for handling incoming data, which can be buffered in memory, growing and shrinking as needed. Then a different thread consumes that hash and does the ‘heavy lifting’ of parsing the JSON, preparing database writes, or whatever else your application needs to do. -  - -#### Global events = global time zones - -The events may occur after business hours or over the weekend, so be sure that your team is prepared for spikes to occur outside your normal business hours. - diff --git a/x-api/posts/filtered-stream/integrate/operators.mdx b/x-api/posts/filtered-stream/integrate/operators.mdx index 307a9d509..88310a2b1 100644 --- a/x-api/posts/filtered-stream/integrate/operators.mdx +++ b/x-api/posts/filtered-stream/integrate/operators.mdx @@ -1,60 +1,198 @@ --- -title: Stream Rule Operators +title: Filtered Stream Operators sidebarTitle: Operators +description: Complete list of operators for Filtered Stream rules keywords: ["stream operators", "filter operators", "rule operators", "streaming operators", "filter syntax", "operators guide"] --- -This page provides a list of operators availble when [building rules](/x-api/posts/search/integrate/build-a-query) for Filtered Stream v2. - -## List of operators - -**Note:** For some operators, an alternate name, or alias, is available. - -| **Operator** | **Type** | **Description** | -|:------------------------------|:-------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `keyword` | Standalone | Matches a keyword within the body of a Post. This is a tokenized match, meaning that your keyword string will be matched against the tokenized text of the Post body. Tokenization splits words based on punctuation, symbols, and Unicode basic plane separator characters.
For example, a Post with the text “I like coca-cola” would be split into the following tokens: I, like, coca, cola. These tokens would then be compared to the keyword string used in your rule. To match strings containing punctuation (for example, coca-cola), symbol, or separator characters, you must wrap your keyword in double-quotes.

Example: `pepsi OR cola OR "coca cola"` | -| `emoji` | Standalone | Matches an emoji within the body of a Post. Similar to a keyword, emojis are a tokenized match, meaning that your emoji will be matched against the tokenized text of the Post body.

Note that if an emoji has a variant, you must wrap it in double quotes to add to a rule.

Example: `(😃 OR 😡) 😬` | -| `"exact phrase match"` | Standalone | Matches the exact phrase within the body of a Post.

Example: `("X API" OR #v2) -"filtered stream"` | -| `"keyword1 keyword2"~N` | Standalone | Proximity operator that matches a Post where keywords are within `N` tokens of each other.
Keywords in reverse order can be no more than `N-2` tokens apart. `N` cannot be greater than `6`.

Example: `"social media"~5 OR "API"~3` | -| `#` | Standalone | Matches any Post containing a recognized hashtag, if the hashtag is a recognized entity in a Post.

This operator performs an exact match, NOT a tokenized match, meaning the rule `#thanku` will match posts with the exact hashtag #thanku, but not those with the hashtag #thankunext.

Example: `#thankunext #fanart OR @arianagrande` | -| `@` | Standalone | Matches any Post that mentions the given username, if the username is a recognized entity (including the @ character).

Example: `(@XDevelopers OR @api) -@x` | -| `$` | Standalone | Matches any Post that contains the specified 'cashtag' (where the leading character of the token is the $ character).

Note that the cashtag operator relies on X's 'symbols' entity extraction to match cashtags, rather than trying to extract the cashtag from the body itself.

Example: `$twtr OR @XDevelopers -$fb` | -| `from:` | Standalone | Matches any Post from a specific user.
The value can be either the username (excluding the @ character) or the user's numeric user ID.

You can only pass a single username/ID `from:` operator.

Example: `from:XDevelopers OR from:api -from:X` | -| `to:` | Standalone | Matches any Post that is in reply to a particular user.
The value can be either the username (excluding the @ character) or the user's numeric user ID.

You can only pass a single username/ID per `to:` operator.

Example: `to:XDevelopers OR to:api -to:x` | -| `url:` | Standalone | Performs a tokenized match on any validly-formatted URL of a Post.

This operator can match on the contents of both the `url` or `expanded_url` fields. For example, a Post containing "You should check out X Developer Labs: https://t.co/c0A36SWil4" (with the short URL redirecting to https://developer.x.com) will match both the following rules:

`from:XDevelopers url:"https://developer.x.com"`
(because it will match the contents of `entities.urls.expanded_url`)

`from:XDevelopers url:"https://t.co"`
(because it will match the contents of `entities.urls.url`)

Tokens and phrases containing punctuation or special characters should be double-quoted (for example, `url:"/developer"`). Similarly, to match on a specific protocol, enclose in double-quotes (for example, `url:"https://developer.x.com"`).

You can only pass a single URL per `url:` operator. | -| `retweets_of:` | Standalone | *Available alias:* `retweets_of_user:`
Matches Posts that are Retweets of the specified user. The value can be either the username (excluding the @ character) or the user's numeric user ID.

You can only pass a single username/ID per `retweets_of:` operator.

Example: `retweets_of:XDevelopers OR retweets_of:twitterapi`
See [HERE](/x-api/users/lookup/introduction) for methods for looking up numeric X Account IDs. | -| `context:` | Standalone | Matches Posts with a specific domain id and/or domain id, entity id pair where * represents a wildcard. To learn more about this operator, please visit our page on [Post annotations](/x-api/fundamentals/post-annotations).

You can only pass a single domain/entity per `context:` operator.

`context:domain_id.entity_id`
`context:domain_id.*`
`context:*.entity_id`

Examples:
`context:10.799022225751871488`
(`domain_id.entity_id` returns Posts matching that specific domain-entity pair)

`context:47.*`
(`domain_id.*` returns Posts matching that domain ID, with any domain-entity pair)

`context:*.799022225751871488`
(`*.entity_id` returns Posts matching that entity ID, with any domain-entity pair) | -| `entity:` | Standalone | Matches Posts with a specific entity string value. To learn more about this operator, please visit our page on [annotations](/x-api/fundamentals/post-annotations).

You can only pass a single entity per `entity:` operator.

`entity:"string declaration of entity/place"`

Examples: `entity:"Michael Jordan" OR entity:"Barcelona"` | -| `conversation_id:` | Standalone | Matches Posts that share a common conversation ID. A conversation ID is set to the Post ID of a Post that started a conversation. As Replies to a Post are posted, even Replies to Replies, the `conversation_id` is added to its JSON payload.

You can only pass a single conversation ID per `conversation_id:` operator.

Example: `conversation_id:1334987486343299072 (from:XDevelopers OR from:api)` | -| `bio:` | Standalone | *Available alias:* `user_bio:`
Matches a keyword or phrase within the Post publisher's bio. This is a tokenized match within the contents of the `description` field within the [User object](/x-api/fundamentals/data-dictionary#user).

Example: `bio:developer OR bio:"data engineer" OR bio:academic` | -| `bio_name:` | Standalone | Matches a keyword within the Post publisher's user bio name. This is a tokenized match within the contents of a user's "name" field within the [User object](/x-api/fundamentals/data-dictionary#user).

Example: `bio_name:phd OR bio_name:md` | -| `bio_location:` | Standalone | *Available alias:* `user_bio_location:`
Matches Posts that are published by users whose location contains the specified keyword or phrase. This operator performs a tokenized match, similar to the normal keyword rules on the message body.

This location is part of the [User object](/x-api/fundamentals/data-dictionary#user), matches on the 'location' field, and is a non-normalized, user-generated, free-form string. It is also different from a Post's location (see `place:`).

Example: `bio_location:"big apple" OR bio_location:nyc OR bio_location:manhattan` | -| `place:` | Standalone | Matches Posts tagged with the specified location or X place ID. Multi-word place names (“New York City”, “Palo Alto”) should be enclosed in quotes.

You can only pass a single place per `place:` operator.

Note: See the [GET geo/search](https://developer.x.com/content/developer-twitter/en/docs/geo/places-near-location/api-reference/get-geo-search) standard v1.1 endpoint for how to obtain X place IDs.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `place:"new york city" OR place:seattle OR place:fd70c22040963ac7` | -| `place_country:` | Standalone | Matches Posts where the country code associated with a tagged place/location matches the given ISO alpha-2 character code.

You can find a list of valid ISO codes on [Wikipedia](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).

You can only pass a single ISO code per `place_country:` operator.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `place_country:US OR place_country:MX OR place_country:CA` | -| `point_radius:` | Standalone | Matches against the `place.geo.coordinates` object of the Post when present, and in X, against a place geo polygon, where the Place polygon is fully contained within the defined region.

`point_radius:[longitude latitude radius]`

- Units of radius supported are miles (mi) and kilometers (km)
- Radius must be less than 25mi
- Longitude is in the range of ±180
- Latitude is in the range of ±90
- All coordinates are in decimal degrees
- Rule arguments are contained within brackets, space delimited

You can only pass a single geo polygon per `point_radius:` operator.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `point_radius:[2.355128 48.861118 16km] OR point_radius:[-41.287336 174.761070 20mi]` | -| `bounding_box:` | Standalone | *Available alias:* `geo_bounding_box:`
Matches against the place.geo.coordinates object of the Post when present, and in X, against a place geo polygon, where the place polygon is fully contained within the defined region.

`bounding_box:[west_long south_lat east_long north_lat]`

- `west_long south_lat` represent the southwest corner of the bounding box where `west_long` is the longitude of that point, and `south_lat` is the latitude.
- `east_long north_lat` represent the northeast corner of the bounding box, where `east_long` is the longitude of that point, and `north_lat` is the latitude.
- Width and height of the bounding box must be less than 25mi
- Longitude is in the range of ±180
- Latitude is in the range of ±90
- All coordinates are in decimal degrees.
- Rule arguments are contained within brackets, space delimited.

You can only pass a single geo polygon per `bounding_box:` operator.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `bounding_box:[-105.301758 39.964069 -105.178505 40.09455]` | -| `is:retweet` | Conjunction required | Matches on Retweets that match the rest of the specified rule. This operator looks only for true Retweets (for example, those generated using the Retweet button). Quote Tweets will not be matched by this operator.

Example: `data @XDevelopers -is:retweet` | -| `is:reply` | Conjunction required | Deliver only explicit replies that match a rule. Can also be negated to exclude replies that match a rule from delivery.

When used with the filtered stream, this operator matches on replies to an original Post, replies in quoted Posts, and replies in Retweets.

Example: `from:XDevelopers is:reply` | -| `is:quote` | Conjunction required | Returns all Quote Tweets, also known as Posts with comments.

Example: `"sentiment analysis" is:quote` | -| `is:verified` | Conjunction required | Deliver only Posts whose authors are verified by X.

Example: `#nowplaying is:verified` | -| `-is:nullcast` | Conjunction required | Removes Posts created for promotion only on ads.twitter.com that have a `source:"Twitter for Advertisers (legacy)"` or `source:"Twitter for Advertisers".`
This operator must be negated.

For more info on Nullcasted Posts, see our page on [Post availability](https://developer.x.com/content/developer-twitter/en/docs/twitter-api/v1/tweets/post-and-engage/guides/tweet-availability).

Example: `"mobile games" -is:nullcast` | -| `has:hashtags` | Conjunction required | Matches Posts that contain at least one hashtag.

Example: `from:XDevelopers -has:hashtags` | -| `has:cashtags` | Conjunction required | Matches Posts that contain a cashtag symbol (with a leading '$' character. For example, `$tag`).

Example: `#stonks has:cashtags` | -| `has:links` | Conjunction required | This operator matches Posts which contain links and media in the Post body.

Example: `from:XDevelopers announcement has:links` | -| `has:mentions` | Conjunction required | Matches Posts that mention another X user.

Example: `#nowplaying has:mentions` | -| `has:media` | Conjunction required | *Available alias:* `has:media_link`
Matches Posts that contain a media object, such as a photo, GIF, or video, as determined by X. This will not match on media created with Periscope, or Posts with links to other media hosting sites.

Example: `(kittens OR puppies) has:media` | -| `has:images` | Conjunction required | Matches Posts that contain a recognized URL to an image.

Example: `#meme has:images` | -| `has:video_link` | Conjunction required | *Available alias:* `has:videos`
Matches Posts that contain native X videos, uploaded directly to X. This will not match on videos created with Periscope, or Posts with links to other video hosting sites.

Example: `#icebucketchallenge has:video_link` | -| `has:geo` | Conjunction required | Matches Posts that have Post-specific geolocation data provided by the X user. This can be either a location in the form of a X place, with the corresponding display name, geo polygon, and other fields, or in rare cases, a geo lat-long coordinate.

Note: Operators matching on place (Post geo) will only include matches from original posts. Retweets do not contain any place data.

Example: `recommend #paris has:geo -bakery` | -| `sample:` | Conjunction required | Returns a random percent sample of Posts that match a rule rather than the entire set of Posts. The percent value must be represented by an integer between 1 and 100 (for example, `sample:10` will return a random 10% sample).

This operator first reduces the scope of the stream to the percentage you specified, then the rule/filter is applied to that sampled subset. In other words, if you are using, for example, `sample:10`, each Post will have a 10% chance of being in the sample.

This operator applies to the entire rule and requires all OR'd terms to be grouped.

Example: `#nowplaying @spotify sample:15` | -| `lang:` | Conjunction required | Matches Posts that have been classified by X as being of a particular language (if, and only if, the post has been classified). It is important to note that each Post is currently only classified as being of one language, so AND’ing together multiple languages will yield no results.

You can only pass a single BCP 47 language identifier per `lang:` operator.

Note: if no language classification can be made the provided result is 'und' (for undefined).

Example: `recommend #paris lang:en`

The list below represents the currently supported languages and their corresponding BCP 47 language identifier:

| Language | BCP 47 |
|-|-|
| Amharic | `am` |
| Arabic | `ar` |
| Armenian | `hy` |
| Basque | `eu` |
| Bengali | `bn` |
| Bosnian | `bs` |
| Bulgarian | `bg` |
| Burmese | `my` |
| Croatian | `hr` |
| Catalan | `ca` |
| Czech | `cs` |
| Danish | `da` |
| Dutch | `nl` |
| English | `en` |
| Estonian | `et` |
| Finnish | `fi` |
| French | `fr` |
| Georgian | `ka` |
| German | `de` |
| Greek | `el` |
| Gujarati | `gu` |
| Haitian Creole | `ht` |
| Hebrew | `iw` |
| Hindi | `hi` |
| Latinized Hindi | `hi-Latn` |
| Hungarian | `hu` |
| Icelandic | `is` |
| Indonesian | `in` |
| Italian | `it` |
| Japanese | `ja` |
| Kannada | `kn` |
| Khmer | `km` |
| Korean | `ko` |
| Lao | `lo` |
| Latvian | `lv` |
| Lithuanian | `lt` |
| Malayalam | `ml` |
| Maldivian | `dv` |
| Marathi | `mr` |
| Nepali | `ne` |
| Norwegian | `no` |
| Oriya | `or` |
| Panjabi | `pa` |
| Pashto | `ps` |
| Persian | `fa` |
| Polish | `pl` |
| Portuguese | `pt` |
| Romanian | `ro` |
| Russian | `ru` |
| Serbian | `sr` |
| Simplified Chinese | `zh-CN` |
| Sindhi | `sd` |
| Sinhala | `si` |
| Slovak | `sk` |
| Slovenian | `sl` |
| Sorani Kurdish | `ckb` |
| Spanish | `es` |
| Swedish | `sv` |
| Tagalog | `tl` |
| Tamil | `ta` |
| Telugu | `te` |
| Thai | `th` |
| Tibetan | `bo` |
| Traditional Chinese | `zh-TW` |
| Turkish | `tr` |
| Ukrainian | `uk` |
| Urdu | `ur` |
| Uyghur | `ug` |
| Vietnamese | `vi` |
| Welsh | `cy` | | -| `followers_count:` | | Matches Posts when the author has a followers count within the given range.
If a single number is specified, any number equal to or higher will match.

Example: `followers_count:500`

Additionally, a range can be specified to match any number in the given range.

Example: `followers_count:1000..10000` | -| `tweets_count:` | | *Available alias:* `statuses_count:`
Matches Posts when the author has posted a number of Posts that falls within the given range.
If a single number is specified, any number equal to or higher will match.

Example: `tweets_count:1000`

Additionally, a range can be specified to match any number in the given range.

Example: `tweets_count:1000..10000` | -| `following_count:` | | *Available alias:* `friends_count:`
Matches Posts when the author has a friends count (the number of users they follow) that falls within the given range.
If a single number is specified, any number equal to or higher will match.

Example: `following_count:500`

Additionally, a range can be specified to match any number in the given range.

Example: `following_count:1000..10000` | -| `listed_count:` | | *Available alias:* `user_in_lists_count:`
Matches Posts when the author is included in the specified number of Lists.
If a single number is specified, any number equal to or higher will match.

Example: `listed_count:10`

Additionally, a range can be specified to match any number in the given range.

Example: `listed_count:10..100` | -| `url_title:` | | *Available alias:* `within_url_title:`
Performs a keyword/phrase match on the expanded URL HTML title metadata.

Example: `url_title:snow` | -| `url_description:` | | *Available alias:* `within_url_description:`
Performs a keyword/phrase match on the expanded page description metadata.

Example: `url_description:weather` | -| `url_contains:` | | Matches Posts with URLs that literally contain the given phrase or keyword. To search for patterns with punctuation in them (i.e., google.com) enclose the search term in quotes.
NOTE: This will match against the expanded URL as well.

Example: `url_contains:photos` | -| `source:` | | Matches any Post generated by the given source application. The value must be either the name of the application or the application's URL. Cannot be used alone.

Example: `source:"X for iPhone"`

Note: As an X app developer, Posts created programmatically by your application will have the source of your application Name and Website URL set in your [app settings](/resources/fundamentals/developer-apps#app-management). | -| `in_reply_to_tweet_id:` | | *Available alias:* `in_reply_to_status_id:`
Deliver only explicit Replies to the specified Post.

Example: `in_reply_to_tweet_id:1539382664746020864` | -| `retweets_of_tweet_id:` | | *Available alias:* `retweets_of_status_id:`
Deliver only explicit (or native) Retweets of the specified Post. Note that the status ID used should be the ID of an original Post and not a Retweet.

Example: `retweets_of_tweet_id:1539382664746020864` | \ No newline at end of file +This page provides a complete list of operators available when [building rules](/x-api/posts/filtered-stream/integrate/build-a-rule) for Filtered Stream. + +## Overview + +Operators are used to match on specific Post attributes. There are two types: + +- **Standalone operators** — Can be used alone or with any other operators +- **Conjunction-required operators** — Must be used with at least one standalone operator + +--- + +## Keyword and Phrase Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `keyword` | Standalone | Matches a keyword within the Post body (tokenized match) | `pepsi OR cola OR "coca cola"` | +| `emoji` | Standalone | Matches an emoji within the Post body | `(😃 OR 😡) 😬` | +| `"exact phrase"` | Standalone | Matches the exact phrase within the Post body | `("X API" OR #v2) -"filtered stream"` | +| `"keyword1 keyword2"~N` | Standalone | Proximity match — keywords within N tokens of each other (max N=6) | `"social media"~5` | + +--- + +## Entity Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `#` | Standalone | Matches Posts containing a hashtag (exact match) | `#thankunext #fanart` | +| `@` | Standalone | Matches Posts mentioning a username | `(@XDevelopers OR @api) -@x` | +| `$` | Standalone | Matches Posts containing a cashtag | `$twtr OR @XDevelopers -$fb` | + +--- + +## User Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `from:` | Standalone | Matches Posts from a specific user | `from:XDevelopers OR from:api` | +| `to:` | Standalone | Matches Posts in reply to a specific user | `to:XDevelopers OR to:api` | +| `retweets_of:` | Standalone | Matches Retweets of a specific user | `retweets_of:XDevelopers` | + +--- + +## URL Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `url:` | Standalone | Tokenized match on URL (matches `url` or `expanded_url` fields) | `url:"https://developer.x.com"` | +| `url_title:` | — | Keyword match on expanded URL HTML title metadata | `url_title:snow` | +| `url_description:` | — | Keyword match on expanded page description metadata | `url_description:weather` | +| `url_contains:` | — | Literal match on URL content | `url_contains:photos` | + +--- + +## Context and Entity Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `context:` | Standalone | Matches Posts with a specific domain/entity pair | `context:10.799022225751871488` or `context:47.*` | +| `entity:` | Standalone | Matches Posts with a specific entity string value | `entity:"Michael Jordan"` | +| `conversation_id:` | Standalone | Matches Posts in a conversation thread | `conversation_id:1334987486343299072` | + +--- + +## User Profile Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `bio:` | Standalone | Matches keyword in Post author's bio | `bio:developer OR bio:"data engineer"` | +| `bio_name:` | Standalone | Matches keyword in Post author's name | `bio_name:phd OR bio_name:md` | +| `bio_location:` | Standalone | Matches keyword in Post author's location | `bio_location:"big apple" OR bio_location:nyc` | + +--- + +## Location Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `place:` | Standalone | Matches Posts tagged with a location | `place:"new york city" OR place:seattle` | +| `place_country:` | Standalone | Matches Posts with a country code | `place_country:US OR place_country:MX` | +| `point_radius:` | Standalone | Matches Posts within a radius of a point | `point_radius:[2.355128 48.861118 16km]` | +| `bounding_box:` | Standalone | Matches Posts within a bounding box | `bounding_box:[-105.301758 39.964069 -105.178505 40.09455]` | + +--- + +## Post Type Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `is:retweet` | Conjunction required | Matches Retweets | `data @XDevelopers -is:retweet` | +| `is:reply` | Conjunction required | Matches replies | `from:XDevelopers is:reply` | +| `is:quote` | Conjunction required | Matches Quote Tweets | `"sentiment analysis" is:quote` | +| `is:verified` | Conjunction required | Matches Posts from verified authors | `#nowplaying is:verified` | +| `-is:nullcast` | Conjunction required | Excludes promotional Posts (must be negated) | `"mobile games" -is:nullcast` | + +--- + +## Content Type Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `has:hashtags` | Conjunction required | Matches Posts with hashtags | `from:XDevelopers -has:hashtags` | +| `has:cashtags` | Conjunction required | Matches Posts with cashtags | `#stonks has:cashtags` | +| `has:links` | Conjunction required | Matches Posts with links | `from:XDevelopers has:links` | +| `has:mentions` | Conjunction required | Matches Posts with mentions | `#nowplaying has:mentions` | +| `has:media` | Conjunction required | Matches Posts with media (photo, GIF, video) | `(kittens OR puppies) has:media` | +| `has:images` | Conjunction required | Matches Posts with images | `#meme has:images` | +| `has:video_link` | Conjunction required | Matches Posts with native X videos | `#icebucketchallenge has:video_link` | +| `has:geo` | Conjunction required | Matches Posts with geolocation data | `recommend #paris has:geo` | + +--- + +## Sampling and Language Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `sample:` | Conjunction required | Returns a random percent sample (1-100) | `#nowplaying @spotify sample:15` | +| `lang:` | Conjunction required | Matches Posts classified as a specific language | `recommend #paris lang:en` | + +--- + +## User Metrics Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `followers_count:` | — | Matches Posts from users with follower count in range | `followers_count:1000..10000` | +| `tweets_count:` | — | Matches Posts from users with Post count in range | `tweets_count:1000..10000` | +| `following_count:` | — | Matches Posts from users with following count in range | `following_count:1000..10000` | +| `listed_count:` | — | Matches Posts from users in specified number of Lists | `listed_count:10..100` | + +--- + +## Post Reference Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `in_reply_to_tweet_id:` | — | Matches replies to a specific Post | `in_reply_to_tweet_id:1539382664746020864` | +| `retweets_of_tweet_id:` | — | Matches Retweets of a specific Post | `retweets_of_tweet_id:1539382664746020864` | +| `source:` | — | Matches Posts from a specific source application | `source:"X for iPhone"` | + +--- + +## Logical Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `OR` | Logical OR between expressions | `cat OR dog` | +| Space (AND) | Logical AND between expressions | `cat dog` (both required) | +| `()` | Grouping for complex expressions | `(cat OR dog) -is:retweet` | +| `-` | Negation/exclusion | `cat -grumpy` | + +--- + +## Supported Languages + +The `lang:` operator supports these BCP 47 language codes: + +| Language | Code | Language | Code | Language | Code | +|:---------|:-----|:---------|:-----|:---------|:-----| +| Amharic | `am` | Greek | `el` | Portuguese | `pt` | +| Arabic | `ar` | Gujarati | `gu` | Romanian | `ro` | +| Armenian | `hy` | Hebrew | `iw` | Russian | `ru` | +| Basque | `eu` | Hindi | `hi` | Serbian | `sr` | +| Bengali | `bn` | Hungarian | `hu` | Simplified Chinese | `zh-CN` | +| Bulgarian | `bg` | Indonesian | `in` | Slovak | `sk` | +| Catalan | `ca` | Italian | `it` | Slovenian | `sl` | +| Croatian | `hr` | Japanese | `ja` | Spanish | `es` | +| Czech | `cs` | Kannada | `kn` | Swedish | `sv` | +| Danish | `da` | Korean | `ko` | Tamil | `ta` | +| Dutch | `nl` | Latvian | `lv` | Telugu | `te` | +| English | `en` | Lithuanian | `lt` | Thai | `th` | +| Estonian | `et` | Malayalam | `ml` | Traditional Chinese | `zh-TW` | +| Finnish | `fi` | Marathi | `mr` | Turkish | `tr` | +| French | `fr` | Norwegian | `no` | Ukrainian | `uk` | +| German | `de` | Persian | `fa` | Urdu | `ur` | +| Georgian | `ka` | Polish | `pl` | Vietnamese | `vi` | + +--- + +## Next steps + + + + Learn rule syntax and best practices + + + Get started with Filtered Stream + + + Handle streaming disconnections + + diff --git a/x-api/posts/filtered-stream/integrate/recovery-and-redundancy-features.mdx b/x-api/posts/filtered-stream/integrate/recovery-and-redundancy-features.mdx deleted file mode 100644 index 692a561b1..000000000 --- a/x-api/posts/filtered-stream/integrate/recovery-and-redundancy-features.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Recovery and redundancy -sidebarTitle: Recovery and redundancy -keywords: ["recovery", "redundancy", "stream recovery", "reconnect", "fault tolerance", "stream redundancy", "error recovery"] ---- - -#### Introduction - -When consuming streaming data, maximizing your connection time and receiving all matched data is a fundamental goal. This means that it is important to take advantage of redundant connections, automatically detect disconnections, to reconnect quickly, and to have a plan for recovering lost data. - -In this integration guide, we will discuss different recovery and redundancy features: redundant connections, backfill, and recovery. -  - -**Redundant connections** - -A redundant connection simply allows you to establish more than one simultaneous connections to the filtered stream. This provides redundancy by allowing you to connect to the same stream with two separate consumers, receiving the same data through both connections. Thus, your app has a hot failover for various situations such as if one stream is disconnected or if your application's primary server fails. - -Filtered stream currently only allows Projects with Enterprise access to connect to up to two redundant connections. To use a redundant stream, simply connect to the same URL used for your primary connection. The data for your stream will be sent through both connections. - -#### Recovering missed data after a disconnection: Backfill - -After you've detected a disconnection, your system should be smart enough to reconnect to the stream. If possible, your system should take note of how long the disconnection lasted so that you can use the proper recovery feature to backfill the data.  - -If you are using a Project with Enterprise access and identified that the disconnection lasted five minutes or less, you can use the backfill parameter, backfill_minutes. If you pass this parameter with your [GET /tweets/search/stream](/x-api/posts/filtered-stream#get-2-tweets-search-stream) request, you will receive the Posts that match your rules within the past one to five minutes. We generally deliver these older Posts first before any newly matched Posts, and also do not deduplicate Posts. This means that if you were disconnected for 90 seconds, but request two minutes worth of backfill data, you will receive 30 seconds worth of duplicate Posts, which your system should be tolerant of. Here is an example of what a request might look like with the backfill parameter: - - `curl 'https://api.x.com/2/tweets/search/stream?backfill_minutes=5' -H "Authorization: Bearer $ACCESS_TOKEN"` - - -If you don't have Enterprise access, or identified that the disconnection time lasted for longer than five minutes, you can utilize the [recent search endpoint](/x-api/posts/search/introduction) or the recovery feature to request missed data. However, note that the search Posts endpoints do not include the sample:, bio:, bio_name:, or bio_location: operators, and has certain differences in matching behavior when using accents and diacritics with the keyword and #hashtag operators. These differences could mean that you don't fully recover all Posts that might have been received via the filtered stream endpoints.  - -**Recovering missed data after a disconnection: Recovery** - -If you are using a Project with Enterprise access, you can use the Recovery feature to recover missed data within the last 24 hours if you are unable to reconnect with the 5 minute backfill window. - -The streaming recovery feature allows you to have an extended backfill window of 24 hours. Recovery enables you to 'replay' the time period of missed data. A recovery stream is started when you make a connection request using 'start\_time' and 'end\_time' request parameters. Once connected, Recovery will re-stream the time period indicated, then disconnect.   - -You will be able to make 2 concurrent requests to recovery at the same time, i.e. “two recovery jobs”. Recovery works technically in the same way as backfill, except a start and end time is defined. A recovery period is for a single time range. - -| | | | -| :--- | :--- | :--- | -| **Name** | **Type** | **Description** | -| start_time | date (ISO 8601) | YYYY-MM-DDTHH:mm:ssZ (ISO 8601/RFC 3339).

Date in UTC signifying the start time to recover from. | -| end_time | date (ISO 8601) | YYYY-MM-DDTHH:mm:ssZ (ISO 8601/RFC 3339).

Date in UTC signifying the end time to recover to. | - - -Example request URL: [https://api.x.com/2/tweets/search/stream?start\_time=2022-07-12T15:10:00Z&end\_time=2022-07-12T15:20:00Z](https://api.x.com/2/tweets/search/stream?start_time=1985-04-12T23:20:50.52Z&end_time=1985-04-13T00:20:50.52Z) \ No newline at end of file diff --git a/x-api/posts/filtered-stream/introduction.mdx b/x-api/posts/filtered-stream/introduction.mdx index ee8fac1d4..fdfdc9c37 100644 --- a/x-api/posts/filtered-stream/introduction.mdx +++ b/x-api/posts/filtered-stream/introduction.mdx @@ -1,18 +1,22 @@ --- 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"] +description: Stream near real-time Posts matching your filter rules +keywords: ["filtered stream", "streaming API", "near real-time stream", "stream tweets", "filter rules", "streaming endpoint", "live tweets", "streaming data", "webhook stream"] --- import { Button } from '/snippets/button.mdx'; -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. +The Filtered Stream endpoints let you receive near 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. + + +Filtered Stream prioritizes data hydration and delivery, with approximately 6-7 seconds of P99 latency. For lower latency requirements, see [Powerstream](/x-api/powerstream/introduction). + ## Overview - + Receive Posts within seconds of publication @@ -32,14 +36,11 @@ The Filtered Stream endpoints let you receive real-time Posts that match your fi 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 +3. **Receive Posts** — Get matching Posts in near real-time -``` -┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ Create/ │ │ Connect to │ │ Receive │ -│ manage │ → │ streaming │ → │ matching │ -│ rules │ │ endpoint │ │ Posts │ -└─────────────┘ └─────────────┘ └─────────────┘ +```mermaid actions={false} +flowchart LR + A["Create/manage
rules"] --> B["Connect to
streaming endpoint"] --> C["Receive
matching Posts"] ``` --- @@ -115,10 +116,10 @@ def stream_posts(bearer_token): 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 @@ -185,13 +186,13 @@ The stream delivers edited Posts with their edit history. Each edit creates a ne ## Advanced topics - + Reconnect gracefully - + Handle high throughput - + Build resilient applications diff --git a/x-api/posts/filtered-stream/quickstart.mdx b/x-api/posts/filtered-stream/quickstart.mdx index 5f1c4ae5d..76efa80a0 100644 --- a/x-api/posts/filtered-stream/quickstart.mdx +++ b/x-api/posts/filtered-stream/quickstart.mdx @@ -1,13 +1,13 @@ --- title: Quickstart sidebarTitle: Quickstart -description: Connect to the filtered stream and receive real-time Posts +description: Connect to the filtered stream and receive near real-time Posts keywords: ["filtered stream quickstart", "streaming quickstart", "filtered stream tutorial", "stream guide", "streaming example"] --- import { Button } from '/snippets/button.mdx'; -This guide walks you through connecting to the filtered stream to receive real-time Posts matching your filter rules. +This guide walks you through connecting to the filtered stream to receive near real-time Posts matching your filter rules. **Prerequisites** diff --git a/x-api/posts/manage-tweets/integrate.mdx b/x-api/posts/manage-tweets/integrate.mdx index 2453f120b..aebe48841 100644 --- a/x-api/posts/manage-tweets/integrate.mdx +++ b/x-api/posts/manage-tweets/integrate.mdx @@ -67,7 +67,7 @@ You can only add a location to Posts if you have geo enabled in your profile set ### 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 media upload endpoint](/x-api/media/upload-media) 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. --- diff --git a/x-api/posts/search/integrate/operators.mdx b/x-api/posts/search/integrate/operators.mdx index 3387a799a..81a52249b 100644 --- a/x-api/posts/search/integrate/operators.mdx +++ b/x-api/posts/search/integrate/operators.mdx @@ -1,52 +1,189 @@ --- title: Search Operators sidebarTitle: Operators +description: Complete list of operators for Search queries keywords: ["search operators", "query operators", "search syntax", "search filters", "operators guide", "query syntax"] --- -This page provides a list of operators availble when [building a query](/x-api/posts/search/integrate/build-a-query) for the Search v2 API endpoints. - -## List of operators - -**Note:** For some operators, an alternate name, or alias, is available. - -| **Operator** | **Type** | **Description** | -|:-------------|:---------|:-----------------| -| `keyword` | Standalone | Matches a keyword within the body of a Post. This is a tokenized match, meaning that your keyword string will be matched against the tokenized text of the Post body. Tokenization splits words based on punctuation, symbols, and Unicode basic plane separator characters.

For example, a Post with the text “I like coca-cola” would be split into the following tokens: I, like, coca, cola. These tokens would then be compared to the keyword string used in your query. To match strings containing punctuation (for example coca-cola), symbol, or separator characters, you must wrap your keyword in double-quotes.

Example: `pepsi OR cola OR "coca cola"` | -| `emoji` | Standalone | Matches an emoji within the body of a Post. Similar to a keyword, emojis are a tokenized match, meaning that your emoji will be matched against the tokenized text of the Post body.

Note that if an emoji has a variant, you must wrap it in double quotes to add to a query.

Example: `(😃 OR 😡) 😬` | -| `"exact phrase match"` | Standalone | Matches the exact phrase within the body of a Post.

Example: `("X API" OR #v2) -"recent search"` | -| `#` | Standalone | Matches any Post containing a recognized hashtag, if the hashtag is a recognized entity in a Post.

This operator performs an exact match, NOT a tokenized match, meaning the rule `#thanku` will match posts with the exact hashtag #thanku, but not those with the hashtag #thankunext.

Example: `#thankunext #fanart OR @arianagrande` | -| `@` | Standalone | Matches any Post that mentions the given username, if the username is a recognized entity (including the @ character).

Example: `(@XDevelopers OR @API) -@X` | -| `$` | Standalone | Matches any Post that contains the specified 'cashtag' (where the leading character of the token is the $ character).

Note that the cashtag operator relies on X's 'symbols' entity extraction to match cashtags, rather than trying to extract the cashtag from the body itself.

Example: `$twtr OR @XDevelopers -$fb` | -| `from:` | Standalone | Matches any Post from a specific user.
The value can be either the username (excluding the @ character) or the user’s numeric user ID.

You can only pass a single username/ID per `from:` operator.

Example: `from:XDevelopers OR from:API -from:X` | -| `to:` | Standalone | Matches any Post that is in reply to a particular user.
The value can be either the username (excluding the @ character) or the user’s numeric user ID.

You can only pass a single username/ID per `to:` operator.

Example: `to:XDevelopers OR to:API -to:X` | -| `url:` | Standalone | Performs a tokenized match on any validly-formatted URL of a Post.

This operator matches on the contents of both the `url` or `expanded_url` fields. For example, a Post containing "You should check out X Developer Labs: https://t.co/c0A36SWil4" (with the short URL redirecting to https://developer.twitter.com) will match both the following rules:

`from:XDevelopers url:"https://developer.twitter.com"` (because it will match the contents of `entities.urls.expanded_url`)

`from:XDevelopers url:"https://t.co"` (because it will match the contents of `entities.urls.url`)

Tokens and phrases containing punctuation or special characters should be double-quoted (for example, `url:"/developer"`). Similarly, to match on a specific protocol, enclose in double-quotes (for example, `url:"https://developer.twitter.com"`). | -| `retweets_of:` | Standalone | Matches Posts that are Retweets of the specified user. The value can be either the username (excluding the @ character) or the user’s numeric user ID.

You can only pass a single username/ID per `retweets_of:` operator.

Example: `retweets_of:twitterdev OR retweets_of:twitterapi` | -| `in_reply_to_tweet_id:` | Standalone | *Available alias:* `in_reply_to_status_id:`
Matches on replies to the specified Post.

Example: `in_reply_to_tweet_id:1539382664746020864` | -| `retweets_of_tweet_id:` | Standalone | *Available alias:* `retweets_of_status_id:`
Matches on explicit (or native) Retweets of the specified Post. Note that the Post ID used should be the ID of an original Post and not a Retweet.

Example: `retweets_of_tweet_id:1539382664746020864` | -| `quotes_of_tweet_id:` | Standalone | *Available alias:* `quotes_of_status_id:`
Matches on Quote Tweets of the specified Post. Note that the Post ID used should be the ID of an original Post and not a Quote Tweet.

Example: `quotes_of_tweet_id:1539382664746020864` | -| `context:` | Standalone | Matches Posts with a specific domain id/entity id pair. To learn more about this operator, please visit our page on [annotations](/x-api/fundamentals/post-annotations).

You can only pass a single domain/entity per `context:` operator.

`context:domain_id.entity_id`

However, you can combine multiple domain/entities using the OR operator:

`(context:47.1139229372198469633 OR context:11.1088514520308342784)`

Examples:
`context:10.799022225751871488` (`domain_id.entity_id` returns Posts matching that specific domain-entity pair) | -| `entity:` | Standalone | Matches Posts with a specific entity string value. To learn more about this operator, please visit our page on [annotations](/x-api/fundamentals/post-annotations).
**Please note** that this is only available with recent search.

You can only pass a single `entity:` operator.

`entity:"string declaration of entity/place"`

Examples: `entity:"Michael Jordan" OR entity:"Barcelona"` | -| `conversation_id:` | Standalone | Matches Posts that share a common conversation ID. A conversation ID is set to the Post ID of a Post that started a conversation. As Replies to a Post are posted, even Replies to Replies, the `conversation_id` is added to its JSON payload.

You can only pass a single conversation ID per `conversation_id:` operator.

Example: `conversation_id:1334987486343299072 (from:XDevelopers OR from:api)` | -| `list:` | Standalone | Matches Posts posted by users who are members of a specified list.

For example, if @XDevelopers and @api were members of List 123, and you included `list:123` in your query, your response will only contain Posts that have been published by those accounts. You can find List IDs by using the [List lookup](/x-api/lists/list-lookup/introduction) endpoint.

**Please note** that you can only use a single `list:` operator per query, and you can only specify a single List per `list:` operator.

Example: `list:123` | -| `place:` | Standalone | Matches Posts tagged with the specified location or X place ID. Multi-word place names (“New York City”, “Palo Alto”) should be enclosed in quotes.

You can only pass a single place per `place:` operator.

Note: See the [GET geo/search](https://developer.x.com/content/developer-twitter/en/docs/geo/places-near-location/api-reference/get-geo-search) standard v1.1 endpoint for how to obtain X place IDs.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `place:"new york city" OR place:seattle OR place:fd70c22040963ac7` | -| `place_country:` | Standalone | Matches Posts where the country code associated with a tagged place/location matches the given ISO alpha-2 character code.

You can find a list of valid ISO codes on [Wikipedia](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).

You can only pass a single ISO code per `place_country:` operator.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `place_country:US OR place_country:MX OR place_country:CA` | -| `point_radius:` | Standalone | Matches against the `place.geo.coordinates` object of the Post when present, and in X, against a place geo polygon, where the Place polygon is fully contained within the defined region.

`point_radius:[longitude latitude radius]`

- Units of radius supported are miles (mi) and kilometers (km)
- Radius must be less than 25mi
- Longitude is in the range of ±180
- Latitude is in the range of ±90
- All coordinates are in decimal degrees
- Rule arguments are contained within brackets, space delimited

You can only pass a single geo polygon per `point_radius:` operator.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `point_radius:[2.355128 48.861118 16km] OR point_radius:[-41.287336 174.761070 20mi]` | -| `bounding_box:` | Standalone | *Available alias:* `geo_bounding_box:`
Matches against the place.geo.coordinates object of the Post when present, and in X, against a place geo polygon, where the place polygon is fully contained within the defined region.

`bounding_box:[west_long south_lat east_long north_lat]`

- `west_long south_lat` represent the southwest corner of the bounding box where `west_long` is the longitude of that point, and `south_lat` is the latitude.
- `east_long north_lat` represent the northeast corner of the bounding box, where `east_long` is the longitude of that point, and `north_lat` is the latitude.
- Width and height of the bounding box must be less than 25mi
- Longitude is in the range of ±180
- Latitude is in the range of ±90
- All coordinates are in decimal degrees.
- Rule arguments are contained within brackets, space delimited.

You can only pass a single geo polygons per `bounding_box:` operator.

Note: This operator will not match on Retweets, since Retweet's places are attached to the original Post. It will also not match on places attached to the original Post of a Quote Tweet.

Example: `bounding_box:[-105.301758 39.964069 -105.178505 40.09455]` | -| `min_likes` | Standalone | Matches Posts that have at minimum the specified number of likes. | -| `min_replies` | Standalone | Matches Posts that have at minimum the specified number of replies. | -| `min_reposts` | Standalone | Matches Posts that have at minimum the specified number of reposts. | -| `is:retweet` | Conjunction required | Matches on Retweets that match the rest of the specified rule. This operator looks only for true Retweets (for example, those generated using the Retweet button). Quote Tweets will not be matched by this operator.

Example: `data @XDevelopers -is:retweet` | -| `is:reply` | Conjunction required | Deliver only explicit replies that match a rule. Can also be negated to exclude replies that match a query from delivery.

Note: This operator is also available with the filtered stream endpoint. When used with filtered stream, this operator matches on replies to an original Post, replies in quoted Tweets, and replies in Retweets.

Example: `from:XDevelopers is:reply` | -| `is:quote` | Conjunction required | Returns all Quote Tweets, also known as Posts with comments.

Example: `"sentiment analysis" is:quote` | -| `is:verified` | Conjunction required | Deliver only Posts whose authors are verified by X.

Example: `#nowplaying is:verified` | -| `-is:nullcast` | Conjunction required | Removes Posts created for promotion only on ads.twitter.com that have a `"source":"Twitter for Advertisers (legacy)"` or `"source":"Twitter for Advertisers"`.
This operator must be negated.

For more info on Nullcasted Posts, see our page on [Post availability](https://developer.x.com/content/developer-twitter/en/docs/twitter-api/v1/tweets/post-and-engage/guides/tweet-availability).

Example: `"mobile games" -is:nullcast` | -| `has:hashtags` | Conjunction required | Matches Posts that contain at least one hashtag.

Example: `from:XDevelopers -has:hashtags` | -| `has:cashtags` | Conjunction required | Matches Posts that contain a cashtag symbol (with a leading ‘$’ character. For example, `$tag`).

Example: `#stonks has:cashtags` | -| `has:links` | Conjunction required | This operator matches Posts which contain links and media in the Post body.

Example: `from:XDevelopers announcement has:links` | -| `has:mentions` | Conjunction required | Matches Posts that mention another X user.

Example: `#nowplaying has:mentions` | -| `has:media` | Conjunction required | *Available alias:* `has:media_link`
Matches Posts that contain a media object, such as a photo, GIF, or video, as determined by X. This will not match on media created with Periscope, or Posts with links to other media hosting sites.

Example: `(kittens OR puppies) has:media` | -| `has:images` | Conjunction required | Matches Posts that contain a recognized URL to an image.

Example: `#meme has:images` | -| `has:video_link` | Conjunction required | *Available alias:* `has:videos`
Matches Posts that contain native X videos, uploaded directly to X. This will not match on videos created with Periscope, or Posts with links to other video hosting sites.

Example: `#icebucketchallenge has:video_link` | -| `has:geo` | Conjunction required | Matches Posts that have Post-specific geolocation data provided by the X user. This can be either a location in the form of a X place, with the corresponding display name, geo polygon, and other fields, or in rare cases, a geo lat-long coordinate.

Note: Operators matching on place (Post geo) will only include matches from original Posts. Retweets do not contain any place data.

Example: `recommend #paris has:geo -bakery` | -| `lang:` | Conjunction required | Matches Posts that have been classified by X as being of a particular language (if, and only if, the Post has been classified). It is important to note that each Post is currently only classified as being of one language, so AND'ing together multiple languages will yield no results.

You can only pass a single BCP 47 language identifier per `lang:` operator.

Note: if no language classification can be made the provided result is ‘und’ (for undefined).

Example: `recommend #paris lang:en`

The list below represents the currently supported languages and their corresponding BCP 47 language identifier:

Amharic: `am` \| German: `de` \| Malayalam: `ml` \| Slovak: `sk`
Arabic: `ar` \| Greek: `el` \| Maldivian: `dv` \| Slovenian: `sl`
Armenian: `hy` \| Gujarati: `gu` \| Marathi: `mr` \| Sorani Kurdish: `ckb`
Basque: `eu` \| Haitian Creole: `ht` \| Nepali: `ne` \| Spanish: `es`
Bengali: `bn` \| Hebrew: `iw` \| Norwegian: `no` \| Swedish: `sv`
Bosnian: `bs` \| Hindi: `hi` \| Oriya: `or` \| Tagalog: `tl`
Bulgarian: `bg` \| Latinized Hindi: `hi-Latn` \| Panjabi: `pa` \| Tamil: `ta`
Burmese: `my` \| Hungarian: `hu` \| Pashto: `ps` \| Telugu: `te`
Croatian: `hr` \| Icelandic: `is` \| Persian: `fa` \| Thai: `th`
Catalan: `ca` \| Indonesian: `in` \| Polish: `pl` \| Tibetan: `bo`
Czech: `cs` \| Italian: `it` \| Portuguese: `pt` \| Traditional Chinese: `zh-TW`
Danish: `da` \| Japanese: `ja` \| Romanian: `ro` \| Turkish: `tr`
Dutch: `nl` \| Kannada: `kn` \| Russian: `ru` \| Ukrainian: `uk`
English: `en` \| Khmer: `km` \| Serbian: `sr` \| Urdu: `ur`
Estonian: `et` \| Korean: `ko` \| Simplified Chinese: `zh-CN` \| Uyghur: `ug`
Finnish: `fi` \| Lao: `lo` \| Sindhi: `sd` \| Vietnamese: `vi`
French: `fr` \| Latvian: `lv` \| Sinhala: `si` \| Welsh: `cy`
Georgian: `ka` \| Lithuanian: `lt` | \ No newline at end of file +This page provides a complete list of operators available when [building a query](/x-api/posts/search/integrate/build-a-query) for the Search API endpoints. + +## Overview + +Operators are used to match on specific Post attributes. There are two types: + +- **Standalone operators** — Can be used alone or with any other operators +- **Conjunction-required operators** — Must be used with at least one standalone operator + +--- + +## Keyword and Phrase Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `keyword` | Standalone | Matches a keyword within the Post body (tokenized match) | `pepsi OR cola OR "coca cola"` | +| `emoji` | Standalone | Matches an emoji within the Post body | `(😃 OR 😡) 😬` | +| `"exact phrase"` | Standalone | Matches the exact phrase within the Post body | `("X API" OR #v2) -"recent search"` | + +--- + +## Entity Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `#` | Standalone | Matches Posts containing a hashtag (exact match) | `#thankunext #fanart` | +| `@` | Standalone | Matches Posts mentioning a username | `(@XDevelopers OR @API) -@X` | +| `$` | Standalone | Matches Posts containing a cashtag | `$twtr OR @XDevelopers -$fb` | + +--- + +## User Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `from:` | Standalone | Matches Posts from a specific user | `from:XDevelopers OR from:API` | +| `to:` | Standalone | Matches Posts in reply to a specific user | `to:XDevelopers OR to:API` | +| `retweets_of:` | Standalone | Matches Retweets of a specific user | `retweets_of:twitterdev` | + +--- + +## URL Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `url:` | Standalone | Tokenized match on URL (matches `url` or `expanded_url` fields) | `url:"https://developer.twitter.com"` | + +--- + +## Context and Entity Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `context:` | Standalone | Matches Posts with a specific domain/entity pair | `context:10.799022225751871488` | +| `entity:` | Standalone | Matches Posts with a specific entity string value (recent search only) | `entity:"Michael Jordan"` | +| `conversation_id:` | Standalone | Matches Posts in a conversation thread | `conversation_id:1334987486343299072` | + +--- + +## List Operator + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `list:` | Standalone | Matches Posts from members of a specific List | `list:123` | + +--- + +## Post Reference Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `in_reply_to_tweet_id:` | Standalone | Matches replies to a specific Post | `in_reply_to_tweet_id:1539382664746020864` | +| `retweets_of_tweet_id:` | Standalone | Matches Retweets of a specific Post | `retweets_of_tweet_id:1539382664746020864` | +| `quotes_of_tweet_id:` | Standalone | Matches Quote Tweets of a specific Post | `quotes_of_tweet_id:1539382664746020864` | + +--- + +## Location Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `place:` | Standalone | Matches Posts tagged with a location | `place:"new york city" OR place:seattle` | +| `place_country:` | Standalone | Matches Posts with a country code | `place_country:US OR place_country:MX` | +| `point_radius:` | Standalone | Matches Posts within a radius of a point | `point_radius:[2.355128 48.861118 16km]` | +| `bounding_box:` | Standalone | Matches Posts within a bounding box | `bounding_box:[-105.301758 39.964069 -105.178505 40.09455]` | + +--- + +## Post Type Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `is:retweet` | Conjunction required | Matches Retweets | `data @XDevelopers -is:retweet` | +| `is:reply` | Conjunction required | Matches replies | `from:XDevelopers is:reply` | +| `is:quote` | Conjunction required | Matches Quote Tweets | `"sentiment analysis" is:quote` | +| `is:verified` | Conjunction required | Matches Posts from verified authors | `#nowplaying is:verified` | +| `-is:nullcast` | Conjunction required | Excludes promotional Posts (must be negated) | `"mobile games" -is:nullcast` | + +--- + +## Content Type Operators + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `has:hashtags` | Conjunction required | Matches Posts with hashtags | `from:XDevelopers -has:hashtags` | +| `has:cashtags` | Conjunction required | Matches Posts with cashtags | `#stonks has:cashtags` | +| `has:links` | Conjunction required | Matches Posts with links | `from:XDevelopers has:links` | +| `has:mentions` | Conjunction required | Matches Posts with mentions | `#nowplaying has:mentions` | +| `has:media` | Conjunction required | Matches Posts with media (photo, GIF, video) | `(kittens OR puppies) has:media` | +| `has:images` | Conjunction required | Matches Posts with images | `#meme has:images` | +| `has:video_link` | Conjunction required | Matches Posts with native X videos | `#icebucketchallenge has:video_link` | +| `has:geo` | Conjunction required | Matches Posts with geolocation data | `recommend #paris has:geo` | + +--- + +## Language Operator + +| Operator | Type | Summary | Example | +|:---------|:-----|:--------|:--------| +| `lang:` | Conjunction required | Matches Posts classified as a specific language | `recommend #paris lang:en` | + +--- + +## Logical Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `OR` | Logical OR between expressions | `cat OR dog` | +| Space (AND) | Logical AND between expressions | `cat dog` (both required) | +| `()` | Grouping for complex expressions | `(cat OR dog) -is:retweet` | +| `-` | Negation/exclusion | `cat -grumpy` | + +--- + +## Supported Languages + +The `lang:` operator supports these BCP 47 language codes: + +| Language | Code | Language | Code | Language | Code | +|:---------|:-----|:---------|:-----|:---------|:-----| +| Amharic | `am` | Greek | `el` | Portuguese | `pt` | +| Arabic | `ar` | Gujarati | `gu` | Romanian | `ro` | +| Armenian | `hy` | Hebrew | `iw` | Russian | `ru` | +| Basque | `eu` | Hindi | `hi` | Serbian | `sr` | +| Bengali | `bn` | Hungarian | `hu` | Simplified Chinese | `zh-CN` | +| Bulgarian | `bg` | Indonesian | `in` | Slovak | `sk` | +| Catalan | `ca` | Italian | `it` | Slovenian | `sl` | +| Croatian | `hr` | Japanese | `ja` | Spanish | `es` | +| Czech | `cs` | Kannada | `kn` | Swedish | `sv` | +| Danish | `da` | Korean | `ko` | Tamil | `ta` | +| Dutch | `nl` | Latvian | `lv` | Telugu | `te` | +| English | `en` | Lithuanian | `lt` | Thai | `th` | +| Estonian | `et` | Malayalam | `ml` | Traditional Chinese | `zh-TW` | +| Finnish | `fi` | Marathi | `mr` | Turkish | `tr` | +| French | `fr` | Norwegian | `no` | Ukrainian | `uk` | +| German | `de` | Persian | `fa` | Urdu | `ur` | +| Georgian | `ka` | Polish | `pl` | Vietnamese | `vi` | + +--- + +## Query limitations + +| Access level | Recent search | Full-archive search | +|:-------------|:--------------|:--------------------| +| Self-serve | 512 characters | 1,024 characters | +| Enterprise | 4,096 characters | 4,096 characters | + +--- + +## Next steps + + + + Learn query syntax and best practices + + + Get started with Search + + + Build and test queries interactively + + diff --git a/x-api/posts/volume-streams/introduction.mdx b/x-api/posts/volume-streams/introduction.mdx index ae6018f52..f87bf1b70 100644 --- a/x-api/posts/volume-streams/introduction.mdx +++ b/x-api/posts/volume-streams/introduction.mdx @@ -41,3 +41,22 @@ Learn more about getting access to the X API v2 endpoints in our [getting start Try with API Explorer
+ +--- + +## Streaming fundamentals + + + + Best practices for streaming clients + + + Reconnect gracefully + + + Handle high throughput + + + Build resilient applications + + diff --git a/x-api/powerstream/handling-disconnections.mdx b/x-api/powerstream/handling-disconnections.mdx index 1784394a1..9eb6499e5 100644 --- a/x-api/powerstream/handling-disconnections.mdx +++ b/x-api/powerstream/handling-disconnections.mdx @@ -4,72 +4,51 @@ sidebarTitle: Handling disconnections keywords: ["powerstream disconnections", "handle disconnections", "reconnect powerstream", "stream disconnections", "powerstream errors"] --- -## What is a disconnection? +This page covers Powerstream-specific disconnection handling. For comprehensive guidance on handling disconnections across all streaming endpoints, see the [Handling disconnections fundamentals guide](/x-api/fundamentals/handling-disconnections). -Establishing a connection to the streaming APIs means making a very long lived HTTPS request, and parsing the response incrementally. When connecting to the powerstream endpoint, you should form a HTTPS request and consume the resulting stream for as long as is practical. Our servers will hold the connection open indefinitely, barring server-side error, excessive client-side lag, network issues, routine server maintenance, or duplicate logins. With connections to streaming endpoints, it is likely, and should be expected, that disconnections will take place and reconnection logic built. +## Powerstream disconnection handling -## Why a streaming connection might be disconnected +The core concepts for handling disconnections apply to all streaming endpoints. See the fundamentals guide for: -Your stream can disconnect for a number of reasons. Inspect the error message returned by the stream to understand the reason for the failure. Possible reasons for disconnections are as follows: - -* An authentication error (such as a wrong token or a wrong authentication method being used). -* A streaming server is restarted on the X side. This is usually related to a code deploy and should be generally expected and designed around. -* Your client is not keeping up with the volume of Posts the stream is delivering or is reading data too slowly. Every streaming connection is backed by a queue of messages to be sent to the client. If this queue grows too large over time, the connection will be closed. -* Your account exceeded your daily/monthly quota of Posts. -* You have too many active redundant connections. -* A client stops reading data suddenly. If the rate of Posts being read off of the stream drops suddenly, the connection will be closed. -* Possible networking issues between server and client -* A temporary server side issue, scheduled maintenance and updates. (Check the [status page](/status)) - -## Anticipating disconnects and reconnecting - -When streaming Posts, the goal is to stay connected for as long as possible, recognizing that disconnects may occur. The endpoint provides a 20-second keep alive heartbeat (it will look like a new line character). Use this signal to detect if you’re being disconnected. - -1. Your code should detect when fresh content and the heartbeat stop arriving. -2. If that happens, your code should trigger a reconnection logic. Some clients and languages allow you to specify a read timeout, which you can set to 20 seconds. -3. Your service should detect these disconnections and reconnect as soon as possible. - - -Once an established connection drops, attempt to reconnect immediately. If the reconnect fails, slow down your reconnect attempts according to the type of error experienced: - -* Back off linearly for TCP/IP level network errors. These problems are generally temporary and tend to clear quickly. Increase the delay in reconnects by 250ms each attempt, up to 16 seconds. -* Back off exponentially for HTTP errors for which reconnecting would be appropriate. Start with a 5 second wait, doubling each attempt, up to 320 seconds. -* Back off exponentially for HTTP 429 errors Rate limit exceeded. Start with a 1 minute wait and double each attempt. Note that every HTTP 429 received increases the time you must wait until rate limiting will no longer be in effect for your account. -   +- Why connections disconnect +- Common disconnection errors +- Detecting disconnections +- Reconnection strategies and backoff +- Best practices ## Recovering lost data -If you do experience a disconnect, there are some different strategies that you can use to ensure that you receive all of the data that you might have missed. We've documented some key steps that you can take to recover missed data on our integration guide on [recovering data](/x-api/powerstream/recovery-and-redundancy).  -  -## Rate limits and usage - -To check connection limits response will return three headers. This is useful to understand how many times you can use the rule endpoint, and how many reconnections attempts are allowed for the streaming endpoint. - -* x-rate-limit-limit indicates the number of allotted requests your client is allowed to make during the 15-minute window. +If you experience a disconnect, see [Recovery and redundancy](/x-api/fundamentals/recovery-and-redundancy) for strategies to recover missed data, including: -* x-rate-limit-remaining indicates the number of requests made so far in the 15-minute window. +- **Backfill** — Use the `backfillMinutes` parameter for disconnections of 5 minutes or less +- **Recovery** — Use `startTime` and `endTime` parameters for longer disconnections (up to 24 hours) -* x-rate-limit-reset is a UNIX timestamp indicating when the 15-minute window will restart, resetting x-rate-limit-remaining to 0. +### Powerstream backfill example +```bash +curl 'https://api.x.com/2/powerstream?backfillMinutes=5' \ + -H "Authorization: Bearer $ACCESS_TOKEN" +``` -The filter stream endpoint does not currently report usage data. To check how many Posts have been delivered, your code can implement a metering logic, so that consumption can be measured and paused if needed.  +### Powerstream recovery example -Your code that hosts the client side of the stream simply inserts incoming Posts into a first in, first out (FIFO) queue, or a similar memory structure; a separate process/thread should consume Posts from that queue to parse and prepare content for storage. With this design, you can implement a service that can scale efficiently in case incoming Post volumes changes dramatically. Conceptually, you can think of it as downloading an infinitely long file over HTTP. +```bash +curl 'https://api.x.com/2/powerstream?startTime=2022-07-12T15:10:00Z&endTime=2022-07-12T15:20:00Z' \ + -H "Authorization: Bearer $ACCESS_TOKEN" +``` -## Reconnection best practices - -### Test backoff strategies - -A good way to test a backoff implementation is to use invalid authorization credentials and examine the reconnect attempts. A good implementation will not get any 429 responses. - -### Issue alerts for multiple reconnects - -If a client reaches its upper threshold of its time between reconnects, it should send you notifications so you can triage the issues affecting your connection. - -### Handle DNS changes - -Test that your client process honors the DNS Time To live (TTL). Some stacks will cache a resolved address for the duration of the process and will not pick up DNS changes within the prescribed TTL. Such aggressive caching will lead to service disruptions on your client as X shifts load between IP addresses. - -### User Agent +--- -Ensure your user-agent HTTP header includes the client’s version. This will be critical in diagnosing issues on X's end. If your environment precludes setting the user-agent field, then set an x-user-agent header. +## Next steps + + + + Complete disconnection handling guide + + + Recover missed data + + + Build robust streaming clients + + diff --git a/x-api/powerstream/introduction.mdx b/x-api/powerstream/introduction.mdx index 20126cbe7..39cbee112 100644 --- a/x-api/powerstream/introduction.mdx +++ b/x-api/powerstream/introduction.mdx @@ -1,18 +1,20 @@ --- title: Introduction sidebarTitle: Introduction -keywords: ["powerstream", "powerstream API", "real-time streaming", "streaming rules", "filtered stream", "enterprise streaming", "GNIP powerstream"] +keywords: ["powerstream", "powerstream API", "real-time streaming", "low latency streaming", "streaming rules", "filtered stream", "enterprise streaming", "GNIP powerstream"] --- -Powerstream is our fastest, real-time streaming API for accessing public X data. Similar to the legacy GNIP Powetrack API, it uses rules to filter Posts based on keywords, operators, and metadata. Once a persistent http connection is made to the Powerstream endpoint, you can start receiving matching Posts in near-real time. +Powerstream is our **lowest-latency streaming API** for accessing public X data in real-time. Unlike other streaming endpoints that prioritize data hydration and delivery (with ~6-7 seconds P99 latency), Powerstream is optimized for speed and delivers data with minimal delay. + +Similar to the legacy GNIP Powertrack API, it uses rules to filter Posts based on keywords, operators, and metadata. Once a persistent HTTP connection is made to the Powerstream endpoint, you can start receiving matching Posts in real-time. Currently, Powerstream supports up to 1,000 rules and each rule can be 2048 characters. ## Key Features: -- **Real-time data delivery**: Get data matching your rules in near-real time. +- **Real-time data delivery**: Lowest latency option for streaming Posts as they're published. - **Precise filtering**: Filter for exactly the data you are looking for using Boolean queries with operators. - **Delivery**: JSON response over HTTP/1.1 chunked transfer encoding. -- **Local Data-center support**: Fetch posts only from the local datacenter to reduce latency by avoiding replication lag. +- **Local datacenter support**: Fetch Posts only from the local datacenter to further reduce latency by avoiding replication lag. The Powerstream API is a premium offering available under select Enterprise plans. @@ -114,7 +116,7 @@ else: ``` ### 4. PowerStream (GET /stream) -Connect to the stream for real-time Posts. Use `stream=True` for line-by-line reading. Implement reconnect logic for robustness. +Connect to the stream for real-time, low-latency Posts. Use `stream=True` for line-by-line reading. Implement reconnect logic for robustness. ```python stream_url = base_url @@ -170,64 +172,13 @@ In addition to this, it will also send an initial payload specifying the datacen ## Operators -In order to set rules for filtering, you can use keywords and operators. Check out the list of available operators below. - -### Field-Based Operators - -#### User Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `from:` | Matches posts from a specific user | `from:xdevelopers` or `from:123456` | -| `to:` | Matches posts directed to a specific user | `to:jvaleski` | -| `retweets_of:` | Matches reposts of a specific user | `retweets_of:xdevelopers` | - -#### Content Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `contains:` | Matches posts containing specific text/keywords | `contains:hello` or `contains:-2345.432` | -| `url_contains:` | Matches posts with URLs containing specific text | `url_contains:"com/willplayforfood"` | -| `lang:` | Matches posts in specific languages | `lang:en` | - -#### Entity Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `has:` | Matches posts containing specific entities (Options: mentions, geo, links, media, lang, symbols, images, videos) | `has:images`, `has:geo`, `has:mentions` | -| `is:` | Matches posts of specific types or with specific properties (Options: retweet, reply) | `is:retweet`, `is:reply` | - -#### Location Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `place:` | Matches posts from specific places/locations | `place:"Belmont Central"`, `place:02763fa2a7611cf3` | -| `bounding_box:` | Matches posts within a geographic bounding box | `bounding_box:[-112.424083 42.355283 -112.409111 42.792311]` | -| `point_radius:` | Matches posts within a radius of a point | `point_radius:[-111.464973 46.371179 25mi]`, `point_radius:[-111.464973 46.371179 15km]` | - -#### Advanced/Content Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `bio:` | Matches posts from users with specific bio content (Uses phrase matching) | N/A | -| `bio_name:` | Matches posts from users with specific name in bio (Uses phrase matching) | N/A | - -#### Additional Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `retweets_of_status_id:` | Matches reposts of specific posts | `retweets_of_status_id:1234567890123456789` | -| `in_reply_to_status_id:` | Matches replies to specific posts | `in_reply_to_status_id:1234567890123456789` | - -### Non-Field Operators - -#### Special Syntax Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `@` | Mention operator | `@username` | -| Phrase matching | Matches exact phrases | `"exact phrase"` | - -#### Logical Operators -| Operator | Summary | Example | -|----------|---------|---------| -| `OR` | Logical OR between expressions | `x OR facebook` | -| Space/AND | Logical AND between expressions | `x facebook` (both terms must be present) | -| `()` | Grouping for complex expressions | `(x OR facebook) iphone` | -| `-` | Negation/exclusion | `x -facebook` (x but not facebook) | +In order to set rules for filtering, you can use keywords and operators. + + + Complete list of available operators + + +--- ## Responses @@ -335,3 +286,22 @@ The payload of the Powestream API is the same format as the legacy GNIP Powertra - Reconnection: Exponential backoff on disconnects. - Monitoring: Use `Connection: keep-alive` headers. +--- + +## Streaming fundamentals + + + + Best practices for streaming clients + + + Reconnect gracefully + + + Handle high throughput + + + Build resilient applications + + + diff --git a/x-api/powerstream/operators.mdx b/x-api/powerstream/operators.mdx new file mode 100644 index 000000000..bcf60af7f --- /dev/null +++ b/x-api/powerstream/operators.mdx @@ -0,0 +1,151 @@ +--- +title: Powerstream Operators +sidebarTitle: Operators +description: Complete list of operators for Powerstream filtering rules +keywords: ["powerstream operators", "filter operators", "rule operators", "streaming operators", "powerstream rules", "operators guide"] +--- + +This page provides a complete list of operators available when building rules for [Powerstream](/x-api/powerstream/introduction). + +## Overview + +Powerstream uses Boolean queries with operators to filter Posts based on keywords and metadata. Rules can be up to 2,048 characters and you can have up to 1,000 rules. + +--- + +## User Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `from:` | Matches posts from a specific user | `from:xdevelopers` or `from:123456` | +| `to:` | Matches posts directed to a specific user | `to:jvaleski` | +| `retweets_of:` | Matches reposts of a specific user | `retweets_of:xdevelopers` | + +--- + +## Content Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `contains:` | Matches posts containing specific text/keywords | `contains:hello` or `contains:-2345.432` | +| `url_contains:` | Matches posts with URLs containing specific text | `url_contains:"com/willplayforfood"` | +| `lang:` | Matches posts in specific languages | `lang:en` | + +--- + +## Entity Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `has:` | Matches posts containing specific entities | `has:images`, `has:geo`, `has:mentions` | +| `is:` | Matches posts of specific types or properties | `is:retweet`, `is:reply` | + +### Available `has:` options + +- `has:mentions` — Posts that mention another user +- `has:geo` — Posts with geolocation data +- `has:links` — Posts containing links +- `has:media` — Posts containing media +- `has:lang` — Posts with detected language +- `has:symbols` — Posts containing cashtags +- `has:images` — Posts containing images +- `has:videos` — Posts containing videos + +### Available `is:` options + +- `is:retweet` — Retweets +- `is:reply` — Replies to other posts + +--- + +## Location Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `place:` | Matches posts from specific places/locations | `place:"Belmont Central"` or `place:02763fa2a7611cf3` | +| `bounding_box:` | Matches posts within a geographic bounding box | `bounding_box:[-112.424083 42.355283 -112.409111 42.792311]` | +| `point_radius:` | Matches posts within a radius of a point | `point_radius:[-111.464973 46.371179 25mi]` or `point_radius:[-111.464973 46.371179 15km]` | + +--- + +## User Profile Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `bio:` | Matches posts from users with specific bio content | `bio:"data scientist"` | +| `bio_name:` | Matches posts from users with specific name in bio | `bio_name:PhD` | + +--- + +## Post Reference Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `retweets_of_status_id:` | Matches reposts of specific posts | `retweets_of_status_id:1234567890123456789` | +| `in_reply_to_status_id:` | Matches replies to specific posts | `in_reply_to_status_id:1234567890123456789` | + +--- + +## Special Syntax Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `@` | Mention operator | `@username` | +| `"phrase"` | Matches exact phrases | `"exact phrase"` | + +--- + +## Logical Operators + +| Operator | Summary | Example | +|:---------|:--------|:--------| +| `OR` | Logical OR between expressions | `x OR facebook` | +| Space/AND | Logical AND between expressions | `x facebook` (both terms must be present) | +| `()` | Grouping for complex expressions | `(x OR facebook) iphone` | +| `-` | Negation/exclusion | `x -facebook` (x but not facebook) | + +--- + +## Building rules + +### Basic examples + +``` +# Match posts from a specific user +from:xdevelopers + +# Match posts containing keywords +contains:API contains:developer + +# Match posts with images in English +has:images lang:en +``` + +### Combining operators + +``` +# Posts from @xdevelopers OR @api that aren't retweets +(from:xdevelopers OR from:api) -is:retweet + +# Posts mentioning AI with images, excluding retweets +contains:AI has:images -is:retweet lang:en + +# Posts within 25 miles of a location +point_radius:[-73.935242 40.730610 25mi] lang:en +``` + +--- + +## Next steps + + + + Get started with Powerstream + + + Handle streaming disconnections + + + Recover missed data + + diff --git a/x-api/powerstream/recovery-and-redundancy.mdx b/x-api/powerstream/recovery-and-redundancy.mdx index abb5176e7..3e2cdb4e3 100644 --- a/x-api/powerstream/recovery-and-redundancy.mdx +++ b/x-api/powerstream/recovery-and-redundancy.mdx @@ -4,39 +4,57 @@ sidebarTitle: Recovery and redundancy keywords: ["powerstream recovery", "powerstream redundancy", "recovery features", "redundancy features", "fault tolerance", "error recovery"] --- -When consuming streaming data, maximizing your connection time and receiving all matched data is a fundamental goal. This means that it is important to take advantage of redundant connections, automatically detect disconnections, to reconnect quickly, and to have a plan for recovering lost data. +This page covers Powerstream-specific recovery and redundancy features. For comprehensive guidance on recovery and redundancy across all streaming endpoints, see the [Recovery and redundancy fundamentals guide](/x-api/fundamentals/recovery-and-redundancy). -In this integration guide, we will discuss different recovery and redundancy features: redundant connections, backfill, and recovery. -  +## Powerstream recovery features -## Redundant connections +The core concepts for recovery and redundancy apply to all streaming endpoints. See the fundamentals guide for: -A redundant connection simply allows you to establish more than one simultaneous connections to the stream. This provides redundancy by allowing you to connect to the same stream with two separate consumers, receiving the same data through both connections. Thus, your app has a hot failover for various situations such as if one stream is disconnected or if your application's primary server fails. - -To use a redundant stream, simply connect to the same URL used for your primary connection. The data for your stream will be sent through both connections. +- Redundant connections +- Backfill vs. Recovery decision tree +- Best practices ## Backfill -After you've detected a disconnection, your system should be smart enough to reconnect to the stream. If possible, your system should take note of how long the disconnection lasted so that you can use the proper recovery feature to backfill the data.  - -If you identified that the disconnection lasted five minutes or less, you can use the backfill parameter, `backfillMinutes`. If you pass this parameter with your `GET /powerstream` request, you will receive the Posts that match your rules within the past one to five minutes. We generally deliver these older Posts first before any newly matched Posts, and also do not deduplicate Posts. This means that if you were disconnected for 90 seconds, but request two minutes worth of backfill data, you will receive 30 seconds worth of duplicate Posts, which your system should be tolerant of. Here is an example of what a request might look like with the backfill parameter: - -`curl 'https://api.x.com/2/powerstream?backfillMinutes=5' -H "Authorization: Bearer $ACCESS_TOKEN"` +For disconnections of **5 minutes or less**, use the `backfillMinutes` parameter: +```bash +curl 'https://api.x.com/2/powerstream?backfillMinutes=5' \ + -H "Authorization: Bearer $ACCESS_TOKEN" +``` -If you identified that the disconnection time lasted for longer than five minutes, you can utilize the [recent search endpoint](/x-api/posts/search/introduction) or the recovery feature to request missed data.  + +Older Posts are delivered first. Posts are **not** deduplicated — your system should be tolerant of duplicates. + ## Recovery -You can use the Recovery feature to recover missed data within the last 24 hours if you are unable to reconnect with the 5 minute backfill window. +For disconnections **longer than 5 minutes** (up to 24 hours), use the `startTime` and `endTime` parameters: -The streaming recovery feature allows you to have an extended backfill window of 24 hours. Recovery enables you to 'replay' the time period of missed data. A recovery stream is started when you make a connection request using `startTime` and `endTime` request parameters. Once connected, Recovery will re-stream the time period indicated, then disconnect.   +| Parameter | Type | Description | +|:----------|:-----|:------------| +| `startTime` | ISO 8601 date | Start time to recover from (UTC) | +| `endTime` | ISO 8601 date | End time to recover to (UTC) | -| | | | -| :--- | :--- | :--- | -| **Name** | **Type** | **Description** | -| `startTime` | date (ISO 8601) | YYYY-MM-DDTHH:mm:ssZ (ISO 8601/RFC 3339).

Date in UTC signifying the start time to recover from. | -| `endTime` | date (ISO 8601) | YYYY-MM-DDTHH:mm:ssZ (ISO 8601/RFC 3339).

Date in UTC signifying the end time to recover to. | +```bash +curl 'https://api.x.com/2/powerstream?startTime=2022-07-12T15:10:00Z&endTime=2022-07-12T15:20:00Z' \ + -H "Authorization: Bearer $ACCESS_TOKEN" +``` +Once connected, Recovery will re-stream the specified time period, then disconnect. + +--- -Example request URL: `https://api.x.com/2/powerstream?startTime=2022-07-12T15:10:00Z&endTime=2022-07-12T15:20:00Z` \ No newline at end of file +## Next steps + + + + Complete recovery guide + + + Handle streaming disconnections + + + Build robust streaming clients + + diff --git a/x-api/tools-and-libraries/overview.mdx b/x-api/tools-and-libraries/overview.mdx index 915d08c24..26ad58581 100644 --- a/x-api/tools-and-libraries/overview.mdx +++ b/x-api/tools-and-libraries/overview.mdx @@ -120,8 +120,6 @@ If you've built an X API library, share it with the community: 1. Post in the [Libraries & SDKs forum](https://devcommunity.x.com/c/libraries-and-sdks/63) 2. We may add it to this page! -Use our [version badges](https://twbadges.glitch.me/) in your README to show API compatibility. - --- ## Getting help diff --git a/x-api/users/get-affiliates.mdx b/x-api/users/get-affiliates.mdx new file mode 100644 index 000000000..de66976f2 --- /dev/null +++ b/x-api/users/get-affiliates.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /2/users/{id}/affiliates +--- \ No newline at end of file diff --git a/x-api/webhooks/introduction.mdx b/x-api/webhooks/introduction.mdx index 3c75a3023..ebc93993f 100644 --- a/x-api/webhooks/introduction.mdx +++ b/x-api/webhooks/introduction.mdx @@ -39,11 +39,9 @@ Webhooks enable real-time data delivery to your server. Instead of polling for u ## How webhooks work -``` -┌──────────┐ ┌──────────┐ ┌──────────┐ -│ X Event │ → │ X Server │ → │ Your │ -│ Occurs │ │ │ │ Webhook │ -└──────────┘ └──────────┘ └──────────┘ +```mermaid actions={false} +flowchart LR + A["X Event
Occurs"] --> B["X Server"] --> C["Your
Webhook"] ``` 1. **Event occurs** — A user posts, sends a DM, etc. diff --git a/x-api/what-to-build.mdx b/x-api/what-to-build.mdx index 0f4c9fe67..e3adacd95 100644 --- a/x-api/what-to-build.mdx +++ b/x-api/what-to-build.mdx @@ -130,7 +130,7 @@ Analyze public conversations and extract insights. **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 +- [Filtered stream](/x-api/posts/filtered-stream/introduction) — Near real-time matching posts - [Post annotations](/x-api/fundamentals/post-annotations) — Entity recognition - [Conversation ID](/x-api/fundamentals/conversation-id) — Thread reconstruction diff --git a/x-for-websites/direct-message-button.mdx b/x-for-websites/direct-message-button.mdx new file mode 100644 index 000000000..70e25baea --- /dev/null +++ b/x-for-websites/direct-message-button.mdx @@ -0,0 +1,50 @@ +--- +title: Direct Message button +sidebarTitle: Direct Message button +--- + + +The Message button is a small button to help your customers easily send a Direct Message to you on X. Allow your customers to contact you to ask questions and get support from right on your website. + +A Message button consists of two parts: a link to the direct message composer on X.com, and the X for Websites JavaScript to enhance the link with the recognizable Message button. + +## Message creation flow + +Clicking the button will link the user to the Direct Message composer, pre-populated with the values you define in the button markup. + +## How to add a message button to your website + +[publish.x.com](https://publish.x.com) is a simple configuration tool for to create a message button. Just enter your @screenName to get started. + +### Manually + +1. Create an anchor element with a `twitter-dm-button` class name. Set a `href` attribute value of `https://x.com/messages/compose` to create a link to the X direct message view. The `recipient_id` query parameter is the ID of the @username that should receive the messages. + +```html + +``` + +2. Optionally pre-populate message text by customizing the `text` query parameter. + +```html + +``` + +3. Asynchronously load the X for Websites JavaScript using our loading snippet. The script will initialize the message button after your page content loads. + +### Query parameters + +| Parameter | Description | +|----------------|-----------------------------------------------------------------------------| +| `recipient_id` | **Required**. The user ID of the X user that will receive the messages. | +| `text` | **Optional**. The pre-populated text in the message. Text must be URL encoded. | + +### Button customization + +#### Size + +Add a `data-size` attribute value of `large` to display a larger message button. \ No newline at end of file diff --git a/x-for-websites/embedded-posts/faq.mdx b/x-for-websites/embedded-posts/faq.mdx new file mode 100644 index 000000000..2a0e31811 --- /dev/null +++ b/x-for-websites/embedded-posts/faq.mdx @@ -0,0 +1,32 @@ +--- +title: Frequently asked questions +sidebarTitle: FAQ +--- + +## A fully-rendered Post does not display on my page + +An embedded Post comes in two parts: a `
` containing Post information and the JavaScript file from a X server which converts the `
` into a fully-rendered Post. + +If you see Post text on your page but not a fully-rendered Post it’s possible your CMS or editing interface stripped the ` +``` + +The JavaScript snippet above loads the X widgets JavaScript if it is not already present on the page. The loaded JavaScript will convert embedded Posts, Embedded Timelines, Post Buttons, and Follow Buttons on your page into interactive widgets and buttons. + +## What happens when an author deletes their Post? + +The X widgets JavaScript will not display a fully-rendered Post if the Post no longer exists on X. The fallback `
` containing Post information will be visible on the page. + +## Will embedded Posts adapt to my responsive site? + +An embedded Post will adjust to the width of its containing element when inserted into a page. An embedded Post requires a minimum width of 220 pixels and will fill up to 550 horizontal pixels. + +## How can I create an Embedded Post of a consistent height? + +A basic embedded Post includes Post text up to 140 characters in length, author information, a timestamp, and Post actions. An embedded Post shows the previous Post in a conversation and photos, a video, or a link preview associated with the Post by default. Set conversation and cards parameters to false to limit Post display to just its basic formatting. \ No newline at end of file diff --git a/x-for-websites/embedded-posts/guides/cms-best-practices.mdx b/x-for-websites/embedded-posts/guides/cms-best-practices.mdx new file mode 100644 index 000000000..e7fc7f567 --- /dev/null +++ b/x-for-websites/embedded-posts/guides/cms-best-practices.mdx @@ -0,0 +1,121 @@ +--- +title: Embedded Post CMS best practices +sidebarTitle: CMS best practices +--- + +## Overview + +An [embedded Post](/x-for-websites/embedded-posts/overview) adds pieces of the global conversation happening on X to your site, content, and commentary. Sites powered by a CMS or custom software can provide deeper integration with X content through site-level display preferences, content macros for cross-platform publishing, oEmbed fallback content, and JavaScript loaders described in this guide. Your extra effort will remove barriers between authors and cited content while integrating our third-party content with your site’s content flows and visual design. + +1. [Define site-level theme options](/x-for-websites/embedded-posts/guides/cms-best-practices.html#theme-options) +2. [Define site-level widget options](/x-for-websites/embedded-posts/guides/cms-best-practices.html#widget-options) +3. [Define an embedded Post macro](/x-for-websites/embedded-posts/guides/cms-best-practices.html#macro) +4. [Request fallback markup using oEmbed](/x-for-websites/embedded-posts/guides/cms-best-practices.html#oembed) +5. [Load widget JavaScript using your site’s resource manager](/x-for-websites/embedded-posts/guides/cms-best-practices.html#js-loading) + +## Define site-level theme options + +Integrate X widgets, including embedded Posts, with your site’s color scheme by including HTML `` elements `` in the `` section of your webpage. Choose a light or dark color theme for Post text and background and customize the iframe border color by adding new markup to your site templates. + +```html + + +``` + +X for Websites reads the meta values on the page before constructing a new widget; these values can be overridden for individual widgets by passing the appropriate oEmbed parameter. + +Adding a X widget options section in your CMS administrative interface helps individual publishers customize content without editing theme files. + +## Define site-level widget options + +Embedded Posts may contain images, video, link previews, or other Posts in conversations. Your site can specify site-wide preferences for display consistency across all pages, overriding default display options. + +Adding an embedded Post options section in your CMS administrative interface helps individual publishers set their preferences for Post content display across the entire site. + +## Define an embedded Post macro + +A macro added to your site’s editing interface abstracts away the detail of embedding Posts, allowing an author to focus on content. Behind-the-scenes your macro should determine the best content to display in the current rendering environment, such as HTML and JavaScript on a webpage, native views on iOS and Android, or custom elements in AMP or Apple News Format. + +The simplest macro is a Post URL added to your article content. You may choose to have a more custom macro to capture additional embedded Post options unique to the article display. + +``` +[tweet id="611193269532295168" lang="fr"] +``` + +Verify macro inputs against a set of known embedded Post parameters before passing the option to the X oEmbed endpoint to set author expectations and remove unneeded cruft. + +## Request fallback markup using oEmbed + +An embedded Post should provide appropriate context when viewed on its own, before additional enhancements provided by X for the current viewer. Request and store HTML markup for the requested Post data by requesting content from the [oEmbed API endpoint](/x-for-websites/oembed-api). Add any custom options passed to your site’s macro, or extracted from the current context, as query parameters in the oEmbed request. + +A Post ID is preferred over a Post's web URL: components of the URL such as the associated account’s screen_name may change; the Post ID is unique to the cited content and will remain the same. + +You may choose to omit theme-related options from oEmbed requests if you define these preferences inside your webpage’selements as described above. + +The oEmbed response from X's servers adheres to the [oEmbed specification](https://oembed.com/). X's oEmbed response is a “rich” oEmbed type containing HTML markup suitable for use inside existing HTML code. + +Sites should cache the HTML returned by the oEmbed API for the suggested cache lifetime specified by the `cache_age` response parameter. Your cache method should incorporate the customization parameters passed to the oEmbed API as these parameters will modify the HTML response. + +## Load widget JavaScript using your site’s resource manager + +Many sites, frameworks, and CMS systems have specialized CSS and JavaScript resource managers or module loaders tracking dependencies, asynchronous loading, and versioning. Include the remote-hosted widgets.js in your site’s resource loader whenever your site content includes an Embedded Post or other X widget content. + +Define a new module definition for the X for Websites JavaScript hosted at `https://platform.x.com/widgets.js`. Define a module ID of “twitter-wjs” to uniquely identify the X JavaScript with the common ID used by [asynchronous loading snippets](/x-for-websites/javascript-api/guides/set-up-x-for-websites) — just in case copy-and-pasted JavaScript makes its way onto the webpage. + +Include an `omit_script=true` parameter in your oEmbed request to strip JavaScript from the X response. + +Sites loading page fragments containing an embedded Post will need to request a scan for embedded Post content by calling the `twttr.widgets.load` function `` after the new content is inserted into the page. Pass one or more DOM elements to the load function to restrict the scan for new content to just the new page fragments to improve site performance. + +Sites calling X JavaScript should setup an asynchronous function queue storing functions to be executed in an array accessible at `window.twttr._e`. The X asynchronous loading snippet provides setup of a function queue array with new functions queued by passing a function to `window.twttr.ready`. The X for Websites JavaScript will flush the function queue while first executing its JavaScript. + +## Export Post content in syndicated formats + +A modern news organization may publish alternate versions of its content formatted for Apple News, Facebook Instant Articles, or AMP clients. A Post cited in an article will need to be described in the markup expected by each format. + +### AMP + +A Post should be described in an AMP template using the `` custom element. + +```html + +``` + +The AMP webpage will also need to add the `amp-twitter` component’s JavaScript rendering code to the document’s `` to transform the supplied markup into a rendered embedded Post. + +```html + +``` + +### Apple News Format + +Describe a Post in your article using [the Post component in Apple News Format](https://developer.apple.com/library/ios/documentation/General/Conceptual/Apple_News_Format_Ref/Tweet.html). + +```json +{ + "role": "tweet", + "URL": "https://x.com/interior/status/611193269532295168" +} +``` + +### Facebook Instant Articles Format + +Describe a Post contained in your content with [an op-interactive `
` element](https://developers.facebook.com/docs/instant-articles/reference/embeds). + +```html +
+ +
+``` \ No newline at end of file diff --git a/x-for-websites/embedded-posts/guides/css-for-embedded-posts.mdx b/x-for-websites/embedded-posts/guides/css-for-embedded-posts.mdx new file mode 100644 index 000000000..2204b8716 --- /dev/null +++ b/x-for-websites/embedded-posts/guides/css-for-embedded-posts.mdx @@ -0,0 +1,46 @@ +--- +title: CSS for embedded Posts +sidebarTitle: CSS for embedded Posts +--- + +An embedded Post appears on your page as a `
` with the cited text of a Post; this markup is later interpreted by X for Websites code to render a full embedded Post. The `
` HTML is available in all rendering environments, including RSS feeds and email updates. + +An embedded Post's fallback markup will inherit the CSS rules already applied to `
`, `

`, and `` HTML elements on your webpage. + +Below is some basic CSS to style an embedded Post's `

` content in a way that more closely resembles its fully-rendered form. + +```css +blockquote.twitter-tweet { + display: inline-block; + font-family: "Helvetica Neue", Roboto, "Segoe UI", Calibri, sans-serif; + font-size: 12px; + font-weight: bold; + line-height: 16px; + border-color: #eee #ddd #bbb; + border-radius: 5px; + border-style: solid; + border-width: 1px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); + margin: 10px 5px; + padding: 0 16px 16px 16px; + max-width: 468px; +} + +blockquote.twitter-tweet p { + font-size: 16px; + font-weight: normal; + line-height: 20px; +} + +blockquote.twitter-tweet a { + color: inherit; + font-weight: normal; + text-decoration: none; + outline: 0 none; +} + +blockquote.twitter-tweet a:hover, +blockquote.twitter-tweet a:focus { + text-decoration: underline; +} +``` \ No newline at end of file diff --git a/x-for-websites/embedded-posts/guides/embedded-post-javascript-factory-function.mdx b/x-for-websites/embedded-posts/guides/embedded-post-javascript-factory-function.mdx new file mode 100644 index 000000000..38a5f82f4 --- /dev/null +++ b/x-for-websites/embedded-posts/guides/embedded-post-javascript-factory-function.mdx @@ -0,0 +1,45 @@ +--- +title: Embedded Post JavaScript Factory Function +sidebarTitle: Embedded Post JavaScript Factory Function +--- + +The X for Websites JavaScript library supports dynamic insertion of embedded Posts using the `twttr.widgets.createTweet` function. Pass a Post ID, target parent element, and any custom options. + +The code snippets on this page assume `widgets.js` has successfully loaded on your page. Include an asynchronous script loader on your page while initializing `window.twttr` as described in our [JavaScript loader documentation](/x-for-websites/javascript-api/guides/set-up-x-for-websites). All JavaScript code depending on `widgets.js` should execute on or after `twttr.ready`. + +| Parameter | Description | Example value | +|------------|-----------------------------------------------------------------------------|--------------------------------------------| +| `tweetID` | The numerical ID of the desired Post. | `'20'` | +| `targetEl` | DOM node of the desired parent element. | `document.getElementById('container')` | +| `options` | Override default widget options. See [Embedded Post parameter reference](/x-for-websites/embedded-posts/guides/embedded-x-parameter-reference) for details. | `{ theme: 'dark' }` | + +## Example + +An element with a DOM ID of `container` exists on the page. + +```html +
+``` + +The code snippet below will insert [Post ID 20](https://x.com/jack/status/20) into a page inside an element with a unique ID of `container`. The options object specifies a dark theme customization. + +```javascript +twttr.widgets.createTweet( + '20', + document.getElementById('container'), + { + theme: 'dark' + } +); +``` + +## Promises + +The `twttr.widgets.createTweet` returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). You can execute code after a widget has been inserted onto your page by passing a callback to the resulting promise’s `then` function. + +```javascript +twttr.widgets.createTweet(...) +.then( function( el ) { + console.log('Post added.'); +}); +``` \ No newline at end of file diff --git a/x-for-websites/embedded-posts/guides/embedded-tweet-parameter-reference.mdx b/x-for-websites/embedded-posts/guides/embedded-tweet-parameter-reference.mdx new file mode 100644 index 000000000..45e11a7d5 --- /dev/null +++ b/x-for-websites/embedded-posts/guides/embedded-tweet-parameter-reference.mdx @@ -0,0 +1,45 @@ +--- +title: Embedded Post parameter reference +sidebarTitle: Embedded Post parameter reference +--- + +An embedded Post supports customization in `data-*` attributes and JavaScript factory functions. This reference document describes parameters used in all formats. + +Reference the [oEmbed API](/x-for-websites/oembed-api) to set these parameters as part of an HTML response for a Post ID or URL. + +## Parameters + +| Parameter | Description | Example | +|---------------|-----------------------------------------------------------------------------|------------| +| `id` | The numerical ID of the desired Post. | `20` | +| `cards` | When set to `hidden`, links in a Post are not expanded to photo, video, or link previews. | `hidden` | +| `conversation`| When set to `none`, only the cited Post will be displayed even if it is in reply to another Post. | `none` | +| `theme` | When set to `dark`, displays Post with light text over a dark background. | `dark` | +| `width` | The maximum width of the rendered Post in whole pixels. This value should be between 250 and 550 pixels. | `325` | +| `align` | Float the Post `left`, `right`, or `center` relative to its container. Typically set to allow text or other content to wrap around the Post. | `right` | +| `lang` | A supported [X language code](/x-for-websites/x-for-websites-supported-languages/overview). Loads text components in the specified language. Note: does not affect the text of the cited Post. | `es` | +| `dnt` | When set to `true`, the Post and its embedded page on your site are not used for purposes that include [personalized suggestions](https://support.x.com/articles/20169421) and [personalized ads](https://support.x.com/articles/20170405). | `true` | + +### HTML example + +```html +
+``` + +### JavaScript factory example + +```javascript +twttr.widgets.createTweet( + "20", + document.getElementById("tweet-container"), + { + theme: "dark" + } +); +``` + + +The numeric Post ID should be passed as a string. + \ No newline at end of file diff --git a/x-for-websites/embedded-posts/overview.mdx b/x-for-websites/embedded-posts/overview.mdx new file mode 100644 index 000000000..88c110f8f --- /dev/null +++ b/x-for-websites/embedded-posts/overview.mdx @@ -0,0 +1,57 @@ +--- +title: Embedded Posts +sidebarTitle: Overview +--- + +Embedded Posts bring your pick of content from X into your website articles. An embedded Post includes photos, video and cards media created for display on X, and can even stream live video from Periscope. All aspects of X’s display requirements are handled for you by using our tools; author attribution, Post actions, hashtags, mentions, and other key components of the X experience. + +An embedded Post consists of two parts: An HTML snippet hosted in your web page, and the X for Websites JavaScript to transform that code into a fully-rendered Post. You can copy embedded Post markup generated from the Post menu on x.com, paste a URL into a supporting CMS, or add a Post to the page programmatically using a JavaScript factory function. + +## Embed from x.com + +Every Post displayed on x.com includes an embed code to easily copy-and-paste into your webpage. Activate the Post menu and select “Embed Post to generate markup to include on your webpage: + +## Convert Post URLs using oEmbed + +Programmatically convert a Post URL into embedded Post markup using the oEmbed API. Make embedding a Post in your CMS or app as simple as pasting a Post URL. + +```bash +https://publish.x.com/oembed? + url=https://x.com/Interior/status/463440424141459456 +``` + +Our [CMS best practices guide](/x-for-websites/embedded-posts/guides/cms-best-practices) describes common patterns for sites adding software support for embedded Posts. + +## Customize Post display + +Customize an embedded Post for your site’s visual design and display preferences by including extra parameters in your embedded Post HTML. + +### Match your site’s color scheme + +An embedded Post supports light and dark color themes. Customize X widgets [at the page-level with `` elements](/x-for-websites/webpage-properties/overview) or `add data-*` attributes to individual generated `
` elements. + +View our [embedded Post reference documentation](/x-for-websites/embedded-posts/guides/embedded-post-parameter-reference) for a full list of embedded Post options. + +### Don’t show previous Post in conversation thread + +A Post may be in reply to another Post. By default, we include a summary of the previous Post in the conversation to provide context. + +Set an oEmbed query parameter of `hide_thread=true` or add a `data-conversation="none"` attribute to the resulting `
` element to prevent the display of a parent Post. + +### Hide photos, videos, and Cards + +A Post may include a photo, video, or link to or other content supporting a [Card](https://dev.x.com/cards/overview). By default, this media is displayed in embedded Posts. You can hide this media if editorially desired. + +Set an oEmbed query parameter of `hide_media=true` or add a `data-cards="hidden"` attribute to the resulting `
` element to prevent expanded content display. + +### Customize Alignment + +An embedded Post is aligned left by default. The Post can be center or right aligned if preferred using the align customization. While historically CSS provides numerous hacks for centering content on a web page, this is the recommended and official supported method. + +## Render a Post with JavaScript + +Our widget JavaScript scans the DOM on execution, converting `blockquote.twitter-tweet` elements into fully-rendered embedded Posts based on element content. + +If dynamically inserting new content into a page, pass the new document fragment to [twttr.widgets.load()](/x-for-websites/javascript-api/guides/scripting-loading-and-initialization) to initialize embedded Post content. + +To directly render an embedded Post at runtime use the [twttr.widgets.createTweet()](/x-for-websites/embedded-posts/guides/embedded-post-javascript-factory-function) function. \ No newline at end of file diff --git a/x-for-websites/follow-button/faqs-follow-button.mdx b/x-for-websites/follow-button/faqs-follow-button.mdx new file mode 100644 index 000000000..022e3c9a9 --- /dev/null +++ b/x-for-websites/follow-button/faqs-follow-button.mdx @@ -0,0 +1,10 @@ +--- +title: Frequently asked questions +sidebarTitle: FAQ +--- + +## What are the dimensions of the follow button? + +The follow button is 20 pixels in height. Its width will vary depending on the language you have chosen (or the language of the viewer), the length of the specified `screen_name`, number of followers when `show_count` is active, etc. We recommend you make room for the `screen_name` value, and `show_count` value if you choose to display it. + +Note if you use the JavaScript version of the Follow Button, you can specify the width using the `data-width` attribute. If you are using the iframe version you can use the `style` property. \ No newline at end of file diff --git a/x-for-websites/follow-button/guides/javascript-factory-function-follow-button.mdx b/x-for-websites/follow-button/guides/javascript-factory-function-follow-button.mdx new file mode 100644 index 000000000..aac559691 --- /dev/null +++ b/x-for-websites/follow-button/guides/javascript-factory-function-follow-button.mdx @@ -0,0 +1,47 @@ +--- +title: Follow Button JavaScript Factory Function +sidebarTitle: JavaScript Factory Function +--- + +The X for Websites JavaScript library supports dynamic insertion of a follow button using the `twttr.widgets.createFollowButton` function. Pass X username, target parent element, and any custom options. + +The code snippets on this page assume `widgets.js` has successfully loaded on your page. Include an asynchronous script loader on your page while initializing `window.twttr` as described in our [JavaScript loader documentation](/x-for-websites/javascript-api/guides/set-up-x-for-websites). All JavaScript code depending on `widgets.js` should execute on or after `twttr.ready`. + +## Arguments + +| Parameter | Description | Example value | +|--------------|--------------------------------------------------|--------------------------------------------| +| `username` | A X username | `'XDevelopers'` | +| `targetEl` | DOM node of the desired parent element | `document.getElementById('container')` | +| `options` | Override default widget options | `{ size: 'large' }` | + +## Example + +An element with a DOM ID of `container` exists on the page. + +```html +
+``` + +The code snippet below will create a new Follow button for the X username “XDevelopers” inserted into a page inside an element with a unique ID of `container`. + +```javascript +twttr.widgets.createFollowButton( + 'XDevelopers', + document.getElementById('container'), + { + size: 'large' + } +); +``` + +## Promises + +The `twttr.widgets.createFollowButton` function returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). You can execute code after a widget has been inserted onto your page by passing a callback to the resulting promise’s `then` function. + +```javascript +twttr.widgets.createFollowButton(...) +.then( function( el ) { + console.log('Follow button added.'); +}); +``` \ No newline at end of file diff --git a/x-for-websites/follow-button/guides/parameter-reference-follow-button.mdx b/x-for-websites/follow-button/guides/parameter-reference-follow-button.mdx new file mode 100644 index 000000000..66ffb1f33 --- /dev/null +++ b/x-for-websites/follow-button/guides/parameter-reference-follow-button.mdx @@ -0,0 +1,39 @@ +--- +title: Parameters +sidebarTitle: Parameter Reference +--- + +A Follow button may be customized from its default settings by specifying parameter overrides. + +| Parameter | Description | Example | +|------------------|-----------------------------------------------------------------------------|------------| +| `screen_name` | The X username to be followed. Automatically extracted from the anchor element’s `href` attribute when using JavaScript-enhanced button markup. | `XDevelopers` | +| `show_screen_name` | Set to `false` to hide the username of the specified account. | `false` | +| `size` | Set to `large` to display a larger button. | `large` | +| `lang` | A supported [X language code](/x-for-websites/x-for-websites-supported-languages/overview). | `es` | +| `dnt` | When set to `true`, the button and its embedded page on your site are not used for purposes that include [personalized suggestions](https://support.x.com/articles/20169421) and [personalized ads](https://support.x.com/articles/20170405). | `true` | + +## HTML example + +```html + +``` + +## JavaScript example + +```javascript +twttr.widgets.createFollowButton( + "XDevelopers", + document.getElementById("container"), + { + showScreenName: "false", + size: "large" + } +); +``` \ No newline at end of file diff --git a/x-for-websites/follow-button/guides/web-intent-follow-button.mdx b/x-for-websites/follow-button/guides/web-intent-follow-button.mdx new file mode 100644 index 000000000..b28456749 --- /dev/null +++ b/x-for-websites/follow-button/guides/web-intent-follow-button.mdx @@ -0,0 +1,28 @@ +--- +title: Follow Button Web Intent +sidebarTitle: Web Intent +--- + +[Web Intents](/x-for-websites/web-intents/overview) display X actions optimized for display in a small browser window. A website may link to a follow web intent through a simple link on any website. X for Websites JavaScript widgets use the follow web intent to prompt viewers to follow an account. + +A follow Web Intent window features an inline login form for logged-out X users. + +The user will follow the account on successful login through the inline follow web intent login form. The follow status is confirmed by the “Following” button displayed in the web intent window. + +We recommend rendering a Web Intent child window dimensions of at least 550 pixels wide and 420 pixels high. + +## Web Intent URL + +``` +https://x.com/intent/follow +``` + +## Query parameters + +A follow Web Intent must reference a X account by `screen_name` or `user_id`. A `screen_name` may change; we recommend using `user_id` whenever possible. + +| Parameter | Description | Example value | +|---------------|--------------------------------------------------------------|-------------------| +| `screen_name` | The X username of the account you would like the viewer to follow | `xdevelopers` | +| `user_id` | The X user identifier of the account you would like the viewer to follow | `2244994945` | + diff --git a/x-for-websites/follow-button/overview.mdx b/x-for-websites/follow-button/overview.mdx new file mode 100644 index 000000000..cfc474e1b --- /dev/null +++ b/x-for-websites/follow-button/overview.mdx @@ -0,0 +1,43 @@ +--- +title: Follow button +sidebarTitle: Follow button +--- + +The Follow button is a small button displayed on your websites to help users easily follow a X account. A Follow button consists of two parts: a link to a [follow web intent](/x-for-websites/follow-button/overview) page on x.com and the X for Websites JavaScript to transform the link into our recognizable Follow button. + +## How to add a Follow button to your website + +The [publish.x.com](https://publish.x.com) website includes a simple tool to generate the embed for a Follow button to copy-and-paste into your website template. Just enter a @screenName to get started. + +### Manually + +1. Create an anchor element with a `twitter-follow-button` class name. Set the `href` attribute value pointing to a X profile URL. + +```html + +``` + +2. Customize Follow button parameters using `data-*` attributes. + +```html + +``` + +3. Asynchronously load the X for Websites JavaScript using our loading snippet. The script will initialize the Follow button after your page content loads. + + +## Button customization + +### Hide username + +Hide the username from the displayed Follow button by setting a `data-show-screen-name` attribute value of `false`. + +### Large button style + +Add a `data-size` attribute value of `large` to display a larger Follow button. + diff --git a/x-for-websites/javascript-api/guides/javascript-api.mdx b/x-for-websites/javascript-api/guides/javascript-api.mdx new file mode 100644 index 000000000..31cd08f4d --- /dev/null +++ b/x-for-websites/javascript-api/guides/javascript-api.mdx @@ -0,0 +1,224 @@ +--- +title: "Scripting: Events" +sidebarTitle: "Scripting: Events" +--- + +The X for Websites JavaScript fires events on initialization and after a viewer interacts with a widget. Enhance your application and analytics by tapping into widget events. + +Note that Web Intent events fire when the interaction occurs, not after the action completes. + +Bind a X widget event in your JavaScript code by registering a callback function with `twttr.events.bind`. + +```javascript +twttr.events.bind( + 'click', + function (ev) { + console.log(ev); + } +); +``` + +## The Intent Event Object + +When a detectable action occurs within a Web Intent or widget, an object representing the event is passed to your JavaScript callback. Intent Event objects include the following data: + +### Intent event object properties + +| Property | Data Type | Description | +|----------|--------------|-----------------------------------------------------------------------------------------------------------------------| +| `target` | `HTMLElement` | The DOM node where the widget is instantiated. Most likely an iframe, but may also be the original embed code element if the widget failed to initialize, or another sandboxed element. Use this value to differentiate between different intents or buttons on the same page. | +| `region` | `String` | Extended detail indicating where in a widget the event originated. For example, button or count components of Post button or Follow button integrations, or Post actions within an embedded Post. | +| `data` | `Object` | Key/value pairs relevant to the event. | + +## Waiting for Asynchronous Resources + +Loading the widgets.js file asynchronously will require you to wait before binding events: the functions you are calling do not yet exist. You will need to wrap your event bindings in a callback function such as the twttr.ready asynchronous function queue which will be invoked once everything has loaded. All event examples below assume you have wrapped those event bindings in this callback. + +```javascript +twttr.ready( + function (twttr) { + // bind events here + } +); +``` + +## Available Events + +### loaded + +Occurs after `twttr.widgets.load` has initialized widgets in a page. Includes an array of references to the newly created widget nodes. + +```javascript +twttr.events.bind( + 'loaded', + function (event) { + event.widgets.forEach(function (widget) { + console.log("Created widget", widget.id); + }); + } +); +``` + +### rendered + +Occurs after an individual widget in a page is rendered. Includes a reference to the newly created widget node. Occurs at the same time as `loaded`, but for each individual widget. Also triggered when creating a widget with a factory function. + +```javascript +twttr.events.bind( + 'rendered', + function (event) { + console.log("Created widget", event.target.id); + } +); +``` + +### tweet + +This event will be triggered when the user clicks a Post Web Intent. + +Includes a Post in reply to an [embedded Post](/x-for-websites/timelines/overview) or a Post displayed in an [embedded Timeline](/x-for-websites/timelines/overview). + +```javascript +twttr.events.bind( + 'tweet', + function (event) { + // Do something there + } +); +``` + +### follow + +This event will populate the followed `user_id` or `screen_name` in the event object’s data argument, depending on the parameters you provided in the web intent. + +```javascript +twttr.events.bind( + 'follow', + function (event) { + var followedUserId = event.data.user_id; + var followedScreenName = event.data.screen_name; + } +); +``` + +### retweet + +This event will populate the original Post that was reposted's `source_tweet_id` in the event object’s data argument. + +```javascript +twttr.events.bind( + 'retweet', + function(event) { + var repostedPostId = event.data.source_tweet_id; + } +); +``` + +### like + +This event will populate the liked `tweet_id` in the event object’s data argument. + +```javascript +twttr.events.bind( + 'like', + function(event) { + var likedPostId = event.data.tweet_id; + } +); +``` + +### click + +Receive an event when the user invokes a Web Intent from within an embedded widget. + +## Example: Detecting Events for Web Analytics + + +It’s easy to capture these events and pipe them to your web analytics solution. Please note that you’ll need to be using HTTP or HTTPs protocols on the hosting page for these events. + +```javascript +// Log any kind of Web Intent event to Google Analytics +// Category: "twitter_web_intents" +// Action: Intent Event Type +// Label: Identifier for action taken: tweet_id, screen_name/user_id, click region + +// First, load the widgets.js file asynchronously +window.twttr = (function(d, s, id) { + var js, fjs = d.getElementsByTagName(s)[0], + t = window.twttr || {}; + if (d.getElementById(id)) return; + js = d.createElement(s); + js.id = id; + js.src = "https://platform.x.com/widgets.js"; + fjs.parentNode.insertBefore(js, fjs); + + t._e = []; + t.ready = function(f) { + t._e.push(f); + }; + + return t; +}(document, "script", "twitter-wjs")); + +// Define our custom event handlers +function clickEventToAnalytics (intentEvent) { + if (!intentEvent) return; + var label = intentEvent.region; + pageTracker._trackEvent('twitter_web_intents', intentEvent.type, label); +} + +function postIntentToAnalytics (intentEvent) { + if (!intentEvent) return; + var label = "tweet"; + pageTracker._trackEvent( + 'twitter_web_intents', + intentEvent.type, + label + ); +} + +function likeIntentToAnalytics (intentEvent) { + postIntentToAnalytics(intentEvent); +} + +function repostIntentToAnalytics (intentEvent) { + if (!intentEvent) return; + var label = intentEvent.data.source_tweet_id; + pageTracker._trackEvent( + 'twitter_web_intents', + intentEvent.type, + label + ); +} + +function followIntentToAnalytics (intentEvent) { + if (!intentEvent) return; + var label = intentEvent.data.user_id + " (" + intentEvent.data.screen_name + ")"; + pageTracker._trackEvent( + 'twitter_web_intents', + intentEvent.type, + label + ); +} + +// Wait for the asynchronous resources to load +twttr.ready(function (twttr) { + // Now bind our custom intent events + twttr.events.bind('click', clickEventToAnalytics); + twttr.events.bind('tweet', postIntentToAnalytics); + twttr.events.bind('retweet', repostIntentToAnalytics); + twttr.events.bind('like', likeIntentToAnalytics); + twttr.events.bind('follow', followIntentToAnalytics); +}); +``` + +## Supplemental Events Resources and Examples + +- [Google Analytics Event Tracking Guide](http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html) +- [Google Analytics SocialWidgetTracker plugin](https://github.com/googleanalytics/autotrack/blob/master/lib/plugins/social-widget-tracker.js) +- [X Intent Events with Google Analytics using _gaq.push();](http://null-uk.com/55090968) by [@nulluk](http://x.com/intent/user?screen_name=nulluk) +- [An optimized approach to Google Analytics integration](https://gist.github.com/1004702) by [@yahel](https://x.com/yahel) + +These functions make it possible to integrate X user’s content into your site dynamically in a JavaScript application, and integrate user interactions into your own application experience. + +Please ask questions and share your code and examples in the [developer forum](https://devcommunity.x.com/). You may also refer to the main [X for Websites documentation](/x-for-websites/overview). \ No newline at end of file diff --git a/x-for-websites/javascript-api/guides/scripting-factory-functions.mdx b/x-for-websites/javascript-api/guides/scripting-factory-functions.mdx new file mode 100644 index 000000000..18d89e03b --- /dev/null +++ b/x-for-websites/javascript-api/guides/scripting-factory-functions.mdx @@ -0,0 +1,258 @@ +--- +title: "Scripting: Factory Functions" +sidebarTitle: "Scripting: Factory Functions" +--- + +If you’re integrating your site with X using [X for Websites](/x-for-websites/overview) and [Web Intents](/x-for-websites/web-intents/overview), you can dynamically generate widgets using JavaScript functions. + +X for Websites products—Post buttons, Follow buttons, embedded Posts and timelines—are all loaded using a JavaScript utility named `widgets-js`. When adding an X widget to your page, this JavaScript file is included in the HTML embed code, or you can directly include `https://platform.x.com/widgets.js` in your page. (See the [X for Websites set-up documentation](/x-for-websites/javascript-api/guides/set-up-x-for-websites) for a recommended code snippet.) + +By default, `widgets-js` will find mark-up in a page and convert basic, functional mark-up into rich interactive widgets. In addition, there are a number of functions of `widgets-js` that allow you to work with X content dynamically: + +## Creating widgets at runtime with factory functions + +Widgets can be generated at runtime, without requiring an HTML embed code. A set of factory functions can generate any widget type: + +- [twttr.widgets.createShareButton](/x-for-websites/post-button/guides/javascript-factory-function) +- [twttr.widgets.createFollowButton](/x-for-websites/follow-button/guides/javascript-factory-function-follow-button) +- [twttr.widgets.createHashtagButton](/x-for-websites/post-button/guides/hashtag-button) +- [twttr.widgets.createMentionButton](/x-for-websites/x-button/guides/mention-button) +- [twttr.widgets.createTimeline](/x-for-websites/timelines/overview) +- [twttr.widgets.createTweet](/x-for-websites/post-button/overview) + +### Buttons + +`createShareButton`, `createFollowButton`, `createHashtagButton`, and `createMentionButton` take similar arguments. + +#### Primary argument + +The first argument is required, and is unique to the button type. Provide a string representing one of: + +- `url`: The URL to be shared. +- `screen_name`: The screen_name of a user to be followed, or mentioned. +- `hashtag`: Hashtag to be posted and displayed on the button. + +#### Additional Arguments: + +- `target`: **Required**. The element in which to render the widget. +- `options`: _Optional_. An object hash of additional options to configure the widget. + + +Widgets usually render as iframe elements. When an iframe is moved within the DOM the browser will reload its content. For buttons this can waste bandwidth, while for Posts and timelines this will cause dynamically injected content to be lost. Use the target argument to render widgets into their final location in a page. If you need to delay the display of a widget, use CSS to position the widget off-screen until needed. + + +Every `create` function returns a `Promise`. You can execute code after a widget has been created by passing a callback to: + +```javascript +twttr.widgets.createFoo() + .then(function (element) { + console.log("Widget created.") + }); +``` + +When fulfilled, the promise will pass a reference to a newly created widget element to the chained callback. + +### Examples + +Create a share button for a URL: + +```javascript +twttr.widgets.createShareButton( + '/', + document.getElementById('new-button'), + { + count: 'none', + text: 'Sharing a URL using the Post Button' + }).then(function (el) { + console.log("Button created.") + }); +``` + +Create a Follow button for a user: + +```javascript +twttr.widgets.createFollowButton( + 'endform', + document.getElementById('new-button'), + { + size: 'large' + }).then(function (el) { + console.log("Follow button created.") + }); +``` + +### Options + +Additional configuration and options can be passed to the factory functions, as in the above examples. + +**Additional configuration for all widgets** + +| Option | Values | Default | Notes | +|----------|---------------------------------------------|-------------|-------------------------------------------------------------------------------------------------| +| `lang` | An ISO 639-1 language code | `en` | The language in which to render a widget, if supported (see the [Translation Center](http://translate.x.com/)). | +| `dnt` | `true`, `false` | `false` | When set to `true`, the embed and its embedded page on your site are not used for purposes that include [personalized suggestions](https://support.x.com/articles/20169421) and [personalized ads](https://support.x.com/articles/20170405). | +| `related`| Any comma-separated list of valid X screen names | `Undefined` | A list of X screen names to be suggested for following after a Post or Post action is posted. | +| `via` | Any valid X screen name | `Undefined` | An X user mentioned in the default Post text as `via@user` where appropriate. | + +**Additional configuration options for button widgets** + +| Option | Values | Default | Notes | +|--------|-------------------------|----------------------------------------------|-----------------------------------------------------------------------| +| `align`| `left`, `right` | Locale dependent (`left` or `right`, depending on the text direction of the language) | The alignment of the button within an iframe; use this to ensure flush layout when aligning buttons | +| `size` | `medium`, `large` | `medium` | | + +**Post button additional options** + +| Option | Values | Default | Notes | +|------------|---------------------------------|-------------|----------------------------------------------------------------------| +| `text` | Any string | `Undefined` | The default, highlighted text a user sees in the Post web intent | +| `hashtags` | A comma-separated list of hashtags | `Undefined` | A list of hashtags to be appended to default Post text where appropriate. | + +### Posts + +`createTweet` takes the ID for a Post, and then the same additional arguments as for buttons. + +#### Arguments + +- `tweetId`: The ID of a Post to be rendered. (This should be provided as a `String`, since X IDs are generated from 64-bit integers, and JavaScript integers are limited to 53 bits.) +- `target`: **Required**. The element in which to render the widget. +- `options`: _Optional_. A hash of additional options to configure the widget. + +#### Examples + +Create an embedded Post for [a Post from the US Department of Interior](https://x.com/Interior/status/511181794914627584): + +```javascript +twttr.widgets.createTweet( + '511181794914627584', + document.getElementById('first-tweet'), + { + align: 'left' + }) + .then(function (el) { + console.log("Post displayed.") + }); +``` + +### Options + +| Option | Values | Default | Notes | +|---------------|-------------------------------------|----------------------------------------------|-----------------------------------------------------------------------------------------| +| `conversation`| `none`, `all` | `all` | Posts in response to another Post will display a compact version of the previous Post by default. Use `none` to hide the parent Post in the conversation. | +| `cards` | `hidden`, `visible` | `visible` | Hide photos, videos, and link previews powered by [Cards](https://dev.x.com/cards). | +| `width` | Positive integer | `auto` (derived from container size) | Set the maximum width of the embedded Post | +| `align` | `left`, `right`, `center` | `Undefined` | Float the embedded Post to the left or right so that text wraps around it, or align center so it floats in the middle of a paragraph | +| `theme` | `dark`, `light` | `light` | Toggle the default color scheme of the embedded Post | + +### Timelines + +`createTimeline` takes the data source definition for a timeline. Additional arguments are consistent with embedded Posts. + +#### Arguments + +- **data source**: _Required_ The data source definition object for the content to be displayed in the widget. May be a widget ID string for a legacy widget. +- `target`: **Required**. The element in which to render the widget. +- `options`: _Optional_. A hash of additional options to configure the widget. + +#### Examples + +Create a timeline widget: + +```javascript +twttr.widgets.createTimeline( + { + sourceType: 'profile', + screenName: 'xdevelopers' + }, + document.getElementById('timeline'), + { + width: '450', + height: '700', + related: 'xdevelopers,api' + }).then(function (el) { + console.log('Embedded a timeline.') + }); +``` + +#### Data Source + +The data source definition describes what content will hydrate the embedded timeline. There are several types of data sources: profile; list; URL; widget configuration. + +##### Profile + +To power an embedded timeline with Posts from an individual user use a `profile` data source. To do so, set `sourceType` to `profile` and set one of `screenName` or `userId`. + +| Option | Values | +|-------------|-----------------------------| +| `sourceType`| `profile` | +| `screenName`| Valid X username | +| `userId` | Valid X user ID | + +```javascript +twttr.widgets.createTimeline( + { + sourceType: 'profile', + screenName: 'xdevelopers' + }, + document.getElementById('container') +); +``` + +##### List + +To power an embedded timeline with an X list use a `list` data source. Set `sourceType` to `list` and set both `ownerScreenName` and `slug`, or set an `id`. + +| Option | Values | Notes | +|------------------|-------------------------------------|------------------------------------| +| `sourceType` | `list` | | +| `ownerScreenName`| Valid X username | Used with `slug` | +| `slug` | The string identifier for a list | Used with `ownerScreenName` | +| `id` | Valid X list ID | | + +```javascript +twttr.widgets.createTimeline( + { + sourceType: 'list', + ownerScreenName: 'x', + slug: 'official-x-accts' + }, + document.getElementById('container') +); +``` + +##### URL + +To power an embedded timeline with X content represented by a URL use a `url` data source. Supported content includes profiles and lists. + +| Option | Values | +|-------------|---------------------------------------------| +| `sourceType`| `url` | +| `url` | Absolute URL of an X profile or list | + +```javascript +twttr.widgets.createTimeline( + { + sourceType: 'url', + url: 'https://x.com/xdevelopers' + }, + document.getElementById('container') +); +``` + +### Options + +All the parameters described above for all widgets and for embedded Posts also apply to embedded timelines. + +| Option | Values | Default | Notes | +|--------------|------------------------------------------------------------------------|----------------------|------------------------------------------------------------------------------------------------| +| `chrome` | `noheader`, `nofooter`, `noborders`, `transparent`, `noscrollbar` | `Undefined` | Toggle the display of design elements in the widget. This parameter is a space-separated list of values. | +| `height` | Positive integer | `600` | Set a fixed height of the embedded widget | +| `tweetLimit` | Range: `1`-`20` | `Undefined` | Render a timeline statically, displaying only n number of Posts. | +| `borderColor`| Hexadecimal color | Varies by theme | Adjust the color of borders inside the widget. | +| `ariaPolite` | `polite`, `assertive`, `rude` | `polite` | Apply the specified aria-polite behavior to the rendered timeline. New Posts may be added to the top of a timeline, affecting screen readers. | + +For more information on the options for customizing embedded Timelines refer to [Embedded Timelines](/x-for-websites/timelines/overview). + +These functions make it possible to integrate X user’s content into your site dynamically in a JavaScript application, and integrate user interactions into your own application experience. + +Please ask questions and share your code and examples in the [developer forum](https://devcommunity.x.com/). You may also refer to the main [X for Websites documentation](/x-for-websites/javascript-api/guides/set-up-x-for-websites). \ No newline at end of file diff --git a/x-for-websites/javascript-api/guides/scripting-loading-and-initialization.mdx b/x-for-websites/javascript-api/guides/scripting-loading-and-initialization.mdx new file mode 100644 index 000000000..9e08be4a0 --- /dev/null +++ b/x-for-websites/javascript-api/guides/scripting-loading-and-initialization.mdx @@ -0,0 +1,24 @@ +--- +title: "Scripting: Loading and Initialization" +sidebarTitle: "Scripting: Loading and Initialization" +--- + +## Initializing embedded content after a page has loaded + +Most X for Websites integrations will be well served by the recommended embed code found in the [set-up documentation](/x-for-websites/javascript-api/guides/set-up-x-for-websites), but you may want to optimize how and when the Twitter for Websites JavaScript widgets scans the page DOM to discover new HTML elements eligible for enhancement into buttons or widgets. + +If content is dynamically inserted into a page (such as lazy-loading content or using a `pushState` technique to navigate between articles) it’s necessary to parse new buttons and widgets using the `twttr.widgets.load()` function. + +```javascript +twttr.widgets.load() +``` + +Called without argument, `widgets-js` will search the entire `document.body` DOM tree for uninitialized widgets. For better performance, pass an `HTMLElement` object to restrict the search only to children of the element. + +Example: + +```javascript +twttr.widgets.load( + document.getElementById("container") +); +``` \ No newline at end of file diff --git a/x-for-websites/javascript-api/guides/set-up-x-for-websites.mdx b/x-for-websites/javascript-api/guides/set-up-x-for-websites.mdx new file mode 100644 index 000000000..33b9070e6 --- /dev/null +++ b/x-for-websites/javascript-api/guides/set-up-x-for-websites.mdx @@ -0,0 +1,48 @@ +--- +title: Set up X for Websites +sidebarTitle: Set up X for Websites +--- + +The easiest way to create a X for Websites widget — a Post button, Follow button, embedded Post or timeline — is to use our configuration tools at [publish.x.com](https://publish.x.com) then copy and paste the generated HTML code into the template or widget area for your site. + +- [Generate markup for a Post or Follow button](https://publish.x.com) +- [Generate markup for an embedded Post or timeline](https://publish.x.com) + +## For best performance and reliability, include the widgets.js script in your template + +Include the X for Websites JavaScript once in your page template for optimal web page performance and to enable tracking of [X widget JavaScript events](/x-for-websites/javascript-api/guides/javascript-api). + +If your site is using multiple widgets you can set up X widgets in your pages once, which will make your site faster, and widgets such as embedded Posts will be more reliable for authors when using content management systems. + +```html + +``` + +The above snippet optimizes loading by: + +1. Assign a HTML element ID of `twitter-wjs` to easily identify if the JavaScript file already exists on the page. Exit early if the ID already exists. +2. Asynchronously load the Twitter for Websites JavaScript. +3. Initialize an asynchronous function queue to hold dependent functions until the script is available. + +Include this snippet before any other JavaScript on your page which may depend on the `twttr.ready` asynchronous function queue. + +## Ignore script tags from embeds + +If you include the X JavaScript loader on every page you do not need to include the `", + "width": null, + "height": null, + "type": "rich", + "cache_age": "3153600000", + "provider_name": "X", + "provider_url": "https://x.com", + "version": "1.0" +} +``` \ No newline at end of file diff --git a/x-for-websites/timelines/guides/parameter-reference.mdx b/x-for-websites/timelines/guides/parameter-reference.mdx new file mode 100644 index 000000000..b503689e4 --- /dev/null +++ b/x-for-websites/timelines/guides/parameter-reference.mdx @@ -0,0 +1,47 @@ +--- +title: Parameters +sidebarTitle: Parameter reference +--- + +Customize an embedded timeline with `data-*` attributes added to a fallback anchor element or passed to a JavaScript factory function in an options object. + +| Option | Values | Default | Notes | +|-----------------|---------------------------------------------|----------------------------------------------|------------------------------------------------------------------------------------------------| +| `show-replies` | `true` | `false` | Show Posts in response to another Post or account | +| `chrome` | `noheader`, `nofooter`, `noborders`, `transparent`, `noscrollbar` | `Undefined` | Toggle the display of design elements in the widget. This parameter is a space-separated list of values | +| `theme` | `dark` | `light` | Display light text on a dark background | +| `width` | Positive integer | `auto` (derived from container size) | Set the maximum px width of the embedded Post | +| `height` | Positive integer | `600` | Set a fixed px height of the embedded widget | +| `tweet-limit` | Range: `1`–`20` | `Undefined` | Render a timeline statically, displaying only *n* number of Posts. The height parameter has no effect when a Post limit is set | +| `aria-polite` | `polite`, `assertive`, `rude` | `polite` | Apply the specified aria-polite behavior to the rendered timeline. New Posts may be added to the top of a timeline, affecting screen readers | +| `dnt` | `true`, `false` | `false` | When set to `true`, the timeline and its embedded page on your site are not used for purposes that include [personalized suggestions](https://support.x.com/articles/20169421) and [personalized ads](https://support.x.com/articles/20170405) | + +## HTML example + +```html + +``` + +## JavaScript factory example + +```javascript +twttr.widgets.createTimeline( + { + sourceType: "profile", + screenName: "xdevelopers" + }, + document.getElementById("container"), + { + height: 400, + chrome: "nofooter", + tweetLimit: 2 + } +); +``` \ No newline at end of file diff --git a/x-for-websites/timelines/guides/profile-timeline.mdx b/x-for-websites/timelines/guides/profile-timeline.mdx new file mode 100644 index 000000000..18d151a0c --- /dev/null +++ b/x-for-websites/timelines/guides/profile-timeline.mdx @@ -0,0 +1,42 @@ +--- +title: Embedded profiles +sidebarTitle: Profile Timeline +--- + +A profile timeline displays the latest Posts ordered from newest to oldest from a specific public X account. + +## HTML markup + +A responsive profile timeline can be added to a webpage through a common HTML template. Generate markup for an embedded profile timeline on [publish.x.com](https://publish.x.com). + +A template example: + +```html + +``` + +```html + +``` + +## JavaScript factory function + +X's widget JavaScript library supports dynamic insertion of an embedded profile timeline using the `twttr.widgets.createTimeline` function. Pass a data source definition, target container element, and optional options object to insert an embedded timeline into your page. + +HTML `data-*` parameters are camelCased when passed as an options object property. + +```javascript +twttr.widgets.createTimeline( + { + sourceType: "profile", + screenName: "XDevelopers" + }, + document.getElementById("container") +); +``` \ No newline at end of file diff --git a/x-for-websites/timelines/overview.mdx b/x-for-websites/timelines/overview.mdx new file mode 100644 index 000000000..244167abf --- /dev/null +++ b/x-for-websites/timelines/overview.mdx @@ -0,0 +1,99 @@ +--- +title: Embedded Timelines +sidebarTitle: Overview +--- + +Embedded timelines are an easy way to embed Posts on your website in a compact, linear view. Choose between a profile timeline to get the latest Posts from a X account, or a List timeline containing a curated list of Posts from your favorite X accounts. + +An embedded timeline consists of two parts: including an embed code that links your webpage to the timeline on x.com, and the X for Websites JavaScript to transform the link into a fully-rendered timeline. + + +**Please note:** + +We retired the Likes, Collections, and Moments timelines on January 13, 2023. + +We recommend you use the [Profile](/x-for-websites/timelines/guides/profile-timeline) and [Lists](/x-for-websites/timelines/guides/list-timeline) timelines, which we’re updating to become faster, easier to use, and more up-to-date with X features and functionality. + +You can learn more about this change in our [announcement](https://devcommunity.x.com/t/removing-support-for-embedded-like-collection-and-moment-timelines/150313). + + +## Timeline types + +### Profile timeline + +A profile timeline displays the latest Posts from the specified (public) X account. + +### List timeline + +A list timeline displays the latest Posts from a curated, public list of X accounts. The timeline includes a header displaying the list’s name, description, and curator. To create lists on x.com or in the X app, learn more here. + +## How to add an embedded timeline to your website + +Visit [publish.x.com](https://publish.x.com) to generate embed codes for profiles and lists. + +## Customization + +### Dimensions + +An embedded timeline automatically adjusts to the width of its parent element with a minimum width of 180 pixels and a maximum width of 520 pixels. The grid display has a minimum width of 220 pixels. Set the maximum width or the maximum height of an embedded timeline by adding a data-width or data-height attribute to the embed code anchor element. + +```html + +``` + +### Custom chrome + +Control the frame around the linear timeline by setting a `data-chrome` attribute with space-separated tokens for each chrome component. + +| Token | Description | +|----------------|-----------------------------------------------------------------------------------------------------------------------| +| `noheader` | Hides the timeline header. Implementing sites must add their own X attribution, link to the source timeline, and comply with other X [display requirements](https://about.x.com/company/display-requirements). | +| `nofooter` | Hides the timeline footer and Post composer link, if included in the timeline widget type. | +| `noborders` | Removes all borders within the widget including borders surrounding the widget area and separating Posts. | +| `noscrollbar` | Crops and hides the main timeline scrollbar, if visible. Please consider that hiding standard user interface components can affect the accessibility of your website. | +| `transparent` | Removes the widget’s background color. | + +#### Example + +```html + +``` + +### Limiting the number of Posts displayed + +Display a specific number of items between 1 and 20 by customizing your embed HTML. + +Add a `data-tweet-limit` attribute to the embed code to specify a number of Posts. The timeline will automatically adjust its height to display a specified number of Posts. The timeline is fixed after display; it will not poll for new Posts until the page is refreshed. + +#### Example + +```html + +``` + +### Accessibility: Override ARIA live politeness + +An embedded timeline describes its content for screen readers and other assistive technologies using additional markup defined in [WAI-ARIA standards](http://www.w3.org/WAI/intro/aria.php). A timeline widget is a [live region of a page](http://www.w3.org/WAI/PF/aria-practices/#liveprops) which will receive updates when new Posts become available. + +By default, a timeline has a politeness value of `polite` by default; set a `data-aria-polite` attribute value of assertive to set the embedded timeline live region politeness to `assertive`, for example if you’re using the embedded Timeline as a primary source of live content in your page. + +```html + +``` \ No newline at end of file diff --git a/x-for-websites/web-intents/image-resources.mdx b/x-for-websites/web-intents/image-resources.mdx new file mode 100644 index 000000000..986542e39 --- /dev/null +++ b/x-for-websites/web-intents/image-resources.mdx @@ -0,0 +1,67 @@ +--- +title: Image Resources +sidebarTitle: Image Resources +--- + +The resources are available to support a consistent user experience in applications leveraging Twitter & sites using Web Intents. It is recommended that you store these within your own application. For more information on using these marks, consult our Display Requirements. + +## X Logo + +Download current X logo assets (including various sizes and colors) directly from the official [brand toolkit](https://about.x.com/en/who-we-are/brand-toolkit). + +## Tweet Actions + +A Tweet action represents a possible action on a Tweet with an “on” state if the viewer has already completed the action. + +All Tweet actions are displayed with a medium grey (#AAB8C2) fill in their default state. All Tweet actions should set 50% opacity when the action is being activated by the viewer (e.g. `:active` CSS pseudo). The action retains the previous fill color during its pressed / activation state, switching to the “on” state after the action becomes activated. + +Retweet and like actions appear in an “on” state if the current viewer has performed the action. A `retweeted` or `favorited` [Tweet object](/x-api/fundamentals/data-dictionary#post-tweet) property set to true communicates the expected “on” display state. + +A retweet action is turned off if the current viewer matches the author of the Tweet. A turned off action has 30% opacity. + +## Reply + +```xml + + + +``` + +The reply icon opacity should be adjusted during icon activation. A reply Tweet action does not have an “on” state: a new reply is always available. + +### Reply Tweet action expected colors and opacity +| Default | Pressed | +|----------------------------------|----------------------------------| +| `#AAB8C2` | 50% opacity | + +## Retweet + +```xml + + + +``` + +The retweet icon opacity should be adjusted during icon activation. The retweet icon should appear in the “on” state if the Tweet object response for the current viewer has a `retweeted` property set to `true`. An inactive Tweet action state may exist when the current viewer is the same as the Tweet author. + +### Retweet Tweet action expected colors and opacity + +| Default | Pressed | Inactive | On & Hover | +|----------------------------------|----------------------------------|----------------------------------|----------------------------------| +| `#AAB8C2` | 50% opacity | 30% opacity | `#19CF86` | + +## Like + +```xml + + + +``` + +The like icon opacity should be adjusted during icon activation. The like icon should appear in the “on” state if the Tweet object response for the current viewer has a `favorited` property set to `true`. + +#### Like Tweet action expected colors and opacity + +| Default | Pressed | On & Hover | +|----------------------------------|----------------------------------|----------------------------------| +| `#AAB8C2` | 50% opacity | `#E81C4F` | \ No newline at end of file diff --git a/x-for-websites/web-intents/overview.mdx b/x-for-websites/web-intents/overview.mdx new file mode 100644 index 000000000..3567f54c8 --- /dev/null +++ b/x-for-websites/web-intents/overview.mdx @@ -0,0 +1,150 @@ +--- +title: Web Intents +sidebarTitle: Overview +--- + +Web Intents provide flows for working with Tweets & X Users: Tweet, Reply, Retweet, Like, and Follow. They make it possible for users to interact with X content in the context of your site, without leaving the page or having to authorize a new app just for the interaction. Web intents are mobile web friendly, include native app handlers on iOS and Android when the X app is installed, and are super easy to implement. + +## Working with Web Intents + +Web Intents are the simplest way to let people Tweet or follow X accounts directly from your site. Web Intents automatically bring a viewer into the best logged-in experience to complete your specified action, including Tweet composers or X profile views inside X for iOS and X for Android apps. If a viewer does not have a X account they will have the opportunity to log in to X or create an account before completing the originally-specified action. Web Intents do not require setting up a X application, storing app credentials, or prompting a viewer for app permissions before posting. + +[The X for Websites JavaScript](/x-for-websites/javascript-api/guides/set-up-x-for-websites) will automatically fire appropriate [JavaScript events](/x-for-websites/javascript-api/guides/javascript-api) when included on a webpage. + +Web Intents cannot be loaded inside an iframe. A X author must view the full webpage before deciding to author a new Tweet or Tweet action pre-populated by your specified Web Intent or follow a specified X account. + +Images for icons for likes, replies, and reposts are all available on our [Image Resources](/x-for-websites/web-intents/image-resources) page. Consult our [Display Requirements](https://about.x.com/company/display-requirements) for tips on rendering Tweets and other X resources. + +If your audience speaks a language other than English, we recommend you use localized intents. + +### Get Started + +Web Intents can be invoked flexibly through a light combination of JavaScript and HTML and are meant to be opened in a new window. + +The easiest way to use intents is to include the X for Websites JavaScript on any web page you wish to invoke an intent. If you’ve already set up the [Tweet button](/x-for-websites/post-button/overview), you’re already prepared for Web Intents. + +When combined with standard anchor tags and [familiar iconography](https://dev.x.com/basics/image-resources) like the examples below, this JavaScript will automatically open a window of the appropriate size when clicked. You only need to load `platform.x.com/widgets.js` once. + +```html + +Reply +Retweet +Like +``` + +## List of Web Intents + +### Tweet or Reply to a Tweet + +- [https://x.com/intent/tweet](https://x.com/intent/tweet?in_reply_to=463440424141459456&related=xdevelopers) + + +View the [Tweet Web Intent documentation](/x-for-websites/post-button/overview) for more information about the Tweet intent. + +### Retweet a Tweet + +- [https://x.com/intent/retweet](https://x.com/intent/retweet?tweet_id=463440424141459456&related=xdevelopers,x,support) + +[Retweets](https://support.x.com/articles/77606-what-is-retweet-rt) are a powerful way to enable your users to share your content with their followers. + +#### Supported Parameters + +- `tweet_id` + +Every Tweet is identified by an ID. You can find this value from the API or by viewing the permalink page for any Tweet, usually accessible by clicking on the “published at” date of a tweet. + +### Like a Tweet + + +- [https://twitter.com/intent/like](https://x.com/intent/like?tweet_id=463440424141459456) + + +Users [like](https://support.x.com/articles/20169874) for a variety of reasons: when they love a Tweet, when they want to save it for later, or to offer a signal of thanks. The like intent allows you to provide this Tweet action and follow up with relevant suggested accounts for the user to follow. + +#### Supported Parameters + +- `tweet_id` + +Every Tweet is identified by an ID. You can find this value from the API or by viewing the permalink page for any Tweet, usually accessible by clicking on the timestamp displayed alongside a Tweet. + +### Mini-Profile + +- [https://x.com/intent/user](https://x.com/intent/user?screen_name=NASA) + +This Intent provides an unobtrusive way to link names of people, companies, and services to their X accounts. The new tab prominently features the account’s profile picture, bio, recent tweets and an easy-to-use Follow button. + +More image resources. + +#### Supported parameters + +- `screen_name` + +Every X user has a screen name, but they are subject to change. We recommend using user_id whenever possible. + +- `user_id` + +X User IDs are available from the API and uniquely identify a user. + +### Follow + + +- [https://twitter.com/intent/follow](https://x.com/intent/follow?screen_name=NASA) + +A [follow Web Intent](/x-for-websites/follow-button/guides/web-intent-follow-button) displays an inline sign in form for logged out users and follows the target X account on successful login. + + +## Localization + +You may pass a `lang` query parameter as part of any web intent to override the language display of a logged-in user or languages accepted by a browser. See [X for Websites languages](/x-for-websites/x-for-websites-supported-languages/overview) for a list of supported `lang` values. + +## Optimization + +### Limited Dependencies + +Some sites may prefer to embed the unobtrusive web intents JavaScript inline or without a dependency to `platform.twitter.com`. The snippet below will offer the equivalent functionality without the external dependency. + +```javascript +(function() { + if (window.__twitterIntentHandler) return; + var intentRegex = /twitter\.com\/intent\/(\w+)/, + windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes', + width = 550, + height = 420, + winHeight = screen.height, + winWidth = screen.width; + + function handleIntent(e) { + e = e || window.event; + var target = e.target || e.srcElement, + m, left, top; + + while (target && target.nodeName.toLowerCase() !== 'a') { + target = target.parentNode; + } + + if (target && target.nodeName.toLowerCase() === 'a' && target.href) { + m = target.href.match(intentRegex); + if (m) { + left = Math.round((winWidth / 2) - (width / 2)); + top = 0; + + if (winHeight > height) { + top = Math.round((winHeight / 2) - (height / 2)); + } + + window.open(target.href, 'intent', windowOptions + ',width=' + width + + ',height=' + height + ',left=' + left + ',top=' + top); + e.returnValue = false; + e.preventDefault && e.preventDefault(); + } + } + } + + if (document.addEventListener) { + document.addEventListener('click', handleIntent, false); + } else if (document.attachEvent) { + document.attachEvent('onclick', handleIntent); + } + window.__twitterIntentHandler = true; +}()); +``` \ No newline at end of file diff --git a/x-for-websites/webpage-properties.mdx b/x-for-websites/webpage-properties.mdx new file mode 100644 index 000000000..89ab6df27 --- /dev/null +++ b/x-for-websites/webpage-properties.mdx @@ -0,0 +1,99 @@ +--- +title: X Widgets Webpage Properties +sidebarTitle: Webpage properties +--- + +Set widget preferences across an entire website by including `` and `` elements in your pages page. + +Widget settings specified in a `` may be overridden at the individual widget level. Order of precedence: + +1. web intent link query parameter +2. widget attribute +3. meta or link element + +## Turn off DOM scan for widgets and buttons + +The X for Websites JavaScript scans `document.body` after initialization to locate buttons, widgets, and web intents for enhancement. Turn off this scan if X content is only loaded via [JavaScript factory functions](/x-for-websites/javascript-api/guides/set-up-twitter-for-websites) or if you prefer to directly [call load](/x-for-websites/javascript-api/guides/scripting-loading-and-initialization) on a smaller fragment of the page. + +```html + +``` + +## Canonical link + +The [Post button](/x-for-websites/post-button/overview) uses the [canonical link relation of the page](http://tools.ietf.org/html/rfc6596) expressed in a `` as the shared URL if the URL property is not set in the button markup. + +Add a canonical link in the `` section of your webpage. + +```html + +``` + +## Identify the X profile of the page + +Populate the `via` property of a Tweet button by linking to your X profile page with a me link relationship token. + +Set a `me` link relationship in the `` section of your webpage. + +``` + +``` + +You may also set a `me` relationship from an anchor element on your page, such as a Follow button. + +```html + +Follow @XDevelopers + +``` + +## Theme + +Override the default `light` theme preference for an [embedded Post](/x-for-websites/embedded-posts/overview) or an [embedded Timeline](/x-for-websites/timelines/overview). + +```html + +``` + +## Do not track parameter + +You may choose whether X widgets on your site help to personalize content and suggestions for X users, including ads. You can opt out of having information from your website used for personalization by following the instructions below. Include the following snippet within the `` and `` elements on your pages that include X for Websites widgets: + +```html + +``` + +You may also opt-out of this data use for a specific widget, and can do so by setting the optional `data-dnt` parameter to be true, as shown in the example below: + +```html + + +``` + +## Turn off functionality which may trigger Content Security Policy warnings + +An embedded Tweet or embedded Timeline may display with restricted capabilities when a [Content Security Policy](http://en.wikipedia.org/wiki/Content_Security_Policy) restricts inline loading of X. Set `csp=on` to turn off functionality which could display Content Security Policy warnings on your site. + +```html + +```