# Developer Guidelines Source: https://docs.x.com/developer-guidelines A practical guide to what's allowed and what's not when building with the X API. **Violations can result in app suspension, API access revocation, or permanent account bans.** Always review the official policies before building. Binding legal terms for API access Rules for building on X Specific rules for bots Prohibited activities *** ## Quick Check: Is My App Allowed? Before building, ask yourself these questions. If you answer **"no"** to any of them, your app likely violates X's policies. For interactions, did the user **explicitly request** it? Is your app's purpose and behavior **clear to users**? (Automated accounts must be labeled.) Can users **easily opt out** of any ongoing interactions? Does it provide **real value** beyond self-promotion? Are you **only using the official API** (not scraping/browser automation)? Are you **within rate limits** and respecting usage policies? When in doubt, ask: "Would a user be happy with this experience?" If not, reconsider your approach. *** ## Common Scenarios: Allowed or Not? Real-world examples to help you understand what's permitted. **These rules apply to all apps**—whether you're building a bot, mobile app, web integration, browser extension, analytics dashboard, or any other tool that uses the X API. | Scenario | Allowed? | Why | | --------------------------------------------------------------------- | :--------------------------: | -------------------------------------------------- | | Automated account posts scheduled content (news, weather, quotes) | | Informational, no unsolicited mentions | | App posts RSS feed updates on behalf of user | | Helpful broadcasting | | Alert service posts earthquake/disaster notifications | | Public safety value | | Sports app posts game updates to user's timeline | | Informational | | App posts stock/crypto prices on schedule | | Informational, no manipulation | | App posts identical content across multiple accounts | | Spam / platform manipulation | | App posts to trending topics to gain visibility | | Trend manipulation | | Multiple city-specific alert accounts (e.g., @WeatherNYC, @WeatherLA) | | Allowed—non-duplicative, location-specific content | | Scenario | Allowed? | Why | | ------------------------------------------------------ | :----------------------------------: | --------------------------------------------------------------------------- | | App responds to @mentions asking for help | | User-initiated request | | App auto-replies to anyone mentioning a keyword | | Unsolicited interaction | | App auto-replies to users who reply to your post | | User engaged first—limit 1 reply. [Conditions apply](#gray-areas-explained) | | AI-powered app generates and posts replies | | Requires **prior approval** from X | | App replies with "follow me for more!" to random users | | Spam, unsolicited | | Utility app that unrolls threads when mentioned | | User-initiated utility | | Scenario | Allowed? | Why | | --------------------------------------------------------- | :--------------------------: | ----------------------------------------------------------------------- | | App responds to DMs with helpful info | | User-initiated | | App sends affiliate links when user DMs first | | User-initiated—must disclose. [Conditions apply](#gray-areas-explained) | | App auto-DMs new followers with welcome message | | Unsolicited, even to followers | | App bulk-DMs users about a product launch | | Spam | | Support integration asks "How can I help?" after user DMs | | User-initiated conversation | | Scenario | Allowed? | Why | | ----------------------------------------------------- | :--------------------------: | -------------------------------------------------------------------------------------- | | Third-party app lets user tap a like button on a post | | User-initiated through the app | | App auto-likes posts containing a hashtag | | Automated, not user-initiated | | Mobile app has "auto-like" feature for selected users | | Automated, not user-initiated | | Service sells likes or offers "bulk like" packages | | Selling engagement is prohibited | | App reposts content from a curated list | | OK for informational purposes, no bulk spam. [Conditions apply](#gray-areas-explained) | | Growth tool bulk-follows accounts to grow audience | | Manipulation | | App follows back anyone who follows it | | Bulk/aggressive following | | App adds users to lists in bulk | | Indiscriminate list manipulation | **Likes must be directly initiated by the authenticated user.** Automated, bulk, or indiscriminate liking — including auto-liking by keyword, hashtag, user, or schedule — is prohibited. Apps may not offer "auto-like" features or sell likes as a service. This applies to all apps—bots, mobile apps, browser extensions, or any integration. | Scenario | Allowed? | Why | | ---------------------------------------------------- | :----------------------------------: | ------------------------------------------------------------------------------------------- | | App sends product recommendations when asked | | User-initiated | | App replies to random posts with affiliate links | | Unsolicited spam | | Giveaway app that requires follows/retweets to enter | | Risky—can be seen as engagement manipulation | | Service selling likes/follows/retweets | | Strictly prohibited | | Tip service that sends crypto when user requests | | User-initiated—comply with financial regulations. [Conditions apply](#gray-areas-explained) | | Scenario | Allowed? | Why | | ------------------------------------------------- | :--------------------------: | --------------------------------- | | App tracks brand mentions for analytics dashboard | | Valid use case | | App scrapes X via browser automation (not API) | | **Permanent suspension**—API only | | App stores X data to train AI/ML models | | Prohibited (except Grok) | | App redistributes >1.5M posts in 30 days | | Exceeds redistribution limits | | App benchmarks X performance vs competitors | | Prohibited competitive analysis | | Academic research on public conversation trends | | Valid with proper data handling | **Non-API automation (scraping, browser automation) results in permanent suspension.** Always use the official X API. *** ## Prohibited Activities These activities will get your app suspended or permanently banned. There are no exceptions. | Category | Examples | | ----------------------------- | ------------------------------------------------------------------------------------ | | **Spam & Manipulation** | Identical content across accounts, fake engagement, trend manipulation, bulk posting | | **Unsolicited Outreach** | Auto-replies to random users, bulk DMs, uninvited @mentions | | **Deceptive Bots** | Impersonating humans, hiding bot identity, misleading links/redirects | | **Engagement Selling** | Apps that sell likes, follows, retweets, or views | | **Rate Limit Abuse** | Exceeding limits, designing apps that encourage overuse | | **Non-API Automation** | Browser scripting, scraping, any automation outside official API | | **Account Farms** | Multiple accounts for same duplicative purpose | | **Surveillance** | Profiling, tracking, or monitoring users without consent | | **Unauthorized AI Training** | Using X data to train ML models (Grok excepted) | | **Sensitive Data Derivation** | Inferring health, political, religious, or other sensitive attributes | | **Excessive Redistribution** | Sharing >1.5M Post IDs per 30-day period | *** ## Automation Rules This section applies specifically to **automated accounts** (bots) that post, reply, or interact on behalf of users. If you're building an analytics dashboard, research tool, or other non-automated app, these labeling requirements don't apply to you—but the technical restrictions still do. ### Requirements for Automated Accounts All automated accounts using the X API must meet these requirements: This label appears under your bot's name/handle on its profile. Enable it in your app settings to ensure transparency. State clearly that it's a bot and who operates it. Example: *"Bot by @yourcompany"* or *"Automated account managed by Example Inc."* For accountability and contact purposes, your bot must be associated with a human-managed account. If a user says "stop," stop. Implement keyword detection for common opt-out phrases. No scraping, browser automation, or unofficial methods. Violations result in permanent suspension. Don't try to circumvent or abuse rate limits. Design your app to handle limits gracefully. ### Automated Actions: What's Allowed? | Action | Allowed? | Rules | | ------------------- | :----------------------------------: | -------------------------------------------------------------------------------------------- | | **Post tweets** | | No unsolicited @mentions. No identical cross-posting. | | **Reply to users** | | Only if user engaged first. Max **1 reply per interaction**. | | **Send DMs** | | Only after user DMs you first. Easy opt-out required. | | **Like posts** | | Must be directly user-initiated. Auto-liking, bulk liking, and selling likes are prohibited. | | **Repost** | | OK for informational/entertainment. No bulk spam. | | **Quote tweet** | | Same rules as repost—no spam or manipulation. | | **Follow/Unfollow** | | No bulk, aggressive, or automated following. | | **Add to Lists** | | No bulk or indiscriminate additions. | | **Bookmark** | | Fine for personal/automated use. | | **Search/Read** | | Standard use within rate limits. | *** ## Gray Areas Explained Many developers have questions about edge cases. Here's guidance on common gray areas. **Allowed if:** * User explicitly requests it (e.g., DMs asking for a recommendation) * You clearly disclose the affiliate/sponsored relationship * Links are not misleading (no deceptive redirects) **Not allowed if:** * You auto-reply to random posts with affiliate links * You DM users who didn't ask * You hide the commercial relationship * **Requires prior approval from X** before deployment * Must still follow all rules (no unsolicited mentions, properly labeled) * Contact X via the [Policy Support form](https://help.x.com/forms/platform) before launching * Even with approval, cannot impersonate humans Deploying AI-generated replies without approval is a violation, even if the content itself is helpful. **Not allowed** as automated DMs—this counts as unsolicited contact, even though they followed you. **Alternatives:** * Pinned tweet welcoming new followers * Bio with intro info and links * Auto-reply only if they DM you first **Allowed if:** * Each account serves **non-duplicative** purposes (e.g., @EarthquakeJP, @EarthquakeCA) * Content is meaningfully different (location-specific, language-specific) * Not used to bypass limits or amplify the same message **Not allowed if:** * Posting identical/similar content across accounts * Created to evade suspensions or rate limits **Allowed if:** * User initiates (mentions you, DMs you, or explicitly opts in) * Clear opt-out mechanism exists * Responses are helpful, not promotional * Includes privacy policy link in DMs **Not allowed if:** * You reach out to users who complained publicly (unsolicited) * Responses are primarily promotional **Proceed with caution:** * Requiring follows/retweets as entry can be seen as engagement manipulation * Must comply with [X's contest guidelines](https://help.x.com/en/rules-and-policies/x-contest-rules) * Don't use multiple accounts to amplify * Ensure prizes are real and delivered Consider entry methods that don't require engagement actions, like replying with a specific phrase. *** ## Data Handling & Display Requirements These requirements are legally binding under the Developer Agreement. Non-compliance can result in termination and legal action. ### Content Deletion You must delete X Content from your systems when requested: | Trigger | Deadline | | --------------------------------- | --------------------------------------------- | | X requests deletion | **24 hours** | | User requests deletion | **24 hours** | | Content is suspended/removed on X | **24 hours** | | Your API access is terminated | **10 business days** (must delete all X data) | Use [Compliance Firehose](https://docs.x.com/x-api/compliance/streams) to receive real-time deletion events and stay compliant automatically. ### Off-X Matching **Off-X matching** means associating X data (username, user ID, posts) with off-platform identifiers (your customer database, email lists, device IDs, etc.). **Allowed with express opt-in consent:** * User explicitly agrees to link their X account with your service * Clear disclosure of what data will be matched and why **Without consent, you may only match:** * Information the user directly provided to you * Publicly available X data (posts, bio, display name, username) * Public resources like professional directories **Never match if it would surprise the user.** ### Sensitive Data You **cannot** derive, infer, or store information about X users in these categories: | Category | Examples | | ------------------------------- | -------------------------------------------- | | **Health** | Medical conditions, pregnancy, disabilities | | **Financial status** | Negative financial condition, credit issues | | **Political** | Party affiliation, political beliefs, voting | | **Racial/Ethnic** | Origin, ethnicity | | **Religious/Philosophical** | Beliefs, affiliations | | **Sex life/Sexual orientation** | Any inference about sexuality | | **Trade union** | Membership or affiliation | | **Criminal** | Alleged or actual criminal activity | **Exception:** Aggregate analysis without storing personal identifiers (no user IDs, usernames, or linkable data) may be allowed for research purposes, subject to applicable laws. ### Displaying X Content | Requirement | Details | | -------------------- | -------------------------------------------------------------------------------------------------------- | | **Attribution** | Use proper X branding. Follow [Brand Guidelines](https://about.x.com/en/who-we-are/brand-toolkit). | | **No alterations** | Only modify for display formatting (resizing). Don't edit content, remove timestamps, or strip metadata. | | **No iframes** | Don't display X Content in iframes. Use official embeds or render directly. | | **Respect removals** | Remove content within 24 hours if deleted on X. | *** ## Technical Restrictions These limits apply to all developers. Exceeding them can result in rate limiting or suspension. | Restriction | Limit | | ----------------------------------- | ------------------------------------------------------------------------- | | **Post ID redistribution** | Max 1.5M Post IDs per 30-day period to any single entity | | **Hydrated content redistribution** | Max 50,000 hydrated Posts or Users per recipient per day | | **Rate limits** | Vary by endpoint and tier—[see API docs](/x-api/fundamentals/rate-limits) | | **AI/ML training** | Prohibited (except for Grok) | | **Non-API access** | Prohibited—scraping and browser automation = permanent ban | | **Competitive benchmarking** | Prohibited—can't measure X performance vs. competitors | | **Multiple apps for same use case** | Prohibited—don't create duplicate apps to bypass limits | ### Special Use Cases | Use Case | Requirement | | ------------------------------------ | ---------------------------------------------------------------- | | **Government use** | Requires Enterprise tier | | **Commercial use** | Requires appropriate paid tier; free tier is non-commercial only | | **Academic research** | May have different redistribution limits; contact X for details | | **EU Digital Services Act research** | Specific non-commercial research provisions available | *** ## Security & Compliance Your obligations as a developer: * Use **industry-standard security** practices to protect X data * Never share your API credentials or tokens * Store credentials securely (environment variables, secret managers—not in code) * Implement proper authentication in your apps If you experience a security breach involving X data: * **Notify X immediately** * Take steps to mitigate the breach * Cooperate with X's investigation * Treat any non-public information from X as confidential * Don't disclose API rate limits, internal X data, or non-public features * Don't use confidential info for competitive purposes * X may audit your compliance **up to once per year** * You must provide reasonable access and documentation * Keep records of how you use X data *** ## Summary: Do's and Don'ts **For Automated Accounts:** * Enable "Automated" profile label * Disclose operator in bio * Wait for users to initiate interaction * Provide easy opt-out * Get approval for AI-generated replies **For All Apps:** * Use only the official X API * Respect rate limits and redistribution limits * Delete content within 24 hours when requested * Get opt-in consent for off-X matching * Use proper attribution when displaying X Content * Secure your credentials and notify X of breaches * Keep records of your X data usage **For Automated Accounts:** * Hide automated nature * Send unsolicited DMs, replies, or @mentions * Ignore "stop" requests * Post identical content across accounts * Auto-like, bulk-like, or sell likes (likes must be user-initiated) **For All Apps:** * Scrape or use browser automation * Train AI/ML models on X data (except Grok) * Derive sensitive user data (health, politics, religion, etc.) * Match X data to off-platform IDs without consent * Display X Content in iframes * Redistribute more than limits allow * Create multiple apps for the same use case * Use X data for surveillance or user tracking # Policies and agreements Source: https://docs.x.com/developer-terms ## Overview Developer use of X materials and content is subject to and governed by our Developer Policy and agreements.
# Getting Enterprise Access Source: https://docs.x.com/enterprise-api/getting-started/getting-access Apply for Enterprise API access and get onboarded Enterprise access requires an application and onboarding process. Your dedicated account team will help you get set up with credentials and configure your access. *** ## Step 1: Apply for Enterprise access [Fill out the Enterprise interest form](/forms/enterprise-api-interest) with details about your organization and use case. Our sales team will reach out to understand your data volume, endpoint requirements, and support needs. Receive a tailored plan with pricing, rate limits, and access levels designed for your use case. *** ## Step 2: Get onboarded Once your Enterprise plan is in place, your dedicated account manager will help you set up: Set up your app in the [Developer Console](https://console.x.com) with Enterprise-level access. Your account manager will help configure rate limits, endpoint access, and any custom settings. Generate your API keys and tokens for authentication. *** ## Step 3: Save your credentials You'll receive several credentials depending on your authentication needs: | Credential | Purpose | | :------------------------ | :----------------------------------------------------------------------- | | **API Key & Secret** | Identify your app. Used to generate tokens and sign OAuth 1.0a requests. | | **Bearer Token** | App-only authentication for reading public data. | | **Access Token & Secret** | Make requests on behalf of your own account (OAuth 1.0a). | | **Client ID & Secret** | OAuth 2.0 authentication for user-context requests. | **Save immediately.** Credentials are only displayed once. Store them in a password manager or secure vault. If you lose them, you'll need to regenerate (which invalidates the old ones). *** ## Which credentials do you need? Use the **Bearer Token** for simple, read-only access to public data. ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Best for: Searching posts, looking up users, reading trends. Use **OAuth 2.0** (recommended) or **OAuth 1.0a** to act on behalf of users. OAuth 2.0 offers fine-grained scopes—request only the permissions you need. Best for: Posting, liking, following, accessing DMs. [OAuth 2.0 guide →](/resources/fundamentals/authentication/oauth-2-0/overview) Use your **Access Token & Secret** to make requests as your own account. These tokens represent the account that owns the app. Best for: Testing, personal bots, your own automation. *** ## Credential security best practices Never hardcode credentials in your source code. Add credential files to `.gitignore`. Regenerate credentials periodically as a security measure. Only request the OAuth permissions your app needs. *** ## Next steps Call the API with your new credentials. Understand OAuth 1.0a and OAuth 2.0. # Enterprise Pricing Source: https://docs.x.com/enterprise-api/getting-started/pricing Custom Enterprise pricing for the X API Enterprise plans are custom-tailored to your organization's needs. Work with a dedicated account team to build a package that matches your data volume, endpoint usage, and support requirements. *** ## How Enterprise pricing works Pricing is tailored to your specific data volume, endpoint needs, and throughput requirements. Predictable costs with longer-term agreements and committed usage levels. Custom or unlimited post read volumes, far beyond the 2 million monthly cap on pay-per-use plans. A named account manager and priority technical support are included with every Enterprise plan. *** ## What's included Every Enterprise plan includes access to the full X API plus exclusive Enterprise endpoints: | Feature | Details | | :------------------------- | :-------------------------------------------- | | **All standard endpoints** | Everything available in the pay-per-use X API | | **Volume streams** | Full firehose and language-specific streams | | **Likes streams** | Full and sampled likes in real-time | | **Powerstream** | Advanced filtered streaming | | **Engagement metrics** | Post and media analytics endpoints | | **Account Activity** | Real-time user event subscriptions | | **Stream webhooks** | Filtered stream delivery via webhooks | | **Custom rate limits** | Elevated limits tailored to your needs | | **Dedicated support** | Named account manager and priority resolution | *** ## Enterprise vs. pay-per-use | | Pay-per-use | Enterprise | | :--------------------- | :-------------------------- | :--------------------------------- | | **Pricing model** | Credit-based, pay as you go | Custom contract | | **Monthly post cap** | 2 million reads | Custom / unlimited | | **Volume streams** | Not available | Full firehose and language streams | | **Likes streams** | Not available | Full and sampled likes | | **Engagement metrics** | Not available | Post and media analytics | | **Rate limits** | Standard | Custom / elevated | | **Support** | Community forum | Dedicated account manager | | **Commitment** | None | Contract-based | *** ## Monitoring usage Track your API usage programmatically with the [Usage endpoint](/x-api/usage/introduction): ```bash theme={null} curl "https://api.x.com/2/usage/tweets" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` This returns daily Post consumption counts, helping you: * Track consumption against your contract limits * Generate usage reports for stakeholders * Identify high-consumption endpoints * Plan capacity with your account team *** ## Get started [Apply for Enterprise access](/forms/enterprise-api-interest) and tell us about your use case and data needs. Our sales team will work with you to build a package that fits your requirements and budget. Your dedicated account manager will help you set up credentials, configure access, and start building. *** ## Next steps Contact our sales team to get started. Monitor usage programmatically. # Enterprise API Source: https://docs.x.com/enterprise-api/introduction Enterprise-grade access to X's full firehose, volume streams, and dedicated support The X API Enterprise plan provides the highest level of access to X data. Get complete firehose coverage, volume streams, dedicated account management, and custom rate limits designed for organizations that depend on X data at scale. Apply for Enterprise access with a dedicated account team. Explore all available endpoints, including Enterprise-exclusive ones. Official Python and TypeScript libraries. *** ## Why Enterprise? Enterprise access includes everything in the pay-per-use X API plus exclusive high-volume endpoints, dedicated support, and custom packages tailored to your needs. Stream 100% of public posts in real-time. No sampling, no limits. Get every post as it happens. Access full-volume and language-specific streams, including English, Japanese, Korean, and Portuguese firehoses. Get a dedicated account manager, personalized technical support, and priority issue resolution. Higher rate limits and custom-tailored packages to match your throughput requirements. Access post and media analytics endpoints for deep engagement insights across large datasets. Stay compliant with real-time compliance event streams for posts, users, and likes. *** ## Enterprise-exclusive endpoints These endpoints are only available on Enterprise plans: Full firehose, language-specific streams, and sampled streams. Stream all likes or sampled likes in real-time. High-performance filtered streaming with advanced operators. Deep analytics for post and media engagement. Subscribe to real-time user activity events including posts, DMs, likes, and follows. Receive filtered stream data via webhooks instead of persistent connections. *** ## What you can build Enterprise access powers the most demanding use cases on X. Search, retrieve, and publish posts. Access timelines, threads, and quote posts. Look up users, manage follows, blocks, and mutes. Find live audio conversations and their participants. Send and receive private messages. Create and manage curated lists of accounts. Access trending topics by location. *** ## Key features ### Complete real-time coverage Stream 100% of public posts as they happen. No sampling, no gaps. Enterprise firehose access gives you the complete picture of public conversation on X. Available streams: * **All posts** - Every public post in real-time * **English posts** - All English-language posts * **Japanese posts** - All Japanese-language posts * **Korean posts** - All Korean-language posts * **Portuguese posts** - All Portuguese-language posts * **Sampled streams** - 1% and 10% random samples [Learn more about volume streams](/x-api/posts/volume-streams/introduction) ### Rich data objects Access detailed, structured data for posts, users, media, and more: * **Posts**: Full text, metrics, entities, annotations, conversation threads * **Users**: Profiles, follower counts, verification status * **Media**: Images, videos, GIFs with metadata * **Polls**: Options and vote counts Customize responses with [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) to get exactly the data you need. ### Filtered stream Get posts delivered in real-time as they're published. Define up to 1,000 filtering rules to receive only matching posts. ```bash theme={null} # Add a rule curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Authorization: Bearer $TOKEN" \ -d '{"add": [{"value": "from:xdevelopers"}]}' # Connect to stream curl "https://api.x.com/2/tweets/search/stream" \ -H "Authorization: Bearer $TOKEN" ``` [Learn more about filtered stream](/x-api/posts/filtered-stream/introduction) ### Full-archive search Search the complete history of public posts back to 2006. Build queries with operators for users, keywords, dates, and more. ```bash theme={null} curl "https://api.x.com/2/tweets/search/all?query=AI%20lang:en" \ -H "Authorization: Bearer $TOKEN" ``` ### Engagement metrics Access deep engagement analytics including impressions, likes, reposts, replies, video views, and media-level metrics. [Learn more about search](/x-api/posts/search/introduction) *** ## Enterprise vs. pay-per-use | Feature | Pay-per-use | Enterprise | | :--------------------- | :---------------------- | :--------------------------------- | | **Post search** | Recent and full-archive | Recent and full-archive | | **Filtered stream** | Up to 1,000 rules | 5,000+ rules | | **Volume streams** | - | Full firehose and language streams | | **Likes streams** | - | Full and sampled likes | | **Powerstream** | - | Advanced filtered streaming | | **Engagement metrics** | - | Post and media analytics | | **Account Activity** | - | Real-time user event subscriptions | | **Monthly post cap** | 2 million reads | Custom / unlimited | | **Rate limits** | Standard | Custom / elevated | | **Support** | Community forum | Dedicated account manager | *** ## Get started [Contact our sales team](/forms/enterprise-api-interest) to discuss your needs and get a custom package. Your dedicated account manager will help you set up credentials and configure your access. Use the same modern v2 API endpoints plus Enterprise-exclusive endpoints for your integration. ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Tools & libraries Official Python library with async support. Official TypeScript/JavaScript library. Interactive API explorer. [Browse all libraries](/tools-and-libraries) *** ## Support Enterprise customers get a dedicated point of contact for technical and account support. Get help from the community and X team. # Enterprise data customers Source: https://docs.x.com/enterprise/customer-directory ## Discover companies that use X data to help power innovation. Our enterprise data customers receive commercial-level access to APIs and dedicated account and developer support. Apply for enterprise API access to get the highest level of access and reliability. [Apply for enterprise access →](/resources/enterprise/forms/enterprise-api-interest) ## X Official Partners Work with a trusted X Official Partner to expand what's possible for your business. Each Official Partner has been selected for the program after an extensive evaluation, and represents excellence, value, and trust. [Check our partners →](https://partners.x.com/en) ## Enterprise customers listing # X Official Partner Source: https://docs.x.com/enterprise/partner-directory ## Build your business with X Official Partners ###### Tap into the public conversation on X and turn insights into action with solutions from our partners.
## Work with a trusted X Official Partner to expand what's possible for your business Our partners are vetted for excellence and can provide technology to help you: * Understand consumer trends and preferences  * Collect and analyze product and service feedback  * Engage with customers and resolve issues  * Be alerted to breaking news and events  * Create, publish, and analyze content across social channels  * And more! ## Discover the right Official Partner for your business
### Official Partners represent excellence, value, and trust Each Official Partner has been selected for the program after an extensive evaluation. Our partners are continuously reviewed by X, as this invitation-only program holds its members to the highest performance standards, in order to deliver great experiences for brands. ## See our partners' impressive work # Billing support form Source: https://docs.x.com/forms/billing-support Get billing support for Basic, Pro, and Enterprise API subscriptions.
# Enterprise Access Form Source: https://docs.x.com/forms/enterprise-api-interest # Government End User Request Form Source: https://docs.x.com/forms/government-end-user-request # OAuth API reference index Source: https://docs.x.com/fundamentals/authentication/api-reference ### OAuth 1.0a | | | | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- | | **Purpose** | Method | | Step 1 of the 3-legged OAuth flow and Sign in with X
Allows a Consumer application to obtain an OAuth Request Token to request user authorization. | [POST oauth/request\_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) | | Step 2 of the 3-legged OAuth flow and Sign in with X
Allows a Consumer application to use an OAuth Request Tokento request user authorization. | [GET oauth/authenticate](/resources/fundamentals/authentication/api-reference#get-oauth-authenticate) | | Step 2 of the 3-legged OAuth flow and Sign in with X
Allows a Consumer application to use an OAuth Request Token to request user authorization. | [GET oauth/authorize](/resources/fundamentals/authentication/api-reference#get-oauth-authorize) | | Step 3 of the 3-legged OAuth flow and Sign in with X
Allows a Consumer application to exchange the OAuth Request Token for an OAuth Access Token. | [POST oauth/access\_token](/resources/fundamentals/authentication/api-reference#post-oauth-access-token) | | Allows a registered application to revoke an issued OAuth Access Token. | [POST oauth/invalidate\_token](/resources/fundamentals/authentication/api-reference#post-oauth-invalidate-token) | ### OAuth 2.0 Bearer Token | | | | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | | **Purpose** | Method | | Allows a registered App to generate an OAuth 2 app-only Bearer Token, which can be used to make API requests on an App's behalf, without user context. | [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token) | | Allows a registered App to revoke an issued OAuth 2 app-only Bearer Token. | [POST oauth2/invalidate\_token](/resources/fundamentals/authentication/api-reference#post-oauth2-invalidate-token) | ### POST oauth/request\_token Allows a Consumer application to obtain an OAuth Request Token to request user authorization. This method fulfills [Section 6.1](https://oauth.net/core/1.0/#auth_step1) of the [OAuth 1.0 authentication flow](http://oauth.net/core/1.0/#anchor9). **We require you use HTTPS for all OAuth authorization steps.** **Usage Note:** Only ASCII values are accepted for the `oauth_nonce` **Resource URL[](#resource-url "Permalink to this headline")** `https://api.x.com/oauth/request_token` **Resource Information[](#resource-information "Permalink to this headline")** | | | | :----------------------- | :--- | | Response formats | JSON | | Requires authentication? | No | | Rate limited? | Yes | **Parameters[](#parameters "Permalink to this headline")** | Name | Required | Description | Example | | :-------------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------- | | oauth\_callback | required | For OAuth 1.0a compliance this parameter is **required** . The value you specify here will be used as the URL a user is redirected to should they approve your application's access to their account. Set this to `oob` for out-of-band pin mode. This is also how you specify custom callbacks for use in desktop/mobile applications. Always send an `oauth_callback` on this step, regardless of a pre-registered callback.

We require that any callback URL used with this endpoint will have to be configured within the App’s settings on developer.x.com\* | `http://themattharris.local/auth.php` `twitterclient://callback` | | x\_auth\_access\_type | optional | Overrides the access level an application requests to a users account. Supported values are `read` or `write` . This parameter is intended to allow a developer to register a read/write application but also request read only access when appropriate. | | Learn more about how to approve your callback URLs on [this page](/resources/fundamentals/developer-apps#callback-urls). **Please note** - You can view and edit your existing [X apps](/resources/fundamentals/developer-apps) via the [X app dashboard](https://developer.x.com/en/apps) if you are logged into your X account on developer.x.com. **Example request[](#example-request "Permalink to this headline")** Request URL: `POST https://api.x.com/oauth/request_token` Request POST Body: *N/A* Authorization Header: `OAuth oauth_nonce="K7ny27JTpKVsTgdyLdDfmQQWVLERj2zAK5BslRsqyw", oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1300228849", oauth_consumer_key="OqEqJeafRSF11jBMStrZz", oauth_signature="Pc%2BMLdv028fxCErFyi8KXFM%2BddU%3D", oauth_version="1.0"` Response: `oauth_token=Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik&oauth_token_secret=Kd75W4OQfb2oJTV0vzGzeXftVAwgMnEK9MumzYcM&oauth_callback_confirmed=true` ### GET oauth/authorize Allows a Consumer application to use an OAuth Request Token to request user authorization. This method fulfills [Section 6.2](http://oauth.net/core/1.0/#auth_step2) of the [OAuth 1.0 authentication flow](http://oauth.net/core/1.0/#anchor9). Desktop applications must use this method (and cannot use [GET oauth / authenticate](/resources/fundamentals/authentication/api-reference#get-oauth-authenticate)). **Usage Note:** An `oauth_callback` is never sent to this method, provide it to [POST oauth / request\_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) instead. **Resource URL[](#resource-url "Permalink to this headline")** `https://api.x.com/oauth/authorize` **Resource Information[](#resource-information "Permalink to this headline")** | | | | :----------------------- | :--- | | Response formats | JSON | | Requires authentication? | Yes | | Rate limited? | Yes | **Parameters[](#parameters "Permalink to this headline")** | | | | | | | :----------- | :------- | :-------------------------------------------------------------------------------------------- | :------------ | :------ | | Name | Required | Description | Default Value | Example | | force\_login | optional | Forces the user to enter their credentials to ensure the correct users account is authorized. | | | | screen\_name | optional | Prefills the username input box of the OAuth login screen with the given value. | | | **Example request[](#example-request "Permalink to this headline")** Send the user to the `oauth/authorize` step in a web browser, including an oauth\_token parameter: `https://api.x.com/oauth/authorize?oauth_token=Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik` ### GET oauth/authenticate Allows a Consumer application to use an OAuth `request_token` to request user authorization. This method is a replacement of [Section 6.2](http://oauth.net/core/1.0/#auth_step2) of the [OAuth 1.0 authentication flow](http://oauth.net/core/1.0/#anchor9) for applications using the callback authentication flow. The method will use the currently logged in user as the account for access authorization unless the `force_login` parameter is set to `true`. This method differs from [GET oauth / authorize](/resources/fundamentals/authentication/api-reference#get-oauth-authorize) in that if the user has already granted the application permission, the redirect will occur without the user having to re-approve the application. To realize this behavior, you must enable the *Use Sign in with X* setting on your [application record](https://developer.x.com/apps). **Resource URL[](#resource-url "Permalink to this headline")** `https://api.x.com/oauth/authenticate` **Resource Information[](#resource-information "Permalink to this headline")** | | | | :----------------------- | :--- | | Response formats | JSON | | Requires authentication? | Yes | | Rate limited? | Yes | **Parameters[](#parameters "Permalink to this headline")** | | | | | | | :----------- | :------- | :-------------------------------------------------------------------------------------------- | :------------ | :------ | | Name | Required | Description | Default Value | Example | | force\_login | optional | Forces the user to enter their credentials to ensure the correct users account is authorized. | | *true* | | screen\_name | optional | Prefills the username input box of the OAuth login screen with the given value. | | | **Example request[](#example-request "Permalink to this headline")** Send the user to the `oauth/authenticate` step in a web browser, including an oauth\_token parameter: `https://api.x.com/oauth/authenticate?oauth_token=Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik` ### POST oauth/access\_token Allows a Consumer application to exchange the OAuth Request Token for an OAuth Access Token. This method fulfills [Section 6.3](http://oauth.net/core/1.0/#auth_step3) of the [OAuth 1.0 authentication flow](http://oauth.net/core/1.0/#anchor9). **Resource URL[](#resource-url "Permalink to this headline")** `https://api.x.com/oauth/access_token` **Resource Information[](#resource-information "Permalink to this headline")** | | | | :----------------------- | :--- | | Response formats | JSON | | Requires authentication? | Yes | | Rate limited? | Yes | **Parameters[](#parameters "Permalink to this headline")** | | | | | | | :-------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------ | :------ | | Name | Required | Description | Default Value | Example | | oauth\_token | required | The oauth\_token here must be the same as the oauth\_token returned in the request\_token step. | | | | oauth\_verifier | required | If using the OAuth web-flow, set this parameter to the value of the *oauth\_verifier* returned in the callback URL. If you are using out-of-band OAuth, set this value to the pin-code. For OAuth 1.0a compliance this parameter is **required**. OAuth 1.0a is strictly enforced and applications not using the *oauth\_verifier* will fail to complete the OAuth flow. | | | **Example request[](#example-request "Permalink to this headline")** `POST https://api.x.com/oauth/access_token?oauth_token=qLBVyoAAAAAAx72QAAATZxQWU6P&oauth_verifier=ghLM8lYmAxDbaqL912RZSRjCCEXKDIzx` From PIN-based `POST https://api.x.com/oauth/access_token?oauth_token=9Npq8AAAAAAAx72QBRABZ4DAfY9&oauth_verifier=4868795` **Example response[](#example-response "Permalink to this headline")** `oauth_token=6253282-eWudHldSbIaelX7swmsiHImEL4KinwaGloHANdrY&oauth_token_secret=2EEfA6BG5ly3sR3XjE0IBSnlQu4ZrUzPiYTmrkVU&user_id=6253282&screen_name=xapi` ### POST oauth/invalidate\_token Allows a registered application to revoke an issued OAuth access\_token by presenting its client credentials. Once an access\_token has been invalidated, new creation attempts will yield a different Access Token and usage of the invalidated token will no longer be allowed. **Resource URL[](#resource-url "Permalink to this headline")** `https://api.x.com/1.1/oauth/invalidate_token` **Resource Information[](#resource-information "Permalink to this headline")** | | | | :----------------------- | :-------------------------------------------------------------------------- | | Response formats | JSON | | Requires authentication? | Yes - User context with the access tokens that you would like to invalidate | | Rate limited? | Yes | **Example request[](#example-request "Permalink to this headline")** ```bash theme={null} curl --request POST --url 'https://api.x.com/1.1/oauth/invalidate_token.json' --header 'authorization: OAuth oauth_consumer_key="CLIENT_KEY", oauth_nonce="AUTO_GENERATED_NONCE", oauth_signature="AUTO_GENERATED_SIGNATURE", oauth_signature_method="HMAC-SHA1", oauth_timestamp="AUTO_GENERATED_TIMESTAMP", oauth_token="ACCESS_TOKEN", oauth_version="1.0"' ``` **Example response[](#example-response "Permalink to this headline")** ```bash theme={null} HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Content-Length: 127 ... {"access_token":"ACCESS_TOKEN"} ``` **Example error response after token has been invalidated[](#example-error-response-after-token-has-been-invalidated "Permalink to this headline")** ```bash theme={null} HTTP/1.1 401 Authorization Required ... {"errors": [{ "code": 89, "message": "Invalid or expired token."} ]} ``` ### POST oauth2/token Allows a registered application to obtain an OAuth 2 Bearer Token, which can be used to make API requests on an application's own behalf, without a user context. This is called [Application-only authentication](/resources/fundamentals/authentication/oauth-2-0/application-only). A Bearer Token may be invalidated using oauth2/invalidate\_token. Once a Bearer Token has been invalidated, new creation attempts will yield a different Bearer Token and usage of the previous token will no longer be allowed. Only one bearer token may exist outstanding for an application, and repeated requests to this method will yield the same already-existent token until it has been invalidated. Successful responses include a JSON-structure describing the awarded Bearer Token. Tokens received by this method should be cached. If attempted too frequently, requests will be rejected with a HTTP 403 with code 99. **Resource URL[](#resource-url "Permalink to this headline")** `https://api.x.com/oauth2/token` **Resource Information[](#resource-information "Permalink to this headline")** | | | | :----------------------- | :-------------------------------------------------------------------------------------- | | Response formats | JSON | | Requires authentication? | Yes - Basic auth with your API key as your username and API key secret as your password | | Rate limited? | Yes | **Parameters[](#parameters "Permalink to this headline")** | | | | | | | :---------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------ | :-------------------- | | Name | Required | Description | Default Value | Example | | grant\_type | required | Specifies the type of grant being requested by the application. At this time, only *client\_credentials* is allowed. See [Application-Only Authentication](/resources/fundamentals/authentication/oauth-2-0/application-only) for more information. | | *client\_credentials* | **Example request[](#example-request "Permalink to this headline")** ```bash theme={null} POST /oauth2/token HTTP/1.1 Host: api.x.com User-Agent: My X App v1.0.23 Authorization: Basic eHZ6MWV2R ... o4OERSZHlPZw== Content-Type: application/x-www-form-urlencoded;charset=UTF-8 Content-Length: 29 Accept-Encoding: gzip grant_type=client_credentials ``` **Example response:** ```bash theme={null} HTTP/1.1 200 OK Status: 200 OK Content-Type: application/json; charset=utf-8 ... Content-Encoding: gzip Content-Length: 140 {"token_type":"bearer","access_token":"AAAA%2FAAA%3DAAAAAAAA"} ``` ### POST oauth2/invalidate\_token Allows a registered application to revoke an issued oAuth 2.0 Bearer Token by presenting its client credentials. Once a Bearer Token has been invalidated, new creation attempts will yield a different Bearer Token and usage of the invalidated token will no longer be allowed. Successful responses include a JSON-structure describing the revoked Bearer Token. **Resource URL[](#resource-url "Permalink to this headline")** `https://api.x.com/oauth2/invalidate_token` **Resource Information[](#resource-information "Permalink to this headline")** | | | | :----------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Response formats | JSON | | Requires authentication? | Yes - [oAuth 1.0a](/resources/fundamentals/authentication/oauth-1-0a) with the application's consumer API keys and the application owner's access token & access token secret | | Rate limited? | Yes | **Parameters[](#parameters "Permalink to this headline")** | Name | Required | Description | | :------------ | :------- | :-------------------------------------------------------------- | | access\_token | required | The value of the bearer token that you would like to invalidate | **Example request[](#example-request "Permalink to this headline")** ``` curl --request POST --url 'https://api.x.com/oauth2/invalidate_token?access_token=AAAA%2FAAA%3DAAAAAAAA' --header 'authorization: OAuth oauth_consumer_key="CLIENT_KEY", oauth_nonce="AUTO_GENERATED_NONCE", oauth_signature="AUTO_GENERATED_SIGNATURE", oauth_signature_method="HMAC-SHA1", oauth_timestamp="AUTO_GENERATED_TIMESTAMP", oauth_token="ACCESS_TOKEN", oauth_version="1.0"' ``` **Example response[](#example-response "Permalink to this headline")** ``` Status: 200 OK Content-Type: application/json; charset=utf-8 Content-Length: 135 ... { "access_token": "AAAA%2FAAA%3DAAAAAAAA" } ``` # Basic authentication Source: https://docs.x.com/fundamentals/authentication/basic-auth ## Basic authentication Many of X's enterprise APIs require the use of HTTP Basic Authentication. To make a successful request to an API that requires Basic Authentication, you must pass a valid email address and password combination as an authorization header for each request. The email and password combination are the same ones that you will use to access the [enterprise API console](https://console.gnip.com/), and can be editted from within this console.  When building a request using Basic Authentication, make sure you add the Authentication: Basic HTTP header with encoded credentials over HTTPS. In the following cURL request example, you would replace `` and `` with your credentiails before sending the request: ```bash theme={null} curl -v --compressed -u: "https://gnip-api.x.com/search/30day/accounts//prod/counts.json?query=from%3Axdevelopers" ``` **APIs that require basic authentication:** * [PowerTrack API](/x-api/enterprise-gnip-2.0/powertrack-api) enterprise * [Decahose stream API](http://localhost:3000/x-api/enterprise-gnip-2.0/fundamentals/decahose-api) enterprise * [30-Day Search API](/x-api/enterprise-gnip-2.0/fundamentals/search-api) enterprise * [Full-Archive Search API](/x-api/enterprise-gnip-2.0/fundamentals/search-api) enterprise * [Usage API](/x-api/enterprise-gnip-2.0/fundamentals/usage) enterprise # OAuth FAQ Source: https://docs.x.com/fundamentals/authentication/faq ## General OAuth is an authentication protocol that allows users to approve an application to act on their behalf without sharing their password. More information can be found at [oauth.net](http://oauth.net/). You must have a [X app](/resources/fundamentals/developer-apps) to generate access tokens. Learn more about access tokens [here](/resources/fundamentals/authentication#oauth-1-0a-2).  You must have a [developer account](/resources/fundamentals/developer-portal) to create a [X app](/resources/fundamentals/developer-apps). You can sign up for one [here](https://developer.x.com/en/portal/petition/essential/basic-info). You can view and edit your app from the [X app dashboard](https://developer.x.com/content/developer-twitter/en/apps) if you are logged into your X account on developer.x.com. ## Technical Access tokens are not explicitly expired. An access token will be invalidated if a user explicitly revokes an application in the their X account settings, or if X suspends an application. If an application is suspended, there will be a note in the [X app](/resources/fundamentals/developer-apps) dashboard stating that it has been suspended. Assume a user’s access token *may* become invalid at any time. If this happens, prompt the user to re-authorize the application. Ensuring that this situation is handled gracefully is important for a good user experience. Many users trust an application to read their information, but not necessarily change their user profile information or post new statuses. Updating information via the X API - be it name, location or adding a new status - requires an HTTP POST. Any API method that requires an HTTP POST is considered a write method and requires read & write access. # Best practices Source: https://docs.x.com/fundamentals/authentication/guides/authentication-best-practices Your API keys and tokens should be guarded very carefully.  These credentials are directly tied to your [developer App](/resources/fundamentals/developer-apps) and those X account that have authorized you to make requests on behalf of them. If your keys are compromised, bad actors could use them to make requests to the X endpoints on behalf of your developer App or its authorized users, which could mean their requests might cause you to hit unexpected rate limits, use up your paid access allotment, or even cause your developer App to be suspended. The following sections include best practices that should be considered when managing your API keys and tokens. ## Regenerate API keys and tokens In the event that you believe that your API keys has been exposed, you should regenerate your API keys by following these steps: 1. Navigate to the [Developer Console's "Apps" page](https://developer.x.com/en/portal/projects-and-apps.html). 2. Click on the "Keys and tokens" icon (🗝 ) next to the relevant App. 3. Click on the "Regenerate" button next to the set of keys and tokens that you would like to regenerate.  If you would prefer to regenerate your Access Tokens or Bearer Tokens programatically, you can do so using our authentication endpoints. * If you would like to regenerate your Access Tokens, you must invalidate your tokens using the [POST oauth/invalidate\_token](/resources/fundamentals/authentication/api-reference#post-oauth2-invalidate-token) endpoint, then regenerate your tokens using the [3-legged OAuth flow](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens). * If you would like to regenerate your Bearer Token, you must invalidate your token using the [POST oauth2/invalidate\_token](/resources/fundamentals/authentication/api-reference#post-oauth2-invalidate-token) endpoint, then regenerate your token using the [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token) endpoint. ## Having a central file for your secrets Having a file such as .ENV file or any other sort of .yaml file to contain your secrets is an option that could be helpful but be sure to have a strong .gitignore file that can prevent you from accidentally committing these to a git repository.  ## Environment variables Writing code that utilizes environment variables might be helpful.  An example of this is as follows written in Python: ```python theme={null} import os consumer_key = os.environ.get("CONSUMER_KEY") consumer_secret = os.environ.get("CONSUMER_SECRET") ``` Inside of your terminal you would want to write something like this: ```bash theme={null} export CONSUMER_KEY='xxxxxxxxxxxxxxxxxxx' export CONSUMER_SECRET='xxxxxxxxxxxxxxxxxxxxxxx' ``` ## Source code and version control The most common security mistakes made by developers are having API keys and tokens committed to source code in accessible version control systems like GitHub and BitBucket. Many of these code repositories are publicly accessible. This mistake is made so often in public code repositories that there are lucrative bots that scrape for API keys. * Use server environment variables. By storing API keys in environment variables, you keep them out of your code and version control. This also allows you to use different keys for different environments easily. * Use a configuration file excluded from source control. Add the filename to your [.gitignore](https://git-scm.com/docs/gitignore) file to exclude the file from being tracked by version control. * If you remove the API keys from your code after you have used version control, the API keys are likely still accessible by accessing previous versions of your codebase. Regenerate your API keys, as described in the next section. ## Databases If you need to store your access tokens in a database, please keep the following in mind: * Restrict access to the database in a way such that the access tokens are only readable by the owner of the token. * Restrict edit/write privileges to the database table for access tokens - this should be automated with the key management system. * Encrypt access tokens before storing in any data stores. ## Password management tools Password management tools such as 1password or Last Pass can be helpful in keeping your keys and tokens in secure place. You might want to avoid sharing these in side of a shared team password management tool. ## Web storage & cookies There are two types of web storage: LocalStorage and SessionStorage. These were created as improvements to using Cookies since the storage capacity for web storage is much higher than Cookie storage. However, there are different pros and cons to each of these storage options.   **Web Storage: LocalStorage** Anything stored in local web storage is persistent. This means that the data will persist until the data is explicitly deleted. Depending on the needs of your project, you might view this as a positive. However, you should be mindful of using LocalStorage, since any changes/additions to data will be available on all future visits to the webpage in question. We would not usually recommend using LocalStorage, although there may be a few exceptions to this. If you decide to use LocalStorage, it is good to know that it supports the same-origin policy, so all data stored here will only be available via the same origin. An added performance perk of using LocalStorage would be a resulting decrease in client-server traffic since the data does not have to be sent back to the server for every HTTP request.   **Web Storage: SessionStorage** SessionStorage is similar to LocalStorage, but the key difference is that SessionStorage is not persistent. Once the window (or tab, depending on which browser you are using) that was used to write to SessionStorage is closed, the data will be lost. This is useful in restricting read access to your token within a user session. Using SessionStorage is normally more preferable than LocalStorage when thinking in terms of security. Like LocalStorage, the perks of same-origin policy support and decreased client-server traffic apply to SessionStorage as well.   **Cookies** Cookies are the more traditional way to store session data. You can set an expiration time for each cookie, which would allow for ease of revocability and restriction of access. However, the client-server traffic would definitely increase when using cookies, since the data is being sent back to the server for every HTTP request. If you decide to use cookies, you need to protect against session hijacking. By default, cookies are sent in plaintext over HTTP, which makes their contents vulnerable to packet sniffing and/or man-in-the-middle attacks where attackers may modify your traffic. You should always enforce HTTPS to protect your data in transit. This will provide confidentiality, integrity (of the data), and authentication. However, if your web application or site is available both through HTTP and HTTPS, you will also want to use the 'Secure' flag on the cookie. This will prevent attackers from being able to send links to the HTTP version of your site to a user and listening in on the resulting HTTP request generated. Another secondary defense against session hijacking when using cookies would be to validate the user's identity again before any high-impact actions are carried out. One other flag to consider for improving the security of your cookies would be the 'HttpOnly' flag. This flag tells the browser that the cookie in question shall only be accessible from the server specified. Any attempts made by client-side scripts would be forbidden by this flag, therefore helping to protect against most cross-site scripting (XSS) attacks. # Log in with X Source: https://docs.x.com/fundamentals/authentication/guides/log-in-with-x Use Log in with X, also known as Sign in with X, to place a button on your site or application which allows X users to enjoy the benefits of a registered user account in as little as one click. This works on websites, iOS, mobile, and desktop applications. ## Features * Ease of use - A new visitor to your site only has to click two buttons in order to log in for the first time. * X integration - The Log in with X flow can grant authorization to use X APIs on your users' behalf. * OAuth based - A wealth of client libraries and example code are compatible with the Log in with X API. ## Available for * Browsers - If your users can access a browser, you can integrate with Log in with X. Learn about the browser sign in flow. * Mobile devices - Any web-connected mobile device can take advantage of Log in with X. Learn about the mobile sign in flow. ## Implementing Log in with X The browser and mobile web implementations of Log in with X are based on OAuth. This page demonstrates the requests needed to obtain an access token for the sign in flow. To use the “Log in with X" flow, please go to your [X app settings](/resources/fundamentals/developer-apps) and ensure that the *"Allow this app to be used to Sign in with X?*” option is enabled. This page assumes that the reader knows how to sign requests using the OAuth 1.0a protocol. If you want to know how to sign a request, read the [Authorizing a request](/resources/fundamentals/authentication/oauth-1-0a/authorizing-a-request) page. If you want to check the signing of the requests on this page, the consumer secret used is: L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg. This value is for test purposes and will not work for real requests. The three steps for implementing Log in with X through obtaining a request token, redirecting a user, and converting a request token into an access token are listed below. ### Step 1: Obtaining a request token To start a sign-in flow, your [X app](/resources/fundamentals/developer-apps) must obtain a request token by sending a signed message to [POST oauth/request\_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token). The only unique parameter in this request is oauth\_callback, which must be a URL-encoded version of the URL you wish your user to be redirected to when they complete step 2. The remaining parameters are added by the OAuth signing process. **Note:** Any [callback URL](/resources/fundamentals/developer-apps#callback-urls) that you use with the [POST oauth/request\_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) endpoint will have to be registered within the [X app settings](/resources/fundamentals/developer-apps) in the [Developer Console](/resources/fundamentals/developer-portal). **Example request (Authorization header has been wrapped):** ``` POST /oauth/request_token HTTP/1.1 User-Agent: themattharris' HTTP Client Host: api.x.com Accept: */* Authorization: OAuth oauth_callback="http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F", oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w", oauth_nonce="ea9ec8429b68d6b77cd5600adbbb0456", oauth_signature="F1Li3tvehgcraF8DMJ7OyxO4w9Y%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1318467427", oauth_version="1.0" ``` Your app should examine the HTTP status of the response. Any value other than 200 indicates a failure. The body of the response will contain the oauth\_token, oauth\_token\_secret, and oauth\_callback\_confirmed parameters. Your app should verify that oauth\_callback\_confirmed is true and store the other two values for the next steps. **Example response (response body has been wrapped):** ``` HTTP/1.1 200 OK Date: Thu, 13 Oct 2011 00:57:06 GMT Status: 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 146 Pragma: no-cache Expires: Tue, 31 Mar 1981 05:00:00 GMT Cache-Control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0 Vary: Accept-Encoding Server: tfe oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0& oauth_token_secret=veNRnAWe6inFuo8o2u8SLLZLjolYDmDP7SzL0YfYI& oauth_callback_confirmed=true ``` ### Step 2: Redirecting the user The next step is to direct the user to X so that they may complete the appropriate flow, as described in Browser sign-in flow below. Direct the user to [GET oauth/authenticate](/resources/fundamentals/authentication/api-reference#get-oauth-authenticate), and the request token obtained in step 1 should be passed as the oauth\_token parameter. The most seamless way for a website to implement this would be to issue an HTTP 302 redirect as the response to the original “sign in” request. Mobile and desktop apps should open a new browser window or direct to the URL via an embedded web view. **Example URL to redirect to:** [https://api.x.com/oauth/authenticate?oauth\_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0](https://api.x.com/oauth/authenticate?oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0) The sign in endpoint will behave in one of three ways depending on the user’s status: 1. **Signed in and approved**: If the user is signed in on x.com and has already approved the calling application, they will be immediately authenticated and returned to the callback URL with a valid OAuth request token. The redirect to x.com is not obvious to the user. 2. **Signed in but not approved**: If the user is signed in to x.com but has not approved the calling application, a request to share access with the calling application will be shown. After accepting the authorization request, the user will be redirected to the callback URL with a valid OAuth request token. 3. **Not signed in**: If the user is not signed in on x.com, they will be prompted to enter their credentials and grant access for the application to access their information on the same screen. Once signed in, the user will be returned to the callback URL with a valid OAuth request token. Upon a successful authentication, your callback\_url would receive a request containing the oauth\_token and oauth\_verifier parameters. Your application should verify that the token matches the request token received in step 1. **Request from client’s redirect (querystring parameters wrapped):** ``` GET /sign-in-with-twitter/? oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0& oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.5 (KHTML, like Gecko) Chrome/16.0.891.1 Safari/535.5 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Referer: http://localhost/sign-in-with-twitter/ Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 ``` ### Step 3: Converting the request token to an access token To render the request token into a usable access token, your application must make a request to the [POST oauth/access\_token](/resources/fundamentals/authentication/api-reference#post-oauth-access-token) endpoint, containing the oauth\_verifier value obtained in step 2. The request token is also passed in the oauth\_token portion of the header, but this will have been added by the signing process. **Example request (Authorization header wrapped):** ``` POST /oauth/access_token HTTP/1.1 User-Agent: themattharris' HTTP Client Host: api.x.com Accept: */* Authorization: OAuth oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w", oauth_nonce="a9900fe68e2573b27a37f10fbad6a755", oauth_signature="39cipBtIOHEEnybAR4sATQTpl2I%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1318467427", oauth_token="NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0", oauth_version="1.0" Content-Length: 57 Content-Type: application/x-www-form-urlencoded oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY ``` A successful response contains the oauth\_token, oauth\_token\_secret parameters. The token and token secret should be stored and used for future authenticated requests to the X API. To determine the identity of the user, use [GET account/verify\_credentials](https://dev.x.com/rest/reference/get/account/verify_credentials). **Example response (response body has been wrapped):** ``` HTTP/1.1 200 OK Date: Thu, 13 Oct 2011 00:57:08 GMT Status: 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 157 Pragma: no-cache Expires: Tue, 31 Mar 1981 05:00:00 GMT Cache-Control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0 Vary: Accept-Encoding Server: tfe oauth_token=7588892-kagSNqWge8gB1WwE3plnFsJHAZVfxWD7Vb57p0b4& oauth_token_secret=PbKfYqSryyeKDWz4ebtY3o5ogNLG11WJuZBc9fQrQo ``` ### Log in with X Resources #### Client libraries The client libraries listed at [X libraries](/resources/tools-and-libraries) will help implement Log in with X. Use the /oauth/authenticate endpoint, as described in the previous steps. #### Brand Toolkit X would prefer your application to use the official [X Brand Toolkit](https://about.x.com/en/who-we-are/brand-toolkit) for consistent branding. Save these assets use them when creating a 'Login with X' button. The browser log in flow is appropriate for websites and applications which are able to open or embed a web browser. At a very high level: * The application renders a “Sign in with X” link or button. * The user clicks the sign in button. * The current web browser is redirected to X (or a new browser is opened and directed to X). * The user completes a login and authorization step at X if needed. * X redirects back to an URL under the application’s control, passing authorization information for the user. X keeps track of the authorizations, so for users already signed in to X.com who have authorized the application, no UI is shown - instead, they are automatically redirected back to the application. ### Desktop flow To demonstrate the flows, pretend the website pictured above (“The greatest website ever created”) has implemented this API, as shown by the Sign in with X button on the landing page. When the user clicks the Sign in button, the page they see depends on whether they are signed in and whether they have previously allowed the application to access their account. When the user is signed in to x.com but has not granted access, a list of requested permissions, along with Sign In and Cancel buttons are shown. When the user is not signed in to x.com input fields for a username and password will be shown. Note that even if the user has already granted access to the application, the list of permissions will still be shown. After the user inputs valid credentials (if needed) and clicks “Sign In”, X will redirect the user to the website which started the sign in flow. In the case where the user is already signed in to x.com and has granted access to the website, this redirect happens immediately. The UI flow for mobile web browsers works exactly like the Browser sign in flow, but is optimized for mobile browsers. Below are screenshots for the signed in, signed out, and redirect screens: # Connection to X API using TLS Source: https://docs.x.com/fundamentals/authentication/guides/tls TLS connections are required in order to access X API endpoints. Communicating over TLS preserves user privacy and security by protecting information between the user and the X API as it travels across the public Internet. Connections to the X API require TLS version 1.2. ## Verification ### Use an up-to-date root store It's important that your application or library use a trustworthy and up-to-date root store when verifying the X certificate. Where possible, using the root store provided by your operating system may be the simplest approach here. Alternatively, the [Mozilla (NSS) root store](https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/) is well maintained in a public and transparent manner. Curl also provides [a version of this store in PEM format](https://curl.haxx.se/docs/caextract.html). X currently issues the bulk of our certs from the [DigiCert High Assurance EV Root CA](https://www.digicert.com/digicert-root-certificates.htm), but this is not true for 100% of X-related certificates and may not hold true forever, so trusting only the currently-used Digicert roots may lead to issues with your app in the future. ### Check CRLs and the OCSP status[](#check-crls-and-the-ocsp-status "Permalink to this headline") Many applications do not check the Certificate Revocation List for returned certificates or rely on the operating system to do so. Ensure that your application or TLS library is configured to force CRL and OCSP (Online Certificate Status Protocol) verification before accepting X’s certificate. ### CDNs[](#cdns "Permalink to this headline") When showing Tweets that contain media, use the `media_url_https` attribute for the HTTPS URLs to use when showing images. In the future, all URLs served from API endpoints will provide HTTPS paths. ## Provide an indication of security status If possible, you should show an indication of the current status between your application and X. Some web browsers indicate this by offering a Lock Icon, while others indicate the current connection state with descriptive messaging. # X API v2 authentication mapping Source: https://docs.x.com/fundamentals/authentication/guides/v2-authentication-mapping The following chart illustrates which v2 endpoints map to what authentication methods.    | | | | | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------- | :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | **Endpoint** | **OAuth 1.0a User Context** | **OAuth 2.0** **App Only 
** | **OAuth 2.0
Authorization Code with PKCE** | | [Tweet lookup](/x-api/posts/lookup/introduction)

Retrieve multiple Tweets with a list of IDs

[GET /2/tweets](/x-api/posts/post-lookup-by-post-ids)

Retrieve a single Tweet with an ID

[GET /2/tweets/:id](/x-api/posts/post-lookup-by-post-id) | ✅ | ✅ | ✅  

Scopes:

tweet.read

users.read | | [Manage Tweets](/x-api/posts/manage-tweets/introduction)

Post a Tweet

[POST /2/tweets](/x-api/posts/creation-of-a-post)

Delete a Tweet

[DELETE /2/tweets/:id](/x-api/posts/post-delete-by-post-id) | ✅ | | ✅

Scopes:

tweet.read

tweet.write

users.read | | [Timelines](/x-api/posts/timelines/introduction)

User Tweet timeline

[GET /2/users/:id/tweets](/x-api/posts/user-posts-timeline-by-user-id)

User mention timeline

[GET /2/users/:id/mentions](/x-api/posts/user-mention-timeline-by-user-id)

Reverse chronological home timeline

[GET /2/users/:id/timelines/reverse\_cronological](/x-api/posts/timelines#user-mention-timeline-3) | ✅

✅ | ✅ | ✅

Scopes:

tweet.read

users.read



Scopes:

tweet.read

users.read | | [Recent search](/x-api/posts/search/introduction#recent-search)

Search for Tweets published in the last 7 days

[GET /2/tweets/search/recent](/x-api/posts/recent-search) | ✅ | ✅ | ✅

Scopes: 

tweet.read

users.read | | [Full-archive search](/x-api/posts/search/introduction#full-archive-search)

Only available to those with Academic Research access

Search the full archive of Tweets

[GET /2/tweets/search/all](/x-api/posts/full-archive-search) | | ✅ | | | [Filtered stream](/x-api/posts/filtered-stream/introduction)

Add or delete rules from your stream

[POST /2/tweets/search/stream/rules](/x-api/posts/adddelete-rules)

Retrieve your stream's rules

[GET /2/tweets/search/stream/rules](/x-api/posts/rules-lookup)

Connect to the stream

[GET /2/tweets/search/stream](/x-api/posts/filtered-stream) | | ✅ | | | [Volume streams](/x-api/posts/volume-streams/introduction)

Streams about 1% of all Tweets in real-time.

[GET /2/tweets/sample/stream](/x-api/posts/sample-stream) | | ✅ | | | [Manage Retweets](/x-api/posts/retweets/introduction#manage-retweets)

Retweet a Tweet

[POST /2/users/:id/retweets](/x-api/posts/retrieve-posts-that-repost-a-post)

Delete a Retweet

[DELETE /2/users/:id/retweets/:source\_tweet\_id](/x-api/posts/causes-the-user-in-the-path-to-unretweet-the-specified-post) | ✅ | | ✅

Scopes:

tweet.read

tweet.write

users.read | | [Retweets lookup](/x-api/posts/retweets/introduction#retweets-lookup)

Users who have Retweeted a Tweet

[GET /2/tweets/:id/retweeted\_by](/x-api/users/returns-user-objects-that-have-retweeted-the-provided-post-id) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read | | Bookmarks [lookup](/x-api/posts/bookmarks/introduction#bookmarks-lookup)

Get bookmarked Tweets

[GET /2/tweets/:id/bookmarks](/x-api/bookmarks/bookmarks-by-user) | | | ✅

Scopes:

tweet.read

users.read

bookmark.read | | [Manage Bookmarks](/x-api/posts/bookmarks/introduction#manage-bookmarks)

Bookmark a Tweet

[POST /2/tweets/:id/bookmarks](/x-api/bookmarks/add-post-to-bookmarks)

Remove a Bookmark of a Tweet

[DELETE /2/users/:id/bookmarks:tweet\_id](/x-api/bookmarks/remove-a-bookmarked-post) | | | ✅

Scopes:

tweet.read

users.read

bookmark.write | | [Manage Likes](/x-api/posts/likes/introduction#manage-likes)

Like a Tweet

[POST /2/users/:id/likes](/x-api/posts/causes-the-user-in-the-path-to-like-the-specified-post)

Undo a Like of a Tweet

[DELETE /2/users/:id/likes/:tweet\_id](/x-api/posts/causes-the-user-in-the-path-to-unlike-the-specified-post) | ✅ | | ✅

Scopes:

tweet.read

users.read

like.write | | [Likes lookup](/x-api/posts/likes/introduction#likes-lookup)

Users who have liked a Tweet

[GET /2/tweets/:id/liking\_users](/x-api/users/returns-user-objects-that-have-liked-the-provided-post-id)

Tweets liked by a user

[GET /2/users/:id/liked\_tweets](/x-api/posts/returns-post-objects-liked-by-the-provided-user-id) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read

like.read | | [Hide replies](/x-api/posts/hide-replies/introduction)

Hides or unhides a reply to a Tweet.

[PUT /2/tweets/:id/hidden](/x-api/posts/hide-replies) | ✅ | | ✅

Scopes:

tweet.read

users.read

tweet.moderate.write | | [Users lookup](/x-api/users/lookup/introduction)

Retrieve multiple users with IDs

[GET /2/users](/x-api/users/user-lookup-by-ids)

Retrieve a single user with an ID

[GET /2/users/:id](/x-api/users/user-lookup-by-id)

Retrieve multiple users with usernames

[GET /2/users/by](/x-api/users/user-lookup-by-usernames)

Retrieve a single user with a usernames

[GET /2/users/by/username/:username](/x-api/users/user-lookup-by-username)

Get information about an authenticated user

[GET /2/users/me](/x-api/users/user-lookup-me) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read | | [Manage follows](/x-api/users/follows/introduction#manage-follows)

Allows a user ID to follow another user

[POST /2/users/:id/following](/x-api/users/follow-user)

Allows a user ID to unfollow another user

[DELETE /2/users/:source\_user\_id/following/:target\_user\_id](/x-api/users/unfollow-user) | ✅ | | ✅

Scopes: 

tweet.read

users.read

follows.write | | [Follows lookup](/x-api/users/follows/introduction#follows-lookup)

Lookup following of a user by ID

[GET /2/users/:id/following](/x-api/users/following-by-user-id)

Lookup followers of a user by ID

[GET /2/users/:id/followers](/x-api/users/followers-by-user-id) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read

follows.read | | [Blocks lookup](/x-api/users/blocks/introduction#blocks-lookup)

Returns a list of users who are blocked by the specified user ID 

[GET /2/users/:id/blocking](/x-api/users/returns-user-objects-that-are-blocked-by-provided-user-id) | ✅ | | ✅

Scopes:

tweet.read

users.read

block.read | | [Manage Mutes](/x-api/users/mutes/introduction#manage-mutes)

Allows a user ID to mute another user

[POST /2/users/:id/muting](/x-api/users/mute-user-by-user-id)

Allows a user ID to unmute another user

[DELETE /2/users/:source\_user\_id/muting/:target\_user\_id](/x-api/users/unmute-user-by-user-id) | ✅ | | ✅

Scopes:

tweet.read

users.read

mute.write | | [Mutes lookup](/x-api/users/mutes/introduction#mutes-lookup)

Returns a list of users who are muted by the specified user ID

[GET /2/users/:id/muting](/x-api/users/returns-user-objects-that-are-muted-by-the-provided-user-id) | ✅ | | ✅

Scopes:

tweet.read

users.read

mute.read | | [Spaces lookup](/x-api/spaces/lookup/introduction)

Lookup Space by ID

[GET /2/spaces/:id](/x-api/spaces/space-lookup-by-space-id)

Lookup multiple Spaces 

[GET /2/spaces](/x-api/spaces/space-lookup-up-space-ids)

Discover Spaces created by user ID

[GET /2/spaces/by/creator\_ids](/x-api/spaces/space-lookup-by-their-creators) | | ✅ | ✅

Scopes:

tweet.read

users.read

space.read | | [Spaces lookup](/x-api/spaces/lookup/introduction)

Get users who purchased a ticket to a Space

[GET /2/spaces/:id/buyers](/x-api/spaces/retrieve-the-list-of-users-who-purchased-a-ticket-to-the-given-space) | | | ✅

Scopes:

tweet.read

users.read

space.read | | [Spaces search](/x-api/spaces/search/introduction)

Returns live or scheduled Spaces matching your specified search terms.

[GET /2/spaces/search](/x-api/spaces/search-for-spaces) | | ✅ | ✅

Scopes:

tweet.read

users.read

space.read | | [List lookup](/x-api/lists/list-lookup/introduction)

Lookup a specific list by ID

[GET /2/lists/:id](/x-api/lists/list-lookup-by-list-id)

Lookup a user's owned List

[GET /2/users/:id/owned\_lists](/x-api/lists/get-a-users-owned-lists) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read

list.read | | [Manage Lists](/x-api/lists/manage-lists/introduction)

Creates a new List on behalf of an authenticated user

[POST /2/lists](/x-api/lists/create-list) | ✅ | | ✅

Scopes:

tweet.read

users.read

list.read

list.write | | [Manage Lists](/x-api/lists/manage-lists/introduction)

Deletes a List the authenticated user owns

[DELETE /2/lists/:id](/x-api/lists/delete-list)

Updates the metadata for a List the authenticated user owns

[PUT /2/lists/:id](/x-api/lists/update-list) | ✅ | | ✅

Scopes:

tweet.read

users.read

list.write | | [List Tweets lookup](/x-api/lists/list-tweets/introduction)

Lookup Tweets from a specified List

[GET /2/lists/:id/tweets](/x-api/posts/list-posts-timeline-by-list-id) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read

list.read | | [List members lookup](/x-api/lists/list-members/introduction#list-members-lookup)

Returns a list of members from a specified List

[GET /2/lists/:id/members](/x-api/users/returns-user-objects-that-are-members-of-a-list-by-the-provided-list-id)

Returns all Lists a specified user is a member of

[GET /2/users/:id/list\_memberships](/x-api/lists/get-a-users-list-memberships) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read

list.read | | [Manage List members](/x-api/lists/list-members/introduction#manage-list-members)

Add a member to a List that the authenticated user owns

[POST /2/lists/:id/members](/x-api/users/returns-user-objects-that-are-members-of-a-list-by-the-provided-list-id)

Removes a member from a List the authenticated user owns

[DELETE /2/lists/:id/members/:user\_id](/x-api/lists/remove-a-list-member) | ✅ | | ✅

Scopes:

tweet.read

users.read

list.write | | [List follows lookup](/x-api/lists/list-lookup/introduction)

Returns all followers of a specified List

[GET /2/lists/:id/followers](/x-api/users/returns-user-objects-that-follow-a-list-by-the-provided-list-id)

Returns all Lists a specified user follows

[GET /2/users/:id/followed\_lists](/x-api/lists/get-users-followed-lists) | ✅ | ✅ | ✅

Scopes:

tweet.read

users.read

list.read | | [Manage List follows](/x-api/lists/manage-lists/introduction)

Follows a List on behalf of an authenticated user

[POST /2/users/:id/followed\_lists](/x-api/lists/follow-a-list)

Unfollows a List on behalf of an authenticated user

[DELETE /2/users/:id/followed\_lists/:list\_id](/x-api/lists/unfollow-a-list) | ✅ | | ✅

Scopes:

tweet.read

users.read

list.write | | [Pinned List lookup](/x-api/lists/pinned-lists/introduction)

Returns the pinned Lists of the authenticated user

[GET /2/users/:id/pinned\_lists](/x-api/lists/get-a-users-pinned-lists) | ✅ | | ✅

Scopes:

tweet.read

users.read

list.read | | [Manage pinned List](/x-api/lists/pinned-lists/introduction)

Pins a List on behalf of an authenticated user

[POST /2/users/:id/pinned\_lists](/x-api/lists/pin-a-list)

Unpins a List on behalf of an authenticated user

[DELETE /2/users/:id/pinned\_lists/:list\_id](/x-api/lists/unpin-a-list) | ✅ | | ✅

Scopes:

tweet.read

users.read

list.write | | [Batch compliance](/x-api/compliance/batch-compliance/introduction)

Creates a new compliance job

[POST /2/compliance/jobs](/x-api/compliance/create-compliance-job)

Returns status and download information about a specified compliance job

[GET /2/compliance/jobs/:job\_id](/x-api/compliance/get-compliance-job)

Returns a list of recent compliance jobs

[GET /2/compliance/jobs](/x-api/compliance/list-compliance-jobs) | | ✅ | | # API Key and Secret Source: https://docs.x.com/fundamentals/authentication/oauth-1-0a/api-key-and-secret ### API Key and Secret The API Key and Secret (also known as Consumer Key and Secret) are the most fundamental credentials required to access the X API. These credentials act as the username and password for your X App, and are used by the X API to understand which App requests are coming from.  These credentials can be used by [authentication endpoints](/resources/fundamentals/authentication/api-reference) to generate additional credentials, such as [user Access Tokens and Secrets](/resources/fundamentals/authentication/oauth-1-0a/api-key-and-secret), and [Bearer Tokens](/resources/fundamentals/authentication/oauth-2-0/bearer-tokens). You also need to use these credentials along with Access Tokens and other authorization parameters to [authorize requests](/resources/fundamentals/authentication/oauth-1-0a/authorizing-a-request) that require OAuth 1.0a User Context authentication.  #### How to acquire an API Key and Secret To acquire a X API Key and Secret, please follow these steps: 1. [Sign up for a X developer account](https://developer.x.com/en/apply-for-access) 2. Create a [X App](/resources/fundamentals/developer-apps) within the [Developer Console](/resources/fundamentals/developer-portal). Note that if you would like to use [X API v2](/x-api/introduction), you must use keys and tokens from a developer App.   When you create your X App, you will be presented with your API Key and Secret, along with a Bearer Token. Please note that we only display these credentials once, so make sure to save them in your password manager or somewhere secure. We have more recommendations on how to handle your keys and tokens within our [authentication best practices](/resources/fundamentals/authentication/guides/authentication-best-practices) page, including details on what you should do if your credentials have been compromised.   #### How to find and regenerate your API Key and Secret after App creation If you've already created an App and need to find or regenerate your API Key and Secret, please follow these steps: 1. Navigate to the Developer Console 2. Expand the 'Apps' dropdown in the sidenav 3. Open the App which is associated with the API Key and Secret that you would like to find or regenerate 4. Navigate to the Keys and tokens tab From there, you will find all of the credentials associated with your App.    #### How to use your API Key and Secret If you are just exploring the X Developer Platform, we recommend that you use a [tool or library](/resources/tools-and-libraries) to see what’s available on the platform. These tools handle authentication gracefully, and can save you a lot of time and frustration. We specifically recommend [getting started with Postman](/tutorials/postman-getting-started) or [Insomnia](https://insomnia.rest/) for beginner developers.  If you are interested in building a request from scratch, please read our guide on [authorizing an OAuth 1.0a request](/resources/fundamentals/authentication/oauth-1-0a/authorizing-a-request).   # Authorizing a request Source: https://docs.x.com/fundamentals/authentication/oauth-1-0a/authorizing-a-request ### Authorizing a request The purpose of this document is to show you how to modify HTTP requests for the purpose of sending authorized requests to the X API. All of X's APIs are based on the HTTP protocol. This means that any software you write which uses X's APIs sends a series of structured messages to X’s servers. For example, a request to post the text “**Hello Ladies + Gentlemen, a signed OAuth request!**” as a Tweet will look something like this: ``` POST /1.1/statuses/update.json?include_entities=true HTTP/1.1 Accept: */* Connection: close User-Agent: OAuth gem v0.4.4 Content-Type: application/x-www-form-urlencoded Content-Length: 76 Host: api.x.com status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21 ``` Any HTTP library should be able to generate and issue the above request with a minimum of difficulty. However, the above request is considered invalid, since there is no way of knowing: 1. Which application is making the request 2. Which user the request is posting on behalf of 3. Whether the user has granted the application authorization to post on the user’s behalf 4. Whether the request has been tampered by a third party while in transit To allow applications to provide this information, X’s API relies on the [OAuth 1.0a protocol](http://tools.ietf.org/html/rfc5849). At a very simplified level, X’s implementation requires that requests needing authorization contain an additional HTTP Authorization header with enough information to answer the questions listed above. A version of the HTTP request shown above, modified to include this header, looks like this (normally the Authorization header would need to be on one line, but has been wrapped for legibility here): ``` POST /1.1/statuses/update.json?include_entities=true HTTP/1.1 Accept: */* Connection: close User-Agent: OAuth gem v0.4.4 Content-Type: application/x-www-form-urlencoded Authorization: OAuth oauth\_consumer\_key="xvz1evFS4wEEPTGEFPHBog", oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D", oauth\_signature\_method="HMAC-SHA1", oauth_timestamp="1318622958", oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", oauth_version="1.0" Content-Length: 76 Host: api.x.com status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21 ``` When this request was created, it would have been accepted by the X API as valid. If this signing process sounds like it is beyond the scope of your integration, consider using [Web Intents](https://dev.x.com/web/intents), which do not need to use OAuth to interact with the X API.   **Collecting parameters** You should be able to see that the header contains 7 key/value pairs, where the keys all begin with the string “oauth\_”. For any given X API request, collecting these 7 values and creating a similar header will allow you to specify authorization for the request. How each value was generated is described below: **Consumer key** The oauth\_consumer\_key identifies which application is making the request. Obtain this value from the settings page for your [X app](/resources/fundamentals/developer-apps) in the [Developer Console](/resources/fundamentals/developer-portal). | | | | :------------------- | :--------------------- | | oauth\_consumer\_key | xvz1evFS4wEEPTGEFPHBog | **Nonce** The oauth\_nonce parameter is a unique token your application should generate for each unique request. X will use this value to determine whether a request has been submitted multiple times. The value for this request was generated by base64 encoding 32 bytes of random data, and stripping out all non-word characters, but any approach which produces a relatively random alphanumeric string should be OK here. | | | | :----------- | :----------------------------------------- | | oauth\_nonce | kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg | **Signature** The oauth\_signature parameter contains a value which is generated by running all of the other request parameters and two secret values through a signing algorithm. The purpose of the signature is so that X can verify that the request has not been modified in transit, verify the application sending the request, and verify that the application has authorization to interact with the user’s account. The process for calculating the oauth\_signature for this request is described in [Creating a signature](/resources/fundamentals/authentication/oauth-1-0a/creating-a-signature). | | | | :--------------- | :--------------------------- | | oauth\_signature | tnnArxj06cWHq44gCs1OSKk/jLY= | **Signature method** The oauth\_signature\_method used by X is HMAC-SHA1. This value should be used for any authorized request sent to X’s API. | | | | :----------------------- | :-------- | | oauth\_signature\_method | HMAC-SHA1 | **Timestamp** The oauth\_timestamp parameter indicates when the request was created. This value should be the number of seconds since the Unix epoch at the point the request is generated, and should be easily generated in most programming languages. X will reject requests which were created too far in the past, so it is important to keep the clock of the computer generating requests in sync with NTP. | | | | :--------------- | :--------- | | oauth\_timestamp | 1318622958 | **Token** The oauth\_token parameter typically represents a user’s permission to share access to their account with your application. There are a few authentication requests where this value is not passed or is a different form of token, but those are covered in detail in [Obtaining access tokens](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens). For most general-purpose requests, you will use what is referred to as an **access token**. You can generate a valid [access token](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) for your account on the settings page for your [X app](/resources/fundamentals/developer-apps) on the [Developer Console](/resources/fundamentals/developer-portal). | | | | :----------- | :------------------------------------------------- | | oauth\_token | 370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb | **Version** The oauth\_version parameter should always be 1.0 for any request sent to the X API. | | | | :------------- | :-- | | oauth\_version | 1.0 | #### Building the header string To build the header string, imagine writing to a string named DST. 1. Append the string “OAuth ” (including the space at the end) to DST. 2. For each key/value pair of the 7 parameters listed above: 1. [Percent encode](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) the key and append it to DST. 2. Append the equals character ‘=’ to DST. 3. Append a double quote ‘”’ to DST. 4. [Percent encode](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) the value and append it to DST. 5. Append a double quote ‘”’ to DST. 6. If there are key/value pairs remaining, append a comma ‘,’ and a space ‘ ‘ to DST. Pay particular attention to the percent encoding of the values when building this string. For example, the oauth\_signature value of tnnArxj06cWHq44gCs1OSKk/jLY= must be encoded as tnnArxj06cWHq44gCs1OSKk%2FjLY%3D. Performing these steps on the parameters collected above results in the following string: ``` OAuth oauth\_consumer\_key="xvz1evFS4wEEPTGEFPHBog", oauth\_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", oauth\_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D", oauth\_signature\_method="HMAC-SHA1", oauth\_timestamp="1318622958", oauth\_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", oauth_version="1.0" ``` This value should be set as the Authorization header for the request. # Creating a signature Source: https://docs.x.com/fundamentals/authentication/oauth-1-0a/creating-a-signature ### Creating a signature This page explains how to generate an OAuth 1.0a HMAC-SHA1 signature for an HTTP request. This signature will be suitable for passing to the X API as part of an authorized request, as described in [authorizing a request.](/resources/fundamentals/authentication/oauth-1-0a/authorizing-a-request) The request used to demonstrate signing is a POST to [https://api.x.com/1.1/statuses/update.json](https://api.x.com/1.1/statuses/update.json). The raw request looks like this: ``` POST /1.1/statuses/update.json?include_entities=true HTTP/1.1 Accept: */* Connection: close User-Agent: OAuth gem v0.4.4 Content-Type: application/x-www-form-urlencoded Content-Length: 76 Host: api.x.com status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21 ``` **Collecting the request method and URL** To produce a signature, start by determining the HTTP method and URL of the request. These two are known when creating the request, so they are easy to obtain. The request method will almost always be GET or POST for X API requests. | | | | :---------- | :--- | | HTTP Method | POST | The base URL is the URL to which the request is directed, minus any query string or hash parameters. It is important to use the correct protocol here, so make sure that the “https\://” portion of the URL matches the actual request sent to the API. | | | | :------- | :--------------------------------------------------------------------------------------- | | Base URL | [https://api.x.com/1.1/statuses/update.json](https://api.x.com/1.1/statuses/update.json) | #### Collecting parameters Next, gather all of the parameters included in the request. There are two such locations for these additional parameters - the URL (as part of the query string) and the request body. The sample request includes a single parameter in both locations: ``` POST /1.1/statuses/update.json?include_entities=true HTTP/1.1 Accept: */* Connection: close User-Agent: OAuth gem v0.4.4 Content-Type: application/x-www-form-urlencoded Content-Length: 76 Host: api.x.com status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21 ``` An HTTP request has parameters that are URL encoded, but you should collect the raw values. In addition to the request parameters, every oauth\_\* parameter needs to be included in the signature, so collect those too. Here are the parameters from [authorizing a request](/resources/fundamentals/authentication/oauth-1-0a/authorizing-a-request): | | | | :----------------------- | :------------------------------------------------- | | status | Hello Ladies + Gentlemen, a signed OAuth request! | | include\_entities | true | | oauth\_consumer\_key | xvz1evFS4wEEPTGEFPHBog | | oauth\_nonce | kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg | | oauth\_signature\_method | HMAC-SHA1 | | oauth\_timestamp | 1318622958 | | oauth\_token | 370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb | | oauth\_version | 1.0 | These values need to be encoded into a single string, which will be used later on. The process to build the string is very specific: 1. [Percent encode](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters) every key and value that will be signed. 2. Sort the list of parameters alphabetically [\[1\]](/resources/fundamentals/authentication/oauth-1-0a/creating-a-signature) by encoded key [\[2\]](/resources/fundamentals/authentication/oauth-1-0a/creating-a-signature). 3. For each key/value pair: 4. Append the encoded key to the output string. 5. Append the ‘=’ character to the output string. 6. Append the encoded value to the output string. 7. If there are more key/value pairs remaining, append a ‘&’ character to the output string.   \[1] The OAuth spec says to sort lexicographically, which is the default alphabetical sort for many libraries. \[2] In the case of two parameters with the same encoded key, the OAuth spec says to continue sorting based on value. However, X does not accept duplicate keys in API requests   **Parameter string** The following *parameter string* will be produced by repeating these steps with the parameters collected above: | status | Hello Ladies + Gentlemen, a signed OAuth request! | | :----------------------- | :------------------------------------------------- | | `include_entities` | true | | `oauth_consumer_key` | xvz1evFS4wEEPTGEFPHBog | | `oauth_nonce` | kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg | | `oauth_signature_method` | HMAC-SHA1 | | `oauth_timestamp` | 1318622958 | | `oauth_token` | 370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb | | `oauth_version` | 1.0 | #### Creating the signature base string The three values collected so far must be joined to make a single string, from which the signature will be generated. This is called the **signature base string** by the OAuth specification. To encode the HTTP method, base URL, and parameter string into a single string: 1. Convert the HTTP Method to uppercase and set the output string equal to this value. 2. Append the ‘&’ character to the output string. 3. [Percent encode](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters) the URL and append it to the output string. 4. Append the ‘&’ character to the output string. 5. [Percent encode](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters) the parameter string and append it to the output string.   This will produce the following *signature base string*: ``` POST&https%3A%2F%2Fapi.x.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521 ``` Make sure to percent encode the parameter string. The signature base string should contain exactly 2 ampersand ‘&’ characters. The percent ‘%’ characters in the parameter string should be encoded as %25 in the signature base string.   #### Getting a signing key The last pieces of data to collect are secrets which identify the [X app](/resources/fundamentals/developer-apps) making the request, and the user the request is on behalf of. It is very important to note that these values are incredibly sensitive and should never be shared with anyone. The value which identifies your app to X is called the **consumer secret** and can be found in the [Developer Console](/resources/fundamentals/developer-portal) by viewing the [app details page](/resources/fundamentals/developer-apps). This will be the same for every request your X app sends. | | | | :-------------- | :------------------------------------------ | | Consumer secret | kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw | The value which identifies the account your application is acting on behalf of is called the **OAuth token secret**. This value can be obtained in several ways, all of which are described in [obtaining access tokens](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens). | | | | :----------------- | :---------------------------------------- | | OAuth token secret | LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE | Once again, it is very important to keep these values private to your application. If you feel that your values have been compromised, regenerate your tokens (the tokens on this page have been marked as invalid for real requests). Both of these values need to be combined to form a **signing key** which will be used to generate the signature. The signing key is simply the [percent encoded](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters) token secret: Note that there are some flows, such as when obtaining a [request token](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens), where the token secret is not yet known. In this case, the signing key should consist of the [percent encoded](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters) **consumer secret** followed by an ampersand character ‘&’. | | | | :---------- | :------------------------------------------------------------------------------------- | | Signing key | kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw\&LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE | #### Calculating the signature Finally, the signature is calculated by passing the signature base string and signing key to the HMAC-SHA1 hashing algorithm. The details of the algorithm are explained as hash\_hmac function. The output of the HMAC signing function is a binary string. This needs to be base64 encoded to produce the signature string. For example, the output given the base string and signing key given on this page is 2E CF 77 84 98 99 6D 0D DA 90 5D C7 17 7C 75 07 3F 3F CD 4E. That value, when converted to base64, is the OAuth signature for this request: | | | | :-------------- | :--------------------------- | | OAuth signature | Ls93hJiZbQ3akF3HF3x1Bz8/zU4= | # OAuth Echo Source: https://docs.x.com/fundamentals/authentication/oauth-1-0a/oauth-echo ### OAuth Echo OAuth Echo is a means to securely delegate OAuth authorization with a third party while interacting with an API. There are four parties involved in this interaction: * **the User** who is using X through a particular, authorized X application * **the Consumer**, or the X application that is attempting to interact with the 3rd party media provider (e.g. the photo-sharing site) * **the Delegator**, or the 3rd party media provider * **the Service Provider** a.k.a. X itself   Essentially, prepare a request for the delegator to send to the X API on behalf of an application and a user. Add what would otherwise be a signed OAuth request into an HTTP header and ask the delegator to send that request to X after completing the intermediary operation. Here’s an example: the User wants to upload a photo. The Consumer is going to call upload on the Delegator with a POST. The POST should contain the image, but it should also contain two additional items as HTTP headers: * `x-auth-service-provider` — effectively, this is the realm that identity delegation should be sent to — in the case of X, set this to [https://api.x.com/1.1/account/verify\_credentials.json](https://api.x.com/1.1/account/verify_credentials.json). iOS5-based X integrations will add an additional application\_id parameter to this URL that will also be used to calculate the oauth\_signature used in x-verify-credentials-authorization. * `x-verify-credentials-authorization` — Consumer should create all the OAuth parameters necessary so it could call [https://api.x.com/1.1/account/verify\_credentials.json](https://api.x.com/1.1/account/verify_credentials.json) using OAuth in the HTTP header (e.g. it should look like OAuth oauth\_consumer\_key=”...”, oauth\_token=”...”, oauth\_signature\_method=”...”, oauth\_signature=”...”, oauth\_timestamp=”...”, oauth\_nonce=”...”, oauth\_version=”...” ).   Keep in mind that the entire transaction period needs to occur within an amount of time where the `oauth_timestamp` will still be valid. Alternatively, instead of sending these two parameters in the header, they could be sent in the POST as x\_auth\_service\_provider and x\_verify\_credentials\_authorization — in this case, remember to escape and include the parameters in the OAuth signature base string — similar to encoding parameters in any request. It’s best to use HTTP headers to keep the operations as separate as possible. The Delegators goal, at this point, is to verify that the User is who they say they are before it saves the media. Once the Delegator receives all the data above via its upload method, it should temporarily store the image, and then construct a call to the endpoint specified in the x-auth-service-provider header — in this case, [https://api.x.com/1.1/account/verify\_credentials.json](https://api.x.com/1.1/account/verify_credentials.json), using the same OAuth authentication header provided by the Consumer in the x-verify-credentials-authorization header.   #### OAuth Echo best practices Use the URL provided by `x-auth-service-provider` to perform the lookup, *not* a hard-coded value. Apple iOS, for example, adds an additional application\_id parameter to all OAuth requests, and its existence should be maintained at each stage of OAuth Echo. For the OAuth authorization portion, take the header value in x-verify-credentials-authorization, and place that into its own Authorization header for its call to the service provider. For good measure, confirm that the value in `x-auth-service-provider` is what it should be. * If the Service Provider returns an HTTP 200, then good. The Delegator should permanently store the image, generate a URL, and return it. * If the Service Provider doesn’t return an HTTP 200, then dump the image, and then return an error back to the Consumer. # Obtaining Access Tokens using 3-legged OAuth flow Source: https://docs.x.com/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens ### Obtaining Access Tokens using 3-legged OAuth flow To perform actions on behalf of another user, you'll need to obtain their access tokens. Access tokens specify the X account the request is made on behalf of, so for you to obtain these they will need to first grant you access. These tokens do not expire but can be revoked by the user at any time. X allows you to obtain user access tokens through the 3-legged OAuth flow, which allows your application to obtain an **access token** and access token secret by redirecting a user to X and having them authorize your application. This flow is almost identical to the flow described in [implementing Log in with X](/resources/fundamentals/authentication/guides/log-in-with-x), with two exceptions: * The [GET oauth/authorize](/resources/fundamentals/authentication/api-reference#get-oauth-authorize) endpoint is used instead of [GET oauth/authenticate](/resources/fundamentals/authentication/api-reference#get-oauth-authenticate). * The user will **always** be prompted to authorize access to your application, even if access was previously granted.   Before you get started, you will need to check your [application's](/resources/fundamentals/developer-apps) permissions and know the consumer keys and callback URL. If you don't have a callback URL or publicly accessible UI, consider using [PIN-based authorization](/resources/fundamentals/authentication/oauth-1-0a/pin-based-oauth), which is intended for applications that cannot access or embed a web browser in order to redirect the user after authorization.  The possible states for the 3-legged sign in interaction are illustrated in the following flowchart: ![](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/docs/obtaining-access-tokens.png.twimg.1920.png) #### Overview of the process At a high level, the 3-Legged OAuth process will: 1. Create a request for a consumer application to obtain a request token. 2. Have the user authenticate, and send the consumer application a request token. 3. Convert the request token into a usable user access token. **Terminology clarification** In the guide below, you may see different terms referring to the same thing. **Client credentials:** * App Key === API Key === Consumer API Key === Consumer Key === Customer Key === `oauth_consumer_key` * App Key Secret === API Secret Key === Consumer Secret === Consumer Key === Customer Key === `oauth_consumer_secret` * Callback URL === `oauth_callback`   **Temporary credentials:** * Request Token === `oauth_token` * Request Token Secret === `oauth_token_secret` * oauth\_verifier   **Token credentials:** * Access token === Token === resulting `oauth_token` * Access token secret === Token Secret === resulting `oauth_token_secret` #### Walkthrough steps **Step 1: [POST oauth/request\_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token)** Create a request for a consumer application to obtain a request token. The only unique parameter in this request is oauth\_callback, which must be a [URL encoded](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters) version of the URL you wish your user to be redirected to when they complete step 2. The remaining parameters are added by the OAuth signing process. Please note - any callback URL that you use with the [POST oauth/request\_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) endpoint will have to be configured within your [developer App's](/resources/fundamentals/developer-apps) settings in the app details page of Developer Console.   **Request includes:** `oauth_callback="https%3A%2F%2FyourCallbackUrl.com"` `oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w" ` Your app should examine the HTTP status of the response. Any value other than 200 indicates a failure. The body of the response will contain the `oauth_token`, `oauth_token_secret`, and `oauth_callback_confirmed` parameters. Your app should verify that `oauth_callback_confirmed` is true and store the other two values for the next steps.   **Response includes** `oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0` `oauth_token_secret=veNRnAWe6inFuo8o2u8SLLZLjolYDmDP7SzL0YfYI` `oauth_callback_confirmed=true` **Step 2: [GET oauth/authorize](/resources/fundamentals/authentication/api-reference#get-oauth-authorize)** Have the user authenticate, and send the consumer application a request token.   **Example URL to redirect user to:** `https://api.x.com/oauth/authorize?oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0` Upon successful authentication, your `callback_url` would receive a request containing the `oauth_token` and `oauth_verifier` parameters. Your application should verify that the token matches the request token received in step 1.   **Request from client’s redirect:** `https://yourCallbackUrl.com?oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0&oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY` **Step 3: [POST oauth/access\_token](/resources/fundamentals/authentication/api-reference#post-oauth-access-token)** Convert the request token into a usable access token. To render the request token into a usable access token, your application must make a request to the [POST oauth/access\_token](/resources/fundamentals/authentication/api-reference#post-oauth-access-token) endpoint, containing the `oauth_verifier` value obtained in step 2. The request token is also passed in the `oauth_token` portion of the header, but this will have been added by the signing process.   **Request includes:** `POST /oauth/access_token` `oauth_consumer_key=cChZNFj6T5R0TigYB9yd1w` `oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0` `oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY` A successful response contains the `oauth_token`, `oauth_token_secret` parameters. The token and token secret should be stored and used for future authenticated requests to the X API. To determine the identity of the user, use [GET account/verify\_credentials](/resources/fundamentals/authentication/api-reference).   **Response includes:** `oauth_token=7588892-kagSNqWge8gB1WwE3plnFsJHAZVfxWD7Vb57p0b4` `oauth_token_secret=PbKfYqSryyeKDWz4ebtY3o5ogNLG11WJuZBc9fQrQo` **Using these credentials for OAuth 1.0a (application-user) required requests** Now you've obtained the user access tokens; you can use them to access certain APIs such as [POST statuses/update](/x-api/posts/manage-tweets/introduction) to create Tweets on the users' behalf.   **Request includes:** `POST statuses/update.json` `oauth_consumer_key=cChZNFj6T5R0TigYB9yd1w` `oauth_token=7588892-kagSNqWge8gB1WwE3plnFsJHAZVfxWD7Vb57p0b4` #### Sample use case The standard flow is web-based and uses the 3-legged authorization OAuth flow. The screenshots outlined here are part of a sample that you can view the source of at [https://github.com/xdevplatform/twauth-web](https://github.com/xdevplatform/twauth-web). At some point in your application, you will want to redirect to X in order to authorize your application. When you redirect to X with the request token, the user will be prompted to authorize your application. Upon authorizing your application, the user will be redirected to the callback URL provided when you generated the request token. You will use this to obtain the permanent access token for this user and store it locally. # Percent encoding parameters Source: https://docs.x.com/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters ### Percent encoding parameters Parts of the X API, particularly those dealing with OAuth signatures, require strings to be encoded according to [RFC 3986, Section 2.1](http://tools.ietf.org/html/rfc3986#section-2.1). Since many implementations of URL encoding algorithms are not fully compatible with RFC 3986, bad encodings are a cause of many OAuth signature errors. For this reason, the exact signing algorithm to use is covered on this page. This page covers the URL encoding process described in [RFC 3986, Section 2.1](http://tools.ietf.org/html/rfc3986#section-2.1). We encourage you to reference that specification in case of any ambiguity or conflict with this document.   #### Encoding a string The following algorithm assumes you are encoding a string SRC by copying its values byte-by-byte to a string DST. **Step 1: While SRC contains unread bytes, read the next byte (8 bits) from SRC.** Typically, this is considered a character, but in the case of encodings where a character may be more than one byte (such as UTF-8), just read the first byte. **Step 2: Check whether the read byte matches any of the following ASCII equivalents.** The following table has been broken down into rows for legibility, but you only need to determine whether the read byte exists in the table at all, not the specific row. | Name | ASCII characters | Equivalent byte values | | :------------------ | :------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | | Digits | ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 | | Uppercase letters | ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’ | 0x41, 0x42, 0x43, 0x44, 0x45,0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51,0x52, 0x53, 0x54, 0x55, 0x56, 0x57,0x58, 0x59, 0x5A | | Lowercase letters | ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’ | 0x61, 0x62, 0x63, 0x64, 0x65,0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,0x72, 0x73, 0x74, 0x75, 0x76, 0x77,0x78, 0x79, 0x7A | | Reserved characters | ‘-‘, ‘.’, ‘\_’, ‘\~’ | 0x2D, 0x2E, 0x5F, 0x7E | **Step 2b: If the byte is not listed in the above table, continue.** Any other value must be encoded. **Step 2a: If the byte is listed in the above table, copy it into DST and go back to Step 1.** Characters listed in the above table do not need to be escaped, so you will just copy the byte directly. **Step 3: Write the character ‘%’ to DST.** The percent character ‘%’ (or 0x25 in hex and 00100101 in binary) indicates that the next two bytes will represent an encoded byte. **Step 4: Write two characters representing the uppercase ASCII-encoded hex value of the current byte to DST.** This is a bit confusing, so here is an example. Pretend the current byte is 0xE6 (11100110 in binary). This corresponds with the UTF-8 encoded value of ‘æ’. To encode this value, write the character ‘E’ (0x45, from the table above) and then the character ‘6’ (0x36) to DST. The last three characters are written should have been “%E6”. Note that if you write a letter such as A,B,C,D,E or F, you must use the uppercase character. **Step 5: Return to Step 1.** Keep going until the entirety of SRC is copied to DST.   #### Examples The following examples may be helpful to compare with the output of your own code. You should consider any differences an error. Spaces encoded as “+” characters are an example of incorrect encoding. | Original string | Encoded string | | :----------------- | :-------------------------- | | Ladies + Gentlemen | Ladies%20%2B%20Gentlemen | | An encoded string! | An%20encoded%20string%21 | | Dogs, Cats & Mice | Dogs%2C%20Cats%20%26%20Mice | | ☃ | %E2%98%83 | # PIN-based authorization Source: https://docs.x.com/fundamentals/authentication/oauth-1-0a/pin-based-oauth ### PIN-based authorization The PIN-based OAuth flow is a version of the [3-legged OAuth](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) process and is intended for applications that cannot access or embed a web browser to redirect the user after authorization. Examples of such applications would be command-line applications, embedded systems, game consoles, and certain types of mobile apps. PIN-based OAuth flow is initiated by an app in the `request_token` with the `oauth_callback` set to `oob`. The term `oob` means out-of-band OAuth.  The user still visits X to login or authorize the app, but they will not be automatically redirected to the application upon approving access. Instead, they will see a numerical PIN code, with instructions to return to the application and enter this value. **Note:** The `callback_url` within the X app settings is still required, even when using PIN-based auth.   #### Implementing the PIN-based OAuth flow The PIN-based flow is implemented in the same way as [3-legged authorization](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) (and [Sign in with X](/resources/fundamentals/authentication#log-in-with-x)), with the following differences: 1. The value for `oauth_callback` must be set to `oob` during the [POST oauth/request\_token](/resources/fundamentals/authentication/api-reference#post-oauth-request-token) call. 2. After the user is sent to X to authorize your app using either a [GET oauth/authenticate](/resources/fundamentals/authentication/api-reference#get-oauth-authenticate) or [GET oauth/authorize URL](/resources/fundamentals/authentication/api-reference#get-oauth-authorize), they will not be redirected to your `callback_url`, instead they will see a screen with a X generated \~7 digit PIN with directions to enter the PIN into your applications name. 3. The user enters this PIN into your application, and your application uses the PIN number as the `oauth_verifier` in the [POST oauth/access\_token](/resources/fundamentals/authentication/api-reference#post-oauth-access-token) to obtain an access\_token. **Note:** PIN numbers are not reusable, and the `access_token` obtained should be used for application-user requests. # App only authentication and OAuth 2.0 Bearer Token Source: https://docs.x.com/fundamentals/authentication/oauth-2-0/application-only ### App only authentication and OAuth 2.0 Bearer Token X offers applications the ability to issue authenticated requests on behalf of the application itself, as opposed to on behalf of a specific user. X's implementation is based on the [Client Credentials Grant](http://tools.ietf.org/html/rfc6749#section-4.4) flow of the [OAuth 2 specification](http://tools.ietf.org/html/rfc6749). Application-only authentication doesn't include any user-context and is a form of authentication where an application makes API requests on its own behalf. This method is for developers that just need read-only access to public information.  You can do application-only authentication using your apps consumer API keys, or by using a App only Access Token (Bearer Token). This means that the only requests you can make to a X API must not require an authenticated user. With application-only authentication, you can perform actions such as: * Pull user timelines * Access friends and followers of any account * Access lists resources * Search Tweets Please note that only [OAuth 1.0a](/resources/fundamentals/authentication/oauth-1-0a/api-key-and-secret) or [OAuth 2.0 Authorization Code Flow](/resources/fundamentals/authentication/oauth-2-0/authorization-code) with PKCE is required to issues requests on behalf of users. The [API reference](/resources/fundamentals/authentication/api-reference) page describes the authentication method required to use an API. You will need user-authentication, user-context, with an [access token](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) to perform the following: * Post Tweets or other resources * Search for users * Use any geo endpoint * Access Direct Messages or account credentials * Retrieve user's email addresses #### Auth Flow To use this method, you need to use a [App only Access Token](/resources/fundamentals/authentication/oauth-2-0/application-only)(also known as [Bearer Token](/resources/fundamentals/authentication/oauth-2-0/bearer-tokens)). You can generate an App only Access Token (Bearer Token) by passing your consumer key and secret through the [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token) endpoint.  The application-only auth flow follows these steps: * An application encodes its consumer key and secret into a specially encoded set of credentials. * An application makes a request to the [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token) endpoint to exchange these credentials for an [App only Access Token](/resources/fundamentals/authentication/oauth-2-0/application-only). * When accessing the REST API, the application uses the App only Access Token to authenticate. Because there is no need to sign a request, this approach is much simpler than the standard OAuth 1.0a model. #### About application-only auth **Tokens are passwords** Keep in mind that the consumer key & secret and the App only Access Token (Bearer Token) itself grant access to make requests on behalf of an application. These values should be considered as sensitive as passwords, and must not be shared or distributed to untrusted parties. **SSL required** All requests (both to obtain and use the tokens) *must* use HTTPS endpoints. Follow the best practices detailed in [Connecting to X API using TLS](/resources/fundamentals/authentication/guides/tls) — peers should **always** be verified. **No user-context** When issuing requests using application-only auth, there is no concept of a "current user". Therefore, endpoints such as [POST statuses/update](/x-api/posts/creation-of-a-post) will not function with application-only auth. See [using OAuth](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens) for more information for issuing requests on behalf of a user. **Rate limiting** Applications have two kinds of rate limiting pools. Requests made on behalf of users with access tokens, also known as user-context, depletes from a different rate limiting context than that used in application-only authentication. So, in other words, requests made on behalf of users will not deplete from the rate limits available through app-only auth, and requests made through app-only auth will not deplete from the rate limits used in user-based auth. Read more about [API Rate Limiting](/x-api/fundamentals/rate-limits) and [review the limits](https://developer.x.com/en/portal/products). #### Issuing application-only requests **Step 1: Encode consumer key and secret** The steps to encode an application’s consumer key and secret into a set of credentials to obtain a Bearer Token are: 1. URL encode the consumer key and consumer secret according to [RFC 1738](http://www.ietf.org/rfc/rfc1738.txt). Note that at the time of writing, this will not actually change the consumer key and secret, but this step should still be performed in case the format of those values changes in the future. 2. Concatenate the encoded consumer key, a colon character ":", and the encoded consumer secret into a single string. 3. [Base64 encode](http://en.wikipedia.org/wiki/Base64) the string from the previous step. Below are example values showing the result of this algorithm. Note that the consumer secret used in this page is for test purposes and will not work for real requests. | | | | :------------------------------------------------------------ | :------------------------------------------------------------------------------------------ | | Consumer key | xvz1evFS4wEEPTGEFPHBog | | Consumer secret | L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg | | RFC 1738 encoded consumer

key (does not change) | xvz1evFS4wEEPTGEFPHBog | | RFC 1738 encoded consumer

secret (does not change) | L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg | | Bearer Token credentials | xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg | | Base64 encoded Bearer Token credentials | :: eHZ6MWV2RlM0d0VFUFRHRUZQSEJvZzpMOHFxOVBaeVJnNmllS0dFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw== | **Step 2: Obtain an App only Access Token (Bearer Token)** The value calculated in step 1 must be exchanged for an App only Access Token by issuing a request to [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token): * The request must be an HTTP POST request. * The request must include an `Authorization` header with the value of `Basic .` * The request must include a `Content-Type` header with the value of `application/x-www-form-urlencoded;charset=UTF-8.` * The body of the request must be `grant_type=client_credentials`. **Example request (Authorization header has been wrapped):** ```json theme={null} POST /oauth2/token HTTP/1.1 Host: api.x.com User-Agent: My X App v1.0.23 Authorization: Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEJvZzpMOHFxOVBaeVJn NmllS0dFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw== Content-Type: application/x-www-form-urlencoded;charset=UTF-8 Content-Length: 29 Accept-Encoding: gzip grant\_type=client\_credentials ``` If the request was formatted correctly, the server would respond with a JSON-encoded payload: **Example response:** ```json theme={null} HTTP/1.1 200 OK Status: 200 OK Content-Type: application/json; charset=utf-8 ... Content-Encoding: gzip Content-Length: 140 {"token\_type":"bearer","access\_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"} ``` Applications should verify that the value associated with the `token_type` key of the returned object is `bearer`. The value associated with the `access_token` key is the App only Access Token (Bearer Token). Note that one App only Access Token is valid for an application at a time. Issuing another request with the same credentials to `/oauth2/token` will return the same token until it is invalidated. **Step 3: Authenticate API requests with the App only Access Token (Bearer Token)** The App only Access Token (Bearer Token) may be used to issue requests to API endpoints that support application-only auth. To use the App Access Token, construct a normal HTTPS request and include an `Authorization` header with the value of `Bearer . Signing is not required.` **Example request (Authorization header has been wrapped):** ``` GET /1.1/statuses/user\_timeline.json?count=100&screen\_name=twitterapi HTTP/1.1 Host: api.x.com User-Agent: My X App v1.0.23 Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAA AAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Accept-Encoding: gzip ``` **Invalidating an App only Access Token (Bearer Token)** Should a App only Access Token become compromised or need to be invalidated for any reason, issue a call to [POST oauth2/invalidate\_token](/resources/fundamentals/authentication/api-reference#post-oauth2-invalidate-token). **Example request (Authorization header has been wrapped):** ```bash theme={null} POST /oauth2/invalidate_token HTTP/1.1 Authorization: Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEJvZzpMOHFxOVBaeVJn NmllS0dFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw== User-Agent: My X App v1.0.23 Host: api.x.com Accept: */* Content-Length: 119 Content-Type: application/x-www-form-urlencoded access_token=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ``` **Example response:** ```json theme={null} HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Content-Length: 127 ... {"access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"} ``` #### Common error cases This section describes some common mistakes involved in the negotiation and use of Bearer Tokens. Be aware that not all possible error responses are covered here - be observant of unhandled error codes and responses. **Invalid requests to obtain or revoke an App only Access Token** Attempts to: * Obtain a App only Access Token (Bearer Token) with an invalid request (for example, leaving out `grant_type=client_credentials`). * Obtain or revoke an App only Access Token (Bearer Token) with incorrect or expired app credentials. * Invalidate an incorrect or revoked App only Access Token (Bearer Token). * Obtain an App only Access Token (Bearer Token) too frequently in a short period of time. Will result in: ```json theme={null} HTTP/1.1 403 Forbidden Content-Length: 105 Content-Type: application/json; charset=utf-8 ... {"errors":\[{"code":99,"label":"authenticity\_token\_error","message":"Unable to verify your credentials"}\]} ``` #### API request contains invalid App only Access Token (Bearer Token) Using an incorrect or revoked Access Token to make API requests will result in: ```json theme={null} HTTP/1.1 401 Unauthorized Content-Type: application/json; charset=utf-8 Content-Length: 61 ... {"errors":\[{"message":"Invalid or expired token","code":89}\]} ``` #### App only Access Token (Bearer Token) used on endpoint which doesn't support application-only auth Requesting an endpoint which requires a user context (such as `statuses/home_timeline`) with a n App only Access Token (Bearer Token) will produce: ```json theme={null} HTTP/1.1 403 Forbidden Content-Type: application/json; charset=utf-8 Content-Length: 91 ... {"errors":\[{"message":"Your credentials do not allow access to this resource","code":220}\]} ``` # OAuth 2.0 Authorization Code Flow with PKCE Source: https://docs.x.com/fundamentals/authentication/oauth-2-0/authorization-code ### OAuth 2.0 Authorization Code Flow with PKCE #### Introduction OAuth 2.0 is an industry-standard authorization protocol that allows for greater control over an application’s scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user.  To enable OAuth 2.0 in your App, you must enable it in your’s App’s authentication settings found in the App settings section of the Developer Console. #### How long will my credentials stay valid?   By default, the access token you create through the Authorization Code Flow with PKCE will only stay valid for two hours unless you’ve used the `offline.access` scope. #### Refresh tokens Refresh tokens allow an application to obtain a new access token without prompting the user via the refresh token flow. If the scope `offline.access` is applied an OAuth 2.0 refresh token will be issued. With this refresh token, you obtain an access token. If this scope is not passed, we will not generate a refresh token. An example of the request you would make to use a refresh token to obtain a new access token is as follows: ```bash theme={null} POST 'https://api.x.com/2/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'refresh_token=bWRWa3gzdnk3WHRGU1o0bmRRcTJ5VUxWX1lZTDdJSUtmaWcxbTVxdEFXcW5tOjE2MjIxNDc3NDM5MTQ6MToxOnJ0OjE' \ --data-urlencode 'grant_type=refresh_token' \ --data-urlencode 'client_id=rG9n6402A3dbUJKzXTNX4oWHJ ``` #### App settings You can select your App’s authentication settings to be OAuth 1.0a or OAuth 2.0. You can also enable an App to access both OAuth 1.0a and OAuth 2.0. OAuth 2.0 can be used with the X API v2 only. If you have selected OAuth 2.0 you will be able to see a Client ID in your App’s Keys and Tokens section.  #### Confidential Clients [Confidential clients](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) can hold credentials in a secure way without exposing them to unauthorized parties and securely authenticate with the authorization server they keep your client secret safe. Public clients as they’re usually running in a browser or on a mobile device and are unable to use your client secrets. If you select a type of App that is a confidential client, you will be provided with a client secret.  If you selected a type of client that is a confidential client in the Developer Console, you will also be able to see a Client Secret. Your options are Native App, Single page App, Web App, Automated App, or bot. Native App and Single page Apps are public clients and Web App and Automated App or bots are confidential clients. You don’t need client id for confidential clients with a valid Authorization Header. You still are required to include Client Id in the body for the requests with a public client.  #### Scopes Scopes allow you to set granular access for your App so that your App only has the permissions that it needs. To learn more about what scopes map to what endpoints, view our [authentication mapping guide](/resources/fundamentals/authentication/guides/v2-authentication-mapping). | | | | :------------------- | :----------------------------------------------------------------------------------------------------------- | | **Scope** | **Description** | | tweet.read | All the Tweets you can view, including Tweets from protected accounts. | | tweet.write | Tweet and Retweet for you. | | tweet.moderate.write | Hide and unhide replies to your Tweets. | | users.email | Email from an authenticated user. | | users.read | Any account you can view, including protected accounts. | | follows.read | People who follow you and people who you follow. | | follows.write | Follow and unfollow people for you. | | offline.access | Stay connected to your account until you revoke access. | | space.read | All the Spaces you can view. | | mute.read | Accounts you’ve muted. | | mute.write | Mute and unmute accounts for you. | | like.read | Tweets you’ve liked and likes you can view. | | like.write | Like and un-like Tweets for you. | | list.read | Lists, list members, and list followers of lists you’ve created or are a member of, including private lists. | | list.write | Create and manage Lists for you. | | block.read | Accounts you’ve blocked. | | 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 For the most part, the rate limits are the same as they are authenticating with OAuth 1.0a, with the exception of Tweets lookup and Users lookup. We are increasing the per-App limit from 300 to 900 requests per 15 minutes while using OAuth 2.0 for Tweet lookup and user lookup. To learn more be sure to check out our [documentation on rate limits](/resources/fundamentals/rate-limits). #### Grant types We only provide [authorization code](https://oauth.net/2/grant-types/authorization-code/) with [PKCE](https://oauth.net/2/pkce/) and [refresh token](https://oauth.net/2/grant-types/refresh-token/) as the supported [grant types](https://oauth.net/2/grant-types/) for this initial launch. We may provide more grant types in the future. #### OAuth 2.0 Flow OAuth 2.0 uses a similar flow to what we are currently using for OAuth 1.0a. You can check out a diagram and detailed explanation in our [documentation on this subject](/resources/fundamentals/authentication/oauth-1-0a/obtaining-user-access-tokens).  #### Glossary | | | | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Term** | **Description** | | Grant types | The OAuth framework specifies several grant types for different use cases and a framework for creating new grant types. Examples include authorization code, client credentials, device code, and refresh token. | | Confidential client | Clients are applications that can securely authenticate with the authorization server, for example, keeping their registered client secret safe. | | Public client | Clients cannot use registered client secrets, such as applications running in a browser or mobile device. | | Authorization code flow | Used by both confidential and public clients to exchange an authorization code for an access token. | | PKCE | An extension to the authorization code flow to prevent several attacks and to be able to perform the OAuth exchange from public clients securely. | | Client ID | Can be found in the keys and tokens section of the Developer Console under the header "Client ID." If you don't see this, please get in touch with our team directly. The Client ID will be needed to generate the authorize URL. | | Redirect URI | Your callback URL. You will need to have [exact match validation](https://datatracker.ietf.org/doc/html/rfc6749#section-10.6). | | Authorization code | This allows an application to hit APIs on behalf of users. Known as the auth\_code. The auth\_code has a time limit of 30 seconds once the App owner receives an approved auth\_code from the user. You will have to exchange it with an access token within 30 seconds, or the auth\_code will expire. | | Access token | Access tokens are the token that applications use to make API requests on behalf of a user. | | Refresh token | Allows an application to obtain a new access token without prompting the user via the refresh token flow. | | Client Secret | If you have selected an App type that is a confidential client you will be provided with a “Client Secret” under “Client ID” in your App’s keys and tokens section. | #### Parameters To construct an OAuth 2.0 authorize URL, you will need to ensure you have the following parameters in the authorization URL.  | | | | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Parameter** | **Description** | | response\_type | You will need to specify that this is a code with the word “code”. | | client\_id | Can be found in the Developer Console under the header "Client ID". | | redirect\_uri | Your callback URL. This value must correspond to one of the Callback URLs defined in your App’s settings. For OAuth 2.0, you will need to have [exact match validation](https://datatracker.ietf.org/doc/html/rfc6749#section-10.6) for your callback URL. | | state | A random string you provide to verify against [CSRF attacks](https://auth0.com/docs/protocols/state-parameters).  The length of this string can be up to 500 characters. | | code\_challenge | A [PKCE](https://www.oauth.com/oauth2-servers/pkce/authorization-request/) parameter, a random secret for each request you make. | | code\_challenge\_method | Specifies the method you are using to make a request (S256 OR plain). | #### Authorize URL  With OAuth 2.0, you create an authorize URL, which you can use to allow a user to authenticate via an authentication flow, similar to “Sign In” with X.  An example of the URL you are creating is as follows:  ``` https://x.com/i/oauth2/authorize?response_type=code&client_id=M1M5R3BMVy13QmpScXkzTUt5OE46MTpjaQ&redirect_uri=https://www.example.com&scope=tweet.read%20users.read%20account.follows.read%20account.follows.write&state=state&code_challenge=challenge&code_challenge_method=plain ``` You will need to have the proper encoding for this URL to work, be sure to check out our documentation on the [percent encoding](/resources/fundamentals/authentication/oauth-1-0a/percent-encoding-parameters). # Using and generating an app-only Bearer Token Source: https://docs.x.com/fundamentals/authentication/oauth-2-0/bearer-tokens ### Using and generating an app-only Bearer Token A bearer token allows developers to have a more secure point of entry for using the X APIs, and are one of the core features of OAuth 2.0.  Authentication, which uses a Bearer Token, is also known as application-only authentication. A Bearer Token is a byte array of unspecified format that you generate using a script like a curl command. You can also obtain a Bearer Token from the Developer Console inside the keys and tokens section of your App's settings. More information about this feature can be found on [OAuth's official documentation](https://oauth.net/2/bearer-tokens/). #### When are they used? The products that require the use of a Bearer Token are as follows: * [Engagement API](/x-api/enterprise-gnip-2.0/fundamentals/engagement-api) * [Account Activity API](/x-api/enterprise-gnip-2.0/fundamentals/account-activity) * Other APIs that utilize OAuth 2.0 Bearer Token authentication such as v2 and Labs endpoints. #### Prerequisites You will need to [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info) and to have created a [X App](/resources/fundamentals/developer-apps). Once you have those, you'll also need to obtain the API keys found in the [Developer Console](/resources/fundamentals/developer-portal). Follow the steps below: 1. Login to your X account on developer.x.com. 2. Navigate to the [X App dashboard](https://developer.x.com/content/developer-twitter/en/apps) and open the X App for which you would like to generate access tokens. 3. Navigate to the "keys and tokens" page. 4. You'll find the API keys, user Access Tokens, and Bearer Token on this page. ### How to generate a Bearer Token You can find the Bearer Token for your App with the rest of your "Keys and Tokens". Copy the following cURL request into your command line after making changes to the following consumer API keys previously obtained from your [X App](/resources/fundamentals/developer-apps). Note that the consumer API keys used on this page have been decommissioned and will not work for real requests. * **API key** `` e.g.`xvz1evFS4wEEPTGEFPHBog` * **API secret key** `` e.g. `L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg` ```bash theme={null} curl -u "$API_KEY:$API_SECRET_KEY" \ --data 'grant_type=client_credentials' \ 'https://api.x.com/oauth2/token' ``` Here's an example of how the curl request should look with your API keys entered: ```bash theme={null} curl -u 'xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg' \ --data 'grant_type=client_credentials' \ 'https://api.x.com/oauth2/token' ``` Here is what the response would look like. Note that this is a decommissioned Bearer Token: `{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAMLheAAAAAAA0%2BuSeid%2BULvsea4JtiGRiSDSJSI%3DEUifiRBkKG5E2XzMDjRfl76ZC9Ub0wnz4XsNiRVBChTYbJcE3F"}` Our Bearer Token used to authenticate to resources with OAuth 2.0 would be: `AAAAAAAAAAAAAAAAAAAAAMLheAAAAAAA0%2BuSeid%2BULvsea4JtiGRiSDSJSI%3DEUifiRBkKG5E2XzMDjRfl76ZC9Ub0wnz4XsNiRVBChTYbJcE3F` # OAuth 2.0 Source: https://docs.x.com/fundamentals/authentication/oauth-2-0/overview ### Bearer Token (also known as app-only) OAuth 2.0 Bearer Token authenticates requests on behalf of your [developer App](/resources/fundamentals/developer-apps). As this method is specific to the App, it does not involve any users. This method is typically for developers that need read-only access to public information.  This authentication method requires for you to pass a Bearer Token with your request, which you can generate within the Keys and tokens section of your developer Apps. Here is an example of what a request looks like with a fake Bearer Token: ```json theme={null} curl "https://api.x.com/2/tweets?ids=1261326399320715264,1278347468690915330" \ -H "Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAFnz2wAAAAAAxTmQbp%2BIHDtAhTBbyNJon%2BA72K4%3DeIaigY0QBrv6Rp8KZQQLOTpo9ubw5Jt?WRE8avbi" ``` API calls using app-only authentication are [rate limited](/resources/fundamentals/rate-limits) per endpoint at the App level. To use this method, you'll need a Bearer Token, which you can generate by passing your API Key and Secret through the [POST oauth2/token](/resources/fundamentals/authentication/api-reference#post-oauth2-token) endpoint, or by generating it in the "keys and token" section of your App settings in the [Developer Console](/resources/fundamentals/developer-portal). If you'd like to revoke a Bearer Token, you can use the [POST oauth2/invalidate\_token](/resources/fundamentals/authentication/api-reference#post-oauth2-invalidate-token) endpoint, or click where it says "revoke" next to the Bearer Token in the "keys and tokens" section of your App settings. ### OAuth 2.0 Authorization Code Flow with PKCE OAuth 2.0 Authorization Code Flow with PKCE allows you to authenticate on behalf of another user with have more control over an application’s scopes and improves authorization flows across multiple devices. In other words, developers building applications for people on X will have more control over the information their App requests from its users, so that you only have to ask your end-users for the data and information you need. This modern authorization protocol will allow you to present your end-users with a more streamlined consent flow for authorizing your app, which only displays the specific scopes you have requested from them. Not only does this reduce your data burden, but it may also lead to increased trust from end-users. # How to connect to endpoints using OAuth 2.0 Authorization Code Flow with PKCE Source: https://docs.x.com/fundamentals/authentication/oauth-2-0/user-access-token ### How to connect to endpoints using OAuth 2.0 Authorization Code Flow with PKCE #### How to connect to the endpoints To authenticate your users, your App will need to implement an authorization flow. This authorization flow lets you direct your users to an authorization dialog on X. From there, the primary X experience will show the authorization dialog and handle the authorization on behalf of your App. Your users will be able to authorize your App or decline permission. After the user makes their choice, X will redirect the user to your App, where you can exchange the authorization code for an access token (if the user authorized your App), or handle a rejection (if the user did not authorize your App). #### Working with confidential clients If you are working with confidential clients, you will need to use a [basic authentication](https://datatracker.ietf.org/doc/html/rfc2617#section-2) scheme for generating an authorization header with base64 encoding while making requests to the token endpoints. The `userid` and `password` are separated by a single colon (":") character within a base64 encoded string in the credentials. An example would look like this: `-header 'Authorization: Basic V1ROclFTMTRiVWhwTWw4M2FVNWFkVGQyTldNNk1UcGphUTotUm9LeDN4NThKQThTbTlKSXQyZm1BanEzcTVHWC1icVozdmpKeFNlR3NkbUd0WEViUA=='` If the user agent wishes to send the Client ID "Aladdin" and password "open sesame,” it would use the following header field: `Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==` To create the basic authorization header you will need to base64 encoding on your Client ID and Client Secret which can be obtained from your App’s “Keys and Tokens” page inside of the [Developer Console.](https://developer.x.com/en/portal/dashboard) #### Steps to connect using OAuth 2.0 **Step 1: Construct an Authorize URL** Your App will need to build an authorize URL to X, indicating the scopes your App needs to authorize. For example, if your App needs to lookup Tweets, users and to manage follows, it should request the following scopes: `tweet.read%20users.read%20follows.read%20follows.write` The URL will also contain the `code_challenge` and state parameters, in addition to the other required parameters. In production you should use a random string for the `code_challenge`. **Step 2: GET oauth2/authorize** Have the user authenticate and send the application an authorization code. If you have enabled OAuth 2.0 for your App you can find your Client ID inside your App’s “Keys and Tokens” page. An example URL to redirect the user to would look like this: ``` https://x.com/i/oauth2/authorize?response_type=code&client_id=M1M5R3BMVy13QmpScXkzTUt5OE46MTpjaQ&redirect_uri=https://www.example.com&scope=tweet.read%20users.read%20follows.read%20follows.write&state=state&code_challenge=challenge&code_challenge_method=plain ``` An example URL with offline\_access would look like this: ``` https://x.com/i/oauth2/authorize?response_type=code&client_id=M1M5R3BMVy13QmpScXkzTUt5OE46MTpjaQ&redirect_uri=https://www.example.com&scope=tweet.read%20users.read%20follows.read%20offline.access&state=state&code_challenge=challenge&code_challenge_method=plain ``` Upon successful authentication, the redirect\_uri  you would receive a request containing the auth\_code parameter. Your application should verify the state parameter. An example request from client’s redirect would be: ``` https://www.example.com/?state=state&code=VGNibzFWSWREZm01bjN1N3dicWlNUG1oa2xRRVNNdmVHelJGY2hPWGxNd2dxOjE2MjIxNjA4MjU4MjU6MToxOmFjOjE ``` **Step 3: POST oauth2/token - Access Token** At this point, you can use the authorization code to create an access token and refresh token (only if `offline.access` scope is requested). You can make a POST request to the following endpoint: ``` https://api.x.com/2/oauth2/token ``` You will need to pass in the `Content-Type` of `application/x-www-form-urlencoded` via a header.  Additionally, you should have in your request: `code`, `grant_type`, `client_id` and `redirect_uri`, and the `code_verifier`. Here is an example token request for a public client: ```json theme={null} curl --location --request POST 'https://api.x.com/2/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'code=VGNibzFWSWREZm01bjN1N3dicWlNUG1oa2xRRVNNdmVHelJGY2hPWGxNd2dxOjE2MjIxNjA4MjU4MjU6MToxOmFjOjE' \ --data-urlencode 'grant_type=authorization_code' \ --data-urlencode 'client_id=rG9n6402A3dbUJKzXTNX4oWHJ' \ --data-urlencode 'redirect_uri=https://www.example.com' \ --data-urlencode 'code_verifier=challenge' ``` Here is an example using a confidential client:  ```json theme={null} curl --location --request POST 'https://api.x.com/2/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Basic V1ROclFTMTRiVWhwTWw4M2FVNWFkVGQyTldNNk1UcGphUTotUm9LeDN4NThKQThTbTlKSXQyZm1BanEzcTVHWC1icVozdmpKeFNlR3NkbUd0WEViUA=='\ --data-urlencode 'code=VGNibzFWSWREZm01bjN1N3dicWlNUG1oa2xRRVNNdmVHelJGY2hPWGxNd2dxOjE2MjIxNjA4MjU4MjU6MToxOmFjOjE' \ --data-urlencode 'grant_type=authorization_code' \ --data-urlencode 'redirect_uri=https://www.example.com' \ --data-urlencode 'code_verifier=challenge' ``` **Step 4: Connect to the APIs** You are now ready to connect to the endpoints using OAuth 2.0. To do so, you will request the API as you would using [Bearer Token authentication](/resources/fundamentals/authentication/oauth-2-0/application-only). Instead of passing your Bearer Token, you’ll want to use the access token you generated in the last step. As a response, you should see the appropriate payload corresponding to the endpoint you are requesting. This request is the same for both public and confidential clients.  An example of the request you would make would look as follows: ```json theme={null} curl --location --request GET 'https://api.x.com/2/tweets?ids=1261326399320715264,1278347468690915330' \ --header 'Authorization: Bearer Q0Mzb0VhZ0V5dmNXSTEyNER2MFNfVW50RzdXdTN6STFxQlVkTGhTc1lCdlBiOjE2MjIxNDc3NDM5MTQ6MToxOmF0OjE' ``` **Step 5: POST oauth2/token - refresh token** A refresh token allows an application to obtain a new access token without prompting the user. You can create a refresh token by making a POST request to the following endpoint: [https://api.x.com/2/oauth2/token](https://api.x.com/2/oauth2/token) You will need to add in the `Content-Type` of `application/x-www-form-urlencoded` via a header. In addition, you will also need to pass in your refresh\_token, set your grant\_type to be a `refresh_token`, and define your `client_id`. This request will work for public clients: ```json theme={null} POST 'https://api.x.com/2/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'refresh_token=bWRWa3gzdnk3WHRGU1o0bmRRcTJ5VUxWX1lZTDdJSUtmaWcxbTVxdEFXcW5tOjE2MjIxNDc3NDM5MTQ6MToxOnJ0OjE' \ --data-urlencode 'grant_type=refresh_token' \ --data-urlencode 'client_id=rG9n6402A3dbUJKzXTNX4oWHJ' ``` Here is an example of one for confidential clients: ```json theme={null} POST 'https://api.x.com/2/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Basic V1ROclFTMTRiVWhwTWw4M2FVNWFkVGQyTldNNk1UcGphUTotUm9LeDN4NThKQThTbTlKSXQyZm1BanEzcTVHWC1icVozdmpKeFNlR3NkbUd0WEViUA=='\ --data-urlencode 'refresh_token=bWRWa3gzdnk3WHRGU1o0bmRRcTJ5VUxWX1lZTDdJSUtmaWcxbTVxdEFXcW5tOjE2MjIxNDc3NDM5MTQ6MToxOnJ0OjE'\ --data-urlencode 'grant_type=refresh_token' ``` **Step 6: POST oauth2/revoke - Revoke Token** A revoke token invalidates an access token or refresh token. This is used to enable a "log out" feature in clients, allowing you to clean up any security credentials associated with the authorization flow that may no longer be necessary. The revoke token is for an App to revoke a token and not a user. You can create a revoke token request by making a POST request to the following URL if the App wants to programmatically revoke the access given to it: ``` https://api.x.com/2/oauth2/revoke ``` You will need to pass in the `Content-Type` of `application/x-www-form-urlencoded` via a header, your token, and your client\_id. In some cases, a user may wish to revoke access given to an App, they can revoke access by visiting the [connected Apps page](https://x.com/settings/connected_apps). ```bash theme={null} curl --location --request POST 'https://api.x.com/2/oauth2/revoke' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=Q0Mzb0VhZ0V5dmNXSTEyNER2MFNfVW50RzdXdTN6STFxQlVkTGhTc1lCdlBiOjE2MjIxNDc3NDM5MTQ6MToxOmF0OjE' \ --data-urlencode 'client_id=rG9n6402A3dbUJKzXTNX4oWHJ' ``` This request will work for confidential clients: ```bash theme={null} curl --location --request POST 'https://api.x.com/2/oauth2/revoke' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Basic V1ROclFTMTRiVWhwTWw4M2FVNWFkVGQyTldNNk1UcGphUTotUm9LeDN4NThKQThTbTlKSXQyZm1BanEzcTVHWC1icVozdmpKeFNlR3NkbUd0WEViUA=='\ --data-urlencode 'token=Q0Mzb0VhZ0V5dmNXSTEyNER2MFNfVW50RzdXdTN6STFxQlVkTGhTc1lCdlBiOjE2MjIxNDc3NDM5MTQ6MToxOmF0OjE' ``` # Authentication Source: https://docs.x.com/fundamentals/authentication/overview X APIs handle enormous amounts of data. The way we ensure this data is secured for developers and users alike is through authentication. There are a few methods for authentication, each listed below. Most developers will not need to deal with the complexities surrounding authentication since client libraries automatically handle these difficulties. You can find a list of available client libraries on our [Tools and libraries](/resources/tools-and-libraries) page. ## Authentication methods OAuth 1.0a allows an authorized X developer App to access private account information or perform a X action on behalf of a X account.
[**Learn More**](/resources/fundamentals/authentication/oauth-1-0a/api-key-and-secret)
App only Access Token allows a X developer app to access information publicly available on X.
[**Learn More**](/resources/fundamentals/authentication/oauth-2-0/overview)
Many of X's enterprise APIs require the use of HTTP Basic Authentication.
[**Learn More**](/resources/fundamentals/authentication/basic-auth)
OAuth 2.0 User Context allows you to authenticate on behalf of another account with greater control over an application's scope, and authorization flows across multiple devices.
[**Learn More**](/resources/fundamentals/authentication/oauth-2-0/authorization-code)
**Note:** Your App's API Keys and App only Access Token, as well as your personal Access Token and Access Token Secret can be obtained from the [X developer Apps](/resources/fundamentals/developer-apps) section found in the [Developer Console](/resources/fundamentals/developer-portal). **If you would like to make requests on behalf of another user**, you will need to generate a separate set of Access Tokens for that user using the [3-legged OAuth flow](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens), and pass that user's tokens with your OAuth 1.0a User Context or OAuth 2.0 user context requests. ## Additional resources Learn how to generate tokens and authenticate requests using our integration guides. Review our reference guides for our authentication endpoints. Make sure you protect yourself and understand the best practices for storing your keys and tokens. Question? Visit our FAQs. # Counting Characters Source: https://docs.x.com/fundamentals/counting-characters How X counts characters in posts Posts on X can contain up to **280 characters**. However, not all characters count equally—emojis, URLs, and certain Unicode ranges have special counting rules. *** ## Character weights X uses a weighted character counting system. Most characters count as 1, but some count as 2: | Character type | Weight | Max characters | | :--------------------------------- | :---------- | :------------- | | Latin, punctuation, common symbols | 1 | 280 | | Emojis | 2 | 140 emojis | | CJK (Chinese, Japanese, Korean) | 2 | 140 characters | | Other Unicode | 2 (default) | Varies | Use the open-source [twitter-text](https://github.com/twitter/twitter-text) library to accurately count characters in your app. *** ## Emoji counting All emojis count as **2 characters**, regardless of complexity: | Emoji | Display | Character count | Unicode | | :---------- | :---------------- | :-------------- | :---------------- | | 👾 | Single emoji | 2 | U+1F47E | | 🙋🏽 | With skin tone | 2 | 🙋 + 🏽 modifier | | 👨‍🎤 | Combined with ZWJ | 2 | 👨 + ZWJ + 🎤 | | 👨‍👩‍👧‍👦 | Family sequence | 2 | 4 emojis + 3 ZWJs | Zero-width joiners (ZWJ) combine emojis visually but don't add to the count. *** ## URL handling All URLs are wrapped with `t.co` shortener and count as **23 characters**, regardless of the original length: ``` https://example.com → 23 characters https://example.com/very/long/path → 23 characters ``` This applies to any valid URL detected in post text. *** ## Special cases | Content | Counting rule | | :----------------------- | :----------------------------------------------------------- | | **@mentions in replies** | Auto-populated @mentions at the start of replies don't count | | **New @mentions** | @mentions you add manually count normally | | **Media** | Attached media (via official clients) counts as 0 characters | | **Hashtags** | Count normally (# + tag text) | *** ## Text encoding The X API requires **UTF-8** encoding. Character length is calculated using Unicode Normalization Form C (NFC). Example with `café`: | Form | Bytes | Characters | | :--------------- | :---------- | :--------- | | NFC (composed) | `c a f é` | 4 | | NFD (decomposed) | `c a f e ́` | 5 | X normalizes to NFC, so both encode to 4 characters. *** ## Implementation Use the official [twitter-text](https://github.com/twitter/twitter-text) library for accurate character counting: ```javascript theme={null} import { parseTweet } from 'twitter-text'; const result = parseTweet('Hello, world! 👋'); console.log(result.weightedLength); // 16 console.log(result.valid); // true ``` ```python theme={null} from twitter_text import parse_tweet result = parse_tweet('Hello, world! 👋') print(result.weightedLength) # 16 print(result.valid) # True ``` The library handles all edge cases including emoji sequences, URL detection, and Unicode normalization. *** ## Resources Official open-source library for text parsing. Character weight definitions and Unicode ranges. # Apps Source: https://docs.x.com/fundamentals/developer-apps Create and configure apps to access the X API Apps are containers for your API credentials. Each app has its own keys, tokens, and settings. *** ## App credentials When you create an app, you can generate these credentials: | Credential | Use case | | :------------------------ | :--------------------------------------------------------------------------- | | **API Key & Secret** | Authenticate with OAuth 1.0a. Used to sign requests or generate user tokens. | | **Access Token & Secret** | Make requests on behalf of your own account (OAuth 1.0a). | | **Client ID & Secret** | Authenticate with OAuth 2.0. Used for authorization code flow. | | **Bearer Token** | App-only authentication for public data endpoints. | Choose **OAuth 2.0** for new projects. It offers fine-grained scopes and is required for X API v2 user-context endpoints. *** ## Creating an app Go to [console.x.com](https://console.x.com) and sign in. Enter a name, description, and use case for your app. After creation, generate the keys and tokens you need. Save credentials immediately—they're only shown once. *** ## App permissions (OAuth 1.0a) OAuth 1.0a apps have three permission levels: * View posts, users, and public data * Cannot post, like, or modify anything * Cannot access Direct Messages * All read permissions * Post and delete posts * Follow/unfollow users * Like and repost * Cannot access Direct Messages * All read and write permissions * Send and read Direct Messages Changing permissions requires users to re-authorize your app to get new tokens with the updated scope. *** ## OAuth 2.0 app types When configuring OAuth 2.0, select your app type: | Type | Client | Use case | | :---------------------- | :----------- | :------------------------------------------------------- | | **Web App** | Confidential | Server-side applications that can securely store secrets | | **Automated App / Bot** | Confidential | Bots and automated services running on servers | | **Native App** | Public | Mobile or desktop apps that can't secure secrets | | **Single Page App** | Public | Browser-based JavaScript apps | **Confidential clients** receive a Client Secret. **Public clients** use PKCE only. *** ## Callback URLs Callback URLs (redirect URIs) are required for OAuth flows. After a user authorizes your app, they're redirected to your callback URL with an authorization code. ### Requirements * Add callback URLs to your app's allowlist in the Developer Console * URLs must match exactly (including trailing slashes) * Maximum of **10 callback URLs** per app * Use `https://` in production * For local development, use `http://127.0.0.1` (not `localhost`) ### Disallowed protocols These protocols cannot be used: `javascript`, `data`, `file`, `ftp`, `mailto`, `telnet`, and other non-standard schemes. `vbscript`, `javascript`, `vbs`, `data`, `mocha`, `keyword`, `livescript`, `ftp`, `file`, `gopher`, `acrobat`, `callto`, `daap`, `itpc`, `itms`, `firefoxurl`, `hcp`, `ldap`, `mailto`, `mmst`, `mmsu`, `msbd`, `rtsp`, `mso-offdap`, `snews`, `news`, `nntp`, `outlook`, `stssync`, `rlogin`, `telnet`, `tn3270`, `shell`, `sip` *** ## Best practices Create different apps for development, staging, and production. Regenerate keys periodically and if you suspect a compromise. Request only the permissions your app actually needs. Check the Developer Console regularly to track API usage. *** ## Automated account labels If your app runs a bot account, you can label it as automated: 1. Go to your bot account's **Settings** 2. Select **Your account** → **Automation** 3. Link your managing account This builds trust with users and distinguishes your bot from spam. *** ## Troubleshooting Ensure your callback URL is exactly as registered in the Developer Console, including protocol and any trailing slashes. HTTP-encode the URL when passing it as a query parameter. ```json theme={null} { "errors": [{ "code": 415, "message": "Callback URL not approved for this client application." }] } ``` If your app shows as suspended, check your email for a notice from the X platform team. Use the [Platform Help Form](https://help.x.com/forms/platform) to appeal. # Developer Console Source: https://docs.x.com/fundamentals/developer-portal Manage your apps, monitor usage, and access API credentials The [Developer Console](https://console.x.com) is your central hub for managing X API access. Create apps, generate credentials, monitor usage, and manage billing—all in one place. *** ## What you can do Set up apps to get API credentials. Configure authentication, permissions, and callback URLs. Track API usage in real-time. View costs per endpoint and manage your credit balance. Create API keys, access tokens, and OAuth credentials for your apps. Buy credits for pay-per-usage billing. No subscriptions or commitments required. *** ## Getting started Go to [console.x.com](https://console.x.com) and sign in with your X account. Accept the Developer Agreement. Click **New App** and provide a name and description. This generates your API credentials. Copy and securely store your API Key, API Secret, and Access Tokens. These won't be shown again. Use your credentials to authenticate API requests. Check out the [quickstart guide](/x-api/getting-started/make-your-first-request). **Store credentials securely.** API keys and tokens are only displayed once when generated. Use a password manager or secure vault. If lost, you'll need to regenerate them, which invalidates the old credentials. *** ## Billing & credits The X API uses pay-per-usage pricing with a credit-based system: | Feature | Description | | :----------------------- | :------------------------------------------------------------- | | **No monthly fees** | Pay only for what you use—no subscriptions | | **Credit-based** | Purchase credits upfront, deducted as you use the API | | **Real-time tracking** | Monitor usage and costs in the console dashboard | | **Per-endpoint pricing** | Different endpoints have different costs | | **Deduplication** | Same resource requested twice in 24 hours is only charged once | View current pricing and purchase credits in the [Developer Console](https://console.x.com). [Learn more about pricing →](/x-api/getting-started/pricing) *** ## Team management Team management is available for **Enterprise accounts** only. Enterprise accounts can invite team members to collaborate: | Role | Capabilities | | :---------------- | :--------------------------------------------------------- | | **Administrator** | Full access: manage apps, billing, team members, and roles | | **Developer** | Manage own apps, read-only access to team apps | To invite a team member, navigate to the Team page in your console and enter their X handle. *** ## Next steps Learn about app settings, permissions, and credentials. Use your credentials to call the API. # Rate Limits Source: https://docs.x.com/fundamentals/rate-limits Understand API rate limits across X developer products Rate limits control how many API requests you can make in a given time period. They ensure fair usage and system stability across the platform. *** ## Rate limits by product Each X API product has its own rate limiting approach: Per-endpoint limits based on 15-minute windows. View detailed tables and recovery tips. Custom limits based on your enterprise agreement and data package. Limits specific to ad management and analytics endpoints. *** ## How rate limits work | Concept | Description | | :-------------------- | :------------------------------------------------------ | | **Time window** | Most limits reset every 15 minutes | | **Per-user limits** | Apply when using OAuth 1.0a or OAuth 2.0 user tokens | | **Per-app limits** | Apply when using Bearer Token (app-only) authentication | | **Endpoint-specific** | Each endpoint has its own limit | *** ## Checking your limits Every API response includes headers showing your current rate limit status: ``` x-rate-limit-limit: 900 x-rate-limit-remaining: 847 x-rate-limit-reset: 1705420800 ``` | Header | Description | | :----------------------- | :--------------------------------------------- | | `x-rate-limit-limit` | Maximum requests allowed in the current window | | `x-rate-limit-remaining` | Requests remaining in the current window | | `x-rate-limit-reset` | Unix timestamp when the limit resets | *** ## Rate limit errors When you exceed a rate limit, you'll receive a **429 Too Many Requests** response: ```json theme={null} { "errors": [{ "code": 88, "message": "Rate limit exceeded" }] } ``` *** ## Best practices Store API responses locally to reduce repeated requests for the same data. When rate limited, wait before retrying. Double the wait time with each retry. Monitor rate limit headers to avoid hitting limits proactively. For real-time data, use filtered stream instead of polling search endpoints. *** ## Next steps For detailed rate limit tables by endpoint, see the rate limits page for your specific API: * [X API v2 Rate Limits](/x-api/fundamentals/rate-limits) * [X Ads API Rate Limits](/x-ads-api/fundamentals/rate-limiting) # Security Source: https://docs.x.com/fundamentals/security Security best practices for X API developers Building secure applications protects both your users and the X platform. This guide covers essential security practices for X API developers. *** ## Core requirements All API requests must use HTTPS. Plain HTTP is rejected. Never expose API keys or tokens in client-side code, logs, or repositories. *** ## Protecting credentials Your API keys and tokens are the keys to your app. Keep them secure: Store credentials in environment variables, not in code. ```bash theme={null} export X_API_KEY="your-api-key" export X_API_SECRET="your-api-secret" ``` Add credential files to `.gitignore`. Use tools like `git-secrets` to prevent accidental commits. Regenerate keys periodically and immediately if you suspect a compromise. Only request the OAuth scopes your app actually needs. ### If credentials are compromised 1. **Regenerate immediately** in the [Developer Console](https://console.x.com) 2. **Revoke old tokens** — regenerating automatically invalidates old credentials 3. **Audit usage** — check for unauthorized API activity 4. **Update your app** — deploy new credentials to all environments *** ## Application security ### Input validation Never trust user input. Validate and sanitize all data before using it: ```python theme={null} # Bad - vulnerable to injection query = f"from:{user_input}" # Good - validate input first import re if re.match(r'^[a-zA-Z0-9_]{1,15}$', user_input): query = f"from:{user_input}" ``` ### Output encoding Escape X API data before displaying in HTML to prevent XSS: ```javascript theme={null} // Bad - vulnerable to XSS element.innerHTML = tweet.text; // Good - escape HTML element.textContent = tweet.text; ``` ### Common vulnerabilities to prevent | Vulnerability | Prevention | | :----------------- | :---------------------------------------------------------- | | **XSS** | Escape all user-generated content before rendering | | **CSRF** | Use anti-CSRF tokens in forms; verify OAuth state parameter | | **SQL Injection** | Use parameterized queries, never concatenate user input | | **Open redirects** | Validate callback URLs against an allowlist | *** ## OAuth security ### State parameter Always use the `state` parameter in OAuth flows to prevent CSRF: ```python theme={null} import secrets # Generate state before authorization state = secrets.token_urlsafe(32) session['oauth_state'] = state # Verify state after callback if request.args.get('state') != session.get('oauth_state'): abort(403) # State mismatch - possible CSRF ``` ### Token storage | Token type | Storage recommendation | | :----------------- | :------------------------------------------------- | | **Access tokens** | Encrypted database or secure vault | | **Refresh tokens** | Encrypted database with additional access controls | | **Bearer tokens** | Environment variables or secure configuration | *** ## Secure development practices Conduct regular security reviews and penetration testing. Keep dependencies updated. Use tools to detect vulnerable packages. Log security events but never log credentials or sensitive data. Set up alerts for unusual API usage patterns. *** ## Reporting security issues If you discover a security vulnerability affecting X: **Report within 48 hours.** X Developer Platform users must notify X no more than 48 hours after suspecting a security incident. Report vulnerabilities in X's systems through HackerOne. If your app using X data is breached, report through the same channel. *** ## Compliance checklist * [ ] All API requests use TLS/HTTPS * [ ] Credentials stored securely (not in code or logs) * [ ] User tokens encrypted at rest * [ ] Input validation on all user-supplied data * [ ] Output encoding to prevent XSS * [ ] CSRF protection on OAuth flows * [ ] Security logging enabled (without sensitive data) * [ ] Incident response plan documented * [ ] Dependencies regularly updated * [ ] Minimal OAuth scopes requested *** ## Resources Implement OAuth correctly. Configure minimal required permissions. # X IDs Source: https://docs.x.com/fundamentals/x-ids Understanding unique identifiers in the X API Every object in the X API—posts, users, lists, DMs, spaces—has a unique ID. Understanding how these IDs work helps you build reliable integrations. *** ## ID format X IDs are **64-bit unsigned integers** generated using a system called "Snowflake." Each ID encodes: * **Timestamp** — When the object was created * **Worker number** — Which server generated the ID * **Sequence number** — Order within that millisecond This means IDs are roughly time-ordered: higher IDs generally represent newer objects. IDs are globally unique across all of X, not just within a single object type. *** ## String vs. integer representation **Always use string IDs in your code.** Some programming languages (like JavaScript) can't accurately represent 64-bit integers. In JavaScript, integers are limited to 53 bits. This causes precision loss with large IDs: ```javascript theme={null} // This loses precision! const id = 10765432100123456789; console.log(id.toString()); // "10765432100123458000" — wrong! // Use strings instead const id = "10765432100123456789"; console.log(id); // "10765432100123456789" — correct! ``` ### API versions | Version | ID format | | :------------- | :---------------------------------------------------------------------- | | **X API v2** | IDs are returned as strings by default | | **X API v1.1** | Returns both `id` (integer) and `id_str` (string) — always use `id_str` | *** ## Working with IDs ### Storing IDs Store IDs as strings or 64-bit integers in your database: | Database | Recommended type | | :--------- | :-------------------------------------- | | PostgreSQL | `BIGINT` or `TEXT` | | MySQL | `BIGINT UNSIGNED` or `VARCHAR(20)` | | MongoDB | String | | SQLite | `TEXT` (SQLite integers max at 63 bits) | ### Comparing IDs When comparing IDs for chronological ordering: ```python theme={null} # Python - safe for 64-bit integers if int(id1) > int(id2): print("id1 is newer") # JavaScript - compare as strings (lexicographically works for same-length IDs) # Or use BigInt if (BigInt(id1) > BigInt(id2)) { console.log("id1 is newer"); } ``` *** ## Common ID types | Object | Example ID | Notes | | :----------- | :-------------------- | :--------------------------------- | | Post (Tweet) | `1234567890123456789` | Also called Tweet ID | | User | `2244994945` | Older accounts have shorter IDs | | List | `1234567890` | | | Space | `1YqGodQbNXDxv` | Alphanumeric, not Snowflake format | | DM Event | `1234567890123456789` | | *** ## Related resources See ID fields for each object type. Retrieve posts by ID. # X Developer Platform Livestreams Source: https://docs.x.com/livestreams View recordings and replays of previous broadcasts about the X Developer Platform, designed to help the developer community learn and build with our tools. These include deep dives, getting started guides, and more. ## Past Broadcasts # Make Your First Request Source: https://docs.x.com/make-your-first-request Get up and running with the X API in minutes This guide walks you through making your first X API request. You'll need a [developer account with app credentials](/x-api/getting-started/getting-access) before starting. *** ## Quick start with cURL The fastest way to test the API is with cURL. Let's look up a user: ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Replace `$BEARER_TOKEN` with your actual Bearer Token. You'll get a response like: ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "xdevelopers" } } ``` *** ## Step-by-step guide In the [Developer Console](https://console.x.com), navigate to your app and copy the Bearer Token. Start with one of these beginner-friendly endpoints: | Endpoint | What it does | | :------------------------------------------------ | :--------------------------------- | | [User lookup](/x-api/users/lookup/introduction) | Get user profile by username or ID | | [Post lookup](/x-api/posts/lookup/introduction) | Get post by ID | | [Recent search](/x-api/posts/search/introduction) | Search posts from the last 7 days | Use cURL, Postman, or your preferred HTTP client: ```bash theme={null} # Look up a user by username curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Responses are JSON. The primary data is in the `data` field: ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "xdevelopers" } } ``` *** ## Request more data with fields By default, endpoints return minimal fields. Use the `fields` parameter to request additional data: ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers?user.fields=created_at,description,public_metrics" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Response: ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "xdevelopers", "created_at": "2013-12-14T04:35:55.000Z", "description": "The voice of the X Developer Platform", "public_metrics": { "followers_count": 570842, "following_count": 2048, "tweet_count": 14052, "listed_count": 1672 } } } ``` [Learn more about fields →](/x-api/fundamentals/fields) *** ## More examples ```bash theme={null} curl "https://api.x.com/2/tweets/1460323737035677698?tweet.fields=created_at,public_metrics" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```bash theme={null} curl "https://api.x.com/2/tweets/search/recent?query=from:xdevelopers&tweet.fields=created_at" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```bash theme={null} curl "https://api.x.com/2/users/2244994945/tweets?max_results=5" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Using code instead of cURL ```python theme={null} import requests bearer_token = "YOUR_BEARER_TOKEN" url = "https://api.x.com/2/users/by/username/xdevelopers" headers = {"Authorization": f"Bearer {bearer_token}"} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript theme={null} const bearerToken = "YOUR_BEARER_TOKEN"; const url = "https://api.x.com/2/users/by/username/xdevelopers"; fetch(url, { headers: { Authorization: `Bearer ${bearerToken}` } }) .then(res => res.json()) .then(data => console.log(data)); ``` For production use, we recommend the official SDKs: * [Python SDK](/xdks/python/overview) * [TypeScript SDK](/xdks/typescript/overview) They handle authentication, pagination, and rate limiting automatically. *** ## Tools for testing Visual API testing with our collection. Examples in multiple languages. Full endpoint documentation. *** ## Troubleshooting * Check that your Bearer Token is correct * Ensure the token hasn't been regenerated * Verify the `Authorization` header format: `Bearer YOUR_TOKEN` * Your app may not have access to this endpoint * Some endpoints require user-context authentication (OAuth 1.0a or 2.0) * Check your app's permissions in the Developer Console * You've hit a rate limit * Check the `x-rate-limit-reset` header for when to retry * Implement exponential backoff in your code [Full error reference →](/x-api/fundamentals/response-codes-and-errors) *** ## Next steps Understand OAuth for user-context requests. Discover what you can build. Faster development with official libraries. Ideas for what to create. # Subscribe to developer news Source: https://docs.x.com/newsletter Sign up for emails about the latest news, product updates, and events from the X Developer team. # X Developer Platform Source: https://docs.x.com/overview Build, analyze, and innovate with X's real-time global data. Access posts, users, trends, and more through modern APIs with flexible pay-per-usage pricing. ## Products Choose the access level that fits your needs. Credit-based pricing with no commitments. Pay only for what you use. High-volume endpoints, dedicated account management, and custom rate limits. 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) *** ## Get started
X logo
Owned Reads let you access your own data at reduced cost. Requests for your own posts, bookmarks, followers, likes, and more are priced at \$0.001 per resource.

Get your API keys and make your first request in minutes. Explore endpoints for posts, users, spaces, DMs, lists, and more. Official Python and TypeScript SDKs for faster development. *** ## Resources Step-by-step guides for common use cases and integrations. Official SDKs and community libraries for every language. Get help from the community and X team. llms.txt, skill.md, MCP servers, and resources for AI-powered development. *** ## Other products Programmatically manage ad campaigns, targeting, creatives, and analytics. # Tools & Libraries Source: https://docs.x.com/tools-and-libraries Official SDKs, developer tools, AI integrations, and community libraries for the X API Speed up your development with official SDKs, developer tools, and community libraries. *** ## Official SDKs Async support, type hints, and automatic token refresh. Perfect for data analysis and automation. Full type safety and ESM support. Works in Node.js and modern bundlers. ### Quick start ```bash theme={null} pip install xdk ``` ```python theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") for page in client.posts.search_recent(query="api", max_results=10): if page.data and len(page.data) > 0: print(page.data[0].text) break ``` ```bash theme={null} npm install @xdevplatform/xdk ``` ```typescript theme={null} import { Client } from '@xdevplatform/xdk'; const client = new Client({ bearerToken: 'YOUR_BEARER_TOKEN' }); const userResponse = await client.users.getByUsername('XDevelopers'); console.log(userResponse.data?.username); ``` *** ## Developer tools Interactive API testing for all v2 endpoints. curl-like CLI for the X API with built-in OAuth authentication. No manual token management. Local mock server for testing X API v2 endpoints without using real credits. XMCP, llms.txt, skill.md, docs MCP server, and resources for AI-powered development. ### Other tools | Tool | Description | | :------------------------------------------------------ | :---------------------------------------------------------- | | [OpenAPI Spec](https://api.x.com/2/openapi.json) | Machine-readable API specification | | [twitter-text](https://github.com/twitter/twitter-text) | Parse and validate post text, count characters | | [Embed Generator](https://publish.x.com/#) | Build embeddable posts, timelines, or buttons for your site | *** ## Community libraries Community-maintained libraries with X API v2 support. Check each library's documentation for current coverage. | Library | Description | | :----------------------------------------------------------- | :------------------------------------- | | [tweepy](https://github.com/tweepy/tweepy) | Popular Python library with v2 support | | [twarc](https://twarc-project.readthedocs.io/) | CLI and library for data collection | | [python-twitter](https://github.com/sns-sdks/python-twitter) | Simple Python wrapper | | [TwitterAPI](https://github.com/geduldig/TwitterAPI) | Minimal Python wrapper | | Library | Description | | :------------------------------------------------------------------- | :------------------------------------------- | | [node-twitter-api-v2](https://github.com/PLhery/node-twitter-api-v2) | Strongly-typed, full-featured Node.js client | | [twitter.js](https://github.com/twitterjs/twitter.js) | Object-oriented Node.js library | | [twitter-v2](https://github.com/HunterLarco/twitter-v2) | Async client library | | Library | Description | | :-------------------------------------------------------------- | :---------------------- | | [go-twitter](https://github.com/g8rswimmer/go-twitter) | Go library for v2 API | | [gotwi](https://github.com/michimani/gotwi) | Go wrapper for v2 | | [twitter-stream](https://github.com/Fallenstedt/twitter-stream) | Filtered stream wrapper | | Library | Description | | :---------------------------------------------------- | :--------------------- | | [twittered](https://github.com/redouane59/twittered) | Java client for v2 | | [twitter4j-v2](https://github.com/takke/twitter4j-v2) | Twitter4J v2 wrapper | | [KTweet](https://github.com/ChromasIV/KTweet) | Kotlin v2 library | | [Tweedle](https://github.com/tyczj/Tweedle) | Kotlin Android library | | Library | Description | | :---------------------------------------------------------------- | :-------------------- | | [twitter-api-v2-php](https://github.com/noweh/twitter-api-v2-php) | PHP v2 client | | [bird-elephant](https://github.com/danieldevine/bird-elephant) | PHP v2 library | | [twitteroauth](https://github.com/abraham/twitteroauth) | Popular OAuth library | | Library | Description | | :---------------------------------------------------------------- | :---------------- | | [tweetkit](https://github.com/julianfssen/tweetkit) | Ruby v2 client | | [twitter\_oauth2](https://github.com/nov/twitter_oauth2) | OAuth 2.0 library | | [omniauth-twitter2](https://github.com/unasuke/omniauth-twitter2) | OmniAuth strategy | | Language | Library | | :------------- | :--------------------------------------------------------------------------------------------------------- | | **C#/.NET** | [Tweetinvi](https://github.com/linvi/tweetinvi), [LinqToTwitter](https://github.com/JoeMayo/LinqToTwitter) | | **Rust** | [twitter-v2](https://github.com/jpopesculian/twitter-v2-rs) | | **Swift** | [Twift](https://github.com/daneden/Twift/), [TwitterAPIKit](https://github.com/mironal/TwitterAPIKit) | | **R** | [academictwitteR](https://github.com/cjbarrie/academictwitteR) | | **PowerShell** | [BluebirdPS](https://github.com/thedavecarroll/BluebirdPS) | Community libraries are not maintained by X. Check their repositories for support and current status. *** ## Code samples * [X API v2 Sample Code](https://github.com/xdevplatform/Twitter-API-v2-sample-code) — Examples in Python, JavaScript, Ruby, and more * [Code Samples Repo](https://github.com/xdevplatform/samples) — Examples using the official XDKs * [X Developer GitHub](https://github.com/xdevplatform) — Official repos and tools *** ## X Ads API libraries For tools and libraries specific to the X Ads API, see the [Ads API tools and libraries](/x-ads-api/tools-and-libraries) page. # Agent Resources Source: https://docs.x.com/tools/ai Machine-readable files, MCP servers, and resources for AI agents working with the X API Resources for connecting AI tools to the X API and its documentation. *** ## Use the X API from AI tools Give your AI agent the ability to call X API endpoints directly. XMCP exposes 200+ X API endpoints as callable MCP tools. Docs MCP lets agents search these docs. Machine-readable API definition for auto-generating clients or feeding into agents. | Resource | What it does | URL | | :--------------- | :-------------------------------------------------------------------- | :------------------------------------------------------------------- | | **XMCP** | MCP server — agents can create posts, search, look up users, etc. | [github.com/xdevplatform/xmcp](https://github.com/xdevplatform/xmcp) | | **OpenAPI Spec** | Machine-readable API definition for code generation and agent tooling | [api.x.com/2/openapi.json](https://api.x.com/2/openapi.json) | *** ## Read X API docs from AI tools Give your AI agent context on how the X API works. Documentation index and full-content files for LLMs to ingest. Capability summary that tells agents what actions are possible with the X API. Search and read documentation pages from your AI assistant via MCP. | Resource | What it does | URL | | :---------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | | **llms.txt** | Documentation index — page titles, URLs, and descriptions | [docs.x.com/llms.txt](https://docs.x.com/llms.txt) | | **llms-full.txt** | Complete docs in a single file for full-context loading | [docs.x.com/llms-full.txt](https://docs.x.com/llms-full.txt) | | **skill.md** | Capability summary — actions, inputs, and constraints | [docs.x.com/skill.md](https://docs.x.com/skill.md) | | **Docs MCP** | MCP server — agents can search and read doc pages on the fly | [docs.x.com/mcp](https://docs.x.com/mcp) | # llms.txt & llms-full.txt Source: https://docs.x.com/tools/llms-txt Machine-readable documentation files that let AI tools index and understand X API documentation A structured index of all documentation pages. Gives AI tools a map of what's available. The complete documentation in a single Markdown file. Full context for any AI tool. The [`llms.txt` standard](https://llmstxt.org) is like a sitemap for AI. It helps LLMs understand your documentation structure and find relevant content — similar to how `sitemap.xml` helps search engines. | File | What it contains | Best for | | :-------------------------------------------------- | :------------------------------------ | :------------------------------------------------------ | | [`llms.txt`](https://docs.x.com/llms.txt) | Page titles, URLs, and descriptions | Quick overview, navigation, finding specific topics | | [`llms-full.txt`](https://docs.x.com/llms-full.txt) | All documentation content in one file | Full context, comprehensive answers, deep understanding | These files are also available at `/.well-known/llms.txt` and `/.well-known/llms-full.txt` for tools that follow the `.well-known` convention. Every page in the documentation can be fetched as raw Markdown by appending `.md` to its URL (e.g., `https://docs.x.com/tools/llms-txt.md`). *** ## How to use it Most AI tools accept URLs directly. Just provide the URL and the tool will fetch and parse the content: * **Grok**: Paste `https://docs.x.com/llms-full.txt` into the chat and ask questions about the X API * **Cursor / Windsurf**: Add `https://docs.x.com/llms-full.txt` as documentation context for your project * **Custom agents**: Fetch the file programmatically and include it in your system prompt or context window ```bash theme={null} # Fetch the documentation index curl https://docs.x.com/llms.txt # Fetch the complete documentation curl https://docs.x.com/llms-full.txt # Fetch any individual page as Markdown curl https://docs.x.com/tools/llms-txt.md ``` # MCP Servers Source: https://docs.x.com/tools/mcp MCP servers for calling X API endpoints and searching X API documentation from AI tools Two [MCP](https://modelcontextprotocol.io) (Model Context Protocol) servers are available for working with X from AI tools: | Server | What it does | URL | | :----------- | :--------------------------------------------------------------- | :---------------------------------- | | **XMCP** | Call X API endpoints (create posts, search, look up users, etc.) | `http://127.0.0.1:8000/mcp` (local) | | **Docs MCP** | Search and read X API documentation | `https://docs.x.com/mcp` (hosted) | *** ## XMCP — X API endpoints [XMCP](https://github.com/xdevplatform/xmcp) is an official MCP server that exposes X API endpoints as callable tools. Run it locally and connect any MCP-compatible client — like Cursor, Windsurf, or your own agent — to read and write to X programmatically. Source code, setup instructions, and configuration. Learn about the Model Context Protocol standard. XMCP loads the X API [OpenAPI specification](https://api.x.com/2/openapi.json) at startup and converts every operation into an MCP tool. Your AI assistant can then call these tools to interact with the X API — creating posts, searching, looking up users, and more. **Key features:** * **200+ tools** automatically generated from the OpenAPI spec — search posts, create posts, look up users, manage likes, and more * **OAuth 1.0a authentication** with browser-based consent flow * **Tool allow-listing** via `X_API_TOOL_ALLOWLIST` to restrict which API operations are available * **Works with any MCP client** — Cursor, Windsurf, or custom implementations * **Optional Grok test client** using the xAI API ### Quick setup Requires Python 3.9+. ```bash theme={null} git clone https://github.com/xdevplatform/xmcp && cd xmcp python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt ``` Copy the example environment file and add your X API credentials: ```bash theme={null} cp env.example .env ``` Edit `.env` with your app's OAuth consumer key, consumer secret, and bearer token from the [Developer Console](https://console.x.com). Set your callback URL (e.g., `http://127.0.0.1:8976/oauth/callback`) — make sure to register it in the Developer Console too. ```bash theme={null} python server.py ``` The MCP server starts at `http://127.0.0.1:8000/mcp` by default. Host and port are configurable via environment variables. Point your MCP-compatible client to `http://127.0.0.1:8000/mcp`. See the [XMCP README](https://github.com/xdevplatform/xmcp) for client-specific configuration. ### Configuration Add XMCP to your MCP client settings: ```json theme={null} { "mcpServers": { "xmcp": { "url": "http://127.0.0.1:8000/mcp" } } } ``` Once connected, you can ask your AI assistant to interact with X directly — "search for recent posts about AI", "look up @XDevelopers", or "create a post". ### Tool allow-listing By default, XMCP exposes all X API operations as tools. Use the `X_API_TOOL_ALLOWLIST` environment variable to restrict which tools are available: ```bash theme={null} X_API_TOOL_ALLOWLIST="createPosts,getUsersByUsername,searchPostsRecent,likePost" ``` This is useful for limiting what an AI agent can do — for example, allowing read-only operations while blocking post creation or deletion. ### Limitations * **No streaming or webhook endpoints** — these require persistent connections that don't fit the MCP request/response model * **Spec fetched at startup** — restart the server to pick up any API spec updates * **Tokens stored in memory** — OAuth tokens are not persisted across restarts *** ## Docs MCP — documentation search An MCP server for the X API documentation is hosted at `https://docs.x.com/mcp`. Connect it to your AI tool to search and read documentation pages without leaving your workflow. ### Available tools | Tool | Description | | :----------- | :---------------------------------------------------------------------------------------------------- | | `search_x` | Search across the X documentation for relevant information, code examples, API references, and guides | | `get_page_x` | Retrieve the full content of a specific documentation page by its path | ### Configuration Add the docs MCP server to your MCP client configuration: ```json theme={null} { "mcpServers": { "x-docs": { "url": "https://docs.x.com/mcp" } } } ``` This is useful when you're building with the X API and want your AI assistant to look up endpoint details, authentication guides, or code examples on the fly. *** ## Using both servers together You can connect both MCP servers simultaneously. This gives your AI assistant the ability to both look up documentation *and* call the API: ```json theme={null} { "mcpServers": { "xmcp": { "url": "http://127.0.0.1:8000/mcp" }, "x-docs": { "url": "https://docs.x.com/mcp" } } } ``` *** ## OpenAPI specification The machine-readable API specification for all X API v2 endpoints. This is the same spec that XMCP uses to generate its tools. | Resource | URL | | :---------------------- | :--------------------------------------------------------------------- | | **OpenAPI Spec (JSON)** | [`https://api.x.com/2/openapi.json`](https://api.x.com/2/openapi.json) | ```bash theme={null} curl https://api.x.com/2/openapi.json -o openapi.json ``` You can use it to auto-generate API clients, import into [Postman](https://www.postman.com/xapidevelopers/x-api-public-workspace/collection/34902927-2efc5689-99c6-4ab6-8091-996f35c2fd80), feed into custom AI agents, or validate request/response schemas. # Python XDK Source: https://docs.x.com/tools/python-xdk Official Python client library for the X API v2 The [Python XDK](https://github.com/xdevplatform/xdk-py) is the official client library for the X API v2. It handles authentication, pagination, and streaming so you can focus on building. Source code, issues, and releases. *** ## Installation ```bash theme={null} pip install xdk ``` Requires Python 3.8+. *** ## Quick start ```python theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search for posts for page in client.posts.search_recent(query="X API", max_results=10): if page.data and len(page.data) > 0: print(page.data[0].text) break ``` *** ## Key features | Feature | Description | | :----------------------- | :-------------------------------------------------------------------- | | **OAuth support** | Bearer Token, OAuth 2.0 with PKCE, and OAuth 1.0a | | **Automatic pagination** | Iterate through results without manual `next_token` handling | | **Streaming** | Real-time data via persistent connections (filtered stream, etc.) | | **Full API coverage** | All X API v2 endpoints — search, timelines, filtered stream, and more | *** ## Authentication ```python theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") ``` ```python theme={null} from xdk import Client from xdk.oauth2_auth import OAuth2PKCEAuth auth = OAuth2PKCEAuth( client_id="YOUR_CLIENT_ID", redirect_uri="YOUR_CALLBACK_URL", scope="tweet.read users.read offline.access" ) auth_url = auth.get_authorization_url() tokens = auth.fetch_token(authorization_response=callback_url) client = Client(bearer_token=tokens["access_token"]) ``` ```python theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) ``` *** ## Common methods | Category | Method | | :--------- | :----------------------------- | | **Posts** | `client.posts.search_recent()` | | **Users** | `client.users.get_me()` | | **Spaces** | `client.spaces.get()` | | **Lists** | `client.lists.get()` | | **DMs** | `client.direct_messages.get()` | *** ## Learn more Development install, prerequisites, and verification. Step-by-step first request walkthrough. Detailed guide for all auth methods. Automatic pagination and iterators. Real-time data via filtered stream. Complete client and model reference. For code examples, see the [samples repo](https://github.com/xdevplatform/samples/tree/main/python). # skill.md Source: https://docs.x.com/tools/skill-md A capability summary that tells AI agents what they can do with the X API The live skill.md file for the X API documentation. The [`skill.md`](https://docs.x.com/skill.md) file follows the [agentskills.io specification](https://agentskills.io/specification) and describes what AI agents can *do* with the X API. While [`llms.txt`](/tools/llms-txt) is a directory of pages, `skill.md` is a capability summary — it lists specific actions, required inputs, and constraints so agents can use the API more reliably. *** ## What's included * **Capabilities** — what agents can accomplish (search posts, create posts, manage users, etc.) * **Skills** — specific actions organized by category with required parameters * **Workflows** — step-by-step procedures for common tasks * **Context** — background on authentication, rate limits, and architecture ```bash theme={null} # Fetch the skill file curl https://docs.x.com/skill.md ``` *** ## Discovery endpoints Agents can discover skill files programmatically via the well-known endpoints: ```bash theme={null} # Discovery endpoint (agent-skills 0.2.0 spec) curl https://docs.x.com/.well-known/agent-skills/index.json # Original discovery format curl https://docs.x.com/.well-known/skills/index.json ``` You can add X API capabilities to any agent that supports the [skills CLI](https://www.npmjs.com/package/skills): ```bash theme={null} npx skills add https://docs.x.com ``` # TypeScript XDK Source: https://docs.x.com/tools/typescript-xdk Official TypeScript client library for the X API v2 The [TypeScript XDK](https://github.com/xdevplatform/twitter-api-typescript-sdk) is the official client library for the X API v2. Full type safety, automatic pagination, and event-driven streaming. Source code, issues, and releases. *** ## Installation ```bash npm theme={null} npm install @xdevplatform/xdk ``` ```bash yarn theme={null} yarn add @xdevplatform/xdk ``` ```bash pnpm theme={null} pnpm add @xdevplatform/xdk ``` Requires Node.js 16+ and TypeScript 4.5+ (if using TypeScript). *** ## Quick start ```typescript theme={null} import { Client } from '@xdevplatform/xdk'; const client = new Client({ bearerToken: 'YOUR_BEARER_TOKEN' }); const userResponse = await client.users.getByUsername('XDevelopers'); console.log(userResponse.data?.username); ``` *** ## Key features | Feature | Description | | :----------------------- | :--------------------------------------------------------------- | | **Type safety** | Complete TypeScript definitions for all endpoints and parameters | | **Authentication** | Bearer Token, OAuth 2.0 with PKCE, and OAuth 1.0a | | **Automatic pagination** | Async iteration support for paginated endpoints | | **Streaming** | Event-driven streaming with automatic reconnection | | **Full API coverage** | Users, Posts, Lists, Bookmarks, Communities, and more | *** ## Authentication ```typescript theme={null} import { Client } from '@xdevplatform/xdk'; const client = new Client({ bearerToken: 'YOUR_BEARER_TOKEN' }); ``` ```typescript theme={null} import { Client, OAuth2, generateCodeVerifier, generateCodeChallenge } from '@xdevplatform/xdk'; const oauth2 = new OAuth2({ clientId: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', redirectUri: 'https://your-app.com/callback', scope: ['tweet.read', 'users.read', 'offline.access'], }); const codeVerifier = generateCodeVerifier(); const codeChallenge = await generateCodeChallenge(codeVerifier); oauth2.setPkceParameters(codeVerifier, codeChallenge); const authUrl = await oauth2.getAuthorizationUrl('state'); const tokens = await oauth2.exchangeCode(authCode, codeVerifier); const client = new Client({ accessToken: tokens.access_token }); ``` ```typescript theme={null} import { Client, OAuth1 } from '@xdevplatform/xdk'; const oauth1 = new OAuth1({ apiKey: 'YOUR_API_KEY', apiSecret: 'YOUR_API_SECRET', accessToken: 'YOUR_ACCESS_TOKEN', accessTokenSecret: 'YOUR_ACCESS_TOKEN_SECRET' }); const client = new Client({ oauth1: oauth1 }); ``` *** ## Common methods | Category | Method | | :--------- | :------------------------------- | | **Posts** | `client.posts.search()` | | **Users** | `client.users.getMe()` | | **Spaces** | `client.spaces.findSpaceById()` | | **Lists** | `client.lists.getList()` | | **DMs** | `client.directMessages.lookup()` | *** ## Learn more Package managers, TypeScript setup, and requirements. Detailed guide for all auth methods. Async iteration and paginated responses. Event-driven streaming with reconnection. Complete client, interface, and type reference. For code examples, see the [samples repo](https://github.com/xdevplatform/samples/tree/main/javascript). # xurl Source: https://docs.x.com/tools/xurl Command-line tool for the X API with built-in authentication [xurl](https://github.com/xdevplatform/xurl) is a curl-like command-line tool for the X API. It handles OAuth authentication automatically so you can make API requests without manually managing tokens or signing requests. Source code, releases, and documentation. *** ## Installation Install with Go, or download a pre-built binary from [releases](https://github.com/xdevplatform/xurl/releases). ```bash theme={null} go install github.com/xdevplatform/xurl@latest ``` *** ## Setup Authorize xurl with your X API credentials on first use: ```bash theme={null} xurl auth ``` This opens a browser-based OAuth flow. Once authorized, xurl stores your tokens locally so you don't need to authenticate again. *** ## Usage ### Raw API requests Use xurl like curl — it handles auth headers automatically: ```bash theme={null} # Look up a user xurl /2/users/by/username/xdevelopers # Search recent posts xurl "/2/tweets/search/recent?query=from:xdevelopers&max_results=10" # Create a post xurl -X POST /2/tweets -d '{"text": "Hello from xurl!"}' ``` ### Shortcut commands xurl includes built-in shortcuts for common operations: ```bash theme={null} # Look up a user by username xurl user xdevelopers # Search recent posts xurl search "X API" # Post a tweet xurl post "Hello from xurl!" ``` *** ## Why use xurl? | Feature | curl | xurl | | :------------------- | :-------------------------- | :------------------------------------ | | **Authentication** | Manual OAuth header setup | Automatic — just run `xurl auth` once | | **Token management** | You handle refresh/rotation | Built-in token storage and refresh | | **API shortcuts** | Full URL required | Shorthand commands for common tasks | | **Request signing** | Manual OAuth 1.0a signing | Automatic for all requests | *** ## Using xurl with AI agents xurl includes a [`SKILL.md`](https://github.com/xdevplatform/xurl/blob/main/SKILL.md) file that describes its capabilities in a machine-readable format. AI agents can use this to understand how to invoke xurl commands on your behalf. ```bash theme={null} npx skills add https://github.com/xdevplatform/xurl ``` *** ## Related Get started with the X API using cURL or SDKs. Test endpoints locally with mock data — no API credits needed. # Tutorials Source: https://docs.x.com/tutorials Instructions and examples to help you get started. Learn how to explore a user's Posts and mentions using the user Post timeline and user mention timeline endpoints from the last 7 days.

[**View tutorial**](/tutorials/explore-a-users-posts)
Learn how to start using Postman to make requests to the X API and X Ads API.

[**View tutorial**](/tutorials/postman-getting-started)
Learn about using R to connect to the user lookup endpoint and how to work with JSON returned from X API v2.

[**View tutorial**](/tutorials/getting-started-with-r-and-v2-of-the-x-api)
Learn to use the full-archive search endpoint to search the complete history of public X data, build a dataset by retrieving geo-tagged Posts, and how to page through the available Posts for a query.
[**View tutorial**](/tutorials/getting-historical-posts-using-the-full-archive-search-endpoint)
This guide gives a high-level overview on how to ingest Posts at scale, and “slice and dice” those Tweets via metadata to narrow them down to a specific category, or sub-categories.
[**View tutorial**](/tutorials/post-processing-x-data-with-the-google-cloud-platform)
# What to Build Source: https://docs.x.com/what-to-build Ideas and inspiration for building with the X API The X API opens up possibilities across moderation, analytics, community building, and more. Here are ideas to inspire your next project. *** ## Build for safety & moderation Help create healthier conversations on X. Build tools to help users manage replies, filter content, and protect their experience. Detect and flag abusive content, provide bulk blocking tools. **Relevant endpoints:** * [Blocks](/x-api/users/blocks/introduction) — Block and unblock users * [Mutes](/x-api/users/mutes/introduction) — Mute accounts and keywords * [Hide replies](/x-api/posts/hide-replies/introduction) — Hide unwanted replies * [Manage posts](/x-api/posts/manage-tweets/introduction) — Control reply settings *** ## Build for creators Help people express themselves and grow their audience. Schedule posts, threads, and content calendars. Sync content across platforms and formats. Help users compose and publish long-form threads. Create helpful bots that add value to the conversation. **Relevant endpoints:** * [Manage posts](/x-api/posts/manage-tweets/introduction) — Create and delete posts * [Media upload](/x-api/media/quickstart/media-upload-chunked) — Upload images and videos * [Spaces](/x-api/spaces/lookup/introduction) — Discover audio conversations *** ## Build for analytics Help users understand their impact and audience. Visualize engagement metrics, growth trends, and reach. Analyze followers, engagement patterns, and demographics. Track hashtags, topics, and conversation volume. Compare accounts, benchmark performance. **Relevant endpoints:** * [Metrics](/x-api/fundamentals/metrics) — Engagement and performance data * [Search posts](/x-api/posts/search/introduction) — Historical and recent search * [Post counts](/x-api/posts/counts/introduction) — Volume analytics * [Annotations](/x-api/fundamentals/post-annotations) — Topic classification *** ## Build for communities Help people connect and organize around shared interests. Moderation, member management, and engagement tools. Organize Spaces, live events, and group activities. Aggregate and curate content by topic or interest. Build tools for specific languages and regions. **Relevant endpoints:** * [Lists](/x-api/lists/list-lookup/introduction) — Curated account lists * [Spaces](/x-api/spaces/lookup/introduction) — Audio conversations * [Follows](/x-api/users/follows/introduction) — Relationship management *** ## Build for research Analyze public conversations and extract insights. Study public discourse, information spread, and trends. Source stories, verify information, track breaking news. Monitor brand sentiment, industry trends, and competitors. Train models, analyze networks, study behavior patterns. **Relevant endpoints:** * [Full-archive search](/x-api/posts/search/introduction) — Search posts back to 2006 * [Filtered stream](/x-api/posts/filtered-stream/introduction) — Near real-time matching posts * [Post annotations](/x-api/fundamentals/post-annotations) — Entity recognition * [Conversation ID](/x-api/fundamentals/conversation-id) — Thread reconstruction *** ## Build for good Use the API to make a positive impact. Monitor emergencies, coordinate aid, spread awareness. Build tools that make X more accessible to everyone. Create learning tools, teaching resources, and tutorials. Help people stay informed and participate in democracy. *** ## Build with AI agents Connect AI tools to the X API and build intelligent workflows. Use XMCP to give AI assistants direct access to X API endpoints — create posts, search, and manage accounts via natural language. Feed X data into LLMs for sentiment analysis, content generation, and automated reporting. Give Grok, Cursor, or your preferred AI tool full context on the X API with llms.txt for faster development. Build agents that monitor trends, respond to mentions, or curate content automatically. **Get started:** * [XMCP](/tools/mcp#xmcp--x-api-endpoints) — MCP server for the X API * [llms-full.txt](https://docs.x.com/llms-full.txt) — Feed complete docs to your AI tool * [OpenAPI Spec](https://api.x.com/2/openapi.json) — Machine-readable API definition [Browse all agent resources](/tools/ai) *** ## Getting started Ready to build? Here's your path forward: [Sign up for a developer account](/x-api/getting-started/getting-access) and create an app. [Make your first request](/x-api/getting-started/make-your-first-request) and explore the API. Pick an [SDK or library](/tools-and-libraries) for your language. Start small, iterate, and share what you've built! *** ## Share your work Built something with the X API? We'd love to see it: * Share in the [Developer Forum](https://devcommunity.x.com) * Tag [@XDevelopers](https://x.com/XDevelopers) on X * Submit to our [Success Stories](/success-stories) # Increasing access Source: https://docs.x.com/x-ads-api/getting-started/increasing-access ### Requesting additional App-level permissions If your application is limited to any of the below app-level permissions and you wish to access additional endpoints, please reach out to your X representative to apply for **Standard Access**. Developers who requested Ads API access prior to July 2023 may have requested any of the following app-level permissions, which are now included with the Standard Access level: * **Analytics** (Read-only): User has access to [Analytics](https://developer.x.com/en/docs/twitter-ads-api/analytics/overview) endpoints with [read-only](https://developer.x.com/en/docs/apps/app-permissions) access. User cannot access Campaign Management, Creative or Custom Audiences endpoints, but will be able to pull metrics by passing through creative entities in the request parameters of the Analytics endpoints. To pull the granularity of media\_id or media\_name, the user application would need to have Ads API write access. * **Campaign Management & Creative** (Read & Write): User has access to [Campaign Management](https://developer.x.com/en/docs/twitter-ads-api/campaign-management/overview) and [Creative](https://developer.x.com/en/docs/twitter-ads-api/creatives/overview) endpoints with [read & write](https://developer.x.com/en/docs/apps/app-permissions) access. User also has access to Analytics endpoints. * **Custom Audiences** (Read & Write): User has access to [Custom Audiences](https://developer.x.com/en/docs/twitter-ads-api/audiences/overview) endpoints with [read & write](https://developer.x.com/en/docs/apps/app-permissions) access. User also has access to Campaign Management, Creative, and Analytics endpoints. * **Conversion** (Read & Write): User has access to [Mobile](https://developer.x.com/en/docs/twitter-ads-api/measurement/mobile-conversions/overview) and [Web Conversion](https://developer.x.com/en/docs/twitter-ads-api/measurement/web-conversions/overview) endpoints with [read & write](https://developer.x.com/en/docs/apps/app-permissions) access. User may or may not have access to other Ads API endpoints. ### Requesting an increase to user OAuth token limits Developers who requested Ads API access prior to July 2023 may have a limit of 5 user OAuth tokens on their application. If you need to increase your developer App's token limit, please reach out to your X representative. **Note:** We do not increase the [rate limits](https://developer.x.com/en/docs/twitter-ads-api/rate-limiting) for any Ads API endpoint. View our [best practices](https://developer.x.com/en/docs/twitter-ads-api/rate-limiting) to learn how to stay within rate limits. # Step-by-step guide Source: https://docs.x.com/x-ads-api/getting-started/step-by-step-guide ## How to get access to the Ads API 1. Sign up for a [developer account](https://developer.x.com/en/apply-for-access). 2. Create a [developer App](https://developer.x.com/app) and secure your token. 3. Visit [ads.x.com/help](https://ads.x.com/help) to submit a request for Ads API access for each of your developer app(s). **Note:** If you are already building on the X Developer Platform and have a developer account, skip to step three. ### Step one: Signup for a developer account To make a request to any of X's API products, you must first sign up for a developer account. Within the Developer Console, create an App and developer App. This will provide you a set of credentials that you will use to authenticate all requests to the API. ### Step two: Save your App's key and tokens and keep them secure Within your developer App, you will be provided a set of API Keys (also known as Consumer Keys). You will also have the chance to generate a set of Access Tokens that can be used to make requests on behalf of your personal X account, and a Bearer Token that can be used to authenticate endpoints that require OAuth 2.0 Bearer Token. As these keys and tokens do not expire unless regenerated, we suggest creating environment variables, or using a secure password manager. Please also take note of your App ID, which can be found in the URL of your App details. This will make it easier to request access to the Ads API in the next step. **Note:** Your keys and tokens will only display once in the Developer Console, so it is important that you store these credentials in your password management system as soon as you generate them. If you misplace or forget the keys and tokens, you will need to regenerate them, which creates new keys and tokens, and invalidates the old ones. This means that you will have to update any integrations that you may have set up with your prior credentials. Learn more about our [authentication best practices](https://developer.x.com/en/docs/authentication/guides/authentication-best-practices). ### Step three: Apply for access to the Ads API At this point you will have [basic access to the X API](https://developer.x.com/en/docs/twitter-api/v1), but not specific X Ads API functionality. Next, you will need to request access and be approved for Ads API access. Visit [ads.x.com/help](https://ads.x.com/help) to submit a request for Ads API access for each of your developer app(s). ### Access tiers As part of the application process, you'll need to specify what level of access you require. Learn more about [App-level and Ad Account-level permissions](https://developer.x.com/en/docs/twitter-ads-api/obtaining-ads-account-access#levels-of-access). #### Conversion Only Access to Mobile and Web Conversion endpoints with read & write access. #### Standard Access Access to Analytics, Campaign Management, Creatives, Custom Audiences, and Conversion endpoints with read & write access. **Note:** After your app is approved for Ads API access, you will need to [regenerate any user access tokens](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens) to make properly [authenticated Ads API requests](https://developer.x.com/en/docs/twitter-ads-api/making-authenticated-requests). **Note:** Ads API developers who requested access prior to July 2023 may have different levels of access and permissions, and may be limited to five OAuth tokens. See our guide on [increasing access](https://developer.x.com/en/docs/twitter-ads-api/increasing-access) to access to additional endpoints or lift token limits for existing applications. ## Make your first request To test your access to the Ads API make a request to the [GET accounts](https://developer.x.com/en/docs/twitter-ads-api/campaign-management/api-reference/accounts) endpoint. This endpoint will return the ad accounts the currently authorized user has access to. Use the ad account IDs from this request in subsequent API requests to read and write data for a specific ad account. Using [Twurl](https://developer.x.com/en/docs/tutorials/using-twurl) on the command line the request would look like the following. **Example Request** ```bash theme={null} twurl -H ads-api.x.com "/11/accounts" ``` **Example Response** ```json theme={null} { "request": { "params": {} }, "data": [ { "name": "Furni", "business_name": null, "timezone": "America/Los_Angeles", "timezone_switch_at": "2016-04-06T07:00:00Z", "id": "18ce54ayf0z", "created_at": "2016-04-07T14:40:15Z", "salt": "b88939e5cabbca720159cb3659d73c06", "updated_at": "2017-02-08T08:49:53Z", "business_id": null, "approval_status": "ACCEPTED", "deleted": false } ] } ``` ## Up next . . . * Read through the [Ads API documentation](https://developer.x.com/en/docs/twitter-ads-api) fundamentals such as [versioning](https://developer.x.com/en/docs/twitter-ads-api/versioning) and [hierarchy](https://developer.x.com/en/docs/twitter-ads-api/hierarchy-terminology). * Check out the various [Ads API tools & libraries](https://developer.x.com/en/docs/twitter-ads-api/tools-and-libraries) to aid you in your Ads API integration. * Understand the steps to [increase Ads API access](https://developer.x.com/en/docs/twitter-ads-api/increasing-access). * Familiarize yourself with available [support resources](https://developer.x.com/en/support/twitter-ads-api). * Follow [@API](https://x.com/API) for live updates on API changes, service issues and enhancements to X promoted products. # Introduction Source: https://docs.x.com/x-ads-api/introduction ## Connect to the X Ads platform The X Ads API connects developers to X's advertising platform to build solutions to meet the needs of X's advertisers around the world. *** Programmatically create, schedule, and manage ad campaigns to engage people on X. Create and manage Tailored Audiences using X, web or mobile data you provide. Draft and publish posts, extend the functionality of posts with Cards and manage and upload images and videos. Use async and synchronous API endpoints to retrieve granular insights of ad campaigns by a full range of metrics. Check out our curated selection of X-built and community-supported client libraries. We have built out a Postman collection for our v2 endpoints to help you explore the API using their visual client! If you would rather work with our endpoints using a command line tool, xurl is built for you! ## Need help? Visit our support section, where you can find troubleshooting tips, contact details, live API status monitor, and other helpful information that can help you understand how to overcome any obstacle. [Visit the Ads API support section](https://developer.x.com/en/support/twitter-ads-api.html) *** Help us build the next generation of the X API: [Give us your product feedback >](https://twitterdevfeedback.uservoice.com/forums/930250-twitter-api) # API Consistency Source: https://docs.x.com/x-api/fundamentals/consistency Consistent patterns across X API v2 endpoints X API v2 is designed with consistent patterns across all endpoints. Once you learn how one endpoint works, the same patterns apply everywhere. *** ## Consistent patterns ### URL structure All v2 endpoints follow a predictable pattern: ``` /version/resource/{id}?parameters /version/resource/verb?parameters ``` Examples: ``` /2/tweets/1234567890 # Get a specific post /2/tweets/search/recent # Search recent posts /2/users/by/username/xdevelopers # Get user by username /2/users/1234/followers # Get user's followers ``` ### Response structure All responses use the same top-level structure: ```json theme={null} { "data": { ... }, // Primary object(s) "includes": { ... }, // Expanded objects "meta": { ... }, // Pagination, counts "errors": [ ... ] // Partial errors (if any) } ``` ### ID format All IDs are returned as strings to ensure language compatibility: ```json theme={null} { "id": "1234567890123456789", "author_id": "2244994945" } ``` *** ## Fields and expansions The same [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) parameters work consistently: | Object | Fields parameter | Works across | | :----- | :--------------- | :---------------------------------- | | Post | `tweet.fields` | All endpoints returning posts | | User | `user.fields` | All endpoints returning users | | Media | `media.fields` | All endpoints with media expansions | | Poll | `poll.fields` | All endpoints with poll expansions | | Place | `place.fields` | All endpoints with place expansions | *** ## Object schemas The same object type has the same fields regardless of which endpoint returns it: * A Post from search has the same fields as a Post from lookup * A User from followers has the same fields as a User from search * Expanded objects match their standalone counterparts *** ## Authentication All endpoints use the same authentication methods: | Method | Header format | | :----------- | :----------------------------------- | | Bearer Token | `Authorization: Bearer {token}` | | OAuth 1.0a | `Authorization: OAuth {parameters}` | | OAuth 2.0 | `Authorization: Bearer {user_token}` | *** ## Error handling Errors follow a consistent format: ```json theme={null} { "title": "Invalid Request", "detail": "The query parameter is missing", "type": "https://api.x.com/2/problems/invalid-request" } ``` [See all error types →](/x-api/fundamentals/response-codes-and-errors) *** ## Pagination All paginated endpoints use the same token system: | Parameter | Description | | :----------------- | :------------------------------------------ | | `max_results` | Results per page | | `pagination_token` | Token from `next_token` or `previous_token` | [Learn more about pagination →](/x-api/fundamentals/pagination) *** ## Naming conventions * American English spelling (`favorites` not `favourites`) * Snake\_case for field names (`author_id`, `created_at`) * Consistent terminology (`retweet_count`, not `repost_count` in fields) *** ## Empty values Fields with no value are omitted rather than returned as `null`: ```json theme={null} // User without a bio { "id": "1234", "name": "Example User", "username": "example" // "description" is omitted, not null } ``` *** ## Entity consistency The `entities` object only contains entities parsed from text: * `urls` * `hashtags` * `mentions` * `cashtags` Media and polls are in `attachments`, not `entities`. *** ## What this means for you Patterns you learn on one endpoint apply to all endpoints. Same object types have same structures across the API. Build reusable functions for common patterns. Consistent error formats simplify troubleshooting. *** ## Report inconsistencies Found an inconsistency? Let us know: * [Developer Forum](https://devcommunity.x.com) * [Developer Feedback](https://t.co/devfeedback) # Consuming streaming data Source: https://docs.x.com/x-api/fundamentals/consuming-streaming-data Best practices for building clients that consume X streaming APIs 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 theme={null} 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 theme={null} { "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} theme={null} 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 # Conversation ID Source: https://docs.x.com/x-api/fundamentals/conversation-id Track and reconstruct conversation threads Every reply on X belongs to a conversation thread. The `conversation_id` field lets you identify, track, and reconstruct entire conversation trees. *** ## How it works When someone posts and others reply, all replies share the same `conversation_id`—the ID of the original post that started the conversation. ```mermaid actions={false} theme={null} 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`. *** ## Requesting conversation\_id Add `conversation_id` to your `tweet.fields`: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567891?tweet.fields=conversation_id,in_reply_to_user_id,referenced_tweets" \ -H "Authorization: Bearer $TOKEN" ``` Response: ```json theme={null} { "data": { "id": "1234567891", "text": "@user Great point!", "conversation_id": "1234567890", "in_reply_to_user_id": "2244994945", "referenced_tweets": [{ "type": "replied_to", "id": "1234567890" }] } } ``` *** ## Getting a full conversation Use `conversation_id` as a search operator to retrieve all posts in a thread: ```bash theme={null} curl "https://api.x.com/2/tweets/search/recent?\ query=conversation_id:1234567890&\ tweet.fields=author_id,created_at,in_reply_to_user_id&\ expansions=author_id" \ -H "Authorization: Bearer $TOKEN" ``` This returns all replies to the original post, sorted reverse-chronologically. *** ## Use cases Build the full conversation tree: ```python theme={null} import requests conversation_id = "1234567890" url = f"https://api.x.com/2/tweets/search/recent" params = { "query": f"conversation_id:{conversation_id}", "tweet.fields": "author_id,in_reply_to_user_id,referenced_tweets,created_at", "max_results": 100 } response = requests.get(url, headers=headers, params=params) replies = response.json()["data"] # Sort by created_at to get chronological order replies.sort(key=lambda x: x["created_at"]) ``` Stream replies to specific conversations in real-time: ```bash theme={null} # Add a filtered stream rule for a conversation curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Authorization: Bearer $TOKEN" \ -d '{"add": [{"value": "conversation_id:1234567890"}]}' ``` Count replies in a conversation: ```bash theme={null} curl "https://api.x.com/2/tweets/counts/recent?\ query=conversation_id:1234567890" \ -H "Authorization: Bearer $TOKEN" ``` *** ## Related fields | Field | Description | | :-------------------- | :----------------------------------------------------- | | `conversation_id` | ID of the original post that started the thread | | `in_reply_to_user_id` | User ID of the post being replied to | | `referenced_tweets` | Array with `type: "replied_to"` and the parent post ID | *** ## Example: Full thread retrieval ```json theme={null} { "data": [ { "id": "1234567893", "text": "@user2 @user1 I agree with you both!", "conversation_id": "1234567890", "author_id": "3333333333", "created_at": "2024-01-15T12:05:00.000Z", "in_reply_to_user_id": "2222222222", "referenced_tweets": [{"type": "replied_to", "id": "1234567892"}] }, { "id": "1234567892", "text": "@user1 That's interesting!", "conversation_id": "1234567890", "author_id": "2222222222", "created_at": "2024-01-15T12:03:00.000Z", "in_reply_to_user_id": "1111111111", "referenced_tweets": [{"type": "replied_to", "id": "1234567890"}] }, { "id": "1234567891", "text": "@user1 Great point!", "conversation_id": "1234567890", "author_id": "4444444444", "created_at": "2024-01-15T12:02:00.000Z", "in_reply_to_user_id": "1111111111", "referenced_tweets": [{"type": "replied_to", "id": "1234567890"}] } ], "meta": { "result_count": 3 } } ``` *** ## Notes * The original post's `conversation_id` equals its own `id` * `conversation_id` is available on all v2 endpoints that return posts * Use with [filtered stream](/x-api/posts/filtered-stream/introduction) to monitor conversations in real-time * Combine with [pagination](/x-api/fundamentals/pagination) for large threads *** ## Next steps Search by conversation\_id. Monitor conversations in real-time. # Data Dictionary Source: https://docs.x.com/x-api/fundamentals/data-dictionary Complete field reference for X API v2 objects The X API returns structured JSON objects representing posts, users, media, and more. This reference documents every field available for each object type. *** ## Quick navigation | Object | Description | Fields parameter | | :-------------------------- | :------------------------------ | :--------------- | | [Post (Tweet)](#post-tweet) | Posts, replies, reposts, quotes | `tweet.fields` | | [User](#user) | Account profiles and metadata | `user.fields` | | [Space](#space) | Live audio conversations | `space.fields` | | [List](#list) | Curated collections of accounts | `list.fields` | | [Media](#media) | Images, videos, GIFs | `media.fields` | | [Poll](#poll) | Poll questions and options | `poll.fields` | | [Place](#place) | Location and geo data | `place.fields` | Use [fields parameters](/x-api/fundamentals/fields) to request specific fields, and [expansions](/x-api/fundamentals/expansions) to include related objects. *** ## Post (Tweet) Posts are the core content unit on X. Each Post object includes text, metadata, and references to related objects like authors, media, and polls. **Default fields:** `id`, `text`, `edit_history_tweet_ids` Use `tweet.fields` to request additional fields and `expansions` to include related objects. ### All Post fields | Field Value | Type | Description | How it Can Be Used | | :-------------------------------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------- | | **id (default)** | string | The unique identifier of the requested Tweet. | Use this to programmatically retrieve a specific Tweet. | | **text (default)** | string | The actual UTF-8 text of the Tweet. See [twitter-text](https://github.com/twitter/twitter-text/) for details on valid characters. | Keyword extraction and sentiment analysis/classification. | | **edit\_history\_tweet\_ids (default)** | object | Unique identifiers indicating all versions of a Tweet. For Tweets with no edits, there will be one ID. For Tweets with an edit history, there will be multiple IDs. | Use this information to find the edit history of a Tweet. | | **article** | object | Contains metadata for the Article present in this Tweet. | Use this to get the text and entities of an Article. | | **attachments** | object | Specifies the type of attachments (if any) present in this Tweet. | Understanding the objects returned for requested expansions. | | **author\_id** | string | The unique identifier of the User who posted this Tweet. | Hydrating User object, sharing dataset for peer review. | | **card\_uri** | string | The URI for the Card present in this tweet. | | | **community\_id** | string | The unique identifier for the Community this Post belongs to. | | | **context\_annotations** | array | Contains context annotations for the Tweet. | Entity recognition/extraction, topical analysis. | | **conversation\_id** | string | The Tweet ID of the original Tweet of the conversation (which includes direct replies, replies of replies). | Use this to reconstruct the conversation from a Tweet. | | **created\_at** | date (ISO 8601) | Creation time of the Tweet. | Useful for time-series analysis and understanding when a Tweet was created. | | **display\_text\_range** | array | An array containing a start and end index for the portion of text that gets displayed. | Useful for knowing which portion of text is displayed by default for long posts. | | **edit\_controls** | object | Indicates how much longer the Tweet can be edited and the number of remaining edits. | Use this to determine if a Tweet is eligible for editing. | | **entities** | object | Entities that have been parsed out of the text of the Tweet. See entities in Twitter Objects. | Provides additional information about hashtags, URLs, mentions, etc. | | **geo** | object | Indicates the location or place of a geo-tagged Tweet. | Use this to determine get location of a geo-tagged Tweet. | | **in\_reply\_to\_user\_id** | string | If the represented Tweet is a reply, this field will contain the original Tweet’s author ID. | Determine if a Tweet was in reply to another Tweet. | | **lang** | string | Language of the Tweet, if detected by Twitter. | Classify Tweets by spoken language. | | **non\_public\_metrics** | object | Non-public engagement metrics for the Tweet at the time of the request. Requires user context authentication. Includes `impression_count`, `user_profile_clicks`, `url_link_clicks`, and `engagements`. | Determine total impressions and engagement generated for the Tweet. | | **note\_tweet** | object | Contains the full text of a Post for long-form Posts (>280 characters). | Get the complete text of a post. | | **organic\_metrics** | object | Engagement metrics, tracked in an organic context, for the Tweet at the time of the request. Requires user context authentication. | Measure organic engagement for the Tweet. | | **possibly\_sensitive** | boolean | Indicates if the content may be recognized as sensitive. | Study circulation of certain types of content. | | **promoted\_metrics** | object | Engagement metrics, tracked in a promoted context, for the Tweet at the time of the request. Requires user context authentication. | Measure engagement for the Tweet when it was promoted. | | **public\_metrics** | object | Public engagement metrics for the Tweet at the time of the request. Includes `retweet_count`, `reply_count`, `like_count`, `quote_count`, `impression_count`, and `bookmark_count`. | Measure Tweet engagement. | | **referenced\_tweets** | array | A list of Tweets this Tweet refers to, such as Retweets, quoted Tweets, or replies. | Understand conversational aspects of retweets, etc. | | **reply\_settings** | string | Shows who can reply to a given Tweet. Options are "everyone", "mentioned\_users", and "followers". | Determine conversation reply settings for the Tweet. | | **withheld** | object | Contains withholding details for [withheld content](https://help.x.com/en/rules-and-policies/tweet-withheld-by-country). | | | **scopes** | object | Contains scope details for the tweet. | Indicates who can view the post. Only returned for promoted posts. | | **media\_metadata** | array | Contains metadata for media attachments of the Tweet. | Get additional metadata like the `alt_text` of a Tweet's media attachment. | **Retrieving a Tweet Object** **Sample Request** In the following request, we are requesting fields for the Tweet on the [Tweets lookup](/resources/fundamentals/rate-limits) endpoint. Be sure to replace `$BEARER_TOKEN` with your own generated [Bearer Token](/resources/fundamentals/authentication). ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets?ids=1212092628029698048&tweet.fields=attachments,author_id,context_annotations,created_at,entities,geo,id,in_reply_to_user_id,lang,possibly_sensitive,public_metrics,referenced_tweets,text,withheld&expansions=referenced_tweets.id' --header 'Authorization: Bearer $BEARER_TOKEN' ``` **Sample Response** ``` { "data": [ { "text": "We believe the best future version of our API will come from building it with YOU. Here’s to another great year with everyone who builds on the Twitter platform. We can’t wait to continue working with you in the new year. https://t.co/yvxdK6aOo2", "edit_history_tweet_ids": [ "1212092628029698048" ], "lang": "en", "in_reply_to_user_id": "2244994945", "entities": { "urls": [ { "start": 222, "end": 245, "url": "https://t.co/yvxdK6aOo2", "expanded_url": "https://x.com/LovesNandos/status/1211797914437259264/photo/1", "display_url": "pic.x.com/yvxdK6aOo2", "media_key": "16_1211797899316740096" } ], "annotations": [ { "start": 42, "end": 44, "probability": 0.5359, "type": "Other", "normalized_text": "API" }, { "start": 144, "end": 150, "probability": 0.9832, "type": "Other", "normalized_text": "Twitter" } ] }, "author_id": "2244994945", "referenced_tweets": [ { "type": "replied_to", "id": "1212092627178287104" } ], "id": "1212092628029698048", "public_metrics": { "retweet_count": 7, "reply_count": 3, "like_count": 38, "quote_count": 1, "bookmark_count": 2, "impression_count": 1250 }, "context_annotations": [ { "domain": { "id": "29", "name": "Events [Entity Service]", "description": "Real world events. " }, "entity": { "id": "1186637514896920576", "name": " New Years Eve" } }, { "domain": { "id": "29", "name": "Events [Entity Service]", "description": "Real world events. " }, "entity": { "id": "1206982436287963136", "name": "Happy New Year: It’s finally 2020 everywhere!", "description": "Catch fireworks and other celebrations as people across the globe enter the new year.\nPhoto via @GettyImages " } }, { "domain": { "id": "119", "name": "Holiday", "description": "Holidays like Christmas or Halloween" }, "entity": { "id": "1186637514896920576", "name": " New Years Eve" } }, { "domain": { "id": "119", "name": "Holiday", "description": "Holidays like Christmas or Halloween" }, "entity": { "id": "1206982436287963136", "name": "Happy New Year: It’s finally 2020 everywhere!", "description": "Catch fireworks and other celebrations as people across the globe enter the new year.\nPhoto via @GettyImages " } }, { "domain": { "id": "30", "name": "Entities [Entity Service]", "description": "Entity Service top level domain, every item that is in Entity Service should be in this domain" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "131", "name": "Unified Twitter Taxonomy", "description": "A taxonomy of user interests. " }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "131", "name": "Unified Twitter Taxonomy", "description": "A taxonomy of user interests. " }, "entity": { "id": "847868745150119936", "name": "Family & relationships", "description": "Hobbies and interests" } }, { "domain": { "id": "131", "name": "Unified Twitter Taxonomy", "description": "A taxonomy of user interests. " }, "entity": { "id": "1196446161223028736", "name": "Social media" } }, { "domain": { "id": "29", "name": "Events [Entity Service]", "description": "Real world events. " }, "entity": { "id": "1206982436287963136", "name": "Happy New Year: It’s finally 2020 everywhere!", "description": "Catch fireworks and other celebrations as people across the globe enter the new year.\nPhoto via @GettyImages " } }, { "domain": { "id": "119", "name": "Holiday", "description": "Holidays like Christmas or Halloween" }, "entity": { "id": "1206982436287963136", "name": "Happy New Year: It’s finally 2020 everywhere!", "description": "Catch fireworks and other celebrations as people across the globe enter the new year.\nPhoto via @GettyImages " } } ], "created_at": "2019-12-31T19:26:16.000Z", "attachments": { "media_keys": [ "16_1211797899316740096" ] }, "possibly_sensitive": false } ], "includes": { "tweets": [ { "text": "These launches would not be possible without the feedback you provided along the way, so THANK YOU to everyone who has contributed your time and ideas. Have more feedback? Let us know ⬇️ https://t.co/Vxp4UKnuJ9", "edit_history_tweet_ids": [ "1212092627178287104" ], "lang": "en", "in_reply_to_user_id": "2244994945", "entities": { "urls": [ { "start": 187, "end": 210, "url": "https://t.co/Vxp4UKnuJ9", "expanded_url": "https://twitterdevfeedback.uservoice.com/forums/921790-twitter-developer-labs", "display_url": "twitterdevfeedback.uservoice.com/forums/921790-…", "status": 200, "title": "Updates on our feedback channels", "description": "We build our developer platform in the open, with your input and feedback. Over the past year, hearing directly from you and the users of your apps has helped us build developer products that cater to the use case you helped us identify. We want to make this the way we build products, and moving forward, we are consolidating our feedback channels. Meeting you where you are Effective today, we are going to retire our UserVoice feedback channel in favor of more frequent direct engagements with y...", "unwound_url": "https://devcommunity.x.com/t/updates-on-our-feedback-channels/169706" } ] }, "author_id": "2244994945", "referenced_tweets": [ { "type": "replied_to", "id": "1212092626247110657" } ], "id": "1212092627178287104", "public_metrics": { "retweet_count": 2, "reply_count": 1, "like_count": 19, "quote_count": 0, "bookmark_count": 0, "impression_count": 430 }, "created_at": "2019-12-31T19:26:16.000Z", "possibly_sensitive": false } ] } ``` ### User The user object contains Twitter user account metadata describing the referenced user. The user object is the primary object returned in the [users lookup](/x-api/users/lookup/introduction) endpoint. When requesting additional user fields on this endpoint, simply use the fields parameter `user.fields`. The user object can also be found as a child object and expanded in the Tweet object. The object is available for expansion with `?expansions=author_id` or `?expansions=in_reply_to_user_id` to get the condensed object with only default fields. Use the expansion with the field parameter: `user.fields` when requesting additional fields to complete the object.   | Field value | Type | Description | How it can be used | | :------------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | id (default) | string | The unique identifier of this user.

`"id": "2244994945"` | Use this to programmatically retrieve information about a specific Twitter user. | | name (default) | string | The name of the user, as they’ve defined it on their profile. Not necessarily a person’s name. Typically capped at 50 characters, but subject to change.

`"name": "Twitter Dev"` | | | username (default) | string | The Twitter screen name, handle, or alias that this user identifies themselves with. Usernames are unique but subject to change. Typically a maximum of 15 characters long, but some historical accounts may exist with longer names.

`"username": "TwitterDev"` | | | affiliation | object | Contains details about a user's affiliation. | Can be used to get a user's affiliate badge. | | confirmed\_email | string | The confirmed email of the authenticated user. | | | connection\_status | array | Provides a list of relation between the authenticating user and the user being looked up such as following, followed, follow request sent, follow request received, blocking, muting

"connection\_status": \[
           "follow\_request\_received",
           "follow\_request\_sent",
           "blocking",
           "followed\_by",
           "following",
           "muting"
] | Can be used to determine the connection status between the authenticating user and the user being looked up. | | created\_at | date (ISO 8601) | The UTC datetime that the user account was created on Twitter.

`"created_at": "2013-12-14T04:35:55.000Z"` | Can be used to determine how long a someone has been using Twitter | | description | string | The text of this user's profile description (also known as bio), if the user provided one.

`"description": "The voice of the X Dev team and your official source for updates, news, and events, related to the X API."` | | | entities | object | Contains details about text that has a special meaning in the user's description.

`"entities": {
       "url": {
           "urls": [
               {
                   "start": 0,
                   "end": 23,
                   "url": "https://t.co/3ZX3TNiZCY",
                   "expanded_url": "/content/developer-twitter/en/community",
                   "display_url": "developer.x.com/en/community"
               }
           ]
       },
       "description": {
           "urls": [
               {
                   "start": 0,
                   "end": 23,
                   "url": "https://t.co/3ZX3TNiZCY",
                   "expanded_url": "/content/developer-twitter/en/community",
                   "display_url": "developer.x.com/en/community"
               },
           "hashtags": [
               {
                   "start": 23,
                   "end": 30,
                   "tag": "DevRel"
               },
               {
                   "start": 113,
                   "end": 130,
                   "tag": "BlackLivesMatter"
               },
           "mentions": [
               {
                   "start": 0,
                   "end": 10,
                   "tag": "TwitterDev"
               },
           "cashtags": [
               {
                   "start": 12,
                   "end": 16,
                   "tag": "twtr"
               }
           ]
       }
   }` | Entities are JSON objects that provide additional information about hashtags, urls, user mentions, and cashtags associated with the description. Reference each respective entity for further details.

All user ******start****** indices are inclusive, while all user ******end****** indices are exclusive. | | is\_identity\_verified | boolean | Indicates if the user is ID verified. | | | location | string | The location specified in the user's profile, if the user provided one. As this is a freeform value, it may not indicate a valid location, but it may be fuzzily evaluated when performing searches with location queries.

`"location": "127.0.0.1"` | | | most\_recent\_tweet\_id | string | Unique identifier of this user's most recent Tweet. | Determine the most recent Tweet of the user. | | parody | boolean | Indicates whether or not this user account has the Parody label. | | | pinned\_tweet\_id | string | Unique identifier of this user's pinned Tweet.

`"pinned_tweet_id": "1255542774432063488"` | Determine the Tweet pinned to the top of the user’s profile. Can potentially be used to determine the user’s language. | | profile\_banner\_url | string | The URL to the profile banner for this user, as shown on the user's profile.

`"profile_banner_url": "https://pbs.twimg.com/profile_banners/1716450569358098432/1721022977"` | Can be used to download this user's profile banner. | | profile\_image\_url | string | The URL to the profile image for this user, as shown on the user's profile.

`"profile_image_url": "https://pbs.twimg.com/profile_images/1267175364003901441/tBZNFAgA_normal.jpg"` | Can be used to download this user's profile image. | | protected | boolean | Indicates if this user has chosen to protect their Tweets (in other words, if this user's Tweets are private).

`"protected": false` | | | public\_metrics | object | Contains details about activity for this user.

`"public_metrics": {             "followers_count": 507902,             "following_count": 1863,             "tweet_count": 3561,             "listed_count": 1550         }` | Can potentially be used to determine a Twitter user’s reach or influence, quantify the user’s range of interests, and the user’s level of engagement on Twitter. | | receives\_your\_dm | boolean | Indicates whether or not this user will receive the authenticated user's DM. | | | subscription | object | Contains details on whether or not the user is subscribed to the authenticated user. | | | subscription\_type | string | A string representing the type of X Premium subscription the authenticated user has. Example: `None`, `Basic`, `Premium`,`PremiumPlus`. Will always return `None` if the user is not the authenticated user. | | | url | string | The URL specified in the user's profile, if present.

`"url": "https://t.co/3ZX3TNiZCY"` | A URL provided by a Twitter user in their profile. This could be a homepage, but is not always the case. | | verified | boolean | Indicates if this user is a verified Twitter User.

`"verified": true` | Indicates whether or not this Twitter user has a verified account. A verified account lets people know that an account of public interest is authentic. | | verified\_followers\_count | string | A string representing the number of verified followers of a user. | | | verified\_type | string | A string representing the type of verification a user has. Example: "blue", "business", "government" | | | withheld | object | Contains withholding details for [withheld content](https://help.x.com/en/rules-and-policies/tweet-withheld-by-country), if applicable. | | **Retrieving a user object** **Sample Request** In the following request, we are requesting fields for the user on the [users lookup](/x-api/users/lookup/introduction) endpoint. Be sure to replace `$BEARER_TOKEN` with your own generated [Bearer Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token).   ```{ theme={null} curl --request GET 'https://api.x.com/2/users? ids=2244994945&user.fields=created_at,description,entities,id,location,name,pinned_tweet_id,profile_image_url,protected,url,username,verified,withheld&expansions=pinned_tweet_id' --header 'Authorization: Bearer $BEARER_TOKEN' } ``` **Sample Response** ```{ theme={null} "data": [ { "id": "2244994945", "name": "Twitter Dev", "username": "TwitterDev", "location": "127.0.0.1", "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "/content/developer-twitter/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 23, "end": 30, "tag": "DevRel" }, { "start": 113, "end": 130, "tag": "BlackLivesMatter" } ] } }, "verified": true, "description": "The voice of Twitter's #DevRel team, and your official source for updates, news, & events about Twitter's API. \n\n#BlackLivesMatter", "url": "https://t.co/3ZX3TNiZCY", "profile_image_url": "https://pbs.twimg.com/profile_images/1267175364003901441/tBZNFAgA_normal.jpg", "protected": false, "pinned_tweet_id": "1255542774432063488", "created_at": "2013-12-14T04:35:55.000Z" } ], "includes": { "tweets": [ { "id": "1255542774432063488", "text": "During these unprecedented times, what’s happening on Twitter can help the world better understand & respond to the pandemic. \n\nWe're launching a free COVID-19 stream endpoint so qualified devs & researchers can study the public conversation in real-time. https://t.co/BPqMcQzhId" } ] } } ``` ### Space Spaces allow expression and interaction via live audio conversations. The Space data dictionary contains relevant metadata about a Space; all the details are updated in real time. User objects can be found and expanded in the user resource. These objects are available for expansion by adding at least one of `host_ids`, `creator_id`, `speaker_ids`, `mentioned_user_ids` to the `expansions` query parameter. Unlike Tweets, Spaces are ephemeral and become unavailable after they end or when they are canceled by their creator. When your app handles Spaces data, you are responsible for returning the most up-to-date information and must remove data that is no longer available from the platform. The [Spaces lookup endpoints](/x-api/spaces/lookup/introduction) can help you ensure you respect the users’ expectations and intent. | Field Value | Type | Description | How it can be used | | :----------------- | :-------------- | :------------------------------------------------------------------------------------------ | :------------------------------------------------------ | | id (default) | string | The unique identifier of the requested Space.
`"id": "1zqKVXPQhvZJB"` | Uniquely identify a Space returned in the response. | | state (default) | string | Indicates if the Space has started, will start, or has ended.
`"state": "live"` | Filter live or scheduled Spaces. | | created\_at | date (ISO 8601) | Creation time of this Space.
`"created_at": "2021-07-04T23:12:08.000Z"` | Understand when a Space was created and sort by time. | | creator\_id | string | Unique identifier of the Space creator.
`"creator_id": "2244994945"` | | | ended\_at | date (ISO 8601) | Time when the Space ended, if applicable.
`"ended_at": "2021-07-04T00:11:44.000Z"` | Determine when a live Space ended for runtime duration. | | host\_ids | array | Unique identifiers of the Space hosts.
`"host_ids": ["2244994945", "6253282"]` | Expand User objects, understand engagement. | | lang | string | Language of the Space, if detected.
`"lang": "en"` | Classify Spaces by language. | | is\_ticketed | boolean | Indicates if this is a ticketed Space.
`"is_ticketed": false` | Highlight content of interest. | | invited\_user\_ids | array | List of user IDs invited as speakers.
`"invited_user_ids": ["2244994945", "6253282"]` | Expand User objects, understand engagement. | | participant\_count | integer | Number of users in the Space, including Hosts and Speakers.
`"participant_count": 420` | Understand engagement, create reports. | | subscriber\_count | integer | Number of people who set a reminder for a Space.
`"subscriber_count": 36` | Understand event interest. | | scheduled\_start | date (ISO 8601) | Scheduled start time of the Space.
`"scheduled_start": "2021-07-14T08:00:00.000Z"` | Integrate with calendar notifications. | | speaker\_ids | array | List of users who spoke at any point.
`"speaker_ids": ["2244994945", "6253282"]` | Expand User objects, understand engagement. | | started\_at | date (ISO 8601) | Actual start time of a Space.
`"started_at": "2021-07-14T08:00:12.000Z"` | Determine Space start time. | | title | string | Title of the Space.
`"title": "Say hello to the Space data object!"` | Understand keywords, hashtags, mentions. | | topic\_ids | array | IDs of topics selected by the Space creator.
`"topic_ids": ["2244994945", "6253282"]` | Understand keywords, hashtags, mentions. | | updated\_at | date (ISO 8601) | Last update to Space metadata.
`"updated_at": "2021-07-11T14:44:44.000Z"` | Keep information up to date. | \*\*Retrieving a Space Object \*\* **Sample Request** In the following request, we are requesting fields for the Space on the [Spaces lookup endpoint](/x-api/spaces/lookup/introduction). Be sure to replace `$BEARER_TOKEN` with your own generated [Bearer Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token). ```bash theme={null} curl "https://api.x.com/2/spaces/1DXxyRYNejbKM?space.fields=created_at,creator_id,created_athost_ids,lang,is_ticketed,invited_user_ids,participant_count,scheduled_start,speaker_ids,started_at,state,title,updated_at&expansions=creator_id,host_ids,invited_user_ids,speaker_ids" --header "Authorization: Bearer $BEARER_TOKEN" ``` \*\* Sample Response \*\* ``` { "data": { "id": "1zqKVXPQhvZJB", "state": "live", "created_at": "2021-07-04T23:12:08.000Z", "host_ids": [ "2244994945", "6253282" ], "lang": "en", "is_ticketed": false, "invited_user_ids": [ "2244994945", "6253282" ], "participant_count": 420, "scheduled_start": "2021-07-14T08:00:00.000Z", "speaker_ids": [ "2244994945", "6253282" ], "started_at": "2021-07-14T08:00:12.000Z", "title": "Say hello to the Space data object!", "updated_at": "2021-07-11T14:44:44.000Z" }, "includes": { "users": [ { "id": "2244994945", "name": "Twitter Dev", "username": "TwitterDev" }, { "id": "6253282", "name": "Twitter API", "username": "TwitterAPI" } ] } } ``` ### List The list object contains [Twitter Lists](https://help.x.com/en/using-twitter/twitter-lists) metadata describing the referenced List. The List object is the primary object returned in the List lookup endpoint. When requesting additional List fields on this endpoint, simply use the fields parameter `list.fields`. The List object is not found as a child of other data objects. However, user objects can be found and expanded in the user resource. These objects are available for expansion by adding `owner_id` to the `expansions` query parameter. Use this expansion with the `list.fields` field parameter when requesting additional fields to complete the primary List object and `user.fields` to complete the expansion object. | Field Value | Type | Description | How it can be used | | :-------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- | | id (default) | string | The unique identifier of this List.
`"id": "2244994945"` | Use this to programmatically retrieve information about a specific List. | | name (default) | string | The name of the List, as defined when creating the List.
`"name": "Twitter Lists"` | | | created\_at | date (ISO 8601) | The UTC datetime when the List was created.
`"created_at": "2013-12-14T04:35:55.000Z"` | Determine how long a List has been on Twitter. | | description | string | A brief description to inform users about the List.
`"description": "People that are active members of the Bay area cycling community on Twitter."` | | | follower\_count | integer | Shows how many users follow this List.
`"follower_count": 198` | | | member\_count | integer | Shows how many members are part of this List.
`"member_count": 60` | | | private | boolean | Indicates if the List is private.
`"private": false` | | | owner\_id | string | Unique identifier of this List's owner.
`"owner_id": "1255542774432063488"` | Can be used to find out if this user owns other Lists and expand User objects. | **Retrieving a User Object** **Sample Request** In the following request, we are requesting fields for the user on the [List lookup by ID](/x-api/lists/list-lookup/introduction) endpoint. Replace `$BEARER_TOKEN` with your generated [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). ```bash theme={null} curl --request GET 'https://api.x.com/2/lists/1355797419175383040?list.fields=created_at,description,private,follower_count,member_count,owner_id&expansions=owner_id' --header 'Authorization: Bearer $BEARER_TOKEN' ``` \*\* Sample Response\*\* ``` { "data": { "name": "Twitter Comms", "member_count": 60, "id": "1355797419175383040", "private": false, "description": "", "follower_count": 198, "owner_id": "257366942", "created_at": "2021-01-31T08:37:48.000Z" }, "includes": { "users": [ { "created_at": "2011-02-25T07:51:26.000Z", "name": "Ashleigh Hay 🤸🏼‍♀️", "id": "257366942", "username": "shleighhay", "verified": false } ] } } ``` ### Media Media refers to any image, GIF, or video attached to a Tweet. The media object is not a primary object on any endpoint, but can be found and expanded in the Tweet object. The object is available for expansion with `?expansions=attachments.media_keys` to get the condensed object with only default fields. Use the expansion with the field parameter: `media.fields` when requesting additional fields to complete the object. | Field value | Type | Description | How it can be used | | :------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | | media\_key (default) | string | Unique identifier of the expanded media content.
` "media_key": "13_1263145212760805376"` | Can be used to programmatically retrieve media | | type (default) | string | Type of content (animated\_gif, photo, video).
` "type": "video"` | Classify the media as a photo, GIF, or video | | url | string | A direct URL to the media file on Twitter. | Returns a Media object with a URL field for photos | | duration\_ms | integer | Available when type is video. Duration in milliseconds of the video.
` "duration_ms": 46947` | | | height | integer | Height of this content in pixels.
` "height": 1080` | | | non\_public\_metrics | object | Non-public engagement metrics for the media content at the time of the request. Requires user context authentication.
` "non_public_metrics": { "playback_0_count": 1561, "playback_100_count": 116, "playback_25_count": 559, "playback_50_count": 305, "playback_75_count": 183,}` | Determine video engagement: how many users played through to each quarter of the video. | | organic\_metrics | object | Engagement metrics for the media content, tracked in an organic context, at the time of the request. Requires user context authentication.
` "organic_metrics": { "playback_0_count": 1561, "playback_100_count": 116, "playback_25_count": 559, "playback_50_count": 305, "playback_75_count": 183, "view_count": 629}` | Determine organic media engagement. | | preview\_image\_url | string | URL to the static placeholder preview of this content.
` "preview_image_url": "https://pbs.twimg.com/media/EYeX7akWsAIP1_1.jpg"` | | | promoted\_metrics | object | Engagement metrics for the media content, tracked in a promoted context, at the time of the request. Requires user context authentication.
` "promoted_metrics": { "playback_0_count": 259, "playback_100_count": 15, "playback_25_count": 113, "playback_50_count": 57, "playback_75_count": 25, "view_count": 124}` | Determine media engagement when the Tweet was promoted. | | public\_metrics | object | Public engagement metrics for the media content at the time of the request.
` "public_metrics": { "view_count": 6865141}` | Determine total number of views for the video attached to the Tweet. | | width | integer | Width of this content in pixels.
` "width": 1920` | | | alt\_text | string | A description of an image to enable and support accessibility. Can be up to 1000 characters long. Alt text can only be added to images at the moment.
` "alt_text": "Rugged hills along the Na Pali coast on the island of Kauai"` | Can be used to provide a written description of an image in case a user is visually impaired. | | variants | array | Each media object may have multiple display or playback variants, with different resolutions or formats.
` "variants": [{ "bit_rate": 632000, "content_type": "video/mp4", "url": "https://video.twimg.com/ext_tw_video/1527322141724532740/pu/vid/320x568/lnBaR2hCqE-R_90a.mp4?tag=12"}]` | | **Retrieving a media object** **Sample Request** In the following request, we are requesting fields for the media object attached to the Tweet on the [Tweet lookup](/x-api/posts/lookup/introduction) endpoint. Since media is a child object of a Tweet, the `attachment.media_keys` expansion is required. Be sure to replace `$BEARER_TOKEN` with your own generated [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets?ids=1263145271946551300&expansions=attachments.media_keys&media.fields=duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width,alt_text' --header 'Authorization: Bearer $BEARER_TOKEN' ``` ``` { "data": [ { "text": "Testing, testing...\n\nA new way to have a convo with exactly who you want. We’re starting with a small % globally, so keep your 👀 out to see it in action. https://t.co/pV53mvjAVT", "id": "1263145271946551300", "attachments": { "media_keys": [ "13_1263145212760805376" ] } } ], "includes": { "media": [ { "duration_ms": 46947, "type": "video", "height": 1080, "media_key": "13_1263145212760805376", "public_metrics": { "view_count": 6909260 }, "preview_image_url": "https://pbs.twimg.com/media/EYeX7akWsAIP1_1.jpg", "width": 1920 } ] } } ``` ### Poll A poll included in a Tweet is not a primary object on any endpoint, but can be found and expanded in the Tweet object. The object is available for expansion with `?expansions=attachments.poll_ids` to get the condensed object with only default fields. Use the expansion with the field parameter: `poll.fields` when requesting additional fields to complete the object. | Field value | Type | Description | | :---------------- | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------- | | id (default) | string | Unique identifier of the expanded poll. | | | | `{"id": "1199786642791452673"}` | | options (default) | array | Contains objects describing each choice in the referenced poll. | | | | `{"options": [ { "position": 1, "label": "“C Sharp”", "votes": 795 }, { "position": 2, "label": "“C Hashtag”", "votes": 156 } ]}` | | duration\_minutes | integer | Specifies the total duration of this poll. | | | | `{"duration_minutes": 1440}` | | end\_datetime | date (ISO 8601) | Specifies the end date and time for this poll. | | | | `{"end_datetime": "2019-11-28T20:26:41.000Z"}` | | voting\_status | string | Indicates if this poll is still active and can receive votes, or if the voting is now closed. | | | | `{"voting_status": "closed"}` | **Retrieving a poll object** **Sample Request** In the following request, we are requesting fields for the poll object attached to the Tweet on the [Tweets lookup](/x-api/posts/lookup/introduction) endpoint. Since poll is a child object of a Tweet, the `attachments.poll_id` expansion is required. Be sure to replace `$BEARER_TOKEN` with your own generated [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets?ids=1199786642791452673&expansions=attachments.poll_ids&poll.fields=duration_minutes,end_datetime,id,options,voting_status' --header 'Authorization: Bearer $BEARER_TOKEN' ``` **Sample Response** ``` { "data": [ { "text": "C#", "id": "1199786642791452673", "attachments": { "poll_ids": [ "1199786642468413448" ] } } ], "includes": { "polls": [ { "id": "1199786642468413448", "voting_status": "closed", "duration_minutes": 1440, "options": [ { "position": 1, "label": "“C Sharp”", "votes": 795 }, { "position": 2, "label": "“C Hashtag”", "votes": 156 } ], "end_datetime": "2019-11-28T20:26:41.000Z" } ] } } ``` ### Place The place tagged in a Tweet is not a primary object on any endpoint, but can be found and expanded in the Tweet resource. The object is available for expansion with `?expansions=geo.place_id` to get the condensed object with only default fields. Use the expansion with the field parameter: `place.fields` when requesting additional fields to complete the object. | Field value | Type | Description | How it can be used | | :------------------- | :----- | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------- | | full\_name (default) | string | A longer-form detailed place name. | Classify a Tweet by a specific place name | | | | `"full_name": "Manhattan, NY"` | | | id (default) | string | The unique identifier of the expanded place, if this is a point of interest tagged in the Tweet. | Use this to programmatically retrieve a place | | | | `"id": "01a9a39529b27f36"` | | | contained\_within | array | Returns the identifiers of known places that contain the referenced place. | | | country | string | The full-length name of the country this place belongs to. | Classify a Tweet by country name | | | | `"country": "United States"` | | | country\_code | string | The ISO Alpha-2 country code this place belongs to. | Classify a Tweet by country code | | | | `"country_code": "US"` | | | geo | object | Contains place details in GeoJSON format. | | | | | \`\`\`json | | | | | "geo": | | | | | "type": "Feature", | | | | | "bbox": \[ | | | | | -74.026675, | | | | | 40.683935, | | | | | -73.910408, | | | | | 40.877483 | | | | | ], | | | | | "properties": | | | | | } | | | | | \`\`\` | | | name | string | The short name of this place. | Classify a Tweet by a specific place name | | | | `"name": "Manhattan"` | | | place\_type | string | Specified the particular type of information represented by this place information, such as a city name, or a point of interest. | Classify a Tweet by a specific type of place | | | | `"place_type": "city"` | | **Retrieving a place object** **Sample Request** In the following request, we are requesting fields for the place object attached to the Tweet on the [Tweets lookup](/x-api/posts/lookup/introduction) endpoint. Since place is a child object of a Tweet, the `geo.place_id` expansion is required. Be sure to replace `$BEARER_TOKEN` with your own generated [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets?ids=1136048014974423040&expansions=geo.place_id&place.fields=contained_within,country,country_code,full_name,geo,id,name,place_type' --header 'Authorization: Bearer $BEARER_TOKEN' ``` **Sample Response** ``` { "data": [ { "text": "We’re sharing a live demo of the new Twitter Developer Labs program, led by a member of our DevRel team, @jessicagarson #TapIntoTwitter https://t.co/ghv7f4dW5M", "id": "1136048014974423040", "geo": { "place_id": "01a9a39529b27f36" } } ], "includes": { "places": [ { "geo": { "type": "Feature", "bbox": [ -74.026675, 40.683935, -73.910408, 40.877483 ], "properties": {} }, "country_code": "US", "name": "Manhattan", "id": "01a9a39529b27f36", "place_type": "city", "country": "United States", "full_name": "Manhattan, NY" } ] } } ``` ### Direct Message events Direct Message (DM) conversations are made up of events. The X API v2 currently supports three event types: MessageCreate, ParticipantsJoin, and ParticipantsLeave. DM event objects are returned by the [Direct Message lookup](/x-api/direct-messages/lookup/introduction) endpoints, and a MessageCreate event is created when Direct Messages are successfully created with the [Manage Direct Messages](/x-api/direct-messages/lookup/introduction) endpoints. When requesting DM events, there are three default event object attributes, or fields, included: id, event\_type, and text. To receive additional event fields, use the [fields](/x-api/fundamentals/fields) parameter dm\_event.fields to select others. Other available event fields include the following: dm\_conversation\_id, created\_at, sender\_id, attachments, participant\_ids, and referenced\_tweets. Several of these fields provide the IDs of other X objects related to the Direct Message event: * sender\_id - The ID of the account that sent the message, or who invited a participant to a group conversation * partricipants\_ids - An array of account IDs. For ParticipantsJoin and ParticipantsLeave events this array will contain a single ID of the account that created the event * attachments - Provides media IDs for content that has been uploaded to Twitter by the sender * referenced\_tweets - If a Tweet URL is found in the text field, the ID of that Tweet is included in the response The sender\_id, participant\_ids, referenced\_tweets.id, and attachments.media\_keys [expansions](/x-api/fundamentals/expansions) are available to expand on these Twitter object IDs. | | | | | | :-------------------- | :----------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Field value** | **Type** | **Description** | **How it can be used** | | id (default) | string | The unique identifier of the event.

"id": "1050118621198921728" | Use this to programmatically retrieve a specific conversation event (available with v1.1 endpoints). | | event\_type (default) | string | Describes the type of event. Three types are currently supported: 

\* MessageCreate

\* ParticipantsJoin

\* ParticipantsLeave


"event\_type": "MessageCreate" | When retrieving a conversation's history, understanding when messages were created, and for group conversations, understanding when participants joined and left. All GET methods support filtering on specific event types with the event\_type= query parameter. . | | text (default) | string | The actual UTF-8 text of the Direct Message. 

"text": "Hello, just you!" | With chatbots, this can be used to analyze message contents and determining automated responses. Could be used to build conversation search features. | | entities | object | Entities that have been parsed out of the text of the DM. | Provides additional information about hashtags, URLs, mentions, etc. | | sender\_id | string | ID of the User creating the event. To expand this object in the response, include sender\_id as an expansion  and use the user.fields query parameter to specify User object attributes of interest.

"sender\_id": "906948460078698496" | Retrieve the User object of who created the MessageCreate or ParticipantsJoin event. | | participant\_ids | array (of strings) | IDs of the participants joining and leaving a group conversation. Also used when creating new group conversations. To expand this object in the response, include participant\_ids as an expansion and use the user.fields query parameter to specify User object attributes of interest.

"participant\_ids": \[

     "906948460078698496"

] | Used to retrieve User objects for participants joining and leaving group conversations. | | dm\_conversation\_id | string | The unique identifier of the conversation the event is apart of.

"dm\_conversation\_id": "1584988213961031680" | Use this to programmatically retrieve events from a conversation, and add Direct Messages to it. | | created\_at | date (ISO 8601) | Creation time (UTC) of the Tweet.

"created\_at": "2019-06-04T23:12:08.000Z" | This field can be used to understand when a Direct Message was created or when conversation participants joined or left. | | referenced\_tweets | array | ID for any Tweet mentioned in the Direct Message text. To expand this object in the response, include referenced\_tweets.id as an expansion and use the tweet.fields query parameter to specify Tweet object attributes of interest.

"referenced\_tweets": \[

   

"id": "1578868150510456833"

   

] | When Direct Messages reference a Tweet, these IDs can be used to lookup the Tweet's details. | | attachments | object | For Direct Messages with attached Media, provides the media key of the uploaded content (photo, video, or GIF). To expand this object in the response, include attachments.media\_keys as an expansion and use the media.fields query parameter to specify media object attributes of interest. Currently, one attachment is supported. 

"attachments":

    "media\_keys": \[

        "3\_1136048009270239232"

    ]

| Understanding the media objects attached to Direct Messages. | **Retrieving a Direct Message event object** **Sample Request** For this example, we will build a request that retrieves events associated with a one-to-one conversation. This request will return fundamental Direct Message event fields, along with additional fields for referenced Tweets and their authors. Let's build a query that asks for: * Fundamental event attributes such as when it was created and what conversation it is part of (dm\_conversation). * The account ID and description of who sent the Direct Message. * The text of any referenced Tweet, and when it was posted. * The account ID and description of any referenced Tweet author. To return those attributes, your request query would include the following: `?dm_event.fields=id,sender_id,text,created_at,dm_conversation_id&expansions=sender_id,referenced_tweets.id&tweet.fields=created_at,text,author_id&user.fields=description` ```bash theme={null} curl --request GET 'https://api.x.com/2/dm_conversations/with/:participant_id/dm_events?tweet.fields=created_at,text,author_id&user.fields=description&expansions=sender_id,participant_ids,referenced_tweets.id&dm_event.fields=id,sender_id,text,participant_ids,created_at,' --header 'Authorization: Bearer $BEARER_TOKEN' ``` Be sure to replace \$BEARER\_TOKEN with your own generated [Bearer Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token). **Sample Response** ``` { "data": [{ "id": "1585047616894574596", "sender_id": "944480690", "text": "Hello, just you!", "created_at": "2022-10-25T23:16:15.000Z", "event_type": "MessageCreate", "dm_conversation_id": "944480690-906948460078698496" }, { "id": "1581048670673260549", "sender_id": "944480690", "text": "Simple Tweet link: https://t.co/IYFbRIdXHg", "referenced_tweets": [{ "id": "1578900353814519810" }], "created_at": "2022-10-14T22:25:52.000Z", "event_type": "MessageCreate", "dm_conversation_id": "944480690-906948460078698496" }, { "id": "1580705121553420292", "sender_id": "944480690", "text": "Adding a new 1-to-1 Direct Message.", "created_at": "2022-10-13T23:40:43.000Z", "event_type": "MessageCreate", "dm_conversation_id": "944480690-906948460078698496" } ], "includes": { "users": [{ "name": "API Demos", "description": "Hosting TwitterDev integrations... @TwitterDev #DevRel", "id": "944480690", "username": "FloodSocial" }, { "name": "the SnowBot", "description": "Home of the @TwitterDev SnowBot... Serving snow reports, snow photos, and snow research links... Chatbot is currently being remodeled for Twitter APIv2.", "id": "906948460078698496", "username": "SnowBotDev" } ], "tweets": [{ "text": "Feeling kind of bad that I didn’t wish everybody a happy new Colorado Water Year…\n\nHappy Water Year to all my Colorado friends and colleagues, new and old… \n\nMay this be a generous water year, although not too generous…", "id": "1578900353814519810", "created_at": "2022-10-09T00:09:13.000Z", "author_id": "944480690", "edit_history_tweet_ids": [ "1578900353814519810" ] } ] }, "meta": { "result_count": 3, "next_token": "18LAA581J5II7LA00C00ZZZZ", "previous_token": "1BLC45G1H8CAL5DG0G00ZZZZ" } } ``` ### Community Communities are dedicated places for X users to connect, share, and get closer to the discussions they care about most. Posts in Communities can be seen by anyone on X, but only others within the Community itself can engage and participate in the discussion. The Community object contains relevant metadata about a Community. | Field value | Type | Description | | | :------------ | :-------------- | :------------------------------------------------------------- | :- | | created\_at | date (ISO 8601) | Creation time of the Community. | | | id | string | The unique identifier of the Community. | | | name | string | The name of the Community. | | | description | string | The text of the Community’s description, if provided. | | | access | string | The access level of the Community.

Can be one of: | | | | | - `Public` | | | | | - `Closed` | | | join\_policy | string | The join policy for the Community.

Can be one of: | | | | | - `Open` | | | | | - `RestrictedJoinRequestsDisabled` | | | | | - `RestrictedJoinRequestsRequireAdminApproval` | | | | | - `RestrictedJoinRequestsRequireModeratorApproval` | | | | | - `SuperFollowRequired` | | | member\_count | integer | The number of members that have joined the Community. | | **Retrieving Community objects** **Sample Request** In the following request, we are requesting specific fields while searching for a list of Communities based on a provided keyword. Be sure to replace `$BEARER_TOKEN` with your own generated [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only). ```bash theme={null} curl --location 'https://api.x.com/2/communities/search?query=anime&community.fields=access,created_at,description,id,join_policy,member_count,name' --header 'Authorization: $BEARER_TOKEN' ``` **Sample Response** ``` { "data": [ { "id": "Q29tbXVuaXR5OjE3NTg3NDc4MTc2NDI3MDA5MjI=", "description": "Welcome to the Anime Community! Where anime fans gather to share their favorite shows and discuss everything anime-related.", "join_policy": "Open", "access": "Public", "member_count": 39915, "name": "Anime Community", "created_at": "2024-02-17T06:58:50.000Z" }, { "id": "Q29tbXVuaXR5OjE1MDY3OTM5NTMxMDYwNDI4OTE=", "description": "Join and text about anime 🥰", "join_policy": "Open", "access": "Public", "member_count": 26019, "name": "Anime World 🌸", "created_at": "2022-03-24T00:44:07.000Z" }, { "id": "Q29tbXVuaXR5OjE0OTY3NzYyMTU5Mzk1MzQ4NDk=", "description": "For all anime lovers and creators!", "join_policy": "Open", "access": "Public", "member_count": 5612, "name": "Anime", "created_at": "2022-02-24T09:17:13.000Z" } ], "meta": { "next_token": "7140dibdnow9c7btw481s8m561gat797rboud5r80xvzm" } } ``` ## How to use fields and expansions By default, the X API v2 data objects include a small number of default fields when making a request without the use of the [fields](/x-api/fundamentals/fields) or [expansions](/x-api/fundamentals/expansions) parameters. This guide will show you how to use the `fields` and `expansions` query parameters in your request to receive additional objects and fields in your response. In this guide, we will be requesting several fields in the following Tweet screenshot.   ![This image includes a screenshot of a Tweet posted by @X. You can see the Tweet text, username, published date and time, source, and public metrics. It also includes a video. ](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/ScreenShotDataDictionaryGuide.png.twimg.1920.png) As you can see in the screenshot, there are several visible pieces of information related to the Tweet, including the Tweet author, Tweet metrics, created timestamp, video, and video view count. There are also several pieces of data that are not visible within the screenshot, but are still available to request.  When making a request to the API, the default response is simple, containing only the default Tweet fields (id and text). You will also only receive the primary object that returns with the given endpoint that you are using, and not any of the associated data objects that might relate to the primary object. This simplicity, along with the fields and expansions parameters, enable you to request only those fields you require, depending on your use case.    #### Requesting additional fields and objects. First off, we will be requesting a [Tweet object](/x-api/fundamentals/data-dictionary#tweet) using a Tweet ID and the [GET /tweets endpoint](/x-api/posts/lookup/introduction). Request: ```bash theme={null} curl --request GET --url 'https://api.x.com/2/tweets?ids=1260294888811347969' \ --header 'Authorization: Bearer $BEARER_TOKEN' ``` Response: ``` { "data": [ { "id": "1260294888811347969", "text": "Don’t miss the Tweets about your Tweet. \n\nNow on iOS, you can see Retweets with comments all in one place. https://t.co/oanjZfzC6y" } ] } ``` The following step-by-step guide will show you how to retrieve the additional data we can see in the screenshot. 1. Identify the additional fields that you would like to request by using our [object model](/x-api/fundamentals/data-dictionary#tweet), or by reviewing the list of fields in the endpoints’ API reference pages. In this case, we will be requesting the following additional fields: attachments, author\_id, created\_at, public\_metrics. 2. Build the `tweet.fields` query parameter with the above fields as its value using a comma-separated list: `?tweet.fields=attachments,author_id,created_at,public_metrics` 3. Add the query parameter to the GET /tweets request that you made earlier. Request: `curl --request GET --url 'https://api.x.com/2/tweets?ids=1260294888811347969&tweet.fields=attachments,author_id,created_at,public_metrics' \ --header 'Authorization: Bearer $BEARER_TOKEN'` Response:   ``` { "data": [ { "id": "1260294888811347969", "text": "Don’t miss the Tweets about your Tweet. \n\nNow on iOS, you can see Retweets with comments all in one place. https://t.co/oanjZfzC6y", "author_id": "783214", "public_metrics": { "retweet_count": 5219, "reply_count": 1828, "like_count": 17141, "quote_count": 3255 }, "attachments": { "media_keys": [ "13_1260294804770041858" ] }, "created_at": "2020-05-12T19:44:51.000Z" } ] } ``` 4. Next, we are going to request fields related to the video that was included in the Tweet. To do so, we will use the `expansions` parameter with `attachments.media_keys` as the value, and add this to the request. ?expansions=attachments.media\_keys Request:   ```bash theme={null} curl --request GET --url 'https://api.x.com/2/tweets?ids=1260294888811347969&tweet.fields=attachments,author_id,created_at,public_metrics&expansions=attachments.media_keys' \ --header 'Authorization: Bearer $BEARER_TOKEN' ``` Response, with the media object represented in the includes object:   ``` { "data": [ { "id": "1260294888811347969", "text": "Don’t miss the Tweets about your Tweet. \n\nNow on iOS, you can see Retweets with comments all in one place. https://t.co/oanjZfzC6y", "public_metrics": { "retweet_count": 5219, "reply_count": 1828, "like_count": 17141, "quote_count": 3255 }, "created_at": "2020-05-12T19:44:51.000Z", "attachments": { "media_keys": [ "13_1260294804770041858" ] }, "author_id": "783214" } ], "includes": { "media": [ { "media_key": "13_1260294804770041858", "type": "video" } ] } } ``` 5. And finally, we are going to request the view count and duration of the video. These aren’t default fields so we have to specifically request them. Use the `media.fields` parameter with the comma-separated values, `public_metrics` and `duration_ms` in your request. ?media.fields=public\_metrics,duration\_ms Request:   `curl --request GET --url 'https://api.x.com/2/tweets?ids=1260294888811347969&tweet.fields=attachments,author_id,created_at,public_metrics&expansions=attachments.media_keys&media.fields=duration_ms,public_metrics' --header 'Authorization: Bearer $BEARER_TOKEN'` Response, which now includes all the data that can be seen in the Tweet screenshot:   ``` { "data": [ { "id": "1260294888811347969", "text": "Don’t miss the Tweets about your Tweet. \n\nNow on iOS, you can see Retweets with comments all in one place. https://t.co/oanjZfzC6y", "author_id": "783214", "public_metrics": { "retweet_count": 5219, "reply_count": 1828, "like_count": 17141, "quote_count": 3255 }, "created_at": "2020-05-12T19:44:51.000Z", "attachments": { "media_keys": [ "13_1260294804770041858" ] } } ], "includes": { "media": [ { "duration_ms": 36503, "media_key": "13_1260294804770041858", "public_metrics": { "view_count": 1534703 }, "type": "video" } ] } } ``` In total, we included the following parameters in this example: * ids=1260294888811347969 * tweet.fields=attachments,author\_id,created\_at,public\_metrics * expansions=attachments.media\_keys * media.fields=public\_metrics,duration\_ms   When tied together, here is what the full query string looks like: ``` ?ids=1260294888811347969&tweet.fields=attachments,author\_id,created\_at,public\_metrics&expansions=attachments.media\_keys&media.fields=public\_metrics,duration\_ms ``` ## X API v2 Example payloads ### Tweet ```json theme={null} { "data": [ { "conversation_id": "1304102743196356610", "id": "1307025659294674945", "possibly_sensitive": false, "public_metrics": { "retweet_count": 11, "reply_count": 2, "like_count": 70, "quote_count": 1 }, "entities": { "urls": [ { "start": 74, "end": 97, "url": "https://t.co/oeF3ZHeKQQ", "expanded_url": "https://dev.to/twitterdev/understanding-the-new-tweet-payload-in-the-twitter-api-v2-1fg5", "display_url": "dev.to/twitterdev/und…", "images": [ { "url": "https://pbs.twimg.com/news_img/1317156296982867969/2uLfv-Bh?format=jpg&name=orig", "width": 1128, "height": 600 }, { "url": "https://pbs.twimg.com/news_img/1317156296982867969/2uLfv-Bh?format=jpg&name=150x150", "width": 150, "height": 150 } ], "status": 200, "title": "Understanding the new Tweet payload in the X API v2", "description": "X recently announced the new X API v2, rebuilt from the ground up to deliver new features...", "unwound_url": "https://dev.to/twitterdev/understanding-the-new-tweet-payload-in-the-twitter-api-v2-1fg5" } ] }, "text": "Here’s an article that highlights the updates in the new Tweet payload v2 https://t.co/oeF3ZHeKQQ", "in_reply_to_user_id": "2244994945", "created_at": "2020-09-18T18:36:15.000Z", "author_id": "2244994945", "referenced_tweets": [ { "type": "replied_to", "id": "1304102743196356610" } ], "lang": "en", "source": "Twitter Web App" } ], "includes": { "users": [ { "created_at": "2013-12-14T04:35:55.000Z", "profile_image_url": "https://pbs.twimg.com/profile_images/1283786620521652229/lEODkLTh_normal.jpg", "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "https://developer.x.com/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 17, "end": 28, "tag": "TwitterDev" }, { "start": 105, "end": 116, "tag": "TwitterAPI" } ] } }, "id": "2244994945", "verified": true, "location": "127.0.0.1", "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", "pinned_tweet_id": "1293593516040269825", "username": "TwitterDev", "public_metrics": { "followers_count": 513961, "following_count": 2039, "tweet_count": 3635, "listed_count": 1672 }, "name": "Twitter Dev", "url": "https://t.co/3ZX3TNiZCY", "protected": false } ], "tweets": [ { "conversation_id": "1304102743196356610", "id": "1304102743196356610", "possibly_sensitive": false, "public_metrics": { "retweet_count": 31, "reply_count": 12, "like_count": 104, "quote_count": 4 }, "entities": { "mentions": [ { "start": 146, "end": 158, "username": "suhemparack" } ], "urls": [ { "start": 237, "end": 260, "url": "https://t.co/CjneyMpgCq", "expanded_url": "https://x.com/TwitterDev/status/1304102743196356610/video/1", "display_url": "pic.x.com/CjneyMpgCq" } ], "hashtags": [ { "start": 8, "end": 19, "tag": "TwitterAPI" } ] }, "attachments": { "media_keys": [ "13_1303848070984024065" ] }, "text": "The new #TwitterAPI includes some improvements to the Tweet payload. You’re probably wondering — what are the main differences? 🧐\n\nIn this video, @SuhemParack compares the v1.1 Tweet payload with what you’ll find using our v2 endpoints. https://t.co/CjneyMpgCq", "created_at": "2020-09-10T17:01:37.000Z", "author_id": "2244994945", "lang": "en", "source": "Twitter Media Studio" } ] } } ``` ### Tweet reply ```json theme={null} { "data": [ { "lang": "en", "conversation_id": "1296887091901718529", "text": "See how @PennMedCDH are using Twitter data to understand the COVID-19 health crisis 📊\n\nhttps://t.co/1tdA8uDWes", "referenced_tweets": [ { "type": "replied_to", "id": "1296887091901718529" } ], "possibly_sensitive": false, "entities": { "annotations": [ { "start": 30, "end": 36, "probability": 0.6318, "type": "Product", "normalized_text": "Twitter" } ], "mentions": [ { "start": 8, "end": 19, "username": "PennMedCDH" } ], "urls": [ { "start": 87, "end": 110, "url": "https://t.co/1tdA8uDWes", "expanded_url": "https://developer.x.com/en/use-cases/success-stories/penn", "display_url": "developer.x.com/en/use-cases/s…", "status": 200, "title": "Penn Medicine Center for Digital Health", "description": "Penn Med Center for Digital Health has created a COVID-19 Twitter map that includes charts detailing sentiment, symptoms reported, state-by-state data cuts, and border data on the COVID-19 outbreak. In addition, their Penn Med With You initiative uses aggregate regional information from Twitter to inform their website and text-messaging service. The service uses this information to disseminate relevant and timely resources.", "unwound_url": "https://developer.x.com/en/use-cases/success-stories/penn" } ] }, "id": "1296887316556980230", "public_metrics": { "retweet_count": 9, "reply_count": 3, "like_count": 26, "quote_count": 2 }, "author_id": "2244994945", "in_reply_to_user_id": "2244994945", "context_annotations": [ { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "123", "name": "Ongoing News Story", "description": "Ongoing News Stories like 'Brexit'" }, "entity": { "id": "1220701888179359745", "name": "COVID-19" } } ], "source": "Twitter Web App", "created_at": "2020-08-21T19:10:05.000Z" } ], "includes": { "users": [ { "created_at": "2013-12-14T04:35:55.000Z", "id": "2244994945", "protected": false, "username": "TwitterDev", "verified": true, "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "https://developer.x.com/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 17, "end": 28, "tag": "TwitterDev" }, { "start": 105, "end": 116, "tag": "TwitterAPI" } ] } }, "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", "pinned_tweet_id": "1293593516040269825", "public_metrics": { "followers_count": 513962, "following_count": 2039, "tweet_count": 3635, "listed_count": 1672 }, "location": "127.0.0.1", "name": "Twitter Dev", "profile_image_url": "https://pbs.twimg.com/profile_images/1283786620521652229/lEODkLTh_normal.jpg", "url": "https://t.co/3ZX3TNiZCY" }, { "created_at": "2013-07-23T16:58:03.000Z", "id": "1615654896", "protected": false, "username": "PennMedCDH", "verified": false, "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/7eS9RuwIb9", "expanded_url": "http://centerfordigitalhealth.upenn.edu/", "display_url": "centerfordigitalhealth.upenn.edu" } ] }, "description": { "mentions": [ { "start": 0, "end": 13, "username": "PennMedicine" } ] } }, "description": "@PennMedicine's Center for Digital Health advances science by researching the implications of the advancement of digital health technology in health care.", "public_metrics": { "followers_count": 1348, "following_count": 455, "tweet_count": 1288, "listed_count": 92 }, "location": "Philadelphia, PA", "name": "Penn Med CDH", "profile_image_url": "https://pbs.twimg.com/profile_images/1067488849725726723/MoO3FQ44_normal.jpg", "url": "https://t.co/7eS9RuwIb9" } ], "tweets": [ { "lang": "en", "conversation_id": "1296887091901718529", "text": "Dr. @RainaMerchant and her team at the Penn Medicine CDH are helping build the future of health care.\n\nThe team is using insights from social data in many different ways — ranging from uncovering risk factors to shedding light on public sentiment. 🔎", "possibly_sensitive": false, "entities": { "annotations": [ { "start": 39, "end": 55, "probability": 0.8274, "type": "Organization", "normalized_text": "Penn Medicine CDH" } ], "mentions": [ { "start": 4, "end": 18, "username": "RainaMerchant" } ] }, "id": "1296887091901718529", "public_metrics": { "retweet_count": 9, "reply_count": 7, "like_count": 32, "quote_count": 0 }, "author_id": "2244994945", "source": "Twitter Web App", "created_at": "2020-08-21T19:09:12.000Z" } ] } } ``` ### Extended Tweet ```json theme={null} { "data": [ { "conversation_id": "1296121314218897408", "id": "1296121314218897408", "possibly_sensitive": false, "public_metrics": { "retweet_count": 54, "reply_count": 9, "like_count": 172, "quote_count": 23 }, "entities": { "urls": [ { "start": 192, "end": 215, "url": "https://t.co/khXhTurm9x", "expanded_url": "https://devcommunity.x.com/t/hide-replies-now-available-in-the-new-twitter-api/140996", "display_url": "devcommunity.com/t/hide-replies…", "images": [ { "url": "https://pbs.twimg.com/news_img/1296121315514957825/3CI24hSI?format=png&name=orig", "width": 400, "height": 400 }, { "url": "https://pbs.twimg.com/news_img/1296121315514957825/3CI24hSI?format=png&name=150x150", "width": 150, "height": 150 } ], "status": 200, "title": "Hide replies now available in the new Twitter API", "description": "Today, we’re happy to announce the general availability of the hide replies endpoint in the new Twitter API. The hide replies endpoint allows you to build tools that help people hide or unhide replies to their Tweets. People manage their replies for many reasons, including to give less attention to comments that are abusive, distracting, misleading, or to make conversations more engaging. Through this endpoint, you can build tools to help people on Twitter hide or unhide replies faster and more...", "unwound_url": "https://devcommunity.x.com/t/hide-replies-now-available-in-the-new-twitter-api/140996" } ], "hashtags": [ { "start": 178, "end": 189, "tag": "TwitterAPI" } ] }, "text": "The hide replies endpoint is launching today! \n\nDevelopers can hide replies to Tweets - a crucial way developers can help improve the health of the public conversation using the #TwitterAPI.\n\nhttps://t.co/khXhTurm9x", "created_at": "2020-08-19T16:26:16.000Z", "context_annotations": [ { "domain": { "id": "65", "name": "Interests and Hobbies Vertical", "description": "Top level interests and hobbies groupings, like Food or Travel" }, "entity": { "id": "848920371311001600", "name": "Technology", "description": "Technology and computing" } }, { "domain": { "id": "66", "name": "Interests and Hobbies Category", "description": "A grouping of interests and hobbies entities, like Novelty Food or Destinations" }, "entity": { "id": "848921413196984320", "name": "Computer programming", "description": "Computer programming" } } ], "author_id": "2244994945", "lang": "en", "source": "Twitter Web App" } ], "includes": { "users": [ { "created_at": "2013-12-14T04:35:55.000Z", "profile_image_url": "https://pbs.twimg.com/profile_images/1283786620521652229/lEODkLTh_normal.jpg", "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "https://developer.x.com/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 17, "end": 28, "tag": "TwitterDev" }, { "start": 105, "end": 116, "tag": "TwitterAPI" } ] } }, "id": "2244994945", "verified": true, "location": "127.0.0.1", "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", "pinned_tweet_id": "1293593516040269825", "username": "TwitterDev", "public_metrics": { "followers_count": 513962, "following_count": 2039, "tweet_count": 3635, "listed_count": 1672 }, "name": "Twitter Dev", "url": "https://t.co/3ZX3TNiZCY", "protected": false } ] } } ``` ### Tweet with media ```json theme={null} { "data": [ { "lang": "en", "conversation_id": "1293593516040269825", "text": "It’s finally here! 🥁 Say hello to the new #TwitterAPI.\n\nWe’re rebuilding the X API v2 from the ground up to better serve our developer community. And today’s launch is only the beginning.\n\nhttps://t.co/32VrwpGaJw https://t.co/KaFSbjWUA8", "attachments": { "media_keys": [ "7_1293565706408038401" ] }, "possibly_sensitive": false, "entities": { "annotations": [ { "start": 78, "end": 88, "probability": 0.4381, "type": "Product", "normalized_text": "Twitter API" } ], "hashtags": [ { "start": 42, "end": 53, "tag": "TwitterAPI" } ], "urls": [ { "start": 195, "end": 218, "url": "https://t.co/32VrwpGaJw", "expanded_url": "https://blog.x.com/developer/en_us/topics/tools/2020/introducing_new_twitter_api.html", "display_url": "blog.x.com/developer/en_u…", "images": [ { "url": "https://pbs.twimg.com/news_img/1336475659279818754/_cmRh7QE?format=jpg&name=orig", "width": 1200, "height": 627 }, { "url": "https://pbs.twimg.com/news_img/1336475659279818754/_cmRh7QE?format=jpg&name=150x150", "width": 150, "height": 150 } ], "status": 200, "title": "Introducing a new and improved X API", "description": "Introducing the new X API - rebuilt from the ground up to deliver new features faster so developers can help the world connect to the public conversation happening on Twitter.", "unwound_url": "https://blog.x.com/developer/en_us/topics/tools/2020/introducing_new_twitter_api.html" }, { "start": 219, "end": 242, "url": "https://t.co/KaFSbjWUA8", "expanded_url": "https://x.com/TwitterDev/status/1293593516040269825/video/1", "display_url": "pic.x.com/KaFSbjWUA8" } ] }, "id": "1293593516040269825", "public_metrics": { "retweet_count": 958, "reply_count": 171, "like_count": 2848, "quote_count": 333 }, "author_id": "2244994945", "context_annotations": [ { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "65", "name": "Interests and Hobbies Vertical", "description": "Top level interests and hobbies groupings, like Food or Travel" }, "entity": { "id": "848920371311001600", "name": "Technology", "description": "Technology and computing" } }, { "domain": { "id": "66", "name": "Interests and Hobbies Category", "description": "A grouping of interests and hobbies entities, like Novelty Food or Destinations" }, "entity": { "id": "848921413196984320", "name": "Computer programming", "description": "Computer programming" } } ], "source": "Twitter Web App", "created_at": "2020-08-12T17:01:42.000Z" } ], "includes": { "media": [ { "height": 720, "duration_ms": 34875, "media_key": "7_1293565706408038401", "type": "video", "preview_image_url": "https://pbs.twimg.com/ext_tw_video_thumb/1293565706408038401/pu/img/66P2dvbU4a02jYbV.jpg", "public_metrics": { "view_count": 279438 }, "width": 1280 } ], "users": [ { "created_at": "2013-12-14T04:35:55.000Z", "id": "2244994945", "protected": false, "username": "TwitterDev", "verified": true, "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "https://developer.x.com/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 17, "end": 28, "tag": "TwitterDev" }, { "start": 105, "end": 116, "tag": "TwitterAPI" } ] } }, "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", "pinned_tweet_id": "1293593516040269825", "public_metrics": { "followers_count": 513962, "following_count": 2039, "tweet_count": 3635, "listed_count": 1672 }, "location": "127.0.0.1", "name": "Twitter Dev", "profile_image_url": "https://pbs.twimg.com/profile_images/1283786620521652229/lEODkLTh_normal.jpg", "url": "https://t.co/3ZX3TNiZCY" } ] } }` ### Retweet `{ "data": [ { "public_metrics": { "retweet_count": 19, "reply_count": 0, "like_count": 0, "quote_count": 0 }, "conversation_id": "1229851574555508737", "id": "1229851574555508737", "entities": { "annotations": [ { "start": 28, "end": 38, "probability": 0.261, "type": "Product", "normalized_text": "Alexa Skill" }, { "start": 44, "end": 50, "probability": 0.7332, "type": "Product", "normalized_text": "Twitter" } ], "mentions": [ { "start": 3, "end": 15, "username": "suhemparack" } ] }, "text": "RT @suhemparack: I built an Alexa Skill for Twitter using APL that allows you to view Tweets and Trends on the echo show!\n\nCheck it out her…", "created_at": "2020-02-18T19:33:59.000Z", "possibly_sensitive": false, "author_id": "2244994945", "referenced_tweets": [ { "type": "retweeted", "id": "1229843515603144704" } ], "context_annotations": [ { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10026792024", "name": "Amazon" } }, { "domain": { "id": "48", "name": "Product", "description": "Products created by Brands. Examples: Ford Explorer, Apple iPhone." }, "entity": { "id": "968221983803494400", "name": "Amazon - Alexa", "description": "Alexa" } }, { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } } ], "source": "Twitter Web App", "lang": "en" } ], "includes": { "users": [ { "profile_image_url": "https://pbs.twimg.com/profile_images/1283786620521652229/lEODkLTh_normal.jpg", "username": "TwitterDev", "name": "Twitter Dev", "location": "127.0.0.1", "url": "https://t.co/3ZX3TNiZCY", "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "https://developer.x.com/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 17, "end": 28, "tag": "TwitterDev" }, { "start": 105, "end": 116, "tag": "TwitterAPI" } ] } }, "id": "2244994945", "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", "verified": true, "public_metrics": { "followers_count": 513962, "following_count": 2039, "tweet_count": 3635, "listed_count": 1672 }, "pinned_tweet_id": "1293593516040269825", "created_at": "2013-12-14T04:35:55.000Z", "protected": false }, { "profile_image_url": "https://pbs.twimg.com/profile_images/1230703695051935747/TbQKe91L_normal.jpg", "username": "suhemparack", "name": "Suhem Parack", "location": "Seattle, WA", "url": "https://t.co/8IkCzClPCz", "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/8IkCzClPCz", "expanded_url": "https://developer.x.com", "display_url": "developer.x.com" } ] }, "description": { "mentions": [ { "start": 42, "end": 50, "username": "Twitter" } ] } }, "id": "857699969263964161", "description": "Developer Relations for Academic Research @Twitter. Talk to me about research with Twitter data. Previously: Amazon Alexa. Views are my own", "verified": false, "public_metrics": { "followers_count": 738, "following_count": 512, "tweet_count": 460, "listed_count": 12 }, "pinned_tweet_id": "1296498078233571329", "created_at": "2017-04-27T20:56:22.000Z", "protected": false } ], "tweets": [ { "public_metrics": { "retweet_count": 19, "reply_count": 1, "like_count": 71, "quote_count": 6 }, "conversation_id": "1229843515603144704", "id": "1229843515603144704", "entities": { "annotations": [ { "start": 11, "end": 21, "probability": 0.3342, "type": "Product", "normalized_text": "Alexa Skill" }, { "start": 27, "end": 33, "probability": 0.6727, "type": "Product", "normalized_text": "Twitter" } ], "urls": [ { "start": 127, "end": 150, "url": "https://t.co/l5J8wq748G", "expanded_url": "https://dev.to/twitterdev/building-an-alexa-skill-for-twitter-using-alexa-presentation-language-1aj0", "display_url": "dev.to/twitterdev/bui…", "status": 200, "unwound_url": "https://dev.to/twitterdev/building-an-alexa-skill-for-twitter-using-alexa-presentation-language-1aj0" } ] }, "text": "I built an Alexa Skill for Twitter using APL that allows you to view Tweets and Trends on the echo show!\n\nCheck it out here 👇\n\nhttps://t.co/l5J8wq748G", "created_at": "2020-02-18T19:01:58.000Z", "possibly_sensitive": false, "author_id": "857699969263964161", "context_annotations": [ { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10026792024", "name": "Amazon" } }, { "domain": { "id": "48", "name": "Product", "description": "Products created by Brands. Examples: Ford Explorer, Apple iPhone." }, "entity": { "id": "968221983803494400", "name": "Amazon - Alexa", "description": "Alexa" } }, { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } } ], "source": "Twitter Web App", "lang": "en" } ] } }` ### Quote Tweet `{ "data": [ { "lang": "en", "conversation_id": "1328399838128467969", "text": "As planned, the Labs v2 endpoints referenced below have now been retired. Please let us know in the forums if you have questions or need help with the X API v2! https://t.co/JaxttUMmjX", "referenced_tweets": [ { "type": "quoted", "id": "1327011423252144128" } ], "possibly_sensitive": false, "entities": { "annotations": [ { "start": 151, "end": 157, "probability": 0.8115, "type": "Product", "normalized_text": "Twitter" } ], "urls": [ { "start": 167, "end": 190, "url": "https://t.co/JaxttUMmjX", "expanded_url": "https://x.com/TwitterDev/status/1327011423252144128", "display_url": "twitter.com/TwitterDev/sta…" } ] }, "id": "1328399838128467969", "public_metrics": { "retweet_count": 7, "reply_count": 4, "like_count": 29, "quote_count": 1 }, "author_id": "2244994945", "context_annotations": [ { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "65", "name": "Interests and Hobbies Vertical", "description": "Top level interests and hobbies groupings, like Food or Travel" }, "entity": { "id": "848920371311001600", "name": "Technology", "description": "Technology and computing" } }, { "domain": { "id": "66", "name": "Interests and Hobbies Category", "description": "A grouping of interests and hobbies entities, like Novelty Food or Destinations" }, "entity": { "id": "848921413196984320", "name": "Computer programming", "description": "Computer programming" } } ], "source": "Twitter Web App", "created_at": "2020-11-16T18:09:36.000Z" } ], "includes": { "users": [ { "created_at": "2013-12-14T04:35:55.000Z", "id": "2244994945", "protected": false, "username": "TwitterDev", "verified": true, "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "https://developer.x.com/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 17, "end": 28, "tag": "TwitterDev" }, { "start": 105, "end": 116, "tag": "TwitterAPI" } ] } }, "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", "pinned_tweet_id": "1293593516040269825", "public_metrics": { "followers_count": 513962, "following_count": 2039, "tweet_count": 3635, "listed_count": 1672 }, "location": "127.0.0.1", "name": "Twitter Dev", "profile_image_url": "https://pbs.twimg.com/profile_images/1283786620521652229/lEODkLTh_normal.jpg", "url": "https://t.co/3ZX3TNiZCY" } ], "tweets": [ { "lang": "en", "conversation_id": "1327011423252144128", "text": "👋 Friendly reminder that Twitter Developer Labs v2 hide replies and recent search will be retired next Monday, November 16! We encourage you to migrate to the new hide replies and recent search endpoints now available in the v2 #TwitterAPI. Details: https://t.co/r6z6CI7kEy", "possibly_sensitive": false, "entities": { "annotations": [ { "start": 26, "end": 50, "probability": 0.4387, "type": "Product", "normalized_text": "Twitter Developer Labs v2" } ], "hashtags": [ { "start": 228, "end": 239, "tag": "TwitterAPI" } ], "urls": [ { "start": 250, "end": 273, "url": "https://t.co/r6z6CI7kEy", "expanded_url": "https://devcommunity.x.com/t/retiring-labs-v2-recent-search-and-hide-replies/145795", "display_url": "devcommunity.com/t/retiring-lab…", "images": [ { "url": "https://pbs.twimg.com/news_img/1327011425240313856/PkurOyu1?format=jpg&name=orig", "width": 1200, "height": 630 }, { "url": "https://pbs.twimg.com/news_img/1327011425240313856/PkurOyu1?format=jpg&name=150x150", "width": 150, "height": 150 } ], "status": 200, "title": "Retiring Labs v2 recent search and hide replies", "description": "As we said in our Early Access and hide replies announcements, the following Twitter Developer Labs v2 endpoints will be retired on November 16th. Labs v2 recent search Labs v2 hide replies If called, these endpoints will respond with an HTTP 410 status and return no data. Based on your feedback from Labs, we incorporated corresponding functionality into the X API v2. The relevant documentation can be found using the links below. Click here to enroll in v2 access if you haven’t already...", "unwound_url": "https://devcommunity.x.com/t/retiring-labs-v2-recent-search-and-hide-replies/145795" } ] }, "id": "1327011423252144128", "public_metrics": { "retweet_count": 8, "reply_count": 2, "like_count": 33, "quote_count": 4 }, "author_id": "2244994945", "context_annotations": [ { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "65", "name": "Interests and Hobbies Vertical", "description": "Top level interests and hobbies groupings, like Food or Travel" }, "entity": { "id": "848920371311001600", "name": "Technology", "description": "Technology and computing" } }, { "domain": { "id": "66", "name": "Interests and Hobbies Category", "description": "A grouping of interests and hobbies entities, like Novelty Food or Destinations" }, "entity": { "id": "848921413196984320", "name": "Computer programming", "description": "Computer programming" } } ], "source": "Twitter Web App", "created_at": "2020-11-12T22:12:32.000Z" } ] } } ``` ### Retweeted quote Tweet ``` { "data": [ { "lang": "en", "conversation_id": "1225470895902412800", "text": "RT @AureliaSpecker: 📣 If you enjoyed the London commute tutorial I wrote in November last year, check out the refactored version that uses…", "referenced_tweets": [ { "type": "retweeted", "id": "1224709550214873090" } ], "possibly_sensitive": false, "entities": { "annotations": [ { "start": 42, "end": 47, "probability": 0.6999, "type": "Place", "normalized_text": "London" } ], "mentions": [ { "start": 3, "end": 18, "username": "AureliaSpecker" } ] }, "id": "1225470895902412800", "public_metrics": { "retweet_count": 12, "reply_count": 0, "like_count": 0, "quote_count": 0 }, "author_id": "2244994945", "context_annotations": [ { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "65", "name": "Interests and Hobbies Vertical", "description": "Top level interests and hobbies groupings, like Food or Travel" }, "entity": { "id": "848920371311001600", "name": "Technology", "description": "Technology and computing" } }, { "domain": { "id": "66", "name": "Interests and Hobbies Category", "description": "A grouping of interests and hobbies entities, like Novelty Food or Destinations" }, "entity": { "id": "848921413196984320", "name": "Computer programming", "description": "Computer programming" } } ], "source": "Twitter for iPhone", "created_at": "2020-02-06T17:26:44.000Z" } ], "includes": { "users": [ { "created_at": "2013-12-14T04:35:55.000Z", "id": "2244994945", "protected": false, "username": "TwitterDev", "verified": true, "entities": { "url": { "urls": [ { "start": 0, "end": 23, "url": "https://t.co/3ZX3TNiZCY", "expanded_url": "https://developer.x.com/en/community", "display_url": "developer.x.com/en/community" } ] }, "description": { "hashtags": [ { "start": 17, "end": 28, "tag": "TwitterDev" }, { "start": 105, "end": 116, "tag": "TwitterAPI" } ] } }, "description": "The voice of the #TwitterDev team and your official source for updates, news, and events, related to the #TwitterAPI.", "pinned_tweet_id": "1293593516040269825", "public_metrics": { "followers_count": 513962, "following_count": 2039, "tweet_count": 3635, "listed_count": 1672 }, "location": "127.0.0.1", "name": "Twitter Dev", "profile_image_url": "https://pbs.twimg.com/profile_images/1283786620521652229/lEODkLTh_normal.jpg", "url": "https://t.co/3ZX3TNiZCY" }, { "created_at": "2013-01-18T23:45:43.000Z", "id": "1102321381", "protected": false, "username": "AureliaSpecker", "verified": false, "entities": { "description": { "mentions": [ { "start": 7, "end": 17, "username": "TwitterUK" }, { "start": 86, "end": 95, "username": "_dormrod" } ] } }, "description": "devrel @TwitterUK • Swiss in London • mother of houseplants • personal hairdresser to @_dormrod", "pinned_tweet_id": "1253069421322567681", "public_metrics": { "followers_count": 1036, "following_count": 1330, "tweet_count": 855, "listed_count": 26 }, "location": "London, UK", "name": "Aurelia Specker", "profile_image_url": "https://pbs.twimg.com/profile_images/1137517534104772608/8FBYgc6G_normal.jpg", "url": "" } ], "tweets": [ { "lang": "en", "conversation_id": "1224709550214873090", "text": "📣 If you enjoyed the London commute tutorial I wrote in November last year, check out the refactored version that uses Twitter's new search endpoint 🚇 https://t.co/87XIPZmZBJ\n\n#DEVcommunity #Pythontutorial @TwitterDev @TwitterAPI https://t.co/dXrJYvn3hY", "referenced_tweets": [ { "type": "quoted", "id": "1195000047089389573" } ], "possibly_sensitive": false, "entities": { "annotations": [ { "start": 22, "end": 27, "probability": 0.7075, "type": "Place", "normalized_text": "London" }, { "start": 120, "end": 126, "probability": 0.7355, "type": "Product", "normalized_text": "Twitter" } ], "mentions": [ { "start": 206, "end": 217, "username": "TwitterDev" }, { "start": 218, "end": 229, "username": "TwitterAPI" } ], "hashtags": [ { "start": 176, "end": 189, "tag": "DEVcommunity" }, { "start": 190, "end": 205, "tag": "Pythontutorial" } ], "urls": [ { "start": 151, "end": 174, "url": "https://t.co/87XIPZmZBJ", "expanded_url": "https://bit.ly/2OrnrCC", "display_url": "bit.ly/2OrnrCC", "status": 200, "unwound_url": "https://dev.to/twitterdev/migrate-to-twitter-s-newly-released-labs-recent-search-endpoint-3npe" }, { "start": 230, "end": 253, "url": "https://t.co/dXrJYvn3hY", "expanded_url": "https://x.com/AureliaSpecker/status/1195000047089389573", "display_url": "twitter.com/AureliaSpecker…" } ] }, "id": "1224709550214873090", "public_metrics": { "retweet_count": 12, "reply_count": 0, "like_count": 43, "quote_count": 2 }, "author_id": "1102321381", "context_annotations": [ { "domain": { "id": "46", "name": "Brand Category", "description": "Categories within Brand Verticals that narrow down the scope of Brands" }, "entity": { "id": "781974596752842752", "name": "Services" } }, { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "10045225402", "name": "Twitter" } }, { "domain": { "id": "65", "name": "Interests and Hobbies Vertical", "description": "Top level interests and hobbies groupings, like Food or Travel" }, "entity": { "id": "848920371311001600", "name": "Technology", "description": "Technology and computing" } }, { "domain": { "id": "66", "name": "Interests and Hobbies Category", "description": "A grouping of interests and hobbies entities, like Novelty Food or Destinations" }, "entity": { "id": "848921413196984320", "name": "Computer programming", "description": "Computer programming" } } ], "source": "Twitter Web App", "created_at": "2020-02-04T15:01:25.000Z" } ] } } ``` # Edit Posts Source: https://docs.x.com/x-api/fundamentals/edit-posts Working with edited posts in the X API Posts on X can be edited within 30 minutes of posting, up to 5 times. The X API provides full access to edit history and metadata. *** ## Edit rules | Rule | Details | | :-------------- | :-------------------------------------------- | | **Time window** | 30 minutes from original post | | **Edit limit** | 5 edits maximum | | **ID behavior** | Each edit creates a new post ID | | **Deletion** | Deleting any version deletes the entire chain | *** ## What can't be edited Some post types are not editable: * Promoted posts (ads) * Posts with polls * Replies to others (non-self-thread) * Reposts (Quote posts *can* be edited) * Community posts * Collaborative posts * Scheduled posts *** ## Edit data in responses ### Default fields All post responses include `edit_history_tweet_ids` by default: ```json theme={null} { "data": { "id": "1234567891", "text": "Updated text (edited)", "edit_history_tweet_ids": ["1234567890", "1234567891"] } } ``` * Single ID = never edited * Multiple IDs = edit history (oldest first) ### Edit controls Request `edit_controls` for edit status: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567891?tweet.fields=edit_controls" \ -H "Authorization: Bearer $TOKEN" ``` ```json theme={null} { "data": { "id": "1234567891", "text": "Updated text (edited)", "edit_history_tweet_ids": ["1234567890", "1234567891"], "edit_controls": { "is_edit_eligible": true, "editable_until": "2024-01-15T12:30:00.000Z", "edits_remaining": 3 } } } ``` | Field | Description | | :----------------- | :-------------------------------- | | `is_edit_eligible` | Whether the post can be edited | | `editable_until` | Timestamp when edit window closes | | `edits_remaining` | Number of edits left (0-5) | *** ## Getting edit history Use the `edit_history_tweet_ids` expansion to get full post objects for all versions: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567891?\ tweet.fields=edit_controls&\ expansions=edit_history_tweet_ids" \ -H "Authorization: Bearer $TOKEN" ``` ```json theme={null} { "data": { "id": "1234567891", "text": "Updated text (edited)", "edit_history_tweet_ids": ["1234567890", "1234567891"], "edit_controls": { "is_edit_eligible": true, "editable_until": "2024-01-15T12:30:00.000Z", "edits_remaining": 3 } }, "includes": { "tweets": [{ "id": "1234567890", "text": "Original text (with typo)", "edit_history_tweet_ids": ["1234567890", "1234567891"], "edit_controls": { "is_edit_eligible": true, "editable_until": "2024-01-15T12:30:00.000Z", "edits_remaining": 3 } }] } } ``` *** ## Which version is returned? By default, the API returns the **most recent version** of an edited post. To get a specific version, request it by its post ID directly: ```bash theme={null} # Get original version curl "https://api.x.com/2/tweets/1234567890" -H "Authorization: Bearer $TOKEN" # Get edited version curl "https://api.x.com/2/tweets/1234567891" -H "Authorization: Bearer $TOKEN" ``` *** ## Metrics for edited posts Each version of an edited post has its own engagement metrics. The metrics are attributed to the version that was visible when the engagement occurred. *** ## Availability Edit metadata is available: * For all posts created since September 29, 2022 * On all v2 endpoints that return posts * Including search, timelines, stream, and lookup Posts created before this date do not have edit metadata. *** ## Use cases Monitor posts for edits and log the differences: ```python theme={null} def check_for_edits(post): history = post.get("edit_history_tweet_ids", []) if len(history) > 1: print(f"Post {post['id']} has been edited {len(history) - 1} times") ``` Display an "edited" badge in your UI: ```javascript theme={null} const isEdited = post.edit_history_tweet_ids?.length > 1; if (isEdited) { showEditedBadge(); } ``` Retrieve the original version of an edited post: ```python theme={null} original_id = post["edit_history_tweet_ids"][0] original = api.get_tweet(original_id) ``` *** ## Next steps Retrieve posts with edit history. Complete post object reference. # Expansions Source: https://docs.x.com/x-api/fundamentals/expansions Include related objects in API responses Expansions let you include related objects in a single API response. Instead of making multiple requests, get a post and its author, media, or referenced posts in one call. *** ## How expansions work When you request an expansion, the API includes the full object in the `includes` section of the response: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890?expansions=author_id" \ -H "Authorization: Bearer $TOKEN" ``` Response: ```json theme={null} { "data": { "id": "1234567890", "text": "Hello world!", "author_id": "2244994945" }, "includes": { "users": [{ "id": "2244994945", "name": "X Developers", "username": "xdevelopers" }] } } ``` The `author_id` in `data` links to the user object in `includes`. *** ## Post expansions | Expansion | Returns | Use case | | :------------------------------- | :-------------- | :------------------------------------ | | `author_id` | User object | Get post author details | | `referenced_tweets.id` | Post object(s) | Get quoted/replied-to posts | | `referenced_tweets.id.author_id` | User object(s) | Get authors of referenced posts | | `in_reply_to_user_id` | User object | Get user being replied to | | `attachments.media_keys` | Media object(s) | Get images, videos, GIFs | | `attachments.poll_ids` | Poll object | Get poll options and votes | | `geo.place_id` | Place object | Get location details | | `entities.mentions.username` | User object(s) | Get mentioned users | | `edit_history_tweet_ids` | Post object(s) | Get previous versions of edited posts | *** ## User expansions | Expansion | Returns | Use case | | :---------------- | :---------- | :--------------------- | | `pinned_tweet_id` | Post object | Get user's pinned post | *** ## Space expansions | Expansion | Returns | Use case | | :----------------- | :------------- | :----------------- | | `creator_id` | User object | Get Space creator | | `host_ids` | User object(s) | Get Space hosts | | `speaker_ids` | User object(s) | Get Space speakers | | `invited_user_ids` | User object(s) | Get invited users | *** ## DM expansions | Expansion | Returns | Use case | | :----------------------- | :------------- | :---------------------------- | | `sender_id` | User object | Get message sender | | `participant_ids` | User object(s) | Get conversation participants | | `attachments.media_keys` | Media object | Get attached media | | `referenced_tweets.id` | Post object | Get referenced post | *** ## List expansions | Expansion | Returns | Use case | | :--------- | :---------- | :------------- | | `owner_id` | User object | Get list owner | *** ## Combining with fields Expansions return default fields for each object. To get additional fields, combine expansions with field parameters: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890?\ expansions=author_id,attachments.media_keys&\ user.fields=description,public_metrics&\ media.fields=url,alt_text" \ -H "Authorization: Bearer $TOKEN" ``` Response: ```json theme={null} { "data": { "id": "1234567890", "text": "Check out this image!", "author_id": "2244994945", "attachments": { "media_keys": ["3_1234567890"] } }, "includes": { "users": [{ "id": "2244994945", "name": "X Developers", "username": "xdevelopers", "description": "The voice of the X Developer Platform", "public_metrics": { "followers_count": 570842 } }], "media": [{ "media_key": "3_1234567890", "type": "photo", "url": "https://pbs.twimg.com/media/example.jpg", "alt_text": "Example image" }] } } ``` *** ## Multiple expansions Request multiple expansions as a comma-separated list: ```bash theme={null} expansions=author_id,referenced_tweets.id,attachments.media_keys ``` *** ## Common patterns Get a post with author, media, and referenced posts: ```bash theme={null} expansions=author_id,attachments.media_keys,referenced_tweets.id tweet.fields=created_at,public_metrics,conversation_id user.fields=username,name,profile_image_url media.fields=url,preview_image_url,type ``` Get replies with their authors: ```bash theme={null} expansions=author_id,in_reply_to_user_id,referenced_tweets.id tweet.fields=conversation_id,in_reply_to_user_id,created_at user.fields=username,name ``` Get a user profile with their pinned post: ```bash theme={null} expansions=pinned_tweet_id user.fields=description,public_metrics,verified tweet.fields=created_at,public_metrics ``` *** ## Linking data and includes The objects in `includes` don't contain position information. Link them using IDs: ```python theme={null} # Python example response = api_call() post = response["data"] users = {u["id"]: u for u in response["includes"]["users"]} # Get the author author = users.get(post["author_id"]) print(f"{author['name']} said: {post['text']}") ``` ```javascript theme={null} // JavaScript example const { data: post, includes } = response; const users = Object.fromEntries( includes.users.map(u => [u.id, u]) ); const author = users[post.author_id]; console.log(`${author.name} said: ${post.text}`); ``` *** ## Next steps Request specific fields for each object. Complete object schemas. # Fields Source: https://docs.x.com/x-api/fundamentals/fields Request specific data fields in API responses The X API v2 returns minimal data by default. Use **fields parameters** to request additional data for each object type. *** ## How fields work By default, a post lookup returns only `id`, `text`, and `edit_history_tweet_ids`. To get more data, add field parameters to your request: ```bash theme={null} # Default response - minimal fields curl "https://api.x.com/2/tweets/1234567890" \ -H "Authorization: Bearer $TOKEN" # With additional fields curl "https://api.x.com/2/tweets/1234567890?tweet.fields=created_at,public_metrics,author_id" \ -H "Authorization: Bearer $TOKEN" ``` *** ## Available field parameters Each object type has its own fields parameter: | Object | Parameter | Documentation | | :----------- | :------------- | :-------------------------------------------------------- | | Post (Tweet) | `tweet.fields` | [Post fields](/x-api/fundamentals/data-dictionary#tweet) | | User | `user.fields` | [User fields](/x-api/fundamentals/data-dictionary#user) | | Media | `media.fields` | [Media fields](/x-api/fundamentals/data-dictionary#media) | | Poll | `poll.fields` | [Poll fields](/x-api/fundamentals/data-dictionary#poll) | | Place | `place.fields` | [Place fields](/x-api/fundamentals/data-dictionary#place) | *** ## Example: Post fields Request specific post fields with `tweet.fields`: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890?tweet.fields=created_at,public_metrics,lang" \ -H "Authorization: Bearer $TOKEN" ``` Response: ```json theme={null} { "data": { "id": "1234567890", "text": "Hello world!", "edit_history_tweet_ids": ["1234567890"], "created_at": "2024-01-15T12:00:00.000Z", "lang": "en", "public_metrics": { "retweet_count": 10, "reply_count": 5, "like_count": 100, "quote_count": 2, "bookmark_count": 3, "impression_count": 1500 } } } ``` *** ## Example: User fields Request specific user fields with `user.fields`: ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers?user.fields=created_at,description,public_metrics" \ -H "Authorization: Bearer $TOKEN" ``` Response: ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "xdevelopers", "created_at": "2013-12-14T04:35:55.000Z", "description": "The voice of the X Developer Platform", "public_metrics": { "followers_count": 570842, "following_count": 2048, "tweet_count": 14052, "listed_count": 1672 } } } ``` *** ## Fields for related objects To get fields on related objects (like the author of a post), you need two things: 1. An **expansion** to include the related object 2. The **fields parameter** for that object type ```bash theme={null} # Get post with author details curl "https://api.x.com/2/tweets/1234567890?expansions=author_id&user.fields=description,public_metrics" \ -H "Authorization: Bearer $TOKEN" ``` Response: ```json theme={null} { "data": { "id": "1234567890", "text": "Hello world!", "author_id": "2244994945" }, "includes": { "users": [{ "id": "2244994945", "name": "X Developers", "username": "xdevelopers", "description": "The voice of the X Developer Platform", "public_metrics": { "followers_count": 570842, "following_count": 2048 } }] } } ``` [Learn more about expansions →](/x-api/fundamentals/expansions) *** ## Common field combinations ``` tweet.fields=created_at,public_metrics,possibly_sensitive ``` ``` user.fields=created_at,description,location,public_metrics,verified ``` ``` tweet.fields=created_at,author_id,conversation_id,in_reply_to_user_id,referenced_tweets expansions=author_id,referenced_tweets.id user.fields=username,name ``` ``` tweet.fields=attachments expansions=attachments.media_keys media.fields=url,preview_image_url,alt_text,public_metrics ``` *** ## Important notes **You cannot request subfields.** When you request `public_metrics`, you get all metrics (likes, reposts, replies, quotes, bookmarks, impressions). You can't request just `public_metrics.like_count`. * Field order in responses may differ from request order * Missing fields in responses mean the value is `null` or empty * Some fields require specific authentication (e.g., private metrics need user context) * Check each endpoint's API reference for available fields *** ## Next steps Include related objects in responses. Complete field reference for all objects. # Handling disconnections Source: https://docs.x.com/x-api/fundamentals/handling-disconnections Best practices for detecting and recovering from streaming disconnections 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 theme={null} { "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 theme={null} { "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 # Handling high-volume capacity Source: https://docs.x.com/x-api/fundamentals/high-volume-capacity Best practices for handling high-volume streaming data events 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} theme={null} 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 # Metrics Source: https://docs.x.com/x-api/fundamentals/metrics Access engagement metrics for posts and media The X API provides engagement metrics for posts and media. Access public metrics with any authentication, or private metrics for your own content with user authentication. *** ## Metric types | Type | Authentication | Description | | :------------- | :------------- | :------------------------------------------------------- | | **Public** | Bearer Token | Visible to anyone (impressions, likes, reposts, replies) | | **Non-public** | User context | Private metrics (clicks) | | **Organic** | User context | Metrics from non-promoted views | | **Promoted** | User context | Metrics from ad views | **30-day limit**: Non-public, organic, and promoted metrics are only available for posts created within the last 30 days. *** ## Available metrics ### Post metrics | Metric | Type | Field path | | :------------- | :--------- | :--------------------------------------- | | Reposts | Public | `public_metrics.retweet_count` | | Quotes | Public | `public_metrics.quote_count` | | Likes | Public | `public_metrics.like_count` | | Replies | Public | `public_metrics.reply_count` | | Impressions | Public | `public_metrics.impression_count` | | Bookmarks | Public | `public_metrics.bookmark_count` | | URL clicks | Non-public | `non_public_metrics.url_link_clicks` | | Profile clicks | Non-public | `non_public_metrics.user_profile_clicks` | | Engagements | Non-public | `non_public_metrics.engagements` | ### Media metrics (videos) | Metric | Type | Field path | | :------------ | :--------- | :-------------------------------------- | | Views | Public | `public_metrics.view_count` | | Playback 0% | Non-public | `non_public_metrics.playback_0_count` | | Playback 25% | Non-public | `non_public_metrics.playback_25_count` | | Playback 50% | Non-public | `non_public_metrics.playback_50_count` | | Playback 75% | Non-public | `non_public_metrics.playback_75_count` | | Playback 100% | Non-public | `non_public_metrics.playback_100_count` | *** ## Requesting metrics ### Public metrics (any auth) ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890?tweet.fields=public_metrics" \ -H "Authorization: Bearer $TOKEN" ``` Response: ```json theme={null} { "data": { "id": "1234567890", "text": "Hello world!", "public_metrics": { "retweet_count": 50, "reply_count": 12, "like_count": 234, "quote_count": 5, "bookmark_count": 8, "impression_count": 5432 } } } ``` ### Private metrics (user context) Requires OAuth 1.0a or OAuth 2.0 with user context for posts you own: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890?tweet.fields=non_public_metrics,organic_metrics" \ -H "Authorization: OAuth oauth_consumer_key=...,oauth_token=..." ``` Response: ```json theme={null} { "data": { "id": "1234567890", "text": "Hello world!", "non_public_metrics": { "impression_count": 5432, "url_link_clicks": 89, "user_profile_clicks": 156, "engagements": 345 }, "organic_metrics": { "impression_count": 5432, "like_count": 234, "reply_count": 12, "retweet_count": 50, "url_link_clicks": 89, "user_profile_clicks": 156 } } } ``` *** ## Video metrics For video playback metrics, use the media expansion: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890?\ tweet.fields=attachments&\ expansions=attachments.media_keys&\ media.fields=public_metrics,non_public_metrics" \ -H "Authorization: OAuth ..." ``` Response: ```json theme={null} { "data": { "id": "1234567890", "text": "Check out this video!", "attachments": { "media_keys": ["13_9876543210"] } }, "includes": { "media": [{ "media_key": "13_9876543210", "type": "video", "public_metrics": { "view_count": 12543 }, "non_public_metrics": { "playback_0_count": 12543, "playback_25_count": 9876, "playback_50_count": 7654, "playback_75_count": 5432, "playback_100_count": 3210 } }] } } ``` *** ## Organic vs. promoted metrics If a post was promoted as an ad, metrics split between organic and promoted views: | Context | Description | | :----------- | :---------------------------------- | | **Organic** | Metrics from normal timeline views | | **Promoted** | Metrics from paid ad impressions | | **Public** | Combined total (organic + promoted) | Request both to see the breakdown: ```bash theme={null} tweet.fields=public_metrics,organic_metrics,promoted_metrics ``` *** ## Metric definitions Count of times the post appeared on a user's screen. Not unique—the same user viewing twice counts as two impressions. Number of reposts (retweets). Does not include quote posts. Number of quote posts (reposts with comment). These are always organic. Number of times the post has been bookmarked by users. Aggregated across all posts containing the video. A video reposted in multiple posts has one total view count. Number of unique users who played through each percentage of the video. Useful for understanding drop-off rates. *** ## Requirements summary | Metric field | Authentication required | | :------------------- | :--------------------------------- | | `public_metrics` | Bearer Token (any) | | `non_public_metrics` | User context (owned posts only) | | `organic_metrics` | User context (owned posts only) | | `promoted_metrics` | User context (promoted posts only) | *** ## Next steps Complete field reference. Set up user context auth. # Pagination Source: https://docs.x.com/x-api/fundamentals/pagination Navigate through large result sets When an API response contains more results than can be returned at once, use pagination to retrieve all pages of data. *** ## How pagination works 1. Make your initial request with `max_results` 2. Check the response for a `next_token` in the `meta` object 3. If present, make another request with that token as `pagination_token` 4. Repeat until no `next_token` is returned ```bash theme={null} # Initial request curl "https://api.x.com/2/users/12345/tweets?max_results=100" \ -H "Authorization: Bearer $TOKEN" # Response includes next_token # {"data": [...], "meta": {"next_token": "abc123", ...}} # Next page curl "https://api.x.com/2/users/12345/tweets?max_results=100&pagination_token=abc123" \ -H "Authorization: Bearer $TOKEN" ``` *** ## Pagination tokens | Token | Description | | :----------------- | :---------------------------------------------------------------- | | `next_token` | In response `meta`. Use to get the next page. | | `previous_token` | In response `meta`. Use to go back a page. | | `pagination_token` | Request parameter. Set to `next_token` or `previous_token` value. | *** ## Response structure ```json theme={null} { "data": [ {"id": "1234", "text": "..."}, {"id": "1235", "text": "..."} ], "meta": { "result_count": 100, "next_token": "7140w9gefhslx3", "previous_token": "77qp89slxjd" } } ``` When there are no more results, `next_token` is omitted: ```json theme={null} { "data": [...], "meta": { "result_count": 42, "previous_token": "77qp89abc" } } ``` *** ## Pagination parameters | Parameter | Description | Default | | :----------------- | :--------------------------- | :---------------- | | `max_results` | Results per page | Endpoint-specific | | `pagination_token` | Token from previous response | None | Check each endpoint's API reference for specific `max_results` limits. *** ## Example: Paginating through all results ```python theme={null} import requests def get_all_tweets(user_id, bearer_token): url = f"https://api.x.com/2/users/{user_id}/tweets" headers = {"Authorization": f"Bearer {bearer_token}"} params = {"max_results": 100} all_tweets = [] while True: response = requests.get(url, headers=headers, params=params) data = response.json() if "data" in data: all_tweets.extend(data["data"]) # Check for next page next_token = data.get("meta", {}).get("next_token") if not next_token: break params["pagination_token"] = next_token return all_tweets ``` ```javascript theme={null} async function getAllTweets(userId, bearerToken) { const url = `https://api.x.com/2/users/${userId}/tweets`; const headers = { Authorization: `Bearer ${bearerToken}` }; let allTweets = []; let paginationToken = null; do { const params = new URLSearchParams({ max_results: 100 }); if (paginationToken) { params.set("pagination_token", paginationToken); } const response = await fetch(`${url}?${params}`, { headers }); const data = await response.json(); if (data.data) { allTweets.push(...data.data); } paginationToken = data.meta?.next_token; } while (paginationToken); return allTweets; } ``` *** ## Best practices Request the maximum allowed `max_results` to minimize API calls. The last page may have fewer results than `max_results`. Save `next_token` if you need to resume pagination later. For new data, use `since_id` instead of paginating repeatedly. *** ## Result ordering Results are returned in **reverse chronological order**: * First result on first page = most recent * Last result on last page = oldest This applies within and across pages. *** ## Notes * Pagination tokens are opaque strings—don't parse or modify them * Tokens may expire after some time * If you get fewer results than `max_results`, more may still exist (continue until no `next_token`) * Use [SDKs](/tools-and-libraries) for automatic pagination handling *** ## Next steps Understand request limits when paginating. Libraries with built-in pagination. # Post Annotations Source: https://docs.x.com/x-api/fundamentals/post-annotations Entity and context annotations for semantic analysis Annotations provide semantic metadata about post content. X analyzes posts to identify entities (people, places, products) and context (topics, domains) to help you understand and filter content. *** ## Annotation types ### Entity annotations Named-entity recognition (NER) identifies specific mentions in post text: | Type | Examples | | :--------------- | :---------------------- | | **Person** | Barack Obama, Elon Musk | | **Place** | San Francisco, Japan | | **Product** | iPhone, ChatGPT | | **Organization** | NASA, Google | | **Other** | Super Bowl, Diabetes | Entity annotations include a confidence score and position in the text. ### Context annotations Semantic analysis that classifies posts by topic and domain: * **Domain**: Broad category (Sports, Entertainment, Technology) * **Entity**: Specific topic within the domain (NBA, Marvel Movies, AI) Context annotations help filter and categorize posts without relying on keywords. *** ## Requesting annotations Add `context_annotations` and `entities` to your `tweet.fields`: ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890?tweet.fields=context_annotations,entities" \ -H "Authorization: Bearer $TOKEN" ``` *** ## Response structure ```json theme={null} { "data": { "id": "1234567890", "text": "Just saw the new Marvel movie - it was amazing!", "entities": { "annotations": [ { "start": 17, "end": 22, "probability": 0.9234, "type": "Organization", "normalized_text": "Marvel" } ] }, "context_annotations": [ { "domain": { "id": "86", "name": "Movie", "description": "A film" }, "entity": { "id": "1234567890", "name": "Marvel Cinematic Universe" } }, { "domain": { "id": "65", "name": "Interests and Hobbies Vertical" }, "entity": { "id": "781974596752842752", "name": "Entertainment" } } ] } } ``` *** ## Entity annotation fields | Field | Description | | :---------------- | :-------------------------------- | | `start` | Start position in text | | `end` | End position in text | | `probability` | Confidence score (0-1) | | `type` | Entity type (Person, Place, etc.) | | `normalized_text` | Standardized entity name | *** ## Context domains X uses 80+ domains to categorize posts. Common domains include: | ID | Domain | | :- | :---------- | | 3 | TV Shows | | 4 | TV Episodes | | 54 | Musician | | 56 | Actor | | 86 | Movie | | 91 | Podcast | | ID | Domain | | :- | :------------ | | 6 | Sports Events | | 11 | Sport | | 12 | Sports Team | | 26 | Sports League | | 60 | Athlete | | 93 | Coach | | ID | Domain | | :-- | :------------- | | 45 | Brand Vertical | | 46 | Brand Category | | 47 | Brand | | 48 | Product | | 165 | Technology | | 166 | Stocks | | ID | Domain | | :-- | :----------------------- | | 10 | Person | | 13 | Place | | 29 | Events | | 35 | Politicians | | 119 | Holiday | | 131 | Unified Twitter Taxonomy | Domain 131 (Unified Twitter Taxonomy) powers X's Topics feature visible to users on the platform. *** ## Using annotations in filters ### Search and filtered stream Filter posts by context annotation entity ID: ```bash theme={null} # Posts about a specific entity context:86.1234567890 # Posts in a specific domain context:86.* ``` ### Practical examples ```bash theme={null} # Posts about the NBA query=context:26.852137520 # Posts about Apple products query=context:47.10026792024 # Posts about movies query=context:86.* ``` *** ## Language support Annotations are available for multiple languages: | Language | Coverage | | :--------- | :------- | | English | Highest | | Japanese | High | | Spanish | High | | Portuguese | Medium | | French | Medium | | Hindi | Medium | Coverage varies by domain and market. *** ## Important notes **Not all posts are annotated.** Annotation coverage depends on: * Language support * Topic coverage in X's taxonomy * Semantic richness of the post text * Annotations are not retroactive—only applied when entities are tracked * The same entity can appear in multiple domains (e.g., a celebrity is both Person and Actor) * Entity IDs are stable across domains *** ## Resources CSV of available context annotation entities. # Usage and Billing Source: https://docs.x.com/x-api/fundamentals/post-cap Understanding API usage tracking and pay-per-usage billing X API v2 uses **pay-per-usage** pricing. You're charged based on actual API consumption, tracked at the app level. *** ## How billing works | Concept | Description | | :---------------------- | :---------------------------------------------------- | | **Credit-based** | Purchase credits upfront, deducted as you use the API | | **Per-request pricing** | Different endpoints have different costs | | **Real-time tracking** | Monitor usage in the Developer Console | Pay-per-usage plans are subject to a monthly cap of 2 million Post reads. If you need higher volume, consider an [Enterprise plan](/forms/enterprise-api-interest). *** ## Tracked endpoints Posts retrieved from these endpoints count toward usage: | Category | Endpoints | | :-------------- | :---------------------------------------------------------------------------------------------------------------------------- | | **Post lookup** | [GET /2/tweets](/x-api/posts/lookup/introduction) | | **Search** | [Recent search](/x-api/posts/search/introduction), [Full-archive search](/x-api/posts/search/introduction) | | **Streaming** | [Filtered stream](/x-api/posts/filtered-stream/introduction), [Filtered stream webhooks](/x-api/webhooks/stream/introduction) | | **Timelines** | [User posts](/x-api/posts/timelines/introduction), [User mentions](/x-api/posts/timelines/introduction) | | **Engagement** | [Liked posts](/x-api/posts/likes/introduction), [Bookmarks](/x-api/posts/bookmarks/introduction) | | **Lists** | [List posts](/x-api/lists/list-tweets/introduction) | | **Spaces** | [Spaces lookup](/x-api/spaces/lookup/introduction) | *** ## Deduplication **Daily deduplication**: If the same post is returned from multiple queries within a day, it only counts once for billing. This means: * Retrieving the same post multiple times in one day = 1 charge * Retrieving a post again the next day = 1 additional charge * Different posts in the same request = each counts separately *** ## Monitoring usage Track your usage in the [Developer Console](https://console.x.com): | Metric | Description | | :----------------- | :------------------------------------ | | **Total usage** | Posts retrieved in the billing period | | **By endpoint** | Breakdown by endpoint type | | **By app** | Usage per developer app | | **Cost tracking** | Real-time cost calculations | | **Credit balance** | Remaining prepaid credits | *** ## Managing costs Configure spending limits in the Developer Console. Get notified before hitting budget thresholds. Cache responses to avoid re-fetching the same posts. Use precise filters to retrieve only needed posts. *** ## Pricing details For current pricing per endpoint and operation, visit the [Developer Console](https://console.x.com). Pricing may vary by: * Endpoint type (search vs. lookup vs. stream) * Operation (read vs. write) * Data scope (recent vs. full-archive) *** ## Enterprise options For high-volume needs with custom pricing: * Dedicated support * Volume discounts * Custom rate limits * Complete data access Discuss custom solutions for your needs. *** ## FAQs API requests will fail until you purchase more credits. Set up balance alerts to avoid interruption. No. Pay only for what you use. You can have months with zero usage and zero cost. Each unique post delivered through filtered stream counts toward usage, subject to daily deduplication. No. Only successful responses that return data are billed. *** ## Next steps View pricing and purchase credits. Understand request limits (separate from billing). # X API Rate Limits Source: https://docs.x.com/x-api/fundamentals/rate-limits Per-endpoint rate limits for X API v2 Rate limits control the number of requests you can make to each endpoint. Exceeding limits results in a 429 error until the window resets. *** ## How rate limits work | Concept | Description | | :------------------ | :--------------------------------------------- | | **Time window** | Usually 15 minutes or 24 hours | | **Per-user limits** | Apply with OAuth 1.0a or OAuth 2.0 user tokens | | **Per-app limits** | Apply with Bearer Token (app-only) | | **Per-endpoint** | Each endpoint has its own limits | *** ## Checking your limits Response headers show your current rate limit status: ``` x-rate-limit-limit: 900 x-rate-limit-remaining: 847 x-rate-limit-reset: 1705420800 ``` | Header | Description | | :----------------------- | :-------------------------------- | | `x-rate-limit-limit` | Maximum requests allowed | | `x-rate-limit-remaining` | Requests remaining in window | | `x-rate-limit-reset` | Unix timestamp when window resets | *** ## Rate limit tables View the rate limit for each endpoint below. You can also see these limits in the [Developer Console](https://console.x.com). Limits are shown per 15 minutes unless otherwise noted (e.g., "/24hrs" or "/sec"). ### Posts (25 endpoints) #### Tweets lookup | Method | Endpoint | Per App | Per User | | :----- | :-------------- | :---------- | :---------- | | GET | `/2/tweets` | 3,500/15min | 5,000/15min | | GET | `/2/tweets/:id` | 450/15min | 900/15min | #### Recent search | Method | Endpoint | Per App | Per User | Notes | | :----- | :------------------------ | :-------- | :-------- | :-------------------------------------------- | | GET | `/2/tweets/search/recent` | 450/15min | 300/15min | 10 default, 100 max results; 512 query length | #### Full-archive search | Method | Endpoint | Per App | Per User | Notes | | :----- | :--------------------- | :--------------- | :------- | :--------------------------------------------- | | GET | `/2/tweets/search/all` | 1/sec, 300/15min | 1/sec | 10 default, 500 max results; 1024 query length | #### Post counts | Method | Endpoint | Per App | Per User | Notes | | :----- | :------------------------ | :-------- | :------- | :---------------- | | GET | `/2/tweets/counts/recent` | 300/15min | — | 512 query length | | GET | `/2/tweets/counts/all` | 300/15min | — | 1024 query length | #### Filtered stream | Method | Endpoint | Per App | Per User | Notes | | :----- | :------------------------------ | :-------- | :------- | :-------------------------------------------------------- | | GET | `/2/tweets/search/stream` | 50/15min | — | 1 connection; 1000 rules; 1024 rule length; 250 posts/sec | | GET | `/2/tweets/search/stream/rules` | 450/15min | — | 1 connection; 1000 rules; 1024 rule length | | POST | `/2/tweets/search/stream/rules` | 100/15min | — | 1 connection; 1000 rules; 1024 rule length | #### Manage posts | Method | Endpoint | Per App | Per User | | :----- | :-------------- | :----------- | :-------- | | POST | `/2/tweets` | 10,000/24hrs | 100/15min | | DELETE | `/2/tweets/:id` | — | 50/15min | #### Timelines | Method | Endpoint | Per App | Per User | | :----- | :--------------------------------------------- | :----------- | :-------- | | GET | `/2/users/:id/tweets` | 10,000/15min | 900/15min | | GET | `/2/users/:id/mentions` | 450/15min | 300/15min | | GET | `/2/users/:id/timelines/reverse_chronological` | — | 180/15min | #### Likes lookup | Method | Endpoint | Per App | Per User | | :----- | :--------------------------- | :------- | :------- | | GET | `/2/tweets/:id/liking_users` | 75/15min | 75/15min | | GET | `/2/users/:id/liked_tweets` | 75/15min | 75/15min | #### Manage likes | Method | Endpoint | Per App | Per User | | :----- | :----------------------------- | :------ | :-------------------- | | POST | `/2/users/:id/likes` | — | 50/15min, 1,000/24hrs | | DELETE | `/2/users/:id/likes/:tweet_id` | — | 50/15min, 1,000/24hrs | #### Retweets lookup | Method | Endpoint | Per App | Per User | Notes | | :----- | :--------------------------- | :------- | :------- | :-------------- | | GET | `/2/tweets/:id/retweeted_by` | 75/15min | 75/15min | — | | GET | `/2/tweets/:id/quote_tweets` | 75/15min | 75/15min | — | | GET | `/2/users/reposts_of_me` | — | 75/15min | 100 max results | #### Manage retweets | Method | Endpoint | Per App | Per User | | :----- | :-------------------------------- | :------ | :------- | | POST | `/2/users/:id/retweets` | — | 50/15min | | DELETE | `/2/users/:id/retweets/:tweet_id` | — | 50/15min | #### Hide replies | Method | Endpoint | Per App | Per User | | :----- | :--------------------------- | :------ | :------- | | PUT | `/2/tweets/:tweet_id/hidden` | — | 50/15min | *** ### Users (14 endpoints) #### Users lookup | Method | Endpoint | Per App | Per User | | :----- | :------------------------------- | :-------- | :-------- | | GET | `/2/users` | 300/15min | 900/15min | | GET | `/2/users/:id` | 300/15min | 900/15min | | GET | `/2/users/by` | 300/15min | 900/15min | | GET | `/2/users/by/username/:username` | 300/15min | 900/15min | | GET | `/2/users/me` | — | 75/15min | #### Search users | Method | Endpoint | Per App | Per User | | :----- | :---------------- | :-------- | :-------- | | GET | `/2/users/search` | 300/15min | 900/15min | #### Follows lookup | Method | Endpoint | Per App | Per User | | :----- | :----------------------- | :-------- | :-------- | | GET | `/2/users/:id/following` | 300/15min | 300/15min | | GET | `/2/users/:id/followers` | 300/15min | 300/15min | #### Manage follows | Method | Endpoint | Per App | Per User | | :----- | :--------------------------------------------------- | :------ | :------- | | POST | `/2/users/:id/following` | — | 50/15min | | DELETE | `/2/users/:source_user_id/following/:target_user_id` | — | 50/15min | #### Blocks lookup | Method | Endpoint | Per App | Per User | | :----- | :---------------------- | :------ | :------- | | GET | `/2/users/:id/blocking` | — | 15/15min | #### Mutes lookup | Method | Endpoint | Per App | Per User | | :----- | :-------------------- | :------ | :------- | | GET | `/2/users/:id/muting` | — | 15/15min | #### Manage mutes | Method | Endpoint | Per App | Per User | | :----- | :------------------------------------------------ | :------ | :------- | | POST | `/2/users/:id/muting` | — | 50/15min | | DELETE | `/2/users/:source_user_id/muting/:target_user_id` | — | 50/15min | *** ### Spaces (6 endpoints) #### Spaces lookup | Method | Endpoint | Per App | Per User | | :----- | :------------------------- | :--------------- | :--------------- | | GET | `/2/spaces/:id` | 300/15min | 300/15min | | GET | `/2/spaces` | 300/15min | 300/15min | | GET | `/2/spaces/:id/tweets` | 300/15min | 300/15min | | GET | `/2/spaces/by/creator_ids` | 300/15min, 1/sec | 300/15min, 1/sec | | GET | `/2/spaces/:id/buyers` | 300/15min | 300/15min | #### Search Spaces | Method | Endpoint | Per App | Per User | | :----- | :----------------- | :-------- | :-------- | | GET | `/2/spaces/search` | 300/15min | 300/15min | *** ### Direct Messages (8 endpoints) #### Direct Messages lookup | Method | Endpoint | Per App | Per User | | :----- | :--------------------------------------------------- | :------ | :------- | | GET | `/2/dm_events` | — | 15/15min | | GET | `/2/dm_events/:id` | — | 15/15min | | GET | `/2/dm_conversations/:dm_conversation_id/dm_events` | — | 15/15min | | GET | `/2/dm_conversations/with/:participant_id/dm_events` | — | 15/15min | #### Manage Direct Messages | Method | Endpoint | Per App | Per User | | :----- | :-------------------------------------------------- | :---------- | :--------------------- | | POST | `/2/dm_conversations` | 1,440/24hrs | 15/15min, 1,440/24hrs | | POST | `/2/dm_conversations/with/:participant_id/messages` | 1,440/24hrs | 15/15min, 1,440/24hrs | | POST | `/2/dm_conversations/:dm_conversation_id/messages` | 1,440/24hrs | 15/15min, 1,440/24hrs | | DELETE | `/2/dm_events/:id` | 4,000/24hrs | 300/15min, 1,500/24hrs | *** ### Lists (14 endpoints) #### Lists lookup | Method | Endpoint | Per App | Per User | | :----- | :------------------------- | :------- | :------- | | GET | `/2/lists/:id` | 75/15min | 75/15min | | GET | `/2/users/:id/owned_lists` | 15/15min | 15/15min | #### List Tweets lookup | Method | Endpoint | Per App | Per User | | :----- | :-------------------- | :-------- | :-------- | | GET | `/2/lists/:id/tweets` | 900/15min | 900/15min | #### List member lookup | Method | Endpoint | Per App | Per User | | :----- | :------------------------------ | :-------- | :-------- | | GET | `/2/lists/:id/members` | 900/15min | 900/15min | | GET | `/2/users/:id/list_memberships` | 75/15min | 75/15min | #### Manage Lists | Method | Endpoint | Per App | Per User | | :----- | :------------- | :------ | :-------- | | POST | `/2/lists` | — | 300/15min | | DELETE | `/2/lists/:id` | — | 300/15min | | PUT | `/2/lists/:id` | — | 300/15min | #### Manage List members | Method | Endpoint | Per App | Per User | | :----- | :------------------------------ | :------ | :-------- | | POST | `/2/lists/:id/members` | — | 300/15min | | DELETE | `/2/lists/:id/members/:user_id` | — | 300/15min | #### Manage List follows | Method | Endpoint | Per App | Per User | | :----- | :------------------------------------- | :------ | :------- | | POST | `/2/users/:id/followed_lists` | — | 50/15min | | DELETE | `/2/users/:id/followed_lists/:list_id` | — | 50/15min | #### Pinned Lists | Method | Endpoint | Per App | Per User | | :----- | :----------------------------------- | :------- | :------- | | GET | `/2/users/:id/pinned_lists` | 15/15min | 15/15min | | POST | `/2/users/:id/pinned_lists` | — | 50/15min | | DELETE | `/2/users/:id/pinned_lists/:list_id` | — | 50/15min | *** ### Bookmarks (5 endpoints) #### Bookmarks lookup | Method | Endpoint | Per App | Per User | | :----- | :------------------------------------------ | :------- | :-------- | | GET | `/2/users/:id/bookmarks` | — | 180/15min | | GET | `/2/users/:id/bookmarks/folders` | 50/15min | 50/15min | | GET | `/2/users/:id/bookmarks/folders/:folder_id` | 50/15min | 50/15min | #### Manage Bookmarks | Method | Endpoint | Per App | Per User | | :----- | :--------------------------------- | :------ | :------- | | POST | `/2/users/:id/bookmarks` | — | 50/15min | | DELETE | `/2/users/:id/bookmarks/:tweet_id` | — | 50/15min | *** ### Compliance (3 endpoints) #### Batch compliance | Method | Endpoint | Per App | Per User | | :----- | :--------------------------- | :-------- | :------- | | POST | `/2/compliance/jobs` | 150/15min | — | | GET | `/2/compliance/jobs/:job_id` | 150/15min | — | | GET | `/2/compliance/jobs` | 150/15min | — | *** ### Usage (1 endpoint) | Method | Endpoint | Per App | Per User | | :----- | :---------------- | :------- | :------- | | GET | `/2/usage/tweets` | 50/15min | — | *** ### Trends (2 endpoints) #### Personalized Trends | Method | Endpoint | Per App | Per User | | :----- | :----------------------------- | :------------------- | :------------------ | | GET | `/2/users/personalized_trends` | 200/24hrs, 200/15min | 100/24hrs, 10/15min | #### Trends by WOEID | Method | Endpoint | Per App | Per User | | :----- | :----------------------- | :------- | :------- | | GET | `/2/trends/by/woeid/:id` | 75/15min | — | *** ### Communities (2 endpoints) | Method | Endpoint | Per App | Per User | Notes | | :----- | :---------------------- | :-------- | :-------- | :-------------- | | GET | `/2/communities/:id` | 300/15min | 300/15min | — | | GET | `/2/communities/search` | 300/15min | 300/15min | 100 max results | *** ### Analytics (1 endpoint) | Method | Endpoint | Per App | Per User | | :----- | :-------------------- | :-------- | :-------- | | GET | `/2/tweets/analytics` | 300/15min | 300/15min | *** ### Media (8 endpoints) | Method | Endpoint | Per App | Per User | | :----- | :----------------------------- | :------------ | :---------- | | POST | `/2/media/upload` | 50,000/24hrs | 500/15min | | GET | `/2/media/upload` | 100,000/24hrs | 1,000/15min | | POST | `/2/media/upload/initialize` | 180,000/24hrs | 1,875/15min | | POST | `/2/media/upload/:id/append` | 180,000/24hrs | 1,875/15min | | POST | `/2/media/upload/:id/finalize` | 180,000/24hrs | 1,875/15min | | POST | `/2/media/metadata` | 50,000/24hrs | 500/15min | | POST | `/2/media/subtitles` | 10,000/24hrs | 100/15min | | DELETE | `/2/media/subtitles` | 10,000/24hrs | 100/15min | *** ### Activity & Webhooks | Method | Endpoint | Per App | Per User | Notes | | :----- | :------------------------------------------- | :-------- | :------- | :--------------------------- | | GET | `/2/activity/stream` | 450/15min | — | 2 connections; 250 posts/sec | | POST | `/2/activity/subscriptions` | 500/15min | — | — | | GET | `/2/activity/subscriptions` | 500/15min | — | — | | PUT | `/2/activity/subscriptions/:subscription_id` | 500/15min | — | — | | DELETE | `/2/activity/subscriptions/:subscription_id` | 500/15min | — | — | | POST | `/2/webhooks` | 450/15min | — | — | | GET | `/2/webhooks` | 450/15min | — | — | | PUT | `/2/webhooks/:webhook_id` | 450/15min | — | — | | DELETE | `/2/webhooks/:webhook_id` | 450/15min | — | — | | POST | `/2/webhooks/replay` | 100/15min | — | — | *** ### Other endpoints | Method | Endpoint | Per App | Per User | | :----- | :---------------------------------------- | :-------------------- | :------------------ | | GET | `/2/tweets/sample10/stream` | 100/15min | — | | GET | `/2/news/:id` | 200/15min | — | | GET | `/2/news/search` | 200/15min | 200/15min | | POST | `/2/users/:id/dm/block` | 25/15min, 1,000/24hrs | 10/15min, 400/24hrs | | POST | `/2/users/:id/dm/unblock` | 25/15min, 1,000/24hrs | 10/15min, 400/24hrs | | GET | `/2/users/by/username/:username/tweets` | 1,500/15min | 900/15min | | GET | `/2/users/by/username/:username/mentions` | 450/15min | 180/15min | | GET | `/2/users/:id/following/spaces` | 300/15min | 300/15min | | GET | `/2/tweets/:id/retweets` | 75/15min | 75/15min | | DELETE | `/2/connections/all` | 25/15min | 25/15min | *** ## Handling rate limits When you hit a rate limit, you'll receive a 429 response: ```json theme={null} { "errors": [{ "code": 88, "message": "Rate limit exceeded" }] } ``` ### Recovery strategy 1. Check `x-rate-limit-reset` for when the window resets 2. Wait until that time before retrying 3. Use exponential backoff if needed ```python theme={null} import time def make_request_with_backoff(url, headers): response = requests.get(url, headers=headers) if response.status_code == 429: reset_time = int(response.headers.get('x-rate-limit-reset', 0)) wait_time = max(reset_time - time.time(), 60) time.sleep(wait_time) return make_request_with_backoff(url, headers) return response ``` *** ## Best practices Store results locally to reduce repeated requests. For real-time data, use filtered stream instead of polling. Track remaining requests to avoid hitting limits. Distribute requests across the time window. *** ## Rate limits vs. billing Rate limits and billing are separate: | Concept | Purpose | | :---------------- | :--------------------------------------------- | | **Rate limits** | Control request frequency for system stability | | **Usage billing** | Charge for data retrieved (pay-per-usage) | You can be within rate limits but still incur usage costs, or hit rate limits without additional cost. *** ## Enterprise rate limits Enterprise customers have custom rate limits. Contact your account manager or [apply for Enterprise access](/enterprise/forms/enterprise-api-interest). *** ## Next steps Handle 429 and other errors. Learn about access levels and features. # Recovery and redundancy Source: https://docs.x.com/x-api/fundamentals/recovery-and-redundancy Features for recovering missed data and building resilient streaming applications Learn how to maximize connection time and recover missed data when using X streaming endpoints including [Filtered Stream](/x-api/posts/filtered-stream/introduction), [Firehose Streams](/x-api/stream/stream-all-posts), [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. Firehose Streams use a `partition` parameter to support up to 20 concurrent connections, while Sample10 (Decahose) supports up to 2 partitions. 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` | | Firehose Stream | `backfill_minutes` | `?backfill_minutes=5` | | Sample Stream (1%) | `backfill_minutes` | `?backfill_minutes=5` | | Sample10 Stream (Decahose) | `backfill_minutes` | `?backfill_minutes=5` | | Powerstream | `backfillMinutes` | `?backfillMinutes=5` | Example requests: **Filtered Stream:** ```bash theme={null} curl 'https://api.x.com/2/tweets/search/stream?backfill_minutes=5' \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` **Firehose Stream:** ```bash theme={null} curl 'https://api.x.com/2/tweets/firehose/stream?backfill_minutes=5&partition=1' \ -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 theme={null} 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" ``` **Firehose Stream:** ```bash theme={null} curl 'https://api.x.com/2/tweets/firehose/stream?start_time=2022-07-12T15:10:00Z&end_time=2022-07-12T15:20:00Z&partition=1' \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` **Powerstream:** ```bash theme={null} 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 * Firehose, Sample10 (Decahose), and language-specific firehose streams also support recovery * The basic 1% Sample Stream does not support recovery; use the Search endpoint instead *** ## 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} theme={null} 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 # Response Codes & Errors Source: https://docs.x.com/x-api/fundamentals/response-codes-and-errors HTTP status codes and error handling for the X API The X API uses standard HTTP status codes. Successful requests return 2xx codes; errors return 4xx or 5xx codes with details in the response body. *** ## HTTP status codes ### Success codes | Code | Meaning | Description | | :------ | :--------- | :---------------------------------------------- | | **200** | OK | Request successful | | **201** | Created | Resource created (POST requests) | | **204** | No Content | Success with no response body (DELETE requests) | ### Client error codes | Code | Meaning | Common causes | | :------ | :---------------- | :--------------------------------------------------------- | | **400** | Bad Request | Invalid JSON, malformed query, missing required parameters | | **401** | Unauthorized | Invalid or missing authentication credentials | | **403** | Forbidden | Valid auth but no permission for this resource or action | | **404** | Not Found | Resource doesn't exist or has been deleted | | **409** | Conflict | Stream has no rules (filtered stream only) | | **429** | Too Many Requests | Rate limit or usage cap exceeded | ### Server error codes | Code | Meaning | What to do | | :------ | :-------------------- | :------------------------------------------- | | **500** | Internal Server Error | Wait and retry; check [status page](/status) | | **502** | Bad Gateway | Wait and retry | | **503** | Service Unavailable | X is overloaded; wait and retry | | **504** | Gateway Timeout | Wait and retry | *** ## Error response format Error responses include structured details: ```json theme={null} { "title": "Invalid Request", "detail": "The 'query' parameter is required.", "type": "https://api.x.com/2/problems/invalid-request" } ``` | Field | Description | | :------- | :---------------------------------- | | `type` | URI identifying the error type | | `title` | Short error description | | `detail` | Specific explanation for this error | Additional fields may be present depending on the error type. *** ## Error types | Type | Description | | :-------------------------------- | :------------------------------------------ | | `about:blank` | Generic error (see HTTP status code) | | `.../invalid-request` | Malformed request or invalid parameters | | `.../resource-not-found` | Post, user, or other resource doesn't exist | | `.../not-authorized-for-resource` | No access to private/protected content | | `.../client-forbidden` | App not enrolled or lacks required access | | `.../usage-capped` | Usage cap exceeded | | `.../rate-limit-exceeded` | Rate limit exceeded | | `.../streaming-connection` | Stream connection problem | | `.../rule-cap` | Too many filtered stream rules | | `.../invalid-rules` | Rule syntax error | | `.../duplicate-rules` | Rule already exists | *** ## Partial errors Some requests can partially succeed. A 200 response may include both `data` and `errors`: ```json theme={null} { "data": [ {"id": "123", "text": "Hello"} ], "errors": [ { "resource_id": "456", "resource_type": "tweet", "title": "Not Found Error", "detail": "Could not find tweet with id: [456].", "type": "https://api.x.com/2/problems/resource-not-found" } ] } ``` This happens when requesting multiple resources and some are unavailable. *** ## Troubleshooting common errors **Check your authentication:** * Verify you're using the correct authentication method for the endpoint * Ensure credentials haven't been regenerated * Check the `Authorization` header format * For OAuth 1.0a, verify signature calculation [Authentication guide →](/resources/fundamentals/authentication/overview) **Check your access:** * Verify your app has access to this endpoint * Some endpoints require specific enrollment or approval * User-context endpoints need appropriate OAuth scopes * The resource may be private or protected **Rate limited:** * Check `x-rate-limit-reset` header for when to retry * Implement exponential backoff * Consider caching responses * Spread requests across the time window [Rate limits guide →](/x-api/fundamentals/rate-limits) **Fix your request:** * Validate JSON syntax * Check for missing required parameters * Verify parameter types (strings vs. numbers) * Escape special characters in queries **Check these factors:** * Protected accounts' posts are only visible with authorization * Deleted posts return 404 * Some posts are withheld in certain regions * Verify search query syntax is correct **Handle reconnection:** * Implement automatic reconnect with backoff * Use recovery features for missed data * Check for full-buffer disconnects (client not consuming fast enough) * Verify at least one stream rule exists [Streaming guide →](/x-api/posts/filtered-stream/integrate/handling-disconnections) *** ## Rate limit headers Every response includes rate limit information: ``` x-rate-limit-limit: 900 x-rate-limit-remaining: 847 x-rate-limit-reset: 1705420800 ``` | Header | Description | | :----------------------- | :-------------------------------- | | `x-rate-limit-limit` | Max requests in current window | | `x-rate-limit-remaining` | Requests remaining | | `x-rate-limit-reset` | Unix timestamp when window resets | *** ## Best practices Always check HTTP status before parsing the response body. Check for `errors` array even in 200 responses. Use exponential backoff for 429 and 5xx errors. Include request ID and timestamp for debugging. *** ## Getting help When posting questions about errors, include: * The API endpoint URL * Request headers (sanitize credentials) * Full error response * What you expected to happen * Steps you've tried Ask questions and search solutions. Check for known issues. # Versioning Source: https://docs.x.com/x-api/fundamentals/versioning X API versioning strategy and deprecation policy The X API uses version numbers in endpoint paths to provide stability while allowing for evolution. Understanding our versioning strategy helps you plan integrations and stay current. *** ## Current versions | Version | Status | Description | | :------------- | :-------- | :--------------------------------------------------- | | **v2** | Current | Modern endpoints, flexible pricing, all new features | | **v1.1** | Legacy | Limited support, minimal updates | | **Enterprise** | Available | High-volume access with dedicated support | Use **X API v2** for all new projects. This is where all new features ship. *** ## Version in URLs The version number appears in the endpoint path: ``` https://api.x.com/2/tweets ^ version ``` *** ## Breaking vs. non-breaking changes ### Breaking changes (require code updates) These changes only occur in major version bumps: * Removing an endpoint * Removing a response field * Removing a query parameter * Adding a new required parameter * Changing a field's data type * Renaming a field or resource * Changing response codes or error types * Modifying authorization scopes ### Non-breaking changes (additive) These can happen at any time without version changes: * Adding a new endpoint * Adding a new optional parameter * Adding a new response field * Adding new OAuth scopes * Changing error message text * Nulling fields for privacy/security reasons *** ## Release schedule | Type | Frequency | Notice | | :----------------------- | :-------------------- | :-------------------------------- | | **Major versions** | No more than annually | Migration guides provided | | **Non-breaking changes** | Ongoing | Changelog updates | | **Security patches** | As needed | May be applied to current version | *** ## Deprecation policy When we release a new major version: 1. **Deprecation**: Previous version is marked deprecated 2. **Support period**: Deprecated version continues to work for a defined period 3. **Retirement**: Deprecated version is removed ### Definitions | Status | Meaning | | :------------- | :--------------------------------------------------- | | **Active** | Fully supported with new features and fixes | | **Deprecated** | No new features; critical bugs only; use discouraged | | **Retired** | No longer accessible | *** ## Staying informed Get notified about changes: All platform changes and updates. Breaking change notices. Platform news and updates. Monthly digest. *** ## Migration resources When a new version is released, we provide: * **Migration guides**: Step-by-step upgrade instructions * **Endpoint mapping**: v1 to v2 equivalents * **Data format changes**: Object model differences Current migration guidance. v1 to v2 endpoint mapping. *** ## Best practices Start new projects on the latest version. Subscribe to changelog and forum updates. Test in development before production updates. Don't wait until deprecation to upgrade. # Getting Access Source: https://docs.x.com/x-api/getting-started/getting-access Sign up for API access and get your credentials Get started with the X API in three steps: sign up, create an app, and save your credentials. *** ## Step 1: Create a developer account Visit [console.x.com](https://console.x.com) and sign in with your X account. Review and accept the Developer Agreement and Policy. Provide basic information about how you'll use the API. *** ## Step 2: Create an app After signing up, create an app to get your API credentials: From the Developer Console dashboard, create a new app. Provide a name, description, and use case for your app. The console will generate your API keys and tokens. *** ## Step 3: Save your credentials You'll receive several credentials depending on your authentication needs: | Credential | Purpose | | :------------------------ | :----------------------------------------------------------------------- | | **API Key & Secret** | Identify your app. Used to generate tokens and sign OAuth 1.0a requests. | | **Bearer Token** | App-only authentication for reading public data. | | **Access Token & Secret** | Make requests on behalf of your own account (OAuth 1.0a). | | **Client ID & Secret** | OAuth 2.0 authentication for user-context requests. | **Save immediately.** Credentials are only displayed once. Store them in a password manager or secure vault. If you lose them, you'll need to regenerate (which invalidates the old ones). *** ## Which credentials do you need? Use the **Bearer Token** for simple, read-only access to public data. ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Best for: Searching posts, looking up users, reading trends. Use **OAuth 2.0** (recommended) or **OAuth 1.0a** to act on behalf of users. OAuth 2.0 offers fine-grained scopes—request only the permissions you need. Best for: Posting, liking, following, accessing DMs. [OAuth 2.0 guide →](/resources/fundamentals/authentication/oauth-2-0/overview) Use your **Access Token & Secret** to make requests as your own account. These tokens represent the account that owns the app. Best for: Testing, personal bots, your own automation. *** ## Credential security best practices Never hardcode credentials in your source code. Add credential files to `.gitignore`. Regenerate credentials periodically as a security measure. Only request the OAuth permissions your app needs. *** ## Next steps Call the API with your new credentials. Understand OAuth 1.0a and OAuth 2.0. # Pricing Source: https://docs.x.com/x-api/getting-started/pricing Pay-per-usage pricing for the X API The X API uses **pay-per-usage** pricing. No subscriptions—pay only for what you use. *** ## How it works Purchase credits upfront in the Developer Console. Credits are deducted as you make API requests. Different endpoints have different costs. View current rates in the Developer Console. No contracts, subscriptions, or minimum spend. Start and stop anytime. Monitor usage and costs live in the Developer Console. 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. *** ## Credit consumption details All prices are per resource fetched (reads) or per request (writes/actions). [Purchase credits](https://console.x.com) in the Developer Console. ### Read operations Charged per resource returned in the response. | Resource | Unit Cost | | :---------------------------- | :------------------- | | **Posts: Read** | \$0.005 per resource | | **User: Read** | \$0.010 per resource | | **DM Event: Read** | \$0.010 per resource | | **Following/Followers: Read** | \$0.010 per resource | | **List: Read** | \$0.005 per resource | | **Space: Read** | \$0.005 per resource | | **Community: Read** | \$0.005 per resource | | **Note: Read** | \$0.005 per resource | | **Media: Read** | \$0.005 per resource | | **Analytics: Read** | \$0.005 per resource | | **Trend: Read** | \$0.010 per resource | ### Write operations Charged per request. | Action | Unit Cost | | :----------------------------- | :------------------ | | **Content: Create** | \$0.015 per request | | **Content: Create (with URL)** | \$0.200 per request | | **DM Interaction: Create** | \$0.015 per request | | **User Interaction: Create** | \$0.015 per request | | **Interaction: Delete** | \$0.010 per request | | **Content: Manage** | \$0.005 per request | | **List: Create** | \$0.010 per request | | **List: Manage** | \$0.005 per request | | **Bookmark** | \$0.005 per request | | **Media Metadata** | \$0.005 per request | | **Privacy: Update** | \$0.010 per request | | **Mute: Delete** | \$0.005 per request | | **Counts: Recent** | \$0.005 per request | | **Counts: All** | \$0.010 per request | Prices are subject to change. Current rates are always available in the [Developer Console](https://console.x.com) and on the [developer.x.com pricing page](https://developer.x.com/#pricing). *** ## Owned Reads Owned Reads are requests made by your own developer app for your own data (posts, bookmarks, followers, likes, lists, and more). These endpoints are priced at **\$0.001 per resource** (1,000 resources for \$1). The following endpoints qualify for Owned Read pricing when `{id}` matches the authenticated user and that user is the owner of the developer app: | Endpoint | Description | | :----------------------------------- | :------------------- | | `GET /2/users/{id}/tweets` | Your own posts | | `GET /2/users/{id}/mentions` | Your mentions | | `GET /2/users/{id}/liked_tweets` | Posts you liked | | `GET /2/users/{id}/bookmarks` | Your bookmarks | | `GET /2/users/{id}/followers` | Your followers | | `GET /2/users/{id}/following` | Accounts you follow | | `GET /2/users/{id}/blocking` | Accounts you blocked | | `GET /2/users/{id}/muting` | Accounts you muted | | `GET /2/users/{id}/owned_lists` | Lists you own | | `GET /2/users/{id}/followed_lists` | Lists you follow | | `GET /2/users/{id}/list_memberships` | Lists you belong to | | `GET /2/users/{id}/pinned_lists` | Your pinned lists | Owned Reads make it significantly cheaper to build apps that work with a user's own data, such as dashboard apps, personal analytics, or account management tools. *** ## Deduplication All resources are deduplicated within a **24-hour UTC day window**. If you request and are charged for a resource (such as a Post), requesting the same resource again within that window will not incur an additional charge. This means: * Requesting the same Post multiple times in a day counts as one charge * The deduplication window resets at midnight UTC * This applies to all billable resources (Posts, users, etc.) Deduplication is a **soft guarantee**. While it occurs in the vast majority of cases, there may be specific edge cases like service outages that result in resources not being deduplicated. *** ## Credit balance Your credit balance is displayed in the Developer Console. Credits are deducted in real-time as you make API requests. Monitor your credit balance regularly to avoid service interruptions. Add credits before your balance reaches zero to ensure uninterrupted API access. ***Note:** It is possible for an account credit balance to go slightly negative. In this case, API requests will be blocked until you add credits to cover the negative balance.* ### Auto-recharge Enable auto-recharge to automatically top up your credit balance and avoid service interruptions. Configure this in the Developer Console: | Setting | Description | | :-------------------- | :---------------------------------------------------------------------------- | | **Recharge amount** | The amount to add when auto-recharge triggers (e.g., \$25) | | **Trigger threshold** | Auto-recharge activates when your balance falls below this amount (e.g., \$5) | Auto-recharge requires a saved payment method set as your default. You can cancel anytime in the Developer Console or by contacting support. *** ### Spending limits Set a maximum amount you can spend per billing cycle to control costs. When the limit is reached, API requests will be blocked until the next billing cycle. | Option | Description | | :----------------- | :------------------------------------------------------------------- | | **Spending limit** | Set a specific dollar amount as your maximum spend per billing cycle | Use spending limits to prevent unexpected charges, especially during development and testing. *** ## 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): ```bash theme={null} curl "https://api.x.com/2/usage/tweets" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` This returns daily Post consumption counts, helping you: * Track consumption against your budget * Set up alerts when approaching limits * Identify high-consumption endpoints * Generate usage reports *** ## Next steps Purchase credits and view current pricing. Monitor usage programmatically. # X API Source: https://docs.x.com/x-api/introduction Programmatic access to X's posts, users, spaces, and more The X API gives you programmatic access to X's public conversation. Read posts, publish content, manage users, and analyze trends—all through modern REST endpoints with flexible pay-per-usage pricing. Create an app and make your first request in minutes. Explore all available endpoints. Official Python and TypeScript libraries. *** ## What you can build Search, retrieve, and publish posts. Access timelines, threads, and quote posts. Look up users, manage follows, blocks, and mutes. Find live audio conversations and their participants. Send and receive private messages. Create and manage curated lists of accounts. Access trending topics by location. *** ## Pricing The X API uses **pay-per-usage** pricing. No subscriptions—pay only for what you use. Start small and grow. Costs scale with your actual usage. No contracts or minimum spend. Stop anytime. Monitor usage and costs live in the Developer Console. Purchase credits upfront. Deducted as you use the API. 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)
*** ## Key features ### Rich data objects Access detailed, structured data for posts, users, media, and more: * **Posts**: Full text, metrics, entities, annotations, conversation threads * **Users**: Profiles, follower counts, verification status * **Media**: Images, videos, GIFs with metadata * **Polls**: Options and vote counts Customize responses with [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) to get exactly the data you need. ### Filtered stream Get posts delivered in near real-time as they're published. Define up to 1,000 filtering rules to receive only matching posts. ```bash theme={null} # Add a rule curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Authorization: Bearer $TOKEN" \ -d '{"add": [{"value": "from:xdevelopers"}]}' # Connect to stream curl "https://api.x.com/2/tweets/search/stream" \ -H "Authorization: Bearer $TOKEN" ``` [Learn more about filtered stream →](/x-api/posts/filtered-stream/introduction) ### Full-archive search Search the complete history of public posts—back to 2006. Build queries with operators for users, keywords, dates, and more. ```bash theme={null} curl "https://api.x.com/2/tweets/search/all?query=AI%20lang:en" \ -H "Authorization: Bearer $TOKEN" ``` ### Metrics Access engagement metrics including impressions, likes, reposts, replies, and video views. [Learn more about search →](/x-api/posts/search/introduction) *** ## Quick start Sign up at [console.x.com](https://console.x.com) and create an app. Generate your Bearer Token for app-only requests. Try looking up a user: ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Tools & libraries Official Python library with async support. Official TypeScript/JavaScript library. Interactive API explorer. [Browse all libraries →](/tools-and-libraries) *** ## Support Get help from the community and X team. FAQs and troubleshooting guides. # X API Overview Source: https://docs.x.com/x-api/overview Complete API reference for posts, users, spaces, DMs, lists, trends, and more The X API gives you programmatic access to X's public conversation. All endpoints below are available on [pay-per-use](/x-api/introduction) plans. Endpoints marked as Enterprise-only require an [Enterprise](/enterprise-api/introduction) plan. *** ## Endpoints Search, retrieve, and publish posts. Access timelines, threads, and quote posts. Look up profiles, manage follows, blocks, and mutes. Send and receive private messages. Find live audio conversations and participants. Create and manage curated lists of accounts. Like and unlike posts. Look up who liked a post. Trending topics by location and personalized trends. Upload images, videos, and GIFs. Manage subtitles and metadata. Look up and search communities. Create, evaluate, and search community notes. Search and retrieve news stories. Batch compliance jobs and compliance streams. *** ## Streaming & real-time Get posts delivered in real-time matching your filter rules. Subscribe to real-time activity events like follows, likes, and profile updates. Receive event data via HTTP callbacks. *** ## Enterprise-only These endpoints require an [Enterprise plan](/enterprise-api/introduction). Full firehose and language-specific streams. Stream all or sampled likes in real-time. High-performance filtered streaming with advanced operators. Post and media engagement metrics at scale. Real-time user event subscriptions. Filtered stream delivery via webhooks. *** ## Quick start ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Get your credentials and call the API in minutes. Official Python and TypeScript libraries. # Build a query Source: https://docs.x.com/x-api/posts/counts/integrate/build-a-query #### Building a query **Query limitations!** Your queries will be limited depending on which [access level](/x-api/getting-started/about-x-api) you are using.  Your query can be 512 characters long for pay-per-use customers, or up to 4,096 characters for Enterprise customers. If you have Enterprise access, please reach out to your account manager.  **Operator availability** While most operators are available to any developer, there are several that are reserved for those that have been approved for Enterprise access. We list which access level each operator is available to in the [list of operators](/x-api/posts/search/integrate/build-a-query) table using the following labels: * Core operators: Available when using any [Project](/resources/fundamentals/developer-apps). * Advanced operators: Available when using a Project with Enterprise access    #### Operator types: standalone and conjunction-required **Standalone operators** can be used alone or together with any other operators (including those that require conjunction). For example, the following query will work because it uses the #hashtag operator, which is standalone: \#xapiv2 **Conjunction-required** operators cannot be used by themselves in a query; they can only be used when at least one standalone operator is included in the query. This is because using these operators alone would be far too general, and would match on an extremely high volume of Posts. For example, the following queries are not supported since they contain only conjunction-required operators: has:media has:links OR is:retweet If we add in a standalone operator, such as the phrase "X data", the query would then work properly.  "X data" has:mentions (has:media OR has:links) #### Boolean operators and grouping If you would like to string together multiple operators in a single query, you have the following tools at your disposal: | | | | :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **AND logic** | Successive operators with a space between them will result in boolean "AND" logic, meaning that Posts will match only if both conditions are met. For example, snow day #NoSchool will match Posts containing the terms snow and day and the hashtag #NoSchool. | | **OR logic** | Successive operators with OR between them will result in OR logic, meaning that Posts will match if either condition is met. For example, specifying grumpy OR cat OR #meme will match any Posts containing at least the terms grumpy or cat, or the hashtag #meme. | | **NOT logic, negation** | Prepend a dash (-) to a keyword (or any operator) to negate it (NOT). For example, cat #meme -grumpy will match Posts containing the hashtag #meme and the term cat, but only if they do not contain the term grumpy. One common query clause is -is:retweet, which will not match on Retweets, thus matching only on original Posts, Quote Tweets, and replies. All operators can be negated, but negated operators cannot be used alone. | | **Grouping** | You can use parentheses to group operators together. For example, (grumpy cat) OR (#meme has:images) will return either Posts containing the terms grumpy and cat, or Posts with images containing the hashtag #meme. Note that ANDs are applied first, then ORs are applied. | **A note on negations** The operators -is:nullcast must always be negated. Negated operators cannot be used alone. Do not negate a set of operators grouped together in a set of parentheses. Instead, negate each individual operator. For example, instead of using skiing -(snow OR day OR noschool), we suggest that you use skiing -snow -day -noschool.  **Order of operations** When combining AND and OR functionality, the following order of operations will dictate how your query is evaluated. 1. Operators connected by AND logic are combined first 2. Then, operators connected with OR logic are applied For example: * apple OR iphone ipad would be evaluated as apple OR (iphone ipad) * ipad iphone OR android would be evaluated as (iphone ipad) OR android To eliminate uncertainty and ensure that your query is evaluated as intended, group terms together with parentheses where appropriate.  For example: * (apple OR iphone) ipad * iphone (ipad OR android)   **Punctuation, diacritics, and case sensitivity** If you specify a keyword or hashtag query with character accents or diacritics, it will match Post text that contains both the term with the accents and diacritics, as well as those terms with normal characters. For example, queries with a keyword Diacrítica or hashtag #cumpleaños will match *Diacrítica* or *#cumpleaños*, as well as with *Diacritica* or *#cumpleanos* without the tilde í or eñe. Characters with accents or diacritics are treated the same as normal characters and are not treated as word boundaries. For example, a query with the keyword cumpleaños would only match activities containing the word *cumpleaños* and would not match activities containing *cumplea*, *cumplean*, or *os*. All operators are evaluated in a case-insensitive manner. For example, the query cat will match Posts with all of the following: *cat*, *CAT*, *Cat*. The [filtered stream](/x-api/posts/filtered-stream) matching behavior acts differently from Post counts. When [building a filtered stream rule](/x-api/posts/filtered-stream/integrate/build-a-rule), know that keywords and hashtags that include accents and diacritics will only match on terms that also include the accent and diacritic, and will not match on terms that use normal characters instead.  For example, filtered stream rules that include a keyword Diacrítica or hashtag #cumpleaños will only match the terms *Diacrítica* and *#cumpleaños*, and will not match on *Diacritica* or *#cumpleanos* without the tilde í or eñe **Specificity and efficiency** When you start to build your query, it is important to keep a few things in mind. * Using broad, standalone operators for your query such as a single keyword or #hashtag is generally not recommended since it will likely match on a massive volume of Posts. Creating a more robust query will result in a more specific set of matching Posts, and will hopefully increase the accuracy of your Post counts to help you find more valuable insights.  * For example, if your query was just the keyword happy you will likely get anywhere from 200,000 - 300,000 Posts per day. * Adding more conditional operators narrows your results, for example (happy OR happiness) place\_country:GB -birthday -is:retweet * Writing efficient queries is also beneficial for staying within the characters query length restriction. The character count includes the entire query string including spaces and operators. * For example, the following query is 59 characters long: (happy OR happiness) place\_country:GB -birthday -is:retweet **Quote Tweet matching behavior** When using the Post counts endpoints, operators will not match on the content from the original Post that was quoted, but will match on the content included in the Quote Tweet. However, please note that [filtered stream](/x-api/posts/filtered-stream) will match on both the content from the original Post that was quoted and the Quote Tweet's content.   **Iteratively building a query** **Test your query early and often** Getting a query to return the "right" results the first time is rare. There is so much on X that may or may not be obvious at first and the query syntax described above may be hard to match to your desired query. As you build a query, it is important for you to periodically test it out using one of the [Search Post](/x-api/posts/search/introduction) endpoints to ensure that the Posts that are matching your query are relevant to your use case. For this section, we are going to start with the following query and adjust it based on the results that we receive during our test:  happy OR happiness **Use results to narrow the query** As you test the query with Search Posts, you should scan the returned Posts to see if they include the data that you are expecting and hoping to receive. Starting with a broad query and a superset of Post matches allows you to review the result and narrow the query to filter out undesired results.   When we tested the example query, we noticed that we were getting Posts in a variety of different languages. In this situation, we want to only receive Posts that are in english, so we’re going to add the lang: operator: (happy OR happiness) lang:en The test delivered a number of Posts wishing people a happy birthday, so we are going to add -birthday as a negated keyword operator. We also want to only receive original Posts, so we’ve added the negated -is:retweet operator: (happy OR happiness) lang:en -birthday -is:retweet **Adjust for inclusion where needed** If you notice that you are not receiving data via Search Posts that you expect and know that there are existing Posts that should return, you may need to broaden your query by removing operators that may be filtering out the desired data.  For our example, we noticed that there were other Posts in our personal timeline that expressed the emotion that we are looking for and weren’t included in the test results. To ensure we have greater coverage, we are going to add the keywords, excited and elated. (happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet **Adjust for popular trends/bursts over the time period** Trends come and go on X quickly. Maintaining your query should be an active process. If you plan to use a query for a while, we suggest that you periodically check in on the data that you are receiving to see if you need to make any adjustments. In our example, we notice that we started to receive some Posts that are wishing people a “happy holidays”. Since we don’t want these Posts included in our results, we are going to add a negated -holidays keyword. (happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet -holidays  Once you've properly tested and iterated upon your query, you can start sending it with the Post counts endpoints to start to receive just the volume of Posts rather than the full Post payloads. #### Adding a query to your request To add your query to your request, you must use the query parameter. As with any query parameters, you must make sure to HTTP encode the query that you developed. Here is an example of what this might look like using a cURL command. If you would like to use this command, please make sure to replace \$BEARER\_TOKEN with your own [Bearer Token](/resources/fundamentals/authentication#oauth-2-0): ``` curl https://api.x.com/2/tweets/counts/recent?query=cat%20has%3Amedia%20-grumpy&tweet.fields=created_at&max_results=100 -H "Authorization: Bearer $BEARER_TOKEN" ``` #### Query examples **Tracking a natural disaster** The following query matched on original Posts coming from weather agencies and gauges that discuss Hurricane Harvey, which hit Houston in 2017. Here is what the query would look like without the HTTP encoding: has:geo (from:NWSNHC OR from:NHC\_Atlantic OR from:NWSHouston OR from:NWSSanAntonio OR from:USGS\_TexasRain OR from:USGS\_TexasFlood OR from:JeffLindner1) -is:retweet And here is what the query would look like with the HTTP encoding, the query parameter, and the recent Post counts URI: [https://api.x.com/2/tweets/counts/recent?query=-is%3Aretweet%20has%3Ageo%20(from%3ANWSNHC%20OR%20from%3ANHC\\\_Atlantic%20OR%20from%3ANWSHouston%20OR%20from%3ANWSSanAntonio%20OR%20from%3AUSGS\\\_TexasRain%20OR%20from%3AUSGS\_TexasFlood%20OR%20from%3AJeffLindner1)](https://api.x.com/2/tweets/counts/recent?query=-is%3Aretweet%20has%3Ageo%20\(from%3ANWSNHC%20OR%20from%3ANHC\\_Atlantic%20OR%20from%3ANWSHouston%20OR%20from%3ANWSSanAntonio%20OR%20from%3AUSGS\\_TexasRain%20OR%20from%3AUSGS_TexasFlood%20OR%20from%3AJeffLindner1\)) **Reviewing the sentiment of a conversation** The next rule could be used to better understand the sentiment of the conversation developing around the hashtag, *#nowplaying*, but scoped to just Posts published within North America. Here is what the two different queries, one for positive and one for negative, would look like without the HTTP encoding: \#nowplaying (happy OR exciting OR excited OR favorite OR fav OR amazing OR lovely OR incredible) (place\_country:US OR place\_country:MX OR place\_country:CA) -horrible -worst -sucks -bad -disappointing \#nowplaying (horrible OR worst OR sucks OR bad OR disappointing) (place\_country:US OR place\_country:MX OR place\_country:CA) -happy -exciting -excited -favorite -fav -amazing -lovely -incredible And here is what the query would look like with the HTTP encoding, the query parameter, and the recent Post counts URI: [https://api.x.com/2/tweets/counts/recent?query=%23nowplaying%20(happy%20OR%20exciting%20OR%20excited%20OR%20favorite%20OR%20fav%20OR%20amazing%20OR%20lovely%20OR%20incredible)%20(place\\\_country%3AUS%20OR%20place\\\_country%3AMX%20OR%20place\_country%3ACA)%20-horrible%20-worst%20-sucks%20-bad%20-disappointing](https://api.x.com/2/tweets/counts/recent?query=%23nowplaying%20\(happy%20OR%20exciting%20OR%20excited%20OR%20favorite%20OR%20fav%20OR%20amazing%20OR%20lovely%20OR%20incredible\)%20\(place\\_country%3AUS%20OR%20place\\_country%3AMX%20OR%20place_country%3ACA\)%20-horrible%20-worst%20-sucks%20-bad%20-disappointing) [https://api.x.com/2/tweets/counts/recent?query=%23nowplaying%20(horrible%20OR%20worst%20OR%20sucks%20OR%20bad%20OR%20disappointing)%20(place\\\_country%3AUS%20OR%20place\\\_country%3AMX%20OR%20place\_country%3ACA)%20-happy%20-exciting%20-excited%20-favorite%20-fav%20-amazing%20-lovely%20-incredible](https://api.x.com/2/tweets/counts/recent?query=%23nowplaying%20\(horrible%20OR%20worst%20OR%20sucks%20OR%20bad%20OR%20disappointing\)%20\(place\\_country%3AUS%20OR%20place\\_country%3AMX%20OR%20place_country%3ACA\)%20-happy%20-exciting%20-excited%20-favorite%20-fav%20-amazing%20-lovely%20-incredible) **Find Posts that relate to a specific Post annotation** This rule was built to filter for original Posts that included an image of a pet that is not a cat, where the language identified in the Post is Japanese. To do this, we used the context: operator to take advantage of the[Post annotation](/x-api/fundamentals/post-annotations) functionality. We first used the[Post lookup](/x-api/posts/lookup/introduction) endpoint and the tweet.fields=context\_annotations fields parameter to identify which domain.entity IDs we need to use in our query: * Posts that relate to cats return **domain** 66 (Interests and Hobbies category) with entity 852262932607926273 (Cats).  * Posts that relate to pets return **domain** 65 (Interests and Hobbies Vertical) with entity 852262932607926273 (Pets).  Here is what the query would look like without the HTTP encoding: context:65.852262932607926273 -context:66.852262932607926273 -is:retweet has:images lang:ja And here is what the query would look like with the HTTP encoding, the query parameter, and the recent Post counts URI: [https://api.x.com/2/tweets/counts/recent?query=context%3A65.852262932607926273%20-context%3A66.852262932607926273%20-is%3Aretweet%20has%3Aimages%20lang%3Aja](https://api.x.com/2/tweets/counts/recent?query=context%3A65.852262932607926273%20-context%3A66.852262932607926273%20-is%3Aretweet%20has%3Aimages%20lang%3Aja) #### Operators | Operator | Type | Availability | Description | | :--------------------- | :------------------- | :----------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `keyword` | Standalone | Core | 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 | Core | 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 | Core | Matches the exact phrase within the body of a Post. Example: `("X API" OR #v2) -"recent counts"` | | `#` | Standalone | Core | 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 | Core | Matches any Post that mentions the given username, if the username is a recognized entity (including the @ character). Example: `(@XDevelopers OR @API) -@X` | | `$` | Standalone | Advanced | 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 | Core | 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 | Core | 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 | Core | Performs a tokenized match on any validly-formatted URL of a Post. This operator can 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](https://t.co/c0A36SWil4)" (with the short URL redirecting to [https://developer.x.com](https://developer.x.com)) will match both the following rules: `from:XDevelopers url:"https://developer.x.com"` and `from:XDevelopers url:"https://t.co"`. Tokens and phrases containing punctuation or special characters should be double-quoted. | | `retweets_of:` | Standalone | Core | 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:API` | | `context:` | Standalone | Core | Matches Posts with a specific domain id/entity id pair. You can only pass a single domain/entity per `context:` operator. Example: `context:domain_id.entity_id`. You can combine multiple domain/entities using the OR operator: `(context:47.113922 9372198469633 OR context:11.1088514520308342784)` | | `entity:` | Standalone | Core | Matches Posts with a specific entity string value. You can only pass a single `entity:` operator. Example: `entity:"string declaration of entity/place"`. Please note that this is only available with recent search. | | `conversation_id:` | Standalone | Core | 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 | Advanced | 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 endpoint. Example: `list:123` | | `place:` | Standalone | Advanced | 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 standard v1.1 endpoint for how to obtain X place IDs. Example: `place:"new york city" OR place:seattle OR place:fd70c22040963ac7` | | `place_country:` | Standalone | Advanced | 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. You can only pass a single ISO code per `place_country:` operator. Example: `place_country:US OR place_country:MX OR place_country:CA` | | `point_radius:` | Standalone | Advanced | 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. Example: `point_radius:[2.355128 48.861118 16km] OR point_radius:[-41.287336 174.761070 20mi]` | | `bounding_box:` | Standalone | Advanced | 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]`. 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. Example: `bounding_box:[-105.301758 39.964069 -105.178505 40.09455]` | | `is:retweet` | Conjunction required | Core | 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 | Core | 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 Posts, and replies in Retweets. Example: `from:XDevelopers is:reply` | | `is:quote` | Conjunction required | Core | Returns all Quote Tweets, also known as Posts with comments. Example: `"sentiment analysis" is:quote` | | `is:verified` | Conjunction required | Core | Deliver only Posts whose authors are verified by X. Example: `#nowplaying is:verified` | | `-is :nullcast` | Conjunction required | Advanced | Removes Posts created for promotion only on ads.x.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. Example: `"mobile games" -is:nullcast` | | `has:hashtags` | Conjunction required | Core | Matches Posts that contain at least one hashtag. Example: `from:XDevelopers -has:hashtags` | | `has:cashtags` | Conjunction required | Advanced | Matches Posts that contain a cashtag symbol (with a leading ‘$’ character. For example, `$tag`). Example: `#stonks has:cashtags\` | | `has:links` | Conjunction required | Core | This operator matches Posts which contain links and media in the Post body. Example: `from:XDevelopers announcement has:links` | | `has:mentions` | Conjunction required | Core | Matches Posts that mention another X user. Example: `#nowplaying has:mentions` | | `has:media` | Conjunction required | Core | 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 | Core | Matches Posts that contain a recognized URL to an image. Example: `#meme has:images` | | `has:videos` | Conjunction required | Core | 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:videos` | | `has:geo` | Conjunction required | Advanced | 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 | Core | 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` | | | | | | | :---------------- | :--------------------------- | :---------------------------- | :----------------------------- | | 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** | | | # Overview Source: https://docs.x.com/x-api/posts/counts/integrate/overview This page covers tools and key concepts for integrating the Post counts endpoints. *** ## Helpful tools Before we start to explore some key concepts, we recommend that you use one of the following tools or code samples to start testing the functionality of these endpoints. ### Code samples Interested in getting set up with these endpoints with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [GitHub page](https://github.com/xdevplatform/Twitter-API-v2-sample-code), including a [Python client](https://github.com/xdevplatform/search-tweets-python). ### Libraries Take advantage of one of our many [community third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the appropriate version tag. ### Postman Postman is a great tool that you can use to test out these endpoints. Each Postman request includes all of the given endpoint's parameters to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our [Using Postman](/tutorials/postman-getting-started) page. *** ## Key concepts ### Authentication All X API v2 endpoints require requests to be [authenticated](/resources/fundamentals/authentication) with a set of credentials, also known as keys and tokens. This specific endpoint requires the use of [OAuth 2.0 Bearer Token](/resources/fundamentals/authentication#oauth-2-0), which means that you must pass a [Bearer Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) to make a successful request. You can either generate a Bearer Token from directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. ### Developer Console, Projects, and developer Apps To work with any X API v2 endpoints, you must have a [developer account](/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. Your keys and tokens within that developer App will work for the recent Post counts endpoints. If you would like to use the full-archive Post counts endpoint, or utilize the advanced operators and longer query length, you will need to have been approved for enterprise access. Please visit our section on enterprise access to learn more. ### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the volume, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that every developer can make on behalf of an app or on behalf of an authenticated user. This endpoint is rate limited at the App-level, meaning that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by the credentials that you are using). ### Building queries The central feature of these endpoints is their use of a single query to filter the Posts into the counts that deliver to you. These queries are made up of operators that match on Post and user attributes, such as message keywords, hashtags, and URLs. Operators can be combined into queries with boolean logic and parentheses to help refine the query's matching behavior. You can use our guide on [how to build a query](/x-api/posts/counts/integrate/build-a-query) to learn more. ### Pagination For recent Post counts, there is no next\_token returned, which means that regardless of the granularity, you will get the Post volume for the last 7 days in one API call. For full-archive Post counts, you will get data for the last 30 days. For data more than 30 days, you will get a next\_token which you can then use to paginate to get the additional data. # Post Counts Source: https://docs.x.com/x-api/posts/counts/introduction Get Post volume data for queries without retrieving the Posts themselves The Post counts endpoints return the volume of Posts matching a query over time, without returning the Posts themselves. Use these endpoints to analyze trends, understand conversation size, and refine queries before searching. ## Overview Build trendlines and visualizations showing Post volume over time Estimate result size before running full search queries Identify when conversations peaked around events Understand conversation scale without retrieving all Posts *** ## Endpoints | Endpoint | Description | Access | | :---------------------------------------------------------------------- | :-------------------------------- | :---------------------- | | GET [`/2/tweets/counts/recent`](/x-api/posts/get-count-of-recent-posts) | Count Posts from last 7 days | All developers | | GET [`/2/tweets/counts/all`](/x-api/posts/get-count-of-all-posts) | Count Posts from complete archive | Pay-per-use, Enterprise | *** ## Granularity options Specify how counts are grouped using the `granularity` parameter: | Granularity | Description | Use case | | :---------- | :------------------------ | :------------------- | | `minute` | Counts per minute | Real-time monitoring | | `hour` | Counts per hour (default) | Daily analysis | | `day` | Counts per day | Long-term trends | *** ## Recent counts Count Posts from the **last 7 days**. Available to all developers. ### Features * Counts grouped by minute, hour, or day * Same query operators as recent search * 512-character query length ### Example response ```json theme={null} { "data": [ { "start": "2024-01-15T00:00:00.000Z", "end": "2024-01-15T01:00:00.000Z", "tweet_count": 1523 }, { "start": "2024-01-15T01:00:00.000Z", "end": "2024-01-15T02:00:00.000Z", "tweet_count": 1247 }, { "start": "2024-01-15T02:00:00.000Z", "end": "2024-01-15T03:00:00.000Z", "tweet_count": 892 } ], "meta": { "total_tweet_count": 3662 } } ``` Make your first recent counts request Full endpoint documentation *** ## Full-archive counts Count Posts from the **complete archive** back to 2006. Full-archive counts is available to pay-per-use and Enterprise customers. ### Features * Count historical Posts from any time period * All query operators available * 1,024-character query length (4,096 for Enterprise) ### Pagination Results paginate at 31 days per response: * **Day granularity**: 31 days per page * **Hour granularity**: 744 hours (31 days) per page * **Minute granularity**: 44,640 minutes (31 days) per page Make your first full-archive counts request Full endpoint documentation *** ## Query operators Post counts use the same query syntax as search endpoints: ``` python lang:en -is:retweet ``` Learn query syntax and operators *** ## Important notes **Counts vs search results** Counts may not exactly match search results. Search endpoints apply additional compliance filtering that counts endpoints do not perform. *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [Bearer Token](/resources/fundamentals/authentication) Count Posts from the last 7 days Count historical Posts Key concepts and best practices Working code examples # Full-Archive Post Counts Source: https://docs.x.com/x-api/posts/counts/quickstart/full-archive-tweet-counts Get historical Post volume back to 2006 This guide walks you through getting historical Post counts back to March 2006. Full-archive Post counts requires [Self-serve](/x-api/getting-started/about-x-api) or [Enterprise](/x-api/getting-started/about-x-api) access. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with Self-serve or Enterprise access * Your App's Bearer Token *** ## Get full-archive Post counts Use the same query syntax as full-archive search. For example, to count Posts from @XDevelopers: ``` from:XDevelopers ``` Specify `start_time` and `end_time` to search specific historical periods: | Parameter | Format | Example | | :----------- | :------- | :--------------------- | | `start_time` | ISO 8601 | `2020-01-01T00:00:00Z` | | `end_time` | ISO 8601 | `2020-12-31T23:59:59Z` | ```bash cURL theme={null} curl "https://api.x.com/2/tweets/counts/all?\ query=from%3AXDevelopers&\ start_time=2020-01-01T00%3A00%3A00Z&\ end_time=2020-12-31T23%3A59%3A59Z&\ granularity=day" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get full-archive Post counts response = client.posts.count_all( query="from:XDevelopers", start_time="2020-01-01T00:00:00Z", end_time="2020-12-31T23:59:59Z", granularity="day" ) for bucket in response.data: print(f"{bucket.start}: {bucket.tweet_count} Posts") print(f"Total: {response.meta.total_tweet_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get full-archive Post counts const response = await client.posts.countAll({ query: "from:XDevelopers", startTime: "2020-01-01T00:00:00Z", endTime: "2020-12-31T23:59:59Z", granularity: "day", }); response.data?.forEach((bucket) => { console.log(`${bucket.start}: ${bucket.tweet_count} Posts`); }); console.log(`Total: ${response.meta?.total_tweet_count}`); ``` ```json theme={null} { "data": [ { "end": "2020-01-02T00:00:00.000Z", "start": "2020-01-01T00:00:00.000Z", "tweet_count": 3 }, { "end": "2020-01-03T00:00:00.000Z", "start": "2020-01-02T00:00:00.000Z", "tweet_count": 5 } ], "meta": { "total_tweet_count": 8 } } ``` *** ## Granularity options Control how counts are grouped: | Granularity | Description | | :---------- | :------------------------ | | `minute` | Counts per minute | | `hour` | Counts per hour (default) | | `day` | Counts per day | *** ## Paginate through results For large time ranges, use the `next_token` from the response: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/counts/all?\ query=from%3AXDevelopers&\ start_time=2015-01-01T00%3A00%3A00Z&\ end_time=2020-12-31T23%3A59%3A59Z&\ granularity=day&\ next_token=abc123" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get counts with pagination next_token = None while True: response = client.posts.count_all( query="from:XDevelopers", start_time="2015-01-01T00:00:00Z", end_time="2020-12-31T23:59:59Z", granularity="day", next_token=next_token ) for bucket in response.data: print(f"{bucket.start}: {bucket.tweet_count}") next_token = response.meta.next_token if not next_token: break ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get counts with pagination let nextToken = undefined; do { const response = await client.posts.countAll({ query: "from:XDevelopers", startTime: "2015-01-01T00:00:00Z", endTime: "2020-12-31T23:59:59Z", granularity: "day", nextToken, }); response.data?.forEach((bucket) => { console.log(`${bucket.start}: ${bucket.tweet_count}`); }); nextToken = response.meta?.next_token; } while (nextToken); ``` *** ## Key differences from recent counts | Feature | Recent Counts | Full-Archive Counts | | :----------------- | :------------- | :---------------------- | | Time range | Last 7 days | March 2006 to now | | Access required | All developers | Pay-per-use, Enterprise | | Default time range | Last 7 days | Last 30 days | *** ## Common parameters | Parameter | Description | Default | | :------------ | :-------------------------- | :---------- | | `query` | Search query (required) | — | | `granularity` | Time bucket size | `hour` | | `start_time` | Oldest timestamp (ISO 8601) | 30 days ago | | `end_time` | Newest timestamp (ISO 8601) | Now | | `next_token` | Pagination token | — | *** ## Next steps Get recent Post counts Master query syntax Full endpoint documentation # Recent Post Counts Source: https://docs.x.com/x-api/posts/counts/quickstart/recent-tweet-counts Get Post volume for the last 7 days This guide walks you through getting Post counts (volume) for the last 7 days. **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 *** ## Get recent Post counts Use the same query syntax as recent search. For example, to count Posts from @XDevelopers: ``` from:XDevelopers ``` ```bash cURL theme={null} curl "https://api.x.com/2/tweets/counts/recent?\ query=from%3AXDevelopers&\ granularity=day" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get recent Post counts response = client.posts.count_recent( query="from:XDevelopers", granularity="day" ) for bucket in response.data: print(f"{bucket.start}: {bucket.tweet_count} Posts") print(f"Total: {response.meta.total_tweet_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get recent Post counts const response = await client.posts.countRecent({ query: "from:XDevelopers", granularity: "day", }); response.data?.forEach((bucket) => { console.log(`${bucket.start}: ${bucket.tweet_count} Posts`); }); console.log(`Total: ${response.meta?.total_tweet_count}`); ``` ```json theme={null} { "data": [ { "end": "2024-01-16T00:00:00.000Z", "start": "2024-01-15T00:00:00.000Z", "tweet_count": 5 }, { "end": "2024-01-17T00:00:00.000Z", "start": "2024-01-16T00:00:00.000Z", "tweet_count": 3 }, { "end": "2024-01-18T00:00:00.000Z", "start": "2024-01-17T00:00:00.000Z", "tweet_count": 8 } ], "meta": { "total_tweet_count": 16 } } ``` *** ## Granularity options Control how counts are grouped: | Granularity | Description | | :---------- | :------------------------ | | `minute` | Counts per minute | | `hour` | Counts per hour (default) | | `day` | Counts per day | ```bash cURL theme={null} # Get hourly counts curl "https://api.x.com/2/tweets/counts/recent?\ query=python%20lang%3Aen&\ granularity=hour" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get hourly counts response = client.posts.count_recent( query="python lang:en", granularity="hour" ) for bucket in response.data: print(f"{bucket.start}: {bucket.tweet_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get hourly counts const response = await client.posts.countRecent({ query: "python lang:en", granularity: "hour", }); response.data?.forEach((bucket) => { console.log(`${bucket.start}: ${bucket.tweet_count}`); }); ``` *** ## Filter by time range Limit counts to a specific time period: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/counts/recent?\ query=from%3AXDevelopers&\ start_time=2024-01-10T00%3A00%3A00Z&\ end_time=2024-01-15T00%3A00%3A00Z&\ granularity=day" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get counts for a specific time range response = client.posts.count_recent( query="from:XDevelopers", start_time="2024-01-10T00:00:00Z", end_time="2024-01-15T00:00:00Z", granularity="day" ) print(f"Total Posts: {response.meta.total_tweet_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get counts for a specific time range const response = await client.posts.countRecent({ query: "from:XDevelopers", startTime: "2024-01-10T00:00:00Z", endTime: "2024-01-15T00:00:00Z", granularity: "day", }); console.log(`Total Posts: ${response.meta?.total_tweet_count}`); ``` *** ## Common parameters | Parameter | Description | Default | | :------------ | :-------------------------- | :--------- | | `query` | Search query (required) | — | | `granularity` | Time bucket size | `hour` | | `start_time` | Oldest timestamp (ISO 8601) | 7 days ago | | `end_time` | Newest timestamp (ISO 8601) | Now | *** ## Next steps Get historical Post counts Master query syntax Full endpoint documentation # Build a rule Source: https://docs.x.com/x-api/posts/filtered-stream/integrate/build-a-rule Learn how to build filtered stream rules using operators The filtered stream endpoints deliver Posts that match a set of rules applied to the stream. Rules are made up of operators that match on a variety of Post attributes. Multiple rules can be applied using the [POST /tweets/search/stream/rules](/x-api/stream/update-stream-rules) endpoint. Once you've added rules and connected using [GET /tweets/search/stream](/x-api/stream/get-stream-rules), only Posts matching your rules will be delivered. You do not need to disconnect to add or remove rules. *** ## Rule limitations Limits on the number of rules depend on your [access level](/x-api/getting-started/about-x-api). See the [filtered stream introduction](/x-api/posts/filtered-stream/introduction) for specific limits. *** ## Operator types: standalone and conjunction-required **Standalone operators** can be used alone or together with any other operators (including those that require conjunction). For example, this rule works because `#hashtag` is a standalone operator: ``` #xapiv2 ``` **Conjunction-required operators** cannot be used by themselves in a rule; they can only be used when at least one standalone operator is included. This is because using these operators alone would match an extremely high volume of Posts. For example, the following rules are **not supported** since they contain only conjunction-required operators: ``` has:media ``` ``` has:links OR is:retweet ``` If we add a standalone operator, such as the phrase `"X data"`, the rule works properly: ``` "X data" has:mentions (has:media OR has:links) ``` *** ## Boolean operators and grouping String together multiple operators using these tools: | Operator | Description | Example | | :------------------------- | :------------------------------------ | :---------------------------------------------------------------------- | | **AND** (space) | Posts must match both conditions | `snow day #NoSchool` matches Posts with "snow" AND "day" AND #NoSchool | | **OR** | Posts must match either condition | `grumpy OR cat OR #meme` matches Posts with "grumpy" OR "cat" OR #meme | | **NOT** (dash) | Exclude Posts matching this condition | `cat #meme -grumpy` matches Posts with "cat" and #meme but NOT "grumpy" | | **Grouping** (parentheses) | Group operators together | `(grumpy cat) OR (#meme has:images)` matches either group | **A note on negations** * All operators can be negated except for `sample:` * The operator `-is:nullcast` must always be negated * Negated operators cannot be used alone * Do not negate grouped operators. Instead of `skiing -(snow OR day OR noschool)`, use `skiing -snow -day -noschool` *** ## Order of operations When combining AND and OR: 1. Operators connected by AND logic are combined first 2. Then, operators connected with OR logic are applied **Examples:** | Query | Evaluated as | | :----------------------- | :------------------------- | | `apple OR iphone ipad` | `apple OR (iphone ipad)` | | `ipad iphone OR android` | `(iphone ipad) OR android` | To eliminate uncertainty, use parentheses: ``` (apple OR iphone) ipad ``` ``` iphone (ipad OR android) ``` *** ## Punctuation, diacritics, and case sensitivity **Diacritics:** Filtered stream rules with accents only match Posts that also include the accent. For example, `diacrítica` matches *diacrítica* but **not** *diacritica*. **Case sensitivity:** All operators are case-insensitive. The rule `cat` matches *cat*, *CAT*, and *Cat*. **Search Posts behaves differently** When [building search queries](/x-api/posts/search/integrate/build-a-query), keywords with accents match Posts both with and without the accents. For example, `Diacrítica` matches both *Diacrítica* and *Diacritica*. *** ## Quote Tweet matching When using filtered stream, operators match on both the Quote Tweet's content **and** the content from the original Post that was quoted. [Search Posts](/x-api/posts/search/introduction) behaves differently—it only matches on the Quote Tweet's content, not the original Post. *** ## Specificity and efficiency Using broad operators like a single keyword or hashtag is not recommended—it will match a massive volume of Posts and quickly consume your connection. **Tips for building effective rules:** 1. **Start specific, then broaden** — Create targeted rules that return relevant results 2. **Use multiple operators** — Combine operators to narrow results 3. **Watch your character count** — The entire rule string counts toward the limit **Example progression:** ``` # Too broad - 200,000+ Posts per day happy # Better - adds language filter and exclusions (happy OR happiness) lang:en -birthday -is:retweet # Even better - 59 characters, more specific (happy OR happiness) place_country:GB -birthday -is:retweet ``` *** ## Iteratively building a rule ### Step 1: Start with a basic rule ``` happy OR happiness ``` ### Step 2: Test and narrow based on results We noticed Posts in many languages. Add a language filter: ``` (happy OR happiness) lang:en ``` We're getting birthday wishes. Exclude them and Retweets: ``` (happy OR happiness) lang:en -birthday -is:retweet ``` ### Step 3: Broaden for better coverage We want to capture more sentiment. Add related keywords: ``` (happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet ``` ### Step 4: Adjust for trends Holiday Posts are appearing. Exclude them: ``` (happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet -holidays ``` *** ## Adding and removing rules Use [POST /2/tweets/search/stream/rules](/x-api/stream/update-stream-rules) to add or remove rules. ### Adding rules Submit an `add` JSON body with the `value` (the rule) and optional `tag` (to identify matching Posts): ```bash theme={null} curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -d '{ "add": [ {"value": "cat has:media", "tag": "cats with media"}, {"value": "cat has:media -grumpy", "tag": "happy cats with media"}, {"value": "meme", "tag": "funny things"}, {"value": "meme has:images"} ] }' ``` ### Removing rules Submit a `delete` JSON body with the rule IDs to remove: ```bash theme={null} curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -d '{ "delete": { "ids": [ "1165037377523306498", "1165037377523306499" ] } }' ``` *** ## Rule examples ### Tracking a natural disaster Match Posts from weather agencies about Hurricane Harvey: ```json theme={null} { "value": "-is:retweet has:geo (from:NWSNHC OR from:NHC_Atlantic OR from:NWSHouston OR from:NWSSanAntonio OR from:USGS_TexasRain OR from:USGS_TexasFlood OR from:JeffLindner1)", "tag": "Hurricane Harvey - weather agencies with geo" } ``` ### Sentiment analysis for #nowplaying **Positive sentiment:** ```json theme={null} { "value": "#nowplaying (happy OR exciting OR excited OR favorite OR fav OR amazing OR lovely OR incredible) (place_country:US OR place_country:MX OR place_country:CA) -horrible -worst -sucks -bad -disappointing", "tag": "#nowplaying positive" } ``` **Negative sentiment:** ```json theme={null} { "value": "#nowplaying (horrible OR worst OR sucks OR bad OR disappointing) (place_country:US OR place_country:MX OR place_country:CA) -happy -exciting -excited -favorite -fav -amazing -lovely -incredible", "tag": "#nowplaying negative" } ``` ### Using Post annotations Find Japanese Posts about pets (not cats) with images using the `context:` operator: First, use [Post lookup](/x-api/posts/lookup/introduction) with `tweet.fields=context_annotations` to identify domain.entity IDs: * Cats: `domain` 66, `entity` 852262932607926273 * Pets: `domain` 65, `entity` 852262932607926273 ```json theme={null} { "value": "context:65.852262932607926273 -context:66.852262932607926273 -is:retweet has:images lang:ja", "tag": "Japanese pets with images - no cats" } ``` *** ## Next steps Complete list of available operators Connect to your stream Code examples in multiple languages # Matching Posts to Rules Source: https://docs.x.com/x-api/posts/filtered-stream/integrate/matching-returned-tweets ### Matching returned Posts to their associated rule The filtered stream endpoint gives you the ability to have multiple rules in place through a single connection. Before you start receiving Posts in your stream, you’ll need to create a rule which specifies what types of Posts you’re interested in.  When you specify your rules to match Posts based on a wide variety of attributes, including user attributes, geo-location, language, and many others, you can attach a tag to distinguish this rule at a higher level. Tags are a good way to contextualize your rule, especially if you have many rules in place. The filtering rules determine which Post activities will be sent through the connection. If you need to add a new filtering rule to capture a different type of Post, or remove an existing rule, your app can send a request to the [POST tweets/search/stream/rules](/x-api/stream/update-stream-rules) endpoint to make it happen. When that request is sent, the filtering rules are automatically modified and the changes simply take effect in the data stream with no need to reconnect. Most rule additions take effect  about 20 seconds or less. It’s unlikely, but depending on external factors (for example, network connectivity), it may take longer before you start receiving matching Posts. If you can’t find a rule in the list rule endpoint, make sure that the rule creation request succeeded; this can be done by checking your logs for any error messages. #### Matching rules When an activity is delivered through your filtered stream connection, in a matching\_rules array, it contains which list of filters matched against the Post delivered. In the Post payload there is additional string metadata which includes the rule id and tag that caused a specific Post to be delivered. If multiple rules match a single Post, the activity is delivered a single time with each of the matching rules included in this metadata. The matching rules provide an easy way to associate a specific Post with specific rules, which is especially helpful if you have many distinct rules. Since the data is delivered through a single stream in this manner, ensuring you have unique id and tag is essential for matching.  **Here is an example of how the ”matching\_rules” array appears in the Post payload:**   ```"matching_rules": [ theme={null} { "id": "1166916266197536768", "tag": "test-rule-tag-00000" }, { "id": "1166916266197536769", "tag": "test-rule-tag-12345" } ] ``` #### Rule tags At the time they are created, each filtering rule may be created with a tag. Rule tags have no special meaning as they are simply treated as opaque strings carried along with a rule. They will be included in the matching\_rules metadata in activities returned, and are aimed at making distinguishing your rules easier at a higher level.  Tags provide an easy way to create logical groupings of filtering rules. For example, you may generate a unique ID for a specific rule as its tag, and allow your app to reference that ID within activities it processes to associate a result with specific customers, campaigns, categories, or other related groups. Note that tags cannot be updated on an existing rule and can only be included when a rule is created. In order to “update” or “rename” a tag, you need to first delete the rule, then add it again with the desired tag. The best solution is to simply use a unique identifier as your tag, which your system can associate with various other data points within your own app, all without having to change anything in the rule set. ### Filtered stream - Recovery and redundancy features Note:These recovery and redundancy features are only available to those that have Enterprise access. # Filtered Stream Operators Source: https://docs.x.com/x-api/posts/filtered-stream/integrate/operators Complete list of operators for Filtered Stream rules 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 # Filtered Stream Source: https://docs.x.com/x-api/posts/filtered-stream/introduction Stream near real-time Posts matching your filter rules 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 Add and remove rules without disconnecting Match on keywords, hashtags, users, and more Optionally receive Posts via webhooks *** ## How it works 1. **Create rules** — Define filter rules using operators 2. **Connect to stream** — Establish a persistent HTTP connection 3. **Receive Posts** — Get matching Posts in near real-time ```mermaid actions={false} theme={null} flowchart LR A["Create/manage
rules"] --> B["Connect to
streaming endpoint"] --> C["Receive
matching Posts"] ``` *** ## Endpoints | Method | Endpoint | Description | | :----- | :------------------------------------------------------------------- | :-------------------- | | GET | [`/2/tweets/search/stream`](/x-api/stream/stream-filtered-posts) | Connect to the stream | | POST | [`/2/tweets/search/stream/rules`](/x-api/stream/update-stream-rules) | Add or delete rules | | GET | [`/2/tweets/search/stream/rules`](/x-api/stream/get-stream-rules) | List current rules | *** ## Access levels | Feature | Pay-per-use | Enterprise | | :---------------- | :---------- | :---------- | | Rules per project | 1,000 | 25,000+ | | Rule length | 1,024 chars | 2,048 chars | | Connections | 1 | Multiple | | All operators | ✓ | ✓ | Get higher limits and additional features *** ## Building rules Rules use the same operators as search queries: ``` (AI OR "machine learning") lang:en -is:retweet ``` ### Example rules | Rule | Matches | | :--------------------------------- | :--------------------------- | | `#python` | Posts with #python hashtag | | `from:elonmusk` | Posts by @elonmusk | | `"breaking news" has:images` | Posts with phrase and images | | `(@XDevelopers OR @X) -is:retweet` | Mentions, excluding retweets | Learn rule syntax and operators *** ## Connecting to the stream Establish a persistent HTTP connection to receive Posts: ```python theme={null} import requests def stream_posts(bearer_token): url = "https://api.x.com/2/tweets/search/stream" headers = {"Authorization": f"Bearer {bearer_token}"} response = requests.get(url, headers=headers, stream=True) for line in response.iter_lines(): if line: print(line.decode("utf-8")) ``` ### Keep-alive signals The stream sends blank lines (`\r\n`) every 20 seconds to maintain the connection. If you don't receive data or a keep-alive for 20 seconds, reconnect. Reconnect gracefully Process Posts efficiently *** ## Webhook delivery Instead of maintaining a persistent connection, you can receive Posts via webhooks: Set up webhook delivery for filtered stream *** ## Post edits The stream delivers edited Posts with their edit history. Each edit creates a new Post ID: ```json theme={null} { "data": { "id": "1234567893", "text": "Hello world! (edited)", "edit_history_tweet_ids": ["1234567890", "1234567891", "1234567893"] } } ``` Learn more about Post edits *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [Bearer Token](/resources/fundamentals/authentication) Connect to the stream in minutes Learn rule syntax All available operators Working code examples *** ## Advanced topics Reconnect gracefully Handle high throughput Build resilient applications Identify which rules matched # Quickstart Source: https://docs.x.com/x-api/posts/filtered-stream/quickstart Connect to the filtered stream and receive near real-time Posts This guide walks you through connecting to the filtered stream to receive near real-time Posts matching your filter rules. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * Your App's Bearer Token (found in the Developer Console under "Keys and tokens") *** Rules define which Posts to receive. Use operators to match on keywords, hashtags, users, and more. **Example rule:** Match Posts containing "cat" with images: ``` cat has:images ``` Learn rule syntax and operators Add your rule using the rules endpoint. Include a `tag` to identify which rule matched each Post: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Authorization: Bearer $BEARER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "add": [ {"value": "cat has:images", "tag": "cats with images"} ] }' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Add a rule to the filtered stream response = client.filtered_stream.add_rules( add=[{"value": "cat has:images", "tag": "cats with images"}] ) for rule in response.data: print(f"Rule added: {rule.id} - {rule.value}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Add a rule to the filtered stream const response = await client.filteredStream.addRules({ add: [{ value: "cat has:images", tag: "cats with images" }], }); response.data?.forEach((rule) => { console.log(`Rule added: ${rule.id} - ${rule.value}`); }); ``` **Response:** ```json theme={null} { "data": [ { "id": "1273026480692322304", "value": "cat has:images", "tag": "cats with images" } ], "meta": { "sent": "2024-01-15T10:30:00.000Z", "summary": { "created": 1, "not_created": 0, "valid": 1, "invalid": 0 } } } ``` List all active rules to confirm your rule was added: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/search/stream/rules" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get all active rules response = client.filtered_stream.get_rules() for rule in response.data: print(f"Active rule: {rule.id} - {rule.value}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get all active rules const response = await client.filteredStream.getRules(); response.data?.forEach((rule) => { console.log(`Active rule: ${rule.id} - ${rule.value}`); }); ``` Open a persistent connection to receive matching Posts: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/search/stream?\ tweet.fields=created_at,author_id&\ expansions=author_id&\ user.fields=username" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Connect to the filtered stream for post in client.filtered_stream.stream( tweet_fields=["created_at", "author_id"], expansions=["author_id"], user_fields=["username"] ): print(f"New post: {post.data.text}") print(f"Matching rules: {post.matching_rules}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Connect to the filtered stream const stream = client.filteredStream.stream({ tweetFields: ["created_at", "author_id"], expansions: ["author_id"], userFields: ["username"], }); for await (const post of stream) { console.log(`New post: ${post.data?.text}`); console.log(`Matching rules: ${post.matching_rules}`); } ``` Matching Posts stream as JSON objects: ```json theme={null} { "data": { "id": "1234567890", "text": "Look at this cute cat! 🐱", "author_id": "9876543210", "created_at": "2024-01-15T10:35:00.000Z", "edit_history_tweet_ids": ["1234567890"] }, "includes": { "users": [ { "id": "9876543210", "username": "catperson" } ] }, "matching_rules": [ { "id": "1273026480692322304", "tag": "cats with images" } ] } ``` The `matching_rules` array shows which rules matched the Post, using the tags you defined. Remove rules by their ID: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Authorization: Bearer $BEARER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "delete": { "ids": ["1273026480692322304"] } }' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Delete rules by ID response = client.filtered_stream.delete_rules( delete={"ids": ["1273026480692322304"]} ) print(f"Deleted: {response.meta.summary.deleted}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Delete rules by ID const response = await client.filteredStream.deleteRules({ delete: { ids: ["1273026480692322304"] }, }); console.log(`Deleted: ${response.meta?.summary?.deleted}`); ``` *** ## Managing your connection The stream sends blank lines (`\r\n`) every 20 seconds. If you don't receive data or a keep-alive for 20 seconds, reconnect. Press `Ctrl+C` to close the connection, or close your terminal window. Only one connection per App is allowed. Opening a new connection will close any existing one. *** ## Next steps Learn rule syntax All available operators Reconnect gracefully Full endpoint documentation # Get count of all Posts Source: https://docs.x.com/x-api/posts/get-count-of-all-posts get /2/tweets/counts/all Retrieves the count of Posts matching a search query from the full archive. # Get count of recent Posts Source: https://docs.x.com/x-api/posts/get-count-of-recent-posts get /2/tweets/counts/recent Retrieves the count of Posts from the last 7 days matching a search query. # Search all Posts Source: https://docs.x.com/x-api/posts/search-all-posts get /2/tweets/search/all Retrieves Posts from the full archive matching a search query. # Search recent Posts Source: https://docs.x.com/x-api/posts/search-recent-posts get /2/tweets/search/recent Retrieves Posts from the last 7 days matching a search query. # Build a query Source: https://docs.x.com/x-api/posts/search/integrate/build-a-query Learn how to build search queries using operators The search endpoints accept a single query with a GET request and return a set of historical Posts that match the query. Queries are made up of operators that match on a variety of Post attributes. *** ## Query limitations Your queries will be limited depending on which [access level](/x-api/getting-started/about-x-api) you are using: | Access level | Recent search | Full-archive search | | :----------- | :--------------- | :------------------ | | Self-serve | 512 characters | 1,024 characters | | Enterprise | 4,096 characters | 4,096 characters | *** ## Operator availability While most operators are available to any developer, some are reserved for certain access levels: * **Core operators:** Available when using any [Project](/resources/fundamentals/developer-apps) * **Advanced operators:** Available when using a Project with certain access levels See the complete [list of operators](/x-api/posts/search/integrate/operators) for availability details. *** ## Operator types: standalone and conjunction-required **Standalone operators** can be used alone or together with any other operators (including those that require conjunction). For example, this query works because `#hashtag` is a standalone operator: ``` #xapiv2 ``` **Conjunction-required operators** cannot be used by themselves in a query; they can only be used when at least one standalone operator is included. This is because using these operators alone would match an extremely high volume of Posts. For example, the following queries are **not supported** since they contain only conjunction-required operators: ``` has:media ``` ``` has:links OR is:retweet ``` If we add a standalone operator, such as the phrase `"X data"`, the query works properly: ``` "X data" has:mentions (has:media OR has:links) ``` *** ## Boolean operators and grouping String together multiple operators using these tools: | Operator | Description | Example | | :------------------------- | :------------------------------------ | :---------------------------------------------------------------------- | | **AND** (space) | Posts must match both conditions | `snow day #NoSchool` matches Posts with "snow" AND "day" AND #NoSchool | | **OR** | Posts must match either condition | `grumpy OR cat OR #meme` matches Posts with "grumpy" OR "cat" OR #meme | | **NOT** (dash) | Exclude Posts matching this condition | `cat #meme -grumpy` matches Posts with "cat" and #meme but NOT "grumpy" | | **Grouping** (parentheses) | Group operators together | `(grumpy cat) OR (#meme has:images)` matches either group | **A note on negations** * The operator `-is:nullcast` must always be negated * Negated operators cannot be used alone * Do not negate grouped operators. Instead of `skiing -(snow OR day OR noschool)`, use `skiing -snow -day -noschool` *** ## Order of operations When combining AND and OR: 1. Operators connected by AND logic are combined first 2. Then, operators connected with OR logic are applied **Examples:** | Query | Evaluated as | | :----------------------- | :------------------------- | | `apple OR iphone ipad` | `apple OR (iphone ipad)` | | `ipad iphone OR android` | `(iphone ipad) OR android` | To eliminate uncertainty, use parentheses: ``` (apple OR iphone) ipad ``` ``` iphone (ipad OR android) ``` *** ## Punctuation, diacritics, and case sensitivity **Diacritics:** Search queries with accents or diacritics match Posts both with and without the accents. For example, `Diacrítica` matches both *Diacrítica* and *Diacritica*. **Case sensitivity:** All operators are case-insensitive. The query `cat` matches *cat*, *CAT*, and *Cat*. **Filtered stream behaves differently** When [building filtered stream rules](/x-api/posts/filtered-stream/integrate/build-a-rule), keywords with accents only match Posts that also include the accent. For example, `Diacrítica` only matches *Diacrítica*, not *Diacritica*. *** ## Quote Tweet matching When using Search Posts, operators match on the Quote Tweet's content but **not** on the content from the original Post that was quoted. [Filtered stream](/x-api/posts/filtered-stream/introduction) behaves differently—it matches on both the Quote Tweet and the original Post's content. *** ## Specificity and efficiency Using broad operators like a single keyword or hashtag is not recommended—it will match a massive volume of Posts and quickly consume your usage limits. **Tips for building effective queries:** 1. **Start specific, then broaden** — Create targeted queries that return relevant results 2. **Use multiple operators** — Combine operators to narrow results 3. **Watch your character count** — The entire query string counts toward the limit **Example progression:** ``` # Too broad - 200,000+ Posts per day happy # Better - adds language filter and exclusions (happy OR happiness) lang:en -birthday -is:retweet # Even better - 59 characters, more specific (happy OR happiness) place_country:GB -birthday -is:retweet ``` *** ## Iteratively building a query ### Step 1: Start with a basic query ``` happy OR happiness ``` ### Step 2: Test and narrow based on results We noticed Posts in many languages. Add a language filter: ``` (happy OR happiness) lang:en ``` We're getting birthday wishes. Exclude them and Retweets: ``` (happy OR happiness) lang:en -birthday -is:retweet ``` ### Step 3: Broaden for better coverage We want to capture more sentiment. Add related keywords: ``` (happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet ``` ### Step 4: Adjust for trends Holiday Posts are appearing. Exclude them: ``` (happy OR happiness OR excited OR elated) lang:en -birthday -is:retweet -holidays ``` *** ## Adding a query to your request Use the `query` parameter and HTTP encode your query: ```bash theme={null} curl "https://api.x.com/2/tweets/search/recent?\ query=cat%20has%3Amedia%20-grumpy&\ tweet.fields=created_at&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Query examples ### Tracking a natural disaster Match Posts from weather agencies about Hurricane Harvey: **Query:** ``` has:geo (from:NWSNHC OR from:NHC_Atlantic OR from:NWSHouston OR from:NWSSanAntonio OR from:USGS_TexasRain OR from:USGS_TexasFlood OR from:JeffLindner1) -is:retweet ``` **Full request URL:** ``` https://api.x.com/2/tweets/search/recent?query=has%3Ageo%20(from%3ANWSNHC%20OR%20from%3ANHC_Atlantic%20OR%20from%3ANWSHouston%20OR%20from%3ANWSSanAntonio%20OR%20from%3AUSGS_TexasRain%20OR%20from%3AUSGS_TexasFlood%20OR%20from%3AJeffLindner1)%20-is%3Aretweet ``` ### Sentiment analysis for #nowplaying **Positive sentiment:** ``` #nowplaying (happy OR exciting OR excited OR favorite OR fav OR amazing OR lovely OR incredible) (place_country:US OR place_country:MX OR place_country:CA) -horrible -worst -sucks -bad -disappointing ``` **Negative sentiment:** ``` #nowplaying (horrible OR worst OR sucks OR bad OR disappointing) (place_country:US OR place_country:MX OR place_country:CA) -happy -exciting -excited -favorite -fav -amazing -lovely -incredible ``` ### Using Post annotations Find Japanese Posts about pets (not cats) with images using the `context:` operator: First, use [Post lookup](/x-api/posts/lookup/introduction) with `tweet.fields=context_annotations` to identify domain.entity IDs: * Cats: `domain` 66, `entity` 852262932607926273 * Pets: `domain` 65, `entity` 852262932607926273 **Query:** ``` context:65.852262932607926273 -context:66.852262932607926273 -is:retweet has:images lang:ja ``` *** ## Tools Build and test your queries interactively *** ## Next steps Complete list of available operators Make your first search request Full integration documentation # Search Operators Source: https://docs.x.com/x-api/posts/search/integrate/operators Complete list of operators for Search queries 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 # Integration Guide Source: https://docs.x.com/x-api/posts/search/integrate/overview Key concepts and best practices for integrating Search Posts This guide covers the key concepts you need to integrate the Search Posts endpoints into your application. *** ## Authentication ### Recent search Recent search supports multiple authentication methods: | Method | Use case | | :----------------------------------------------------------------------------------------------------------------------------- | :--------------- | | [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Public Post data | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Private metrics | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Private metrics | ### Full-archive search Full-archive search only supports [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) authentication. Private metrics (`non_public_metrics`, `organic_metrics`, `promoted_metrics`) are not available with full-archive search because it only supports App-Only authentication. *** ## Building queries Queries use operators to match Posts. Combine operators with boolean logic: ``` (AI OR "machine learning") lang:en -is:retweet has:links ``` ### Query length limits | Access level | Recent search | Full-archive search | | :----------- | :------------ | :------------------ | | Self-serve | 512 chars | 1,024 chars | | Enterprise | 4,096 chars | 4,096 chars | ### Operator types | Type | Description | Example | | :----------------------- | :-------------------------------------- | :------------------------ | | **Standalone** | Can be used alone | `#python`, `from:user` | | **Conjunction-required** | Must be used with a standalone operator | `has:media`, `is:retweet` | Learn query syntax in detail See all available operators *** ## Fields and expansions By default, the response includes only `id`, `text`, and `edit_history_tweet_ids`. Use parameters to request additional data. ### Example request ```bash cURL theme={null} curl "https://api.x.com/2/tweets/search/recent?\ query=python&\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id,attachments.media_keys&\ user.fields=username,verified&\ media.fields=url,type" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search recent Posts for page in client.posts.search_recent( query="python", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id", "attachments.media_keys"], user_fields=["username", "verified"], media_fields=["url", "type"], max_results=100 ): for post in page.data: print(f"{post.text} - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search recent Posts const paginator = client.posts.searchRecent("python", { tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id", "attachments.media_keys"], userFields: ["username", "verified"], mediaFields: ["url", "type"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text} - Likes: ${post.public_metrics?.like_count}`); }); } ``` ### Available expansions | Expansion | Returns | | :--------------------------- | :------------------------- | | `author_id` | Author's user object | | `attachments.media_keys` | Attached media objects | | `attachments.poll_ids` | Attached poll objects | | `referenced_tweets.id` | Quoted or replied-to Posts | | `geo.place_id` | Place objects | | `entities.mentions.username` | Mentioned user objects | Learn more about customizing responses *** ## Pagination Search endpoints return results in pages. Use the `next_token` from the response to fetch additional pages. ### How it works 1. Make your initial request with `max_results` 2. Check the `meta` object for `next_token` 3. Include `next_token` in subsequent requests 4. Repeat until no `next_token` is returned ### Example ```bash cURL theme={null} # First request curl "https://api.x.com/2/tweets/search/recent?query=python&max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" # Subsequent request with pagination token curl "https://api.x.com/2/tweets/search/recent?query=python&max_results=100&next_token=NEXT_TOKEN" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # The SDK handles pagination automatically all_posts = [] for page in client.posts.search_recent(query="python", max_results=100): if page.data: all_posts.extend(page.data) print(f"Found {len(all_posts)} posts") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); async function getAllResults(query) { const allPosts = []; // The SDK handles pagination automatically const paginator = client.posts.searchRecent(query, { maxResults: 100 }); for await (const page of paginator) { if (page.data) { allPosts.push(...page.data); } } return allPosts; } // Usage const posts = await getAllResults("python"); console.log(`Found ${posts.length} posts`); ``` Learn more about pagination *** ## Post edits Posts can be edited up to 5 times within 30 minutes. The search endpoints always return the most recent version. ### Considerations * `edit_history_tweet_ids` contains all Post IDs (oldest first) * Posts fetched after the 30-minute window represent the final version * For near-real-time use cases, recently-published Posts may still be edited Learn more about Post edits *** ## Best practices Use multiple operators to narrow results and reduce noise. Start broad, then refine based on results. Implement proper pagination for large result sets. Store results locally to avoid repeated requests. *** ## Next steps Master query syntax All available operators Handle large result sets Full endpoint documentation # Pagination Source: https://docs.x.com/x-api/posts/search/integrate/paginate ### Recent search pagination #### Introduction Search queries typically match on more Posts than can be returned in a single API response. When that happens, the data is returned in a series of 'pages'. Pagination refers to methods for requesting all of the pages in order to retrieve the entire data set. Here are fundamental recent search pagination details: * The recent search endpoints will respond to a query with at least one page, and provide a next\_token in its JSON response if additional pages are available. To receive matching Posts, this process can be repeated until no token is included in the response. * The next\_token does not expire. Multiple requests using the same next\_token value will receive the same results, regardless of when the request is made. * Posts are delivered in reverse-chronological order, in the UTC timezone. This is true within individual pages, as well as across multiple pages:  * The first Post in the first response will be the most recent one matching your query. * The last Post in the last response will be the oldest one matching your query. * The max\_results request parameter enables you to configure the number of Posts returned per response. This defaults to 10 Posts and has a maximum of 100.  * Every pagination implementation will involve parsing next\_tokens from the response payload, and including them in the 'next page' search request. See below for more details on how to construct these 'next page' requests.   The recent search endpoint was designed to support two fundamental use patterns: * **Get historical** - Requesting matching Posts from a time period of interest. These are typically one-time requests in support of historical research. Search requests can be based on start\_time and end\_time request parameters. recent search endpoint respond with Posts delivered in reverse-chronological order, starting with the most recent matching Post.  * **Polling** - Requesting matching Posts that have been posted since the last Post received. These use cases often have a near-real-time focus and are characterized by frequent requests, "listening" for new Posts of interest. The recent search endpoint provide the since\_id request parameter in support of the 'polling' pattern. To help with navigating by Post IDs, the until\_id request parameter is also available.   Next, we'll discuss the historical mode. This is the default mode of the recent search endpoint and illustrates the fundamentals of pagination. Then we'll discuss examples of polling use cases. When polling triggers pagination, there is an additional step to manage search requests.   #### Retrieving historical data This section outlines how you can retrieve Posts from a period of interest (currently limited to the last seven days) using the start\_time and end\_time request parameters. Historical requests are typically one-time requests in support of research and analysis.  Making requests for a period of data is the default mode of the recent search endpoint. If a search request does not specify a start\_time, end\_time, or since\_id request parameter, the end\_time will default to "now" (actually 30 seconds before the time of query) and the start\_time will default to seven days ago. The endpoint will respond with the first 'page' of Posts in reverse-chronological order, starting with the most recent Post. The response JSON payload will also include a next\_token if there are additional pages of data. To collect the entire set of matching Posts, regardless of the number of pages, requests are made until no next\_token is provided.  For example, here is an initial request for Posts with the keyword snow from the last week: [https://api.x.com/2/tweets/search/recent?query=snow](https://api.x.com/2/tweets/search/recent?query=snow) The response includes the most recent 10 Posts, along with these "meta" attributes in the JSON response: ``` "meta": {         "newest_id": "1204860593741553664",         "oldest_id": "1204860580630278147",         "next_token": "b26v89c19zqg8o3fobd8v73egzbdt3qao235oql",         "result_count": 10     } ``` To retrieve the next 10 Posts, this next\_token is added to the original request. The request would be: [https://api.x.com/2/tweets/search/recent?query=snow\&next\_token=b26v89c19zqg8o3fobd8v73egzbdt3qao235oql](https://api.x.com/2/tweets/search/recent?query=snow\&next_token=b26v89c19zqg8o3fobd8v73egzbdt3qao235oql) The process of looking for a next\_token and including it in a subsequent request can be repeated until all (or some number of) Posts are collected, or until a specified number of requests have been made. If data fidelity (collecting all matches of your query) is key to your use case, a simple "repeat until request.next\_token is null" design will suffice.    #### Polling and listening use cases This section outlines how you can retrieve recent Posts by polling the recent search endpoint with the since\_id request parameter.  With polling use cases, "any new Posts of interest?" queries are made on an on-going, frequent basis. Unlike historical use cases, that base requests on time, polling use cases typically base requests on Post IDs. Central to the polling use pattern is that every new Post has a [unique ID](/resources/fundamentals/x-ids) that is 'emitted' from the X platform generally in ascending order. If one Post has an ID smaller than another, it means it was posted earlier. The recent search endpoint support navigating the Post archive by Post ID. Responses from the endpoint include oldest\_id and newest\_id Post IDs. In the polling mode, requests are made with the since\_id set to the largest/newest ID received so far.  For example, say a query for new Posts about snow is made every five minutes, and the last Post we received had a Post ID of 10000. When it is time to poll, the request looks like: [https://api.x.com/2/tweets/search/recent?query=snow\&since\_id=10000](https://api.x.com/2/tweets/search/recent?query=snow\&since_id=10000) Next, let's say seven Posts were posted since our last request. Since all of these fit on a single data 'page', there is no next\_token. The response provides the Post ID of the most recent (newest) Post: ``` "meta": {         "newest_id": "12000",         "oldest_id": "10005",         "result_count": 7     } ``` To make the next polling query, this newest\_id value is used to set the next since\_id parameter: `https://api.x.com/2/tweets/search/recent?query=snow&since_id=12000` When there is more data available, and next tokens are provided, only th newest\_id value from the first page of results is needed. Each page of data will include newest\_id and oldest\_id values, but the value provided in the first page is the only one needed for the next, regularly scheduled, polling request. So, If you are implementing a polling design, or searching for Posts by ID range, pagination logic is slightly more complicated.  Now say that there are now 18 more matching Posts. The endpoint would respond with this initial response with a full data page and a next\_token for requesting the next page of data from this five minute period. It would also include the newest Post ID need for the next polling interval in five minutes.   ``` "meta": {         "newest_id": "13800",         "oldest_id": "12500",         "next_token": "fnsih9chihsnkjbvkjbsc",         "result_count": 10     } ``` To collect all the matching data for this five minute period, pass the next\_token in your next request, along with the same since\_id value as the previous request. [https://api.x.com/2/tweets/search/recent?query=snow\&since\\\_id=12000\&next\\\_token=fnsih9chihsnkjbvkjbsc](https://api.x.com/2/tweets/search/recent?query=snow\&since\\_id=12000\&next\\_token=fnsih9chihsnkjbvkjbsc) ``` "meta": {         "newest_id": "12300",         "oldest_id": "12010",         "result_count": 8     } ``` This second response provides the remaining eight Posts, and no next\_token. Note that we do not update our newest\_id value (12300), and instead base our next since\_id request on the first response's newest\_id value: [https://api.x.com/2/tweets/search/recent?query=snow\&since\_id=13800](https://api.x.com/2/tweets/search/recent?query=snow\&since_id=13800) # Search Posts Source: https://docs.x.com/x-api/posts/search/introduction Search for Posts by keyword, hashtag, user, and more with powerful query operators The Search Posts endpoints let you find Posts matching specific criteria using powerful query operators. Search for keywords, hashtags, mentions, URLs, and more. ## Overview X offers two search endpoints with different time ranges and access requirements: Search Posts from the **last 7 days**. Available to all developers. Search the **complete archive** back to 2006. Available to pay-per-use and Enterprise customers. *** ## Use cases * **Brand monitoring** — Track mentions of your brand or products * **Trend analysis** — Analyze conversations around topics or events * **Research** — Gather data for academic or market research * **Real-time listening** — Build applications that react to new Posts *** ## Endpoints | Method | Endpoint | Description | Access | | :----- | :------------------------------------------------------------ | :---------------------------- | :---------------------- | | GET | [`/2/tweets/search/recent`](/x-api/posts/search-recent-posts) | Search Posts from last 7 days | All developers | | GET | [`/2/tweets/search/all`](/x-api/posts/search-all-posts) | Search complete Post archive | Pay-per-use, Enterprise | *** ## Query operators Build queries using operators that match on Post attributes: ``` (AI OR "artificial intelligence") lang:en -is:retweet has:links ``` | Operator | Example | Description | | :------- | :------------------- | :------------------------------ | | Keyword | `python` | Match Posts containing the word | | Phrase | `"machine learning"` | Match exact phrase | | Hashtag | `#AI` | Match Posts with hashtag | | Mention | `@XDevelopers` | Match Posts mentioning user | | Operator | Example | Description | | :------------- | :--------------- | :------------------------- | | `from:` | `from:elonmusk` | Posts by a user | | `to:` | `to:XDevelopers` | Replies to a user | | `retweets_of:` | `retweets_of:X` | Retweets of a user's Posts | | Operator | Example | Description | | :------------- | :--------------- | :---------------------- | | `has:images` | `cat has:images` | Posts with images | | `has:videos` | `has:videos` | Posts with videos | | `has:links` | `has:links` | Posts with links | | `has:mentions` | `has:mentions` | Posts with mentions | | `url:` | `url:github.com` | Posts with specific URL | | Operator | Example | Description | | :------------ | :------------ | :---------------------- | | `lang:` | `lang:en` | Posts in a language | | `-is:retweet` | `-is:retweet` | Exclude retweets | | `-is:reply` | `-is:reply` | Exclude replies | | `is:verified` | `is:verified` | Posts by verified users | See all available operators and their access requirements *** ## Recent Search Search Posts from the **last 7 days**. Available to all developers. ### Features * Up to 100 Posts per request * Pagination for large result sets * All core query operators * 512-character query length (4,096 for Enterprise) Make your first recent search request Full endpoint documentation *** ## Full-Archive Search Search the **complete Post archive** dating back to March 2006. Full-archive search is available to pay-per-use and Enterprise customers. ### Features * Up to 500 Posts per request * Access to complete Post history * All query operators available * 1,024-character query length (4,096 for Enterprise) Make your first full-archive search request Full endpoint documentation *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Learn query syntax and operators Navigate through large result sets Key concepts and best practices Working code examples # Full-Archive Search Quickstart Source: https://docs.x.com/x-api/posts/search/quickstart/full-archive-search Search the complete Post archive back to 2006 This guide walks you through making your first full-archive search request to find Posts from the complete X archive, dating back to March 2006. Full-archive search requires [Self-serve](/x-api/getting-started/about-x-api) or [Enterprise](/x-api/getting-started/about-x-api) access. [Upgrade your access](https://developer.x.com/en/portal/products) to use this endpoint. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * Your App's Bearer Token (found in the Developer Console under "Keys and tokens") *** ## Step 1: Build a query Full-archive search supports all query operators. Build queries the same way as recent search: ``` from:XDevelopers lang:en ``` Full-archive search supports queries up to 1,024 characters (4,096 for Enterprise). *** ## Step 2: Set a time range By default, results return Posts from the last 30 days. Use `start_time` and `end_time` to search specific periods: | Parameter | Format | Example | | :----------- | :------- | :--------------------- | | `start_time` | ISO 8601 | `2020-01-01T00:00:00Z` | | `end_time` | ISO 8601 | `2020-12-31T23:59:59Z` | *** ## Step 3: Make a request ```bash cURL theme={null} curl "https://api.x.com/2/tweets/search/all?\ query=from%3AXDevelopers&\ start_time=2020-01-01T00%3A00%3A00Z&\ end_time=2020-12-31T23%3A59%3A59Z&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Full-archive search with pagination for page in client.posts.search_all( query="from:XDevelopers", start_time="2020-01-01T00:00:00Z", end_time="2020-12-31T23:59:59Z", max_results=100 ): for post in page.data: print(post.text) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Full-archive search with pagination const paginator = client.posts.searchAll({ query: "from:XDevelopers", startTime: "2020-01-01T00:00:00Z", endTime: "2020-12-31T23:59:59Z", maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(post.text); }); } ``` *** ## Step 4: Review the response ```json theme={null} { "data": [ { "id": "1271111223220809728", "text": "Tune in tonight and watch as @jessicagarson takes us through...", "edit_history_tweet_ids": ["1271111223220809728"] }, { "id": "1270799243071062016", "text": "As we work towards building the new Twitter API...", "edit_history_tweet_ids": ["1270799243071062016"] } ], "meta": { "newest_id": "1271111223220809728", "oldest_id": "1270799243071062016", "result_count": 2 } } ``` Posts created before the edit feature was introduced (September 2022) won't include `edit_history_tweet_ids`. *** ## Step 5: Add fields and expansions Request additional data with query parameters: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/search/all?\ query=from%3AXDevelopers&\ start_time=2020-01-01T00%3A00%3A00Z&\ end_time=2020-12-31T23%3A59%3A59Z&\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id&\ user.fields=username,description&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search with fields and expansions for page in client.posts.search_all( query="from:XDevelopers", start_time="2020-01-01T00:00:00Z", end_time="2020-12-31T23:59:59Z", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id"], user_fields=["username", "description"], max_results=100 ): for post in page.data: print(f"{post.created_at}: {post.text[:50]}...") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search with fields and expansions const paginator = client.posts.searchAll({ query: "from:XDevelopers", startTime: "2020-01-01T00:00:00Z", endTime: "2020-12-31T23:59:59Z", tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id"], userFields: ["username", "description"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); }); } ``` *** ## Step 6: Paginate through results The SDKs handle pagination automatically. For cURL, use the `next_token` from the response: ```bash theme={null} curl "https://api.x.com/2/tweets/search/all?\ query=from%3AXDevelopers&\ max_results=500&\ next_token=b26v89c19zqg8o3fo7gesq314yb9l2l4ptqy" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Learn more about navigating large result sets *** ## Key differences from recent search | Feature | Recent Search | Full-Archive Search | | :---------------------- | :--------------------- | :---------------------- | | Time range | Last 7 days | March 2006 to now | | Access required | All developers | Pay-per-use, Enterprise | | Max results per request | 100 | 500 | | Query length | 512 chars | 1,024 chars | | Rate limit | 450 / 15 min | 300 / 15 min, 1 / sec | | Authentication | App-Only, User Context | App-Only only | *** ## Common parameters | Parameter | Description | Default | | :------------- | :------------------------- | :----------- | | `query` | Search query (required) | — | | `max_results` | Posts per page (10-500) | 10 | | `start_time` | Oldest Post timestamp | 30 days ago | | `end_time` | Newest Post timestamp | Now | | `next_token` | Pagination token | — | | `tweet.fields` | Additional Post fields | `id`, `text` | | `expansions` | Related objects to include | — | *** ## Next steps Master query syntax and operators See all available operators Handle large result sets Full endpoint documentation # Recent Search Quickstart Source: https://docs.x.com/x-api/posts/search/quickstart/recent-search Make your first recent search request in minutes This guide walks you through making your first recent search request to find Posts from the last 7 days. **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 (found in the Developer Console under "Keys and tokens") *** Search queries use operators to match Posts. Start with a simple keyword: ``` python ``` Or combine multiple operators: ``` python lang:en -is:retweet ``` This matches Posts containing "python" in English, excluding retweets. See the [full operator reference](/x-api/posts/search/integrate/operators) for all available options. ```bash cURL theme={null} curl "https://api.x.com/2/tweets/search/recent?query=python%20lang%3Aen%20-is%3Aretweet" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search recent Posts for page in client.posts.search_recent( query="python lang:en -is:retweet" ): for post in page.data: print(post.text) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search recent Posts const paginator = client.posts.searchRecent({ query: "python lang:en -is:retweet", }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(post.text); }); } ``` The default response includes `id`, `text`, and `edit_history_tweet_ids`: ```json theme={null} { "data": [ { "id": "1234567890123456789", "text": "Just started learning Python and loving it!", "edit_history_tweet_ids": ["1234567890123456789"] }, { "id": "1234567890123456788", "text": "Python tip: use list comprehensions for cleaner code", "edit_history_tweet_ids": ["1234567890123456788"] } ], "meta": { "newest_id": "1234567890123456789", "oldest_id": "1234567890123456788", "result_count": 2 } } ``` Request additional data with query parameters: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/search/recent?\ query=python%20lang%3Aen%20-is%3Aretweet&\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id&\ user.fields=username,verified&\ max_results=10" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search with fields and expansions for page in client.posts.search_recent( query="python lang:en -is:retweet", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id"], user_fields=["username", "verified"], max_results=10 ): for post in page.data: print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search with fields and expansions const paginator = client.posts.searchRecent({ query: "python lang:en -is:retweet", tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id"], userFields: ["username", "verified"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); }); } ``` **Response:** ```json theme={null} { "data": [ { "id": "1234567890123456789", "text": "Just started learning Python and loving it!", "created_at": "2024-01-15T10:30:00.000Z", "author_id": "9876543210", "public_metrics": { "retweet_count": 5, "reply_count": 2, "like_count": 42, "quote_count": 1 }, "edit_history_tweet_ids": ["1234567890123456789"] } ], "includes": { "users": [ { "id": "9876543210", "username": "pythondev", "verified": false } ] }, "meta": { "newest_id": "1234567890123456789", "oldest_id": "1234567890123456789", "result_count": 1 } } ``` The SDKs handle pagination automatically. For cURL, use the `next_token` from the response: ```bash theme={null} curl "https://api.x.com/2/tweets/search/recent?\ query=python&\ max_results=100&\ next_token=b26v89c19zqg8o3fo7gesq314yb9l2l4ptqy" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` Learn more about navigating large result sets *** ## Example queries ``` from:XDevelopers ``` ``` #Python -is:retweet ``` ``` "machine learning" has:images lang:en ``` ``` @elonmusk -is:retweet -is:reply ``` ``` url:github.com lang:en ``` *** ## Next steps Master query syntax and operators See all available operators Search the complete Post archive Full endpoint documentation # Stream filtered Posts Source: https://docs.x.com/x-api/stream/stream-filtered-posts get /2/tweets/search/stream Streams Posts in real-time matching the active rule set. # Get Bookmarks by folder ID Source: https://docs.x.com/x-api/bookmarks/get-bookmarks-by-folder-id get /2/users/{id}/bookmarks/folders/{folder_id} Retrieves Posts in a specific Bookmark folder by its ID for the authenticated user. # Introduction Source: https://docs.x.com/x-api/direct-messages/blocks/introduction The manage DM blocks endpoints enable you to block or unblock a specified account on behalf of an authenticated user. For these endpoints, there are two POST methods available: * **/2/users/:id/dm/block**: Allows you to block an account * **/2/users/:id/dm/unblock**: Allows you to unblock an account ### Getting started ### Authentication Since you are making requests on behalf of a user, you must authenticate these endpoints with either [OAuth 1.0a User Context](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2), and utilize the user Access Tokens associated with the user you are making the request on behalf of. You can generate this user Access Token using the [3-legged OAuth flow](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens) (OAuth 1.0a) or using the [Authorization Code with PKCE grant flow](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) (OAuth 2.0). ### Making a request Block Once a user has authenticated with your app, you can call the Block endpoint on behalf of user as shown below: ``` curl --request POST 'https://api.x.com/2/users/:id/dm/block' --header 'Authorization: ••••••' ``` If the request is successful, you should see the JSON response as shown below: ``` { "data": { "blocked": true } } ``` **Unblock** Once a user has authenticated with your app, you can call the Unblock endpoint on behalf of user as shown below: ``` curl --request POST 'https://api.x.com/2/users/:id/dm/unblock' --header 'Authorization: ••••••' ``` If the request is successful, you should see the JSON response as shown below: ``` { "data": { "blocked": false } } ``` # Create DM conversation Source: https://docs.x.com/x-api/direct-messages/create-dm-conversation post /2/dm_conversations Initiates a new direct message conversation with specified participants. # Create DM message by conversation ID Source: https://docs.x.com/x-api/direct-messages/create-dm-message-by-conversation-id post /2/dm_conversations/{dm_conversation_id}/messages Sends a new direct message to a specific conversation by its ID. # Create DM message by participant ID Source: https://docs.x.com/x-api/direct-messages/create-dm-message-by-participant-id post /2/dm_conversations/with/{participant_id}/messages Sends a new direct message to a specific participant by their ID. # Delete DM event Source: https://docs.x.com/x-api/direct-messages/delete-dm-event delete /2/dm_events/{event_id} Deletes a specific direct message event by its ID, if owned by the authenticated user. # Get DM event by ID Source: https://docs.x.com/x-api/direct-messages/get-dm-event-by-id get /2/dm_events/{event_id} Retrieves details of a specific direct message event by its ID. # Get DM events Source: https://docs.x.com/x-api/direct-messages/get-dm-events get /2/dm_events Retrieves a list of recent direct message events across all conversations. # Get DM events for a DM conversation Source: https://docs.x.com/x-api/direct-messages/get-dm-events-for-a-dm-conversation get /2/dm_conversations/with/{participant_id}/dm_events Retrieves direct message events for a specific conversation. # Get DM events for a DM conversation Source: https://docs.x.com/x-api/direct-messages/get-dm-events-for-a-dm-conversation-1 get /2/dm_conversations/{id}/dm_events Retrieves direct message events for a specific conversation. # Integration Guide Source: https://docs.x.com/x-api/direct-messages/lookup/integrate Key concepts and best practices for integrating DM lookup endpoints This guide covers the key concepts you need to integrate the Direct Messages lookup endpoints into your application. *** ## Authentication DM endpoints require user authentication to access private conversations: | Method | Description | | :----------------------------------------------------------------------------------------------------------------------------- | :------------- | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | App-Only authentication is not supported. All Direct Messages are private. ### Required scopes (OAuth 2.0) | Scope | Required for | | :----------- | :-------------------- | | `dm.read` | Reading DM events | | `tweet.read` | Required with dm.read | | `users.read` | Required with dm.read | *** ## Conversation types Always has exactly two participants. Conversation ID format: `{smaller_user_id}-{larger_user_id}` Two or more participants. Membership can change over time. *** ## Event types | Event | Description | Key fields | | :------------------ | :----------------- | :----------------------------- | | `MessageCreate` | A message was sent | `text`, `sender_id` | | `ParticipantsJoin` | User joined group | `participant_ids`, `sender_id` | | `ParticipantsLeave` | User left group | `participant_ids` | ### Example events ```json theme={null} { "id": "1582838499983564806", "event_type": "MessageCreate", "text": "Hi everyone.", "sender_id": "944480690", "dm_conversation_id": "1578398451921985538", "created_at": "2022-10-19T20:58:00.000Z" } ``` ```json theme={null} { "id": "1582835469712138240", "event_type": "ParticipantsJoin", "participant_ids": ["944480690"], "sender_id": "17200003", "dm_conversation_id": "1578398451921985538", "created_at": "2022-10-19T20:45:58.000Z" } ``` ```json theme={null} { "id": "1582838535115067392", "event_type": "ParticipantsLeave", "participant_ids": ["944480690"], "dm_conversation_id": "1578398451921985538", "created_at": "2022-10-19T20:58:09.000Z" } ``` *** ## Fields and expansions ### Default fields | Event type | Default fields | | :--------------------- | :------------------------------------ | | MessageCreate | `id`, `event_type`, `text` | | ParticipantsJoin/Leave | `id`, `event_type`, `participant_ids` | ### Available fields | Field | Description | Events | | :------------------- | :---------------- | :------------------ | | `dm_conversation_id` | Conversation ID | All | | `created_at` | Event timestamp | All | | `sender_id` | Who sent/invited | MessageCreate, Join | | `attachments` | Media attachments | MessageCreate | | `referenced_tweets` | Shared Posts | MessageCreate | ### Available expansions | Expansion | Returns | | :----------------------- | :---------------------------- | | `sender_id` | User object for sender | | `participant_ids` | User objects for participants | | `attachments.media_keys` | Media objects | | `referenced_tweets.id` | Post objects | ### Example with expansions ```bash cURL theme={null} curl "https://api.x.com/2/dm_events?\ dm_event.fields=created_at,sender_id,attachments&\ expansions=sender_id,attachments.media_keys&\ user.fields=username,profile_image_url&\ media.fields=url,type" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get DM events with expansions for page in client.dm_events.list( dm_event_fields=["created_at", "sender_id", "attachments"], expansions=["sender_id", "attachments.media_keys"], user_fields=["username", "profile_image_url"], media_fields=["url", "type"], max_results=100 ): for event in page.data: print(f"Event: {event.event_type} - {event.text}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); const paginator = client.dmEvents.list({ dmEventFields: ["created_at", "sender_id", "attachments"], expansions: ["sender_id", "attachments.media_keys"], userFields: ["username", "profile_image_url"], mediaFields: ["url", "type"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((event) => { console.log(`Event: ${event.event_type} - ${event.text}`); }); } ``` *** ## Pagination DM events are returned in reverse chronological order (newest first): ```bash cURL theme={null} # First request curl "https://api.x.com/2/dm_events?max_results=100" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" # Subsequent request with pagination token curl "https://api.x.com/2/dm_events?max_results=100&pagination_token=NEXT_TOKEN" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # The SDK handles pagination automatically all_events = [] for page in client.dm_events.list(max_results=100): if page.data: all_events.extend(page.data) print(f"Found {len(all_events)} DM events") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); async function getAllDMEvents() { const allEvents = []; // The SDK handles pagination automatically const paginator = client.dmEvents.list({ maxResults: 100 }); for await (const page of paginator) { if (page.data) { allEvents.push(...page.data); } } return allEvents; } // Usage const events = await getAllDMEvents(); console.log(`Found ${events.length} DM events`); ``` Events from up to **30 days ago** are available. *** ## ID compatibility with v1.1 Conversation and event IDs are shared between v1.1 and v2 endpoints. This means you can: * Use v2 to retrieve events, then use v1.1 to delete specific messages * Reference conversation IDs from x.com URLs in API requests *** ## Next steps Make your first DM lookup request Send Direct Messages Full endpoint documentation Working code examples # Direct Messages Lookup Source: https://docs.x.com/x-api/direct-messages/lookup/introduction Retrieve Direct Message events and conversations The Direct Messages lookup endpoints let you retrieve DM events for the authenticated user, including messages from both one-to-one and group conversations. ## Overview Get all DM events for the user Get events from a specific conversation Get events by conversation ID Messages, joins, and leaves *** ## Endpoints | Method | Endpoint | Description | | :----- | :------------------------------------------------------------------------------------------------------------------ | :-------------------------------------- | | GET | [`/2/dm_events`](/x-api/direct-messages/get-dm-events) | Get all DM events for the user | | GET | [`/2/dm_conversations/with/:participant_id/dm_events`](/x-api/direct-messages/get-dm-events-for-a-dm-conversation) | Get events from one-to-one conversation | | GET | [`/2/dm_conversations/:dm_conversation_id/dm_events`](/x-api/direct-messages/get-dm-events-for-a-dm-conversation-1) | Get events by conversation ID | *** ## Event types | Event | Description | | :------------------ | :------------------------------------- | | `MessageCreate` | A message was sent in the conversation | | `ParticipantsJoin` | A user joined the conversation | | `ParticipantsLeave` | A user left the conversation | *** ## Data retention Events from up to **30 days ago** are available through these endpoints. *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [3-legged OAuth](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) Make your first DM lookup request Key concepts and best practices Full endpoint documentation Working code examples # Quickstart Source: https://docs.x.com/x-api/direct-messages/lookup/quickstart Retrieve Direct Message events and conversations This guide walks you through retrieving Direct Message events for the authenticated user. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Get all DM events Retrieve all DM events for the authenticated user: ```bash cURL theme={null} curl "https://api.x.com/2/dm_events?\ dm_event.fields=created_at,sender_id,text&\ max_results=100" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get all DM events with pagination for page in client.dm_events.list( dm_event_fields=["created_at", "sender_id", "text"], max_results=100 ): for event in page.data: print(f"{event.event_type}: {event.text}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get all DM events with pagination const paginator = client.dmEvents.list({ dmEventFields: ["created_at", "sender_id", "text"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((event) => { console.log(`${event.event_type}: ${event.text}`); }); } ``` ### Response ```json theme={null} { "data": [ { "id": "1234567890", "event_type": "MessageCreate", "text": "Hello! How are you?", "sender_id": "9876543210", "created_at": "2024-01-15T10:30:00.000Z" } ], "meta": { "result_count": 1, "next_token": "abc123" } } ``` *** ## Get one-to-one conversation Retrieve DM events from a specific one-to-one conversation: ```bash cURL theme={null} curl "https://api.x.com/2/dm_conversations/with/9876543210/dm_events?\ dm_event.fields=created_at,sender_id,text" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get DM events from a one-to-one conversation for page in client.dm_events.get_by_participant( participant_id="9876543210", dm_event_fields=["created_at", "sender_id", "text"] ): for event in page.data: print(f"{event.created_at}: {event.text}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get DM events from a one-to-one conversation const paginator = client.dmEvents.getByParticipant("9876543210", { dmEventFields: ["created_at", "sender_id", "text"], }); for await (const page of paginator) { page.data?.forEach((event) => { console.log(`${event.created_at}: ${event.text}`); }); } ``` Replace `9876543210` with the other participant's user ID. *** ## Get conversation by ID Retrieve DM events from a specific conversation ID: ```bash cURL theme={null} curl "https://api.x.com/2/dm_conversations/1234567890-9876543210/dm_events?\ dm_event.fields=created_at,sender_id,text" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get DM events from a conversation by ID for page in client.dm_events.get_by_conversation( dm_conversation_id="1234567890-9876543210", dm_event_fields=["created_at", "sender_id", "text"] ): for event in page.data: print(f"{event.created_at}: {event.text}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get DM events from a conversation by ID const paginator = client.dmEvents.getByConversation("1234567890-9876543210", { dmEventFields: ["created_at", "sender_id", "text"], }); for await (const page of paginator) { page.data?.forEach((event) => { console.log(`${event.created_at}: ${event.text}`); }); } ``` *** ## Filter by event type Get only specific event types: ```bash cURL theme={null} curl "https://api.x.com/2/dm_events?\ event_types=MessageCreate&\ dm_event.fields=created_at,sender_id,text" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get only MessageCreate events for page in client.dm_events.list( event_types=["MessageCreate"], dm_event_fields=["created_at", "sender_id", "text"] ): for event in page.data: print(f"{event.text}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get only MessageCreate events const paginator = client.dmEvents.list({ eventTypes: ["MessageCreate"], dmEventFields: ["created_at", "sender_id", "text"], }); for await (const page of paginator) { page.data?.forEach((event) => { console.log(event.text); }); } ``` ### Event types | Type | Description | | :------------------ | :----------------------- | | `MessageCreate` | A message was sent | | `ParticipantsJoin` | User joined conversation | | `ParticipantsLeave` | User left conversation | *** ## Include user data Expand sender information: ```bash cURL theme={null} curl "https://api.x.com/2/dm_events?\ dm_event.fields=created_at,sender_id,text&\ expansions=sender_id&\ user.fields=username,profile_image_url" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get DM events with sender info for page in client.dm_events.list( dm_event_fields=["created_at", "sender_id", "text"], expansions=["sender_id"], user_fields=["username", "profile_image_url"] ): for event in page.data: # Match sender from includes print(f"{event.sender_id}: {event.text}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get DM events with sender info const paginator = client.dmEvents.list({ dmEventFields: ["created_at", "sender_id", "text"], expansions: ["sender_id"], userFields: ["username", "profile_image_url"], }); for await (const page of paginator) { page.data?.forEach((event) => { console.log(`${event.sender_id}: ${event.text}`); }); // Sender user objects are in page.includes.users } ``` ### Response with expansion ```json theme={null} { "data": [ { "id": "1234567890", "event_type": "MessageCreate", "text": "Hello!", "sender_id": "9876543210" } ], "includes": { "users": [ { "id": "9876543210", "username": "example_user", "profile_image_url": "https://..." } ] } } ``` *** ## Common parameters | Parameter | Description | | :----------------- | :----------------------------------- | | `max_results` | Events per page (1-100, default 100) | | `pagination_token` | Token for next page | | `dm_event.fields` | Event fields to return | | `event_types` | Filter by event type | | `expansions` | Related objects to include | *** ## Next steps Send Direct Messages Key concepts and best practices Full endpoint documentation # Integration Guide Source: https://docs.x.com/x-api/direct-messages/manage/integrate Key concepts and best practices for sending Direct Messages This guide covers the key concepts you need to integrate the manage Direct Messages endpoints into your application. *** ## Authentication DM endpoints require user authentication: | Method | Description | | :----------------------------------------------------------------------------------------------------------------------------- | :------------- | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | App-Only authentication is not supported. All Direct Messages are private. ### Required scopes (OAuth 2.0) | Scope | Required for | | :----------- | :---------------------------- | | `dm.write` | Sending and deleting messages | | `dm.read` | Required with dm.write | | `tweet.read` | Required with dm scopes | | `users.read` | Required with dm scopes | *** ## Endpoints overview | Method | Endpoint | Description | | :----- | :-------------------------------------------------- | :-------------------------- | | POST | `/2/dm_conversations/with/:participant_id/messages` | Send one-to-one message | | POST | `/2/dm_conversations` | Create group conversation | | POST | `/2/dm_conversations/:dm_conversation_id/messages` | Add message to conversation | | DELETE | `/2/dm_events/:event_id` | Delete a message | *** ## Sending messages ### One-to-one message Send a message to a specific user. Creates a new conversation if one doesn't exist: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/dm_conversations/with/9876543210/messages" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Hello!"}' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Send a one-to-one DM response = client.dm_conversations.create_message( participant_id="9876543210", text="Hello!" ) print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Send a one-to-one DM const response = await client.dmConversations.createMessage({ participantId: "9876543210", text: "Hello!", }); console.log(response.data); ``` ### Group conversation Create a new group and send the first message: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/dm_conversations" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "conversation_type": "Group", "participant_ids": ["944480690", "906948460078698496"], "message": {"text": "Welcome to our group!"} }' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Create a group conversation response = client.dm_conversations.create( conversation_type="Group", participant_ids=["944480690", "906948460078698496"], message={"text": "Welcome to our group!"} ) print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Create a group conversation const response = await client.dmConversations.create({ conversationType: "Group", participantIds: ["944480690", "906948460078698496"], message: { text: "Welcome to our group!" }, }); console.log(response.data); ``` The `conversation_type` field must be set to `"Group"` (case sensitive). ### Add to existing conversation Send a message to any conversation you're part of: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/dm_conversations/1582103724607971328/messages" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Another message"}' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Add message to existing conversation response = client.dm_conversations.add_message( dm_conversation_id="1582103724607971328", text="Another message" ) print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Add message to existing conversation const response = await client.dmConversations.addMessage({ dmConversationId: "1582103724607971328", text: "Another message", }); console.log(response.data); ``` *** ## Media attachments Attach one piece of media (photo, video, or GIF) per message. Use the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked) to upload your file and get a `media_id`. ```json theme={null} { "text": "Check out this image!", "attachments": [{"media_id": "1583157113245011970"}] } ``` * The authenticated user must have uploaded the media * Media is available for 24 hours after upload * Only one attachment per message is supported *** ## Sharing Posts Include a Post in your message by adding the Post URL to the text: ```json theme={null} { "text": "Have you seen this? https://x.com/XDevelopers/status/1580559079470145536" } ``` The response will include a `referenced_tweets` field with the Post ID. *** ## Message requirements | Field | Required | Notes | | :------------ | :------- | :------------------------- | | `text` | Yes\* | Required if no attachments | | `attachments` | Yes\* | Required if no text | \*At least one of `text` or `attachments` must be provided. *** ## ID compatibility with v1.1 Conversation and event IDs are shared between v1.1 and v2 endpoints. This enables hybrid workflows: * Create messages with v2 * Delete messages with v1.1 (not yet available in v2) * Reference conversation IDs from x.com URLs *** ## Error handling | Status | Error | Solution | | :----- | :---------------- | :-------------------------------- | | 400 | Invalid request | Check request body format | | 401 | Unauthorized | Verify access token | | 403 | Forbidden | Check scopes and user permissions | | 429 | Too Many Requests | Wait and retry | ### Common issues The recipient may have DM settings that prevent messages from unknown users, or may have blocked you. Ensure the media was uploaded by the same authenticated user and is less than 24 hours old. Verify all participant IDs are valid and the users allow group DM invites. *** ## Next steps Send your first Direct Message Retrieve DM conversations Upload media for attachments Full endpoint documentation # Manage Direct Messages Source: https://docs.x.com/x-api/direct-messages/manage/introduction Send and delete Direct Messages The Manage Direct Messages endpoints let you send and delete Direct Messages on behalf of authenticated users. ## Overview Send a DM to another user Delete a DM for yourself Start a new conversation Send to group conversations *** ## Endpoints | Method | Endpoint | Description | | :----- | :-------------------------------------------------- | :------------------------------ | | POST | `/2/dm_conversations` | Create a new conversation | | POST | `/2/dm_conversations/with/:participant_id/messages` | Send to one-to-one conversation | | POST | `/2/dm_conversations/:dm_conversation_id/messages` | Send to existing conversation | | DELETE | `/2/dm_events/:id` | Delete a DM event | *** ## Example: Send a message ```bash theme={null} curl -X POST "https://api.x.com/2/dm_conversations/with/1234567890/messages" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Hello! How are you?"}' ``` ## Example response ```json theme={null} { "data": { "dm_conversation_id": "1234567890-0987654321", "dm_event_id": "1122334455667788990" } } ``` *** ## Message types You can send text messages and attach media: ```json theme={null} { "text": "Check out this photo!", "attachments": [{ "media_id": "1234567890123456789" }] } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) Send your first DM Key concepts and best practices Retrieve DM events Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/direct-messages/manage/quickstart Send Direct Messages and create group conversations This guide walks you through sending Direct Messages and creating group conversations. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 2.0 PKCE with `dm.write` and `dm.read` scopes) *** ## Send a one-to-one message You need the user ID of the person you want to message. You can get this from the [User lookup endpoint](/x-api/users/lookup/introduction). ```bash cURL theme={null} curl -X POST "https://api.x.com/2/dm_conversations/with/9876543210/messages" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Hello! This is a message from the API."}' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Send a one-to-one message response = client.dm.send_message( participant_id="9876543210", text="Hello! This is a message from the API." ) print(f"Message sent: {response.data.dm_event_id}") print(f"Conversation: {response.data.dm_conversation_id}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Send a one-to-one message const response = await client.dm.sendMessage({ participantId: "9876543210", text: "Hello! This is a message from the API.", }); console.log(`Message sent: ${response.data?.dm_event_id}`); console.log(`Conversation: ${response.data?.dm_conversation_id}`); ``` ```json theme={null} { "data": { "dm_conversation_id": "1234567890-9876543210", "dm_event_id": "1582103724607971332" } } ``` *** ## Create a group conversation Gather the user IDs of everyone you want in the group (excluding yourself). ```bash cURL theme={null} curl -X POST "https://api.x.com/2/dm_conversations" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "conversation_type": "Group", "participant_ids": ["944480690", "906948460078698496"], "message": {"text": "Welcome to our new group!"} }' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Create a group conversation response = client.dm.create_conversation( conversation_type="Group", participant_ids=["944480690", "906948460078698496"], message={"text": "Welcome to our new group!"} ) print(f"Group created: {response.data.dm_conversation_id}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Create a group conversation const response = await client.dm.createConversation({ conversationType: "Group", participantIds: ["944480690", "906948460078698496"], message: { text: "Welcome to our new group!" }, }); console.log(`Group created: ${response.data?.dm_conversation_id}`); ``` ```json theme={null} { "data": { "dm_conversation_id": "1582103724607971328", "dm_event_id": "1582103724607971332" } } ``` Save the `dm_conversation_id` to add more messages later. *** ## Add a message to an existing conversation Send a message to a conversation you're already part of: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/dm_conversations/1582103724607971328/messages" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Adding another message to the conversation."}' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Add message to existing conversation response = client.dm.send_message_to_conversation( dm_conversation_id="1582103724607971328", text="Adding another message to the conversation." ) print(f"Message sent: {response.data.dm_event_id}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Add message to existing conversation const response = await client.dm.sendMessageToConversation( "1582103724607971328", { text: "Adding another message to the conversation." } ); console.log(`Message sent: ${response.data?.dm_event_id}`); ``` *** ## Send a message with media First, upload your media using the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked). ```bash cURL theme={null} curl -X POST "https://api.x.com/2/dm_conversations/with/9876543210/messages" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "Check out this image!", "attachments": [{"media_id": "1234567890123456789"}] }' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Send message with media response = client.dm.send_message( participant_id="9876543210", text="Check out this image!", attachments=[{"media_id": "1234567890123456789"}] ) print(f"Message with media sent: {response.data.dm_event_id}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Send message with media const response = await client.dm.sendMessage({ participantId: "9876543210", text: "Check out this image!", attachments: [{ mediaId: "1234567890123456789" }], }); console.log(`Message with media sent: ${response.data?.dm_event_id}`); ``` *** ## Delete a message Delete a message you sent: ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/dm_events/1582103724607971332" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Delete a message response = client.dm.delete_message("1582103724607971332") print(f"Deleted: {response.data.deleted}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Delete a message const response = await client.dm.deleteMessage("1582103724607971332"); console.log(`Deleted: ${response.data?.deleted}`); ``` **Response:** ```json theme={null} { "data": { "deleted": true } } ``` You can only delete messages you sent, not messages from other participants. *** ## Required scopes When using OAuth 2.0 PKCE, your access token must have these scopes: | Scope | Description | | :----------- | :------------------------------------------ | | `dm.write` | Send and delete messages | | `dm.read` | Read conversations (required with dm.write) | | `tweet.read` | Required for some expansions | | `users.read` | Required for user expansions | *** ## Next steps Retrieve DM conversations Key concepts and best practices Full endpoint documentation Working code examples # Integration guide Source: https://docs.x.com/x-api/posts/bookmarks/integrate This page contains information on several tools and critical concepts that you should know as you integrate the manage Bookmarks endpoints into your system. We've broken the page into a couple of different sections: * [Helpful tools](/x-api/users/blocks/integrate#helpful-tools) * Key Concepts * [Authentication](/x-api/users/blocks/integrate#authentication) * \[Developer Console, Projects, and Apps]\(/x-api/users/blocks/integrate#Developer Console-projects-and-developer-apps) * [Rate limits](/x-api/users/blocks/integrate#rate-limits) ### Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: #### Postman Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. #### Code samples Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). #### Third-party libraries Take advantage of one of our communities' [third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. ### Key concepts #### Authentication All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. These specific endpoints require the use of [OAuth 2.0 Authorization Code Flow with PKCE](/resources/fundamentals/authentication/oauth-2-0/authorization-code), which means that you must use a set of keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are requesting on behalf of. If you want to generate a set of Access Tokens for another user, they must authorize or authenticate your App using an [Authorize URL](/resources/fundamentals/authentication#authorize-url). Please note that OAuth 2.0 can be tricky to use. If you are not familiar with this authentication method, we recommend using a [library](/tools-and-libraries) or a tool like Postman to properly authenticate your requests. #### Developer Console, Projects, and developer Apps To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have a [developer account](/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. #### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/resources/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. There is a user rate limit of 180 requests per 15 min window for the GET method. With the GET method of the Bookmarks lookup endpoint you will get back 800 of your most recent Bookmarked Posts. Additionally, there is a user rate limit of 50 requests per 15 minutes for the POST and DELETE methods. *** ### Code examples #### Get bookmarks ```bash cURL theme={null} curl "https://api.x.com/2/users/123/bookmarks?tweet.fields=created_at,public_metrics" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get user's bookmarks for page in client.bookmarks.get(user_id="123", tweet_fields=["created_at", "public_metrics"]): for post in page.data: print(f"{post.text} - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get user's bookmarks const paginator = client.bookmarks.get("123", { tweetFields: ["created_at", "public_metrics"], }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text} - Likes: ${post.public_metrics?.like_count}`); }); } ``` #### Create a bookmark ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/123/bookmarks" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1234567890"}' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Bookmark a Post response = client.bookmarks.create(user_id="123", tweet_id="1234567890") print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Bookmark a Post const response = await client.bookmarks.create("123", { tweetId: "1234567890" }); console.log(response.data); ``` # Bookmarks Source: https://docs.x.com/x-api/posts/bookmarks/introduction Save and manage bookmarked Posts for the authenticated user The Bookmarks endpoints let you view, add, and remove bookmarked Posts for the authenticated user. Bookmarks are private and only visible to the user who created them. ## Overview Get all bookmarked Posts Bookmark a Post Remove a bookmarked Post Organize bookmarks into folders *** ## Endpoints | Method | Endpoint | Description | | :----- | :------------------------------------------------------------------------------------- | :-------------------- | | GET | [`/2/users/:id/bookmarks`](/x-api/users/get-bookmarks) | Get user's bookmarks | | POST | [`/2/users/:id/bookmarks`](/x-api/users/create-bookmark) | Bookmark a Post | | DELETE | [`/2/users/:id/bookmarks/:tweet_id`](/x-api/users/delete-bookmark) | Remove a bookmark | | GET | [`/2/users/:id/bookmarks/folders`](/x-api/users/get-bookmark-folders) | Get bookmark folders | | GET | [`/2/users/:id/bookmarks/folders/:folder_id`](/x-api/users/get-bookmarks-by-folder-id) | Get Posts in a folder | *** ## Example: Get bookmarks ```bash theme={null} curl "https://api.x.com/2/users/123456789/bookmarks?\ tweet.fields=created_at,author_id,public_metrics" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ## Example: Add bookmark ```bash theme={null} curl -X POST "https://api.x.com/2/users/123456789/bookmarks" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1234567890"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) or [3-legged OAuth](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) Get a user's bookmarks Add and remove bookmarks Key concepts and best practices Full endpoint documentation # Bookmarks Lookup Source: https://docs.x.com/x-api/posts/bookmarks/quickstart/bookmarks-lookup Retrieve your bookmarked Posts This guide walks you through retrieving your bookmarked Posts using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token with `bookmark.read` scope (OAuth 2.0 PKCE) *** ## Get your bookmarks You need your authenticated user's ID. You can find it using the `/2/users/me` endpoint or from the [user lookup endpoint](/x-api/users/lookup/introduction). ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/bookmarks?\ tweet.fields=created_at,public_metrics,author_id&\ max_results=10" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get bookmarked Posts with pagination for page in client.bookmarks.get( "2244994945", tweet_fields=["created_at", "public_metrics", "author_id"], max_results=10 ): for post in page.data: print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get bookmarked Posts with pagination const paginator = client.bookmarks.get("2244994945", { tweetFields: ["created_at", "public_metrics", "author_id"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); }); } ``` ```json theme={null} { "data": [ { "id": "1501258597237342208", "text": "Have you built a project using the X API you'd like to share with the community? We'd love to hear from you!", "created_at": "2024-01-15T10:30:00.000Z", "author_id": "2244994945", "public_metrics": { "retweet_count": 15, "reply_count": 8, "like_count": 89, "quote_count": 3 } }, { "id": "1501258542258348032", "text": "This is just one way developer innovation helps make X a better place...", "created_at": "2024-01-15T09:15:00.000Z", "author_id": "2244994945", "public_metrics": { "retweet_count": 22, "reply_count": 5, "like_count": 156, "quote_count": 7 } } ], "meta": { "result_count": 2, "next_token": "7140dibdnow9c7btw4539n0vybdnx19ylpayqf16fjt4l" } } ``` *** ## Include author information Use expansions to get data about Post authors: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/bookmarks?\ tweet.fields=created_at,author_id&\ expansions=author_id&\ user.fields=username,verified" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get bookmarks with author info for page in client.bookmarks.get( "2244994945", tweet_fields=["created_at", "author_id"], expansions=["author_id"], user_fields=["username", "verified"] ): for post in page.data: print(f"{post.text[:50]}...") # Author info is in page.includes.users ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get bookmarks with author info const paginator = client.bookmarks.get("2244994945", { tweetFields: ["created_at", "author_id"], expansions: ["author_id"], userFields: ["username", "verified"], }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}...`); }); // Author info is in page.includes?.users } ``` *** ## Required scopes When using OAuth 2.0 PKCE, your access token must have these scopes: | Scope | Description | | :-------------- | :------------------------------ | | `bookmark.read` | Read bookmarks | | `tweet.read` | Read Post data | | `users.read` | Read user data (for expansions) | *** ## Next steps Add and remove bookmarks Full endpoint documentation # Manage Bookmarks Source: https://docs.x.com/x-api/posts/bookmarks/quickstart/manage-bookmarks Add and remove bookmarks using the X API This guide walks you through adding and removing bookmarks using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token with `bookmark.write` scope (OAuth 2.0 PKCE) *** ## Add a bookmark You need your authenticated user's ID. You can find it using the `/2/users/me` endpoint or the [user lookup endpoint](/x-api/users/lookup/introduction). Find the Post ID in the URL when viewing a Post: ``` https://x.com/XDevelopers/status/1460323737035677698 └── This is the Post ID ``` ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/2244994945/bookmarks" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1460323737035677698"}' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Add a bookmark response = client.bookmarks.create( user_id="2244994945", tweet_id="1460323737035677698" ) print(f"Bookmarked: {response.data.bookmarked}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Add a bookmark const response = await client.bookmarks.create("2244994945", { tweetId: "1460323737035677698", }); console.log(`Bookmarked: ${response.data?.bookmarked}`); ``` ```json theme={null} { "data": { "bookmarked": true } } ``` *** ## Remove a bookmark Delete a Post from your bookmarks: ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/users/2244994945/bookmarks/1460323737035677698" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Remove a bookmark response = client.bookmarks.delete( user_id="2244994945", tweet_id="1460323737035677698" ) print(f"Bookmarked: {response.data.bookmarked}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Remove a bookmark const response = await client.bookmarks.delete("2244994945", "1460323737035677698"); console.log(`Bookmarked: ${response.data?.bookmarked}`); ``` **Response:** ```json theme={null} { "data": { "bookmarked": false } } ``` *** ## Required scopes When using OAuth 2.0 PKCE, your access token must have these scopes: | Scope | Description | | :--------------- | :----------------------- | | `bookmark.write` | Add and remove bookmarks | | `tweet.read` | Read Post data | | `users.read` | Read user data | *** ## Next steps Get your bookmarked Posts Full endpoint documentation # Create or Edit Post Source: https://docs.x.com/x-api/posts/create-post post /2/tweets Creates a new Post for the authenticated user, or edits an existing Post when edit_options are provided. Supports paid partnership disclosure via the paid_partnership field. Quote-posting (using the `quote_tweet_id` parameter) requires an [Enterprise plan](/enterprise-api/introduction). It is not available on self-serve (pay-per-use) tiers. # Delete Post Source: https://docs.x.com/x-api/posts/delete-post delete /2/tweets/{id} Deletes a specific Post by its ID, if owned by the authenticated user. # Get Liking Users Source: https://docs.x.com/x-api/posts/get-liking-users get /2/tweets/{id}/liking_users Retrieves a list of Users who liked a specific Post by its ID. # Get Post by ID Source: https://docs.x.com/x-api/posts/get-post-by-id get /2/tweets/{id} Retrieves details of a specific Post by its ID. # Get Posts by IDs Source: https://docs.x.com/x-api/posts/get-posts-by-ids get /2/tweets Retrieves details of multiple Posts by their IDs. # Get Quoted Posts Source: https://docs.x.com/x-api/posts/get-quoted-posts get /2/tweets/{id}/quote_tweets Retrieves a list of Posts that quote a specific Post by its ID. # Get Reposted by Source: https://docs.x.com/x-api/posts/get-reposted-by get /2/tweets/{id}/retweeted_by Retrieves a list of Users who reposted a specific Post by its ID. # Get Reposts Source: https://docs.x.com/x-api/posts/get-reposts get /2/tweets/{id}/retweets Retrieves a list of Posts that repost a specific Post by its ID. # Apps Source: https://docs.x.com/x-api/posts/hide-replies/apps ## Apps using the hide replies endpoint Here's a collection of noteworthy apps built by developers who integrated the hide replies endpoint. By matching their technology with the X API, they created useful experiences to help all users enhance the public conversation. ![Clarabridge](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/hide-replies/clarabridge.jpg.twimg.1920.jpg) **Clarabridge** Clarabridge integrates hide replies so that companies can keep their X feed clean and interesting to read. Replies can be hidden manually, and agents can also use Clarabridge’s text analytics to automatically detect profanity and other language that is not in line with brand policy. [Go to website ▸](https://www.clarabridge.com/ "Go to website ▸") [Clarabridge on X](https://x.com/Clarabridge "Clarabridge on X") ![Perspective API template app](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/hide-replies/perspective-logo.png.twimg.1920.png) **Perspective API template app** Perspective is an artificial intelligence trained by Jigsaw (a unit within Alphabet) to detect toxic comments. This app detects a person's incoming replies, and automatically hides a reply based on the “score” that indicates how confident Perspective is that a comment is similar to toxic comments it’s seen in the past. This is an open source template app developed by X. We encourage you to personalize it to build tools for your users. [Use app ▸](https://quintessential-yoke.glitch.me "Use app ▸") [Jigsaw on X](https://x.com/jigsaw "Jigsaw on X") !\[Reshuffle]\(data:image/svg+xml,%3Csvg xmlns=%22[http://www.w3.org/2000/svg%22](http://www.w3.org/2000/svg%22) viewBox=%220 0 150 150%22%3E%3C/svg%3E) **Reshuffle** Reshuffle is a platform to connect business applications with flexible scripts. They built an integration script to detect and hide replies that include keywords you define. To try it out, make sure you have a valid app enabled for Hide replies, then follow the instructions in the repo. [Go to app ▸](https://github.com/reshufflehq/reshuffle "Go to app ▸") [Reshuffle on X](https://x.com/reshufflehq "Reshuffle on X") !\[Hide unwanted replies]\(data:image/svg+xml,%3Csvg xmlns=%22[http://www.w3.org/2000/svg%22](http://www.w3.org/2000/svg%22) viewBox=%220 0 396 397%22%3E%3C/svg%3E) **Hide unwanted replies** Dara Oladosu, the developer behind the popular app QuotedReplies, built an app that automatically hides replies that meet some of the criteria that he’s determined are more likely to exhibit abusive behavior, including replies that contain certain keywords he’s muted in the past. [Go to app ▸](https://hideunwantedreplies.com/ "Go to app ▸") [Dara Oladosu on X](https://x.com/dara_tobi "Dara Oladosu on X") !\[Pandaflow]\(data:image/svg+xml,%3Csvg xmlns=%22[http://www.w3.org/2000/svg%22](http://www.w3.org/2000/svg%22) viewBox=%220 0 94 94%22%3E%3C/svg%3E) **Pandaflow** Pandaflow is a no code platform to automate workflows. They integrate the endpoint so users can programmatically hide replies based on a custom flow they define. [Go to app ▸](https://pandaflow.io/ "Go to app ▸") [Pandaflow on X](https://x.com/pandaflowio "Pandaflow on X") # Manage replies by topic Source: https://docs.x.com/x-api/posts/hide-replies/integrate/manage-replies-by-topic ### Manage replies by topic Through the hide replies endpoint, you can build integrations to help people and brands keep their conversation on topic. This page shows how to manage a conversation using the hide replies and [recent search](/x-api/posts/recent-search) endpoints. Recent search has functionality to a conversation and its replies, and the Post payload returns [Post annotations](/x-api/fundamentals/post-annotations) to help you understand the context and topic of each Post, regardless of language. The app's flow will have controls to display and manage a conversation: 1. It asks the user’s permission to read their Posts and manage their replies. 2. It pulls a recent conversation from a Post URL, and checks that the conversation is from the authenticating user. 3. It will call the recent search endpoint to display each Post in the conversation. The request will include a conversation ID search query and the annotation expansion to determine if the Post is sports-related or not, according to X's interpretation of the Post. 4. It calls Hide replies to hide a reply when the user chooses to do so. It will also provide a way to undo this action in case, so that the user is always in control.  5. For longer conversations, it will provide controls to [paginate through search results](/x-api/posts/search/integrate/paginate).   #### Optimize for the user (and for usage) You can design a flow in a way that puts the user in control of any action they intend to make. Keeping this principle in mind also helps you build an integration that can optimize for Post consumption. 1. Because the authenticated user can only manage conversations they started, your flow should terminate early when that's not the case. * Make an initial Post lookup request. Terminate the flow early if the Post URL is not valid or the conversation was not started by the authenticating user. * This way, your app doesn't have to make a recent search request if the conversation cannot be moderated by the authenticated user. 2. Request [user and Post fields](/x-api/fundamentals/fields) in the same request to avoid making separate requests. This approach can also improve your app's performance. 3. Avoid making requests when needed. This app caches a reply's hidden status in the user's browser. This is useful for larger conversations, where the user may want to pick up their moderation efforts at a later stage, and it helps your app optimize requests to hide or unhide replies. # Manage replies by topic Source: https://docs.x.com/x-api/posts/hide-replies/integrate/manage-replies-in-realtime ### Manage replies in realtime With the hide replies endpoint, you can build a workflow to helps your users hide replies that have a very high-probability of being irrelevant. Useful apps often combine technologies so that they can be valuable to their users. This page shows how to hide replies by using the [Perspective API](https://www.perspectiveapi.com/). This API is an artificial intelligence trained by [Jigsaw](https://jigsaw.google.com/), a unit within Google, to detect toxic comments. The application logic will work in the following way: 1. It asks the user’s permission to read their Posts and hide or unhide their replies. 2. It uses the [Account Activity API](https://developer.x.com/en/docs/x-api/v1/accounts-and-users/subscribe-account-activity/overview) to detect incoming replies. 3. It asks the Perspective API to give a “score” (a number between 0 and 1) that indicates how confident their algorithm is that a comment is similar to toxic comments it’s seen in the past. (Perspective does not store the text sent to the service). 4. It calls hide replies if the algorithm’s score is very high. #### Strive for transparency Because Perspective is not trained on actual Posts, certain language nuances may cause this app to hide a reply that a user wants to remain unhidden. Regardless of the technology or the approach you use when designing your app, always make the best possible effort to ensure that your users understand what your app has hidden and can make changes. * The best option is to always trust the user and to give them full control over their decisions. This means your user experience should include controls to undo any action taken by your app on behalf of the user. * When using an artificial intelligence, your app should use a very high confidence threshold to detect and hide Posts. * Not everybody uses the same words, and your app should be designed to avoid any bias. Be mindful of reclaimed words and slang that may lead to false positives. * If you are training an artificial intelligence, consider adopting a model that closely reflects language often used on X. # Hide Replies Source: https://docs.x.com/x-api/posts/hide-replies/introduction Hide and unhide replies to Posts you authored The Hide Replies endpoint lets you hide or unhide replies to Posts authored by the authenticated user. Hidden replies are still accessible but require an extra click to view. ## Overview Hide a reply to your Post Unhide a previously hidden reply Moderate discussions on your Posts *** ## Endpoint | Method | Endpoint | Description | | :----- | :------------------------------------------------------ | :--------------------- | | PUT | [`/2/tweets/:tweet_id/hidden`](/x-api/posts/hide-reply) | Hide or unhide a reply | *** ## How it works Send a PUT request with `hidden: true` to hide a reply, or `hidden: false` to unhide it: ```json theme={null} { "hidden": true } ``` *** ## Example: Hide a reply ```bash theme={null} curl -X PUT "https://api.x.com/2/tweets/1234567890/hidden" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"hidden": true}' ``` ## Example response ```json theme={null} { "data": { "hidden": true } } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) Hide your first reply Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/posts/hide-replies/quickstart Hide and unhide replies to your Posts This guide walks you through hiding and unhiding replies to Posts in conversations you started. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Hide a reply Get the ID of the reply you want to hide. You can only hide replies to conversations started by the authenticated user. ``` https://x.com/user/status/1232720193182412800 └── This is the Post ID ``` ```bash cURL theme={null} curl -X PUT "https://api.x.com/2/tweets/1232720193182412800/hidden" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"hidden": true}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Hide a reply response = client.posts.hide_reply("1232720193182412800", hidden=True) print(f"Hidden: {response.data.hidden}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Hide a reply const response = await client.posts.hideReply("1232720193182412800", { hidden: true, }); console.log(`Hidden: ${response.data?.hidden}`); ``` ```json theme={null} { "data": { "hidden": true } } ``` The reply is now hidden from the main conversation view. Users can still see it by clicking "View hidden replies." *** ## Unhide a reply To make a hidden reply visible again: ```bash cURL theme={null} curl -X PUT "https://api.x.com/2/tweets/1232720193182412800/hidden" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"hidden": false}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Unhide a reply response = client.posts.hide_reply("1232720193182412800", hidden=False) print(f"Hidden: {response.data.hidden}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Unhide a reply const response = await client.posts.hideReply("1232720193182412800", { hidden: false, }); console.log(`Hidden: ${response.data?.hidden}`); ``` **Response:** ```json theme={null} { "data": { "hidden": false } } ``` *** ## Important notes * You can only hide replies to conversations **you started** * Hidden replies are still visible via "View hidden replies" * The reply author is not notified when their reply is hidden *** ## Next steps Moderate replies based on content Moderate replies as they arrive Full endpoint documentation # Hide reply Source: https://docs.x.com/x-api/posts/hide-reply put /2/tweets/{tweet_id}/hidden Hides or unhides a reply to a conversation owned by the authenticated user. # Likes Source: https://docs.x.com/x-api/posts/likes/introduction Like and unlike Posts, and retrieve like information The Likes endpoints let you like and unlike Posts, see which users liked a Post, and get Posts liked by a user. ## Overview Like a Post on behalf of a user Remove a like from a Post See who liked a Post Get Posts a user has liked *** ## Endpoints ### Likes lookup | Method | Endpoint | Description | | :----- | :------------------------------------------------------------ | :------------------------- | | GET | [`/2/tweets/:id/liking_users`](/x-api/posts/get-liking-users) | Get users who liked a Post | | GET | [`/2/users/:id/liked_tweets`](/x-api/users/get-liked-posts) | Get Posts liked by a user | ### Manage likes | Method | Endpoint | Description | | :----- | :--------------------------------------------------------- | :------------ | | POST | [`/2/users/:id/likes`](/x-api/users/like-post) | Like a Post | | DELETE | [`/2/users/:id/likes/:tweet_id`](/x-api/users/unlike-post) | Unlike a Post | *** ## Important notes The liking users endpoint returns a maximum of **100 users** per Post for all time, regardless of the actual number of likes. *** ## Example: Get liking users ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890/liking_users?\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example: Like a Post ```bash theme={null} curl -X POST "https://api.x.com/2/users/123456789/likes" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1234567890"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Get likes for a Post Like and unlike Posts Full endpoint documentation Working code examples # Likes Lookup Source: https://docs.x.com/x-api/posts/likes/quickstart/likes-lookup Get users who liked a Post and Posts liked by a user This guide walks you through retrieving likes data using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * Your App's Bearer Token (for public data) or User Access Token (for private metrics) *** ## Get users who liked a Post Retrieve the list of users who liked a specific Post: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1354143047324299264/liking_users?\ user.fields=created_at,username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get users who liked a Post with pagination for page in client.posts.get_liking_users( "1354143047324299264", user_fields=["created_at", "username", "verified"] ): for user in page.data: print(f"{user.username} - Joined: {user.created_at}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get users who liked a Post with pagination const paginator = client.posts.getLikingUsers("1354143047324299264", { userFields: ["created_at", "username", "verified"], }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Joined: ${user.created_at}`); }); } ``` ### Response ```json theme={null} { "data": [ { "created_at": "2008-12-04T18:51:57.000Z", "id": "17874544", "username": "TwitterSupport", "name": "Twitter Support", "verified": true }, { "created_at": "2007-02-20T14:35:54.000Z", "id": "783214", "username": "Twitter", "name": "Twitter", "verified": true } ], "meta": { "result_count": 2, "next_token": "7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" } } ``` *** ## Get a user's liked Posts Retrieve Posts that a specific user has liked: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/liked_tweets?\ tweet.fields=created_at,public_metrics&\ max_results=10" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a user's liked Posts with pagination for page in client.users.get_liked_tweets( "2244994945", tweet_fields=["created_at", "public_metrics"], max_results=10 ): for post in page.data: print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get a user's liked Posts with pagination const paginator = client.users.getLikedTweets("2244994945", { tweetFields: ["created_at", "public_metrics"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); }); } ``` ### Response ```json theme={null} { "data": [ { "id": "1362449997430542337", "text": "Honored to be the first developer to be featured...", "created_at": "2021-02-18T17:45:00.000Z", "public_metrics": { "retweet_count": 5, "reply_count": 2, "like_count": 42, "quote_count": 1 } } ], "meta": { "result_count": 1, "next_token": "7140dibdnow9c7btw4539n0vybdnx19ylpayqf16fjt4l" } } ``` *** ## Include additional data Use expansions to get related data like pinned Posts: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1354143047324299264/liking_users?\ user.fields=created_at&\ expansions=pinned_tweet_id&\ tweet.fields=created_at" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get liking users with pinned Post expansion for page in client.posts.get_liking_users( "1354143047324299264", user_fields=["created_at"], expansions=["pinned_tweet_id"], tweet_fields=["created_at"] ): for user in page.data: print(f"{user.username}") # Pinned Posts are in page.includes.tweets ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get liking users with pinned Post expansion const paginator = client.posts.getLikingUsers("1354143047324299264", { userFields: ["created_at"], expansions: ["pinned_tweet_id"], tweetFields: ["created_at"], }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(user.username); }); // Pinned Posts are in page.includes?.tweets } ``` *** ## Next steps Like and unlike Posts Full endpoint documentation # Manage Likes Source: https://docs.x.com/x-api/posts/likes/quickstart/manage-likes Like and unlike Posts using the X API This guide walks you through liking and unliking Posts using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Like a Post You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). Find the Post ID in the URL when viewing a Post: ``` https://x.com/XDevelopers/status/1228393702244134912 └── This is the Post ID ``` ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/123456789/likes" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1228393702244134912"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Like a Post response = client.posts.like( user_id="123456789", tweet_id="1228393702244134912" ) print(f"Liked: {response.data.liked}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Like a Post const response = await client.posts.like("123456789", { tweetId: "1228393702244134912", }); console.log(`Liked: ${response.data?.liked}`); ``` ```json theme={null} { "data": { "liked": true } } ``` *** ## Unlike a Post Remove a like from a Post: ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/users/123456789/likes/1228393702244134912" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Unlike a Post response = client.posts.unlike( user_id="123456789", tweet_id="1228393702244134912" ) print(f"Liked: {response.data.liked}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Unlike a Post const response = await client.posts.unlike("123456789", "1228393702244134912"); console.log(`Liked: ${response.data?.liked}`); ``` **Response:** ```json theme={null} { "data": { "liked": false } } ``` *** ## Next steps Get users who liked a Post Full endpoint documentation # Integration Guide Source: https://docs.x.com/x-api/posts/lookup/integrate Key concepts and best practices for integrating Post lookup This guide covers the key concepts you need to integrate the Post lookup endpoints into your application. *** ## Authentication All X API v2 endpoints require authentication. Choose the method that fits your use case: | Method | Best for | Can access private metrics? | | :----------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | :-------------------------------- | | [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Server-to-server, public data | No | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | User-facing apps | Yes (for authorized user's Posts) | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy integrations | Yes (for authorized user's Posts) | ### App-Only authentication For public Post data, use a Bearer Token: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1234567890" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a single Post by ID response = client.posts.get("1234567890") print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.posts.get("1234567890"); console.log(response.data); ``` ### User Context authentication To access private metrics, authenticate on behalf of the Post author: The following fields require User Context authentication: * `tweet.fields.non_public_metrics` * `tweet.fields.promoted_metrics` * `tweet.fields.organic_metrics` * `media.fields.non_public_metrics` * `media.fields.promoted_metrics` * `media.fields.organic_metrics` *** ## Fields and expansions The X API v2 returns minimal data by default. Use `fields` and `expansions` to request exactly what you need. ### Default response ```json theme={null} { "data": { "id": "1234567890", "text": "Hello world!", "edit_history_tweet_ids": ["1234567890"] } } ``` ### Available fields | Field | Description | | :-------------------- | :--------------------------------- | | `created_at` | Post creation timestamp | | `author_id` | Author's user ID | | `public_metrics` | Like, retweet, reply, quote counts | | `entities` | Hashtags, mentions, URLs, cashtags | | `attachments` | Media keys, poll IDs | | `conversation_id` | Thread identifier | | `context_annotations` | Topic/entity classifications | | `in_reply_to_user_id` | User being replied to | | `lang` | Detected language | | `source` | Posting client | | `possibly_sensitive` | Sensitive content flag | | `reply_settings` | Who can reply | | Field | Description | | :------------------ | :------------------------ | | `username` | @handle | | `name` | Display name | | `profile_image_url` | Avatar URL | | `verified` | Verification status | | `description` | Bio | | `public_metrics` | Follower/following counts | | `created_at` | Account creation date | | Field | Description | | :------------------ | :-------------------------- | | `url` | Media URL | | `preview_image_url` | Thumbnail URL | | `type` | photo, video, animated\_gif | | `duration_ms` | Video duration | | `height`, `width` | Dimensions | | `alt_text` | Accessibility text | ### Example with fields ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1234567890?\ tweet.fields=created_at,public_metrics,entities&\ expansions=author_id,attachments.media_keys&\ user.fields=username,verified&\ media.fields=url,type" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a Post with additional fields and expansions response = client.posts.get( "1234567890", tweet_fields=["created_at", "public_metrics", "entities"], expansions=["author_id", "attachments.media_keys"], user_fields=["username", "verified"], media_fields=["url", "type"] ) print(response.data) print(response.includes) # Contains expanded user and media objects ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.posts.get("1234567890", { tweetFields: ["created_at", "public_metrics", "entities"], expansions: ["author_id", "attachments.media_keys"], userFields: ["username", "verified"], mediaFields: ["url", "type"], }); console.log(response.data); console.log(response.includes); // Contains expanded user and media objects ``` *** ## Post edits Posts can be edited up to 5 times within 30 minutes of creation. ### How it works * Each edit creates a new Post ID * `edit_history_tweet_ids` contains all versions (oldest first) * The endpoint always returns the most recent version ### Example response ```json theme={null} { "data": { "id": "1234567893", "text": "Hello world! (edited twice)", "edit_history_tweet_ids": [ "1234567890", "1234567891", "1234567893" ] } } ``` Posts retrieved after their 30-minute edit window represent the final version. For real-time use cases, be aware that recently-published Posts may still be edited. *** ## Error handling ### Common errors | Status | Error | Solution | | :----- | :---------------- | :-------------------------------- | | 400 | Invalid request | Check parameter formatting | | 401 | Unauthorized | Verify authentication credentials | | 403 | Forbidden | Check App permissions | | 404 | Not Found | Post deleted or doesn't exist | | 429 | Too Many Requests | Wait and retry (see rate limits) | ### Deleted or protected Posts If a Post is deleted or from a protected account you don't follow: * Single Post lookup returns `404` * Multi-Post lookup omits the Post from results with an `errors` array ```json theme={null} { "data": [ { "id": "1234567890", "text": "Available post" } ], "errors": [ { "resource_id": "1234567891", "resource_type": "tweet", "title": "Not Found Error", "detail": "Could not find tweet with id: [1234567891]." } ] } ``` *** ## Best practices Use the multi-Post endpoint to fetch up to 100 Posts at once, reducing API calls. Specify only the fields you need to minimize response size and processing time. Cache Post data locally to reduce repeated requests for the same content. For real-time apps, consider re-fetching Posts after the 30-minute edit window. *** ## Next steps Complete endpoint documentation All available objects and fields Working code examples Handle errors gracefully # Post Lookup Source: https://docs.x.com/x-api/posts/lookup/introduction Retrieve Posts by ID to get details, verify availability, and examine edit history The Post lookup endpoints allow you to retrieve one or more Posts by their IDs. Use these endpoints to get up-to-date Post details, verify Post availability, or examine edit history. ## Overview Posts are the core content on X. Each Post can contain: * Up to 280 characters of text * Attached media (images, videos, GIFs) * Polls, places, and URLs * Replies, quotes, and mentions Posts can be edited up to 5 times within 30 minutes of creation. Each edit generates a new Post ID, and the edit history is preserved. Retrieve a specific Post by ID Retrieve up to 100 Posts in one request Access the complete edit history of a Post Include author, media, polls, and more *** ## Use cases * **Display Post content** — Show Post details in your application * **Verify availability** — Check if a Post still exists or was deleted * **Compliance management** — Track Post changes for compliance events * **Analytics** — Retrieve engagement metrics for specific Posts *** ## Endpoints | Method | Endpoint | Description | | :----- | :--------------------------------------------- | :----------------------------------------- | | GET | [`/2/tweets/:id`](/x-api/posts/get-post-by-id) | Retrieve a single Post by ID | | GET | [`/2/tweets`](/x-api/posts/get-posts-by-ids) | Retrieve multiple Posts by IDs (up to 100) | *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Make your first Post lookup request Learn key concepts and best practices See full endpoint documentation Explore code examples # Quickstart Source: https://docs.x.com/x-api/posts/lookup/quickstart Make your first Post lookup request in minutes This guide walks you through making your first Post lookup request using the X API v2. **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 (found in the Developer Console under "Keys and tokens") *** Every Post has a unique ID. You can find it in the Post's URL: ``` https://x.com/XDevelopers/status/1228393702244134912 └── This is the Post ID ``` ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1228393702244134912" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a single Post by ID response = client.posts.get("1228393702244134912") print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.posts.get("1228393702244134912"); console.log(response.data); ``` The default response includes the Post's `id`, `text`, and `edit_history_tweet_ids`: ```json theme={null} { "data": { "id": "1228393702244134912", "text": "What did the developer write in their Valentine's card?\n\nwhile(true) {\n I = Love(You);\n}", "edit_history_tweet_ids": ["1228393702244134912"] } } ``` Use query parameters to get more data: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1228393702244134912?\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a Post with additional fields and expansions response = client.posts.get( "1228393702244134912", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id"], user_fields=["username", "verified"] ) print(response.data) print(response.includes) # Contains author user object ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.posts.get("1228393702244134912", { tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id"], userFields: ["username", "verified"], }); console.log(response.data); console.log(response.includes); // Contains author user object ``` **Response:** ```json theme={null} { "data": { "id": "1228393702244134912", "text": "What did the developer write in their Valentine's card?...", "created_at": "2020-02-14T19:00:55.000Z", "author_id": "2244994945", "public_metrics": { "retweet_count": 156, "reply_count": 23, "like_count": 892, "quote_count": 12 }, "edit_history_tweet_ids": ["1228393702244134912"] }, "includes": { "users": [ { "id": "2244994945", "username": "XDevelopers", "verified": true } ] } } ``` Retrieve up to 100 Posts in a single request: ```bash cURL theme={null} curl "https://api.x.com/2/tweets?\ ids=1228393702244134912,1227640996038684673,1199786642791452673&\ tweet.fields=created_at,author_id" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get multiple Posts by IDs response = client.posts.get_posts( ids=["1228393702244134912", "1227640996038684673", "1199786642791452673"], tweet_fields=["created_at", "author_id"] ) for post in response.data: print(f"{post.id}: {post.text[:50]}...") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.posts.getPosts({ ids: ["1228393702244134912", "1227640996038684673", "1199786642791452673"], tweetFields: ["created_at", "author_id"], }); response.data?.forEach((post) => { console.log(`${post.id}: ${post.text?.slice(0, 50)}...`); }); ``` *** ## Next steps Learn authentication, rate limits, and best practices Master the fields and expansions system See all available parameters Explore more examples # Integration guide Source: https://docs.x.com/x-api/posts/manage-tweets/integrate This page covers tools and key concepts for integrating the manage Posts endpoints into your system. *** ## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: ### Postman Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. ### Code samples Interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). ### Third-party libraries Take advantage of one of our communities' [third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. ## Key concepts ### Authentication All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. These specific endpoints requires the use of [OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize or authenticate your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/tools-and-libraries), use a tool like Postman, or use OAuth 2.0 to authenticate your requests. [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application's scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. To enable OAuth 2.0 in your App, you must enable it in your's App's authentication settings found in the App settings section of the Developer Console. ### Developer Console, Projects, and developer Apps To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must have a \[developer account]/resources/fundamentals/developer-portal), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. ### Post creation restrictions (self-serve) The following restrictions apply to self-serve (Pay Per Use) API customers only. Enterprise API customers are not subject to these limits. **Reply restrictions** Replies via the API are only permitted if the replying account has been explicitly summoned by the original post's author. This means one of the following must be true: * The original author @mentions the replying user/account in their post. * The original author quotes a post from the replying user/account. If neither condition is met, the reply request will be rejected. **Cashtag limit** Posts created via the API are limited to a maximum of 1 cashtag (e.g. `$TICKER`) per post. Requests that include more than 1 cashtag will be rejected. ### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](https://developer.x.com/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. There is a user rate limit of 200 requests per 15 minutes for the POST method. The DELETE method has a rate limit of 50 requests for 15 minutes. Additionally, there is a limit of [300 requests per 3 hours](https://blog.x.com/developer/en_us/topics/tools/2018/new-developer-requirements-to-protect-our-platform), including Posts created with either manage Posts or manage Retweets. ### Source labels Your App name and website URL will be shown as the [source label](https://help.x.com/en/using-twitter/how-to-tweet#source-labels) within metadata for any Posts created programmatically by your application. If you change the use case of a X App, be sure to update the use case in these settings in order to ensure you are in compliance with the [Developer Terms](https://developer.x.com/content/developer-twitter/en/developer-terms/agreement-and-policy). ### Profile settings You can only add a location to Posts if you have geo enabled in your profile settings. If you don't have geo enabled, you can still add a location parameter in your request body, but it won't get attached to your Post. The same is also true for tagging users in images. If the user you're tagging doesn't have photo-tagging enabled, their names won't show up in the list of tagged users even though the Post is successfully created. ### 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 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. *** ## Code examples ### Create a Post ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: OAuth ..." \ -H "Content-Type: application/json" \ -d '{"text": "Hello world!"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Create a Post response = client.posts.create(text="Hello world!") print(f"Created Post: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Create a Post const response = await client.posts.create({ text: "Hello world!" }); console.log(`Created Post: ${response.data?.id}`); ``` ### Create a reply ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: OAuth ..." \ -H "Content-Type: application/json" \ -d '{"text": "This is a reply!", "reply": {"in_reply_to_tweet_id": "1234567890"}}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Create a reply response = client.posts.create( text="This is a reply!", reply={"in_reply_to_tweet_id": "1234567890"} ) print(f"Created reply: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Create a reply const response = await client.posts.create({ text: "This is a reply!", reply: { inReplyToTweetId: "1234567890" }, }); console.log(`Created reply: ${response.data?.id}`); ``` ### Delete a Post ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/tweets/1234567890" \ -H "Authorization: OAuth ..." ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Delete a Post response = client.posts.delete("1234567890") print(f"Deleted: {response.data.deleted}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Delete a Post const response = await client.posts.delete("1234567890"); console.log(`Deleted: ${response.data?.deleted}`); ``` **Next steps** [API reference](/x-api/posts/manage-tweets#manage-tweets-api-reference-index) # Manage Posts Source: https://docs.x.com/x-api/posts/manage-tweets/introduction Create and delete Posts on behalf of authenticated users The Manage Posts endpoints let you create and delete Posts on behalf of authenticated users. Build applications that post content, create threads, or manage user Posts. ## Overview Publish a new Post Delete an existing Post Reply to another Post Quote another Post *** ## Endpoints | Method | Endpoint | Description | | :----- | :------------------------------------------ | :---------------- | | POST | [`/2/tweets`](/x-api/posts/create-post) | Create a new Post | | DELETE | [`/2/tweets/:id`](/x-api/posts/delete-post) | Delete a Post | *** ## Creating Posts ### Basic Post ```bash theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Hello from the API!"}' ``` **Self-serve customers:** Posts created via the API are limited to a maximum of 1 cashtag per post. ### Reply to a Post ```bash theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "This is a reply!", "reply": { "in_reply_to_tweet_id": "1234567890" } }' ``` **Self-serve customers:** Replies are only permitted if the original post's author has explicitly summoned the replying account by @mentioning them or quoting one of their posts. ### Quote a Post ```bash theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "Check this out!", "quote_tweet_id": "1234567890" }' ``` ### Post with media ```bash theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "Photo of the day", "media": { "media_ids": ["1234567890123456789"] } }' ``` Upload media first using the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked), then reference the `media_id` in your Post. ### Post with poll ```bash theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "What is your favorite color?", "poll": { "options": ["Red", "Blue", "Green", "Yellow"], "duration_minutes": 1440 } }' ``` *** ## Deleting Posts ```bash theme={null} curl -X DELETE "https://api.x.com/2/tweets/1234567890" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` You can only delete Posts authored by the authenticated user. *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) or [3-legged OAuth](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow) Create your first Post Key concepts and best practices Upload media for Posts Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/posts/manage-tweets/quickstart Create and delete Posts using the X API This guide walks you through creating and deleting Posts using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Tokens (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Create a Post The POST `/2/tweets` endpoint requires a JSON body with at least `text` or `media`: ```json theme={null} { "text": "Hello from the X API!" } ``` ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Hello from the X API!"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Create a Post response = client.posts.create(text="Hello from the X API!") print(f"Created Post: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Create a Post const response = await client.posts.create({ text: "Hello from the X API!" }); console.log(`Created Post: ${response.data?.id}`); ``` A successful response includes the new Post's `id` and `text`: ```json theme={null} { "data": { "id": "1445880548472328192", "text": "Hello from the X API!" } } ``` *** ## Advanced examples ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "This is a reply!", "reply": { "in_reply_to_tweet_id": "1234567890" } }' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Create a reply response = client.posts.create( text="This is a reply!", reply={"in_reply_to_tweet_id": "1234567890"} ) print(f"Created reply: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Create a reply const response = await client.posts.create({ text: "This is a reply!", reply: { inReplyToTweetId: "1234567890" }, }); console.log(`Created reply: ${response.data?.id}`); ``` ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "Check this out!", "quote_tweet_id": "1234567890" }' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Quote a Post response = client.posts.create( text="Check this out!", quote_tweet_id="1234567890" ) print(f"Created quote: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Quote a Post const response = await client.posts.create({ text: "Check this out!", quoteTweetId: "1234567890", }); console.log(`Created quote: ${response.data?.id}`); ``` First, upload media using the [Media Upload endpoint](/x-api/media/quickstart/media-upload-chunked), then reference the `media_id`: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "Photo of the day!", "media": { "media_ids": ["1234567890123456789"] } }' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Post with media response = client.posts.create( text="Photo of the day!", media={"media_ids": ["1234567890123456789"]} ) print(f"Created Post with media: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Post with media const response = await client.posts.create({ text: "Photo of the day!", media: { mediaIds: ["1234567890123456789"] }, }); console.log(`Created Post with media: ${response.data?.id}`); ``` ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "What is your favorite color?", "poll": { "options": ["Red", "Blue", "Green", "Yellow"], "duration_minutes": 1440 } }' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Post with poll response = client.posts.create( text="What is your favorite color?", poll={"options": ["Red", "Blue", "Green", "Yellow"], "duration_minutes": 1440} ) print(f"Created poll: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Post with poll const response = await client.posts.create({ text: "What is your favorite color?", poll: { options: ["Red", "Blue", "Green", "Yellow"], durationMinutes: 1440 }, }); console.log(`Created poll: ${response.data?.id}`); ``` *** ## Delete a Post You need the ID of the Post you want to delete. This is returned when you create a Post. ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/tweets/1445880548472328192" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Delete a Post response = client.posts.delete("1445880548472328192") print(f"Deleted: {response.data.deleted}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Delete a Post const response = await client.posts.delete("1445880548472328192"); console.log(`Deleted: ${response.data?.deleted}`); ``` ```json theme={null} { "data": { "deleted": true } } ``` You can only delete Posts that you authored. *** ## Next steps Key concepts and best practices Upload media for Posts Full endpoint documentation Working code examples # Quote Posts Source: https://docs.x.com/x-api/posts/quote-tweets/introduction Retrieve Quote Posts for a specific Post The Quote Posts endpoint lets you retrieve Posts that quote a specific Post. See how users are commenting on and sharing content. ## Overview Get all Quote Posts for a Post See how content is being discussed *** ## Endpoint | Method | Endpoint | Description | | :----- | :------------------------------------------------------------ | :------------------------- | | GET | [`/2/tweets/:id/quote_tweets`](/x-api/posts/get-quoted-posts) | Get Quote Posts for a Post | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890/quote_tweets?\ tweet.fields=created_at,author_id,public_metrics&\ expansions=author_id&\ user.fields=username" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example response ```json theme={null} { "data": [ { "id": "9876543210", "text": "Great point! This is exactly what we need.", "author_id": "1111111111", "created_at": "2024-01-15T10:30:00.000Z" } ], "includes": { "users": [ { "id": "1111111111", "username": "example_user" } ] }, "meta": { "result_count": 1 } } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Get Quote Posts for a Post Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/posts/quote-tweets/quickstart Retrieve Quote Posts for a specific Post This guide walks you through retrieving Quote Posts (Posts that quote another Post). **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 *** Get the ID of the Post you want to find quotes for. You can find it in the Post's URL: ``` https://x.com/XDevelopers/status/1409931481552543749 └── This is the Post ID ``` ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1409931481552543749/quote_tweets?\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id&\ user.fields=username,verified&\ max_results=10" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get Quote Posts with pagination for page in client.posts.get_quote_tweets( "1409931481552543749", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id"], user_fields=["username", "verified"], max_results=10 ): for post in page.data: print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get Quote Posts with pagination const paginator = client.posts.getQuoteTweets("1409931481552543749", { tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id"], userFields: ["username", "verified"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); }); } ``` ```json theme={null} { "data": [ { "id": "1495979553889697792", "text": "Great thread on the new API features! https://t.co/...", "author_id": "29757971", "created_at": "2022-02-22T04:31:34.000Z", "public_metrics": { "retweet_count": 5, "reply_count": 2, "like_count": 42, "quote_count": 1 }, "edit_history_tweet_ids": ["1495979553889697792"] } ], "includes": { "users": [ { "id": "29757971", "username": "developer", "verified": false } ] }, "meta": { "result_count": 1, "next_token": "avdjwk0udyx6" } } ``` The SDKs handle pagination automatically. For cURL, use the `next_token` to get more Quote Posts: ```bash theme={null} curl "https://api.x.com/2/tweets/1409931481552543749/quote_tweets?\ max_results=10&\ pagination_token=avdjwk0udyx6" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Next steps Look up Retweets Look up Posts by ID Full endpoint documentation # Integration guide Source: https://docs.x.com/x-api/posts/retweets/integrate This page covers tools and key concepts for integrating the Retweet endpoints. *** ## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: #### Postman Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. #### Code samples Interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). #### Third-party libraries Take advantage of one of our communities' [third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. ### Key concepts #### Authentication All X API v2 endpoints require for you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context or OAuth 2.0 Bearer Token to authenticate your requests to the Retweets lookup endpoint. The manage Retweets endpoints require the use of OAuth 1.0a User Context, which means that you must use a set of API keys and user access tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of access tokens for another user, they must authorize or authenticate your App using the 3-legged OAuth flow. Please note that OAuth 1.0a can be tricky to use. If you are not familiar with this authentication method, we recommend that you use a [library](/tools-and-libraries) to properly authenticate your requests. **Please note** If you are requesting the following fields, OAuth 1.0a User Context is required: * tweet.fields.non\_public\_metrics * tweet.fields.promoted\_metrics * tweet.fields.organic\_metrics * media.fields.non\_public\_metrics * media.fields.promoted\_metrics * media.fields.organic\_metrics #### Developer Console, Projects, and developer Apps To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. #### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. The manage Retweets endpoints are limited to 50 requests per 15 min (per user). Additionally, for the POST endpoint, you are limited to 300 requests per 3-hour window (per user, per app). With the Retweets lookup endpoint, you are limited to 75 requests per 15-min window. Additionally, only the 100 most recent Retweeting users will be returned by this endpoint. #### Fields and expansions The X API v2 allows users to select exactly which data they want to return from the API using a set of tools called fields and expansions. The expansion parameter allows you to expand objects referenced in the payload. For example, this endpoint allows you to pull the following [expansions](/x-api/fundamentals/expansions): * attachments.poll\_ids * attachments.media\_keys * author\_id, entities.mentions.username * geo.place\_id * in\_reply\_to\_user\_id, * referenced\_tweets.id, * referenced\_tweets.id.author\_id The fields parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. These endpoints delivers Post objects primarily. By default, the Post object returns the id and text fields. To receive additional fields such as tweet.created\_at or tweet.entities, you will have to specifically request those using a fields parameter. Some important fields that you may want to consider using in your integration are our poll data, metrics, Post annotations, and conversation ID fields. We've added a guide on how to [use fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). *** ### Code examples #### Get users who retweeted a Post ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1234567890/retweeted_by?user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get users who retweeted a Post response = client.posts.get_retweeted_by( "1234567890", user_fields=["username", "verified"] ) for user in response.data: print(f"{user.username} - Verified: {user.verified}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get users who retweeted a Post const response = await client.posts.getRetweetedBy("1234567890", { userFields: ["username", "verified"], }); response.data?.forEach((user) => { console.log(`${user.username} - Verified: ${user.verified}`); }); ``` #### Retweet a Post ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/123/retweets" \ -H "Authorization: OAuth ..." \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1234567890"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Retweet a Post response = client.posts.retweet(user_id="123", tweet_id="1234567890") print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Retweet a Post const response = await client.posts.retweet("123", { tweetId: "1234567890" }); console.log(response.data); ``` # Retweets Source: https://docs.x.com/x-api/posts/retweets/introduction Retweet and undo retweets, and retrieve retweet information The Retweets endpoints let you retweet and undo retweets, see who retweeted a Post, and get reposts of your own Posts. ## Overview Retweet a Post on behalf of a user Remove a retweet See who retweeted a Post Get reposts of your own Posts *** ## Endpoints ### Retweets lookup | Method | Endpoint | Description | | :----- | :------------------------------------------------------------ | :---------------------------------------- | | GET | [`/2/tweets/:id/retweeted_by`](/x-api/posts/get-reposted-by) | Get users who retweeted a Post | | GET | [`/2/tweets/:id/quote_tweets`](/x-api/posts/get-quoted-posts) | Get quote Posts of a Post | | GET | [`/2/users/reposts_of_me`](/x-api/users/get-reposts-of-me) | Get reposts of authenticated user's Posts | ### Manage retweets | Method | Endpoint | Description | | :----- | :-------------------------------------------------------------- | :------------- | | POST | [`/2/users/:id/retweets`](/x-api/users/repost-post) | Retweet a Post | | DELETE | [`/2/users/:id/retweets/:tweet_id`](/x-api/users/unrepost-post) | Undo a retweet | *** ## Example: Get retweeting users ```bash theme={null} curl "https://api.x.com/2/tweets/1234567890/retweeted_by?\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example: Retweet a Post ```bash theme={null} curl -X POST "https://api.x.com/2/users/123456789/retweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1234567890"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Get retweets for a Post Retweet and undo retweets Get reposts of your Posts Full endpoint documentation # Manage Retweets Source: https://docs.x.com/x-api/posts/retweets/quickstart/manage-retweets Retweet and undo Retweets using the X API This guide walks you through Retweeting and undoing Retweets using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Retweet a Post You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). Find the Post ID in the URL when viewing a Post: ``` https://x.com/XDevelopers/status/1228393702244134912 └── This is the Post ID ``` ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/123456789/retweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tweet_id": "1228393702244134912"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Retweet a Post response = client.posts.retweet( user_id="123456789", tweet_id="1228393702244134912" ) print(f"Retweeted: {response.data.retweeted}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Retweet a Post const response = await client.posts.retweet("123456789", { tweetId: "1228393702244134912", }); console.log(`Retweeted: ${response.data?.retweeted}`); ``` ```json theme={null} { "data": { "retweeted": true } } ``` *** ## Undo a Retweet Remove a Retweet: ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/users/123456789/retweets/1228393702244134912" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Undo a Retweet response = client.posts.unretweet( user_id="123456789", tweet_id="1228393702244134912" ) print(f"Retweeted: {response.data.retweeted}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Undo a Retweet const response = await client.posts.unretweet("123456789", "1228393702244134912"); console.log(`Retweeted: ${response.data?.retweeted}`); ``` **Response:** ```json theme={null} { "data": { "retweeted": false } } ``` *** ## Next steps Get users who Retweeted a Post Get Quote Posts Full endpoint documentation # Retweets Lookup Source: https://docs.x.com/x-api/posts/retweets/quickstart/retweets-lookup Get users who Retweeted a Post This guide walks you through retrieving users who Retweeted a specific Post. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * Your App's Bearer Token (for public data) or User Access Token (for private metrics) *** ## Get users who Retweeted a Post Get the ID of the Post you want to look up Retweets for: ``` https://x.com/XDevelopers/status/1354143047324299264 └── This is the Post ID ``` ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1354143047324299264/retweeted_by?\ user.fields=created_at,username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get users who Retweeted a Post with pagination for page in client.posts.get_retweeted_by( "1354143047324299264", user_fields=["created_at", "username", "verified"] ): for user in page.data: print(f"{user.username} - Joined: {user.created_at}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get users who Retweeted a Post with pagination const paginator = client.posts.getRetweetedBy("1354143047324299264", { userFields: ["created_at", "username", "verified"], }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Joined: ${user.created_at}`); }); } ``` ```json theme={null} { "data": [ { "created_at": "2008-12-04T18:51:57.000Z", "id": "17874544", "username": "TwitterSupport", "name": "Twitter Support", "verified": true }, { "created_at": "2007-02-20T14:35:54.000Z", "id": "783214", "username": "Twitter", "name": "Twitter", "verified": true } ], "meta": { "result_count": 2, "next_token": "7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" } } ``` *** ## Include additional data Use expansions to get related data like pinned Posts: ```bash cURL theme={null} curl "https://api.x.com/2/tweets/1354143047324299264/retweeted_by?\ user.fields=created_at&\ expansions=pinned_tweet_id&\ tweet.fields=created_at" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get Retweeting users with expansions for page in client.posts.get_retweeted_by( "1354143047324299264", user_fields=["created_at"], expansions=["pinned_tweet_id"], tweet_fields=["created_at"] ): for user in page.data: print(f"{user.username}") # Pinned Posts are in page.includes.tweets ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get Retweeting users with expansions const paginator = client.posts.getRetweetedBy("1354143047324299264", { userFields: ["created_at"], expansions: ["pinned_tweet_id"], tweetFields: ["created_at"], }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(user.username); }); // Pinned Posts are in page.includes?.tweets } ``` ### Response with expansion ```json theme={null} { "data": [ { "pinned_tweet_id": "1389270063807598594", "created_at": "2018-11-21T14:24:58.000Z", "id": "1065249714214457345", "username": "TwitterSpaces", "name": "Spaces" } ], "includes": { "tweets": [ { "created_at": "2021-05-03T17:26:09.000Z", "id": "1389270063807598594", "text": "now, everyone with 600 or more followers can host a Space..." } ] } } ``` *** ## Next steps Retweet and undo Retweets Get Quote Posts Full endpoint documentation # Retweets of Me Source: https://docs.x.com/x-api/posts/retweets/quickstart/retweets-of-me Get your Posts that have been Retweeted This guide walks you through retrieving your Posts that have been Retweeted by others. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Why use Retweets of Me? The Retweets of Me endpoint helps you: * **Track engagement** — See which of your Posts are being shared * **Understand resonance** — Learn what content resonates with your audience * **Inform strategy** — Adjust your content strategy based on sharing patterns *** ## Get your Retweeted Posts ```bash cURL theme={null} curl "https://api.x.com/2/users/reposts_of_me?\ tweet.fields=created_at,public_metrics&\ max_results=10" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get your Posts that have been Retweeted for page in client.posts.get_reposts_of_me( tweet_fields=["created_at", "public_metrics"], max_results=10 ): for post in page.data: print(f"{post.text[:50]}... - Retweets: {post.public_metrics.retweet_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get your Posts that have been Retweeted const paginator = client.posts.getRepostsOfMe({ tweetFields: ["created_at", "public_metrics"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}... - Retweets: ${post.public_metrics?.retweet_count}`); }); } ``` *** ## Response ```json theme={null} { "data": [ { "id": "1848781937210802364", "text": "ever wanted to discover trends.. before they're trends?...", "created_at": "2024-01-15T10:30:00.000Z", "public_metrics": { "retweet_count": 42, "reply_count": 5, "like_count": 156, "quote_count": 8 }, "edit_history_tweet_ids": ["1848781937210802364"] }, { "id": "1847990559081648620", "text": "posting is just journaling with an audience", "created_at": "2024-01-14T15:20:00.000Z", "public_metrics": { "retweet_count": 28, "reply_count": 12, "like_count": 89, "quote_count": 3 }, "edit_history_tweet_ids": ["1847990559081648620"] } ], "meta": { "result_count": 2, "next_token": "7140dibdnow9c7btw481s8m561gat797rboud5r80xvzm" } } ``` *** ## Filter by time range Get Retweeted Posts from a specific period: ```bash cURL theme={null} curl "https://api.x.com/2/users/reposts_of_me?\ start_time=2024-01-01T00%3A00%3A00Z&\ end_time=2024-01-31T23%3A59%3A59Z&\ tweet.fields=created_at,public_metrics" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get Retweeted Posts from a time range for page in client.posts.get_reposts_of_me( start_time="2024-01-01T00:00:00Z", end_time="2024-01-31T23:59:59Z", tweet_fields=["created_at", "public_metrics"] ): for post in page.data: print(f"{post.created_at}: {post.text[:50]}...") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get Retweeted Posts from a time range const paginator = client.posts.getRepostsOfMe({ startTime: "2024-01-01T00:00:00Z", endTime: "2024-01-31T23:59:59Z", tweetFields: ["created_at", "public_metrics"], }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); }); } ``` *** ## Common parameters | Parameter | Description | Default | | :----------------- | :------------------------------- | :----------- | | `max_results` | Results per page (1-100) | 10 | | `start_time` | Oldest Post timestamp (ISO 8601) | — | | `end_time` | Newest Post timestamp (ISO 8601) | — | | `pagination_token` | Token for next page | — | | `tweet.fields` | Additional Post fields | `id`, `text` | *** ## Next steps See who Retweeted a Post Retweet and undo Retweets Full endpoint documentation # Integration Guide Source: https://docs.x.com/x-api/posts/timelines/integrate Key concepts and best practices for integrating Timelines endpoints This guide covers the key concepts you need to integrate the Timelines endpoints into your application. *** ## Authentication ### Endpoint requirements | Endpoint | App-Only | User Context | | :--------------------- | :------- | :----------- | | User Posts timeline | ✓ | ✓ | | User mentions timeline | ✓ | ✓ | | Home timeline | — | ✓ (required) | ### Private metrics To access private metrics, you must authenticate on behalf of the Post author: These fields require User Context authentication: * `tweet.fields.non_public_metrics` * `tweet.fields.promoted_metrics` * `tweet.fields.organic_metrics` * `media.fields.non_public_metrics` * `media.fields.promoted_metrics` * `media.fields.organic_metrics` *** ## Fields and expansions By default, responses include only `id`, `text`, and `edit_history_tweet_ids`. Request additional data: ### Example request ```bash cURL theme={null} curl "https://api.x.com/2/users/123/tweets?\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id,attachments.media_keys&\ user.fields=username,verified&\ media.fields=url,type" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get user's Posts timeline for page in client.posts.get_user_posts( user_id="123", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id", "attachments.media_keys"], user_fields=["username", "verified"], media_fields=["url", "type"], max_results=100 ): for post in page.data: print(f"{post.text} - {post.public_metrics}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get user's Posts timeline with pagination const paginator = client.posts.getUserPosts("123", { tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id", "attachments.media_keys"], userFields: ["username", "verified"], mediaFields: ["url", "type"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text} - ${JSON.stringify(post.public_metrics)}`); }); } ``` ### Key fields | Field | Description | | :-------------------- | :----------------------- | | `created_at` | Post creation timestamp | | `public_metrics` | Engagement counts | | `conversation_id` | Thread identifier | | `context_annotations` | Topic classifications | | `entities` | Hashtags, mentions, URLs | Learn more about customizing responses *** ## Pagination Timelines return up to 100 Posts per request. Use pagination for larger result sets. ### How it works 1. Make initial request with `max_results` 2. Get `next_token` from the `meta` object 3. Include `pagination_token` in next request 4. Repeat until no `next_token` is returned ### Example ```bash cURL theme={null} # First request curl "https://api.x.com/2/users/123/tweets?max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" # Subsequent request with pagination token curl "https://api.x.com/2/users/123/tweets?max_results=100&pagination_token=NEXT_TOKEN" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # The SDK handles pagination automatically all_posts = [] for page in client.posts.get_user_posts(user_id="123", max_results=100): if page.data: all_posts.extend(page.data) print(f"Found {len(all_posts)} posts") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); async function getAllTimelinePosts(userId) { const allPosts = []; // The SDK handles pagination automatically with async iteration const paginator = client.posts.getUserPosts(userId, { maxResults: 100 }); for await (const page of paginator) { if (page.data) { allPosts.push(...page.data); } } return allPosts; } // Usage const posts = await getAllTimelinePosts("123"); console.log(`Found ${posts.length} posts`); ``` Learn more about pagination *** ## Filtering results ### Time-based filtering | Parameter | Description | | :----------- | :------------------------------- | | `start_time` | Oldest Post timestamp (ISO 8601) | | `end_time` | Newest Post timestamp (ISO 8601) | | `since_id` | Return Posts after this ID | | `until_id` | Return Posts before this ID | ### Exclude parameter Remove specific Post types from results: ```bash cURL theme={null} curl "https://api.x.com/2/users/123/tweets?exclude=retweets,replies" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Exclude retweets and replies for page in client.posts.get_user_posts( user_id="123", exclude=["retweets", "replies"] ): for post in page.data: print(post.text) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Exclude retweets and replies const paginator = client.posts.getUserPosts("123", { exclude: ["retweets", "replies"], }); for await (const page of paginator) { page.data?.forEach((post) => console.log(post.text)); } ``` | Value | Effect | | :--------- | :--------------- | | `retweets` | Exclude retweets | | `replies` | Exclude replies | *** ## Volume limits Each timeline has maximum retrieval limits: | Endpoint | Maximum Posts | | :--------------------------- | :---------------- | | User Posts timeline | 3,200 most recent | | User Posts (exclude=replies) | 800 most recent | | User mentions timeline | 800 most recent | | Home timeline | 3,200 or 7 days | Requesting Posts beyond these limits returns a successful response with no data. *** ## Post edits Posts can be edited up to 5 times within 30 minutes. Timeline endpoints always return the most recent version. ### Considerations * Posts older than 30 minutes represent their final version * Near-real-time use cases should account for potential edits * Use Post lookup to verify final state if needed Learn more about Post edits *** ## Post metrics ### Public metrics Available for all Posts with App-Only or User Context authentication: ```json theme={null} { "public_metrics": { "retweet_count": 156, "reply_count": 23, "like_count": 892, "quote_count": 12, "bookmark_count": 34, "impression_count": 15200 } } ``` ### Private metrics Requires User Context authentication from the Post author: * Only available for Posts from the last 30 days * Only returned for Posts authored by the authenticated user * Returns error for other users' Posts *** ## Edge cases When requesting non-public metrics for Posts older than 30 days, you may receive a `next_token` with `result_count: 0`. To avoid this: * Keep requests within the last 30 days * Use `max_results` of at least 10 Requesting promoted metrics for Posts that weren't promoted returns an empty response. This is a known issue. For Retweets with text over 140 characters, the text field is truncated. Use the `referenced_tweets.id` expansion to get the full text. *** ## Next steps Get a user's home feed Get mentions for a user Full endpoint documentation Handle large result sets # Timelines Source: https://docs.x.com/x-api/posts/timelines/introduction Retrieve Posts from user timelines and home feeds The Timelines endpoints let you retrieve Posts from user timelines, mention feeds, and home feeds. Get a user's posted content, see what Posts mention them, or view their personalized home timeline. ## Overview Get Posts authored by a user Get Posts mentioning a user Get personalized home feed Posts in time order *** ## Endpoints | Method | Endpoint | Description | | :----- | :-------------------------------------------------------------------------- | :-------------------------- | | GET | [`/2/users/:id/tweets`](/x-api/users/get-posts) | Get Posts by a user | | GET | [`/2/users/:id/mentions`](/x-api/users/get-mentions) | Get Posts mentioning a user | | GET | [`/2/users/:id/timelines/reverse_chronological`](/x-api/users/get-timeline) | Get home timeline | *** ## User Posts timeline Get the most recent Posts authored by a specific user. ### Features * Up to 3,200 most recent Posts * Filter out replies and retweets * Pagination support * Historical access with time-based filtering Full endpoint documentation *** ## User mentions timeline Get Posts that mention a specific user. ### Features * Up to 800 most recent mentions * Includes replies and quote Posts * Pagination support Get mentions for a user Full endpoint documentation *** ## Reverse chronological home timeline Get the authenticated user's home timeline in reverse chronological order. ### Features * Posts from followed accounts * Most recent 3,200 Posts (or 7 days) * Excludes algorithmic ranking * Requires user authentication This endpoint requires [OAuth 1.0a User Context](/resources/fundamentals/authentication) or [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2). Get a user's home timeline Full endpoint documentation *** ## Filtering options ### Exclude parameter Filter out specific Post types: | Value | Effect | | :--------- | :--------------- | | `retweets` | Exclude retweets | | `replies` | Exclude replies | ```bash theme={null} curl "https://api.x.com/2/users/123/tweets?exclude=retweets,replies" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ### Time-based filtering | Parameter | Description | | :----------- | :------------------------------- | | `start_time` | Oldest Post timestamp (ISO 8601) | | `end_time` | Newest Post timestamp (ISO 8601) | | `since_id` | Return Posts after this ID | | `until_id` | Return Posts before this ID | *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Get a user's home feed Get mentions for a user Key concepts and best practices Working code examples # Home Timeline Quickstart Source: https://docs.x.com/x-api/posts/timelines/quickstart/reverse-chron-quickstart Get a user's home timeline in reverse chronological order This guide walks you through making your first request to the reverse chronological home timeline endpoint. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Tokens (this endpoint requires user authentication) *** ## Step 1: Get the user ID You need the user ID for the account whose home timeline you want to retrieve. Find it using the username lookup endpoint: ```bash cURL theme={null} curl "https://api.x.com/2/users/by/username/XDevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") response = client.users.get_by_username("XDevelopers") print(f"User ID: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.users.getByUsername("XDevelopers"); console.log(`User ID: ${response.data?.id}`); ``` The response includes the user ID: ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "XDevelopers" } } ``` *** ## Step 2: Request the home timeline Make a GET request with the user ID and User Access Token: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/timelines/reverse_chronological" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get home timeline with pagination for page in client.posts.get_home_timeline("2244994945"): for post in page.data: print(post.text) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get home timeline with pagination const paginator = client.posts.getHomeTimeline("2244994945"); for await (const page of paginator) { page.data?.forEach((post) => { console.log(post.text); }); } ``` *** ## Step 3: Review the response ```json theme={null} { "data": [ { "id": "1524796546306478083", "text": "Today marks the launch of Devs in the Details...", "edit_history_tweet_ids": ["1524796546306478083"] }, { "id": "1524468552404668416", "text": "Join us tomorrow for a discussion about bots...", "edit_history_tweet_ids": ["1524468552404668416"] } ], "meta": { "result_count": 2, "newest_id": "1524796546306478083", "oldest_id": "1524468552404668416", "next_token": "7140dibdnow9c7btw421dyz6jism75z99gyxd8egarsc4" } } ``` *** ## Step 4: Add fields and expansions Request additional data with query parameters: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/timelines/reverse_chronological?\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id&\ user.fields=username,verified&\ max_results=10" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get home timeline with fields and expansions for page in client.posts.get_home_timeline( "2244994945", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id"], user_fields=["username", "verified"], max_results=10 ): for post in page.data: print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get home timeline with fields and expansions const paginator = client.posts.getHomeTimeline("2244994945", { tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id"], userFields: ["username", "verified"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); }); } ``` *** ## Step 5: Paginate through results The SDKs handle pagination automatically. For cURL, use the `next_token` from the response to get more results: ```bash theme={null} curl "https://api.x.com/2/users/2244994945/timelines/reverse_chronological?\ max_results=10&\ pagination_token=7140dibdnow9c7btw421dyz6jism75z99gyxd8egarsc4" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` *** ## Common parameters | Parameter | Description | Default | | :------------ | :------------------------------------- | :------ | | `max_results` | Results per page (1-100) | 10 | | `start_time` | Oldest Post timestamp (ISO 8601) | — | | `end_time` | Newest Post timestamp (ISO 8601) | — | | `since_id` | Return Posts after this ID | — | | `until_id` | Return Posts before this ID | — | | `exclude` | Exclude `retweets`, `replies`, or both | — | *** ## Next steps Get Posts mentioning a user Key concepts and best practices Full endpoint documentation Navigate large result sets # User Mentions Timeline Source: https://docs.x.com/x-api/posts/timelines/quickstart/user-mention-quickstart Get Posts that mention a specific user This guide walks you through retrieving Posts that mention a specific user. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * Your App's Bearer Token (for public data) or User Access Token (for private metrics) *** ## Get user mentions Find the user ID using the [user lookup endpoint](/x-api/users/lookup/introduction). For example, @XDevelopers has user ID `2244994945`. ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/mentions?\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id&\ user.fields=username,verified&\ max_results=10" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get mentions timeline with pagination for page in client.posts.get_user_mentions( "2244994945", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id"], user_fields=["username", "verified"], max_results=10 ): for post in page.data: print(f"@{post.author_id}: {post.text[:50]}...") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get mentions timeline with pagination const paginator = client.posts.getUserMentions("2244994945", { tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id"], userFields: ["username", "verified"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`@${post.author_id}: ${post.text?.slice(0, 50)}...`); }); } ``` ```json theme={null} { "data": [ { "id": "1301573587187331074", "text": "Hey @XDevelopers, love the new API!", "author_id": "1234567890", "created_at": "2024-01-15T10:30:00.000Z", "public_metrics": { "retweet_count": 5, "reply_count": 2, "like_count": 42, "quote_count": 1 } } ], "includes": { "users": [ { "id": "1234567890", "username": "developer", "name": "Dev Person", "verified": false } ] }, "meta": { "newest_id": "1301573587187331074", "oldest_id": "1301573587187331074", "result_count": 1, "next_token": "t3buvdr5pujq9g7bggsnf3ep2ha28" } } ``` *** ## Filter mentions ### Exclude replies Get only original Posts that mention the user: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/mentions?\ exclude=replies&\ max_results=10" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get mentions excluding replies for page in client.posts.get_user_mentions( "2244994945", exclude=["replies"], max_results=10 ): for post in page.data: print(post.text) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get mentions excluding replies const paginator = client.posts.getUserMentions("2244994945", { exclude: ["replies"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(post.text); }); } ``` ### Get mentions in a time range ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/mentions?\ start_time=2024-01-01T00%3A00%3A00Z&\ end_time=2024-01-31T23%3A59%3A59Z" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get mentions in a time range for page in client.posts.get_user_mentions( "2244994945", start_time="2024-01-01T00:00:00Z", end_time="2024-01-31T23:59:59Z" ): for post in page.data: print(f"{post.created_at}: {post.text[:50]}...") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get mentions in a time range const paginator = client.posts.getUserMentions("2244994945", { startTime: "2024-01-01T00:00:00Z", endTime: "2024-01-31T23:59:59Z", }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.created_at}: ${post.text?.slice(0, 50)}...`); }); } ``` *** ## Common parameters | Parameter | Description | Default | | :----------------- | :------------------------------------- | :------ | | `max_results` | Results per page (1-100) | 10 | | `start_time` | Oldest Post timestamp (ISO 8601) | — | | `end_time` | Newest Post timestamp (ISO 8601) | — | | `since_id` | Return Posts after this ID | — | | `until_id` | Return Posts before this ID | — | | `exclude` | Exclude `retweets`, `replies`, or both | — | | `pagination_token` | Token for next page | — | *** ## Next steps Get user's home timeline Key concepts and best practices Full endpoint documentation Navigate large result sets # Get stream rule counts Source: https://docs.x.com/x-api/stream/get-stream-rule-counts get /2/tweets/search/stream/rules/counts Retrieves the count of rules in the active rule set for the filtered stream. # Get stream rules Source: https://docs.x.com/x-api/stream/get-stream-rules get /2/tweets/search/stream/rules Retrieves the active rule set or a subset of rules for the filtered stream. # Update stream rules Source: https://docs.x.com/x-api/stream/update-stream-rules post /2/tweets/search/stream/rules Adds or deletes rules from the active rule set for the filtered stream. # Block DMs Source: https://docs.x.com/x-api/users/block-dms post /2/users/{id}/dm/block Blocks direct messages to or from a specific User by their ID for the authenticated user. # Integration Guide Source: https://docs.x.com/x-api/users/blocks/integrate Key concepts and best practices for integrating blocks endpoints This guide covers the key concepts you need to integrate the blocks endpoints into your application. The block and unblock users endpoints are only available under the Enterprise plan. You can fill out the Enterprise interest form [here](/forms/enterprise-api-interest). *** ## Authentication Blocks endpoints require user authentication: | Method | Description | | :----------------------------------------------------------------------------------------------------------------------------- | :------------------------------- | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended for new applications | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | App-Only authentication is not supported. You must authenticate on behalf of a user. ### Required scopes (OAuth 2.0) | Scope | Required for | | :------------ | :------------------------------- | | `block.read` | Retrieving blocked accounts | | `block.write` | Blocking and unblocking accounts | | `users.read` | Required with block scopes | *** ## Endpoints overview | Method | Endpoint | Description | Availability | | :----- | :-------------------------------------------------- | :--------------------------- | :------------------------------------------------------------- | | GET | `/2/users/:id/blocking` | Get list of blocked accounts |
Pay-per-useEnterprise
| | POST | `/2/users/:id/blocking` | Block an account | Enterprise | | DELETE | `/2/users/:source_user_id/blocking/:target_user_id` | Unblock an account | Enterprise | *** ## Fields and expansions ### Default response ```json theme={null} { "data": [ { "id": "1234567890", "name": "Example User", "username": "example" } ] } ``` ### Available fields | Field | Description | | :------------------ | :------------------------ | | `created_at` | Account creation date | | `description` | User bio | | `profile_image_url` | Avatar URL | | `public_metrics` | Follower/following counts | | `verified` | Verification status | | Expansion | Description | | :---------------- | :----------------- | | `pinned_tweet_id` | User's pinned Post | *** ## What happens when you block * See your Posts (unless logged out) * Follow you * Send you DMs * Add you to Lists * Tag you in photos * See their Posts * Follow them * Send them DMs When you block someone who follows you, they are automatically unfollowed. *** ## Pagination For users with large block lists, results are paginated: ```bash cURL theme={null} # First request curl "https://api.x.com/2/users/123/blocking?max_results=100" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" # Subsequent request with pagination token curl "https://api.x.com/2/users/123/blocking?max_results=100&pagination_token=NEXT_TOKEN" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client # Use OAuth 2.0 user access token client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # The SDK handles pagination automatically all_blocked = [] for page in client.users.get_blocking(user_id="123", max_results=100): if page.data: all_blocked.extend(page.data) print(f"Blocked {len(all_blocked)} users") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); async function getAllBlockedUsers(userId) { const allBlocked = []; // The SDK handles pagination automatically const paginator = client.users.getBlocking(userId, { maxResults: 100 }); for await (const page of paginator) { if (page.data) { allBlocked.push(...page.data); } } return allBlocked; } // Usage const blocked = await getAllBlockedUsers("123"); console.log(`Blocked ${blocked.length} users`); ``` *** ## Error handling | Status | Error | Solution | | :----- | :---------------- | :----------------------------------------------------------------------------------------------- | | 400 | Invalid request | Check user ID format | | 401 | Unauthorized | Verify access token | | 403 | Forbidden | Check scopes and permissions. Block/unblock require [Enterprise](/forms/enterprise-api-interest) | | 404 | Not Found | User doesn't exist | | 429 | Too Many Requests | Wait and retry | *** ## Next steps Make your first blocks request Mute users instead of blocking Full endpoint documentation Working code examples # Blocks Source: https://docs.x.com/x-api/users/blocks/introduction Block and unblock users, and retrieve blocked user lists The Blocks endpoints let you retrieve the list of users blocked by the authenticated user, as well as block and unblock users. The block and unblock users endpoints are only available under the Enterprise plan. You can fill out the Enterprise interest form [here](/forms/enterprise-api-interest). ## Overview Get your blocked user list Block a user Unblock a user *** ## Endpoints | Method | Endpoint | Description | Availability | | :----- | :--------------------------------------------------- | :---------------- | :------------------------------------------------------------- | | GET | [`/2/users/:id/blocking`](/x-api/users/get-blocking) | Get blocked users |
Pay-per-useEnterprise
| | POST | `/2/users/:id/blocking` | Block a user | Enterprise | | DELETE | `/2/users/:source_user_id/blocking/:target_user_id` | Unblock a user | Enterprise | *** ## Example: Get blocked users ```bash theme={null} curl "https://api.x.com/2/users/123456789/blocking?\ user.fields=username,description" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ## Example: Block a user (Enterprise only) ```bash theme={null} curl -X POST "https://api.x.com/2/users/123456789/blocking" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_user_id": "9876543210"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) Get started with blocks Key concepts and best practices Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/users/blocks/quickstart Block and unblock users, and retrieve your block list This guide walks you through retrieving your block list, as well as blocking and unblocking users. The block and unblock users endpoints are only available under the Enterprise plan. You can fill out the Enterprise interest form [here](/forms/enterprise-api-interest). **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Get blocked users You need your authenticated user's ID to retrieve your block list. You can get it from the `/2/users/me` endpoint or use the ID from your tokens. ```bash cURL theme={null} curl "https://api.x.com/2/users/123456789/blocking?\ user.fields=username,verified,created_at&\ max_results=100" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get blocked users with pagination for page in client.users.get_blocking( "123456789", user_fields=["username", "verified", "created_at"], max_results=100 ): for user in page.data: print(f"{user.username} - Created: {user.created_at}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get blocked users with pagination const paginator = client.users.getBlocking("123456789", { userFields: ["username", "verified", "created_at"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Created: ${user.created_at}`); }); } ``` ```json theme={null} { "data": [ { "id": "17874544", "name": "Example User", "username": "example_user", "verified": false, "created_at": "2008-12-04T18:51:57.000Z" } ], "meta": { "result_count": 1, "next_token": "abc123" } } ``` *** ## Block a user (Enterprise only) Get the user ID of the account you want to block. ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/123456789/blocking" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_user_id": "9876543210"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Block a user response = client.users.block( source_user_id="123456789", target_user_id="9876543210" ) print(f"Blocking: {response.data.blocking}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Block a user const response = await client.users.block("123456789", { targetUserId: "9876543210", }); console.log(`Blocking: ${response.data?.blocking}`); ``` ```json theme={null} { "data": { "blocking": true } } ``` *** ## Unblock a user (Enterprise only) ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/users/123456789/blocking/9876543210" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Unblock a user response = client.users.unblock( source_user_id="123456789", target_user_id="9876543210" ) print(f"Blocking: {response.data.blocking}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Unblock a user const response = await client.users.unblock("123456789", "9876543210"); console.log(`Blocking: ${response.data?.blocking}`); ``` ```json theme={null} { "data": { "blocking": false } } ``` *** ## Next steps Mute users instead of blocking Manage follows Key concepts and best practices Full endpoint documentation # Create Bookmark Source: https://docs.x.com/x-api/users/create-bookmark post /2/users/{id}/bookmarks Adds a post to the authenticated user’s bookmarks. # Delete Bookmark Source: https://docs.x.com/x-api/users/delete-bookmark delete /2/users/{id}/bookmarks/{tweet_id} Removes a Post from the authenticated user’s Bookmarks by its ID. # Follow User Source: https://docs.x.com/x-api/users/follow-user post /2/users/{id}/following Causes the authenticated user to follow a specific user by their ID. # Follows Source: https://docs.x.com/x-api/users/follows/introduction Manage follows and retrieve follower/following information The Follows endpoints let you follow and unfollow users, and retrieve follower and following lists for any user. ## Overview Follow a user on behalf of the authenticated user Unfollow a user Get a user's followers Get who a user follows *** ## Endpoints ### Follows lookup | Method | Endpoint | Description | | :----- | :----------------------------------------------------- | :--------------------- | | GET | [`/2/users/:id/followers`](/x-api/users/get-followers) | Get a user's followers | | GET | [`/2/users/:id/following`](/x-api/users/get-following) | Get who a user follows | ### Manage follows | Method | Endpoint | Description | | :----- | :--------------------------------------------------------------------------------- | :-------------- | | POST | [`/2/users/:id/following`](/x-api/users/follow-user) | Follow a user | | DELETE | [`/2/users/:source_user_id/following/:target_user_id`](/x-api/users/unfollow-user) | Unfollow a user | *** ## Example: Get followers ```bash theme={null} curl "https://api.x.com/2/users/2244994945/followers?\ user.fields=username,verified,public_metrics" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example: Follow a user ```bash theme={null} curl -X POST "https://api.x.com/2/users/123456789/following" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_user_id": "2244994945"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Get started with follows Full endpoint documentation Working code examples # Quickstart Source: https://docs.x.com/x-api/users/follows/quickstart Get followers and following lists, and manage follows This guide walks you through retrieving followers and following lists, and managing follows. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * Your App's Bearer Token (for lookups) * User Access Token (for managing follows) *** ## Get a user's followers Retrieve the list of users following a specific user: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/followers?\ user.fields=username,verified,public_metrics&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a user's followers with pagination for page in client.users.get_followers( "2244994945", user_fields=["username", "verified", "public_metrics"], max_results=100 ): for user in page.data: print(f"{user.username} - Followers: {user.public_metrics.followers_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get a user's followers with pagination const paginator = client.users.getFollowers("2244994945", { userFields: ["username", "verified", "public_metrics"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Followers: ${user.public_metrics?.followers_count}`); }); } ``` ### Response ```json theme={null} { "data": [ { "id": "1234567890", "name": "Developer", "username": "dev_user", "verified": false, "public_metrics": { "followers_count": 500, "following_count": 200, "tweet_count": 1500 } } ], "meta": { "result_count": 1, "next_token": "abc123" } } ``` *** ## Get who a user follows Retrieve the list of users that a specific user follows: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/following?\ user.fields=username,verified&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get users that a user follows for page in client.users.get_following( "2244994945", user_fields=["username", "verified"], max_results=100 ): for user in page.data: print(f"{user.username} - Verified: {user.verified}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get users that a user follows const paginator = client.users.getFollowing("2244994945", { userFields: ["username", "verified"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Verified: ${user.verified}`); }); } ``` *** ## Follow a user Follow a user on behalf of the authenticated user: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/123456789/following" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_user_id": "2244994945"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Follow a user response = client.users.follow( source_user_id="123456789", target_user_id="2244994945" ) print(f"Following: {response.data.following}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Follow a user const response = await client.users.follow("123456789", { targetUserId: "2244994945", }); console.log(`Following: ${response.data?.following}`); ``` ### Response ```json theme={null} { "data": { "following": true, "pending_follow": false } } ``` If the target account is protected, `pending_follow` will be `true` until the follow request is approved. *** ## Unfollow a user Unfollow a user on behalf of the authenticated user: ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/users/123456789/following/2244994945" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Unfollow a user response = client.users.unfollow( source_user_id="123456789", target_user_id="2244994945" ) print(f"Following: {response.data.following}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Unfollow a user const response = await client.users.unfollow("123456789", "2244994945"); console.log(`Following: ${response.data?.following}`); ``` ### Response ```json theme={null} { "data": { "following": false } } ``` *** ## Common parameters | Parameter | Description | | :----------------- | :------------------------------------- | | `max_results` | Results per page (1-1000, default 100) | | `pagination_token` | Token for next page | | `user.fields` | Additional user fields | | `expansions` | Related objects to include | *** ## Next steps Look up user profiles Block and unblock users Mute and unmute users Full endpoint documentation # Get affiliates Source: https://docs.x.com/x-api/users/get-affiliates get /2/users/{id}/affiliates Retrieves a list of Users who are affiliated with a specific organization User by their ID. # Get blocking Source: https://docs.x.com/x-api/users/get-blocking get /2/users/{id}/blocking Retrieves a list of Users blocked by the specified User ID. # Get Bookmark folders Source: https://docs.x.com/x-api/users/get-bookmark-folders get /2/users/{id}/bookmarks/folders Retrieves a list of Bookmark folders created by the authenticated user. # Get Bookmarks Source: https://docs.x.com/x-api/users/get-bookmarks get /2/users/{id}/bookmarks Retrieves a list of Posts bookmarked by the authenticated user. # Get followers Source: https://docs.x.com/x-api/users/get-followers get /2/users/{id}/followers Retrieves a list of Users who follow a specific User by their ID. # Get following Source: https://docs.x.com/x-api/users/get-following get /2/users/{id}/following Retrieves a list of Users followed by a specific User by their ID. # Get liked Posts Source: https://docs.x.com/x-api/users/get-liked-posts get /2/users/{id}/liked_tweets Retrieves a list of Posts liked by a specific User by their ID. # Get mentions Source: https://docs.x.com/x-api/users/get-mentions get /2/users/{id}/mentions Retrieves a list of Posts that mention a specific User by their ID. # Get muting Source: https://docs.x.com/x-api/users/get-muting get /2/users/{id}/muting Retrieves a list of Users muted by the authenticated user. # Get my User Source: https://docs.x.com/x-api/users/get-my-user get /2/users/me Retrieves details of the authenticated user. # Get Posts Source: https://docs.x.com/x-api/users/get-posts get /2/users/{id}/tweets Retrieves a list of posts authored by a specific User by their ID. # Get Reposts of me Source: https://docs.x.com/x-api/users/get-reposts-of-me get /2/users/reposts_of_me Retrieves a list of Posts that repost content from the authenticated user. # Get Timeline Source: https://docs.x.com/x-api/users/get-timeline get /2/users/{id}/timelines/reverse_chronological Retrieves a reverse chronological list of Posts in the authenticated User’s Timeline. # Get User by ID Source: https://docs.x.com/x-api/users/get-user-by-id get /2/users/{id} Retrieves details of a specific User by their ID. # Get User by username Source: https://docs.x.com/x-api/users/get-user-by-username get /2/users/by/username/{username} Retrieves details of a specific User by their username. # Get Users by IDs Source: https://docs.x.com/x-api/users/get-users-by-ids get /2/users Retrieves details of multiple Users by their IDs. # Get Users by usernames Source: https://docs.x.com/x-api/users/get-users-by-usernames get /2/users/by Retrieves details of multiple Users by their usernames. # Like Post Source: https://docs.x.com/x-api/users/like-post post /2/users/{id}/likes Causes the authenticated user to Like a specific Post by its ID. # Integration Guide Source: https://docs.x.com/x-api/users/lookup/integrate Key concepts and best practices for integrating User lookup This guide covers the key concepts you need to integrate the User lookup endpoints into your application. *** ## Authentication All X API v2 endpoints require authentication. Choose the method that fits your use case: | Method | Best for | Can access private metrics? | | :----------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | :------------------------------- | | [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Server-to-server, public data | No | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | User-facing apps | Yes (for authorized user's data) | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy integrations | Yes (for authorized user's data) | ### App-Only authentication For public user data, use a Bearer Token: ```bash cURL theme={null} curl "https://api.x.com/2/users/by/username/XDevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get user by username response = client.users.get_by_username("XDevelopers") print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.users.getByUsername("XDevelopers"); console.log(response.data); ``` ### User Context authentication Required for the authenticated user endpoint (`/2/users/me`): ```bash cURL theme={null} curl "https://api.x.com/2/users/me" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client # Using OAuth 2.0 user access token client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get authenticated user's profile response = client.users.get_me() print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; // Using OAuth 2.0 user access token const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); const response = await client.users.getMe(); console.log(response.data); ``` The `/2/users/me` endpoint only works with User Context authentication. App-Only tokens will return an error. *** ## Fields and expansions The X API v2 returns minimal data by default. Use `fields` and `expansions` to request exactly what you need. ### Default response ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "XDevelopers" } } ``` ### Available fields | Field | Description | | :------------------ | :--------------------------- | | `created_at` | Account creation timestamp | | `description` | User bio | | `entities` | Parsed URLs in bio | | `location` | User-defined location | | `pinned_tweet_id` | Pinned Post ID | | `profile_image_url` | Avatar URL | | `protected` | Whether account is protected | | `public_metrics` | Follower/following counts | | `url` | Website URL | | `verified` | Verification status | | `withheld` | Withholding information | | Field | Description | | :--------------- | :----------------------- | | `created_at` | Post creation timestamp | | `text` | Post content | | `public_metrics` | Engagement counts | | `entities` | Hashtags, mentions, URLs | ### Example with fields ```bash cURL theme={null} curl "https://api.x.com/2/users/by/username/XDevelopers?\ user.fields=created_at,description,public_metrics,verified&\ expansions=pinned_tweet_id&\ tweet.fields=created_at,public_metrics" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get user with additional fields and expansions response = client.users.get_by_username( "XDevelopers", user_fields=["created_at", "description", "public_metrics", "verified"], expansions=["pinned_tweet_id"], tweet_fields=["created_at", "public_metrics"] ) print(response.data) print(response.includes) # Contains expanded pinned tweet ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.users.getByUsername("XDevelopers", { userFields: ["created_at", "description", "public_metrics", "verified"], expansions: ["pinned_tweet_id"], tweetFields: ["created_at", "public_metrics"], }); console.log(response.data); console.log(response.includes); // Contains expanded pinned tweet ``` ### Response with expansions ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "XDevelopers", "created_at": "2013-12-14T04:35:55.000Z", "verified": true, "pinned_tweet_id": "1234567890", "public_metrics": { "followers_count": 583423, "following_count": 2048, "tweet_count": 14052 } }, "includes": { "tweets": [ { "id": "1234567890", "text": "Welcome to the X Developer Platform!", "created_at": "2024-01-15T10:00:00.000Z" } ] } } ``` Learn more about customizing responses *** ## Batch lookups Look up multiple users in a single request: ```bash cURL (by IDs) theme={null} curl "https://api.x.com/2/users?ids=2244994945,783214,6253282&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```bash cURL (by usernames) theme={null} curl "https://api.x.com/2/users/by?usernames=XDevelopers,X,XAPI&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get multiple users by IDs response = client.users.get_users( ids=["2244994945", "783214", "6253282"], user_fields=["username", "verified"] ) for user in response.data: print(f"{user.username}: {user.verified}") # Get multiple users by usernames response = client.users.get_users_by_usernames( usernames=["XDevelopers", "X", "XAPI"], user_fields=["username", "verified"] ) for user in response.data: print(f"{user.username}: {user.verified}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get multiple users by IDs const byIds = await client.users.getUsers({ ids: ["2244994945", "783214", "6253282"], userFields: ["username", "verified"], }); byIds.data.forEach((user) => { console.log(`${user.username}: ${user.verified}`); }); // Get multiple users by usernames const byUsernames = await client.users.getUsersByUsernames({ usernames: ["XDevelopers", "X", "XAPI"], userFields: ["username", "verified"], }); byUsernames.data.forEach((user) => { console.log(`${user.username}: ${user.verified}`); }); ``` Batch requests are limited to 100 users. Use multiple requests for larger datasets. *** ## Error handling ### Common errors | Status | Error | Solution | | :----- | :---------------- | :---------------------------------- | | 400 | Invalid request | Check parameter formatting | | 401 | Unauthorized | Verify authentication credentials | | 403 | Forbidden | Check App permissions | | 404 | Not Found | User doesn't exist or was suspended | | 429 | Too Many Requests | Wait and retry (see rate limits) | ### Suspended or deleted users If a user is suspended or deleted: * Single user lookup returns `404` * Multi-user lookup omits the user from results with an `errors` array ```json theme={null} { "data": [ { "id": "2244994945", "username": "XDevelopers" } ], "errors": [ { "resource_id": "1234567890", "resource_type": "user", "title": "Not Found Error", "detail": "Could not find user with id: [1234567890]." } ] } ``` ### Protected users For protected accounts you don't follow: * Basic info (id, name, username) is available * Protected content (pinned Post) may be restricted * `protected: true` indicates the account status *** ## Best practices Use multi-user endpoints to fetch up to 100 users at once, reducing API calls. Specify only the fields you need to minimize response size. Cache user profiles locally to reduce repeated requests. Check for partial errors in batch responses. *** ## Next steps Complete endpoint documentation All available objects and fields Working code examples Handle errors gracefully # User Lookup Source: https://docs.x.com/x-api/users/lookup/introduction Retrieve user profiles by ID, username, or for the authenticated user The User lookup endpoints let you retrieve profile information for one or more users. Look up users by their ID, username, or get details for the currently authenticated user. ## Overview Look up users by their unique user ID Look up users by their @handle Retrieve up to 100 users per request Get details for the current user *** ## Endpoints | Method | Endpoint | Description | | :----- | :-------------------------------------------------------------------- | :--------------------------------- | | GET | [`/2/users/:id`](/x-api/users/get-user-by-id) | Get user by ID | | GET | [`/2/users`](/x-api/users/get-users-by-ids) | Get users by IDs (up to 100) | | GET | [`/2/users/by/username/:username`](/x-api/users/get-user-by-username) | Get user by username | | GET | [`/2/users/by`](/x-api/users/get-users-by-usernames) | Get users by usernames (up to 100) | | GET | [`/2/users/me`](/x-api/users/get-my-user) | Get authenticated user | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/users/by/username/XDevelopers?\ user.fields=created_at,description,public_metrics,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example response ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "XDevelopers", "created_at": "2013-12-14T04:35:55.000Z", "description": "The voice of the X developer community", "verified": true, "public_metrics": { "followers_count": 583423, "following_count": 2048, "tweet_count": 14052, "listed_count": 1672 } } } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Look up users by ID or username Get the current user's profile Key concepts and best practices Full endpoint documentation # Authenticated User Quickstart Source: https://docs.x.com/x-api/users/lookup/quickstart/authenticated-lookup Get the currently authenticated user's profile This guide walks you through retrieving the currently authenticated user's profile using the `/me` endpoint. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Get the authenticated user Make a request to the `/me` endpoint with a User Access Token: ```bash cURL theme={null} curl "https://api.x.com/2/users/me?\ user.fields=created_at,description,verified,public_metrics,profile_image_url" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get the authenticated user response = client.users.get_me( user_fields=["created_at", "description", "verified", "public_metrics", "profile_image_url"] ) print(f"Username: {response.data.username}") print(f"ID: {response.data.id}") print(f"Followers: {response.data.public_metrics.followers_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get the authenticated user const response = await client.users.getMe({ userFields: ["created_at", "description", "verified", "public_metrics", "profile_image_url"], }); console.log(`Username: ${response.data?.username}`); console.log(`ID: ${response.data?.id}`); console.log(`Followers: ${response.data?.public_metrics?.followers_count}`); ``` *** ## Response ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "XDevelopers", "created_at": "2013-12-14T04:35:55.000Z", "description": "The voice of the X developer community", "verified": true, "profile_image_url": "https://pbs.twimg.com/profile_images/...", "public_metrics": { "followers_count": 583423, "following_count": 2048, "tweet_count": 14052, "listed_count": 1672 } } } ``` *** ## Use case The `/me` endpoint is essential when: * **Verifying authentication** — Confirm the user is properly authenticated * **Getting the user ID** — Retrieve the authenticated user's ID for other API calls * **Personalizing experiences** — Display the user's profile in your app * **On behalf of requests** — Know who you're making requests for *** ## Include pinned Post Request the user's pinned Post: ```bash cURL theme={null} curl "https://api.x.com/2/users/me?\ user.fields=pinned_tweet_id&\ expansions=pinned_tweet_id&\ tweet.fields=created_at,text" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get authenticated user with pinned Post response = client.users.get_me( user_fields=["pinned_tweet_id"], expansions=["pinned_tweet_id"], tweet_fields=["created_at", "text"] ) print(f"Username: {response.data.username}") # Pinned Post is in response.includes.tweets ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get authenticated user with pinned Post const response = await client.users.getMe({ userFields: ["pinned_tweet_id"], expansions: ["pinned_tweet_id"], tweetFields: ["created_at", "text"], }); console.log(`Username: ${response.data?.username}`); // Pinned Post is in response.includes?.tweets ``` ### Response with expansion ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "XDevelopers", "pinned_tweet_id": "1234567890" }, "includes": { "tweets": [ { "id": "1234567890", "text": "Welcome to my profile!", "created_at": "2024-01-01T00:00:00.000Z" } ] } } ``` *** ## Available fields | Field | Description | | :------------------ | :------------------------ | | `created_at` | Account creation date | | `description` | User bio | | `profile_image_url` | Avatar URL | | `verified` | Verification status | | `public_metrics` | Follower/following counts | | `location` | User-defined location | | `url` | User's website | | `protected` | Protected account status | | `pinned_tweet_id` | Pinned Post ID | *** ## Authentication requirement The `/me` endpoint requires User Context authentication. App-Only (Bearer Token) authentication is not supported. Use either: * [OAuth 1.0a User Context](/resources/fundamentals/authentication) * [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) *** ## Next steps Look up other users Key concepts and best practices Full endpoint documentation # User Lookup Quickstart Source: https://docs.x.com/x-api/users/lookup/quickstart/user-lookup Look up users by ID or username This guide walks you through looking up users by their ID or username. **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 *** ## Look up by ID ### Single user ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945?\ user.fields=created_at,description,verified,public_metrics" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get user by ID response = client.users.get( "2244994945", user_fields=["created_at", "description", "verified", "public_metrics"] ) print(f"Name: {response.data.name}") print(f"Username: {response.data.username}") print(f"Followers: {response.data.public_metrics.followers_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get user by ID const response = await client.users.get("2244994945", { userFields: ["created_at", "description", "verified", "public_metrics"], }); console.log(`Name: ${response.data?.name}`); console.log(`Username: ${response.data?.username}`); console.log(`Followers: ${response.data?.public_metrics?.followers_count}`); ``` ### Response ```json theme={null} { "data": { "id": "2244994945", "name": "X Developers", "username": "XDevelopers", "created_at": "2013-12-14T04:35:55.000Z", "description": "The voice of the X developer community", "verified": true, "public_metrics": { "followers_count": 583423, "following_count": 2048, "tweet_count": 14052, "listed_count": 1672 } } } ``` ### Multiple users Look up up to 100 users at once: ```bash cURL theme={null} curl "https://api.x.com/2/users?\ ids=2244994945,783214,6253282&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get multiple users by ID response = client.users.get_users( ids=["2244994945", "783214", "6253282"], user_fields=["username", "verified"] ) for user in response.data: print(f"{user.username} - Verified: {user.verified}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get multiple users by ID const response = await client.users.getUsers({ ids: ["2244994945", "783214", "6253282"], userFields: ["username", "verified"], }); response.data?.forEach((user) => { console.log(`${user.username} - Verified: ${user.verified}`); }); ``` *** ## Look up by username ### Single user ```bash cURL theme={null} curl "https://api.x.com/2/users/by/username/XDevelopers?\ user.fields=created_at,description,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get user by username response = client.users.get_by_username( "XDevelopers", user_fields=["created_at", "description", "verified"] ) print(f"ID: {response.data.id}") print(f"Name: {response.data.name}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get user by username const response = await client.users.getByUsername("XDevelopers", { userFields: ["created_at", "description", "verified"], }); console.log(`ID: ${response.data?.id}`); console.log(`Name: ${response.data?.name}`); ``` ### Multiple users ```bash cURL theme={null} curl "https://api.x.com/2/users/by?\ usernames=XDevelopers,X,elonmusk&\ user.fields=created_at,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get multiple users by username response = client.users.get_users_by_usernames( usernames=["XDevelopers", "X", "elonmusk"], user_fields=["created_at", "verified"] ) for user in response.data: print(f"{user.username} - {user.created_at}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get multiple users by username const response = await client.users.getUsersByUsernames({ usernames: ["XDevelopers", "X", "elonmusk"], userFields: ["created_at", "verified"], }); response.data?.forEach((user) => { console.log(`${user.username} - ${user.created_at}`); }); ``` *** ## Available fields | Field | Description | | :------------------ | :------------------------ | | `created_at` | Account creation date | | `description` | User bio | | `profile_image_url` | Avatar URL | | `verified` | Verification status | | `public_metrics` | Follower/following counts | | `location` | User-defined location | | `url` | User's website | | `protected` | Protected account status | | `pinned_tweet_id` | Pinned Post ID | *** ## Handle errors ### User not found ```json theme={null} { "errors": [ { "resource_type": "user", "title": "Not Found Error", "detail": "Could not find user with username: [nonexistent_user]." } ] } ``` ### Protected user Protected users' data is still returned, but you won't be able to access their Posts unless you follow them. *** ## Next steps Get the current user Key concepts and best practices Full endpoint documentation # Mute User Source: https://docs.x.com/x-api/users/mute-user post /2/users/{id}/muting Causes the authenticated user to mute a specific User by their ID. # Integration Guide Source: https://docs.x.com/x-api/users/mutes/integrate Key concepts and best practices for integrating mutes endpoints This guide covers the key concepts you need to integrate the mutes endpoints into your application. *** ## Authentication Mutes endpoints require user authentication to access private mute lists: | Method | Description | | :----------------------------------------------------------------------------------------------------------------------------- | :------------------------------- | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | Recommended for new applications | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy support | App-Only authentication is not supported. You must authenticate on behalf of a user. ### Required scopes (OAuth 2.0) | Scope | Required for | | :----------- | :--------------------------- | | `mute.read` | Retrieving muted accounts | | `mute.write` | Muting and unmuting accounts | | `users.read` | Required with mute scopes | *** ## Endpoints overview | Method | Endpoint | Description | | :----- | :------------------------------------------------ | :------------------------- | | GET | `/2/users/:id/muting` | Get list of muted accounts | | POST | `/2/users/:id/muting` | Mute an account | | DELETE | `/2/users/:source_user_id/muting/:target_user_id` | Unmute an account | *** ## Fields and expansions ### Default response ```json theme={null} { "data": [ { "id": "1234567890", "name": "Example User", "username": "example" } ] } ``` ### Available fields | Field | Description | | :------------------ | :------------------------ | | `created_at` | Account creation date | | `description` | User bio | | `profile_image_url` | Avatar URL | | `public_metrics` | Follower/following counts | | `verified` | Verification status | | Expansion | Description | | :---------------- | :----------------- | | `pinned_tweet_id` | User's pinned Post | ### Example with fields ```bash cURL theme={null} curl "https://api.x.com/2/users/123456789/muting?\ user.fields=username,verified,created_at&\ max_results=100" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get muted users with additional fields for page in client.users.get_muting( user_id="123456789", user_fields=["username", "verified", "created_at"], max_results=100 ): for user in page.data: print(f"{user.username} - Verified: {user.verified}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); const paginator = client.users.getMuting("123456789", { userFields: ["username", "verified", "created_at"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Verified: ${user.verified}`); }); } ``` *** ## Pagination For users with large mute lists, results are paginated: ```bash cURL theme={null} # First request curl "https://api.x.com/2/users/123/muting?max_results=100" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" # Subsequent request with pagination token curl "https://api.x.com/2/users/123/muting?max_results=100&pagination_token=NEXT_TOKEN" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # The SDK handles pagination automatically all_muted = [] for page in client.users.get_muting(user_id="123", max_results=100): if page.data: all_muted.extend(page.data) print(f"Muted {len(all_muted)} users") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); async function getAllMutedUsers(userId) { const allMuted = []; // The SDK handles pagination automatically const paginator = client.users.getMuting(userId, { maxResults: 100 }); for await (const page of paginator) { if (page.data) { allMuted.push(...page.data); } } return allMuted; } // Usage const muted = await getAllMutedUsers("123"); console.log(`Muted ${muted.length} users`); ``` Learn more about pagination *** ## Behavior differences ### Muting vs Blocking | Feature | Mute | Block | | :------------------ | :--------------- | :----------- | | See their Posts | No (hidden) | No | | They see your Posts | Yes | No | | They follow you | Yes (can follow) | No (removed) | | They can DM you | Yes | No | | Notification sent | No | No | Muting is private — the muted user is not notified and cannot tell they've been muted. *** ## Error handling | Status | Error | Solution | | :----- | :---------------- | :--------------------------- | | 400 | Invalid request | Check user ID format | | 401 | Unauthorized | Verify access token | | 403 | Forbidden | Check scopes and permissions | | 404 | Not Found | User doesn't exist | | 429 | Too Many Requests | Wait and retry | *** ## Next steps Make your first mutes request Block users instead of muting Full endpoint documentation Working code examples # Mutes Source: https://docs.x.com/x-api/users/mutes/introduction Mute and unmute users, and retrieve muted user lists The Mutes endpoints let you mute and unmute users, and retrieve the list of users muted by the authenticated user. ## Overview Mute a user Unmute a user Get your muted user list *** ## Endpoints | Method | Endpoint | Description | | :----- | :---------------------------------------------------------------------------- | :-------------- | | GET | [`/2/users/:id/muting`](/x-api/users/get-muting) | Get muted users | | POST | [`/2/users/:id/muting`](/x-api/users/mute-user) | Mute a user | | DELETE | [`/2/users/:source_user_id/muting/:target_user_id`](/x-api/users/unmute-user) | Unmute a user | *** ## Example: Get muted users ```bash theme={null} curl "https://api.x.com/2/users/123456789/muting?\ user.fields=username,description" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ## Example: Mute a user ```bash theme={null} curl -X POST "https://api.x.com/2/users/123456789/muting" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_user_id": "9876543210"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) Get your muted users Mute and unmute users Key concepts and best practices Full endpoint documentation # Manage Mutes Source: https://docs.x.com/x-api/users/mutes/quickstart/manage-mutes-quickstart Mute and unmute users using the X API This guide walks you through muting and unmuting users using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Mute a user You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). Find the user ID of the account you want to mute using the [user lookup endpoint](/x-api/users/lookup/introduction). ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/123456789/muting" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_user_id": "9876543210"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Mute a user response = client.users.mute( source_user_id="123456789", target_user_id="9876543210" ) print(f"Muting: {response.data.muting}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Mute a user const response = await client.users.mute("123456789", { targetUserId: "9876543210", }); console.log(`Muting: ${response.data?.muting}`); ``` ```json theme={null} { "data": { "muting": true } } ``` *** ## Unmute a user Remove a mute from a user: ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/users/123456789/muting/9876543210" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Unmute a user response = client.users.unmute( source_user_id="123456789", target_user_id="9876543210" ) print(f"Muting: {response.data.muting}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Unmute a user const response = await client.users.unmute("123456789", "9876543210"); console.log(`Muting: ${response.data?.muting}`); ``` **Response:** ```json theme={null} { "data": { "muting": false } } ``` *** ## Mute vs Block | Feature | Mute | Block | | :------------------ | :--- | :---- | | See their Posts | No | No | | They see your Posts | Yes | No | | They can follow you | Yes | No | | They can DM you | Yes | No | | They know | No | Yes | *** ## Next steps Get your muted users Block users instead Full endpoint documentation # Mutes Lookup Source: https://docs.x.com/x-api/users/mutes/quickstart/mutes-lookup Get the list of users you have muted This guide walks you through retrieving your muted users list using the X API. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Get your muted users You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). ```bash cURL theme={null} curl "https://api.x.com/2/users/123456789/muting?\ user.fields=created_at,username,verified&\ max_results=100" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get muted users with pagination for page in client.users.get_muting( "123456789", user_fields=["created_at", "username", "verified"], max_results=100 ): for user in page.data: print(f"{user.username} - Muted") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get muted users with pagination const paginator = client.users.getMuting("123456789", { userFields: ["created_at", "username", "verified"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Muted`); }); } ``` ```json theme={null} { "data": [ { "id": "2244994945", "name": "X Developers", "username": "XDevelopers", "created_at": "2013-12-14T04:35:55.000Z", "verified": true } ], "meta": { "result_count": 1, "next_token": "1710819323648428707" } } ``` *** ## Include additional data Use expansions to get related data like pinned Posts: ```bash cURL theme={null} curl "https://api.x.com/2/users/123456789/muting?\ user.fields=created_at&\ expansions=pinned_tweet_id&\ tweet.fields=created_at" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get muted users with expansions for page in client.users.get_muting( "123456789", user_fields=["created_at"], expansions=["pinned_tweet_id"], tweet_fields=["created_at"] ): for user in page.data: print(f"{user.username}") # Pinned Posts are in page.includes.tweets ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get muted users with expansions const paginator = client.users.getMuting("123456789", { userFields: ["created_at"], expansions: ["pinned_tweet_id"], tweetFields: ["created_at"], }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(user.username); }); // Pinned Posts are in page.includes?.tweets } ``` ### Response with expansion ```json theme={null} { "data": [ { "username": "XDevelopers", "created_at": "2013-12-14T04:35:55.000Z", "id": "2244994945", "name": "X Developers", "pinned_tweet_id": "1430984356139470849" } ], "includes": { "tweets": [ { "created_at": "2021-08-26T20:03:51.000Z", "id": "1430984356139470849", "text": "Help us build a better X Developer Platform!..." } ] }, "meta": { "result_count": 1 } } ``` *** ## Paginate through results The SDKs handle pagination automatically. For cURL, use the `next_token` from the response: ```bash theme={null} curl "https://api.x.com/2/users/123456789/muting?\ max_results=100&\ pagination_token=1710819323648428707" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` *** ## Next steps Mute and unmute users Full endpoint documentation # Repost Post Source: https://docs.x.com/x-api/users/repost-post post /2/users/{id}/retweets Causes the authenticated user to repost a specific Post by its ID. # Search Users Source: https://docs.x.com/x-api/users/search-users get /2/users/search Retrieves a list of Users matching a search query. # User Search Source: https://docs.x.com/x-api/users/search/introduction Search for users by keyword The User Search endpoint lets you search for users by keyword. Find users by name, username, or content in their bio. ## Overview Search by name, username, or bio Find relevant accounts *** ## Endpoint | Method | Endpoint | Description | | :----- | :--------------------------------------------- | :--------------- | | GET | [`/2/users/search`](/x-api/users/search-users) | Search for users | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/users/search?\ query=python%20developer&\ user.fields=description,verified,public_metrics" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example response ```json theme={null} { "data": [ { "id": "1234567890", "name": "Python Developer", "username": "pythondev", "description": "Building cool things with Python", "verified": false, "public_metrics": { "followers_count": 5000, "following_count": 200, "tweet_count": 1500 } } ], "meta": { "result_count": 1 } } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Look up users by ID or username Full endpoint documentation # Unblock DMs Source: https://docs.x.com/x-api/users/unblock-dms post /2/users/{id}/dm/unblock Unblocks direct messages to or from a specific User by their ID for the authenticated user. # Unfollow User Source: https://docs.x.com/x-api/users/unfollow-user delete /2/users/{source_user_id}/following/{target_user_id} Causes the authenticated user to unfollow a specific user by their ID. # Unmute User Source: https://docs.x.com/x-api/users/unmute-user delete /2/users/{source_user_id}/muting/{target_user_id} Causes the authenticated user to unmute a specific user by their ID. # Unrepost Post Source: https://docs.x.com/x-api/users/unrepost-post delete /2/users/{id}/retweets/{source_tweet_id} Causes the authenticated user to unrepost a specific Post by its ID. # Activity Stream Source: https://docs.x.com/x-api/activity/activity-stream get /2/activity/stream Stream of X Activities # Create X activity subscription Source: https://docs.x.com/x-api/activity/create-x-activity-subscription post /2/activity/subscriptions Creates a subscription for an X activity event # Delete X activity subscriptions by IDs Source: https://docs.x.com/x-api/activity/delete-x-activity-subscriptions-by-ids delete /2/activity/subscriptions Deletes multiple subscriptions for X activity events by their IDs # Deletes X activity subscription Source: https://docs.x.com/x-api/activity/deletes-x-activity-subscription delete /2/activity/subscriptions/{subscription_id} Deletes a subscription for an X activity event # Get X activity subscriptions Source: https://docs.x.com/x-api/activity/get-x-activity-subscriptions get /2/activity/subscriptions Get a list of active subscriptions for XAA # Introduction Source: https://docs.x.com/x-api/activity/introduction 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. ## Delivery Mechanisms The X Activity API currently supports the following delivery mechanisms to send events to your app: * Persistent HTTP stream * [Webhook](/x-api/webhooks/introduction) ## Supported Event Types Currently, X Activity API supports the following event types, organized by category: ### Profile Events Profile events are triggered when a user makes changes to their profile information. | 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.handle` | Fired when a user updates their handle | `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` | ### Follow Events Follow events are triggered when the filtered user follows another user, or is followed by 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` | ### Legacy DM Events Legacy DM events pertain to the legacy, unencrypted DM system. | Event Name | Description | Filters | | -------------------- | ------------------------------------------------------------------------------------ | --------- | | `dm.received` | Fired when a user receives an unencrypted direct message | `user_id` | | `dm.sent` | Fired when a user sends an unencrypted direct message | `user_id` | | `dm.read` | Fired when a user reads the filtered users unencrypted DM message, or "read reciept" | `user_id` | | `dm.indicate_typing` | Fired when a user is typing a message to the filtered user | `user_id` | ### Chat Events Chat events pertain to the new, encrypted messaging stack, or XChat. | Event Name | Description | Filters | | ------------------------ | ------------------------------------------------------ | --------- | | `chat.received` | Fired when a user receives an encrypted direct message | `user_id` | | `chat.sent` | Fired when a user sends an encrypted direct message | `user_id` | | `chat.conversation_join` | Fired when a user joins an encrypted chat conversation | `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. **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 | **Account setup** To access these endpoints, you will need: * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info). * To authenticate using the keys and tokens from a [developer App](/resources/fundamentals/developer-apps) that is located within a [Project](/resources/fundamentals/projects). Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access).
# Quickstart Source: https://docs.x.com/x-api/activity/quickstart 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](/resources/fundamentals/authentication) *** ## Getting User IDs 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: **Look up a user's ID by username:** ```bash theme={null} curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" "https://api.x.com/2/users/by/username/xdevelopers" ``` **Get your own user ID:** ```bash theme={null} 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 theme={null} { "data": { "id": "2244994945", "name": "Developers", "username": "XDevelopers" } } ``` *** ## 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 theme={null} { "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 theme={null} 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 theme={null} { "data":[ { "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 } } ``` *** ## Getting the events Once we have created the subscription, we can receive the events via [webhooks](https://docs.x.com/x-api/webhooks/introduction) or a persistent HTTP stream. In this example, we will open the persistent HTTP stream: ```bash theme={null} 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 theme={null} { "data": { "filter": { "user_id": "2244994945" }, "event_type": "profile.update.bio", "tag": "Xdevelopers' bio updates", "payload": { "before": "Mars & Cars", "after": "Mars, Cars & AI" } } } ``` *** ## Subscription Management The X Activity API provides endpoints to manage your subscriptions through standard CRUD operations. ### Create Subscription Create a new subscription to receive events: ```bash theme={null} 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 theme={null} { "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 theme={null} curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ https://api.x.com/2/activity/subscriptions ``` **Response:** ```json theme={null} { "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 theme={null} curl -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ -X DELETE \ https://api.x.com/2/activity/subscriptions/1976325569252868096 ``` **Response:** ```json theme={null} { "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 theme={null} 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 theme={null} { "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 Full endpoint documentation Set up webhook delivery # Update X activity subscription Source: https://docs.x.com/x-api/activity/update-x-activity-subscription put /2/activity/subscriptions/{subscription_id} Updates a subscription for an X activity event # Get Community by ID Source: https://docs.x.com/x-api/communities/get-community-by-id get /2/communities/{id} Retrieves details of a specific Community by its ID. # Communities Lookup Source: https://docs.x.com/x-api/communities/lookup/introduction Retrieve Community details by ID The Communities lookup endpoint lets you retrieve information about X Communities by their ID. ## Overview Communities are public or private groups on X where members can share Posts, discuss topics, and connect around shared interests. Get details for a specific Community Find Communities by keyword *** ## Endpoint | Method | Endpoint | Description | | :----- | :------------------------------------------------------------- | :------------------ | | GET | [`/2/communities/:id`](/x-api/communities/get-community-by-id) | Get Community by ID | *** ## Response fields | Field | Description | | :------------- | :--------------------------- | | `id` | Community ID | | `name` | Community name | | `description` | Community description | | `created_at` | Creation date | | `member_count` | Number of members | | `is_private` | Whether Community is private | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/communities/1234567890?\ community.fields=name,description,member_count,created_at" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Find Communities by keyword Full endpoint documentation # Search Communities Source: https://docs.x.com/x-api/communities/search-communities get /2/communities/search Retrieves a list of Communities matching the specified search query. # Communities Search Source: https://docs.x.com/x-api/communities/search/introduction Search for Communities by keyword The Communities Search endpoint lets you search for Communities by keyword. Discover Communities around topics of interest. ## Overview Search Communities by name and description Find Communities to join *** ## Endpoint | Method | Endpoint | Description | | :----- | :--------------------------------------------------------------- | :--------------------- | | GET | [`/2/communities/search`](/x-api/communities/search-communities) | Search for Communities | *** ## Parameters | Parameter | Description | | :----------------- | :-------------------------- | | `query` | Search query (required) | | `max_results` | Results per page (max 100) | | `community.fields` | Additional Community fields | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/communities/search?\ query=Python&\ max_results=10&\ community.fields=name,description,member_count" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Look up Communities by ID Full endpoint documentation # Create a Community Note Source: https://docs.x.com/x-api/community-notes/create-a-community-note post /2/notes Creates a community note endpoint for LLM use case. # Delete a Community Note Source: https://docs.x.com/x-api/community-notes/delete-a-community-note delete /2/notes/{id} Deletes a community note. # Evaluate a Community Note Source: https://docs.x.com/x-api/community-notes/evaluate-a-community-note post /2/evaluate_note Endpoint to evaluate a community note. # Introduction Source: https://docs.x.com/x-api/community-notes/introduction The Community Notes Endpoints in the X API v2 allow developers to search for and propose Community Notes on X programmatically. Use of the API requires your account being signed up for X Developer AI access and enrolled in Community Notes as an AI Note Writer. You can [enroll and learn more about AI Note Writers](https://communitynotes.x.com/guide/en/api/overview) in the Community Notes Guide. Currently, the API supports two endpoint categories: ## Search ### Search for X Posts that are eligible to receive a Community Note Developers can retrieve a list of X Posts that are eligible to receive a Community Note using the `GET https://api.x.com/2/notes/search/posts_eligible_for_notes` endpoint. ### Search for Community Notes that have been written by the user on X Posts Developers can retrieve a list of Community Notes that have been written by the authenticating user on X Posts using the `GET https://api.x.com/2/notes/search/notes_written` endpoint. ## Evaluate Drafted Community Notes Before Submitting Before submitting a note, developers can get the note evaluated using the `POST https://api.x.com/2/evaluate_note` endpoint. ## Manage Community Notes Developers can submit Community Notes on X Posts using the `POST https://api.x.com/2/notes` endpoint. ## Rate Limits The current rate limits are 90 request per 15 minutes for all Community Notes endpoints and an additional 250 requests per day limit for the Manage Community Notes endpoint. **Note:** These endpoints are currently in a pilot test. Please check out our [Quick Start Guide](https://docs.x.com/x-api/community-notes/quickstart) for details on using these endpoints # Quickstart Source: https://docs.x.com/x-api/community-notes/quickstart Create and search Community Notes via the API This guide walks you through using the Community Notes API to search for eligible Posts and submit notes. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * Enrollment as a [Community Notes AI Note Writer](https://communitynotes.x.com/guide/en/api/overview) * User Access Token (OAuth 1.0a) Currently, `test_mode` must be set to `true` for all requests. Test notes are not publicly visible. *** ## Find Posts eligible for notes ```bash theme={null} curl "https://api.x.com/2/notes/search/posts_eligible_for_notes?\ test_mode=true&\ max_results=100" \ -H "Authorization: OAuth ..." ``` ```python theme={null} from requests_oauthlib import OAuth1Session import json oauth = OAuth1Session( client_key='YOUR_API_KEY', client_secret='YOUR_API_SECRET', resource_owner_key='YOUR_ACCESS_TOKEN', resource_owner_secret='YOUR_ACCESS_TOKEN_SECRET', ) url = "https://api.x.com/2/notes/search/posts_eligible_for_notes" params = {"test_mode": True, "max_results": 100} response = oauth.get(url, params=params) print(json.dumps(response.json(), indent=2)) ``` ```json theme={null} { "data": [ { "id": "1933207126262096118", "text": "Join us to learn more about our new analytics endpoints...", "edit_history_tweet_ids": ["1933207126262096118"] }, { "id": "1930672414444372186", "text": "Thrilled to announce that X API has won the 2025 award...", "edit_history_tweet_ids": ["1930672414444372186"] } ], "meta": { "newest_id": "1933207126262096118", "oldest_id": "1930672414444372186", "result_count": 2 } } ``` Use the Post `id` from the response to write a Community Note. *** ## Submit a Community Note A Community Note requires: * `post_id` — The Post you're adding context to * `text` — Your note (1-280 characters, must include a source URL) * `classification` — Either `misinformed_or_potentially_misleading` or `not_misleading` * `misleading_tags` — Required if classification is misleading * `trustworthy_sources` — Boolean indicating if source is trustworthy ```bash theme={null} curl -X POST "https://api.x.com/2/notes" \ -H "Authorization: OAuth ..." \ -H "Content-Type: application/json" \ -d '{ "test_mode": true, "post_id": "1939667242318541239", "info": { "text": "This claim lacks context. See the full report: https://example.com/report", "classification": "misinformed_or_potentially_misleading", "misleading_tags": ["missing_important_context"], "trustworthy_sources": true } }' ``` ```python theme={null} from requests_oauthlib import OAuth1Session import json oauth = OAuth1Session( client_key='YOUR_API_KEY', client_secret='YOUR_API_SECRET', resource_owner_key='YOUR_ACCESS_TOKEN', resource_owner_secret='YOUR_ACCESS_TOKEN_SECRET', ) payload = { "test_mode": True, "post_id": "1939667242318541239", "info": { "text": "This claim lacks context. See the full report: https://example.com/report", "classification": "misinformed_or_potentially_misleading", "misleading_tags": ["missing_important_context"], "trustworthy_sources": True, } } response = oauth.post("https://api.x.com/2/notes", json=payload) print(json.dumps(response.json(), indent=2)) ``` ```json theme={null} { "data": { "note_id": "1938678124100886981" } } ``` *** ## Get your submitted notes Retrieve notes you've written: ```python theme={null} from requests_oauthlib import OAuth1Session import json oauth = OAuth1Session( client_key='YOUR_API_KEY', client_secret='YOUR_API_SECRET', resource_owner_key='YOUR_ACCESS_TOKEN', resource_owner_secret='YOUR_ACCESS_TOKEN_SECRET', ) url = "https://api.x.com/2/notes/search/notes_written" params = {"test_mode": True, "max_results": 100} response = oauth.get(url, params=params) print(json.dumps(response.json(), indent=2)) ``` **Response:** ```json theme={null} { "data": [ { "id": "1939827717186494817", "info": { "text": "This claim lacks context. https://example.com/report", "classification": "misinformed_or_potentially_misleading", "misleading_tags": ["missing_important_context"], "post_id": "1939719604957577716", "trustworthy_sources": true } } ], "meta": { "result_count": 1 } } ``` *** ## Classification options When classification is `misinformed_or_potentially_misleading`, include one or more tags: | Tag | Description | | :-------------------------- | :------------------------------- | | `disputed_claim_as_fact` | Presents disputed claim as fact | | `factual_error` | Contains factual errors | | `manipulated_media` | Media has been altered | | `misinterpreted_satire` | Satire taken out of context | | `missing_important_context` | Lacks key context | | `outdated_information` | Information is no longer current | | `other` | Other reasons | When classification is `not_misleading`, no misleading tags are required. *** ## Common errors ```json theme={null} {"title": "Unauthorized", "status": 401, "detail": "Unauthorized"} ``` **Resolution:** Check your OAuth credentials are correct. ```json theme={null} {"detail": "User must be an API Note Writer to access this endpoint."} ``` **Resolution:** Enroll as a [Community Notes AI Note Writer](https://communitynotes.x.com/guide/en/api/overview). ```json theme={null} {"message": "User already created a note for this post."} ``` **Resolution:** You can only submit one note per Post. *** ## Next steps Official Community Notes documentation Working code examples # Search for Community Notes Written Source: https://docs.x.com/x-api/community-notes/search-for-community-notes-written get /2/notes/search/notes_written Returns all the community notes written by the user. # Search for Posts Eligible for Community Notes Source: https://docs.x.com/x-api/community-notes/search-for-posts-eligible-for-community-notes get /2/notes/search/posts_eligible_for_notes Returns all the posts that are eligible for community notes. # Integration guide Source: https://docs.x.com/x-api/compliance/batch-compliance/integrate ## Working with resumable uploads When using the Batch compliance endpoints, developers can batch upload large amounts of X data and understand what action is needed to ensure that their datasets reflect user intent and the current state of the content on X. Uploading large amounts of data to a remote server is a relatively straightforward operation when systems and connectivity are stable and reliable. However, this may not always be the case. Some environments may impose a connection timeout, effectively cutting the connection between your app and the upload server after a set amount of time; you may also encounter connection issues, for example when trying to upload a large file from your laptop over a wi-fi connection. In these circumstances, it’s desirable to upload smaller portions of that file at a time, rather than having one single continuous connection. X's batch compliance endpoints rely on Google Cloud Storage to process large files. This type of storage is optimized for various applications; Cloud Storage supports a technique to manage large files called resumable uploads. If the upload goes wrong at any point, Google Cloud Storage is able to resume the operation from where it was left off. ### Creating a resumable job #### Step one: First, you will have to create a compliance job and specify whether you will be uploading Post IDs or user IDs (using the type parameter). Additionally, add resumable to the body and set it to true. Make sure to replace the \$APP\_ACCESS\_TOKEN below with your App only Access Token below. ``` curl --request POST \ 'https://api.x.com/2/compliance/jobs' --header 'Authorization: Bearer $APP_ACCESS_TOKEN --header 'Content-Type: application/json' --data-raw '{ "type": "tweets", "resumable": true }' ``` If your API call is successful, you will get a response similar to the following: ``` { "data": { "download_expires_at": "2021-08-18T19:42:55.000Z", "status": "created", "upload_url": "https://storage.googleapis.com/twttr-tweet-compliance/1425543269983784962/submission/1202726487847104512_1425543269983784962?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210811%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210811T194255Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=355e4c4739ae508304d3df15b4e13e64b6c7752d8d79d73676a4d8e60dc5241f83924ad2a1f8b7bddcc768062bb9c64d39b8e8f7cce7f66ffbea9f9ed33a4da975b3a2c127fb738c1c1ff3c3964bd4d9dc0706e6c8a70e67522160ea774e090d2793e06f890d1158ce86be3031c1c471b74f961b6f18743a28730611000336286ad0111b41fb5d14aa813ff00cf06b3572dc68d0b3c6fdc07f25c1b1196c1af4325a9ead68994944bbef0d2123585ea051deb9765aa7f5832446440bc9ba76af327b69df1fd7b1a99bd4419c128f1f697dbbacbc62bbc7c2c9aebc82a2128be0ed05d48a54d814162daad1232a0d13081e9543ab8557f567149af82281193f37", "created_at": "2021-08-11T19:42:55.000Z", "resumable": false, "id": "1425543269983784962", "type": "tweets", "download_url": "https://storage.googleapis.com/twttr-tweet-compliance/1425543269983784962/delivery/1202726487847104512_1425543269983784962?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210811%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210811T194255Z&X-Goog-Expires=604800&X-Goog-SignedHeaders=host&X-Goog-Signature=0a11dd5a3c5adb508f32ce904568abada863dc9499ba2adeafb3452ccee0dcb3dade17910dbc502dcbe54c130ac4d8638eb176c8b7344de068139b06c970794efa6312f0a5149f40da441eafcaf475f670c93ca73951999902a531d34dfab1e5490918929e5b06ae803b5604e0c0c26852255ccdbc79a2c1e2eefe924e5e6bf5b6603a7f287d1621333b9548ec6cc203716070528bebc2e67c12e92b1f4e54471db92c15a54799f2b855ae224250ca44c47993fd7d79a4940a0f68fe09f73fc8b291e88cfd10ade860b4b35c2b964d1777c1d93cd300c313138d9ca90aa8b3ecd3bf9dc73d3ebe32ba7634228fe07e1e4ecdda57cd94c802afc520162735d5a3", "upload_expires_at": "2021-08-11T19:57:55.000Z" } } ``` Take note of the value from the upload\_url, you will need it in the following steps. #### Step two:  Next, you will need to initiate the resumable upload. In order to do so, make a **POST** call to the upload\_url from the previous step and make sure to include the following headers: Content-Type: text/plain Content-Length: 0 x-goog-resumable: start ``` curl --request POST \ 'https://storage.googleapis.com/twttr-tweet-compliance/1430227686685757442/submission/1202726487847104512_1430227686685757442?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210824%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210824T175707Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-length%3Bcontent-type%3Bhost%3Bx-goog-resumable&X-Goog-Signature=890d958f9c7dcb7f238e4971b59da5afc5b8329fb197c67b5930fe0f9dfe180afe2d4bec341111809b88ccfab46ab1f81f4242abc1af7b67c6e8977c52e6d486f5f43ce6a37a7a6530d25f15e2bcd9bb54655fe4ee22b26f8886ba71b67b7b11afd1198d658d1b6f0c41260f55260a260e1be0239977feba43dce40bc0e8e6293a4a3a3f7ee0afc74d3d2f7f2d3d514f108d5887a52ac85760385e5b9bb67cd26bfcf6b1c19151ea8111e217a29407722dc0dc9ab373334e88c18159546237ec9334f9a1e33717dc82800c6a45bba82706d5aece84ecdf3fcac52b21c8a3085a639047cf2707a8b9e4c296fc7cf05edbb110f07b89e38f0f5ea77e8b313cade7' \ --header 'Content-Type: text/plain' --header \ 'Content-Length: 0' --header \ 'x-goog-resumable: start ``` If this call is successful, you will get a 201 response code. Then, in the response header, copy the value for the location header which will look something like this: ``` https://storage.googleapis.com/twttr-tweet-compliance/1430227686685757442/submission/1202726487847104512_1430227686685757442?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=complianceapi-public-svc-acct%40twttr-compliance-public-prod.iam.gserviceaccount.com%2F20210824%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210824T175707Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-length%3Bcontent-type%3Bhost%3Bx-goog-resumable&X-Goog-Signature=890d958f9c7dcb7f238e4971b59da5afc5b8329fb197c67b5930fe0f9dfe180afe2d4bec341111809b88ccfab46ab1f81f4242abc1af7b67c6e8977c52e6d486f5f43ce6a37a7a6530d25f15e2bcd9bb54655fe4ee22b26f8886ba71b67b7b11afd1198d658d1b6f0c41260f55260a260e1be0239977feba43dce40bc0e8e6293a4a3a3f7ee0afc74d3d2f7f2d3d514f108d5887a52ac85760385e5b9bb67cd26bfcf6b1c19151ea8111e217a29407722dc0dc9ab373334e88c18159546237ec9334f9a1e33717dc82800c6a45bba82706d5aece84ecdf3fcac52b21c8a3085a639047cf2707a8b9e4c296fc7cf05edbb110f07b89e38f0f5ea77e8b313cade7&upload_id=ADPycds-_Ow7aqcpbG4XguXSVAgd_2fy-XiDA2qm-It9PCwBlZhF4e2bfOAQzEmRJ4T_l6jU6LfYdfrKa_KlFFBOyx3PjYzrxQ ``` You can then upload your Post or User IDs to this location by following step two onwards, [from the quick start guide](/x-api/compliance/batch-compliance#getting-started-with-the-batch-compliance-endpoints). Because of their technical complexity, resumable uploads are best used in conjunction with code. This guide will use Node.js with the [needle request library](https://www.npmjs.com/package/needle). ### Install the dependencies Before proceeding, you should have a Node.js environment installed; you can obtain Node.js from its website. Once installed, Node.js will contain a utility called npm; make sure both Node and npm are installed by calling the following command, and ensure it doesn’t result in an error. `$ npm -v 6.4.1` A version number similar to this signifies your environment is ready (note that your version number may differ). We will use npm to install the upload library. Run this command: `$ npm install -g needle` You’re all set; there is no additional configuration required. #### Request a resumable destination When creating a new job, set the resumable parameter to true so you can get a destination that supports a resumable upload. In the response payload, you will receive an upload\_url value. ``` "upload_url":\ "https://storage.googleapis.com/compliance_tweet_ids/customer_test_object_12950882_GlYjiE?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=193969463581-compute%40developer.gserviceaccount.com%2F20200618%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20200618T184154Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=b7bdcf32479b08715be91ed47b06471b8acdcdb319f8e4f423bf3a3056dfa03ed83e47446f33338e292967a15c08fa5ba34395edaf057a2ac975b88e710ca994adb023a9e1673a7c58ce2fa0d73537f72812af78e92b708dfe6b907a7d75bd0f6cfa61fec867e80ac83ced0725d1ee59787c9dbca50d41f7b0f513dad63a7564136b1a70042a2ec6ba6b697cbe480a4405362f7a08255a5e8205aa7baa562f99e6a092f0420f33d67ffaeb132f877fbaf16c969630b5f173e8a3f31c473707241fa4e28f4bed13fb2ea01d3af1c449321a2e6ee9ec1e331b447cabcfc6f9d1f99f564d180f0cc1d28ea54972c996102c67c6501c6c16a00c13d17756f960e0e1" ``` #### Prepare the code to upload a file By default, the library will create a new upload destination by accepting an upload location (called bucket) and the name of the file you wish to upload. Because the batch compliance endpoints create their own destination, we will need to tell the library we already have a location ready to accept our upload.  We will need to pass this value to the upload library, along with the name of the file containing the data to upload. Create a file, and name it ***twitter-upload.js***. Add the following code: ``` const needle = require('needle'); const fs = require('fs'); const path = require('path'); const [, scriptName, filename, uploadURL] = process.argv; if (!filename || !uploadURL) { console.error(`Usage: node ${path.basename(scriptName)} filename upload_url`); process.exit(-1); } async function uploadFile(file, url) { // rangeEnd is the index of the last byte in the file, i.e. number of bytes in file const rangeEnd = (await fs.promises.stat(file)).size; let options = { headers: { 'Content-Range': `bytes */${rangeEnd}`, }, }; const response = await needle('put', url, null, options); switch (response.statusCode) { case 200: case 201: console.log('Upload complete'); return; case 308: return resumeUpload(response, file, url); default: console.log('Got unexpected response code: ', response.statusCode); return; } } async function resumeUpload(response, file, url) { console.log('Upload not completed, resuming'); if (response.headers.range) { let resumeOffset = Number(response.headers.range.split('-')[1]) + 1; let options = { headers: { 'Content-Range': `bytes ${resumeOffset}-${rangeEnd-1}/${rangeEnd}`, 'Content-Length': `${rangeEnd-resumeOffset}`, }, }; let readStream = fs.createReadStream(file, {start: resumeOffset}); return needle('put', url, readStream, options); } else { console.log('Initiating upload'); let options = { headers: { 'Content-Type': 'text/plain' } }; let readStream = fs.createReadStream(file); return needle('put', url, readStream, options); } } // Request resumable session URL async function requestResumableSession(url) { const options = { headers: { 'Content-Type': 'text/plain', 'Content-Length': '0', 'x-goog-resumable': 'start', }, }; const res = await needle('post', url, null, options); if (res.statusCode === 201) { const resumableSessionURL = res.headers['location']; console.log('Starting upload to: ', resumableSessionURL); await uploadFile(filename, resumableSessionURL); } else { console.log('Failed to create resumable session URI'); } } requestResumableSession(uploadURL).then(result => console.log('Upload complete')); ``` Save the file wherever it makes the most sense. Next, in your command line, invoke the script and pass two parameters: 1. The first will be the location of the file (with the Post or User IDs) that you wish to upload. 2. The second will be the upload URL we received from the compliance endpoint response. Ensure the URL is surrounded in double-quotes, and do the same for your file name if it contains spaces or other characters: ``` node twitter-upload.js compliance_upload.txt\ "https://storage.googleapis.com/compliance_tweet_ids/customer_test_object_12950882_GlYjiE?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=193969463581-compute%40developer.gserviceaccount.com%2F20200618%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20200618T184154Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=b7bdcf32479b08715be91ed47b06471b8acdcdb319f8e4f423bf3a3056dfa03ed83e47446f33338e292967a15c08fa5ba34395edaf057a2ac975b88e710ca994adb023a9e1673a7c58ce2fa0d73537f72812af78e92b708dfe6b907a7d75bd0f6cfa61fec867e80ac83ced0725d1ee59787c9dbca50d41f7b0f513dad63a7564136b1a70042a2ec6ba6b697cbe480a4405362f7a08255a5e8205aa7baa562f99e6a092f0420f33d67ffaeb132f877fbaf16c969630b5f173e8a3f31c473707241fa4e28f4bed13fb2ea01d3af1c449321a2e6ee9ec1e331b447cabcfc6f9d1f99f564d180f0cc1d28ea54972c996102c67c6501c6c16a00c13d17756f960e0e1" ``` You will see output similar to this: `Starting upload to: https://storage.googleapis.com/twttr-tweet-compliance/ Upload not completed, resuming Initiating upload` You can pause the upload at any time by pressing Ctrl + C or closing your command line. You will be able to resume the upload from where you left off when you invoke the same command at a later stage. Once the file has been completely uploaded, you will see the following message: `Upload complete` At this point, you will be able to use the [compliance status endpoint](/x-api/compliance/get-compliance-job) to check on the status of your compliance job, and you will be able to download the compliance result when complete. # Batch Compliance Source: https://docs.x.com/x-api/compliance/batch-compliance/introduction Check compliance status for Posts and users in bulk The Batch Compliance endpoints let you upload datasets of Post IDs or user IDs and receive their current compliance status. Use this to identify deleted Posts, suspended accounts, and other compliance events. ## Overview Check status of Posts in bulk Check status of users in bulk Submit jobs and retrieve results later Process millions of IDs *** ## How it works 1. **Create a job** — Specify the type (Posts or users) and upload URL 2. **Upload IDs** — Upload your dataset to the provided URL 3. **Wait for processing** — Job processes asynchronously 4. **Download results** — Get compliance status for each ID *** ## Endpoints | Method | Endpoint | Description | | :----- | :--------------------------------------------------------------------- | :-------------------------- | | POST | [`/2/compliance/jobs`](/x-api/compliance/create-compliance-job) | Create a new compliance job | | GET | [`/2/compliance/jobs/:id`](/x-api/compliance/get-compliance-job-by-id) | Get job status | | GET | [`/2/compliance/jobs`](/x-api/compliance/get-compliance-jobs) | List all jobs | *** ## Job types | Type | Description | | :------- | :--------------------------- | | `tweets` | Check Post compliance status | | `users` | Check user compliance status | *** ## Compliance events ### Post events | Event | Description | | :---------- | :--------------------------- | | `deleted` | Post was deleted by user | | `bounced` | Post failed compliance check | | `protected` | Account became protected | | `suspended` | Account was suspended | | `scrub_geo` | Geo data was removed | ### User events | Event | Description | | :------------ | :----------------------- | | `deleted` | Account was deleted | | `suspended` | Account was suspended | | `protected` | Account became protected | | `deactivated` | Account was deactivated | *** ## Example: Create a job ```bash theme={null} curl -X POST "https://api.x.com/2/compliance/jobs" \ -H "Authorization: Bearer $BEARER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "tweets", "name": "my-compliance-job" }' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [Bearer Token](/resources/fundamentals/authentication) Create your first compliance job Key concepts and best practices Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/compliance/batch-compliance/quickstart Create and run a batch compliance job This guide walks you through creating a batch compliance job to check the compliance status of Posts or users. **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 *** Create a new compliance job specifying the type (tweets or users): ```bash theme={null} curl -X POST "https://api.x.com/2/compliance/jobs" \ -H "Authorization: Bearer $BEARER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "tweets", "name": "my-compliance-job" }' ``` ```python theme={null} import requests bearer_token = "YOUR_BEARER_TOKEN" url = "https://api.x.com/2/compliance/jobs" headers = { "Authorization": f"Bearer {bearer_token}", "Content-Type": "application/json" } payload = { "type": "tweets", "name": "my-compliance-job" } response = requests.post(url, headers=headers, json=payload) print(response.json()) ``` **Response:** ```json theme={null} { "data": { "id": "1234567890", "type": "tweets", "name": "my-compliance-job", "status": "created", "upload_url": "https://storage.googleapis.com/...", "download_url": "https://storage.googleapis.com/...", "created_at": "2024-01-15T10:00:00.000Z" } } ``` Save the `upload_url` and `download_url` for the next steps. Create a text file with one ID per line: ``` 1234567890 1234567891 1234567892 1234567893 ``` Save as `ids.txt`. Upload the file to the provided `upload_url`: ```bash theme={null} curl -X PUT "UPLOAD_URL_FROM_RESPONSE" \ -H "Content-Type: text/plain" \ --data-binary @ids.txt ``` Poll the job status until it's complete: ```bash theme={null} curl "https://api.x.com/2/compliance/jobs/1234567890" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` **Job statuses:** | Status | Description | | :------------ | :---------------------------- | | `created` | Job created, awaiting upload | | `in_progress` | Processing data | | `complete` | Results ready for download | | `failed` | Job failed | | `expired` | Job expired before completion | Once status is `complete`, download from the `download_url`: ```bash theme={null} curl "DOWNLOAD_URL_FROM_RESPONSE" -o results.json ``` **Result format** (one JSON object per line): ```json theme={null} {"id": "1234567890", "action": "delete", "created_at": "2024-01-10T12:00:00.000Z", "redacted_at": "2024-01-12T08:30:00.000Z", "reason": "deleted"} {"id": "1234567891", "action": "delete", "created_at": "2024-01-10T12:00:00.000Z", "redacted_at": "2024-01-13T14:20:00.000Z", "reason": "suspended"} ``` Only IDs with compliance events appear in the results. IDs not in the results are still valid. *** ## Compliance actions | Action | Reason | Description | | :------- | :---------- | :--------------------------- | | `delete` | `deleted` | Post was deleted | | `delete` | `bounced` | Post failed compliance check | | `delete` | `protected` | Account became protected | | `delete` | `suspended` | Account was suspended | | `delete` | `scrub_geo` | Geo data was removed | | Action | Reason | Description | | :------- | :------------ | :----------------------- | | `delete` | `deleted` | Account was deleted | | `delete` | `suspended` | Account was suspended | | `delete` | `protected` | Account became protected | | `delete` | `deactivated` | Account was deactivated | *** ## List all jobs Get all compliance jobs for your App: ```bash theme={null} curl "https://api.x.com/2/compliance/jobs?type=tweets" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Next steps Key concepts and best practices Real-time compliance events Full endpoint documentation # Create Compliance Job Source: https://docs.x.com/x-api/compliance/create-compliance-job post /2/compliance/jobs Creates a new Compliance Job for the specified job type. # Get Compliance Job by ID Source: https://docs.x.com/x-api/compliance/get-compliance-job-by-id get /2/compliance/jobs/{id} Retrieves details of a specific Compliance Job by its ID. # Get Compliance Jobs Source: https://docs.x.com/x-api/compliance/get-compliance-jobs get /2/compliance/jobs Retrieves a list of Compliance Jobs filtered by job type and optional status. # Introduction Source: https://docs.x.com/x-api/compliance/streams/introduction 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. 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 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: **Post compliance stream:** * **delete** - indicates that the Post was deleted. * **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. * **drop** - indicates that the Post should be removed from public view. * **undrop** - indicates that the Post may be displayed again and treated as public. **User compliance stream:** * **user\_delete** - indicates that the User account was deleted * **user\_undelete** - indicates that the User account was undeleted * **user\_protect** - indicates that the User account became private * **user\_unprotect** - indicates that the User account became public * **user\_withheld** - indicates that the User account has been withheld from one or more countries.  * **user\_suspend** - indicates that the User account was suspended * **user\_unsuspend** - indicates that the User account was unsuspended * **user\_profile\_modification** - indicates that the User profile has been updated. This includes an updated description, name, location, and URL. **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/developer-apps).  Learn more about getting access to the X API v2 endpoints in our [getting started guide](/x-api/getting-started/getting-access).
*** ## Streaming fundamentals Best practices for streaming clients Reconnect gracefully Handle high throughput Build resilient applications # Get Connection History Source: https://docs.x.com/x-api/connections/get-connection-history get /2/connections Returns active and historical streaming connections with disconnect reasons for the authenticated application. # Stream Connections Source: https://docs.x.com/x-api/connections/introduction View and manage your streaming connections for X API v2 The Stream Connections endpoints let you retrieve the history of your application's streaming connections and terminate active or multiple connections. Use these to monitor connection status, resolve issues like duplicate connections, and manage streaming sessions for endpoints like Filtered Stream and Sampled Stream. These endpoints require [Bearer Token authentication](/fundamentals/authentication/oauth-2-0/application-only) and are available for apps with access to streaming endpoints. ## Overview View active and past connections End all active streaming connections Stop connections for specific streams Kill specific connections by UUID *** ## Endpoints | Method | Endpoint | Description | | :----- | :------------------------------------------------------------------------------------- | :---------------------------------------------- | | GET | [`/2/connections`](/x-api/connections/get-connection-history) | Get active and historical streaming connections | | DELETE | [`/2/connections/all`](/x-api/connections/terminate-all-connections) | Terminate all active connections | | DELETE | [`/2/connections/{endpoint_id}`](/x-api/connections/terminate-connections-by-endpoint) | Terminate connections for a specific endpoint | | DELETE | [`/2/connections`](/x-api/connections/terminate-multiple-connections) | Terminate multiple connections by UUIDs | *** ## Use cases * **Troubleshoot disconnections** — Check history to diagnose issues ([see handling disconnections](/x-api/fundamentals/handling-disconnections)) * **Enforce connection limits** — Terminate duplicate or excess connections * **Clean up sessions** — Stop all streams before maintenance or redeploy * **Monitor usage** — Track connection patterns across endpoints like filtered\_stream, sample\_stream *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/fundamentals/developer-apps) in the Developer Console with streaming access * Your App's [Bearer Token](/fundamentals/authentication/oauth-2-0/application-only) Full endpoint documentation Learn about streaming # Terminate all connections Source: https://docs.x.com/x-api/connections/terminate-all-connections delete /2/connections/all Terminates all active streaming connections for the authenticated application. # Terminate connections by endpoint Source: https://docs.x.com/x-api/connections/terminate-connections-by-endpoint delete /2/connections/{endpoint_id} Terminates all streaming connections for a specific endpoint ID for the authenticated application. # Terminate multiple connections Source: https://docs.x.com/x-api/connections/terminate-multiple-connections delete /2/connections Terminates multiple streaming connections by their UUIDs for the authenticated application. # Add List member Source: https://docs.x.com/x-api/lists/add-list-member post /2/lists/{id}/members Adds a User to a specific List by its ID. # Create List Source: https://docs.x.com/x-api/lists/create-list post /2/lists Creates a new List for the authenticated user. # Delete List Source: https://docs.x.com/x-api/lists/delete-list delete /2/lists/{id} Deletes a specific List owned by the authenticated user by its ID. # Get List by ID Source: https://docs.x.com/x-api/lists/get-list-by-id get /2/lists/{id} Retrieves details of a specific List by its ID. # Get List followers Source: https://docs.x.com/x-api/lists/get-list-followers get /2/lists/{id}/followers Retrieves a list of Users who follow a specific List by its ID. # Get List members Source: https://docs.x.com/x-api/lists/get-list-members get /2/lists/{id}/members Retrieves a list of Users who are members of a specific List by its ID. # Get List Posts Source: https://docs.x.com/x-api/lists/get-list-posts get /2/lists/{id}/tweets Retrieves a list of Posts associated with a specific List by its ID. # Integration Guide Source: https://docs.x.com/x-api/lists/list-lookup/integrate Key concepts and best practices for integrating List lookup This guide covers the key concepts you need to integrate the List lookup endpoints into your application. *** ## Authentication List lookup endpoints support multiple authentication methods: | Method | Best for | Access to private Lists? | | :----------------------------------------------------------------------------------------------------------------------------- | :------------------ | :----------------------- | | [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0) | Public List data | No | | [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | User-facing apps | Yes (owned/followed) | | [OAuth 1.0a User Context](/resources/fundamentals/authentication) | Legacy integrations | Yes (owned/followed) | ### Example request ```bash cURL theme={null} curl "https://api.x.com/2/lists/84839422?\ list.fields=description,member_count,follower_count,private" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a List by ID response = client.lists.get( list_id="84839422", list_fields=["description", "member_count", "follower_count", "private"] ) print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.lists.get("84839422", { listFields: ["description", "member_count", "follower_count", "private"], }); console.log(response.data); ``` *** ## Endpoints overview | Method | Endpoint | Description | | :----- | :--------------------------------------------------------- | :------------------------ | | GET | [`/2/lists/:id`](/x-api/lists/get-list) | Get List by ID | | GET | [`/2/users/:id/owned_lists`](/x-api/users/get-owned-lists) | Get Lists owned by a user | *** ## Fields and expansions ### Default response ```json theme={null} { "data": { "id": "84839422", "name": "Tech News" } } ``` ### Available fields | Field | Description | | :--------------- | :---------------------- | | `created_at` | List creation timestamp | | `description` | List description | | `follower_count` | Number of followers | | `member_count` | Number of members | | `owner_id` | Owner's user ID | | `private` | Whether List is private | | Field | Description | | :------------------ | :-------------------------- | | `username` | Owner's @handle | | `name` | Owner's display name | | `verified` | Owner's verification status | | `profile_image_url` | Owner's avatar URL | ### Example with expansions ```bash cURL theme={null} curl "https://api.x.com/2/lists/84839422?\ list.fields=description,member_count,follower_count,owner_id&\ expansions=owner_id&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get List with owner expansion response = client.lists.get( list_id="84839422", list_fields=["description", "member_count", "follower_count", "owner_id"], expansions=["owner_id"], user_fields=["username", "verified"] ) print(response.data) print(response.includes) # Contains owner user object ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); const response = await client.lists.get("84839422", { listFields: ["description", "member_count", "follower_count", "owner_id"], expansions: ["owner_id"], userFields: ["username", "verified"], }); console.log(response.data); console.log(response.includes); // Contains owner user object ``` ### Response with expansion ```json theme={null} { "data": { "id": "84839422", "name": "Tech News", "description": "Top tech journalists", "member_count": 50, "follower_count": 1250, "owner_id": "2244994945" }, "includes": { "users": [ { "id": "2244994945", "username": "XDevelopers", "verified": true } ] } } ``` Learn more about customizing responses *** ## Pagination When retrieving owned Lists, results are paginated: ```bash cURL theme={null} # First request curl "https://api.x.com/2/users/123/owned_lists?max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" # Subsequent request with pagination token curl "https://api.x.com/2/users/123/owned_lists?max_results=100&pagination_token=NEXT_TOKEN" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # The SDK handles pagination automatically all_lists = [] for page in client.lists.get_user_owned_lists(user_id="123", max_results=100): if page.data: all_lists.extend(page.data) print(f"Found {len(all_lists)} lists") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); async function getAllOwnedLists(userId) { const allLists = []; // The SDK handles pagination automatically const paginator = client.lists.getUserOwnedLists(userId, { maxResults: 100 }); for await (const page of paginator) { if (page.data) { allLists.push(...page.data); } } return allLists; } // Usage const lists = await getAllOwnedLists("123"); console.log(`Found ${lists.length} lists`); ``` Learn more about pagination *** ## Private Lists * Private Lists are only visible to the owner * You must authenticate as the owner to retrieve private List details * The `private` field indicates whether a List is private *** ## Error handling | Status | Error | Solution | | :----- | :---------------- | :-------------------- | | 400 | Invalid request | Check List ID format | | 401 | Unauthorized | Verify authentication | | 403 | Forbidden | List may be private | | 404 | Not Found | List doesn't exist | | 429 | Too Many Requests | Wait and retry | *** ## Next steps Make your first List lookup request Get Posts from a List Full endpoint documentation Working code examples # List Lookup Source: https://docs.x.com/x-api/lists/list-lookup/introduction Retrieve List details by ID or get Lists owned by a user The List lookup endpoints let you retrieve information about Lists. Look up a specific List by ID or get all Lists owned by a user. ## Overview Get details for a specific List Get all Lists owned by a user *** ## Endpoints | Method | Endpoint | Description | | :----- | :--------------------------------------------------------- | :------------------------ | | GET | [`/2/lists/:id`](/x-api/lists/get-list-by-id) | Get List by ID | | GET | [`/2/users/:id/owned_lists`](/x-api/users/get-owned-lists) | Get Lists owned by a user | *** ## Response fields By default, the response includes `id` and `name`. Request additional fields: | Field | Description | | :--------------- | :---------------------- | | `description` | List description | | `owner_id` | Owner's user ID | | `private` | Whether List is private | | `follower_count` | Number of followers | | `member_count` | Number of members | | `created_at` | List creation date | ### Example request ```bash theme={null} curl "https://api.x.com/2/lists/1234567890?\ list.fields=description,owner_id,member_count,follower_count" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Make your first List lookup request Key concepts and best practices Full endpoint documentation Working code examples # Quickstart Source: https://docs.x.com/x-api/lists/list-lookup/quickstart Look up Lists by ID or owner This guide walks you through looking up List information using the List lookup endpoints. **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 *** ## Get a List by ID Retrieve details for a specific List: ```bash cURL theme={null} curl "https://api.x.com/2/lists/1234567890?\ list.fields=description,owner_id,member_count,follower_count,private,created_at" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a List by ID response = client.lists.get( "1234567890", list_fields=["description", "owner_id", "member_count", "follower_count", "private", "created_at"] ) print(f"List: {response.data.name}") print(f"Members: {response.data.member_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get a List by ID const response = await client.lists.get("1234567890", { listFields: ["description", "owner_id", "member_count", "follower_count", "private", "created_at"], }); console.log(`List: ${response.data?.name}`); console.log(`Members: ${response.data?.member_count}`); ``` ### Response ```json theme={null} { "data": { "id": "1234567890", "name": "Tech News", "description": "Top tech journalists and publications", "owner_id": "2244994945", "private": false, "member_count": 50, "follower_count": 1250, "created_at": "2023-01-15T10:00:00.000Z" } } ``` *** ## Get Lists owned by a user Retrieve all Lists owned by a specific user: ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/owned_lists?\ list.fields=description,member_count,follower_count&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get Lists owned by a user with pagination for page in client.lists.get_owned_lists( "2244994945", list_fields=["description", "member_count", "follower_count"], max_results=100 ): for lst in page.data: print(f"{lst.name} - {lst.member_count} members") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get Lists owned by a user with pagination const paginator = client.lists.getOwnedLists("2244994945", { listFields: ["description", "member_count", "follower_count"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((lst) => { console.log(`${lst.name} - ${lst.member_count} members`); }); } ``` ### Response ```json theme={null} { "data": [ { "id": "1234567890", "name": "Tech News", "description": "Top tech journalists", "member_count": 50, "follower_count": 1250 }, { "id": "9876543210", "name": "Developer Tools", "description": "Useful tools for developers", "member_count": 25, "follower_count": 500 } ], "meta": { "result_count": 2 } } ``` *** ## Include owner information Expand the owner's user data: ```bash cURL theme={null} curl "https://api.x.com/2/lists/1234567890?\ list.fields=description,owner_id&\ expansions=owner_id&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get List with owner info response = client.lists.get( "1234567890", list_fields=["description", "owner_id"], expansions=["owner_id"], user_fields=["username", "verified"] ) print(f"List: {response.data.name}") # Owner info is in response.includes.users ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get List with owner info const response = await client.lists.get("1234567890", { listFields: ["description", "owner_id"], expansions: ["owner_id"], userFields: ["username", "verified"], }); console.log(`List: ${response.data?.name}`); // Owner info is in response.includes?.users ``` ### Response with expansion ```json theme={null} { "data": { "id": "1234567890", "name": "Tech News", "description": "Top tech journalists", "owner_id": "2244994945" }, "includes": { "users": [ { "id": "2244994945", "username": "XDevelopers", "verified": true } ] } } ``` *** ## Available fields | Field | Description | | :--------------- | :---------------------- | | `description` | List description | | `owner_id` | Owner's user ID | | `private` | Whether List is private | | `member_count` | Number of members | | `follower_count` | Number of followers | | `created_at` | List creation date | *** ## Next steps Get Posts from a List Get List members Create and update Lists Full endpoint documentation # Integration guide Source: https://docs.x.com/x-api/lists/list-members/integrate This page covers tools and key concepts for integrating the List members endpoints. *** ## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: ### Postman Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. ### Code samples Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). ### Third-party libraries Take advantage of one of our communities' [third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. *** ## Key concepts ### Authentication All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, OAuth 2.0 Authorization Code with PKCE, or App only to authenticate your requests for the Lists **lookup** endpoints. However, you must authenticate with OAuth 1.0a User Context or OAuth 2.0 for the **manage** Lists endpoints. [OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/tools-and-libraries), use a tool like Postman, or use either OAuth 2.0 or App only to authenticate your requests. [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application's scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. To enable OAuth 2.0 in your App, you must enable it in your's App's authentication settings found in the App settings section of the Developer Console. [App only](/resources/fundamentals/authentication#oauth-2-0) just requires that you pass an [App only Access Token](/resources/fundamentals/authentication#bearer-token-also-known-as-app-only) with your request. You can either generate an App only Access Token directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. ### Developer Console, Projects, and developer Apps To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. ### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user. Lookup (GET) endpoints are rate limited at both the App-level and the user-level; while manage (POST/DELETE) endpoints are limited at the user-level. The app rate limit means that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by using either the API Key and API Secret Key, or the App only Access Token). The user rate limit means that the authenticated user that you are making the request on behalf of can only perform a List lookup a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. | Endpoint | HTTP method | Rate limit | | :----------------------------- | :---------- | :-------------------------- | | /2/lists/:id/members | GET | 900 requests per 15 minutes | | /2/users/:id/list\_memberships | GET | 75 requests per 15 minutes | | /2/lists/:id/members | POST | 300 requests per 15 minutes | | /2/lists/:id/members/:user\_id | DELETE | 300 requests per 15 minutes | ### Fields and expansions The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called `fields` and `expansions`. The `expansions` parameter allows you to expand objects referenced in the payload. For example, looking up List members allows you to pull the following [expansions](/x-api/fundamentals/expansions): * `pinned_tweet_id` The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. List members lookup delivers user objects primarily. By default, the user object returns id, name, and username fields. To receive additional fields such as `user.created_at` or `user.description`, you will have to specifically request those using a user.fields parameter. We've added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions). The chart below shows the field and expansions available for each lookup endpoint: | Endpoint | Fields | Expansions | | :----------------------------- | :---------------------------- | :---------------- | | /2/lists/:id/members | `user.fields`, `tweet.fields` | `pinned_tweet_id` | | /2/users/:id/list\_memberships | `list.fields`, `user.fields` | `owner_id` | ### Pagination Looking up membership/members can return a lot of data. To ensure we are returning consistent, high-performing results at any given time, we use pagination. Pagination is a feature in X API v2 endpoints that return more results than can be returned in a single response. When that happens, the data is returned in a series of 'pages'. Learn more about how to [paginate through results.](/x-api/fundamentals/pagination) # List Members Source: https://docs.x.com/x-api/lists/list-members/introduction View, add, and remove members from Lists The List Members endpoints let you view List members, add members to your Lists, and remove them. You can also see which Lists a user is a member of. ## Overview Get all members of a List Add a user to your List Remove a user from your List See Lists a user is on *** ## Endpoints ### List members lookup | Method | Endpoint | Description | | :----- | :------------------------------------------------------------------- | :--------------------- | | GET | [`/2/lists/:id/members`](/x-api/lists/get-list-members) | Get members of a List | | GET | [`/2/users/:id/list_memberships`](/x-api/users/get-list-memberships) | Get Lists a user is on | ### Manage List members | Method | Endpoint | Description | | :----- | :----------------------------------------------------------------- | :-------------- | | POST | [`/2/lists/:id/members`](/x-api/lists/add-list-member) | Add a member | | DELETE | [`/2/lists/:id/members/:user_id`](/x-api/lists/remove-list-member) | Remove a member | *** ## Example: Get List members ```bash theme={null} curl "https://api.x.com/2/lists/1234567890/members?\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example: Add a member ```bash theme={null} curl -X POST "https://api.x.com/2/lists/1234567890/members" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"user_id": "9876543210"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Get members of a List Add and remove members Key concepts and best practices Full endpoint documentation # List Members Lookup Source: https://docs.x.com/x-api/lists/list-members/quickstart/list-members-lookup Get members of a List This guide walks you through retrieving members of a List. **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 *** ## Get List members You can find the List ID in the URL when viewing a List: ``` https://x.com/i/lists/84839422 └── This is the List ID ``` ```bash cURL theme={null} curl "https://api.x.com/2/lists/84839422/members?\ user.fields=created_at,username,verified&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get List members with pagination for page in client.lists.get_members( "84839422", user_fields=["created_at", "username", "verified"], max_results=100 ): for user in page.data: print(f"{user.username} - Joined: {user.created_at}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get List members with pagination const paginator = client.lists.getMembers("84839422", { userFields: ["created_at", "username", "verified"], maxResults: 100, }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(`${user.username} - Joined: ${user.created_at}`); }); } ``` ```json theme={null} { "data": [ { "id": "1319036828964454402", "name": "Birdwatch", "username": "birdwatch", "created_at": "2020-10-21T22:04:47.000Z", "verified": true }, { "id": "1065249714214457345", "name": "Spaces", "username": "TwitterSpaces", "created_at": "2018-11-21T14:24:58.000Z", "verified": true } ], "meta": { "result_count": 2, "next_token": "5349804505549807616" } } ``` *** ## Include additional data Use expansions to get related data like pinned Posts: ```bash cURL theme={null} curl "https://api.x.com/2/lists/84839422/members?\ user.fields=created_at&\ expansions=pinned_tweet_id&\ tweet.fields=created_at" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get List members with expansions for page in client.lists.get_members( "84839422", user_fields=["created_at"], expansions=["pinned_tweet_id"], tweet_fields=["created_at"] ): for user in page.data: print(f"{user.username}") # Pinned Posts are in page.includes.tweets ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get List members with expansions const paginator = client.lists.getMembers("84839422", { userFields: ["created_at"], expansions: ["pinned_tweet_id"], tweetFields: ["created_at"], }); for await (const page of paginator) { page.data?.forEach((user) => { console.log(user.username); }); // Pinned Posts are in page.includes?.tweets } ``` *** ## Next steps Add and remove members Get List details Full endpoint documentation # Manage List Members Source: https://docs.x.com/x-api/lists/list-members/quickstart/manage-list-members Add and remove members from a List This guide walks you through adding and removing members from a List. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) * A List that you own *** ## Add a member to a List You need the ID of your List and the user ID of the person you want to add. Find user IDs using the [user lookup endpoint](/x-api/users/lookup/introduction). ```bash cURL theme={null} curl -X POST "https://api.x.com/2/lists/1441162269824405510/members" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"user_id": "2244994945"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Add a member to a List response = client.lists.add_member( list_id="1441162269824405510", user_id="2244994945" ) print(f"Is member: {response.data.is_member}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Add a member to a List const response = await client.lists.addMember("1441162269824405510", { userId: "2244994945", }); console.log(`Is member: ${response.data?.is_member}`); ``` ```json theme={null} { "data": { "is_member": true } } ``` *** ## Remove a member from a List ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/lists/1441162269824405510/members/2244994945" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Remove a member from a List response = client.lists.remove_member( list_id="1441162269824405510", user_id="2244994945" ) print(f"Is member: {response.data.is_member}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Remove a member from a List const response = await client.lists.removeMember( "1441162269824405510", "2244994945" ); console.log(`Is member: ${response.data?.is_member}`); ``` **Response:** ```json theme={null} { "data": { "is_member": false } } ``` *** ## Important notes * You can only manage members of Lists you own * Adding a user to a List does not require their permission * Users can see which public Lists they've been added to *** ## Next steps Get List members Create and update Lists Full endpoint documentation # List Members Overview Source: https://docs.x.com/x-api/lists/list-members/quickstart/overview Get started with List members endpoints The List members endpoints let you look up members of a List and manage List membership. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * Your App's Bearer Token (for lookups) * User Access Token (for managing members) *** ## Available endpoints Get members of a List Add and remove members *** ## Authentication | Operation | Authentication | | :----------------- | :------------------------------------------ | | Look up members | Bearer Token, OAuth 1.0a, or OAuth 2.0 PKCE | | Add/remove members | OAuth 1.0a or OAuth 2.0 PKCE | *** ## Quick example ```bash cURL theme={null} # Get List members curl "https://api.x.com/2/lists/84839422/members" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get List members for page in client.lists.get_members("84839422"): for user in page.data: print(f"{user.username}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get List members const paginator = client.lists.getMembers("84839422"); for await (const page of paginator) { page.data?.forEach((user) => { console.log(user.username); }); } ``` *** ## Next steps Get List details Get Posts from a List # Integration guide Source: https://docs.x.com/x-api/lists/list-tweets/integrate This page covers tools and key concepts for integrating the List Posts lookup endpoint. *** ## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: ### Postman Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. ### Code samples Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). ### Third-party libraries Take advantage of one of our communities' [third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. *** ## Key concepts ### Authentication All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use either OAuth 1.0a User Context, App only, or OAuth 2.0 Authorization Code with PKCE to authenticate your requests to this endpoint. [OAuth 1.0a User Context](/resources/fundamentals/authentication#oauth-1-0a-2), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/tools-and-libraries), use a tool like Postman, or use either OAuth 2.0 or App only to authenticate your requests. [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) allows for greater control over an application's scope, and authorization flows across multiple devices. OAuth 2.0 allows you to pick specific fine-grained scopes which give you specific permissions on behalf of a user. To enable OAuth 2.0 in your App, you must enable it in your's App's authentication settings found in the App settings section of the Developer Console. [App only](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) just requires that you pass an [App only Access Token](/resources/fundamentals/authentication#app-only-authentication-and-oauth-2-0-bearer-token) with your request. You can either generate an App only Access Token directly within a developer App, or generate one using the [POST oauth2/token](/resources/fundamentals/authentication#post-oauth2-token) endpoint. ### Developer Console, Projects, and developer Apps To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. ### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](https://developer.x.com/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user. This endpoint is rate limited at both the App-level and the user-level. The app rate limit means that you, the developer, can only make a certain number of requests to this endpoint over a given period of time from any given App (assumed by using either the API Key and API Secret Key, or the Bearer Token). The user rate limit means that the authenticated user that you are making the request on behalf of can only perform a List Post lookup a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. | Endpoint | HTTP method | Rate limit | | :------------------ | :---------- | :-------------------------- | | /2/lists/:id/tweets | GET | 900 requests per 15 minutes | ### Fields and expansions The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called `fields` and `expansions`. The `expansions` parameter allows you to expand objects referenced in the payload. For example, looking up List Posts allows you to pull the following [expansions](/x-api/fundamentals/expansions): * `author_id` The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. This endpoint delivers Post objects primarily. By default, the Post object returns the `id`, and `text` fields. To receive additional fields such as `tweet.created_at` or `tweet.lang`, you will have to specifically request those using a fields parameter. We've added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). The chart below shows the field and expansions available for the lookup endpoint: | Endpoint | Fields | Expansions | | :------------------ | :---------------------------- | :---------- | | /2/lists/:id/tweets | `tweet.fields`, `user.fields` | `author_id` | ### Pagination Looking up List Posts can return a lot of data. To ensure we are returning consistent, high-performing results at any given time, we use pagination. Pagination is a feature in X API v2 endpoints that return more results than can be returned in a single response. When that happens, the data is returned in a series of 'pages'. Learn more about how to [paginate through results.](/x-api/fundamentals/pagination) # List Posts Source: https://docs.x.com/x-api/lists/list-tweets/introduction Retrieve Posts from a List's timeline The List Posts endpoint lets you retrieve Posts from a List's timeline. Get the latest Posts from all members of a List. ## Overview Get Posts from List members Access your curated content feeds *** ## Endpoint | Method | Endpoint | Description | | :----- | :--------------------------------------------------- | :-------------------- | | GET | [`/2/lists/:id/tweets`](/x-api/lists/get-list-posts) | Get Posts from a List | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/lists/1234567890/tweets?\ tweet.fields=created_at,author_id,public_metrics&\ expansions=author_id&\ user.fields=username&\ max_results=100" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Get Posts from a List Key concepts and best practices Get List details Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/lists/list-tweets/quickstart Get Posts from a List timeline This guide walks you through retrieving Posts from a List timeline. **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 *** You can find a List ID in the URL when viewing a List on x.com: ``` https://x.com/i/lists/84839422 └── This is the List ID ``` ```bash cURL theme={null} curl "https://api.x.com/2/lists/84839422/tweets?\ tweet.fields=created_at,public_metrics,author_id&\ expansions=author_id&\ user.fields=username,verified&\ max_results=10" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get Posts from a List with pagination for page in client.lists.get_tweets( "84839422", tweet_fields=["created_at", "public_metrics", "author_id"], expansions=["author_id"], user_fields=["username", "verified"], max_results=10 ): for post in page.data: print(f"{post.text[:50]}... - Likes: {post.public_metrics.like_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get Posts from a List with pagination const paginator = client.lists.getTweets("84839422", { tweetFields: ["created_at", "public_metrics", "author_id"], expansions: ["author_id"], userFields: ["username", "verified"], maxResults: 10, }); for await (const page of paginator) { page.data?.forEach((post) => { console.log(`${post.text?.slice(0, 50)}... - Likes: ${post.public_metrics?.like_count}`); }); } ``` ```json theme={null} { "data": [ { "id": "1458172421115101189", "text": "Check out our latest announcement...", "author_id": "4172587277", "created_at": "2024-01-15T10:30:00.000Z", "public_metrics": { "retweet_count": 42, "reply_count": 5, "like_count": 156, "quote_count": 3 }, "edit_history_tweet_ids": ["1458172421115101189"] } ], "includes": { "users": [ { "id": "4172587277", "username": "TechNews", "verified": true } ] }, "meta": { "result_count": 1, "next_token": "7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" } } ``` The SDKs handle pagination automatically. For cURL, use the `next_token` from the response to get more Posts: ```bash theme={null} curl "https://api.x.com/2/lists/84839422/tweets?\ max_results=10&\ pagination_token=7140dibdnow9c7btw3z2vwioavpvutgzrzm9icis4ndix" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` This endpoint returns up to 800 of the most recent Posts from the List. *** ## Next steps Get List details Get List members Key concepts and best practices Full endpoint documentation # Integration guide Source: https://docs.x.com/x-api/lists/manage-lists/integrate This page covers tools and key concepts for integrating the Lists endpoints. *** ## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: #### Postman Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. #### Code samples Are you interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). #### Third-party libraries Take advantage of one of our communities' [third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. ### Key concepts #### Authentication All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. These specific endpoints requires the use of [OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API keys and user Access Tokens to make a successful request. The Access Tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize or authenticate your App using the [3-legged OAuth flow](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow). Please note that OAuth 1.0a can be tricky to use. If you are not familiar with this authentication method, we recommend that you use a [library](/tools-and-libraries) or a tool like Postman to properly authenticate your requests. #### Developer Console, Projects, and developer Apps To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. #### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests that you can make on behalf of your app or on behalf of an authenticated user. These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. | | | | | :----------- | :-------------- | :-------------------------- | | **Endpoint** | **HTTP method** | **Rate limit** | | /2/lists | POST | 300 requests per 15 minutes | | /2/lists/:id | DELETE / PUT | 300 requests per 15 minutes | *** ### Code examples #### Create a List ```bash cURL theme={null} curl -X POST "https://api.x.com/2/lists" \ -H "Authorization: OAuth ..." \ -H "Content-Type: application/json" \ -d '{"name": "My List", "description": "A list of interesting accounts"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Create a new List response = client.lists.create( name="My List", description="A list of interesting accounts" ) print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Create a new List const response = await client.lists.create({ name: "My List", description: "A list of interesting accounts", }); console.log(response.data); ``` #### Update a List ```bash cURL theme={null} curl -X PUT "https://api.x.com/2/lists/123456789" \ -H "Authorization: OAuth ..." \ -H "Content-Type: application/json" \ -d '{"name": "Updated List Name"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Update a List response = client.lists.update( list_id="123456789", name="Updated List Name" ) print(response.data) ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Update a List const response = await client.lists.update("123456789", { name: "Updated List Name", }); console.log(response.data); ``` # Manage Lists Source: https://docs.x.com/x-api/lists/manage-lists/introduction Create, update, and delete Lists The Manage Lists endpoints let you create, update, and delete Lists on behalf of authenticated users. ## Overview Create a new List Update List name and description Delete a List *** ## Endpoints | Method | Endpoint | Description | | :----- | :----------------------------------------- | :---------------- | | POST | [`/2/lists`](/x-api/lists/create-list) | Create a new List | | PUT | [`/2/lists/:id`](/x-api/lists/update-list) | Update a List | | DELETE | [`/2/lists/:id`](/x-api/lists/delete-list) | Delete a List | *** ## Example: Create a List ```bash theme={null} curl -X POST "https://api.x.com/2/lists" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Tech News", "description": "My favorite tech journalists", "private": false }' ``` ## Example response ```json theme={null} { "data": { "id": "1234567890", "name": "Tech News" } } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) Create your first List Key concepts and best practices Add and remove members Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/lists/manage-lists/quickstart Create, update, and delete Lists This guide walks you through creating, updating, and deleting Lists. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Create a List Define the List name (required) and optional description and privacy settings: ```json theme={null} { "name": "Tech News", "description": "Top tech journalists and publications", "private": false } ``` ```bash cURL theme={null} curl -X POST "https://api.x.com/2/lists" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Tech News", "description": "Top tech journalists and publications", "private": false }' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Create a new List response = client.lists.create( name="Tech News", description="Top tech journalists and publications", private=False ) print(f"List created: {response.data.id} - {response.data.name}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Create a new List const response = await client.lists.create({ name: "Tech News", description: "Top tech journalists and publications", private: false, }); console.log(`List created: ${response.data?.id} - ${response.data?.name}`); ``` ```json theme={null} { "data": { "id": "1441162269824405510", "name": "Tech News" } } ``` Save the `id` to update or delete the List later. *** ## Update a List Modify a List's name, description, or privacy: ```bash cURL theme={null} curl -X PUT "https://api.x.com/2/lists/1441162269824405510" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Tech News & Insights", "description": "Updated description" }' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Update a List response = client.lists.update( "1441162269824405510", name="Tech News & Insights", description="Updated description" ) print(f"Updated: {response.data.updated}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Update a List const response = await client.lists.update("1441162269824405510", { name: "Tech News & Insights", description: "Updated description", }); console.log(`Updated: ${response.data?.updated}`); ``` **Response:** ```json theme={null} { "data": { "updated": true } } ``` *** ## Delete a List You need the ID of the List you want to delete. ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/lists/1441162269824405510" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Delete a List response = client.lists.delete("1441162269824405510") print(f"Deleted: {response.data.deleted}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Delete a List const response = await client.lists.delete("1441162269824405510"); console.log(`Deleted: ${response.data?.deleted}`); ``` ```json theme={null} { "data": { "deleted": true } } ``` You can only delete Lists that you own. *** ## Next steps Add and remove List members Retrieve List details Key concepts and best practices Full endpoint documentation # Integration guide Source: https://docs.x.com/x-api/lists/pinned-lists/integrate This page covers tools and key concepts for integrating the pinned Lists endpoints. *** ## Helpful tools Before we dive into some key concepts that will help you integrate this endpoint, we recommend that you become familiar with: ### Postman Postman is a great tool that you can use to test out an endpoint. Each Postman request includes every path and body parameter to help you quickly understand what is available to you. To learn more about our Postman collections, please visit our ["Using Postman"](/tutorials/postman-getting-started) page. ### Code samples Interested in getting set up with this endpoint with some code in your preferred coding language? We've got a handful of different code samples available that you can use as a starting point on our [Github page](https://github.com/xdevplatform/Twitter-API-v2-sample-code). ### Third-party libraries Take advantage of one of our communities' [third-party libraries](/tools-and-libraries) to help you get started. You can find a library that works with the v2 endpoints by looking for the proper version tag. *** ## Key concepts ### Authentication All X API v2 endpoints require you to authenticate your requests with a set of credentials, also known as keys and tokens. You can use OAuth 1.0a User Context to authenticate your requests to this endpoint. [OAuth 1.0a User Context](/resources/fundamentals/authentication), which means that you must use a set of API Keys and user Access Tokens to make a successful request. The access tokens must be associated with the user that you are making the request on behalf of. If you would like to generate a set of Access Tokens for another user, they must authorize your App using the [3-legged OAuth flow](https://developer.x.com/resources/fundamentals/authentication/obtaining-user-access-tokens). Please note that OAuth 1.0a can be difficult to use. If you are not familiar with this authentication method, we recommend that you use a [library](/tools-and-libraries), use a tool like Postman. ### Developer Console, Projects, and developer Apps To retrieve a set of authentication credentials that will work with the X API v2 endpoints, you must [sign up for a developer account](https://developer.x.com/en/portal/petition/essential/basic-info), set up a [Project](/resources/fundamentals/developer-apps) within that account, and created a [developer App](/resources/fundamentals/developer-apps) within that Project. You can then find your keys and tokens within your developer App. ### Rate limits Every day, many thousands of developers make requests to the X API. To help manage the sheer volume of these requests, [rate limits](/x-api/fundamentals/rate-limits) are placed on each endpoint that limits the number of requests you can make on behalf of your app or on behalf of an authenticated user. These endpoints are rate limited at the user level, meaning that the authenticated user that you are making the request on behalf of can only call the endpoint a certain number of times across any developer App. The chart below shows the rate limits for each endpoint. | Endpoint | HTTP method | Rate limit | | :----------------------------------- | :---------- | :------------------------- | | /2/users/:id/pinned\_lists | POST | 50 requests per 15 minutes | | /2/users/:id/pinned\_lists/:list\_id | DELETE | 50 requests per 15 minutes | | /2/users/:id/pinned\_lists | GET | 15 requests per 15 minutes | ### Fields and expansions The X API v2 GET endpoint allows users to select exactly which data they want to return from the API using a set of tools called `fields` and `expansions`. The `expansions` parameter allows you to expand objects referenced in the payload. For example, looking up pinned Lists allows you to pull the following [expansions](/x-api/fundamentals/expansions): * `owner_id` The `fields` parameter allows you to select exactly which [fields](/x-api/fundamentals/fields) within the different data objects you would like to receive. This endpoint delivers user objects primarily. By default, the List object returns the `id`, and `name` fields. To receive additional fields such as `list.created_at` or `list.description`, you will have to specifically request those using a fields parameter. We've added a guide on using [fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions) together to our [X API v2 data dictionary](/x-api/fundamentals/data-dictionary). The chart below shows the field and expansions available for the lookup endpoint: | Endpoint | Fields | Expansions | | :------------------------- | :--------------------------- | :--------- | | /2/users/:id/pinned\_lists | `list.fields`, `user.fields` | `owner_id` | # Pinned Lists Source: https://docs.x.com/x-api/lists/pinned-lists/introduction View and manage pinned Lists The Pinned Lists endpoints let you view, pin, and unpin Lists for the authenticated user. Pinned Lists appear prominently in the user's X interface. ## Overview Get user's pinned Lists Pin a List Unpin a List *** ## Endpoints | Method | Endpoint | Description | | :----- | :-------------------------------------------------------------- | :--------------- | | GET | [`/2/users/:id/pinned_lists`](/x-api/users/get-pinned-lists) | Get pinned Lists | | POST | [`/2/users/:id/pinned_lists`](/x-api/users/pin-list) | Pin a List | | DELETE | [`/2/users/:id/pinned_lists/:list_id`](/x-api/users/unpin-list) | Unpin a List | *** ## Example: Get pinned Lists ```bash theme={null} curl "https://api.x.com/2/users/123456789/pinned_lists?\ list.fields=name,description,member_count" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ## Example: Pin a List ```bash theme={null} curl -X POST "https://api.x.com/2/users/123456789/pinned_lists" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"list_id": "9876543210"}' ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) Get pinned Lists Pin and unpin Lists Key concepts and best practices Full endpoint documentation # Manage Pinned Lists Source: https://docs.x.com/x-api/lists/pinned-lists/quickstart/manage-pinned-lists Pin and unpin Lists This guide walks you through pinning and unpinning Lists. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Pin a List You need your authenticated user's ID and the ID of the List you want to pin. Find List IDs in the URL when viewing a List. ```bash cURL theme={null} curl -X POST "https://api.x.com/2/users/2244994945/pinned_lists" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"list_id": "1454155907651158017"}' ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Pin a List response = client.lists.pin( user_id="2244994945", list_id="1454155907651158017" ) print(f"Pinned: {response.data.pinned}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Pin a List const response = await client.lists.pin("2244994945", { listId: "1454155907651158017", }); console.log(`Pinned: ${response.data?.pinned}`); ``` ```json theme={null} { "data": { "pinned": true } } ``` *** ## Unpin a List ```bash cURL theme={null} curl -X DELETE "https://api.x.com/2/users/2244994945/pinned_lists/1454155907651158017" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client from xdk.oauth1_auth import OAuth1 oauth1 = OAuth1( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", access_token="YOUR_ACCESS_TOKEN", access_token_secret="YOUR_ACCESS_TOKEN_SECRET" ) client = Client(auth=oauth1) # Unpin a List response = client.lists.unpin( user_id="2244994945", list_id="1454155907651158017" ) print(f"Pinned: {response.data.pinned}") ``` ```javascript JavaScript SDK theme={null} import { Client, OAuth1 } from "@xdevplatform/xdk"; const oauth1 = new OAuth1({ apiKey: "YOUR_API_KEY", apiSecret: "YOUR_API_SECRET", accessToken: "YOUR_ACCESS_TOKEN", accessTokenSecret: "YOUR_ACCESS_TOKEN_SECRET", }); const client = new Client({ oauth1 }); // Unpin a List const response = await client.lists.unpin("2244994945", "1454155907651158017"); console.log(`Pinned: ${response.data?.pinned}`); ``` **Response:** ```json theme={null} { "data": { "pinned": false } } ``` *** ## Important notes * You can only pin Lists you follow or own * Pinned Lists appear at the top of your Lists in the X app * There's a limit on how many Lists you can pin *** ## Next steps Get your pinned Lists Get List details Full endpoint documentation # Pinned Lists Overview Source: https://docs.x.com/x-api/lists/pinned-lists/quickstart/overview Get started with pinned List endpoints The pinned Lists endpoints let you look up a user's pinned Lists and manage which Lists are pinned. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Available endpoints Get your pinned Lists Pin and unpin Lists *** ## Authentication | Operation | Authentication | | :------------------- | :--------------------------- | | Look up pinned Lists | OAuth 1.0a or OAuth 2.0 PKCE | | Pin/unpin Lists | OAuth 1.0a or OAuth 2.0 PKCE | Both lookup and manage operations require user context authentication. App-only (Bearer Token) authentication is not supported. *** ## Quick example ```bash cURL theme={null} # Get pinned Lists curl "https://api.x.com/2/users/2244994945/pinned_lists" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get pinned Lists response = client.lists.get_pinned("2244994945") for lst in response.data: print(f"{lst.name}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get pinned Lists const response = await client.lists.getPinned("2244994945"); response.data?.forEach((lst) => { console.log(lst.name); }); ``` *** ## Next steps Get List details Create and update Lists # Pinned Lists Lookup Source: https://docs.x.com/x-api/lists/pinned-lists/quickstart/pinned-list-lookup Get a user's pinned Lists This guide walks you through retrieving a user's pinned Lists. **Prerequisites** Before you begin, you'll need: * A [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) with an approved App * User Access Token (OAuth 1.0a or OAuth 2.0 PKCE) *** ## Get pinned Lists You need your authenticated user's ID. You can find it using the [user lookup endpoint](/x-api/users/lookup/introduction) or from your Access Token (the numeric part is your user ID). ```bash cURL theme={null} curl "https://api.x.com/2/users/2244994945/pinned_lists?\ list.fields=follower_count,member_count,owner_id&\ expansions=owner_id&\ user.fields=created_at,username" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Get pinned Lists response = client.lists.get_pinned( "2244994945", list_fields=["follower_count", "member_count", "owner_id"], expansions=["owner_id"], user_fields=["created_at", "username"] ) for lst in response.data: print(f"{lst.name} - {lst.follower_count} followers") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Get pinned Lists const response = await client.lists.getPinned("2244994945", { listFields: ["follower_count", "member_count", "owner_id"], expansions: ["owner_id"], userFields: ["created_at", "username"], }); response.data?.forEach((lst) => { console.log(`${lst.name} - ${lst.follower_count} followers`); }); ``` ```json theme={null} { "data": [ { "id": "1454155907651158017", "name": "Tech News", "follower_count": 150, "member_count": 25, "owner_id": "2244994945" } ], "includes": { "users": [ { "id": "2244994945", "username": "XDevelopers", "name": "X Developers", "created_at": "2013-12-14T04:35:55.000Z" } ] }, "meta": { "result_count": 1 } } ``` *** ## Available List fields | Field | Description | | :--------------- | :---------------------- | | `description` | List description | | `owner_id` | Owner's user ID | | `private` | Whether List is private | | `member_count` | Number of members | | `follower_count` | Number of followers | | `created_at` | List creation date | *** ## Next steps Pin and unpin Lists Get List details Full endpoint documentation # Remove List member Source: https://docs.x.com/x-api/lists/remove-list-member delete /2/lists/{id}/members/{user_id} Removes a User from a specific List by its ID and the User’s ID. # Update List Source: https://docs.x.com/x-api/lists/update-list put /2/lists/{id} Updates the details of a specific List owned by the authenticated user by its ID. # Append Media upload Source: https://docs.x.com/x-api/media/append-media-upload post /2/media/upload/{id}/append Appends data to a Media upload request. # Create Media metadata Source: https://docs.x.com/x-api/media/create-media-metadata post /2/media/metadata Creates metadata for a Media file. # Create Media subtitles Source: https://docs.x.com/x-api/media/create-media-subtitles post /2/media/subtitles Creates subtitles for a specific Media file. # Delete Media subtitles Source: https://docs.x.com/x-api/media/delete-media-subtitles delete /2/media/subtitles Deletes subtitles for a specific Media file. # Finalize Media upload Source: https://docs.x.com/x-api/media/finalize-media-upload post /2/media/upload/{id}/finalize Finalizes a Media upload request. # Get Media upload status Source: https://docs.x.com/x-api/media/get-media-upload-status get /2/media/upload Retrieves the status of a Media upload by its ID. # Initialize media upload Source: https://docs.x.com/x-api/media/initialize-media-upload post /2/media/upload/initialize Initializes a media upload. # Introduction Source: https://docs.x.com/x-api/media/introduction A media object represents a single photo, video or animated GIF. Media objects are used by many endpoints within the X API, and may be included in Posts, 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_video`) ## Creation Objects such as Posts, 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 /2/tweets`](/x-api/posts/creation-of-a-post)) 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](/x-api-/media/quickstart/media-upload-chunked) upload (images/GIF/video), or the older [simple](/x-api/media-upload/media-upload) 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 Post using the [`POST /2/tweets`](/x-api/posts/creation-of-a-post) endpoint. ## Retrieving Please refer to the [Media Object](/x-api/fundamentals/data-dictionary#media) in the data dictionary. # Best practices Source: https://docs.x.com/x-api/media/quickstart/best-practices There are a few important concepts to understand when using the [`POST /2/media/upload`](/x-api/media/media-upload) endpoint. Uploading media with OAuth can be a bit tricky, so we’ve outlined some things to keep in mind as well as a working sample of how to use this endpoint here. ## Keep in mind * You may attach up to 4 photos, 1 animated GIF or 1 video in a Post. * The image passed should be the raw binary of the image or binary base64 encoded, no need to otherwise encode or escape the contents as long as the Content-Type is set appropriately (when in doubt: `application/octet-stream`). * When posting base64 encoded images, be sure to set the “Content-Transfer-Encoding: base64” on the image part of the message. * Multi-part message boundaries must be on their own line and terminated by a CRLF. * For working examples of how to POST using this endpoint, we recommend testing with [xurl](https://github.com/xdevplatform/xurl). Also, take a look at the [X Libraries](/resources/tools-and-libraries) available. * Use the `media_id_string` provided in the API response for Javascript and any other languages that cannot accurately represent a long integer. ## Media Categories The Media Category parameter defines the use case of the media file to be uploaded, and can affect file size limits or other constraints enforced for media uploads. It’s important to use the correct media category when uploading media to avoid problems when trying to use the media. It is an optional value passed in the INIT request as part of the upload flow. If media category is not specified, the uploaded media is assumed to be media for a Post (`tweet_image`, `tweet_video`, or `tweet_gif`), depending on the content type. The most common media categories are as follows: * `tweet_image` * `tweet_video` * `tweet_gif` * `dm_image` * `dm_video` * `dm_gif` * `subtitles` If you are an Ads API partner please refer to [these docs](/x-ads-api/creatives#promoted-video) for more information on recommended media category for promoted video. ## Image specifications and recommendations Image files must meet all of the following criteria: * **Supported image media types**: `JPG`, `PNG`, `GIF`, `WEBP` * **Image size**: `<= 5 MB` * **Animated GIF size**: `<= 15 MB` The file size limit above is enforced by the media upload endpoint. In addition, there is a separate product entity specific file size limit which is applied when calling the Post creation (or similar) endpoints with `media_id`. The file size limit and other constraints may vary depending on the `media_category` parameter. ## Animated GIF recommendations A GIF may fail during Post creation even if it is within the file size limit. Adhere to the following constraints to improve success rates. * **Resolution**: `<= 1280x1080` (`width` x `height`) * **Number of frames**: `<= 350` * **Number of pixels**: `<= 300 million` (`width` \* `height` \* `num_frames`) * **File size**: `<= 15Mb` In order to process larger GIFs, use the [chunked upload](/x-api/media/quickstart/media-upload-chunked) endpoint with the `media_category` parameter. This allows the server to process the GIF file asynchronously, which is a requirement for processing larger files. Pass `media_category=tweet_gif` to enable async upload behavior for Posts with an animated GIF. ## Video specifications and recommendations Please use the Async Path for media uploads. ### Recommended * **Video Codec**: `H264 High Profile` * **Frame Rates**: `30 FPS`, `60 FPS` * **Video Resolution**: `1280x720` (landscape), `720x1280` (portrait), `720x720` (square). Subscribed users can upload a 1080p video and get 1080p playback. Unsubscribed users can upload a 720p video and get a 720p playback. * **Minimum Video Bitrate**: `5,000 kbps` * **Minimum Audio Bitrate**: `128 kbps` * **Audio Codec**: `AAC LC` * **Aspect Ratio**: `16:9` (landscape or portrait), `1:1` (square) ### Advanced * **Frame rate**: must be `60 FPS` or less * **Dimensions**: must be between `32x32` and `1280x1024` * **File size**: must not exceed `512 mb` * **Duration**: must be between `0.5 seconds` and `140 seconds` * **Aspect ratio**: must be between `1:3` and `3:1` * **[Pixel aspect ratio](https://en.wikipedia.org/wiki/Pixel_aspect_ratio)**: must have `1:1` * **Pixel format**: Only [YUV](https://en.wikipedia.org/wiki/YUV) 4:2:0 is supported * Audio must be [`AAC` with Low Complexity profile](https://en.wikipedia.org/wiki/Advanced_Audio_Coding#Modular_encoding). (High-Efficiency `AAC` is not supported) * Audio must be `mono` or `stereo`, not 5.1 or greater * Must not have [`open GOP`](https://en.wikipedia.org/wiki/Group_of_pictures) * Must use [`progressive scan`](https://en.wikipedia.org/wiki/Progressive_scan) ### Additional Information In the table below each row represents an upload recommendation, but is not a requirement. All uploads are processed for optimization across multiple platforms. | Orientation | Width | Height | Video Bitrate | Audio Bitrate | | :---------- | :---- | :----- | :------------ | :------------ | | Landscape | 1280 | 720 | 2048K | 128K | | Landscape | 640 | 360 | 768K | 64K | | Landscape | 320 | 180 | 256K | 64K | | Portrait | 720 | 1280 | 2048K | 128K | | Portrait | 360 | 640 | 768K | 64K | | Portrait | 180 | 320 | 256K | 64K | | Square | 720 | 720 | 2048K | 128K | | Square | 480 | 480 | 768K | 64K | | Square | 240 | 240 | 256K | 32K | For an example of how to upload media, please see the [chunked media upload documentation](/x-api/media/quickstart/media-upload-chunked). ### Troubleshooting For issues with the Media APIs, browse the [Media API category](https://devcommunity.x.com/c/x-api/media-apis) in the developer forums for an answer. # Chunked Media Upload Source: https://docs.x.com/x-api/media/quickstart/media-upload-chunked Upload videos and large media files using chunked upload This guide walks you through uploading videos and large media files using the chunked upload workflow. For video or large media uploads, you must: 1. **INIT** — Initialize the upload and get a `media_id` 2. **APPEND** — Upload each chunk of the file 3. **FINALIZE** — Complete the upload 4. **STATUS** — (If needed) Wait for processing to complete See [this sample code](https://github.com/xdevplatform/large-video-upload-python) for a complete Python example. *** ## Step 1: Initialize upload (INIT) Start the upload session to get a `media_id`: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/media/upload" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: multipart/form-data" \ -F "command=INIT" \ -F "media_type=video/mp4" \ -F "total_bytes=1048576" \ -F "media_category=amplify_video" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Initialize chunked upload response = client.media.init_upload( media_type="video/mp4", total_bytes=1048576, media_category="amplify_video" ) media_id = response.data.id print(f"Media ID: {media_id}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Initialize chunked upload const response = await client.media.initUpload({ mediaType: "video/mp4", totalBytes: 1048576, mediaCategory: "amplify_video", }); const mediaId = response.data?.id; console.log(`Media ID: ${mediaId}`); ``` **Response:** ```json theme={null} { "data": { "id": "1880028106020515840", "media_key": "13_1880028106020515840", "expires_after_secs": 1295999 } } ``` *** ## Step 2: Upload chunks (APPEND) Upload each chunk of the file. For example, split a 3 MB file into 3 chunks: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/media/upload" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: multipart/form-data" \ -F "command=APPEND" \ -F "media_id=1880028106020515840" \ -F "segment_index=0" \ -F "media=@/path/to/chunk1.mp4" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Upload chunks chunk_size = 1024 * 1024 # 1 MB chunks with open("video.mp4", "rb") as f: segment_index = 0 while True: chunk = f.read(chunk_size) if not chunk: break client.media.append_upload( media_id=media_id, segment_index=segment_index, media=chunk ) segment_index += 1 print(f"Uploaded chunk {segment_index}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; import fs from "fs"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Upload chunks const chunkSize = 1024 * 1024; // 1 MB chunks const fileBuffer = fs.readFileSync("video.mp4"); let segmentIndex = 0; for (let offset = 0; offset < fileBuffer.length; offset += chunkSize) { const chunk = fileBuffer.slice(offset, offset + chunkSize); await client.media.appendUpload({ mediaId, segmentIndex, media: chunk, }); console.log(`Uploaded chunk ${segmentIndex + 1}`); segmentIndex++; } ``` **Chunking advantages:** * Improved reliability on slow networks * Uploads can be paused and resumed * Failed chunks can be retried individually *** ## Step 3: Finalize upload (FINALIZE) Complete the upload after all chunks are sent: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/media/upload" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: multipart/form-data" \ -F "command=FINALIZE" \ -F "media_id=1880028106020515840" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Finalize upload response = client.media.finalize_upload(media_id=media_id) print(f"Processing state: {response.data.processing_info.state}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Finalize upload const response = await client.media.finalizeUpload({ mediaId }); console.log(`Processing state: ${response.data?.processing_info?.state}`); ``` **Response:** ```json theme={null} { "data": { "id": "1880028106020515840", "media_key": "13_1880028106020515840", "size": 1048576, "expires_after_secs": 86400, "processing_info": { "state": "pending", "check_after_secs": 1 } } } ``` If `processing_info` is returned, proceed to Step 4 to wait for processing. If not, the media is ready to use. *** ## Step 4: Check status (STATUS) If `processing_info` was returned, poll for processing completion: ```bash cURL theme={null} curl "https://api.x.com/2/media/upload?command=STATUS&media_id=1880028106020515840" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client import time client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Wait for processing to complete while True: response = client.media.get_status(media_id=media_id) state = response.data.processing_info.state if state == "succeeded": print("Media ready!") break elif state == "failed": print("Processing failed") break else: check_after = response.data.processing_info.check_after_secs print(f"Processing... checking again in {check_after}s") time.sleep(check_after) ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Wait for processing to complete while (true) { const response = await client.media.getStatus({ mediaId }); const state = response.data?.processing_info?.state; if (state === "succeeded") { console.log("Media ready!"); break; } else if (state === "failed") { console.log("Processing failed"); break; } else { const checkAfter = response.data?.processing_info?.check_after_secs ?? 1; console.log(`Processing... checking again in ${checkAfter}s`); await new Promise((r) => setTimeout(r, checkAfter * 1000)); } } ``` **Processing states:** `pending` → `in_progress` → `succeeded` or `failed` *** ## Step 5: Create Post with media Once processing is complete, create a Post with the media: ```bash cURL theme={null} curl -X POST "https://api.x.com/2/tweets" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "Check out this video!", "media": { "media_ids": ["1880028106020515840"] } }' ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_USER_ACCESS_TOKEN") # Create Post with media response = client.posts.create( text="Check out this video!", media={"media_ids": [media_id]} ) print(f"Posted: {response.data.id}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ accessToken: "YOUR_USER_ACCESS_TOKEN" }); // Create Post with media const response = await client.posts.create({ text: "Check out this video!", media: { mediaIds: [mediaId] }, }); console.log(`Posted: ${response.data?.id}`); ``` *** ## Media categories | Category | Description | | :-------------- | :---------------------- | | `tweet_image` | Image for a Post | | `tweet_gif` | Animated GIF for a Post | | `tweet_video` | Video for a Post | | `amplify_video` | Amplify video | *** ## Next steps File constraints and requirements Post with media Full endpoint documentation # Upload media Source: https://docs.x.com/x-api/media/upload-media post /2/media/upload Uploads a media file for use in posts or other content. # Get news stories by ID Source: https://docs.x.com/x-api/news/get-news-stories-by-id get /2/news/{id} Retrieves news story by its ID. # Introduction Source: https://docs.x.com/x-api/news/introduction The news lookup endpoint allows developers to get news and headlines breaking on X. This endpoint supports app-auth and user-auth authentication for OAuth1 and OAuth2 PKCE. ## Getting started To use the endpoint, you need a [bearer token](/fundamentals/authentication/oauth-2-0/application-only) from the [Developer Console](https://developer.x.com/en/portal/dashboard). Once you have the bearer token, you can call the news API as shown below: ```bash theme={null} curl 'https://api.x.com/2/news/1989418137272422538?news.fields=contexts,cluster_posts_results' --header 'Authorization: Bearer XXXXX' ``` If the request is successful, you should see the JSON response as shown below: ```json theme={null} { "data":{ "category":"News", "name":"Nebius Group Stock Plunges 30% Despite Q3 Revenue Surge and Meta Deal", "summary":"Nebius Group announced third-quarter revenue of $214 million, a fourfold increase year-over-year, alongside a $3 billion five-year GPU cloud services contract with Meta Platforms on November 11, 2025. Despite these positives, shares fell over 30% to $83.58 by November 14, driven by AI sector cooling and a $119.6 million net loss. Analysts hold a strong Buy rating with an average price target of $139.67, while X users see the dip as a buying opportunity.", "hook":"Nebius Group's shares cratered 30% after blockbuster Q3 earnings and a $3 billion Meta deal—yet analysts and investors are calling it a golden buying chance amid AI hype.", "contexts":{ "sports":{ "teams":[] }, "entities":{ "events":[], "organizations":[ "Goldman Sachs", "Nebius Group N.V." ], "people":[], "places":[], "products":[] }, "topics":[ "Stocks" ], "finance":{ "tickers":[ "NBIS" ] } }, "cluster_posts_results":[ { "post_id":"1989409257394245835" }, { "post_id":"1989410019562197162" }, { "post_id":"1989413132993999177" }, { "post_id":"1989411147179610400" }, { "post_id":"1989409829937656067" }, { "post_id":"1989415596249985296" }, { "post_id":"1989415537781477721" }, { "post_id":"1989413002739691628" }, { "post_id":"1989414454644363445" }, { "post_id":"1989411489988710860" } ], "disclaimer":"This story is a summary of posts on X and may evolve over time. Grok can make mistakes, verify its outputs.", "last_updated_at_ms":"2025-11-17T16:21:41.000Z", "id":"1989418137272422538" } } ``` # Search News Source: https://docs.x.com/x-api/news/search-news get /2/news/search Retrieves a list of News stories matching the specified search query. # Get space by ID Source: https://docs.x.com/x-api/spaces/get-space-by-id get /2/spaces/{id} Retrieves details of a specific space by its ID. # Get Space Posts Source: https://docs.x.com/x-api/spaces/get-space-posts get /2/spaces/{id}/tweets Retrieves a list of Posts shared in a specific Space by its ID. # Get Space ticket buyers Source: https://docs.x.com/x-api/spaces/get-space-ticket-buyers get /2/spaces/{id}/buyers Retrieves a list of Users who purchased tickets to a specific Space by its ID. # Get Spaces by creator IDs Source: https://docs.x.com/x-api/spaces/get-spaces-by-creator-ids get /2/spaces/by/creator_ids Retrieves details of Spaces created by specified User IDs. # Get Spaces by IDs Source: https://docs.x.com/x-api/spaces/get-spaces-by-ids get /2/spaces Retrieves details of multiple Spaces by their IDs. # Introduction Source: https://docs.x.com/x-api/spaces/introduction The following page describes the Spaces endpoints included in the X API. To learn more about Spaces in general, please visit [help.x.com](https://help.x.com/en/using-twitter/spaces).  Spaces allow expression and interaction via live audio conversation. The Spaces endpoints provide the tools to create new functionality around Spaces. You can use these endpoints to lookup live or scheduled Spaces, or to build discovery experiences to give your users ways to find Spaces they may be interested in. We encourage you to use your creativity to extend Spaces beyond the way we built it. With these endpoints you can build experiences to suggest Spaces to listeners based on keywords present in the title, or by surfacing accounts who host live or upcoming Spaces and are followed by a user; you can also help Hosts better understand how their Spaces are performing and get more insights on their audience.   ### Important resources The following resources will help you get started and integrate with the Spaces endpoints: * [Getting access to X API v2](/x-api/getting-started/getting-access) * [Spaces data dictionary](/x-api/fundamentals/data-dictionary#space) * [Make your first request to a Spaces endpoint](/x-api/spaces/lookup/introduction)   #### What's currently available | | | | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Spaces lookup** | [Lookup by a single Spaces ID](/x-api/spaces/space-lookup-by-space-id)

[Lookup using multiple Spaces IDs](/x-api/spaces/space-lookup-up-space-ids)

[Lookup by their creator ID](/x-api/spaces/space-lookup-by-their-creators)

[Lookup list of user who purchased a ticket](/x-api/spaces/retrieve-the-list-of-users-who-purchased-a-ticket-to-the-given-space) | | **Search Spaces** | [Search for spaces using a keyword](/x-api/spaces/search-for-spaces) | #### Understanding the lifecycle of Spaces Unlike other resources of the X Developer Platform, Spaces have a set lifecycle. Spaces can be scheduled up to 14 days in advance of their intended start date, and become unavailable after they end. A host can also cancel a previously scheduled Space anytime before it starts. Spaces are accessible while they are live; once ended, they will no longer be available for retrieval using the Spaces endpoints, and an error message will be returned to indicate this condition. When your app handles Spaces data, you are responsible for returning the most up-to-date information, and that you remove data that is no longer available from the platform. The [Spaces lookup endpoint](/x-api/spaces/lookup/introduction) can help you ensure you respect the expectations and intent of your users.   ### Roles in Spaces These endpoints reflect the way Spaces work on the X app. In Spaces, X users can have defined roles depending on how they interact with and interact in a Space.   ### Creator (or primary host) The primary Host is the user who created a Space, and the owner of the Space itself. Currently, Spaces can only have one Host, so the primary Host will be the only Host. In the [Spaces data dictionary](/x-api/fundamentals/data-dictionary#space), the primary Host information will be in the creator\_id field, which can be expanded into a [user object](/x-api/fundamentals/data-dictionary#user).   ### Hosts The primary Hosts can make one or more users co-hosts. In the Spaces data dictionary, these Hosts will appear as host\_ids, which can be expanded into a list of user objects. Host designation can change throughout the duration of a Space, and the metadata returned by these endpoints will reflect the status at the time of the request. Your app will know the primary host by checking the creator\_id value, and who are the co-hosts by checking the host\_ids values.   ### Speaker Speakers are users who have permission to talk in the Space. Zero or more Speakers can be present at any time, and there may be up to 10 Speakers (including the Hosts) in a Space. In the Space data dictionary, speakers will be returned in the speaker\_ids list, which you can expand into a list of user objects.   ### Listener A Listener can listen to a Space, react anytime using the predefined reactions, and ask to become a speaker (when the Hosts allows this in the Space settings). Listener information will only be returned as an aggregate count of participants (including Hosts) in the participant\_count field. # Spaces Lookup Source: https://docs.x.com/x-api/spaces/lookup/introduction Retrieve Space details by ID or find Spaces by their creators The Spaces lookup endpoints let you retrieve information about live or scheduled Spaces. Look up Spaces by their ID or find Spaces created by specific users. ## Overview Get details for a specific Space Look up multiple Spaces at once Find Spaces by their hosts Get users who purchased tickets *** ## Endpoints | Method | Endpoint | Description | | :----- | :-------------------------------------------------------------------- | :----------------------------- | | GET | [`/2/spaces/:id`](/x-api/spaces/get-space-by-id) | Get Space by ID | | GET | [`/2/spaces`](/x-api/spaces/get-spaces-by-ids) | Get Spaces by IDs | | GET | [`/2/spaces/by/creator_ids`](/x-api/spaces/get-spaces-by-creator-ids) | Get Spaces by creator user IDs | | GET | [`/2/spaces/:id/buyers`](/x-api/spaces/get-space-ticket-buyers) | Get ticket buyers for a Space | | GET | [`/2/spaces/:id/tweets`](/x-api/spaces/get-space-posts) | Get Posts shared in a Space | *** ## Response fields By default, the response includes `id` and `state`. Request additional fields: | Field | Description | | :------------------ | :------------------------ | | `title` | Space title | | `host_ids` | Host user IDs | | `speaker_ids` | Speaker user IDs | | `participant_count` | Number of participants | | `scheduled_start` | Scheduled start time | | `started_at` | Actual start time | | `ended_at` | End time | | `is_ticketed` | Whether Space has tickets | ### Space states | State | Description | | :---------- | :------------------- | | `live` | Currently active | | `scheduled` | Scheduled for future | | `ended` | Has ended | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/spaces/1DXxyRYNejbKM?\ space.fields=title,host_ids,participant_count,scheduled_start,state" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Make your first Spaces lookup request Find Spaces by keyword Full endpoint documentation Working code examples # Quickstart Source: https://docs.x.com/x-api/spaces/lookup/quickstart Retrieve Space details by ID or creator This guide walks you through retrieving Space information using the Spaces lookup endpoints. **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 *** ## Get a Space by ID Retrieve details for a specific Space: ```bash cURL theme={null} curl "https://api.x.com/2/spaces/1DXxyRYNejbKM?\ space.fields=title,host_ids,participant_count,scheduled_start,state,created_at" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get a Space by ID response = client.spaces.get( "1DXxyRYNejbKM", space_fields=["title", "host_ids", "participant_count", "scheduled_start", "state", "created_at"] ) print(f"Space: {response.data.title}") print(f"State: {response.data.state}") print(f"Participants: {response.data.participant_count}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get a Space by ID const response = await client.spaces.get("1DXxyRYNejbKM", { spaceFields: ["title", "host_ids", "participant_count", "scheduled_start", "state", "created_at"], }); console.log(`Space: ${response.data?.title}`); console.log(`State: ${response.data?.state}`); console.log(`Participants: ${response.data?.participant_count}`); ``` ### Response ```json theme={null} { "data": { "id": "1DXxyRYNejbKM", "state": "live", "title": "Discussing AI and the Future", "host_ids": ["2244994945"], "participant_count": 245, "created_at": "2024-01-15T09:00:00.000Z" } } ``` *** ## Get multiple Spaces Look up multiple Spaces at once: ```bash cURL theme={null} curl "https://api.x.com/2/spaces?\ ids=1DXxyRYNejbKM,1YqJDqWYNQDGW&\ space.fields=title,state,participant_count" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get multiple Spaces response = client.spaces.get_spaces( ids=["1DXxyRYNejbKM", "1YqJDqWYNQDGW"], space_fields=["title", "state", "participant_count"] ) for space in response.data: print(f"{space.title} - {space.state}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get multiple Spaces const response = await client.spaces.getSpaces({ ids: ["1DXxyRYNejbKM", "1YqJDqWYNQDGW"], spaceFields: ["title", "state", "participant_count"], }); response.data?.forEach((space) => { console.log(`${space.title} - ${space.state}`); }); ``` *** ## Get Spaces by creator Find Spaces hosted by specific users: ```bash cURL theme={null} curl "https://api.x.com/2/spaces/by/creator_ids?\ user_ids=2244994945,783214&\ space.fields=title,state,scheduled_start" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get Spaces by creator response = client.spaces.get_by_creator_ids( user_ids=["2244994945", "783214"], space_fields=["title", "state", "scheduled_start"] ) for space in response.data: print(f"{space.title} - {space.state}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get Spaces by creator const response = await client.spaces.getByCreatorIds({ userIds: ["2244994945", "783214"], spaceFields: ["title", "state", "scheduled_start"], }); response.data?.forEach((space) => { console.log(`${space.title} - ${space.state}`); }); ``` *** ## Include host information Expand host user data: ```bash cURL theme={null} curl "https://api.x.com/2/spaces/1DXxyRYNejbKM?\ space.fields=title,host_ids,state&\ expansions=host_ids&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Get Space with host info response = client.spaces.get( "1DXxyRYNejbKM", space_fields=["title", "host_ids", "state"], expansions=["host_ids"], user_fields=["username", "verified"] ) print(f"Space: {response.data.title}") # Host info is in response.includes.users ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Get Space with host info const response = await client.spaces.get("1DXxyRYNejbKM", { spaceFields: ["title", "host_ids", "state"], expansions: ["host_ids"], userFields: ["username", "verified"], }); console.log(`Space: ${response.data?.title}`); // Host info is in response.includes?.users ``` ### Response with expansion ```json theme={null} { "data": { "id": "1DXxyRYNejbKM", "state": "live", "title": "Discussing AI and the Future", "host_ids": ["2244994945"] }, "includes": { "users": [ { "id": "2244994945", "username": "XDevelopers", "verified": true } ] } } ``` *** ## Space states | State | Description | | :---------- | :------------------- | | `live` | Currently active | | `scheduled` | Scheduled for future | | `ended` | Has ended | *** ## Available fields | Field | Description | | :------------------ | :------------------------ | | `title` | Space title | | `host_ids` | Host user IDs | | `speaker_ids` | Speaker user IDs | | `participant_count` | Current participants | | `scheduled_start` | Scheduled start time | | `started_at` | Actual start time | | `ended_at` | End time | | `is_ticketed` | Whether Space has tickets | | `state` | Current state | *** ## Next steps Find Spaces by keyword Full endpoint documentation # Search Spaces Source: https://docs.x.com/x-api/spaces/search-spaces get /2/spaces/search Retrieves a list of Spaces matching the specified search query. # Spaces Search Source: https://docs.x.com/x-api/spaces/search/introduction Search for live or scheduled Spaces by keyword The Spaces Search endpoint lets you search for live or scheduled Spaces by keyword. Find Spaces about topics of interest. ## Overview Search Spaces by title Find live and upcoming Spaces *** ## Endpoint | Method | Endpoint | Description | | :----- | :------------------------------------------------ | :---------------- | | GET | [`/2/spaces/search`](/x-api/spaces/search-spaces) | Search for Spaces | *** ## Parameters | Parameter | Description | | :------------- | :------------------------------ | | `query` | Search query (required) | | `state` | Filter by `live` or `scheduled` | | `space.fields` | Additional Space fields | | `expansions` | Related objects to include | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/spaces/search?\ query=AI&\ state=live&\ space.fields=title,host_ids,participant_count" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example response ```json theme={null} { "data": [ { "id": "1DXxyRYNejbKM", "state": "live", "title": "Discussing AI and the Future", "host_ids": ["1234567890"], "participant_count": 245 } ], "meta": { "result_count": 1 } } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [keys and tokens](/resources/fundamentals/authentication) Search for your first Space Look up Spaces by ID Full endpoint documentation # Quickstart Source: https://docs.x.com/x-api/spaces/search/quickstart Search for Spaces by keyword This guide walks you through searching for Spaces by keyword. **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 *** ## Search for Spaces Search for Spaces matching a keyword: ```bash cURL theme={null} curl "https://api.x.com/2/spaces/search?\ query=AI&\ space.fields=title,host_ids,participant_count,state&\ state=live" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search for Spaces response = client.spaces.search( query="AI", space_fields=["title", "host_ids", "participant_count", "state"], state="live" ) for space in response.data: print(f"{space.title} - {space.participant_count} participants") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search for Spaces const response = await client.spaces.search({ query: "AI", spaceFields: ["title", "host_ids", "participant_count", "state"], state: "live", }); response.data?.forEach((space) => { console.log(`${space.title} - ${space.participant_count} participants`); }); ``` ### Response ```json theme={null} { "data": [ { "id": "1DXxyRYNejbKM", "state": "live", "title": "Discussing AI and the Future", "host_ids": ["2244994945"], "participant_count": 245 }, { "id": "1YqJDqWYNQDGW", "state": "live", "title": "AI in Healthcare", "host_ids": ["783214"], "participant_count": 89 } ], "meta": { "result_count": 2 } } ``` *** ## Filter by state Search only live or scheduled Spaces: ### Live Spaces only ```bash cURL theme={null} curl "https://api.x.com/2/spaces/search?query=tech&state=live" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search live Spaces only response = client.spaces.search(query="tech", state="live") for space in response.data: print(f"LIVE: {space.title}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search live Spaces only const response = await client.spaces.search({ query: "tech", state: "live", }); response.data?.forEach((space) => { console.log(`LIVE: ${space.title}`); }); ``` ### Scheduled Spaces only ```bash cURL theme={null} curl "https://api.x.com/2/spaces/search?query=tech&state=scheduled" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search scheduled Spaces only response = client.spaces.search(query="tech", state="scheduled") for space in response.data: print(f"SCHEDULED: {space.title}") ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search scheduled Spaces only const response = await client.spaces.search({ query: "tech", state: "scheduled", }); response.data?.forEach((space) => { console.log(`SCHEDULED: ${space.title}`); }); ``` *** ## Include host information Expand host user data: ```bash cURL theme={null} curl "https://api.x.com/2/spaces/search?\ query=AI&\ space.fields=title,host_ids,state&\ expansions=host_ids&\ user.fields=username,verified" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ```python Python SDK theme={null} from xdk import Client client = Client(bearer_token="YOUR_BEARER_TOKEN") # Search with host info response = client.spaces.search( query="AI", space_fields=["title", "host_ids", "state"], expansions=["host_ids"], user_fields=["username", "verified"] ) for space in response.data: print(f"{space.title}") # Host info is in response.includes.users ``` ```javascript JavaScript SDK theme={null} import { Client } from "@xdevplatform/xdk"; const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" }); // Search with host info const response = await client.spaces.search({ query: "AI", spaceFields: ["title", "host_ids", "state"], expansions: ["host_ids"], userFields: ["username", "verified"], }); response.data?.forEach((space) => { console.log(space.title); }); // Host info is in response.includes?.users ``` *** ## Common parameters | Parameter | Description | | :------------- | :------------------------------------ | | `query` | Search query (required) | | `state` | Filter: `live`, `scheduled`, or `all` | | `max_results` | Results to return (1-100) | | `space.fields` | Space fields to include | | `expansions` | Related objects to include | | `user.fields` | User fields to include | *** ## Next steps Look up Spaces by ID Full endpoint documentation # Stream Likes compliance data Source: https://docs.x.com/x-api/stream/stream-likes-compliance-data get /2/likes/compliance/stream Streams all compliance data related to Likes for Users. # Get personalized Trends Source: https://docs.x.com/x-api/trends/get-personalized-trends get /2/users/personalized_trends Retrieves personalized trending topics for the authenticated user. # Get Trends by WOEID Source: https://docs.x.com/x-api/trends/get-trends-by-woeid get /2/trends/by/woeid/{woeid} Retrieves trending topics for a specific location identified by its WOEID. # Personalized Trends Source: https://docs.x.com/x-api/trends/personalized-trends/introduction Get trending topics personalized for the authenticated user The Personalized Trends endpoint returns trending topics tailored to the authenticated user, based on their location and interests. ## Overview Trends tailored to the user Based on user's location Current trending topics *** ## Endpoint | Method | Endpoint | Description | | :----- | :----------------------------- | :---------------------- | | GET | `/2/users/personalized_trends` | Get personalized trends | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/users/personalized_trends" \ -H "Authorization: Bearer $USER_ACCESS_TOKEN" ``` ## Example response ```json theme={null} { "data": [ { "trend_name": "#AI", "tweet_count": 125000 }, { "trend_name": "Machine Learning", "tweet_count": 85000 } ] } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * User Access Tokens via [OAuth 2.0 PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) * Premium User Subscription Get trends for a specific location Full endpoint documentation # Trends by WOEID Source: https://docs.x.com/x-api/trends/trends-by-woeid/introduction Get trending topics for a specific geographic location The Trends by WOEID endpoint returns trending topics for a specific geographic location, identified by a Yahoo! Where On Earth ID (WOEID). ## Overview Trends for any supported location Countries, cities, and regions Current trending topics *** ## Endpoint | Method | Endpoint | Description | | :----- | :------------------------------------------------------------ | :--------------------- | | GET | [`/2/trends/by/woeid/:id`](/x-api/trends/get-trends-by-woeid) | Get trends for a WOEID | *** ## Common WOEIDs | Location | WOEID | | :------------- | :------- | | Worldwide | 1 | | United States | 23424977 | | United Kingdom | 23424975 | | Japan | 23424856 | | New York | 2459115 | | Los Angeles | 2442047 | | London | 44418 | | Tokyo | 1118370 | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/trends/by/woeid/1" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` ## Example response ```json theme={null} { "data": [ { "trend_name": "#AI", "tweet_count": 250000 }, { "trend_name": "Breaking News", "tweet_count": 180000 } ] } ``` *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [Bearer Token](/resources/fundamentals/authentication) Get trends for the authenticated user Full endpoint documentation # Get usage Source: https://docs.x.com/x-api/usage/get-usage get /2/usage/tweets Retrieves usage statistics for Posts over a specified number of days. # Usage Source: https://docs.x.com/x-api/usage/introduction Monitor your API usage and track Post consumption The Usage endpoint lets you monitor your API usage, including the number of Posts consumed. Track your usage programmatically to manage costs and stay within limits. Pay-per-usage plans are subject to a monthly cap of 2 million Post reads. If you need higher volume, consider an [Enterprise plan](/forms/enterprise-api-interest). ## Overview Track Posts consumed View usage by day Monitor usage across your App *** ## Endpoint | Method | Endpoint | Description | | :----- | :------------------------------------------ | :------------------ | | GET | [`/2/usage/tweets`](/x-api/usage/get-usage) | Get Post usage data | *** ## Response data The response includes daily Post consumption counts: ```json theme={null} { "data": { "daily_project_usage": [ { "date": "2024-01-15", "usage": [ { "app_id": "12345678", "tweets_consumed": 15420 } ] } ], "project_id": "1234567890", "project_cap": 10000000 } } ``` ### Fields | Field | Description | | :-------------------- | :------------------------ | | `daily_project_usage` | Array of daily usage data | | `date` | Date in YYYY-MM-DD format | | `app_id` | Your App's ID | | `tweets_consumed` | Posts consumed that day | | `project_cap` | Your monthly Post limit | *** ## Example request ```bash theme={null} curl "https://api.x.com/2/usage/tweets" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Use cases * **Cost monitoring** — Track consumption against your budget * **Alerting** — Set up alerts when approaching limits * **Optimization** — Identify high-consumption endpoints * **Reporting** — Generate usage reports Resources are deduplicated within a 24-hour UTC window, so requesting the same resource multiple times in a day only counts as one charge. [Learn more about pricing →](/x-api/getting-started/pricing) *** ## Getting started **Prerequisites** * An approved [developer account](https://developer.x.com/en/portal/petition/essential/basic-info) * A [Project and App](/resources/fundamentals/developer-apps) in the Developer Console * Your App's [Bearer Token](/resources/fundamentals/authentication) Full endpoint documentation View usage in the dashboard # Follow List Source: https://docs.x.com/x-api/users/follow-list post /2/users/{id}/followed_lists Causes the authenticated user to follow a specific List by its ID. # Get followed Lists Source: https://docs.x.com/x-api/users/get-followed-lists get /2/users/{id}/followed_lists Retrieves a list of Lists followed by a specific User by their ID. # Get List memberships Source: https://docs.x.com/x-api/users/get-list-memberships get /2/users/{id}/list_memberships Retrieves a list of Lists that a specific User is a member of by their ID. # Get owned Lists Source: https://docs.x.com/x-api/users/get-owned-lists get /2/users/{id}/owned_lists Retrieves a list of Lists owned by a specific User by their ID. # Get pinned Lists Source: https://docs.x.com/x-api/users/get-pinned-lists get /2/users/{id}/pinned_lists Retrieves a list of Lists pinned by the authenticated user. # Pin List Source: https://docs.x.com/x-api/users/pin-list post /2/users/{id}/pinned_lists Causes the authenticated user to pin a specific List by its ID. # Unfollow List Source: https://docs.x.com/x-api/users/unfollow-list delete /2/users/{id}/followed_lists/{list_id} Causes the authenticated user to unfollow a specific List by its ID. # Unlike Post Source: https://docs.x.com/x-api/users/unlike-post delete /2/users/{id}/likes/{tweet_id} Causes the authenticated user to Unlike a specific Post by its ID. # Unpin List Source: https://docs.x.com/x-api/users/unpin-list delete /2/users/{id}/pinned_lists/{list_id} Causes the authenticated user to unpin a specific List by its ID. # Changelog Source: https://docs.x.com/changelog The X Developer Platform is updated frequently with new functionality and products to better suit your needs. We will be documenting all changes made to the platform's products via this resource and the @API X account. To subscribe to updates, please [**“Turn on notifications”**](https://help.x.com/en/managing-your-account/notifications-on-mobile-devices#:~:text=In%20the%20top%20menu,%20you,you%20would%20like%20to%20receive) for [**@API**](https://x.com/api). ### Change to DM Events Behavior Today, we made a change to X API V2 DM Events behavior. With the new change participant\_ids array will only be included for ParticipantsJoin and ParticipantsLeave events, and it will list only the participants who joined or left at the time of the event. The MessageCreate event will no longer include the participant\_ids array. Learn more [here](https://devcommunity.x.com/t/upcoming-change-to-x-api-dm-events-endpoints-behavior/241841). ### Launch of Account Activity API Endpoints Today, we launched new X API v2 [Account Activity API endpoints](/x-api/webhooks/introduction). Learn more [here](https://devcommunity.x.com/t/account-activity-api-is-now-available-in-v2/242895). ### Improvements to Media Upload Endpoint We made few improvements to media upload endpoint to improve the developer experience. We introduced Dedicated Endpoints for Chunked Media Upload [Media Upload endpoints](/x-api/media/introduction). Learn more [here](https://devcommunity.x.com/t/media-upload-endpoints-update-and-extended-migration-deadline/241818). ### Support for Email Address Retrieval with OAuth 2.0 Today, we added support for email address retrieval with OAuth 2.0 in X API V2. Learn more [here](https://devcommunity.x.com/t/announcing-support-for-email-address-retrieval-with-oauth-2-0-in-the-x-api-v2/240555). ### Changes to User Affiliation Data Today, we made changes to X API V2 User Affiliation Data. With the new change affiliation.user\_id will return an array of user\_ids. Earlier it used to return a single user\_id as a string, even if a user had multiple affiliations. Learn more [here](https://devcommunity.x.com/t/change-to-x-api-user-affiliation-data/237164). ### Launch of Media Upload Endpoints Today, we launched new X API v2 [Media Upload endpoints](/x-api/media/introduction). Learn more [here](https://devcommunity.x.com/t/announcing-media-upload-endpoints-in-the-x-api-v2/234175/3). ### Launch of New Documentation Site Today, we launched our new X Developer Platform documentation site, [https://docs.x.com](https://docs.x.com). ### Launch of xurl Tool Today, we launched [xurl](https://github.com/xdevplatform/xurl), a new tool that simplifies API testing for X developers, which now supports OAuth 2.0. ### Addition of Community ID Tweet Field Today, we added the `community_id` Tweet Field to the X API v2. Learn more about it [here](https://x.com/tapshah21/status/1860061806653317126). ### Addition of DM Blocking Endpoints Today, we added the DM blocking and DM unblocking endpoints to the X API v2. ### Support for Longform Posts Today, we added the ability to create longform Posts with a length of 25k instead of 4k. ### Support for Long Form Posts Today, we added the ability to create long form Posts using the X API v2 ### Retrieve User Affiliation Today, we added the ability to retrieve a User's affiliation programmatically using the X API v2 ### Get Article Metadata Today, we added the ability to get Article metadata using the X API v2 ### Get Profile Image and Cover Picture Today, we added the ability to get profile Image and cover picture URL via the X V2 API ### Addition of Connection Status Field Today, we added the connection\_status field in the X API v2. Learn more about it [here](https://devcommunity.x.com/t/announcing-connection-status-field-in-the-user-object-in-the-x-api-v2/212588). ### Addition of Trends Lookup Endpoint Today, we added the Trends lookup endpoint in the X API v2. Learn more about it [here](https://devcommunity.x.com/t/announcing-the-users-search-and-trends-lookup-endpoints-in-the-x-api-v2/210567). ### Addition of Users Search Endpoint Today, we added the Users Search endpoint in the X API v2. Learn more about it [here](https://devcommunity.x.com/t/announcing-the-users-search-and-trends-lookup-endpoints-in-the-x-api-v2/210567). ### Get DM Permissions Today, we added the ability to the get DM permissions via the X API v2. Learn more about it [here](https://devcommunity.x.com/t/dm-permissions-available-in-the-x-api-v2/209688). ### Launch of Subscription Type Field Today, we launched a subscription\_type field on the User object in the X API v2. Learn more about it [here](https://devcommunity.x.com/t/new-subscription-type-field-is-available-in-the-twitter-api-v2/209219/2). ### Get Posts Usage Endpoint Today, we launched an endpoint to programmatically get your posts usage in the X API v2. Learn more about it [here](https://developer.x.com/en/docs/twitter-api/usage/tweets/api-reference/get-usage-tweets). ### Launch of Reposts Lookup Endpoint Today, we launched an endpoint to lookup reposts of a post in the X API v2. Learn more about it [here](https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/get-tweets-id-retweets). ### Deprecation of Search Endpoints Today, we are deprecating the search/tweets, users/search, and geo/search endpoints in the X API v1.1. Learn more [here](https://developer.x.com/en/docs/twitter-api/enterprise/powertrack-api/guides/powertrack_recovery_and_redundancy_features). ### Deprecation of Replay API Endpoint Today, we are deprecating the Replay API endpoint from the enterprise X API. You can use [recovery](https://developer.x.com/en/docs/twitter-api/enterprise/powertrack-api/guides/powertrack_recovery_and_redundancy_features) featured instead. ### Deprecation of Historical Powertrack Endpoints Today, we are deprecating the Historical Powertrack and Insights track endpoints from the enterprise X API. ### Removal of Follows Endpoints from Basic and Pro Tiers Today, we are removing the Follows and List Follows endpoints from the Basic and Pro tiers of the X API v2. ### Removal of Manage Blocks Endpoint Today, we are removing the Manage Blocks endpoint from X API v2. ### Support for Bookmarks Count and Long Posts Today, we added support for retrieving bookmarks count and the ability to retrieve posts with more than 280 characters in the X API v2. ### Deprecation of Statuses/Filter Endpoint Today, we are deprecating the statuses/filter endpoint in the X API v1.1 Developers can use the filtered stream endpoint in the X API v2. ### Support for View Counts Today, we are adding support for view counts in the X API v2. A new field called impression\_count is made available in the public\_metrics of the post payload. ### Addition of Verified Type Field Today, we are adding a verified\_type user field to the X API v2 that indicates the type of verification a user account has (blue, business, government or none) . ### Removal of Source Field Today, we are removing the source field from the post payload in the X APIs. ### Addition of Direct Messages Endpoints Today, Today, we are adding six v2 Direct Messages endpoints. This release includes three GET methods for retrieving Direct Message conversation events and three POST methods for creating new Direct Messages. These methods support group conversations for the first time. Please visit our [blog post](https://developer.x.com/en/blog/product-news/2022/new-v2-direct-messages-endpoints) to learn more about this update. ### Support for Edited Post Metadata Today, we are adding the ability for developers to retrieve edited post metadata using the X API v2. Please visit our [blog post](https://developer.x.com/en/blog/product-news/2022/supporting-edit-tweet-functionality) to learn more about this update. ### Addition of Filtering Operator Aliases Filtering/query operator "aliases" were added to search and filtered stream endpoints, across enterprise, premium, and v2. In many cases, the operator refers to posts instead of statuses, e.g. "in\_reply\_to\_tweet\_id" as an alias for "in\_reply\_to\_status\_id." In other cases, the new operator alias adds more clarity to the matching performed. For example., since posts can only have one video attachment, a new "has:video\_link" alias is provided for "has:videos." Check out the [v2 filtered stream operator table](https://developer.x.com/en/docs/twitter-api/tweets/filtered-stream/integrate/build-a-rule#list) to see more examples. ### Updates to Reverse Chronological Home Timeline Beginning today, the reverse chronological home timeline v2 endpoint can return every post created on a timeline over the last 7 days and the most recent 800 regardless of the creation date. ### New Filtering Operators for Filtered Stream Today, 10 new v2 filtering operators are now available to all developers building with the filtered stream endpoints, and another 10 are now more widely available. Please visit our [blog post](https://developer.x.com/en/blog/product-news/2022/twitter-api-v2-filtered-stream) to learn more about this update. ### Updates to Streaming Endpoints Latency Today, we’re making updates to the streaming endpoints in the X API v2 to reduce the latency by 50%. ### Enabling v1.1 Media Endpoints in Essential Access Today, we're enabling the v1.1 media endpoints in Essential access of the X API v2. Please visit our [forum announcement](https://devcommunity.x.com/t/v1-1-media-endpoints-available-for-essential-access-in-the-twitter-api-v2/171664) to learn more about this update. ### Launch of Reverse Chronological Home Timeline Endpoint Today, we’re launching a new endpoint that enables you to retrieve a collection of the most recent posts and reposts posted by who you follow. Please visit our [forum announcement](https://devcommunity.x.com/t/reverse-chronological-home-timeline-in-the-twitter-api-v2/171549) to learn more about this launch. Along with the new endpoints, the variants field is now available for the [media object](https://developer.x.com/en/docs/twitter-api/data-dictionary/object-model/media). This will give you the type of content attached with the URL. ### Launch of Bookmarks Endpoints Today, we’re launching new endpoints that enable you to manage and lookup Bookmarks using the X API v2. The Bookmarks feature has long been available in the X app, but until now, there hasn’t been an endpoint that allows you to retrieve, create or build solutions to manage your Bookmarks via the API. Please visit our [forum announcement](https://devcommunity.x.com/t/build-with-bookmarks-on-the-twitter-api-v2/168804) to learn more about this launch. ### Launch of Quotes Lookup Endpoint Today, we’re launching the [quotes lookup endpoint](https://developer.x.com/en/docs/twitter-api/tweets/retweets/introduction) that lets you get the quotes for a post ID. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/introducing-the-quote-tweets-lookup-endpoint-to-the-twitter-api-v2/168370) to learn more about this launch. ### Updates to Documentation Navigation We’ve heard your feedback. Finding content in our documentation could be difficult, and it was confusing when you clicked on a link and were taken to an entirely different side navigation. Today, we’ve released an updated docs navigation that enables you to browse the full offering of the X Developer Platform from a single side navigation. With this launch, we eliminated redundant pages, removed confusing icons, and improved how you can interact with the different elements within the navigation. If you have any feedback, please let us know via our [feedback channel](https://twitterdevfeedback.uservoice.com/forums/930250-twitter-api). ### Improvements to Reposts and Likes Lookup Endpoints Today, we’re launching improvements to the [reposts lookup](https://developer.x.com/en/docs/twitter-api/tweets/retweets/introduction) and the [Likes lookup](https://developer.x.com/en/docs/twitter-api/tweets/likes/introduction) endpoints, allowing you to get back the complete list of accounts who have Liked or reposted a post, not just the last 100 accounts to do so. To learn more about this launch, please visit our forum announcement to learn more about this launch. ### Support for OAuth 2.0 Authentication Today, all developers can now authenticate using OAuth 2.0 by selecting OAuth 2.0 as an authentication method in the Developer Console. We’ve added support for confidential and public clients and all relevant v2 endpoints to use this authentication method as part of this release. OAuth 2.0 is an industry-standard authorization protocol that provides developers more control over an application’s scopes and improves authorization flows across multiple devices. In other words, developers building applications for people on X will have more control over the information their App requests from its users, so that you only have to ask your end-users for the data and information you need. This modern authorization protocol will allow you to present your end-users with a more streamlined consent flow for authorizing your app, which only displays the specific scopes you have requested from them. To learn more about this launch, please visit our forum announcement. ### Major Platform Updates to X API v2 **Major platform updates: X API v2 is now the primary version, new Essential and Elevated access, and Policy changes** Today, we are announcing that X API v2 is now the primary version of the X API. We have launched enough endpoints and functionality into X API v2 to satisfy the needs of 90% of all existing Apps built on the X API. We are launching the following changes today to further improve upon the X API v2 developer experience: * Fast and free Essential access to the API, and free Elevated access to developers who have had their use cases approved. We have a lot more details on these access levels available on our About the X API page. * We are removing language in our Developer Policy that restricted how you build with X’s core features, and limited the number of users you can support through your app. * We are opening up our platform to encourage you to build tools and products that make X better, healthier, and extend the public conversation. [See a list of solutions we’d love to see you build](https://developer.x.com/en/docs/twitter-api/what-to-build). * Specifically, we’ve removed terms that restricted replication of the X experience, including X’s core features as well as terms that required permission to have high numbers of user tokens. * We know that building solutions that help people on X often means a developer has to build (or replicate) some of the things that are available on X. These changes to our Developer Policy are intended to drive clarity for the developer ecosystem and provide an open API platform that makes it easier for developers to build, innovate, and make an impact on the public conversation. We go into a lot more detail on this launch and the X API launches that led to this moment in our [forum post](https://devcommunity.x.com/t/ushering-in-a-new-era-for-the-twitter-developer-platform-with-the-twitter-api-v2/162087). In addition to exploring earlier changelog entries, we’ve put together some key resources that you can review to understand what all has released to X API v2 to-date: * [X API endpoint map](/x-api/migrate/x-api-endpoint-map) * [About the X API](/x-api/getting-started/about-x-api) ### Launch of List Lookup Endpoints Today, we’re launching the new List lookup endpoints to X API v2. These endpoints will allow users to retrieve details on specified Lists such as followers, members, posts and more. The data retrieved can be used to build solutions that solve for curation, analysis and discoverability use cases and needs. In addition to new List endpoints, a new list: operator has been made available at the Academic Research access level. This will be available to use on search posts and post counts. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-list-lookup-endpoints-for-the-twitter-api-v2/161965). ### Additions to Spaces Endpoints We are adding functionality to the Spaces endpoint to return the list of people who purchased a ticket to a Space, as well as support for Topics. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/adding-new-functionality-to-spaces-endpoints/161959). ### Launch of Manage Posts Endpoints Today, we’re launching new manage posts endpoints to the X API v2. In addition to post and post delete functionality,, we’ve added the ability to post polls, tag people in images, create posts with reply settings, and post to Super Followers. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-manage-tweets-endpoints-for-the-twitter-api-v2/161501). ### Changes to Embedded Buttons Today, we're making some changes to X's embedded buttons. Buttons are now more rounded to match X.com buttons and we've (very slightly) updated our color palette. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/embedded-buttons-have-a-new-look/160528). ### Launch of Lists Endpoint Group Today, we’re launching the new Lists endpoint group to X API v2. These endpoints will allow you to build solutions that curate and organize posts based on preferences, interests, groups, or topics. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-new-twitter-api-v2-manage-list-endpoints/159980). ### Launch of Mutes Lookup Endpoint Today, we’re launching the new [mutes lookup](https://developer.x.com/en/docs/twitter-api/users/mutes/introduction) endpoint to the X API v2. This endpoint will allow authenticated users to retrieve and get information on the accounts they have muted. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-new-mutes-lookup-endpoint-for-the-twitter-api-v2/159680). ### Launch of OAuth 2.0 Beta Today, we’re launching a beta of OAuth 2.0 and fine-grained scopes on the X API v2. . Developers interested in shaping the future of OAuth 2.0 on the X Developer Platform, can request access to the OAuth 2.0 beta. OAuth 2.0 is the industry standard for authentication and will allow for a more modern authorization experience. The OAuth 2.0 beta lets you test this new functionality in a v2 Project and associated App and give us feedback before a General Availability release. However, since this implementation of OAuth 2.0 is currently in development, we strongly recommend that you refrain from testing on a public, production environment, or app. To learn more about this launch, please visit our forum announcement. ### Launch of Batch Compliance Endpoints Today, we’re launching the new [batch compliance endpoints](https://developer.x.com/en/docs/twitter-api/compliance/batch-compliance/introduction) to the X API v2 to make it easier for developers using the X API to keep their X data in compliance with our [Developer Agreement and Policy](https://developer.x.com/en/developer-terms/policy). These endpoints allow developers and researchers to batch upload large amounts of post or User IDs and understand what action is needed to ensure that their datasets reflect user intent and the current state of the content on X. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/new-batch-compliance-endpoints-available-on-the-twitter-api-v2/158534). ### Launch of Spaces Endpoints Today, we’re launching the new [Spaces endpoints](https://developer.x.com/en/docs/twitter-api/spaces/overview) and a [new top-level Spaces data dictionary](https://developer.x.com/en/docs/twitter-api/data-dictionary/object-model/space) to the X API v2. The Spaces endpoints enable you to lookup Spaces by Space ID or user ID, or using keywords, hashtags, or usernames of people mentioned in a title. The new data dictionary contains relevant information about a Space such as its title, ticketed status, and participant metrics; all user IDs can be expanded into full objects. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/introducing-new-spaces-endpoints-on-the-twitter-api-v2/158213). ### New Object for Video Views Limitation Previously, when you requested video view metrics using the enterprise \[Engagement API]/x-api/enterprise-gnip-2.0/fundamentals/engagement-api) on posts that are older than 1800 days old, we delivered a zero value without much context. Today, we will start delivering a new object in these cases called unsupported\_for\_video\_views\_tweet\_ids, which will include a comma-separated list of post IDs that are older than 1800 days old. You will still receive all other requested metrics for these posts in a separate object. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/new-engagement-api-video-views-object-to-clarify-limitation/158163). ### Addition of Alt Text Field Today, we are making the alt\_text field available in the X API v2 media object, in order to enable developers to build more consciously for accessibility. To request this field, you must pass the expansions=attachments.media\_keys parameter, as well as the media.fields=alt\_text parameter. If included in your request, this field will return the alt text for any posts that include images with alt text. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/media-alt-text-field-now-available-in-twitter-api-v2/157939). ### Launch of Reposts Endpoints Today, we’re launching new [reposts](https://developer.x.com/en/docs/twitter-api/tweets/retweets) endpoints to X API v2. Developers can now use these endpoints to repost or undo a repost on behalf of an authenticated user, or to look up who reposted a given post. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-new-twitter-api-v2-retweets-endpoints/156827). ### Launch of Manage Mutes Endpoints Today, we’re launching new [manage mutes](https://developer.x.com/en/docs/twitter-api/users/mutes/introduction) endpoints to the X API v2. These endpoints will allow authenticated users to mute or unmute accounts. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-new-manage-mutes-endpoints-for-the-twitter-api-v2/156261). We have now reduced the `max_results` that can deliver with the [full-archive search](/x-api/posts/full-archive-search) endpoint when you are requesting the `context_annotations` field with the `tweet.fields` parameter to 100, meaning that you can only pull up to 100 posts with the field per page. ### Reliability Features for Streaming Endpoints Today, we are launching two reliability streaming features for X API v2’s filtered stream and sampled stream called backfill and redundant connections, which are now available on the Academic Research product track. The redundant connections feature enables you to connect to a given stream two times, which can help to ensure that you maximize streaming up-time in case one of your streams experiences a disconnection. Backfill can be utilized by adding the backfill\_minutes parameter and a whole number between one and five to your streaming request, and allows you to request missed data that might have resulted from a disconnected stream. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/new-reliability-and-recovery-functionality-available-for-twitter-api-v2-streaming-endpoints/156244/2). ### Launch of Post Counts Endpoints Today, we’re launching the new [post counts endpoints](/x-api/posts/counts/introduction) to the X API v2. This includes two different endpoints: Recent post counts, which is available to all product tracks, and full-archive post counts, which is currently only available to the Academic Research Product Track. You can use these endpoints to receive the count of posts that matches a specified query. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/introducing-new-tweet-counts-endpoints-to-the-twitter-api-v2/155997). ### Improvements to Teams Today, we’re launching key improvements to the functionality of teams based on feedback we’ve heard from the community. We have updated the style and design of the team page. We also made changes to the invitation flow to make it easier to invite team members or to learn more about why an invitation might have failed. If you have a team account you can check out the changes by viewing the [teams page](https://developer.x.com/en/portal/teams) of the Developer Console. To learn more about teams check out our \[documentation on the subject]\([https://developer.x.com/en/docs/Developer](https://developer.x.com/en/docs/Developer) Console/overview#team) and related \[FAQ]\([https://developer.x.com/en/docs/Developer](https://developer.x.com/en/docs/Developer) Console/faq#dev-portal-management-faq). To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-improvements-to-the-functionality-of-teams/155447/2). ### Retirement of Configuration Endpoint Today, we retired the standard v1.1 GET /help/configuration endpoint. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/retiring-the-1-1-configuration-endpoint/153319). ### Consolidation of oEmbed Endpoint Today, we’re sharing that the v1.1 oEmbed endpoint will be retired and removed on November 23, 2021, so that new features can be supported in a consistent manner. After that date, the only official and supported API endpoint for X oEmbeds (embedded posts and timelines) will be via publish.x.com/oembed. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/consolidating-the-oembed-functionality/154690/2). ### Launch of Blocks and Likes Lookup Endpoints Today, we’re launching the new [blocks](/x-api/users/blocks) and [Likes](/x-api/posts/likes) lookup endpoints to the X API v2. These endpoints enable you to use the v2 API to get information about which posts an account has liked, what users have liked a post, and who you or an authenticated user has blocked. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-twitter-api-v2-likes-lookup-and-blocks-lookup/154353). ### Launch of Manage Likes Endpoints Today, we’re launching the new [manage Likes](https://developer.x.com/en/docs/twitter-api/tweets/likes) endpoints to the X API v2. These endpoints enable you to use the v2 API to like and unlike posts. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-new-manage-likes-endpoints-for-thetwitter-api-v2/152780). ### Launch of Manage Blocks Endpoints Today, we’re launching the new [manage blocks](/x-api/users/blocks) endpoints to the X API v2. These endpoints enable you to block or unblock accounts on behalf of a user using user IDs. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/announcing-manage-blocks-for-twitter-api-v2/152358/2). ### Removal of Support for Embedded Timelines Today we’re sharing our plans for the future of [embedded timeline widgets](https://developer.x.com/en/docs/x-for-websites/timelines/overview). On June 23rd 2021, we plan to retire the Likes, Collections, and Moments timelines. We recommended you use the [Profile](https://developer.x.com/en/docs/x-for-websites/timelines/guides/profile-timeline) and [Lists](https://developer.x.com/en/docs/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 [forum announcement](https://devcommunity.x.com/t/removing-support-for-embedded-like-collection-and-moment-timelines/150313). ### Launch of Manage Follows Endpoints Today, we’re launching the new [manage follows](/x-api/users/follows) endpoints to the X API v2. These endpoints enable you to follow or unfollow accounts on behalf of a user using their user ID. This launch comes just a few months after the launch of the [follows lookup](/x-api/users/follows) endpoints, which allow you to retrieve an account’s followers and who they are following. To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/introducing-the-new-manage-follows-endpoints-to-the-twitter-api-v2/149465). ### Introduction of Academic Research Product Track Today, we are introducing the new [Academic Research product track](/resources/fundamentals/developer-apps) to [X API v2](/x-api/introduction). This update introduces a new application process for Academic Researchers that will provide those that are approved with an Academic Research [Project](/resources/fundamentals/developer-apps) that will unlock greater access and advanced functionality. With the new Academic Research product track, you will be able to access the following: * The new X API v2 [full-archive search endpoint](/x-api/posts/full-archive-search), which is only available to the Academic Research product track at this time. * An increased [post cap](/x-api/fundamentals/post-cap) of 10 million, up from the 500,000 that is available to the [Standard product track](/resources/fundamentals/developer-apps) at the Basic [access level](https://developer.x.com/en/products/x-api/early-access/guide#na_2). * New filter operators available to [recent search](/x-api/posts/recent-search), [full-archive search](/x-api/posts/full-archive-search), and [filtered stream](/x-api/posts/filtered-stream), including `$` (aka cashtag), `bio` (only available via filtered stream), `bio_name` (only available via filtered stream), `bio_location` (only available via filtered stream), `place`, `place_country`, `point_radius`, `bounding_box`, `-is:nullcast`, `has:cashtags` and `has:geo`. * An increased rule limit for filtered stream of 1,000 concurrent rules, up from the 25 that is available to the Standard product track at the Basic access level. * An increased query and rule character limit of 1,024 characters long, up from the 512 character limit that is available to the Standard product tack at the Basic access level To learn more about this launch, please visit our [forum announcement](https://devcommunity.x.com/t/introducing-the-new-academic-research-product-track/148632). ### Addition of Reply Settings Field Today we are launching a new field in the post object called reply\_settings. This field is available on all X API v2 endpoints that return posts including post lookup, hide replies, recent search, sampled stream, filtered stream, follows lookup, user post timeline, and user mention timeline endpoints or by [expanding](/x-api/fundamentals/expansions) the post object in any endpoint. This field indicates how the post author has allowed others to reply to their posts, whether it’s everyone, just the people they mention in the post, or those they follow. The field values returned by the `reply_settings` field include `everyone`, `mentionedUsers`, and `following`. If no conversation controls are set for the post, this will still show up in the post object with `everyone` value for the field. ### Launch of User Post and Mention Timeline Endpoints Today, we’re launching the new [user post timeline and user mention timeline endpoints](/x-api/posts/timelines) to the X API v2. These endpoints enable you to request the posts composed by, or mentioning, a specified account on X. Learn more in the forum post announcement. ### Launch of Follows Lookup Endpoints Today, we’re launching the new [follows lookup endpoints](/x-api/users/follows) to the X API v2. These endpoints enable you to retrieve an account’s followers and who they are following using their user ID. Learn more in the forum post announcement. ### Retirement of Labs v2 Endpoints Retired Labs v2 recent search and hide replies endpoints We have retired the following endpoints. If you were using the Labs versions of these endpoints, you can use the linked migration guides to update your integration to start using the new X API v2 versions: * [Recent search v2](/x-api/posts/search/migrate/overview) * [Hide replies v2](/x-api/posts/hide-replies/migrate) We also have additional details about migration to the new X API v2 via our [migration hub](/x-api/migrate/overview). If you have any questions, please reach out to our [Labs forum category](https://devcommunity.x.com/c/labs/57). ### Retirement of Labs v1 Endpoints On August 12th, 2020, we launched the new X API v2: Early access. With this release, we graduated several endpoints to X API v2 from the Labs program, and announced that the Labs version of the endpoints would remain in a deprecated state for 60 days before retiring them. Today marks 60 days since the launch, meaning that we have retired the following endpoints. If you are using the Labs versions of these endpoints, you can use the linked migration guides to update your integration to start using the new X API v2 versions: * post metrics v1 * Filtered stream v1 * Sampled stream v1 We also have additional details about migration to the new X API v2 via our [migration hub](/x-api/migrate/overview). If you have any questions, please reach out to our [Labs forum category](https://devcommunity.x.com/c/labs/57). ### Addition of Post Consumption Status Bar In the [main dashboard page](https://developer.x.com/en/portal/dashboard) of the new Developer Console, you can now check your usage towards the [post cap](/x-api/fundamentals/post-cap) of 500,000 posts per month. This cap is applied at the Project level, across the following v2 endpoints in Basic access: filtered stream, and recent search. In order to see this feature and use the new v2 endpoints, you will need to activate the [new Developer Console experience](https://developer.x.com/en/portal/opt-in.html). ### Launch of Hide Replies Endpoint Today, we’re launching the new hide replies endpoint into the v2 X API. This endpoint enables you to hide abusive, distracting, or misleading replies to posts – a crucial piece to improving the health of the public conversation. Learn more in the forum post announcement. The same endpoint in X Developer Labs is now deprecated, and it will be retired in 90 days. Learn how to [migrate to the v2 endpoint.](/x-api/posts/hide-replies#comparing-twitter-apis-hide-replies-endpoints) ### Early Access to X API v2 Endpoints Today we announced [Early Access to the first endpoints of the new X API](https://blog.x.com/developer/en_us/topics/tools/2020/introducing_new_twitter_api.html)! The new X API features a new API foundation which will allow us to deliver new functionality faster; an updated data format and new functionality that gives you more control over which fields you receive; new post, user, and media fields; and a set of updated endpoints that enable you to listen to and analyze posts, including the following: * [post lookup](/x-api/posts/post-lookup-by-post-ids) * [User lookup](/x-api/users/user-lookup-by-id) * [Recent search](/x-api/posts/recent-search) * [Filtered stream](/x-api/posts/filtered-stream) * [Sampled stream](/x-api/posts/sample-stream) You can find a full list of the new functionality available, and find migration resources on our \[X API v2: Early Access docs page][https://developer.x.com/en/docs/x-api/early-access](https://developer.x.com/en/docs/x-api/early-access)). Learn more about what we have planned in our “\[Guide to the future of the X API][https://developer.x.com/en/docs/x-api/early-access)”](https://developer.x.com/en/docs/x-api/early-access\)”). ### Deprecation of Labs Endpoints We are deprecating certain Labs endpoints due to the launch of their X API v2 replacements in this release. The following Labs v1 endpoints will be retired 60 days from today's release: * post metrics v1 * Filtered stream v1 * Sampled stream v1 The following Labs v2 endpoint will be retired 90 days from today’s release: * Recent search v2 ### Improvements to Hide Replies We added the ability to unhide a reply. Additionally, previously unhidden replies can now be hidden again via this endpoint. Get more details in the [Hide replies documentation](https://developer.x.comhttps://developer.x.com/en/docs/labs). ### Retirement of Labs v1 Endpoints Today we are retiring the Labs v1 posts and users, hide replies, and recent search endpoints. You can read more about our Labs v1 plan in our [forum announcement](https://devcommunity.x.com/t/update-on-labs-v1-endpoints/138641). ### Addition of Quote Tweets Metric Today, the `quote_tweets` metric is now available for 'unowned' posts via the [/totals](/x-api/enterprise-gnip-2.0/fundamentals/engagement-api#post-insights-engagement) endpoint. This means that you can access the quote count for all posts by using app-only authentication. You can read more about this change in our [forum post](https://devcommunity.x.com/t/how-the-new-retweets-and-comments-metric-is-returned-with-the-twitter-api/139131). ### Addition of New Post Metrics Today we are adding two new non-public metrics subfields, `user_profile_clicks` and `url_link_clicks`, to the post object in the X Developer Labs post lookup endpoints. They will be returned when using the `tweet.fields` parameter with any of the following values: `non_public_metrics`, `organic_metrics`, or `promoted_metrics`. To learn more, please visit the [post lookup API reference](https://developer.x.com/en/docs/labs/tweets-and-users/api-reference/get-tweets-id) ### Changes to Repost Counts Starting today, X web, iOS and Android will be showing the total 'reposts and comments' count per post. Using the X API, the total 'reposts and comments' number matching the X interface can be calculated by adding the `retweet_count` and `quote_count` when available. Additionally `quote_count` will be the total unique accounts that have quoted the post, rather than the total posts quoting the post. For more details, please see our [forum post](https://devcommunity.x.com/t/how-the-new-retweets-and-comments-metric-is-returned-with-the-twitter-api/139131/2). ### Organic and Promoted Metrics Groupings We are adding `organic_metrics` and `promoted_metrics` to the post and media objects in the post lookup endpoints. ### Addition of Metrics to Endpoints We are adding metrics to the post and media objects in the post lookup and recent search endpoints. You can read more about these changes in our [forum announcement](https://devcommunity.x.com/t/adding-metrics-to-the-tweets-and-recent-search-endpoints-in-labs/135315) ### Launch of COVID-19 Endpoints Today we are launching a new COVID-19 stream endpoint and an accompanying compliance endpoint. You can read more about these changes in our [forum announcement.](https://devcommunity.x.com/t/new-covid-19-stream-endpoint-available-in-twitter-developer-labs/135540) ### Updates to Replay API User Profiles Starting today, the Replay API will deliver user profile objects that reflect the referenced user at the time the Replay API is running. This change to deliver current user profile objects is similar to the recent Historical PowerTrack update below from Nov. 25, 2019. ### Addition of Post Annotations We are adding annotations to the post object in the posts and recent search endpoints. You can read more about this change in our [forum announcement](https://devcommunity.x.com/t/adding-annotations-to-tweets-and-recent-search-endpoints-in-labs/135193). ### Updates to Developer Policy Today, we announced a significant [update to the Developer Policy](https://developer.x.com/en/developer-terms/policy). We encourage you to read more about this via our [forum](https://devcommunity.x.com/t/a-new-easier-to-understand-twitter-developer-policy/135038) and [blog](https://blog.x.com/developer/en_us/topics/community/2020/twitter_developer_policy_update.html) posts, and to review the revised policy. The Developer Policy is one of the foundations for your use of the X API and it is important to understand your commitments. ### Launch of Hide Replies Endpoint We want to help people feel safe and comfortable having conversations on X. As part of that, today we’re excited to give post authors more control over the conversations they start by supporting the hide replies feature with a new [endpoint](/x-api/posts/hide-replies). You can read more about this new endpoint in our [forum announcement](https://devcommunity.x.com/t/introducing-the-hide-replies-endpoint/134683). ### Release of v2 Labs Endpoints Today, we are bringing our posts and Users, Recent Search and Hide Replies endpoints to v2 of Labs. We are deprecating Labs v1 endpoints and will fully retire them 90 days after this initial release. We will also keep our v1 documentation available for this duration. You can provide us feedback on this versioning strategy via our [feedback channel](https://twitterdevfeedback.uservoice.com/). We're releasing the following changes to posts and Users: * Introduce [fields](/x-api/fundamentals/fields) as a query parameter * Remove [formats](https://developer.x.com/en/docs/labs#payload) as a query parameter * Add path variable for single ID [lookup](https://developer.x.com/en/docs/labs/overview/versioning/migration-guide#id-lookup-path) in posts and Users * Adjusting the path and query parameters for single and multi-username [lookup](/x-api/migrate/overview#username-lookup-path) in GET /users * Change field name `stats` to `public_metrics` Other changes include: * Remove the `most_recent_tweet_id` expansion in Users * Remove HTML tags from the `source` field in posts to make that field easier to parse The core search, pagination and other functionality of Recent Search will stay the same. We have put together some [migration materials](/x-api/migrate/overview) for when you update from v1 to v2. You can read more about this change in our [forum announcement](https://devcommunity.x.com/t/releasing-a-new-version-of-labs-endpoints/134219). ### Addition of Drop/Undrop Events to Compliance Firehose Today we added new drop/undrop event types to the Compliance Firehose API. You can read more about them in our [documentation.](/x-api/enterprise-gnip-2.0/fundamentals/firehouse) ### Changes to Access Token Management Today, we're making changes to the way that Access Tokens and Access Token Secrets are presented and managed within the [applications Dashboard on developer.x.com](https://developer.x.com/content/developer-twitter/en/apps). In order to make API integrations more secure, we will no longer show the Access Token and Access Token Secret on the Dashboard beyond the first time that these values are generated. After the first time, these credentials can not be retrieved. You will be able to regenerate the tokens on the Dashboard, but this will invalidate your current token and secret. You can read more about this change in our [forum announcement](https://devcommunity.x.com/t/upcoming-changes-to-access-token-and-secret-management/130851). ### Removal of SPDY Support Starting January 15, 2020, all connections to the X API (and all other X domains) will no longer support SPDY connections. You can read more about this change in our [forum announcement](https://devcommunity.x.com/t/removing-support-for-spdy-protocol-on-twitter/132530). ### Launch of Recent Search Endpoint in Labs Today, we are releasing a new endpoint to [search for posts posted in the last 7 days](/x-api/posts/recent-search). Learn more about this launch via our [forum announcement.](https://devcommunity.x.com/t/new-recent-search-endpoint-available-in-labs/133076) ### Update to Twurl CLI A new updated major release of our X API CLI, `[twurl](https://github.com/twitter/twurl)`, is now available. This release includes a number of bug fixes and enhancements and introduces Bearer Token support which helps to add functionality around the Premium and X Developer Labs endpoints. Read more in our [forum post](https://devcommunity.x.com/t/twurl-0-9-5-released/132966). ### Addition of Post Annotations in Streams Today in X Developer Labs, we are releasing new metadata elements to the default post payload. These new fields, rendered as part of the post payload, will provide more contextual information about the post. Learn more about this launch via our [forum announcement](https://devcommunity.x.com/t/tweet-annotations-added-to-the-tweet-object-for-the-sampled-stream-and-filtered-stream-endpoints-in-labs/132407). ### Updates to Historical PowerTrack User Profiles Today, we’re updating our "batch historical posts" endpoint (Historical PowerTrack) to provide user profile location, bio description, and display name information that reflects values in place at the of the job processing. When the updated user objects are delivered, there will be a new "updated" string array in the User/Actor object. There are up to three possible values: name, description, location: `"updated": ["name", "description", "location"]` See the [documentation on Historical PowerTrack's data format](https://aem-author-production-version-6-3.twitter.biz/content/developer-twitter/en/docs/tweets/batch-historical/guides/powertrack-data-format.html) for more details. ### Addition to Account Activity API Today, we’re releasing a new feature to the Account Activity API that will provide developers with the ability to view whether an account that mentions your subscribed user is blocked. You can read more about the `user_has_blocked` [data object structure](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#account-activity-data-object-structure) and check out our [forum announcement](https://devcommunity.x.com/t/new-release-account-activity-api-user-has-blocked-field/131157) for more details. ### Launch of Sampled Stream Endpoint in Labs Today, we are releasing a new endpoint to [stream a sample of all public posts](/x-api/posts/recent-search) in real-time, as they are posted. Learn more about this launch via our [forum announcement.](https://devcommunity.x.com/t/new-sampled-stream-endpoint-available-in-labs/130958) ### Launch of Filtered Stream Endpoint in Labs Today, we are releasing a [new streaming endpoint](/x-api/posts/filtered-stream) to retrieve posts, and an endpoint to manage your filters in X Developer Labs. With these endpoints, you will be able to retrieve up to 500,000 posts per month (maximum 50 posts per second); you can set up to 10 filter rules on your stream (these can be applied at the same time). Learn more about this launch via our [forum announcement](https://devcommunity.x.com/t/new-filtered-stream-endpoints-available-in-labs-and-the-future-of-statuses-filter/129888). Requests parameters documented in the API reference pages are now clearly marked as either optional or required. Additionally, API reference pages will describe a JSON body payload when this is allowed by a request. ### Launch of Post Metrics in Labs You can now retrieve engagement data for any post or list of posts from owned/authorized accounts. The posts you query must be no older than 30 days and cannot be reposts. [GET /tweets/metrics/private](https://developer.x.com/en/docs/labs/tweet-metrics/overview.html) supports the following metrics: * `impressions` * `reposts` * `quotes` * `likes` * `replies` * `video views` * `video view quartiles` (where the requester is also the author of the media) Learn more about this launch via our [forum announcement](https://devcommunity.x.com/t/new-twitter-developer-labs-release-metrics-endpoint/129122). ### Changes to DM Permissions Starting today, applications that need to create Direct Messages will be required to have "Read, Write, and Direct Messages" app permissions (RW+DM). Applications attempting to create Direct Messages with only the RW app permission will receive the following error: `{"code": 93,"message": "This application is not allowed to access or delete your direct messages."}` You can learn more about this change via our [forum announcement](https://devcommunity.x.com/t/updates-to-app-permissions-direct-message-write-permission-change/128221). ### Addition of Pinned Post Expansion `pinned_tweet_id` will expand a user’s pinned post through our user object expansion. ### Addition of Quote Counts Metadata Developers can use the new Labs endpoints to pull counts of quotes. ### Addition of Most Recent Post Expansion `most_recent_tweet_id` will expand a user’s most recent post through our user object expansion. Learn more about this launch via our [forum announcement](https://devcommunity.x.com/t/update-to-twitter-developer-labs/128060). ### Requirement for TLS 1.2 Today, all connections to the X API (and all other X domains) will require TLS 1.2. You can read more about this change in our [forum announcement](https://devcommunity.x.com/t/removing-support-for-legacy-tls-versions-1-0-1-1-on-twitter/126648/2). ### New Account Activity API Endpoint Starting today, we are introducing the new [Account Activity API endpoint](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#api-reference) to deactivate user subscriptions using application-only OAuth. This new endpoint offers developers the convenience of only having to provide a bearer token to deactivate a subscription, without requiring the subscribed user’s access token. We are immediately marking the existing enterprise Account Activity API endpoint used to deactivate user subscriptions with 3-Legged OAuth as deprecated. The endpoint will be retired and no longer be available starting from January 15, 2020. Developers can learn more about this API in our [documentation](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#api-reference). ### New Expansions for Tweets Endpoint We added two new expansions to the \[GET /tweets][https://developer.x.com/en/docs/labs/tweets-and-users/api-reference/get-tweets.html](https://developer.x.com/en/docs/labs/tweets-and-users/api-reference/get-tweets.html)) endpoint: entities.mentions.username will expand the username of any recognized user mention in a post into a full user object. `referenced_tweets.id.author_id` expands the ID of the author of any referenced post, when one of the posts requested is a repost, Quoted post or Reply. This expansion will also expand the referenced post ID into a full object. ### Support for Tweets and Users Endpoints in Labs The first two endpoints are now available via the Labs portal. Make sure you have an approved developer account and have applied for Labs access to get started with these endpoints. [Key differences](https://developer.x.com/content/developer-twitter/en/docs/labs/overview/whats-new) from equivalent v1.1 functionality include: * Single endpoint for single object "show" function and batch "lookups" * \[Expansion parameters]) support the inclusion of additional objects as part of one request (e.g. mentioned users, referenced posts, e.g.) * \[Format parameters]) support different data projections with more or less verbose payloads * Rate limiting is at the app level, regardless of auth method (user context or bearer token) Learn more about this launch via our [forum announcement](https://devcommunity.x.com/t/twitter-developer-labs-is-open-to-all-developers/126717). ### Retirement of Terms and Privacy Endpoints We have fully retired the Terms of Service and Privacy Endpoints. The current status of our [Privacy Policy](https://x.com/en/privacy) and [Terms of Service](https://x.com/en/tos) is best served by our up-to-date web pages. You can learn more about this change via our [forum announcement](https://devcommunity.x.com/t/terms-of-service-and-privacy-endpoints-will-no-longer-serve-content-after-june-10/125714). ### Changes to User Object Fields Today some user object fields, including user.lang, will start returning 'null' for updated metadata fields previously announced in our [forum post](https://devcommunity.x.com/t/upcoming-changes-to-user-object-and-get-users-suggestions-endpoints/124732). Developers can learn about this change through our [documentation.](/x-api/fundamentals/data-dictionary#user) ### Launch of Account Activity Replay API Starting today, we are introducing the [Account Activity Replay API](/x-api/enterprise-gnip-2.0/fundamentals/account-activity), a data recovery tool available with the enterprise tier of the [Account Activity API](/x-api/enterprise-gnip-2.0/fundamentals/account-activity) The [Account Activity Replay API](/x-api/enterprise-gnip-2.0/fundamentals/account-activity) allows you to retrieve events from as far back as five days. It should be utilized to recover data in scenarios where your [webhook](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#api-reference) server misses events Developers can learn more about this API in our [documentation](/x-api/enterprise-gnip-2.0/fundamentals/account-activity). ### Support for Native Media in Quotes Starting today, native media (photos, videos, and GIFs) can be added to quotes on X, which will be shown as additional media metadata in the entities sections of quote payloads. Developers can learn more about this in our [documentation](https://developer.x.com/en/docs/x-api/v1/data-dictionary/object-model/tweet). ### Addition of is:reply Operator Today, the operator is:reply becomes available to use with the [enterprise search APIs](/x-api/enterprise-gnip-2.0/fundamentals/search-api) and the paid version of the [premium search endpoints](https://developer.x.com/content/developer-twitter/en/docs/tweets/search/api-reference/premium-search). The is:reply Boolean operator can be used to filter all replies to posts, or to filter out explicit replies that match a rule. This operator functions in the same way as the is:quote operator. Remember that, with the Search API, all ‘is:’ and ‘has:’ operators must be used in conjunction with a standalone operator like a keyword or hashtag. You can learn more about this operator in the [documentation](/x-api/enterprise-gnip-2.0/fundamentals/rules-filtering). ### Changes to Expanded URL Enrichment Today, the expanded URL enrichment will no longer contain the unwound URL object and metadata (including the title and description fields) for URLs linking to posts, Moments, or profiles. Please note that other URLs (to websites, etc.) will continue to be enriched with this metadata, and the url, expanded\_url, and `display_url` will still be available. This change applies to [Decahose](/x-api/enterprise-gnip-2.0/fundamentals/decahose-api), [PowerTrack](/x-api/enterprise-gnip-2.0/powertrack-api), [Historical PowerTrack](https://developer.x.com/content/developer-twitter/en/docs/tweets/batch-historical/overview), and the [Search APIs](https://developer.x.com/en/docs/x-api/v1/tweets/search/overview). ### Addition of is:reply Operator in PowerTrack Today, we are introducing a new operator, is:reply, to help you narrow conversations to those you care about most. You can now filter out replies from your results (in addition to the pre-existing ability to filter out reposts, is:retweet, and Quoted posts, is:quote). This new operator is available today within [PowerTrack](/x-api/enterprise-gnip-2.0/powertrack-api), [Historical PowerTrack](https://developer.x.com/content/developer-twitter/en/docs/tweets/batch-historical/overview), and [Replay](/x-api/enterprise-gnip-2.0/powertrack-api#replay-api). The is:reply Boolean operator can be used to filter all replies to posts, or to filter out explicit replies that match a rule. This operator functions in the same way as the is:quote operator. You can learn more about this operator in the [documentation](/x-api/enterprise-gnip-2.0/fundamentals/rules-filtering). ### Access to Apps in Developer Console Today, we've added the ability for developers to view and edit their existing [X apps](/resources/fundamentals/developer-apps) via the [X app dashboard](https://developer.x.com/content/developer-twitter/en/apps) on developer.x.com as long as they're logged into their X account. Previously, you could only view and edit your existing X apps on developer.x.com if you had applied or been approved for a [developer account](/resources/fundamentals/developer-portal). You still must have an approved developer account to be able to create new X apps. ### Support for Video Subtitles Today, we are announcing that advertisers and publishers are now able to add subtitle files (SRT) to their videos via ads.x.com, Media Studio and our publisher upload API. Subtitles will be viewable on auto-playing video (when no sound is available) on Android and Web. We will release the functionality for iOS in the coming weeks. To read more about the new subtitles endpoints, please review their respective API reference pages: * [POST media/subtitles-create](/x-api/media/create-media-subtitles) * [POST media/subtitles-delete](/x-api/media/delete-media-subtitles) ### Update to Account Activity API Endpoint Today we are announcing an update to the [Account Activity API](/x-api/enterprise-gnip-2.0/fundamentals/account-activity) endpoint, [GET subscriptions/count](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#get-direct-messages-events-list#get-account-activity-subscriptions-count). To enable greater visibility into your billing details and use of the API, beginning today, you can programmatically pull the number of provisioned subscriptions associated with your Account Activity API instance within the JSON. This means you can now see both what your provisioned number of subscriptions are, and how close you are to hitting that limit via the GET subscriptions/count API endpoint. This update has been documented with the [Account Activity API documentation](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#get-direct-messages-events-list#get-account-activity-subscriptions-count). ### New Rate Limits on POST Endpoints Today we are implementing new app-level rate limits on several of our standard POST endpoints: * [POST statuses/update](https://developer.x.com/en/docs/x-api/v1/tweets/post-and-engage/api-reference/post-statuses-update) * [POST statuses/retweet/:id](https://developer.x.com/en/docs/x-api/v1/tweets/post-and-engage/api-reference/get-statuses-retweets-id) * [POST favorites/create](https://developer.x.com/en/docs/x-api/v1/accounts-and-users/follow-search-get-users/api-reference/post-friendships-create) * [POST direct\_messages/events/new](https://developer.x.com/en/docs/x-api/v1/direct-messages/sending-and-receiving/api-reference/new-event) * [POST friendships/create](https://developer.x.com/en/docs/x-api/v1/accounts-and-users/follow-search-get-users/api-reference/post-friendships-create) You can read more about this change in our [forum announcement](https://devcommunity.x.com/t/new-post-endpoint-rate-limit-enforcement-begins-today/115355). ### Update to twitter-text Library Today, we are announcing an update to the [twitter-text](https://github.com/twitter/twitter-text) library to account for the recent changes to the way that X counts emojis. If an emoji previously counted as more than two characters, its count will now be reduced to just two characters to allow for users to make the most out of their 280 characters. You can read more about this change in our [forum announcement](https://devcommunity.x.com/t/new-update-to-the-twitter-text-library-emoji-character-count/114607). ### Retirement of Legacy DM Endpoints We have fully retired the legacy Direct Message endpoints. You can find a list of the retired DM endpoints, as well as their replacements on \[this page]\([https://developer.x.com/content/developer-twitter/en/docs/direct-messages/sending-and-receiving/api-reference](https://developer.x.com/content/developer-twitter/en/docs/direct-messages/sending-and-receiving/api-reference). You can learn more about this update via our [forum announcement](https://devcommunity.x.com/t/details-and-what-to-expect-from-the-api-deprecations-this-week-on-august-16-2018/110746). ### Changes to DM Media Access Today, we are implementing some changes to the process where you [retreive attached media from Direct Messages](https://developer.x.com/en/docs/x-api/v1/direct-messages/message-attachments/overview). We will no longer support accessing `media_url` or `media_url_https` via an authenticated [www.x.com](http://www.x.com) session. The request to fetch `media_url_https` MUST always be signed with the user’s access token using OAuth 1.0A. You can learn more about this update via our [forum announcement](https://devcommunity.x.com/t/direct-message-api-change-to-how-apps-can-access-images-sent-in-direct-messages/112722). ### Retirement of User and Site Streams We have fully retired [User Streams](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction) and [Site Streams](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction). You can learn more about this update via our [forum announcement](https://devcommunity.x.com/t/details-and-what-to-expect-from-the-api-deprecations-this-week-on-august-16-2018/110746). ### New Version of Usage API Today we are launching a new version of the Usage API. With this update, we’re making the API more stable by moving to a new system. With the system update, users will experience a data "hole" as developers will only be able to pull data back to May 1, 2018. Therefore for the upcoming year, they will be missing out on a few months of historical data (until June 2019). A product name is changing within the Usage API from Historical PowerTrack Subscription to Historical PowerTrack 2.0 which will make the product naming consistent with the rest of our user-facing wording. Customers will receive the exact same data in the same format with the update. You will see this change within the products.type JSON object. If you have any questions, please reach out to your account manager. ### API Deprecations The [Account Activity API DM Beta](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction) product was fully retired today. The following services and endpoints will experience degraded service starting today: * [User Streams](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction) * [Site Streams](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction) * [GET direct\_messages](https://developer.x.com/en/docs/x-api/v1/direct-messages/sending-and-receiving/api-reference/list-events) * [GET direct\_messages/sent](https://developer.x.com/content/developer-twitter/en/docs/direct-messages/sending-and-receiving/api-reference/get-sent-message) * [GET direct\_messages/show](https://developer.x.com/content/developer-twitter/en/docs/direct-messages/sending-and-receiving/api-reference/get-message) * [POST direct\_messages/new](https://developer.x.com/content/developer-twitter/en/docs/direct-messages/sending-and-receiving/api-reference/new-message) * [POST direct\_messages/destroy](https://developer.x.com/content/developer-twitter/en/docs/direct-messages/sending-and-receiving/api-reference/delete-message) User Streams and Site Streams will be fully retired on August 23rd. The legacy Direct Messages endpoints will be fully retired on September 17th. You can learn more about this update via our [forum announcement](https://devcommunity.x.com/t/details-and-what-to-expect-from-the-api-deprecations-this-week-on-august-16-2018/110746). ### Changes to App Creation and Rate Limits As of today, you will no longer be able to create new [X apps](/resources/fundamentals/developer-apps) via [apps.x.com](https://apps.x.com/). You will now be redirected to either your [Developer Console](/resources/fundamentals/developer-portal) account or, if you don't have a Developer Console account yet, to the page where [you can apply](https://developer.x.com/content/developer-twitter/en/apply-for-access). We also announced that we will be implementing new app-level rate limits to the following POST endpoints on September 10th, 2018. * [POST statuses/update](https://developer.x.com/en/docs/x-api/v1/tweets/post-and-engage/api-reference/post-statuses-update) * [POST statuses/retweet:id](https://developer.x.com/en/docs/x-api/v1/tweets/post-and-engage/api-reference/post-statuses-retweet-id) * [POST friendships/create](https://developer.x.com/en/docs/x-api/v1/accounts-and-users/follow-search-get-users/api-reference/post-friendships-create) * [POST favorites/create](https://developer.x.com/en/docs/x-api/v1/tweets/post-and-engage/api-reference/post-favorites-create) * [POST direct\_messages/events/new](https://developer.x.com/en/docs/x-api/v1/direct-messages/sending-and-receiving/api-reference/new-event) Finally, we are introducing a new option for people to report suspected violations of our platform policies to you for review. You can learn more about all of these updates via our [blog post](https://cli.re/g32qKr). ### App Management in Developer Console If you have a [Developer Console](/resources/fundamentals/developer-portal) account, you can now create and manage your [X apps](/resources/fundamentals/developer-apps). Please read our \[forum post]\([https://devcommunity.x.com/t/app-creation-and-management-now-available-in-the-Developer](https://devcommunity.x.com/t/app-creation-and-management-now-available-in-the-Developer) Console/107723) for more details. ### Changes to Sign in with X and Account Activity Today, we started requiring that you register the [callback URLs](/resources/fundamentals/developer-apps#callback-urls) that you use with the Sign in with X process. You can read more about this update [here](https://devcommunity.x.com/t/action-required-sign-in-with-twitter-users-must-whitelist-callback-urls/105342). We also announced the addition of the tweet\_delete\_events activity to the Account Activity API. This new activity will be sent for those corresponding deleted events to enable developers to more easily provide a compliant experience for their customers and application users. You can read more about this new activity in our [forum post](https://devcommunity.x.com/t/adding-delete-events-to-the-account-activity-api/106783) or on our [documentation](/x-api/enterprise-gnip-2.0/fundamentals/account-activity). ### Updates to Terms and Policies Today, we made some changes to our [Terms of Service](https://x.com/en/tos), [Privacy Policy](https://x.com/en/privacy), and \[X Developer Agreement][https://developer.x.com/en/developer-terms/agreement-and-policy](https://developer.x.com/en/developer-terms/agreement-and-policy) that include privacy, security, and data protection updates. Please read our [forum post](https://devcommunity.x.com/t/upcoming-changes-to-the-developer-platform/104603) for more details. ### Timezone Fields Made Private Today, the timezone values in X user objects became private fields. From here on out, all\* time\_zone and utc\_offset data objects will return as null. Please read our [forum post](https://devcommunity.x.com/t/upcoming-changes-to-the-developer-platform/104603) for more details. \* They will continue to be available on the account/settings endpoint, for authenticated users only. ### Removal of Klout Data Today, we removed all Klout data from post payloads as part of our GDPR updates. ### Changes to Profile Background and Account Activity Today, we are changing all instances of profile\_background\_image\_url and profile\_background\_image\_url\_https to their default values. Please read our [forum post](https://devcommunity.x.com/t/upcoming-changes-to-the-developer-platform/104603) for more details. In addition to the above change, we are adding a new field to the [Account Activity API](/x-api/enterprise-gnip-2.0/fundamentals/account-activity) payloads to reference which subscription the activity was delivered for. This JSON object is called for\_user\_id and will include the subscribed user's ID who produced that activity as its value. You can see some examples of this new JSON object in our [account activity objects](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#account-activity-data-object-structure) page. ### Changes to Quote Payload Rendering Today, we are adding a change to the way that URLs will be rendered in the quote payload. We’re adding a new entity called the "quoted\_status\_permalink" entity to ensure the quoted post can be referenced and we will be removing the t.co link from the quoted post "text" field. With the new format, the t.co link is no longer appended to the "text" field of the quoted post in the "quoted\_status" object. Further, we will no longer put the quoted post t.co url, expanded\_url, and display\_url in the ‘urls’ entity. Instead, these will be added to a new "quoted\_status\_permalink" object with "url", "expanded", and "display" URL attributes. This means that the "quoted\_status\_permalink" object will be reserved for the t.co link back to the quoted post, whereas the "entities.urls" array can be used to identify any links shared within the original post or quote. Please read our [forum post](https://devcommunity.x.com/t/updating-how-urls-are-rendered-in-the-quote-tweet-payload/105473) and [updated documentation](https://devcommunity.x.com/t/upcoming-changes-to-the-developer-platform/104603) for more details. ### Deprecation of X Kit The announcement of [X Kit](https://developer.x.com/en/docs/x-for-websites) deprecation, after October 31, 2018 X Kit will no longer be actively maintained. Please read our [blog post](https://blog.x.com/developer/en_us/topics/tools/2018/discontinuing-support-for-twitter-kit-sdk.html) for more details. ### Platform Updates for GDPR Today we announced several updates to the platform related to GDPR. Please read our [forum post](https://devcommunity.x.com/t/upcoming-changes-to-the-developer-platform/104603) for more details. ### Addition of Additional Media Info Object The addition of a new object to the post payload – additional\_media\_info object – and also restricting some video details (video\_info) for promoted posts where advertisers have requested we limit video playback to X owned clients. You can find additional information about this change at the following link: [Extended Entities Objects > post with native video](https://developer.x.com/en/docs/x-api/v1/data-dictionary/object-model/extended-entities). ### Launch of Premium Full-Archive Search Launch of the [premium full-archive search endpoint](https://blog.x.com/developer/en_us/topics/tools/2018/access-the-full-history-of-tweets.html). ### Launch of Account Activity APIs Launched both ([All Activities](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction)) and ([Direct Messages](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction)) Standard beta Account Activity APIs, read more about this launch [on our blog](https://blog.x.com/developer/en_us/topics/tools/2017/announcing-more-functionality-to-improve-customer-engagements-on-twitter.html). Announced User streams & Site streams deprecation and sunset date of Tuesday June 19, 2018. ### Update to twitter-text Library Version 2.0 of twitter-text is now available [on GitHub](https://github.com/twitter/twitter-text). This update addresses the change in our character limit that was rolled out on November 7th, 2017. To learn more about this update, please [visit our forum](https://devcommunity.x.com/t/shipped-twitter-text-2-0/98577). ### Launch of Premium APIs Launched the Premium APIs. These new APIs build on the quality and stability of our enterprise data platform, and make it available more widely via a tiered pricing model. For the first time, you can start to search across 30 days of posts (instead of the limited 7 days of data on the standard endpoint), and optionally, you can also access our premium enrichments for profile geo, URL expansion, and poll data. Here’s a taste of some of the new features: * More posts per request (Sandbox + Premium) * A far more rich query language enabling more complex queries (Sandbox + Premium) * A counts endpoint that returns time-series counts of posts (Premium) * Metadata enrichments, such as expanded URLs and Profile Geo (Premium) Read more about this launch [on our blog](https://blog.x.com/developer/en_us/topics/tools/2017/introducing-twitter-premium-apis.html). ### Launch of 280 Character Posts Official launch of 280 character posts in languages where cramming was an issue. Please see our [forum announcement](https://devcommunity.x.com/t/updating-the-character-limit-and-the-twitter-text-library/96425) or official [blog post](https://blog.x.com/official/en_us/topics/product/2017/tweetingmadeeasier.html) for further reference. ### Removal of DM Commands in Statuses/Update The ability to send Direct Messages via the statuses/update API endpoint has been completely removed, and the `enable_dm_commands` and `fail_dm_commands` parameters will no longer have any effect. Read more about this update [on our forum](https://devcommunity.x.com/t/retiring-legacy-dm-commands-on-the-standard-tweet-api/86653 "on our forum"). ### Changes to OAuth Login Flow A change was made to X’s backend OAuth API endpoints which altered the behaviour of the /oauth/authenticate endpoint to match that of the /oauth/authorize endpoint. Read more about this update [on our forum](https://devcommunity.x.com/t/recent-changes-to-twitter-s-oauth-login-flow-and-api-endpoints/94943). ### Updates to Engagement API Metrics The Engagement API was updated to use the same metrics aggregation methodology in use by the X analytics dashboard. Read more about this update [on our forum](https://devcommunity.x.com/t/changes-to-the-engagement-api/94523). ### Changes to DM Commands Failure The default value of the `fail_dm_commands` parameter within the DM statuses/update endpoint has been switched to *true*. All status updates intended as Direct Messages will start to return errors (unless `enable_dm_commands` is *false*, in which case the post will be posted). Read more about this update [on our forum](https://devcommunity.x.com/t/retiring-legacy-dm-commands-on-the-standard-tweet-api/86653). ### Treatment of 280 Character Tweets The response payload for 280 character tweets will be treated the same way as long tweets. Read more about this update [on our forum](https://devcommunity.x.com/t/testing-280-characters-for-certain-languages/94126). ### Addition of Additional Media Info Adding new data to the post payload (`additional_media_info object`) and also restricting some video details (`video_info`) for promoted posts where advertisers have requested we limit video playback to X owned clients. You can find additional information about this change at the following link: [Extended Entities Objects > post with native video](https://developer.x.com/en/docs/x-api/v1/data-dictionary/object-model/extended-entities) ### End of Support for url\_contains Operator in 30-Day Search 30-Day and Full-Archive Search ended support for url\_contains: operator. Deprecation of this operator was announced as part of the Gnip 2.0 migration in August 2016. Any Search query using the url\_contains: Operator will be rejected as invalid. ### Upcoming Removal of url\_contains Operator in Search APIs The 30-Day Search API (both 30-Day and Full-Archive) will no longer support the url\_contains: operator beginning 30 days from today, or after August 4, 2017 (Support of url\_contains: will actually end with the first deploy after that date). Deprecation of this operator was announced as part of the Gnip 2.0 migration in August 2016. When Operator support is ended, any query using the url\_contains: will be rejected as invalid. ### Retirement of xAuth The xAuth authentication mechanism has been removed from all X APIs Read more about this update [on our forum](https://devcommunity.x.com/t/retirement-of-xauth/88022). ### HTTP Headers Forced to Lowercase All HTTP headers have been forced into lowercase (`content-type`, `x-rate-limit-remaining`, `x-access-level` etc). Read more about this update [on our forum](https://devcommunity.x.com/t/upcoming-http-header-changes/86715). ### Optional Parameters for DM Commands The optional `enable_dm_commands` parameter to statuses/update will enable applications to remove DM command support early, before the transition period ends. The default value is *true* (i.e. current legacy behavior), but this may be set to *false* to get the new, post-November 1 behavior. The optional fail\_dm\_commands parameter to statuses/update will make DM commands return HTTP 403 (error code 151) from the API when set to *true*. The default value is *false*. Read more about this update [on our forum](https://devcommunity.x.com/t/retiring-legacy-dm-commands-on-the-standard-tweet-api/86653). ### Support for Emojis in Rules Rules with emojis are now available in the enterprise Search APIs ### Wider Availability of Direct Message APIs Access to several new Direct Message APIs are now more widely available. Read more about this update [on our blog](https://blog.twitter.com/developer/en_us/topics/tools/2017/new-apis-to-power-the-future-of-customer-engagement-in-direct-me.html). ### Retirement of MPEG-DASH Video Support MPEG-DASH URLs (.mpd) has been removed from the payload of a post that contains video. Read more about this update [on our forum](https://devcommunity.x.com/t/retiring-mpeg-dash-video-support-on-march-9th-2017/82761). ### New Enrichments and Payload Changes New Enrichment! Poll Metadata is now available through our enterprise APIs. See the [documentation](/x-api/enterprise-gnip-2.0/fundamentals/account-activity#migration-introduction) for more details. New payload field: Image Alt-Text (decription) field may be present in the entities.media\[] and extended\_entities.media\[] section of the payload if a user chooses chooses the "Add description" option when adding a photo to a post. This is available in enriched native format only. Default payload format: Newly created streams on Gnip products will now default to orriginal format JSON. ### Updates to Historical PowerTrack and Replay API Historical PowerTrack API & Replay API * quote filtering is now supported * Quoted posts are now fully rehydrated within HPT and Replay post payloads ### New Payload Fields for Counts New payload fields: quote\_count and reply\_count are now available in native enriched format payloads across Realtime and Historical APIs ### Support for Geo Operators in Full-Archive Search Full-Archive Search API (FAS): * Added support for additional geo operators: * place: * place\_country: * has:profile\_geo: * profile\_country: * profile\_region: * profile\_locality: ### Rule Management by ID in PowerTrack Rule management (GET and DELETE) by Rule ID is now available for PowerTrack 2.0 and PowerTrack Replay 2.0 ### Support for Geo Operators in 30-Day Search 30-Day Search API: * Added support for additional geo operators: * place: * place\_country: * has:profile\_geo: * profile\_country: * profile\_region: * profile\_locality: # Enterprise API Source: https://docs.x.com/enterprise-api/introduction Enterprise-grade access to X's full firehose, volume streams, and dedicated support The X API Enterprise plan provides the highest level of access to X data. Get complete firehose coverage, volume streams, dedicated account management, and custom rate limits designed for organizations that depend on X data at scale. Apply for Enterprise access with a dedicated account team. Explore all available endpoints, including Enterprise-exclusive ones. Official Python and TypeScript libraries. *** ## Why Enterprise? Enterprise access includes everything in the pay-per-use X API plus exclusive high-volume endpoints, dedicated support, and custom packages tailored to your needs. Stream 100% of public posts in real-time. No sampling, no limits. Get every post as it happens. Access full-volume and language-specific streams, including English, Japanese, Korean, and Portuguese firehoses. Get a dedicated account manager, personalized technical support, and priority issue resolution. Higher rate limits and custom-tailored packages to match your throughput requirements. Access post and media analytics endpoints for deep engagement insights across large datasets. Stay compliant with real-time compliance event streams for posts, users, and likes. *** ## Enterprise-exclusive endpoints These endpoints are only available on Enterprise plans: Full firehose, language-specific streams, and sampled streams. Stream all likes or sampled likes in real-time. High-performance filtered streaming with advanced operators. Deep analytics for post and media engagement. Subscribe to real-time user activity events including posts, DMs, likes, and follows. Receive filtered stream data via webhooks instead of persistent connections. *** ## What you can build Enterprise access powers the most demanding use cases on X. Search, retrieve, and publish posts. Access timelines, threads, and quote posts. Look up users, manage follows, blocks, and mutes. Find live audio conversations and their participants. Send and receive private messages. Create and manage curated lists of accounts. Access trending topics by location. *** ## Key features ### Complete real-time coverage Stream 100% of public posts as they happen. No sampling, no gaps. Enterprise firehose access gives you the complete picture of public conversation on X. Available streams: * **All posts** - Every public post in real-time * **English posts** - All English-language posts * **Japanese posts** - All Japanese-language posts * **Korean posts** - All Korean-language posts * **Portuguese posts** - All Portuguese-language posts * **Sampled streams** - 1% and 10% random samples [Learn more about volume streams](/x-api/posts/volume-streams/introduction) ### Rich data objects Access detailed, structured data for posts, users, media, and more: * **Posts**: Full text, metrics, entities, annotations, conversation threads * **Users**: Profiles, follower counts, verification status * **Media**: Images, videos, GIFs with metadata * **Polls**: Options and vote counts Customize responses with [fields](/x-api/fundamentals/fields) and [expansions](/x-api/fundamentals/expansions) to get exactly the data you need. ### Filtered stream Get posts delivered in real-time as they're published. Define up to 1,000 filtering rules to receive only matching posts. ```bash theme={null} # Add a rule curl -X POST "https://api.x.com/2/tweets/search/stream/rules" \ -H "Authorization: Bearer $TOKEN" \ -d '{"add": [{"value": "from:xdevelopers"}]}' # Connect to stream curl "https://api.x.com/2/tweets/search/stream" \ -H "Authorization: Bearer $TOKEN" ``` [Learn more about filtered stream](/x-api/posts/filtered-stream/introduction) ### Full-archive search Search the complete history of public posts back to 2006. Build queries with operators for users, keywords, dates, and more. ```bash theme={null} curl "https://api.x.com/2/tweets/search/all?query=AI%20lang:en" \ -H "Authorization: Bearer $TOKEN" ``` ### Engagement metrics Access deep engagement analytics including impressions, likes, reposts, replies, video views, and media-level metrics. [Learn more about search](/x-api/posts/search/introduction) *** ## Enterprise vs. pay-per-use | Feature | Pay-per-use | Enterprise | | :--------------------- | :---------------------- | :--------------------------------- | | **Post search** | Recent and full-archive | Recent and full-archive | | **Filtered stream** | Up to 1,000 rules | 5,000+ rules | | **Volume streams** | - | Full firehose and language streams | | **Likes streams** | - | Full and sampled likes | | **Powerstream** | - | Advanced filtered streaming | | **Engagement metrics** | - | Post and media analytics | | **Account Activity** | - | Real-time user event subscriptions | | **Monthly post cap** | 2 million reads | Custom / unlimited | | **Rate limits** | Standard | Custom / elevated | | **Support** | Community forum | Dedicated account manager | *** ## Get started [Contact our sales team](/forms/enterprise-api-interest) to discuss your needs and get a custom package. Your dedicated account manager will help you set up credentials and configure your access. Use the same modern v2 API endpoints plus Enterprise-exclusive endpoints for your integration. ```bash theme={null} curl "https://api.x.com/2/users/by/username/xdevelopers" \ -H "Authorization: Bearer $BEARER_TOKEN" ``` *** ## Tools & libraries Official Python library with async support. Official TypeScript/JavaScript library. Interactive API explorer. [Browse all libraries](/tools-and-libraries) *** ## Support Enterprise customers get a dedicated point of contact for technical and account support. Get help from the community and X team. # X Developer Platform Status Source: https://docs.x.com/status All systems are operational. Normal Normal Normal *** ## Incident History ### April 2026 Incident has been resolved. | **April 14, 21:00 UTC - 21:30 UTC** Incident has been resolved. | **April 1, 20:15 UTC - 20:30 UTC** ### March 2026 Incident has been resolved. | **March 31, 20:45 UTC - 21:00 UTC** Incident is ongoing. | **March 24, 02:00 UTC - Current** Incident has been resolved. | **March 27, 23:20 UTC - March 28, 00:56 UTC** Incident has been resolved. | **March 24, 15:20:00 UTC - 18:00:00 UTC** ### February 2026 Incident has been resolved. | **February 16, 18:20 UTC - 19:50 UTC** Incident has been resolved. | **February 16, 13:27 UTC - 14:29 UTC** ### 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** Incident has been resolved. | **January 16, 15:39 UTC - 21:00 UTC** ### December 2025 Incident has been resolved. | **December 5, 7:00 UTC - October 15, 8:40 UTC** ### September 2025 Incident has been resolved. | **September 10, 20:30 UTC - September 11, 04:30 UTC** Incident has been resolved. | **September 10, 20:30 UTC - September 10, 22:00 UTC** ### August 2025 Incident has been resolved. | **August 12, 01:00 UTC - August 12, 13:30 UTC** ### May 2025 Incident has been resolved. | **June 26, 17:00 UTC - June 26, 17:45 UTC** Incident has been [resolved](https://downdetector.com/status/google/). | **June 12, 15:00 UTC - June 12, 23:00 UTC** Incident has been resolved. | **May 30, 20:20 UTC - May 30, 21:00 UTC** Incident has been resolved. | **May 30, 18:45 UTC - May 30, 21:00 UTC** Incident has been resolved. | **May 28, 17:00 UTC - May 28, 21:41 UTC** Incident has been resolved. | **May 23, 17:35 UTC - May 27, 00:00 UTC** Incident has been resolved. | **May 22, 18:00 UTC** Incident has been resolved. | **May 09, 05:00 - May 09, 07:00 UTC** ### April 2025 Incident has been resolved. | **Apr 02, 17:10 - Apr 02, 20:30 UTC** Incident has been resolved. | **Apr 02, 13:09 - Apr 02, 13:51 UTC** ### March 2025 Incident has been resolved. | **Mar 10, 12:00 - Mar 11, 00:00 UTC** ### February 2025 Incident is ongoing. | **Feb 6, 00:00** Incident has been resolved | **Feb 3, 17:30 - Feb 4, 01:00 UTC** ### January 2025 There are no past incidents. ### December 2024 There are no past incidents. # Success Stories Source: https://docs.x.com/success-stories Companies and individuals all over the world have used the X Developer Platform to creatively innovate, gain valuable insights, and shape the future. ## Browse success stories # Tutorials Source: https://docs.x.com/tutorials Instructions and examples to help you get started. Learn how to explore a user's Posts and mentions using the user Post timeline and user mention timeline endpoints from the last 7 days.

[**View tutorial**](/tutorials/explore-a-users-posts)
Learn how to start using Postman to make requests to the X API and X Ads API.

[**View tutorial**](/tutorials/postman-getting-started)
Learn about using R to connect to the user lookup endpoint and how to work with JSON returned from X API v2.

[**View tutorial**](/tutorials/getting-started-with-r-and-v2-of-the-x-api)
Learn to use the full-archive search endpoint to search the complete history of public X data, build a dataset by retrieving geo-tagged Posts, and how to page through the available Posts for a query.
[**View tutorial**](/tutorials/getting-historical-posts-using-the-full-archive-search-endpoint)
This guide gives a high-level overview on how to ingest Posts at scale, and “slice and dice” those Tweets via metadata to narrow them down to a specific category, or sub-categories.
[**View tutorial**](/tutorials/post-processing-x-data-with-the-google-cloud-platform)
# Explore a user's Posts and mentions with the X API v2 Source: https://docs.x.com/tutorials/explore-a-users-posts The user Post timeline and user mention timeline endpoints allow developers to retrieve the public Posts composed by, or mentioning a user. ## Introduction While the [recent search endpoint](/x-api/posts/search/introduction) allows you to only get Posts published in the last 7 days, the user Post timeline and user mention timeline endpoints allow you to retrieve Posts and mentions that are older than the last 7 days, for an authorized user (using the user ID). Developers can use these endpoints to study topics, entities and sentiment of Posts from a user’s timeline or mentions. In this tutorial, we will show you how to explore a user’s Posts and mentions using the user Tweet timeline and user mention timeline endpoints.   ## Prerequisites * In order to use the user Tweet timeline and user mention timeline endpoints, you will need to have a valid developer account.  * You will also need a [Project](/resources/fundamentals/developer-apps) created. * You must have [signed up](https://developer.x.com/en/portal/petition/essential/basic-info) for a developer account, and have activated the [new Developer Console experience](https://developer.x.com/en/portal/opt-in.html).  * Access is available with active keys and tokens for a developer App that is attached to a [Project](/resources/fundamentals/developer-apps) created in the [Developer Console](/resources/fundamentals/developer-portal). * A Bearer Token from your App in the [X Developer Console](/resources/fundamentals/developer-portal). * If you do not have an approved developer account, you can [apply for one](https://developer.x.com/en/apply-for-access). ## Approved developer account If you do not have one yet, you can [apply for one](https://developer.x.com/en/apply-for-access). ## Create a Project and connect an App In the [Developer Console](https://developer.x.com/en/portal/dashboard), click create a new App. Give it a name, select the appropriate use-case, and provide a Project description. Next, you can either create a new App, or connect an existing [App](/resources/fundamentals/developer-apps) (an App is a container for your API keys that you need in order to make an HTTP request to the X API). Click ‘create a new App instead’ and give your App a name in order to create a new App. Once you click complete, you will get your API keys and the bearer token that you can then use to connect to the new endpoints in the X API v2. Click the (+) next to API key, API secret key and Bearer token and copy these values to a safe place on your local machine. You will need these to make the API calls in the next step. Note: The keys in the screenshot above are hidden, but in your own Developer Console, you will be able to see the actual values for the API key, API secret key and Bearer token.   ## How to get the user ID for a user to use in the user Tweet timeline and user mention timeline endpoints The user Tweet timeline and user mention timeline endpoints allow you to get Posts using the user ID. In order to get the user ID from a username, you can use the new [user lookup endpoint v2](/x-api/users/lookup/quickstart/user-lookup). Replace the USER\_NAME with the username of your choice and XXXX with your own bearer token that you obtained above ```bash theme={null} curl --request GET 'https://api.x.com/2/users/by/username/USER_NAME --header 'Authorization: Bearer XXXXXX' ``` You will see the user ID in response as shown below: ```json theme={null} { "data": { "id": "2244994945", "name": "Developers", "username": "XDevelopers" } } ``` ## Connecting to the user Tweet timeline and user mention timeline endpoints In order to get the user Tweet timeline for a user ,run the following curl command in your terminal (make sure to replace the USER\_ID with the user ID of your choice and XXXX with your own bearer token that you obtained above) ```bash theme={null} curl --request GET 'https://api.x.com/2/users/USER_ID/tweets' --header 'Authorization: Bearer XXXXXX' ``` You will see that the JSON response for these requests contains the ID and text for the Posts by default (example below). ```yaml theme={null} { "id": "1334200897081987072", "text": "👀 If you are new to the X API v2, check out this step-by-step guide to making your first request https://t.co/4rZqThpSbp" } ``` If you want additional fields returned as part of the response (such as user information, additional Tweet fields such as context annotations etc.) then you will need to specify those fields explicitly in your response. Learn how to do this from the [guide on using fields and expansions](/x-api/fundamentals/data-dictionary#how-to-use-fields-and-expansions). You can also get these Posts using programming languages of your choice. Check out our sample code in Python, Node (JavaScript), Java and Ruby for the user Tweet timeline and user mention timeline endpoints on our [Github repository](https://github.com/xdevplatform/Twitter-API-v2-sample-code).   ## Exploring the user’s Posts Once you know how to get Posts using the user Tweet timeline and user mention timeline endpoints, you can start to explore their Posts. For example, if you wanted to identify common named entities present in a user’s mentions, you can do the following: In the API request, specify that you want the context\_annotations object returned in the Tweet responses: ```bash theme={null} curl --request GET 'https://api.x.com/2/users/USER_ID/mentions?tweet.fields=context_annotations' --header 'Authorization: Bearer XXXXXX' ``` In the response, you will see if any named entities are present in the mentions. Here is an example: ```json theme={null} { "domain": { "id": "47", "name": "Brand", "description": "Brands and Companies" }, "entity": { "id": "783214", "name": "X" } } ``` If you wanted to see which popular entities appear in your mentions, you could keep a count of popular entities by parsing each Tweet in the mentions. If you wanted to explore the preview image URL for all Posts in your timeline that contain media, you can do the following: In the API request, specify that you want the preview\_image\_url in the tweet.media fields, and the attachments.media\_keys expansions ```bash theme={null} curl --request GET 'https://api.x.com/2/users/2244994945/mentions?max_results=100&media.fields=preview_image_url&expansions=attachments.media_keys' --header 'Authorization: Bearer XXXXXX' ``` In the response, you will see the preview\_image\_url in the includes object as shown below: ```json theme={null} { "includes": { "media": [ { "media_key": "16_1334657439640121344", "preview_image_url": "https://pbs.twimg.com/tweet_video_thumb/EoWn3rqU8AAtFWL.jpg", "type": "animated_gif" } ] } } ``` Once you have an understanding of how to navigate a user’s Posts, you can also use other APIs and services to do more with the Posts. Below are some resources to keep handy when using the user Tweet timeline and user mention timeline endpoints. ## Resources * Learn more about the [user Tweet timeline and the user mention timeline endpoints](/x-api/posts/timelines/introduction). * Check out the [API reference for the user Tweet timeline endpoint](/x-api/posts/user-posts-timeline-by-user-id) to learn more about what’s available. * Check out the [API reference for the user mention timeline endpoint](/x-api/posts/user-mention-timeline-by-user-id) to learn more about what’s available. * Get inspired by reading our other [tutorials](/tutorials). # Getting historical Posts using the v2 full-archive search endpoint Source: https://docs.x.com/tutorials/getting-historical-posts-using-the-full-archive-search-endpoint ## Introduction The [Search Posts endpoints](/x-api/posts/search/introduction) in the v2 world enable you to receive Posts related to topics of interest, based on a search query that you produce. We have two different endpoints available with v2 Search Posts: recent search, which is available to all developers with an approved account and can search for Posts up to seven days old, and full-archive search, which is only available to researchers approved for the [Academic Research product track](https://developer.x.com/en/products/x-api/early-access/guide#na_2), and can search through the entire archive of Posts dating back to March 2006. You can see our full search offering on our [search overview page](/x-api/posts/search/introduction). These Search Posts endpoints address one of the most common use cases for academic researchers, who might use this for longitudinal studies, or analyzing a past topic or event. This tutorial provides a step-by-step guide for researchers who wish to use the full-archive search endpoint to search the complete history of public X data. It will also demonstrate the different ways to build a dataset, such as by retrieving geo-tagged Posts, and how to page through the available Posts for a query. ### Prerequisites Currently, this endpoint is only available as part of the [Academic Research product track](https://developer.x.com/en/solutions/academic-research/products-for-researchers). In order to use this endpoint, you must [apply for access](https://developer.x.com/en/portal/petition/academic/is-it-right-for-you). Learn more about the [application and requirements for this track](https://developer.x.com/en/solutions/academic-research/application-info). ### Connect an app to the academic project Once you are approved to use the Academic Research product track, you will see your Academic [Project](/resources/fundamentals/developer-apps) in the [Developer Console](https://developer.x.com/en/portal/dashboard). From the "Apps" section, click on "Add App" to connect your [X App](/resources/fundamentals/developer-apps) to the Project. [](https://res.cloudinary.com/practicaldev/image/fetch/s--gHFOyuDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gb7aevhqyfvfjznd0pnd.png) ![This image displays an Academic Project in the Developer Console that does not have an App added to it yet](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/docs/tutorials/getting-historical-tweets-using-the-full-archive-search-endpoint/dev-portal-1.png.twimg.1920.png) Then, you can either choose an existing App and connect it to your project (as shown below). ![This image shows you the page that pops up when you try to add an App to your Academic Project](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/docs/tutorials/getting-historical-tweets-using-the-full-archive-search-endpoint/dev-portal-2.png.twimg.1920.png) Or you can create a new App, give it a name and click complete, to connect a new App to your Academic Project. ![This image shows you the page where you will enter a name for your new App, or enables you to select an existing App](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/docs/tutorials/getting-historical-tweets-using-the-full-archive-search-endpoint/dev-portal-3.png.twimg.1920.png) This will give you your API keys and [Bearer Token](/resources/fundamentals/authentication#using-and-generating-an-app-only-bearer-token) that you can then use to connect to the full-archive search endpoint. ![This image shows you the page that you are shown after you create a new App that displays your keys and tokens](https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/docs/tutorials/getting-historical-tweets-using-the-full-archive-search-endpoint/dev-portal-4.png.twimg.1920.png) **Please note** The keys in the screenshot above are hidden, but in your own Developer Console, you will be able to see the actual values for the API Key, API Secret Key, and Bearer Token. Save these keys and the Bearer Token because you will need those for calling the full-archive search endpoint. ### Connecting to the full-archive search endpoint The cURL command below shows how you can get historical Posts from @XDevelopers handle. Replace the \$BEARER\_TOKEN with your own Bearer Token, paste the full request in your terminal, and press "return". ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets/search/all?query=from:xdevelopers' --header 'Authorization: Bearer $BEARER_TOKEN' ``` You will see the response JSON. By default, only the 10 most recent Posts will be returned. If you want more than 10 Posts per request, you can use the max\_results parameter and set it to a maximum of 500 Posts per request, as shown below: ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets/search/all?query=from:xdevelopers&max_results=500' --header 'Authorization: Bearer $BEARER_TOKEN' ``` ### Building queries As you can see in the example calls above, using the query parameter, you can specify the data that you want to search for. As an example, if you wanted to get all Posts that contain the word *covid* or the word *coronavirus*, you can use the OR operator within brackets, and your query can be `(covid OR coronavirus)` and thus your API call will look like the following: ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets/search/all?query=(covid%20OR%20coronavirus)&max_results=500' --header 'Authorization: Bearer $BEARER_TOKEN' ``` Similarly, if you want all Posts that contain the words *covid19* that are not reposts, you can use the is:retweet operator with the logical NOT (represented by -), so your query can be covid19 -is:retweet and your API call will be: ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets/search/all?query=covid19%20-is:retweet&max_results=500' --header 'Authorization: Bearer $BEARER_TOKEN' ``` Check out [this guide for a complete list of operators](/x-api/posts/search/integrate/build-a-query) that are supported in the full-archive search endpoint. ### Using the start\_time and end\_time parameters to get historical Posts When using the full-archive search endpoint, by default Posts from the last 30 days will be returned. If you want to get Posts that are older than 30 days, you can use the start\_time and end\_time parameters in your API call. These parameters must be in a valid RFC3339 date-time format, for example 2020-12-21T13:00:00.00Z. Thus, if you want to get all Posts from the XDevelopers account for the month of December 2020, your API call will be: ```bash theme={null} curl --request GET 'https://api.x.com/2/tweets/search/all?query=from:XDevelopers&start_time=2020-12-01T00:00:00.00Z&end_time=2021-01-01T00:00:00.00Z' --header 'Authorization: Bearer $BEARER_TOKEN' ``` ### Getting geo-tagged historical Posts Geo-tagged Posts are Posts that have geographic information associated with them such as city, state, country etc. #### Using has:geo operator If you want to get Posts that have geo data, you can use the has:geo operator. For example, the following cURL request will get only those Posts from the @XDevelopers handle that have geo data: ``` curl --request GET 'https://api.x.com/2/tweets/search/all?query=from:xdevelopers%20has:geo' --header 'Aubashthorization: Bearer $BEARER_TOKEN' ``` #### Using place\_country operator Similarly, you can limit Posts that have geo data, to a specific country, using the place\_country operator. The cURL command below will get all Posts from the @XDevelopers handle from the United States: ``` curl --request GET 'https://api.x.com/2/tweets/search/all?query=from:xdevelopers%20place_country:US' --hbasheader 'Authorization: Bearer XXXXX' ``` The country is specified above using the ISO alpha-2 character code. Valid ISO codes can be found [here](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). ### Getting more than 500 historical Posts using the next\_token As mentioned above, by default you can only get up to 500 Posts per request for a query to the full-archive search endpoint. If there are more than 500 Posts available for your query, your json response will include a next\_token which you can append to your API call in order to get the next available Posts for this query. This next\_token is available in the meta object of your JSON response, which looks something: ``` { "newest_id": "12345678...", "oldest_id": "12345678...", "result_count": 500, "nebashxt_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } ``` Hence, to get the next available Posts, use the next\_token value from this meta object and use the value as the value for the next\_token in your API call to the full-archive search endpoint as shown below (You will use your own Bearer Token and the value that you get for the Next Token for your previous API call). ``` curl --request GET 'https://api.x.com/2/tweets/search/all?max_results=500&query=covid&next_token=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' --header 'Authorization: Bearer $BEARER_TOKEN' ``` This way, you can keep checking if a next\_token is available and if you have not reached your desired number of Posts to be collected, you can keep calling the full-archive endpoint with the new next\_token for each request. Below are some resources that can help you when using the full-archive search endpoint. We would love to hear your feedback. Reach out to us on [@XDevelopers](https://x.com/XDevelopers) or on our [community forums](https://devcommunity.x.com/) with questions about this endpoint. ### Additional resources * [Full-archive search endpoint API reference](/x-api/posts/full-archive-search) * [Learn the basics of building a search query](/x-api/posts/search/integrate/build-a-query) # Getting started with Postman Source: https://docs.x.com/tutorials/postman-getting-started In this tutorial, we will discuss what Postman is and how to quickly get set up with it. ## Introduction Postman is a desktop and web application that allows you to make requests to an API from a graphical user interface. We recommend using Postman with the X API, X Ads API, and Labs endpoints when exploring the APIs functionality, as well as when you are troubleshooting issues with your application.  We currently have two Postman collections available: ### Prerequisites Before you get started with using Xs Postman collections, you will need to make sure you have the proper access and credentials for the X developer platform tool of your choosing. You can learn more about getting access via our [“Getting started” page](/resources/platform-overview).  Once you have a [developer account,](https://developer.x.com/en/portal/petition/essential/basic-info) have set up a [developer App](/resources/fundamentals/developer-apps) and have a set of [authentication](/resources/fundamentals/authentication) keys and tokens, and have properly set up your environment to make requests to the specific API that you plan to use, you can follow the below steps to get started.   ## Getting started with X's Postman collections ### Step one: Add one of the X Postman collections to your account While you could build out the specific endpoints that you’d like to use within Postman, we did all of the heavy lifting for you and built out a ready to use collection of relevant APIs. Just click one of the links in the earlier “Postman collections” section and a collection with all of the endpoints associated with the selected API will be added to your Postman app. These collections are also available in the [Postman API network](https://explore.postman.com/). Each endpoint will automatically include available parameters, example responses, and authentication type plugged in, so you just need to add your credentials and parameter values to start exploring the functionality. In this example, we are going to work with the X [API v2 collection](https://www.postman.com/xapidevelopers/x-api-public-workspace/collection/34902927-2efc5689-99c6-4ab6-8091-996f35c2fd80).    ### Step two: Add your keys and tokens as environmental variables When you add the collection to your Postman instance, it will automatically add an environment called ‘X API v2’, which you will need to add your keys and tokens to. In this step, we will walk you through the process of adding your keys and tokens from your developer App into the ‘X API v2’ environment.  To add your keys and tokens to the “X API v2” environment, click on the “manage environments” button in the top right corner of Postman., you will need to click on the settings button in the top right corner.    This image shows the "manage environments" button highlighted in the Postman console. From the list of environments, click on “X API v2”.  Next, add a variable for all of the different keys and tokens that you’ve generated via the Apps dashboard into the table. Here is an example of what your table should look like: | VARIABLE | INITIAL VALUE | CURRENT VALUE | | :--------------- | :---------------------------------------------------------------- | :---------------------------------------------------------------- | | consumer\_key | `QAktM6W6DF6F7XXXXXX` | `QAktM6W6DF6F7XXXXXX` | | consumer\_secret | `AJX560A2Omgwyjr6Mml2esedujnZLHXXXXXX` | `AJX560A2Omgwyjr6Mml2esedujnZLHXXXXXX` | | access\_token | `1995XXXXX-0NGqVhk3s96IX6SgT3H2bbjOPjcyQXXXXXXX` | `1995XXXXX-0NGqVhk3s96IX6SgT3H2bbjOPjcyQXXXXXXX` | | token\_secret | `rHVuh7dgDuJCOGeoe4tndtjKwWiDjBZHLaZXXXXXX` | `rHVuh7dgDuJCOGeoe4tndtjKwWiDjBZHLaZXXXXXX` | | bearer\_token | `AAAAAAAAAAAAAAAAAAAAAL9v6AAAAAAA99t03huuqRYg0mpYAAFRbPR3XXXXXXX` | `AAAAAAAAAAAAAAAAAAAAAL9v6AAAAAAA99t03huuqRYg0mpYAAFRbPR3XXXXXXX` | Note that the keys and tokens used in the above table are made up and will not work if used in a request.  Once you’ve added your credentials as variables and have made sure that the X API v2 environment is selected, you will be ready to make requests to the X API v2 collection. This is because each of the endpoint’s authorization tabs are set up to automatically inherit the variables from this environment.  For using Postman with User Access Tokens skip XXXX to the additional details on how to do so. ### Step three: Select an endpoint  Next step is to choose an endpoint from the collection and start to build your request. You can select an endpoint from the right-hand side navigation. Here is what this looks like: This image shows the "Single Posts" request selected under the "Post Lookup" dropdown in the "X API v2" section. For this example, we are going to use the X API v2 > Post Lookup > Single Post endpoint.  #### Step four: Add values to the Params tab Next step will have you navigate to the Params tab. You should see a set of inactive params with descriptions that explain what the parameter does, and a list of all of the potential values that you can pass with your request.  In this example, we are going to activate the expansions and tweet.fields query parameters and add the following values: | | | | :------------- | :----------------------- | | **Key** | **Value** | | `tweet.fields` | `created_at,attachments` | | expansions | author\_id | In addition to adding the query parameters, we need to add the required Path Variable, id. Since this endpoint returns Posts, we need to add a valid Post ID as the value. You can find the Post ID by navigating to x.com and clicking on a Post, and then looking in the URL. For example, the following URL's Post ID is `1228393702244134912`: `https://x.com/XDevelopers/status/1228393702244134912` On the Params tab, scroll down past all of the query parameters to display the “Path Variables” section. We will be adding the Post ID that you would like to use as a value to the id key. If you’ve entered everything from this step correctly, the Params tab should look like the following: This image shows the "Params" table filled out based on the instructions included earlier in the page. #### Step five: Send your request and review your response Now that everything is set up in your request, you can click the “Send” button.  If everything was set up properly, you should receive the following payload: ```json theme={null} { "data": { "author_id": "2244994945", "text": "What did the developer write in their Valentine’s card?\n \nwhile(true) {\n I = Love(You); \n}", "id": "1228393702244134912", "created_at": "2020-02-14T19:00:55.000Z" }, "includes": { "users": [ { "username": "XDevelopers", "name": "Developers", "id": "2244994945" } ] } } ``` ### Generating a User Access Token with Postman: #### Using OAuth 1.0a to generate a user access token Review the three step process used in the [OAuth1.0a flow test collection](https://www.postman.com/xapidevelopers/x-api-public-workspace/collection/34902927-2efc5689-99c6-4ab6-8091-996f35c2fd80). #### Using OAuth 2.0 to generate a user access token If you want to generate an OAuth 2.0 user access token in Postman you can generate OAuth 2.0 access tokens to use in conjunction with the X [API v2 Postman collection](https://www.postman.com/xapidevelopers/x-api-public-workspace/collection/34902927-2efc5689-99c6-4ab6-8091-996f35c2fd80).  If you click on the collection in your workspace and go to the tab entitled “Auth” and select the type to be “OAuth 2.0”. From there under the heading “Configure New Token” go to where it says “Configuration Options”. You can update the “Grant Type” to “Authorization Code (With PKCE)”. You will want to update your Callback URL to match the callback url associated with your application. Additionally, you will want to update the following parameters: * Auth URL -  [https://x.com/i/oauth2/authorize](https://x.com/i/oauth2/authorize) * Access Token URL -  [https://api.x.com/2/oauth2/token](https://api.x.com/2/oauth2/token) * Client ID - Your OAuth2.0 client ID from Dev Portal * Client Secret - If you are using a confidential client * Update Scope - Scopes to match the endpoints you want to connect to. Example “tweet.read users.read”  * Your callback URL (also known as the redirect URL). This must match what you have in your App's authentication settings. * State - state When you are ready can click “Get New Access Token” to generate an Access Token. If you see a dialog box that says something went wrong you may need to press the back button to login. You will need to authorize your app to have access to your account by clicking where it says “Authorize app” in the dialog box. After you authorize your app you will be directed back to Postman where you can see your token and select the "Use Token" button to start making requests on behalf of an authorized user. You are now ready to use the Postman collection. ## What's next If you click on the button in Postman that says "Code", you can turn the request we just created into the language of your choosing such as Python, Node or Ruby to help you get started. Postman has [great documentation](https://learning.getpostman.com/) that might be helpful. We also have some [sample code on GitHub](https://github.com/xdevplatform) that can help you get integrated with the endpoints more quickly. # Analytics Source: https://docs.x.com/x-ads-api/analytics ## Introduction Analytics metrics help partners and advertisers understand the performance of the content they promote on X. This includes information such as impressions, clicks, video views, and spend. In addition, partners and advertisers are able to get detailed metrics for various segments of the audiences they reach. The Ads API supports two ways of retrieving detailed campaign performance metrics: synchronously and asynchronously. With synchronous analytics calls, the requested metrics are returned in the response. With the asynchronous analytics endpoints, the requested metrics are available in a downloadable results file after the associated "job" has finished processing. The synchronous endpoint supports short time ranges and is ideal for real-time campaign optimizations. The asynchronous endpoints support much longer time ranges and are, thus, intended for fetching much more data, ideal for generating reporting or historical backfills. ## Details ### Synchronous vs. Asynchronous The differences between the synchronous and asynchronous analytics endpoints are summarized in the following table. This information is intended to help developers choose which set of endpoints to use. | Feature | Synchronous | Asynchronous | | :------------------- | :-------------------------------------------------- | :----------------------------------------------------------- | | Rate limiting | User-level: 250 requests / 15 minutes | Account-level: 100 concurrent\* jobs | | Time range | 7 days | 90 days (non-segmented)
45 days (segmented) | | Segmentation | No | Yes | | Response returns | Metrics data | Processing state of the job\*\* | | Recommended use case | Real-time optimization
User interface requests | Regularly-scheduled syncing
Backfilling historical data | \* This refers to the maximum number of jobs that may be in a processing state at any given time. \*\* Once the job has successfully finished processing, a URL is returned. This is where the compressed (gzip) results file can be downloaded from. Outside of this, the endpoints offer the same functionality. ### Use cases There are three major analytics use cases. 1. Real-time optimization: using performance metrics to update active campaigns 2. Synchronization: regularly-scheduled background syncs 3. New account on-boarding: backfilling historical data The synchronous analytics endpoint may be used for real-time optimization to update campaigns based on changes to metrics within the last 5 to 15 minutes. Either endpoint can be used for analytics synchronization. Keep in mind that the desired time range and whether segmentation is required will determine which endpoint to use. New account on-boarding should only be done using the asynchronous analytics endpoints. (The synchronous analytics endpoint should never be used for retrieving large amounts of data.) The asynchronous analytics endpoints can power dashboards and other UI elements if metrics are synced with a backend process. Your implementation should avoid calling the asynchronous analytics endpoints to fulfill user interface requests. ### Request Options Analytics requests are scoped to ads accounts and, thus, require the account ID in the resource path. Request options, listed below, are specified as query parameters. The following types of values are required. * Entities: the entity type as well as up to 20 entity IDs you'd like to request analytics for * Time range: the start and end times, expressed in ISO 8601 * **Note:** must be expressed in whole hours * Metric groups: one or more sets of related metrics (see Metrics and Segmentation for a list of metrics within each metric group) * Granularity: specifies the level of aggregation in which the metrics should be returned * Placement: determines whether metrics are pulled for ads that served on or off of X * **Note:** only a single placement value can be specified per request Use the `start_time` and `end_time` request parameters to specify a time range. These values must be aligned with the specified granularity in the following way. 1. `TOTAL`: specify any time range (within the endpoint's limits) 2. `DAY`: both the start time and end time values must be aligned with midnight in the account's time zone 3. `HOUR`: specify any time range (within the endpoint's limits) End time is exclusive. For example, a request with `start_time=2019-01-01T00:00:00Z` and `end_time=2019-01-02T00:00:00Z` will return a single day's worth of analytics metrics (not two) as this time range covers only a 24 hour period. **Segmentation** Available only through our asynchronous analytics endpoints, segmentation allows partners and advertisers to retrieve metrics broken out particular targeting values. To request segmented metrics, use the `segmentation_type` request parameter. For more details on segmentation options, see [Metrics and Segmentation](/x-ads-api/analytics#metrics-and-segmentation). ## FAQs Why don't the Ads API numbers match what's shown in the X Ads UI? * Make sure you've requested data for both placements: `ALL_ON_TWITTER` and `PUBLISHER_NETWORK`, `SPOTLIGHT`, and `TREND`. * Remember that end times in the Ads API are exclusive; they are inclusive in the Ads UI Why do the numbers change depending on when I request data? * As soon as reporting metrics are available, you are able to retrieve them. They are available in near real-time. These early results are estimates, though, and, as a result, are expected to change. Metrics are finalized after 24 hours, with the exception of spend data. * Spend metrics are generally final within 3 days of the event. However, we process billing data for up to 14 days from the date of the event (for spam filtering, for example). How can I determine which entity IDs to request for a specific time period? * Use the [Active Entities endpoint](/x-ads-api/analytics#active-entities-2) Why are all of the values in the analytics response `null`? * It's likely that the campaign did not serve during during the requested time period * Use the [Active Entities endpoint](/x-ads-api/analytics#active-entities-2) to determine which entities to fetch analytics for and for what time period Why does the API show `null` values while the UI shows 0s? * The UI chooses to display these values as 0s, but the values are equivalent How can I request metrics associated with a granular placement, such as the X timeline? * We support the following placement values in analytics: `ALL_ON_TWITTER` and `PUBLISHER_NETWORK`, `SPOTLIGHT`, and `TREND` (i.e., the [X Audience Platform](/x-ads-api/audiences)) Is it possible to retrieve metrics for deleted or paused entities? * Yes. The entity's status does not impact the availability of analytics metrics. Why don't the segmented values match the non-segmented ones? * Segmented data is *not* expected to roll-up 100% to the non-segmented data, due to how this information is derived. Is it possible to request data segmented by multiple dimensions? * We do not support multi-segmentation. ## Best Practices Some best practices when collecting [analytics](/x-ads-api/analytics) data from the Ads API. ### Rate Limiting and Retries * On queries that are rate limited (those that return an `HTTP 429` status code), you must inspect the `x-rate-limit-reset` header and retry only at or after the time indicated. * On queries that result in an HTTP 503 Service Unavailable status code, you must inspect the `retry-after` header and retry only after the time indicated. * Applications that do not respect the times indicated for retries could have their access to the Ads API revoked or throttled without notice. ### Analytics Metrics In a Nutshell * All analytics metrics are locked and will not change after 24 hours, with the exception of `billed_charge_local_micro`. * The `billed_charge_local_micro` metric is an estimate for up to 3 days after the data is returned. * After 24 hours, this metric can decrease due to credits for overspend (ads served after the given `end_time`) and for billable events that are determined to be junk. This metric changes minimally after 24 hours. * Please see [Analytics](/x-ads-api/analytics) for more information. ### Fetching Real-time, Non-segmented Data * Always provide both a `start_time` and an `end_time`. * Do not pull data for any entities older than 7 days. * Do request data (ideally) with `HOUR` granularity, as you can always aggregate and roll metrics up to get `DAY` and `TOTAL` granularity. * Do request data (ideally) at the `line_items` and `promoted_tweets` level, as you can always aggregate and roll these metrics up to get totals across the entire ads entity hierarchy (i.e. for the campaign, funding instrument or account levels). * Save and store the values of analytics metrics on your side (locally). * Do not repeatedly query for data that is older than 30 days. This data will not change and should be stored locally. * All non-segmented data is real-time and data should be available within seconds of an event occurring. * Group conversion metrics and non-conversion metrics into separate requests. ### Fetching Segmented Data * Refer to guidelines provided for “Fetching Real-time, Non-segmented Data” above. Additional advice provided below. * For most segmented data types, it is possible for data to not be complete for up to 1 hour at times. Data segmented by `INTERESTS` can be delayed for up to 12 hours. * Segmented data is not expected to roll-up 100% to the non-segmented data, due to how this information is derived. ### Fetching Historical Data * When backfilling data (i.e. adding a new advertiser account), you may need make several requests in smaller `start_time` and `end_time` chunks. * Limit your fetches to 30-day date windows. * Throttle these requests and distribute over time so as not to exhaust your rate limits for these fetches. ### Sample You can find a sample script demonstrating some of these best practices (`fetch_stats`) on our [ads-platform-tools github](https://github.com/xdevplatform/ads-platform-tools) repository. ## Metrics by Objective Which metrics are applicable for an entity depends on the [campaign objective](/x-ads-api/campaign-management). Use this guide to determine the relevant metric groups to fetch for each objective type, as well as how additional derived metrics can be calculated. ### `ENGAGEMENTS` **Relevant metric groups:**`ENGAGEMENT` and `BILLING`. `MEDIA` is also applicable if media is used in creatives. | | | | :-------------- | :-------------------------------------- | | Derived Metric | Exposed Metric Calculation | | Engagement Rate | `engagements/impressions` | | CPE | `billed_charge_local_micro/engagements` | | Media View Rate | `media_views/impressions` | ### `WEBSITE_CLICKS` and `WEBSITE_CONVERSIONS` **Relevant metric groups:**`ENGAGEMENT`, `BILLING`, and `WEB_CONVERSION`. `MEDIA` is also applicable if media is used in creatives. | | | | :---------------- | :----------------------------------------------------------------------------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | CPM | `billed_charge_local_micro/impressions/1000` | | Click Rate | `clicks/impressions` | | CPLC | `billed_charge_local_micro/clicks` | | Total Conversions | `conversion_custom` + `conversion_site_visits` + `conversion_sign_ups` + `conversion_downloads` + `conversion_purchases` | | Conversion Rate | Total Conversions / `impressions` | | CPA | `billed_charge_local_micro` / Total Conversions | ### `APP_INSTALLS` and `APP_ENGAGEMENTS` **Relevant metric groups:**`ENGAGEMENT`, `BILLING`, `MOBILE_CONVERSION`, and `LIFE_TIME_VALUE_MOBILE_CONVERSION`. `MEDIA` and `VIDEO` are also applicable if media or video app card is used in creatives. | | | | :------------- | :----------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | CPM | `billed_charge_local_micro/impressions/1000` | | App Click Rate | `app_clicks/impressions` | | CPAC | `billed_charge_local_micro/app_clicks` | | CPI | `billed_charge_local_micro/mobile_conversion_installs` | ### `FOLLOWERS` **Relevant metric groups:**`ENGAGEMENT` and `BILLING`. `MEDIA` is also applicable if media is used in creatives. | | | | :-------------- | :------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | CPM | `billed_charge_local_micro/impressions/1000` | | Follow Rate | `follows/impressions` | | CPF | `billed_charge_local_micro/follows` | | Media View Rate | `media_views/impressions` | ### `LEAD_GENERATION` **Relevant metric groups:**`ENGAGEMENT` and `BILLING`. `MEDIA` is also applicable if media is used in creatives. | | | | :------------- | :------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | CPM | `billed_charge_local_micro/impressions/1000` | | Leads | `card_engagements` | | Lead Rate | `card_engagements/impressions` | | Cost Per Lead | `billed_charge_local_micro/card_engagements` | ### `VIDEO_VIEWS` **Relevant metric groups:**`ENGAGEMENT`, `BILLING`, and `VIDEO`. | | | | :------------- | :-------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | CPM | `billed_charge_local_micro/impressions/1000` | | Video Rate | `video_total_views/impressions` | | Cost Per View | `billed_charge_local_micro/video_total_views` | ### `VIDEO_VIEWS_PREROLL` **Relevant metric groups:**`ENGAGEMENT`, `BILLING`, and `VIDEO`. | | | | :------------- | :-------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | CPM | `billed_charge_local_micro/impressions/1000` | | Video Rate | `video_total_views/impressions` | | Cost Per View | `billed_charge_local_micro/video_total_views` | ## Metrics and Segmentation This document is an overview of the metrics available from our [Analytics](/x-ads-api/analytics) for each entity type, as well as the available segmentation for each metrics. | | | | | | | | | | :------------------- | :-------------------------- | :-------------------- | :---------------- | :---------------- | :---------------------------------- | :---------------------------------------- | :------------------------------------------------------------------------ | | | Metric Groups | | | | | | | | Entity | [`ENGAGEMENT`](#engagement) | [`BILLING`](#BILLING) | [`VIDEO`](#VIDEO) | [`MEDIA`](#MEDIA) | [`WEB_CONVERSION`](#WEB_CONVERSION) | [`MOBILE_CONVERSION`](#MOBILE_CONVERSION) | [`LIFE_TIME_VALUE_MOBILE_CONVERSION`](#LIFE_TIME_VALUE_MOBILE_CONVERSION) | | `ACCOUNT` | ✔\* | | | | | | | | `FUNDING_INSTRUMENT` | ✔\* | ✔ | | | | | | | `CAMPAIGN` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | `LINE_ITEM` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | `PROMOTED_TWEET` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | `MEDIA_CREATIVE` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | `ORGANIC_TWEET` | ✔ | | ✔ | | | | | \*Some metrics in the `ENGAGEMENT` metrics family are not available at the account and funding instrument level. See the `ENGAGEMENT` section for details. ### Available Metrics by Metrics Group #### `ENGAGEMENT` | | | | | | | :---------------------- | :------------------------------------------------------------------- | :--------------------- | :------------ | :----------------------------------------- | | Metric | Description | Segmentation Available | Data Type | Available for Account / Funding Instrument | | `engagements` | Total number of engagements | ✔ | Array of ints | ✔ | | `impressions` | Total number of impressions | ✔ | Array of ints | ✔ | | `retweets` | Total number of retweets | ✔ | Array of ints | ✔ | | `replies` | Total number of replies | ✔ | Array of ints | ✔ | | `likes` | Total number of likes | ✔ | Array of ints | ✔ | | `follows` | Total number of follows | ✔ | Array of ints | ✔ | | `card_engagements` | Total number of card engagements | ✔ | Array of ints | | | `clicks` | Total number of clicks, including favorites and other engagements | ✔ | Array of ints | | | `app_clicks` | Number of app install or app open attempts | ✔ | Array of ints | | | url\_clicks | Total clicks on the link or Website Card in an ad, including earned. | ✔ | Array of ints | | | `qualified_impressions` | Total number of qualified impressions | ✔ | Array of ints | | | `carousel_swipes` | Total swipes on Carousel images or videos | ✔ | Array of ints | | #### `BILLING` | | | | | | :-------------------------- | :--------------------------------- | :--------------------- | :------------ | | Metric | Description | Segmentation Available | Data Type | | `billed_engagements` | Total number of billed engagements | ✔ | Array of ints | | `billed_charge_local_micro` | Total spend in micros | ✔ | Array of ints | #### `VIDEO` Notice about video metrics definition changes: The `video_total_views` metric within the `VIDEO` metrics group will report on any views which are at least 50% in-view for 2 seconds, as per the MRC standard. Our original video view definition of 100% in view for at least 3 seconds will continue to be available as a new `video_3s100pct_views` metric in the `VIDEO` metrics group. To continue to bid and be charged based on the original view definition, use the newly available `VIEW_3S_100PCT` bid\_unit. | | | | | | :--------------------- | :--------------------------------------------------------------------------------------------------------- | :--------------------- | :------------ | | Metric | Description | Segmentation Available | Data Type | | `video_total_views` | Total number of video views | ✔ | Array of ints | | `video_views_25` | Total number of views where at least 25% of the video was viewed. | ✔ | Array of ints | | `video_views_50` | Total number of views where at least 50% of the video was viewed. | ✔ | Array of ints | | `video_views_75` | Total number of views where at least 75% of the video was viewed. | ✔ | Array of ints | | `video_views_100` | Total number of views where at least 100% of the video was viewed. | ✔ | Array of ints | | `video_cta_clicks` | Total clicks on the call to action | ✔ | Array of ints | | `video_content_starts` | Total number of video playback starts | ✔ | Array of ints | | `video_3s100pct_views` | Total number of views where at least 3 seconds were played while 100% in view (legacy `video_total_views`) | ✔ | Array of ints | | `video_6s_views` | Total number of views where at least 6 seconds of the video was viewed | ✔ | Array of ints | | `video_15s_views` | Total number of views where at least 15 seconds of the video or for 95% of the total duration was viewed | ✔ | Array of ints | #### `MEDIA` | | | | | | :------------------ | :------------------------------------------------------------------------------------------ | :--------------------- | :------------ | | Metric | Description | Segmentation Available | Data Type | | `media_views` | Total number of views (autoplay and click) of media across Videos, Vines, GIFs, and Images. | ✔ | Array of ints | | `media_engagements` | Total number of clicks of media across Videos, Vines, GIFs, and Images. | ✔ | Array of ints | #### `WEB_CONVERSION` | | | | | | :----------------------- | :--------------------------------------------------------------------------------------------- | :--------------------- | :---------- | | Metric | Description | Segmentation Available | Data Type | | `conversion_purchases` | Number of conversions of type PURCHASE and the corresponding sale amount and order quantity | `PLATFORMS` only | JSON object | | `conversion_sign_ups` | Number of conversions of type SIGN\_UP and the corresponding sale amount and order quantity | `PLATFORMS` only | JSON object | | `conversion_site_visits` | Number of conversions of type SITE\_VISIT and the corresponding sale amount and order quantity | `PLATFORMS` only | JSON object | | `conversion_downloads` | Number of conversions of type DOWNLOAD and the corresponding sale amount and order quantity | `PLATFORMS` only | JSON object | | `conversion_custom` | Number of conversions of type CUSTOM and the corresponding sale amount and order quantity | `PLATFORMS` only | JSON object | #### `MOBILE_CONVERSION` Mobile conversion stats are available only to advertiser accounts enabled for MACT. | | | | | | :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------- | :---------- | | Metric | Description | Segmentation Available | Data Type | | `mobile_conversion_spent_credits` | Breakdown of mobile conversions of type SPENT\_CREDIT by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_installs` | Breakdown of mobile conversions of type INSTALL by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_content_views` | Breakdown of mobile conversions of type CONTENT\_VIEW by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_add_to_wishlists` | Breakdown of mobile conversions of type ADD\_TO\_WISHLIST by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_checkouts_initiated` | Breakdown of mobile conversions of type CHECKOUT\_INITIATED by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_reservations` | Breakdown of mobile conversions of type RESERVATION by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_tutorials_completed` | Breakdown of mobile conversions of type TUTORIAL\_COMPLETED by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_achievements_unlocked` | Breakdown of mobile conversions of type ACHIEVEMENT\_UNLOCKED by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_searches` | Breakdown of mobile conversions of type SEARCH by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_add_to_carts` | Breakdown of mobile conversions of type ADD\_TO\_CART by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_payment_info_additions` | Breakdown of mobile conversions of type PAYMENT\_INFO\_ADDITION by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_re_engages` | Breakdown of mobile conversions of type RE\_ENGAGE by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_shares` | Breakdown of mobile conversions of type SHARE by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_rates` | Breakdown of mobile conversions of type RATE by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_logins` | Breakdown of mobile conversions of type LOGIN by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_updates` | Breakdown of mobile conversions of type UPDATE by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_levels_achieved` | Breakdown of mobile conversions of type LEVEL\_ACHIEVED by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_invites` | Breakdown of mobile conversions of type INVITE by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | `mobile_conversion_key_page_views` | Breakdown of mobile conversions of type KEY\_PAGE\_VIEW by post\_view and post\_engagement | ✔ | JSON object | | mobile\_conversion\_downloads | Breakdown of mobile conversions of type DOWNLOAD by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | mobile\_conversion\_purchases | Breakdown of mobile conversions of type PURCHASE by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | mobile\_conversion\_sign\_ups | Breakdown of mobile conversions of type SIGN\_UP by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | | mobile\_conversion\_site\_visits | Breakdown of mobile conversions of type SITE\_VISIT by post\_view, post\_engagement, assisted, order\_quantity, and sale\_amount | ✔ | JSON object | #### `LIFE_TIME_VALUE_MOBILE_CONVERSION` Lifetime mobile conversion stats are available only to advertiser accounts enabled for MACT. | | | | | | :-------------------------------------------------------- | :-------------------------------------------------------------- | :--------------------- | :---------- | | Metric | Description | Segmentation Available | Data Type | | `mobile_conversion_lifetime_value_purchases` | Breakdown of mobile conversions of type PURCHASE | | JSON object | | `mobile_conversion_lifetime_value_sign_ups` | Breakdown of mobile conversions of type SIGN\_UP | | JSON object | | `mobile_conversion_lifetime_value_updates` | Breakdown of mobile conversions of type UPDATE | | JSON object | | `mobile_conversion_lifetime_value_tutorials_completed` | Breakdown of mobile conversions of type TUTORIAL\_COMPLETED | | JSON object | | `mobile_conversion_lifetime_value_reservations` | Breakdown of mobile conversions of type RESERVATION | | JSON object | | `mobile_conversion_lifetime_value_add_to_carts` | Breakdown of mobile conversions of type ADD\_TO\_CART | | JSON object | | `mobile_conversion_lifetime_value_add_to_wishlists` | Breakdown of mobile conversions of type ADD\_TO\_WISHLIST | | JSON object | | `mobile_conversion_lifetime_value_checkouts_initiated` | Breakdown of mobile conversions of type CHECKOUT\_INITIATED | | JSON object | | `mobile_conversion_lifetime_value_levels_achieved` | Breakdown of mobile conversions of type LEVEL\_ACHIEVED | | JSON object | | `mobile_conversion_lifetime_value_achievements_unlocked` | Breakdown of mobile conversions of type ACHIEVEMENT\_UNLOCKED | | JSON object | | `mobile_conversion_lifetime_value_shares` | Breakdown of mobile conversions of type SHARE | | JSON object | | `mobile_conversion_lifetime_value_invites` | Breakdown of mobile conversions of type INVITE | | JSON object | | `mobile_conversion_lifetime_value_payment_info_additions` | Breakdown of mobile conversions of type PAYMENT\_INFO\_ADDITION | | JSON object | | `mobile_conversion_lifetime_value_spent_credits` | Breakdown of mobile conversions of type SPENT\_CREDIT | | JSON object | | `mobile_conversion_lifetime_value_rates` | Breakdown of mobile conversions of type RATE | | JSON object | ### Segmentation Segmentation reporting allows the retrieval of metrics broken out by the values of a given targeting type. Segmentation is only available through [asynchronous analytics queries](/x-ads-api/analytics#asynchronous-analytics) due to their significant added complexity. Segmentation **not** supported for `MEDIA_CREATIVE` or `ORGANIC_TWEET` entities. Some segmentation types require additional parameters to be passed in. These are documented below. When segmenting by `CITIES` or `POSTAL_CODES`, the API will **only** returned targeted locations. Region and metro segmentation will return both targeted and non-targeted locations. | | | | | :----------------------------- | :----------------------- | :------------------------ | | Segmentation Type | `country` param required | `platform` param required | | `AGE` | | | | `APP_STORE_CATEGORY` | | | | `AUDIENCES` | | | | `CITIES` | ✔ | | | `CONVERSATIONS` | | | | `CONVERSION_TAGS` | | | | `DEVICES` | | ✔ | | `EVENTS` | | | | `GENDER` | | | | `INTERESTS` | | | | `KEYWORDS` | | | | `LANGUAGES` | | | | `LOCATIONS` | | | | `METROS` | ✔ | | | `PLATFORMS` | | | | `PLATFORM_VERSIONS` | | ✔ | | `POSTAL_CODES` | ✔ | | | `REGIONS` | ✔ | | | `SLIDES` | | | | `SIMILAR_TO_FOLLOWERS_OF_USER` | | | | `TV_SHOWS` | | | ## Derived Metrics Campaign metrics depend on their [campaign objective](/x-ads-api/campaign-management). Use this guide to determine how to calculcate derived metrics for use based on the objectives in place. Any `metric` without curly brackets is one that is returned by the Ads API [analytics](/x-ads-api/analytics#synchronous-analytics) endpoints. Any name surrounded by \{curley brackets} indicates a derived metric for that category. ### ENGAGEMENTS | | | | :------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | `promoted_tweet_search_impressions + promoted_tweet_timeline_impressions + promoted_tweet_profile_impressions` | | | `billed_charge_local_micro / {Impressions} / 1000` | | | \{Total Engagements} | `promoted_account_follows + promoted_tweet_search_engagements + promoted_tweet_timeline_engagements + promoted_tweet_profile_engagements` or `promoted_account_follows + promoted_tweet_search_clicks + promoted_tweet_search_replies + promoted_tweet_search_retweets + promoted_tweet_search_follows + promoted_tweet_timeline_clicks + promoted_tweet_timeline_replies + promoted_tweet_timeline_retweets + promoted_tweet_timeline_follows + promoted_tweet_profile_clicks + promoted_tweet_profile_replies + promoted_tweet_profile_retweets + promoted_tweet_profile_follows` | | \{Engagement Rate} | `{Total Engagements} / {Impressions}` | | `billed_charge_local_micro / {Total Engagements}` | | | \{Media Views} | `promoted_tweet_timeline_media_views + promoted_tweet_search_media_views + promoted_tweet_profile_media_views` | | \{Media View Rate} | `{Media Views} / {Impressions}` | ### WEBSITE\_CLICKS | | | | :------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | `promoted_tweet_search_impressions + promoted_tweet_timeline_impressions + promoted_tweet_profile_impressions` | | | `billed_charge_local_micro / {Impressions} / 1000` | | | \{Link Clicks} | `promoted_tweet_search_url_clicks + promoted_tweet_timeline_url_clicks + promoted_tweet_profile_url_clicks` | | \{Click Rate} | `{Link Clicks} / {Impressions}` | | `billed_charge_local_micro / {Link Clicks}` | | | `conversion_site_visits` | | | \{Conversion Rate} | `conversion_site_visits / {Impressions}` | | `billed_charge_local_micro / conversion_site_visits` | | ### APP\_INSTALLS and APP\_ENGAGEMENTS | | | | :------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | `promoted_tweet_search_impressions + promoted_tweet_timeline_impressions` | | | `billed_charge_local_micro / {Impressions} / 1000` | | | \{App Clicks} | `promoted_tweet_app_install_attempts + promoted_tweet_app_open_attempts + promoted_tweet_timeline_url_clicks + promoted_tweet_search_url_clicks` | | \{App Click Rate} | `{App Clicks} / {Impressions}` | | `billed_charge_local_micro / {App Clicks}` | | | `billed_charge_local_micro / mobile_conversion_installs` | | ### FOLLOWERS | | | | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | `promoted_account_impressions` | | | `billed_charge_local_micro / {Impressions} / 1000` | | | `promoted_account_follows` | | | \{Follow Rate} | `promoted_account_follow_rate` | | `billed_charge_local_micro / promoted_account_follows` | | | \{Media Views} | `promoted_tweet_timeline_media_views + promoted_tweet_search_media_views + promoted_tweet_profile_media_views` | | \{Media View Rate} | `{Media Views} / {Impressions}` | ### LEAD\_GENERATION | | | | :---------------------------------------------------------------------------------------------------------------------------- | :------------------------------------ | | Derived Metric | Exposed Metric Calculation | | `promoted_tweet_search_impressions + promoted_tweet_timeline_impressions + promoted_tweet_profile_impressions` | | | `billed_charge_local_micro / {Impressions} / 1000` | | | `promoted_tweet_search_card_engagements + promoted_tweet_timeline_card_engagements + promoted_tweet_profile_card_engagements` | | | \{Lead Rate} | `{Leads} / {Impressions}` | | \{Cost Per Lead} | `billed_charge_local_micro / {Leads}` | ### VIDEO\_VIEWS | | | | :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | `promoted_tweet_search_impressions + promoted_tweet_timeline_impressions + promoted_tweet_profile_impressions` | | | `billed_charge_local_micro / {Impressions} / 1000` | | | \{Video Views} | `promoted_video_total_views` | | \{Video Rate} | `promoted_video_total_views / {Impressions}` | | \{Cost Per View} | `billed_charge_local_micro / promoted_video_total_views` | ### QUALIFIED\_IMPRESSIONS | | | | :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | Derived Metric | Exposed Metric Calculation | | `promoted_tweet_search_impressions + promoted_tweet_timeline_impressions + promoted_tweet_profile_impressions` | | | `billed_charge_local_micro / {Impressions} / 1000` | | | \{Qualified Impressions} | `promoted_tweet_timeline_qualified_impressions + promoted_tweet_search_qualified_impressions + promoted_tweet_profile_qualified_impressions` | | \{Qualified Impression Rate} | `{Qualified Impressions} / {Impressions}` | | \{Cost Per 1000 Qualified Impressions } | `billed_charge_local_micro / {Qualified Impressions} / 1000` | ### CUSTOM For `placement_type` of `PROMOTED_ACCOUNT` see the `FOLLOWERS` objective above. For all other placements with this objective, see `ENGAGEMENTS` for the corresponding derived metrics. ## Guides ### Active Entities #### Introduction The [Active Entities endpoint](/x-ads-api/analytics#get-stats-accounts-account-id-active-entities) is designed to be used in conjunction with our [synchronous](/x-ads-api/analytics#get-stats-accounts-account-id) and [asynchronous](/x-ads-api/analytics#asynchronous-analytics) analytics endpoints as it provides information about which campaigns to request analytics for. It does this by returning details about ads entities and when their metrics changed. Using this endpoint will greatly simplify your code and analytics fetching logic. This guide includes information and context about the endpoint and its data source. It also provides [usage guidelines](#usage) and a series of [example requests](#example), demonstrating how to use Active Entities in conjunction with our analytics endpoints. The [Summary section](#summary) provides a high-level description of the recommended approach. #### Data Whenever an ads entity metric changes, we record information about that change. These change events are stored in hourly buckets and include details about the entity as well as the time that the change applies to. The latter is necessary because change events do not always correspond to when they were recorded. Billing adjustments are a common reason for this, but there are others, too. #### Endpoint ### Request Active Entities requests are scoped under ads accounts and have three required query parameters: `entity`, `start_time`, and `end_time`. `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t/active_entities?entity=PROMOTED_TWEET&start_time=2019-03-05T00:00:00Z&end_time=2019-03-06T00:00:00Z"` The following `entity` values are supported: `CAMPAIGN`, `FUNDING_INSTRUMENT`, `LINE_ITEM`, `MEDIA_CREATIVE`, `PROMOTED_ACCOUNT`, and `PROMOTED_TWEET`. This reflects the entity types that our analytics endpoints support. The `start_time` and `end_time` values must be expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) and specify which hourly buckets to query. These must be expressed in whole hours. This endpoint also supports three optional parameters that can be used to filter results: `funding_instrument_ids`, `campaign_ids`, and `line_item_ids`. These work at all levels of the ads hierarchy and with any specified `entity` type. ### Response The Active Entities response for the request above is shown below. ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "entity": "PROMOTED_TWEET", "start_time": "2019-03-05T00:00:00Z", "end_time": "2019-03-06T00:00:00Z" } }, "data": [ { "entity_id": "2r0wxw", "activity_start_time": "2019-03-04T20:55:20Z", "activity_end_time": "2019-03-05T03:43:56Z", "placements": [ "ALL_ON_TWITTER" ] }, { "entity_id": "2r30fn", "activity_start_time": "2019-03-05T08:11:08Z", "activity_end_time": "2019-03-05T14:40:59Z", "placements": [ "ALL_ON_TWITTER", "PUBLISHER_NETWORK" ] } ] } ``` The `data` array includes an object for every entity that should be included in a subsequent analytics request. You should not request analytics for IDs outside of this set. Each object includes four fields: `entity_id`, `activity_start_time`, `activity_end_time`, and `placements`. The activity start and end times represent the time range that the associated entity's change events apply to and, thus, determine the dates that should be specified in subsequent analytics requests. The `placements` array can include the following values: `ALL_ON_TWITTER`, `PUBLISHER_NETWORK`, `SPOTLIGHT`, and `TREND`. It indicates which placements should be requested for the given entity ID. #### Usage The Active Entities endpoint should dictate how analytics requests are made. The following usage guidelines are written to support analytics synchronization, enabling partners to keep their data stores in sync with Twitter. In other words, it describes how to perform regularly-scheduled background syncs. There are two decisions a developer must make. 1. How often to request active entities information and, thus, how often to pull analytics. 2. How to use the activity start and end times to determine the analytics request's `start_time` and `end_time` values. These are discussed in greater detail in each of the two subsections, below, after the summary. ### Summary Use the Active Entities endpoint in the following way to dictate how analytics requests are made. Follow this after you've decided how often to request active entities information and, thus, how often to pull analytics. 1. Make the Active Entities request. 2. Split the response by placement. One group for `ALL_ON_TWITTER`, one for `PUBLISHER_NETWORK`, one for `SPOTLIGHT`, and one for `TREND`. 3. For each placement group, do the following. 1. Extract the entity IDs. 2. Determine the analytics `start_time` and `end_time` values. * Find the minimum `activity_start_time`. Round this value down. * Find the maximum `activity_end_time`. Round this value up. 3. Make the analytics request(s). * Group entity IDs into batches of 20. * Use the `start_time` and `end_time` values from #3b. * Specify the appropriate `placement` value. 4. Write to your data store. Please see [active\_entities.py](https://github.com/xdevplatform/twitter-python-ads-sdk/blob/master/examples/active_entities.py) as an example that uses the Python SDK. ### Frequency The answer to the first question determines the time range that should be used in Active Entities requests. For example, if requesting active entities information every hour, the time range should be an hour. If requesting active entities information once a day, the time range should be a day. In other words, time ranges should be selected such that the current request's `start_time` is equal to the previous request's `end_time`. **Note**: A time window should only be requested once. Requesting a time window more than once will lead to unnecessary analytics requests. (Exception below.) For partners wishing to request analytics multiple times an hour for the *current* hour, the same pattern applies—the frequency determines the time range. The table below shows example Active Entities start and end timestamps for this scenario. | | | | | :--------------- | :------------------------- | :----------------------- | | **Request time** | **`start_time` timestamp** | **`end_time` timestamp** | | 00:15:00 | 00:00:00 | 00:15:00 | | 00:30:00 | 00:15:00 | 00:30:00 | | 00:45:00 | 00:30:00 | 00:45:00 | | 01:00:00 | 00:45:00 | 01:00:00 | Given the way that change events are stored, all four Active Entities requests above query the same hourly bucket, which is necessary for this use case. However, after the current hour, this hourly bucket should no longer be queried. ### Activity Times We recommend the following approach to working with activity start and end times. Across all objects in the Active Entities response, find the minimum `activity_start_time` and the maximum `activity_end_time`. Modify these values by rounding the minimum activity start time down and rounding the maximum activity end time up. Specifically, set the timestamps to zero for both and add one day to the end time, as illustrated in the following table. These are the start and end times that should be specified in subsequent analytics requests. | | | | :------------------------------------------------------- | :------------------------------------------------------- | | **Min, max activity times** | **Derived times** | | 2019-03-04T20:55:20Z

2019-03-05T14:40:59Z | 2019-03-04T00:00:00Z

2019-03-06T00:00:00Z | **Note**: It's important to include the timestamps with hours, minutes, and seconds set to zero. Otherwise, if only the date is passed in, we will assume you're requesting analytics starting and ending at midnight in the ads account's timezone, which may not be desirable. For example, if the minimum activity start time is 2019-02-28T01:30:07Z and the timestamp is omitted for an ads account with an offset of -08:00:00, the analytics request will miss changes that happened between 01:30 and 08:00. Alternatively, if you would prefer to request analytics for just the returned activity time window without expanding to full days, you can. Using this approach, the derived start and end times would be 2019-03-04T20:00:00Z and 2019-03-05T15:00:00Z, respectively. (Note that ranges like these are not accepted if you specify `DAY` granularity in the analytics request.) #### Example This section demonstrates how to use Active Entities in conjunction with the synchronous analytics endpoint. (The responses have been slightly modified for readability.) In this example, the Active Entities endpoint is called at the top of each hour, with each request looking at the previous hour. The response determines how the synchronous analytics endpoint is used. The first Active Entities request is made at 03:00:00. The response indicates that line item dvcz7's metrics changed and that those change events apply to the window between 02:02:55 and 02:28:12. ``` `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t/active_entities?entity=LINE_ITEM&start_time=2019-02-11T02:00:00Z&end_time=2019-02-11T03:00:00Z"` ``` ```json theme={null} { "request": {}, "data": [ { "entity_id": "dvcz7", "activity_start_time": "2019-02-11T02:02:55Z", "activity_end_time": "2019-02-11T02:58:12Z", "placements": [ "ALL_ON_TWITTER" ] } ] } ``` Based on these activity start and end times and using the approach described above, the analytics `start_time` and `end_time` values are set to 2019-02-11T00:00:00Z and 2019-02-12T00:00:00Z, respectively. We see that the third element in each of the metrics arrays below are non-zero, as we expected based on the active entities information. ``` `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t?entity=LINE_ITEM&entity_ids=dvcz7&start_time=2019-02-11T00:00:00Z&end_time=2019-02-12T00:00:00Z&granularity=HOUR&metric_groups=ENGAGEMENT,VIDEO&placement=ALL_ON_TWITTER"` ``` ```json theme={null} { "data_type": "stats", "time_series_length": 24, "data": [ { "id": "dvcz7", "id_data": [ { "segment": null, "metrics": { "impressions": [ 0,0,2792,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ], "engagements": [ 0,0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ], "video_total_views": [ 0,0,1326,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] } } ] } ], "request": {} } ``` The next Active Entities request happens at 04:00:00 and only looks at the previous hour. As mentioned above, a time window should only be requested once. Based on the response, we see that change events for this line item apply to *both* 02:00:00 and 03:00:00. In the subsequent analytics request, we expect to see changes for both hours. ``` `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t/active_entities?entity= LINE_ITEM&start_time=2019-02-11T03:00:00Z&end_time=2019-02-11T04:00:00Z"` ``` ```json theme={null} { "request": {}, "data": [ { "entity_id": "dvcz7", "activity_start_time": "2019-02-11T02:07:17Z", "activity_end_time": "2019-02-11T03:49:22Z", "placements": [ "ALL_ON_TWITTER" ] } ] } ``` In addition to seeing non-zero metrics for 03:00:00, we see that the impressions, spend, and MRC video views have been updated from their previous values. Impressions, for example, are now 2,995 for the 02:00:00 hour, up from 2,792. This demonstrates how change events that were recorded during the 03:00:00 hour apply to the 02:00:00 hour. ``` `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t?entity=LINE_ITEM&entity_ids=dvcz7&start_time=2019-02-11T00:00:00Z&end_time=2019-02-12T00:00:00Z&granularity=HOUR&metric_groups=ENGAGEMENT,VIDEO&placement=ALL_ON_TWITTER"` ``` ```json theme={null} { "data_type": "stats", "time_series_length": 24, "data": [ { "id": "dvcz7", "id_data": [ { "segment": null, "metrics": { "impressions": [ 0,0,2995,734,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ], "engagements": [ 0,0,65,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ], "video_total_views": [ 0,0,1449,342,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] } } ] } ], "request": {} } ``` The Active Entities request at 05:00:00, again looking at just the previous hour, shows that change events apply to the 03:00:00 hour only. The changes to analytics metrics in the subsequent request reflect this. ``` `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t/active_entities?entity=LINE_ITEM&start_time=2019-02-11T04:00:00Z&end_time=2019-02-11T05:00:00Z"` ``` ```json theme={null} { "request": {}, "data": [ { "entity_id": "dvcz7", "activity_start_time": "2019-02-11T03:42:39Z", "activity_end_time": "2019-02-11T03:48:48Z", "placements": [ "ALL_ON_TWITTER" ] } ] } ``` The analytics response shows that only metrics for the 03:00:00 hour have changed; the values for the 02:00:00 hour are the same as they were during the previous analytics request. ``` `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t?entity=LINE_ITEM&entity_ids= dvcz7&start_time=2019-02-11T00:00:00Z&end_time=2019-02-12T00:00:00Z&granularity=HOUR&metric_groups=ENGAGEMENT,VIDEO&placement=ALL_ON_TWITTER"` ``` ```json theme={null} { "data_type": "stats", "time_series_length": 24, "data": [ { "id": "dvcz7", "id_data": [ { "segment": null, "metrics": { "impressions": [ 0,0,2995,753,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ], "engagements": [ 0,0,65,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ], "video_total_views": [ 0,0,1449,351,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] } } ] } ], "request": {} } ``` Finally, at 06:00:00 we see that there are no additional change events. **Note**: This does *not* imply that metrics for this line item cannot change in the future, though. ``` `twurl -H ads-api.x.com "/11/stats/accounts/18ce54d4x5t/active_entities?entity=LINE_ITEM&start_time=2019-02-11T05:00:00Z&end_time=2019-02-11T06:00:00Z"` ``` ```json theme={null} { "request": {}, "data": [] } ``` ### Asynchronous Guide ## API Reference ### Asynchronous Analytics #### Introduction The asynchronous analytics endpoints allow partners and advertisers to request metrics by submitting create requests that the server processes asynchronously. (We refer to these as asynchronous analytics "jobs.") With this approach, the client's connection does not need to remain open until the request has been fulfilled. These endpoints, like their synchronous counterpart, allow partners and advertisers to request detailed statistics on campaign performance. They support requesting data for accounts, funding instruments, campaigns, line items, promoted Tweets, and media creatives. The difference between these and the synchronous endpoint is that the asynchronous analytics endpoints support longer date ranges, up to 90 days, as well as segmentation. Additional details on the differences between the two can be found on our [Analytics Overview](/x-ads-api/analytics) page. Unlike our synchronous endpoints, rate limiting is based on the number of concurrent jobs for a given account. In other words, it's based on the number of jobs that can be in a processing state at a given time. We count this at the ads account level. #### Usage Retrieving campaign metrics using the asynchronous analytics endpoints is a multi-step process. It involves creating a job, checking whether the job has finished processing, and, finally, downloading the data. The data file must be decompressed. The four specific steps are outlined below. 1. Create the job using the [POST stats/jobs/accounts/:account\_id](/x-ads-api/analytics#asynchronous-analytics) endpoint. 2. Make requests at regular intervals to the [GET stats/jobs/accounts/:account\_id](/x-ads-api/analytics#asynchronous-analytics) endpoint to determine whether the job has finished processing. 3. Once the job has finished processing, download the data file. 4. Unzip the data file. The response object returned in the data file has the same JSON schema as the synchronous analytics endpoint's response. Segmented campaign metrics are only available via the asynchronous analytics endpoints. Campaign metrics can be broken out by location, gender, interest, keyword, and more. For a full list of options, see the [Metrics and Segmentation](/x-ads-api/analytics#metrics-and-segmentation) page. In order to request segmented metrics, use the `segmentation_type` request parameter when creating the job. #### Example This section demonstrates how to use the asynchronous analytics endpoints. Start by creating a job using the [POST stats/jobs/accounts/:account\_id](/x-ads-api/analytics#asynchronous-analytics) endpoint. The example below requests engagement metrics—such as impressions, likes, clicks, etc.—for a specific line item over a week's time. (Note that the requested time range goes up to, but does not include March 20th since the timestamp is set to midnight.) ``` $ twurl -X POST -H ads-api.x.com "/9/stats/jobs/accounts/18ce54d4x5t?entity=LINE_ITEM&entity_ids=el32n&start_time=2019-03-12T00:00:00Z&end_time=2019-03-20T00:00:00Z&granularity=TOTAL&placement=ALL_ON_TWITTER&metric_groups=ENGAGEMENT" ``` ```json theme={null} { "request": { "params": { "start_time": "2019-03-12T00:00:00Z", "entity_ids": [ "el32n" ], "end_time": "2019-03-20T00:00:00Z", "placement": "ALL_ON_TWITTER", "granularity": "TOTAL", "entity": "LINE_ITEM", "metric_groups": [ "ENGAGEMENT" ] } }, "data": { "start_time": "2019-03-12T00:00:00Z", "segmentation_type": null, "url": null, "id_str": "1120829647711653888", "entity_ids": [ "el32n" ], "end_time": "2019-03-20T00:00:00Z", "country": null, "placement": "ALL_ON_TWITTER", "id": 1120829647711653888, "expires_at": null, "account_id": "18ce54d4x5t", "status": "PROCESSING", "granularity": "TOTAL", "entity": "LINE_ITEM", "created_at": "2019-04-23T23:19:46Z", "platform": null, "updated_at": "2019-04-23T23:19:46Z", "metric_groups": [ "ENGAGEMENT" ] } } ``` This response does not return the line item metrics. It simply provides information about the job you just created. The job ID is needed to check on the status of the job. This is shown in both the `id` and `id_str` response attributes. Next, you'll want to check whether the job you've created using the `id_str` from the previous response, has finished processing as indicated by `"status": "SUCCESS"` in the response. This means the data is ready to download. The `url` field contains the download link. ``` $ twurl -H ads-api.x.com "/9/stats/jobs/accounts/18ce54d4x5t?job_ids=1120829647711653888" ``` ```json theme={null} { "request": { "params": { "job_ids": [ 1120829647711653888 ] } }, "next_cursor": "1120828505715920896", "data": [ { "start_time": "2019-03-12T00:00:00Z", "segmentation_type": null, "url": "https://ton.twimg.com/advertiser-api-async-analytics/zBkuuPeEVx-5OygDVcZpqNtwt51Z5X9d-_AXNRcyhBlhBOgOfi6UDmGBvUFJAKnHY9ABN8z9f9V3Wn4l3OmF4KzJDmTUjNCikq9JwBUYm2AP8pRRoV-kPUgR0PaIqAb4.json.gz", "id_str": "1120829647711653888", "entity_ids": [ "el32n" ], "end_time": "2019-03-20T00:00:00Z", "country": null, "placement": "ALL_ON_TWITTER", "id": 1120829647711653900, "expires_at": "2019-04-25T23:19:48Z", "account_id": "18ce54d4x5t", "status": "SUCCESS", "granularity": "TOTAL", "entity": "LINE_ITEM", "created_at": "2019-04-23T23:19:46Z", "platform": null, "updated_at": "2019-04-23T23:19:48Z", "metric_groups": [ "ENGAGEMENT" ] } ] } ``` While we're passing in a single job ID in the above example, in practice, you'll want to use the `job_ids` parameter to check on the status of multiple jobs at a time by specifying up to 200 job IDs. Next, download the data file using the listed `url` value. ``` $ wget https://ton.twimg.com/advertiser-api-async-analytics/zBkuuPeEVx-5OygDVcZpqNtwt51Z5X9d-_AXNRcyhBlhBOgOfi6UDmGBvUFJAKnHY9ABN8z9f9V3Wn4l3OmF4KzJDmTUjNCikq9JwBUYm2AP8pRRoV-kPUgR0PaIqAb4.json.gz --2019-04-23 17:52:12-- https://ton.twimg.com/advertiser-api-async-analytics/zBkuuPeEVx-5OygDVcZpqNtwt51Z5X9d-_AXNRcyhBlhBOgOfi6UDmGBvUFJAKnHY9ABN8z9f9V3Wn4l3OmF4KzJDmTUjNCikq9JwBUYm2AP8pRRoV-kPUgR0PaIqAb4.json.gz Resolving ton.twimg.com (ton.twimg.com)... 72.21.91.70 Connecting to ton.twimg.com (ton.twimg.com)|72.21.91.70|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 381 [application/gzip] Saving to: 'zBkuuPeEVx-5OygDVcZpqNtwt51Z5X9d-_AXNRcyhBlhBOgOfi6UDmGBvUFJAKnHY9ABN8z9f9V3Wn4l3OmF4KzJDmTUjNCikq9JwBUYm2AP8pRRoV-kPUgR0PaIqAb4.json.gz' zBkuuPeEVx-5OygDVcZpqNtwt51Z5 100%[=================================================>] 381 --.-KB/s in 0s 2019-04-23 17:52:12 (5.27 MB/s) - 'zBkuuPeEVx-5OygDVcZpqNtwt51Z5X9d-_AXNRcyhBlhBOgOfi6UDmGBvUFJAKnHY9ABN8z9f9V3Wn4l3OmF4KzJDmTUjNCikq9JwBUYm2AP8pRRoV-kPUgR0PaIqAb4.json.gz' saved [381/381] ``` Finally, unzip the file. ``` `$ gunzip zBkuuPeEVx-5OygDVcZpqNtwt51Z5X9d-_AXNRcyhBlhBOgOfi6UDmGBvUFJAKnHY9ABN8z9f9V3Wn4l3OmF4KzJDmTUjNCikq9JwBUYm2AP8pRRoV-kPUgR0PaIqAb4.json.gz` ``` The contents of the file are shown below. ```json theme={null} `$ cat zBkuuPeEVx-5OygDVcZpqNtwt51Z5X9d-_AXNRcyhBlhBOgOfi6UDmGBvUFJAKnHY9ABN8z9f9V3Wn4l3OmF4KzJDmTUjNCikq9JwBUYm2AP8pRRoV-kPUgR0PaIqAb4.json` ``` ```json theme={null} { "data_type": "stats", "time_series_length": 1, "data": [ { "id": "el32n", "id_data": [ { "segment": null, "metrics": { "impressions": [ 3482 ], "tweets_send": null, "qualified_impressions": null, "follows": null, "app_clicks": null, "retweets": [ 102 ], "unfollows": null, "likes": [ 15 ], "engagements": [ 171 ], "clicks": [ 30 ], "card_engagements": null, "poll_card_vote": null, "replies": null, "carousel_swipes": null } } ] } ], "request": { "params": { "start_time": "2019-03-12T00:00:00Z", "segmentation_type": null, "entity_ids": [ "el32n" ], "end_time": "2019-03-20T00:00:00Z", "country": null, "placement": "ALL_ON_TWITTER", "granularity": "TOTAL", "entity": "LINE_ITEM", "platform": null, "metric_groups": [ "ENGAGEMENT" ] } } } ``` ### Reach and Average Frequency #### GET stats/accounts/:account\_id/reach/campaigns[](#get-stats-accounts-account-id-reach-campaigns "Permalink to this headline") Retrieve reach and average frequency analytics for specified campaigns. ### Resource URL[](#resource-url "Permalink to this headline") `https://ads-api.x.com/stats/accounts/:account_id/reach/campaigns` ### Parameters[](#parameters "Permalink to this headline") | Name | Description | | :------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_ids
*required* | Scope the response to just the desired campaigns by specifying a comma-separated list of identifiers. Up to 20 IDs may be provided.

**Note**: Up to 20 campaign IDs may be provided.

Type: string

Example: `8fgzf` | | end\_time
*required* | Scopes the retrieved data to the specified end time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-26T07:00:00Z` | | start\_time
*required* | Scopes the retrieved data to the specified start time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-19T07:00:00Z` | ### Example Request[](#example-request "Permalink to this headline") `GET https://ads-api.x.com/12/stats/accounts/18ce54d4x5t/reach/campaigns?campaign_ids=8fgzf&start_time=2017-05-19&end_time=2017-05-26` ### Example Response[](#example-response "Permalink to this headline") ```json theme={null} { "request": { "params": { "campaign_ids": [ "8fgzf" ], "start_time": "2017-05-19T00:00:00Z", "end_time": "2017-05-26T00:00:00Z", "account_id": "18ce54d4x5t" } }, "data_type": "reach", "data": [ { "id": "8fgzf", "total_audience_reach": 1217, "average_frequency": 1.01 } ] } ``` #### GET stats/accounts/:account\_id/reach/funding\_instruments[](#get-stats-accounts-account-id-reach-funding-instruments "Permalink to this headline") Retrieve reach and average frequency analytics for specified funding instruments. ### Resource URL[](#resource-url "Permalink to this headline") `https://ads-api.x.com/stats/accounts/:account_id/reach/funding_instruments` ### Parameters[](#parameters "Permalink to this headline") | Name | Description | | :----------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | funding\_instrument\_ids
*required* | Scope the response to just the desired funding instruments by specifying a comma-separated list of identifiers. Up to 20 IDs may be provided.

**Note**: Up to 20 funding instrument IDs may be provided.

Type: string

Example: `lygyi` | | end\_time
*required* | Scopes the retrieved data to the specified end time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-26T07:00:00Z` | | start\_time
*required* | Scopes the retrieved data to the specified start time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-19T07:00:00Z` | ### Example Request[](#example-request "Permalink to this headline") ``` GET https://ads-api.x.com/12/stats/accounts/18ce54d4x5t/reach/funding_instruments?funding_instrument_ids=lygyi&start_time=2017-05-19&end_time=2017-05-26 ``` ### Example Response[](#example-response "Permalink to this headline") ```json theme={null} { "request": { "params": { "funding_instrument_ids": [ "lygyi" ], "start_time": "2017-05-19T00:00:00Z", "end_time": "2017-05-26T00:00:00Z", "account_id": "18ce54d4x5t" } }, "data_type": "reach", "data": [ { "id": "lygyi", "total_audience_reach": 1217, "average_frequency": 1.01 } ] } ``` ### Synchronous Analytics #### GET stats/accounts/:account\_id[](#get-stats-accounts-account-id "Permalink to this headline") Retrieve synchronous analytics for the current account. A maximum time range (`end_time` - `start_time`) of 7 days is allowed. ### Resource URL[](#resource-url "Permalink to this headline") `https://ads-api.x.com/12/stats/accounts/:account_id` ### Parameters[](#parameters "Permalink to this headline") | Name | Description | | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | end\_time
*required* | Scopes the retrieved data to the specified end time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-26T07:00:00Z` | | entity
*required* | The entity type to retrieve data for.

Type: enum

Possible values: `ACCOUNT`, `CAMPAIGN`, `FUNDING_INSTRUMENT`, `LINE_ITEM`, `ORGANIC_TWEET`, `PROMOTED_ACCOUNT`, `PROMOTED_TWEET`, `MEDIA_CREATIVE` | | entity\_ids
*required* | The specific entities to retrieve data for. Specify a comma-separated list of entity IDs.

**Note**: Up to 20 entity IDs may be provided.

Type: string

Example: `8u94t` | | granularity
*required* | Specify how granular the retrieved data should be.

Type: enum

Possible values: `DAY`, `HOUR`, `TOTAL` | | metric\_groups
*required* | The specific metrics that should be returned. Specify a comma-separated list of metric groups. For more information see [Metrics and Segmentation](/x-ads-api/analytics#metrics-and-segmentation).

**Note**: `MOBILE_CONVERSION` data should be requested separately.

Type: enum

Possible values: `BILLING`, `ENGAGEMENT`, `LIFE_TIME_VALUE_MOBILE_CONVERSION`, `MEDIA`, `MOBILE_CONVERSION`, `VIDEO`, `WEB_CONVERSION` | | placement
*required* | Scopes the retrieved data to a particular placement.

**Note**: Only a single value accepted per request. For entities with both X and X Audience Platform placement, separate requests are required, one for each placement value.

Type: enum

Possible values: `ALL_ON_TWITTER`, `PUBLISHER_NETWORK`, `SPOTLIGHT`, `TREND` | | start\_time
*required* | Scopes the retrieved data to the specified start time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-19T07:00:00Z` | ### Example Request[](#example-request "Permalink to this headline") ``` GET https://ads-api.x.com/12/stats/accounts/18ce54d4x5t?entity=LINE_ITEM&entity_ids=8u94t&start_time=2017-05-19&end_time=2017-05-26&granularity=TOTAL&placement=ALL_ON_TWITTER&metric_groups=ENGAGEMENT ``` ### Example Response[](#example-response "Permalink to this headline") ```json theme={null} { "data_type": "stats", "time_series_length": 1, "data": [ { "id": "8u94t", "id_data": [ { "segment": null, "metrics": { "impressions": [ 1233 ], "tweets_send": null, "qualified_impressions": null, "follows": null, "app_clicks": null, "retweets": null, "likes": [ 1 ], "engagements": [ 58 ], "clicks": [ 58 ], "card_engagements": null, "poll_card_vote": null, "replies": null, "carousel_swipes": null } } ] } ], "request": { "params": { "start_time": "2017-05-19T07:00:00Z", "segmentation_type": null, "entity_ids": [ "8u94t" ], "end_time": "2017-05-26T07:00:00Z", "country": null, "placement": "ALL_ON_TWITTER", "granularity": "TOTAL", "entity": "LINE_ITEM", "platform": null, "metric_groups": [ "ENGAGEMENT" ] } } } ``` ### Active Entities #### GET stats/accounts/:account\_id/active\_entities[](#get-stats-accounts-account-id-active-entities "Permalink to this headline") Retrieve details about which entities' analytics metrics have changed in a given time period. This endpoint should be used in conjunction with our analytics endpoints. The results of this endpoint indicate which ads entities to request analytics for. See our [Active Entities Guide](/x-ads-api/analytics#active-entities) for usage guidelines. Change events are available in hourly buckets. * The `start_time` and `end_time` values specify which hourly buckets to query. * The returned `data` array will include an object for every entity that should be included in subsequent analytics requests. * **IMPORTANT**: The dates that should be specified in subsequent analytics requests should be determined based on the `activity_start_time` and `activity_end_time` values. * These values represent the time ranges that the stored change events *apply to*. This is returned per entity. **Note**: A maximum time range (`end_time` - `start_time`) of 90 days is allowed. ### Resource URL[](#resource-url "Permalink to this headline") `https://ads-api.x.com/12/stats/accounts/:account_id/active_entities` ### Parameters[](#parameters "Permalink to this headline") | Name | Description | | :----------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | end\_time
*required* | Scopes the retrieved data to the specified end time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-26T07:00:00Z` | | entity
*required* | The entity type to retrieve data for.

Type: enum

Possible values: `CAMPAIGN`, `FUNDING_INSTRUMENT`, `LINE_ITEM`, `MEDIA_CREATIVE`, `PROMOTED_ACCOUNT`, `PROMOTED_TWEET` | | start\_time
*required* | Scopes the retrieved data to the specified start time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-19T07:00:00Z` | | campaign\_ids
*optional* | Scope the response to just entities associated with desired campaigns by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

**Note**: Exclusive with `funding_instrument_ids` and `line_item_ids`.

Type: string

Example: `8wku2` | | funding\_instrument\_ids
*optional* | Scope the response to just entities associated with desired funding instruments by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

**Note**: Exclusive with `campaign_ids` and `line_item_ids`.

Type: string

Example: `lygyi` | | line\_item\_ids
*optional* | Scope the response to just entities associated with desired line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

**Note**: Exclusive with `campaign_ids` and `line_item_ids`.

Type: string

Example: `8v7jo` | ### Example Request[](#example-request "Permalink to this headline") `GET https://ads-api.x.com/12/stats/accounts/18ce54d4x5t/active_entities?entity=PROMOTED_TWEET&start_time=2019-02-28&end_time=2019-03-01` ### Example Response[](#example-response "Permalink to this headline") ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "entity": "PROMOTED_TWEET", "start_time": "2019-02-28T08:00:00Z", "end_time": "2019-03-01T08:00:00Z" } }, "data": [ { "entity_id": "2mvb28", "activity_start_time": "2019-02-28T01:30:07Z", "activity_end_time": "2019-03-01T07:42:55Z", "placements": [ "ALL_ON_TWITTER" ] }, { "entity_id": "2mvb29", "activity_start_time": "2019-02-27T11:30:07Z", "activity_end_time": "2019-03-01T07:42:50Z", "placements": [ "ALL_ON_TWITTER", "PUBLISHER_NETWORK" ] }, { "entity_id": "2mvfan", "activity_start_time": "2019-02-27T09:00:05Z", "activity_end_time": "2019-03-01T06:06:36Z", "placements": [ "PUBLISHER_NETWORK" ] }, { "entity_id": "2n17dx", "activity_start_time": "2019-02-28T02:02:26Z", "activity_end_time": "2019-03-01T07:52:44Z", "placements": [ "ALL_ON_TWITTER", "PUBLISHER_NETWORK" ] } ] } ``` # Audiences Source: https://docs.x.com/x-ads-api/audiences ## Custom Audiences ### Overview There are multiple ways for partners to create [Custom Audiences](https://business.x.com/en/targeting/tailored-audiences.html). * [Audience API (CRM)](#crm) * [Web](#web) * [Mobile](#mobile) * [Flexible](#flexible) Please note that you cannot exclude lookalike custom audiences from targeting. Additionally, you cannot target both a custom audience and a custom audience lookalike on the same ad line item (ad group). **Audience management** Audiences can be managed via audience partners and Ads API partners. We offer a series of endpoints in the API to access and maintain custom audiences. For custom audience information, we offer 2 endpoints: * [GET accounts/:account\_id/custom\_audiences](/x-ads-api/audiences) * [GET accounts/:account\_id/custom\_audiences/:custom\_audience\_id](/x-ads-api/audiences) For more details on how to upload and manage audiences, view the [Audience API guide](/x-ads-api/audiences). **Processing Times** Generally speaking audience changes are processed in batches that run every 6-8 hours. While an audience change is processing the existing audience to be updated is unaffected. We do not recommend making more than one update for additions and one update for removals per audience within this timeframe. **Targeting** An audience can only be targeted if it matches at least 100 users active within the past 90 days on X-owned and -operated clients. [GET accounts/:account\_id/custom\_audiences/:custom\_audience\_id](/x-ads-api/audiences) will indicate if an audience may not be targeted because it matches too few users. **Audience API (CRM)** image2 Audience or API partners provide a list of hashed identifiers and X performs a match and produces segments that are made available against media buying on X. Partners can create these audiences with the [Audience API](/x-ads-api/audiences). **How it works?** image3 **Web** We offer a standard cookie matching process when working with MPP audience partners to identify segments to target against media buying on X. In addition, advertsiers can setup a [X Web Event Tag](/x-ads-api/measurement/web-conversions#web-event-tags) to collect website user data and create a corresponding Custom Audience. **Setup Steps** image0 ![]() **How it works?** image1 **Mobile** Please see [the Custom Audiences from Mobile Apps blog post](https://blog.x.com/2014/introducing-tailored-audiences-from-mobile-apps) for details. **Flexible** [Flexible audiences](/x-ads-api/audiences) give advertisers the ability to build and save audience combinations based on existing custom audiences or subsets of existing custom audiences. Subsets of a custom audience’s members can be targeted based on the recency and frequency of interaction. **Restricted Use Cases for Custome Audiences** [Read more about restrictions](https://developer.x.com/en/developer-terms/more-on-restricted-use-cases "Read more about restrictions") ### Audiences FAQ[](#real-time-tailored-audiences "Permalink to this headline") **Q: We pushed a huge amount of data, why does the audience size show up as TOO\_SMALL?** A: Currently the data is being added into audience in realtime, but the job which processes data to provide audience size will run only after a period of time. The correct audience size should be displayed in the UI after a matter of hours. **Q: We finished sending audience data, and waited 24 hours or more, but still cannot target the audience - what should we do as next steps?** A: Please confirm that the following things: * The user ID being passed is correct and not malformed. * The audience names being passed are correct and match previous membership updates. * Please confirm the response from POST commands. * Please confirm the ID Sync pixel is implemented correctly, and as described by ID Sync process enough users have visited the site in question to map users. Unmapped users in the membership updates will not be translated into targeted users. If all else are confirmed as correct and working, please contact X product contacts with information as detailed as possible (see [Guide to Partner Inbounds](/x-ads-api/introduction) for example of preferred information). **Q: How many times can we call the endpoint, and with what algorithm?** A: We strongly recommend that you call our system with incremental deltas, and never re-send the complete audience memberships. The system has been tested to have a throughput sufficient to process incremental data updates for some of the largest websites in the world. The initial upload of audiences should be carefully throttled and first upload is expected to take a significant amount of time to complete. **Q: What is the minimum size for an audience to be used for targeting?** * The minimum size for an audience to is 100 users (post match). If an audience with less than 500 users is matched, it will not be available for targeting in the X Ads UI. **Q: How long will it take to process the audience files? And how long will it take for the audience files to be ready in the X User Interface?** * It typically takes 4-6 hours to process the audience files, but that will depend on the size of the file. Once the file is processed the audiences are available on the X Ads UI. **Q: How is the match rate calculated?** * Match Rate = 90 day active X users / number of users provided **Q: How do we test if an audience file is working properly?** * You can provide a test audience file and use “keltonlynn” as the advertiser handle. We can then verify that the file is able to be properly ingested and loaded into the X UI. **Q: What is a partner user identifier (`p_user_id`)?** * This is the identifier that is used by your company to uniquely identify each of your customers. **Q: What is a standard ID?** * This can be an email address, device ID, X @handle or ID). **Q: How do I get the HMAC Key?** * This will be provided by an encrypted email. Please provide your public PGP key to [mpp-inquiry@x.com](mailto:mpp-inquiry%x.com) and we will send you a test email to verify that everything is working. Once verified, we will send you the HMAC Key. **Q: How do I verify that the hashing process worked using the given HMAC Key?** * X will provide a test file (containing sample email addresses, device IDs, etc…) and a resulting hash file that you can verify your results against. **Q: Is there a file size limitation for the full data match file?** * No, there is no size limitation for the full data match file. **Q: How long will it take for the full data match file to be processed?** * Once the file is received by X, it will take approximately 1 day to process the file. ### CRM image0 This document describes the integration details for Custom Audiences CRM partners including file formats & data exchange process. **Summary** Company will provide a list of hashed common user identifiers (i.e. e-mail addresses) or partner user IDs on behalf of a customer to X to perform a blind match and produce a list of X User IDs for targeting. The segments for targeting will be made available to the advertiser’s specific @handle specified by the filename in ads.x.com campaign setup. All files from Company will be provided to X through a secure package on IronBox ([www.golockbox.com](http://www.golockbox.com)) through a specific account granted to Company by X. X will provide access to IronBox. Documentation on IronBox APIs can be found at [https://secure.goironcloud.com/Docs/Help/](https://secure.goironcloud.com/Docs/Help/). #### Partner ID Matching Requirements If company uses its own standard ID system to track users (i.e. not common user identifiers like email addresses, device ids, X user ID, etc...) then this is the recommended process. **1. Full Data match** Initially, Company will provide a comprehensive list of all user records which include a unique common user identifier with X in a single file to perform a full data match and produce a mapping stored by X of Partner IDs (`p_user_id`) to X IDs (`tw_id`). This will be done on a 2-3 month basis regularly to ensure proper upkeep. Once the match is completed, X will share a baseline match rate from this file with Company via e-mail. The format of this file should be: Name Convention: FullDataMatch.\[CompanyName].txt Hashing Algorithm: HMAC\_SHA-256 Format: Column 1: HMAC hashed value of common identifiers Column 2: Partner User ID (unique per user, non-unique in file) Column Delimiter (CSV): Commas will be used to delimit the hashed common user identifier from the Partner ID Line separated values * Ex: If user record A has Partner User ID 1 and common identifier 1, 2 and 3: | | | | :----------------------- | :--------- | | common user identifier 1 | p\_user\_1 | | common user identifier 2 | p\_user\_1 | | common user identifier 3 | p\_user\_1 | \*See Hashing Directions section for common user identifiers below **2. Custom Segment Lists** Company will provide lists of users in the form of `p_user_id` to create custom audiences for customers for targeting on X. * Line separated values * `p_user_id` * * (Same as provided in 1. Full Data Match section above. If the value provided in full data match is hashed then Company will provide same hashed value in audience file. If value provided is not hashed then Company will provide unhashed value.) #### Standard Matching Requirements If company does not use a standard ID for mapping of all customer user identifiers, this is the recommended process. **Custom Segment Lists** Company will provide lists of hashed common user identifiers directly to X on behalf of customers to to create custom audiences. The format of this file should be: * Line separated values * Hashed common user identifier (i.e. e-mail address) * Follow file naming conventions outlined below * Follow hashing directions for e-mail addresses below (in Hashing Directions) #### Custom Segment List File Naming & Operations The operation of a file will be dictated by the name of the file with the following available operations and general file naming convention: audiencename\_partnername.handle.operation.filetype * audiencename: The name of the Custom Audience. This field is the name that will be displayed when selecting the audience in the ads.x.com campaign setup UI e.g. brand\_loyalty\_card\_holders. * partnername: Name of the company delivering the data on behalf of advertiser e.g. company\_name. * handle: X Account (@handle) that will have access to Custom Audiences e.g. @pepsi, @dietpepsi * operation: new, add, remove, removeall, replace (details below) * : Standard Unix epoch time in seconds, used to ensure that each audience file uploaded is unique * filetype: file should be in \*.txt format #### Creating and Updating Audiences Create a new audience with a single file e.g. loyalty\_card\_holders\_partnername.pepsi.new\.txt Add - Add the matches from a list to an existing audience e.g. loyalty\_card\_holders\_partnername.pepsi.add..txt Remove - Remove the matches from a list from an existing audience Ex: loyalty\_card\_holders\_partnername.pepsi.remove..txt Remove All - Remove the matches produced from a regularly updated cumulative list from all audiences for that client (i.e. Client’s Opt Out List). Ex: partnername.pepsi.removeall.txt * This can be used for a comprehensive list of users who have opted-out from the Advertiser. * X will only respect the latest list provided in this file, and will respect across all existing and future audiences for matched. X users at the time this file was provided & processed. Replace - Remove an existing audience and replace it with a new audience list. Ex: loyalty\_card\_holders\_partnername.pepsi.replace..txt Overall Company Opt-Out - Company will provide a cumulative Opt-Out file to remove users that have opted out as per the Company’s Opt-Out policy. X will only respect the latest list provided in this Company Opt-Out file and will respect across all existing and future audiences for matched X users at the time this file was provided & processed. The format of the Company Opt-Out file will be as follows: Ex: partnername.removeall.txt Delete - Remove an existing audience from the current list of audiences e.g. Ex: loyalty\_card\_holders\_partnername.pepsi.delete.txt #### Hashing Directions X will securely share a base64 encoded production key via PGP for hashing common user identifiers (i.e. email addresses). Company will base64 decode the key to produce a 32-byte key to be used to perform the hashing. Example base64 encoded key: BrQvOg+dACBUmKjRiNxZgJLh6zydjS0ZOv80FelTNzM= Example Base64 decoded key: /:� TшY Normalization: Company will perform basic normalization on the common user identifiers before hashing (except on Device IDs, see Device ID Normalization section). #### E-mail Normalization Namely, strip out the leading and trailing spaces and also lowercase the email address. Ex: Raw e-mail address: testemail\_Organisational\_baseball+884`@`It92I6Ev2B`.`Com After normalization: testemail\_organisational\_baseball+884`@`it92i6ev2b`.`com Hashed value: 74d9584eded0ad1e5572a1c1849f3716751d371d6117a6155dad5363f4b4fbec Note: The specific number of characters for both the encoded hmac and key could vary based on the input and the encoding so the specific number of characters. #### Device ID Normalization We will have the same requirements for hashing of device IDs using a SHA-256 hashing algorithm and a common salt that we provide to data partners. We strip out spaces like we do with email addresses, but there is no lowercase normalization for IDFAs/Android IDs and the exact format of the IDFA/Android ID should be used. Here is example raw format of Device IDs for iOS & Android, pre-hashing: iOS IDFA: DD99CFF7-6186-4602-9DF2-ED3FD0B2D431 Android ID: b5bf2122961b3595 Hashed iOS IDFA: 134fb8cd95c7fd42e2793f469a447198ca5f990968db2dbadad70e723ed9750b Hashed Android ID: 130dddff1939f229476f50bc8adab8fcb7e3525b0e9604fe8effc15e68cee4a4 #### X User ID Normalization X IDs will still be hashed as the grouping of data - ie Customer List of @handles - is private to the advertiser even though it is not PII. We will have the same requirements for hashing of X IDs using a SHA-256 hashing algorithm and a common salt that we provide to data partners. Spaces should be stripped out of both the X ID/`@`username, but User IDs do not require normalization. @usernames should be lowercased for normalization. And the @ symbol should not be included as part of the username. The raw ID format will be: * User ID: 27674040 * @username: testusername Hashed User ID: bf6b57d4e861e83bea8bbed2b800b251a64c95468ee6e8cb07c3368c9ed45e85 Hashed @username: 12201ae78ad1afa907c7112d17f498154ffb0bf9ea523f5390e072a06d7d9812 ### ID Sync Integration Partners sending data with a `p_id` must undergo an ID Sync process to generate a mapping of the advertiser or partner’s user ids to X user ids. This allows advertisers to directly target their own user segments on X. Partners must also set the value of the param `user_identifier_type` to either `TALIST_PARTNER_USER_ID` or `TAWEB_PARTNER_USER_ID` while sending their membership updates. * **Web Only**: This can be done by placing a pixel on the advertiser’s site, as outlined below. * **List**: This can be done using any of the methods described on the [CRM](/x-ads-api/audiences#crm) page. #### Pixel URL | | | :----------------------------------------------------------------- | | **Base URL** | | [https://analytics.x.com/i/adsct](https://analytics.x.com/i/adsct) | #### Pixel Parameters | | | | :------------ | :---------------------------------- | | **Parameter** | **Description** | | `p_id` | Your X-assigned partner id | | `p_user_id` | The user’s id in the partner system | #### ID Sync Pixel: Using an example partner id of 111, and an example `p_user_id` of abc, the constructed pixel would be the following: ```json theme={null}
    
    
``` **Opt-Out File Configuration and Sending Opt-Out Files** Partners should provide X with a list of users that to the partner’s best knowledge have selected to opt-out of targeted ad delivery. The format of file should be sent as: | | | | | | :---------------- | :---------------------------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Column Number** | **Column Name** | **Column Type** | **Description** | | 1 | Partner ID | string | The “partner id” is the ID that X provides to the Partner in order to uniquely identify each Partner. | | 2 | The user’s id in the partner system | string | The `p_user_id` is the unique ID that is used to identify the user by the Partner. The file containing these opt-out users should be uploaded using the [TON upload](/x-ads-api/audiences) endpoint and the path of uploaded data should be sent to the Global Opt Out endpoint here:[PUT accounts/:account\_id/custom\_audiences/global\_opt\_out](/x-ads-api/audiences). | **Sending Membership Updates** As specified in our endpoint documentation, when passing users via the [POST custom\_audience\_memberships](/x-ads-api/audiences) endpoint you should pass a customer ID to enable a cookie based match. Partners sending data with a `p_id` **must** set the `user_identifier_type` to `TALIST_PARTNER_USER_ID` or `TAWEB_PARTNER_USER_ID`. All other steps will remain the same as those listed in the [Real-Time Audience API Integration Guide](/x-ads-api/audiences) ### Custom Audiences User Data This document outlines the format for \[Custom Audience]/x-ads-api/audiences user data. **Data normalization** **Device IDs**: * IDFA - lower-cased with dashes; ex: `4b61639e-47cc-4056-a16a-c8217e029462` * AdID - original format on device is required, not capitalized with dashes; ex: `2f5f5391-3e45-4d02-b645-4575a08f86e` * Android id - original format on device is required, not capitalized without dashes or spaces; ex: `af3802a465767e36` **Email Addresses**: * lowercase, remove leading and trailing spaces; ex: `support@x.com` **X Usernames**: * no @, lowercased and leading and trailing spaces trimmed; ex: `jack` **X User IDs**: * Standard integer; ex: `143567` **Data hashing** The data for each line must be hashed using `SHA256`, without a salt.  Additionally, the final output hash must be in lower case. E.g., 49e0be2aeccfb51a8dee4c945c8a70a9ac500cf6f5cb08112575f74db9b1470d and \*\*not \*\*49E0BE2AECCFB51A8DEE4C945C8A70A9AC500CF6F5CB08112575F74DB9B1470D ``` # hasing user @AdsAPI using python import hashlib hashlib.sha256("adsapi".encode()).hexdigest() #output 49e0be2aeccfb51a8dee4c945c8a70a9ac500cf6f5cb08112575f74db9b1470d ``` Additional code samples for hashing can be found at [github.com/xdevplatform/ads-platform-tools](https://github.com/xdevplatform/ads-platform-tools). ### Custom Audiences: Web info.png **Information** Partners will send a list of IDs (`p_user_ids`) to target on behalf of an advertiser. This is accomplished through an ID Sync process that builds a mapping between the `p_user_ids` and X user ID. This mapping is then used to produce lists of X User IDs that can be used for Targeting. These custom audiences will be made available on the advertiser’s specific @handle specified by the label on ads.x.com Custom Audiences Web campaign setup. X will provide the secure pixel that can be dropped on partner tags and sites in order to match the IDs (`p_user_ids`) to X user IDs. Once the ID Sync process is complete, the targeting files will be created by the partner and will be made available to X through a HTTPS endpoint. These targeting files are ingested on a regular basis by X and are then made available in the X UI. **X Secure Pixel** The X secure pixel will look as follows: [https://analytics.x.com/i/adsct?p\\\_user\\\_id=xyz\&p\_id=123](https://analytics.x.com/i/adsct?p\\_user\\_id=xyz\&p_id=123) **`p_user_id`** - xyz represents the partner user ID that is provided by the Partner **`p_id`** - 123 represents the unique ID for the Partner (provided by X) **Partner HTTPS Endpoint & Targeting User File** Partner will need to provide X with a HTTPS endpoint and credentials (username/password) that can be used to ingest the targeting file on a regular basis. A sample HTTPS endpoint will look as follows: ``` https:///twitter/partner_targeting_%Y-%M-%D.tsv.gz ``` %Y - Format Code for Year (YYYY) %M - Format Code for Month (MM) %D - Format Code for Day (DD) The transmitted data will consist of the following files: 1. Partner Targeting User File 2. Targeting Conversion File All the files will be in the TSV format, where the individual fields of each row are separated from each other by a tab character. Valid field values themselves will never contain the tab character. **Allowed X IP Range:** Here is the range of IPs that can be allowed for access to the Partner Endpoint. * 199.16.156.0/22 * 199.59.148.0/22 **Partner Targeting User File:** | **Column Number** | **Column Name** | **Column Type** | **Description** | | :---------------- | :--------------- | :-------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | partner id | string | The “partner id” is the ID that X provides to the Partner in order to uniquely identify each Partner. | | 2 | advertiser id | string | The “advertiser id” is the @handle for the advertiser. | | 3 | p\_user\_id | string | The “p\_user\_id” is the unique ID that is used to identify the user by the Partner. | | 3 | confidence score | integer | The “confidence score” is optional. Our recommendation for confidence score is to use 0-100. If the use case is for retargeting, then a confidence score of “100” is a user who has been directly retargeted. Any score from 0-99 would correspond to the level of confidence of the look-alike. | | 4 | segment label | string | The “segment label” is optional. Partners can use “segment label” to specify product categories, for example. Our recommendation is to use this “segment label” as this is the human readable name for Custom Audiences in the ads.x.com UI. | **Notes:** Every time we receive a new Partner Targeting File, we expect this to be the full list of users that Partner recommends us to target, not incremental, unless otherwise agreed upon. We will agree with each partner what the frequency of delivery of this Partner Targeting File will be. If we do not receive a Partner Targeting File as expected, we will use the previous version with some pre-defined expiration time. ## Audience API Integration ### Overview The Audience API was launched as part of [v4](/x-ads-api/introduction) of the Ads API and with it, brings several improvements to the legacy Audiences endpoints. This new endpoint is backed by a new Audience processing backend, and brings several improvements in terms of stability, robustness and reliability. The purpose of this guide is to highlight the differences between the Audience API and the legacy Audience upload and management processes.  Reference documentation can be found on the [Audience API](/x-ads-api/audiences) reference documentation page.  **Note**: All Audience user data must be SHA-256 hashed prior to upload. More details, along with the accepted user identifier types and data normalization can be found on the [user data](/x-ads-api/audiences) page. **Changes to Audience Functionality** The following changes to Custom Audiences have been introduced as of v4 and any deprecated endpoints will no longer be available once v3 of the Ads API has been sunset: * **Deprecated** TON Upload: * GET accounts/:account\_id/custom\_audience\_changes * GET accounts/:account\_id/custom\_audience\_changes/:custom\_audience\_change\_id * POST accounts/:account\_id/custom\_audience\_changes * PUT accounts/:account\_id/custom\_audiences/global\_opt\_out * **Deprecated** Real Time Audiences: * POST custom\_audience\_memberships * Custom Audience: * The `list_type` parameter will be removed from the request and response on all [Custom Audience](/x-ads-api/audiences) endpoints. This parameter was previously used to identify the user identifier type of the Audience (i.e., email, X User ID, etc.) however Audiences now have the ability to accept multiple user identifiers for the same Audience thereby making this value irrelevant. * General: * The Audience lookback window has been updated to match against users active within the past 90 days (from 30 days) * The minimum number of matched users required for an audience to be targetable has been decreased to 100 users (from 500 users) **Prerequisites** * Ads API access * For access to the Audience endpoint, you will need to be added to an allowlist. Please fill this form and accept the new [X Ads Products and Services Agreement](/x-ads-api/introduction) if initially accepted prior to 2018-08-01 **Audience Upload Process** The following table lists the primary differences between the old and new Audience creation flows, with more details available further below: | Step in Process | Audience API | (Deprecated) TON Upload | | :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- | | Create a shell Audience | Can be created via the \[POST custom\_audience endpoint]/x-ads-api/audiences | Can be created via the \[POST custom\_audience endpoint]/x-ads-api/audiences | | Add a new user | Use the `operation_type` `Update` with the [Audience endpoint](/x-ads-api/audiences) | Use the `operation` `ADD` with the [POST custom\_audience\_changes](/x-ads-api/audiences) endpoint | | Remove a user | Use the `operation_type` `Delete` with the [Audience endpoint](/x-ads-api/audiences) | Use the `operation` `REMOVE` with the [POST custom\_audience\_changes](/x-ads-api/audiences) endpoint | | Opting-Out Users | Use the `operation_type` `Delete` with the [Audience endpoint](/x-ads-api/audiences) and the corresponding `custom_audience_id`s that the user is a part of | Use the [Global opt-out endpoint](/x-ads-api/audiences) | **Note** Any audiences being updated or opted-out via the TON Upload path must have a corresponding list uploaded via the [TON Upload](/x-ads-api/audiences) endpoint and associated with an Audience using the [custom\_audience\_changes](/x-ads-api/audiences) endpoint. **Rate Limiting** The Audience API endpoint has a rate limit of 1500/1min per account. There are no limits on the number of users that can be sent in a single payload. The only constraints on the payload are: 1\. Total number of operations: 2500 operations 2\. Maximum payload size: 5,000,000 bytes **Audience User Management** In order to create a new Audience, the following steps are required ### Create a new Custom Audience Create a new Custom Audience "shell" using the \[POST custom\_audience]/x-ads-api/audiences endpoint and retrieve the corresponding Custom Audience `id`. This step is required if creating an Audience from scratch. If updating an existing Audience, skip to the next section ### Add Users to an Audience Use the [POST accounts/:account\_id/custom\_audiences/:custom\_audience\_id/users](/x-ads-api/audiences) with the Custom Audience `id` and a sample payload like so: POST [https://ads-api.x.com/11/accounts/18ce54d4x5t/custom\_audiences/1nmth/users](https://ads-api.x.com/11/accounts/18ce54d4x5t/custom_audiences/1nmth/users) ``` # All values must be hashed, unhashed values are used in this example for illustrative purposes [ { "operation_type": "Update", "params": { "effective_at": "2018-05-15T00:00:00Z", "expires_at": "2019-01-01T07:00:00Z", "users": [ { "email": [ "abc@x.com" ], "handle": [ "x", "adsapi" ] }, { "email": [ "edf@x.com" ], "twitter_id": [ "121291606", "17874544" ] } ] } } ] ``` In order to add a user to an Audience, use the `operation_type` `Update`. The new Audience interface enables the ability to pass in multiple user keys for a single user. Each object in the array of JSON objects corresponds to a single user. Using the example payload above, the request will add two users to an Audience, one with an `email` and `handle` and another with an `email` and `twitter_id`.  #### Remove Users from an Audience Similar to the process outlined for adding users, users can be removed from an audience using like so: POST [https://ads-api.x.com/11/accounts/18ce54d4x5t/custom\_audiences/1nmth/users](https://ads-api.x.com/11/accounts/18ce54d4x5t/custom_audiences/1nmth/users) ``` # All values must be hashed, unhashed values are used in this example for illustrative purposes [ { "operation_type": "Delete", "params": { "effective_at": "2018-05-15T00:00:00Z", "expires_at": "2019-01-01T07:00:00Z", "users": [ { "email": [ "abc@x.com" ], "twitter_id": [ "783214", "1225933934" ] }, { "email": [ "edf@x.com" ], "twitter_id": [ "121291606", "17874544" ] } ] } } ] ``` The `operation_type` must be set to `Delete` and users will be matched on any keys that were present when adding users to the audience. For example, if a user was added to an audience using an `email` and `twitter_id`, then the same user can be removed using any one of these keys, i.e., either `email` or `twitter_id` or both. Additionally, it is possible to add and remove users from an Audience within the same request. The endpoint supports multiple `operation_type` per request. #### Opt-Out Users With the deprecation of the global opt-out endpoint, partners are required to `Delete` any users that have opted-out of any Audiences. There are a few ways to achieve this: 1. Keep track of which users are part of which Audiences and remove these users individually from each Audience. 2. Remove the user from **all** Audiences associated with an Ads account. **General Best Practices** * We strongly recommend calling this endpoint in near real-time batches to avoid spiky queues which take longer to process and in general cause unnecessary load on our system. This also ensures users are available for campaign targeting sooner. * A successful API call will return a `success_count` and `total_count` corresponding to the number of `user` objects that have been received in the request. * This endpoint is atomic in nature, that is, either the entire request is successful or in case of any errors then the entire request will fail. In case of an error response, consumers of the API are recommended to fix the error and retry the request with the entire payload.  * Upon failure, partners recommended to use an [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) approach with retries. For example, retry immediately upon the first failure, retry after 1 minute after the second failure and retry after 5 minutes after the third consecutive failure, and so on ## API Reference ### Keyword Insights #### GET insights/keywords/search[](#get-insights-keywords-search "Permalink to this headline") Given a group of keywords, get the associated Tweet volume as well as a set of 30 related keywords. The Tweet volume corresponds to the input keywords only, not the related keywords. A maximum time range (`end_time` - `start_time`) of 7 days is allowed. Please note that results are scoped by a single geo (country). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/insights/keywords/search` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | granularity
*required* | Specifies the granularity of the data returned for the time range denoted by `start_time` and `end_time`. For instance, when set to `HOUR`, you will be presented with a datapoint for each hour between `start_time` and `end_time`. request.

Type: enum

Possible values: `DAY`, `HOUR` | | keywords
*required* | A comma-separated string of keywords to narrow search by. All keywords are OR'ed with one another.

**Note**: A maximum of 10 keywords (`keywords` and `negative_keywords` combined) may be used.

Type: string

Example: `developers` | | start\_time
*required* | Scopes the retrieved data to data collected in the window of time between `start_time` and `end_time`. Expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

Type: string

Example: `2017-07-10T00:00:00Z` | | end\_time
*optional* | Scopes the retrieved data to data collected in the window of time between `start_time` and `end_time`. Expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Defaults to the current time.

Type: string

Example: `2017-07-11T00:00:00Z` | | location
*optional* | A targeting value you would get from the [GET targeting\_criteria/locations](/x-ads-api/campaign-management#get-targeting-criteria-locations) endpoint to narrow results in terms of where the user of the account is located. Note that at present only country level locations are supported.

Type: string

Example: `0ce8b9a7b2742f7e` | | negative\_keywords
*optional* | A comma-separated string of keywords to exclude. All negative keywords are OR'ed with one another.

**Note**: A maximum of 10 keywords (`keywords` and `negative_keywords` combined) may be used.

Type: string

Example: `rain` | **Example Request[](#example-request "Permalink to this headline")** ```json theme={null} GET https://ads-api.x.com/12/insights/keywords/search?end_time=2018-02-02&granularity=DAY&keywords=developers&start_time=2018-02-01 ``` **Example Response[](#example-response "Permalink to this headline")**\* ``` { "request": { "params": { "start_time": "2018-02-01T00:00:00Z", "end_time": "2018-02-02T00:00:00Z", "granularity": "DAY", "keywords": [ "developers" ] } }, "data": { "related_keywords": [ "dev", "developer", "coders", "mysql", "devs", "#technology", "#developers", "security", "programmers", "#tech", "javascript", "#iot", "#bigdata", "cloud", "devops", "php", "developer", "programmer", "engineer", "big data", "agile", "app", "programming", "ios", "maker", "startups", "developer's", "java", "#devops", "startup" ], "tweet_volume": [ 15707 ] } } ``` ### Tailored Audience Permissions #### GET accounts/:account\_id/tailored\_audiences/:tailored\_audience\_id/permissions[](#get-accounts-account-id-tailored-audiences-tailored-audience-id-permissions "Permalink to this headline") Retrieve details for some or all permissions associated with the specified tailored audience. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/5/accounts/:account_id/tailored_audiences/:tailored_audience_id/permissions` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | tailored\_audience\_id
*required* | A reference to the tailored audience you are operating with in the request.

Type: string

Example: `1nmth` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | granted\_account\_ids
*optional* | Scope the response to just the desired accounts by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `18ce54aymz3` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | tailored\_audience\_permission\_ids
*optional* | Scope the response to just the desired tailored audience permissions by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `ri` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/5/accounts/18ce54d4x5t/tailored_audiences/1nmth/permissions` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54d4x5t", "tailored_audience_id": "1nmth" } }, "next_cursor": null, "data": [ { "tailored_audience_id": "1nmth", "permission_level": "READ_ONLY", "id": "ri", "created_at": "2017-06-08T23:17:59Z", "granted_account_id": "18ce54aymz3", "updated_at": "2017-06-08T23:17:59Z", "deleted": false } ] } ``` #### POST accounts/:account\_id/tailored\_audiences/:tailored\_audience\_id/permissions[](#post-accounts-account-id-tailored-audiences-tailored-audience-id-permissions "Permalink to this headline") Create a new permission object allowing the specified audience to be shared with a given account. **Note**: Creating or modifying permissions for a tailored audience requires that the audience be owned by the account attempting to modify permissions. You can check the ownership of a tailored audience by looking at the `is_owner` response attribute in the response for a given audience. **Note**: Audiences can only be shared between ads accounts under the same business or if the ads account that owns the audience has the `SHARE_AUDIENCE_OUTSIDE_BUSINESS` account feature. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/5/accounts/:account_id/tailored_audiences/:tailored_audience_id/permissions` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :--------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | granted\_account\_id
*required* | The account you wish to grant the tailored audience permissions for.

Type: string

Example: `18ce54aymz3` | | permission\_level
*required* | The type of access to the tailored audience that the `granted_account_id` should have.

Type: enum

Possible values: `READ_ONLY`, `READ_WRITE` | | tailored\_audience\_id
*required* | A reference to the tailored audience you are operating with in the request.

Type: string

Example: `2906h` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/5/accounts/18ce54d4x5t/tailored_audiences/2906h/permissions?granted_account_id=18ce54aymz3&permission_level=READ_ONLY` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54d4x5t", "granted_account_id": "18ce54aymz3", "permission_level": "READ_ONLY", "tailored_audience_id": "2906h" } }, "data": { "tailored_audience_id": "2906h", "permission_level": "READ_ONLY", "id": "14m", "created_at": "2017-09-12T23:49:34Z", "granted_account_id": "18ce54aymz3", "updated_at": "2017-09-12T23:49:34Z", "deleted": false } } ``` #### DELETE accounts/:account\_id/tailored\_audiences/:tailored\_audience\_id/permissions/:tailored\_audience\_permission\_id[](#delete-accounts-account-id-tailored-audiences-tailored-audience-id-permissions-tailored-audience-permission-id "Permalink to this headline") Revoke the specified Tailored Audience sharing permission. **Note**: Creating or modifying permissions for a tailored audience requires that the audience be owned by the account attempting to modify permissions. You can check the ownership of a tailored audience by looking at the `is_owner` response attribute in the response for a given audience. When revoked, we guarantee that the granted account (`granted_account_id`) will not be able to target the audience in future campaigns. Existing campaigns will continue to run with the shared audiences; campaigns do not stop and the audience does not get removed from the campaign. It is not possible to copy this campaign after the audience sharing permission has been revoked. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/5/accounts/:account_id/tailored_audiences/:tailored_audience_id/permissions/:tailored_audience_permission_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :--------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | tailored\_audience\_id
*required* | A reference to the tailored audience you are operating with in the request.

Type: string

Example: `1nmth` | | tailored\_audience\_permission\_id
*required* | A reference to the tailored audience permission you are operating with in the request.

Type: string

Example: `ri` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/5/accounts/18ce54d4x5t/tailored_audiences/1nmth/permissions/ri` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54d4x5t", "tailored_audience_permission_id": "ri", "tailored_audience_id": "1nmth" } }, "data": { "tailored_audience_id": "1nmth", "permission_level": "READ_ONLY", "id": "ri", "created_at": "2017-06-08T23:17:59Z", "granted_account_id": "18ce54aymz3", "updated_at": "2017-08-30T18:29:35Z", "deleted": true } } ``` ### Targeted Audiences #### GET accounts/:account\_id/custom\_audiences/:custom\_audience\_id/targeted[](#get-accounts-account-id-custom-audiences-custom-audience-id-targeted "Permalink to this headline") Retrieve a list of active or all line items and campaigns that target a given `custom_audience_id` | Name | Description | | :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | custom\_audience\_id
*required* | A reference to the custom audience you are operating with in the request.

Type: string

Example: `2906h` | | with\_active
*optional* | When `false`, includes line items that have `servable=false` status

Type: boolean

Default: `true`
Possible values: `true`, `false` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/2906h/targeted` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54d4x5t", "custom_audience_id": "2906h", } }, "next_cursor": null, "data": [ { "campaign_id": "59hod", "campaign_name": "test-campaign", "line_items": [ { "id": "5gzog", "name": "test-line-item", "servable": true } ] }, { "campaign_id": "arja7", "campaign_name": "Untitled campaign", "line_items": [ { "id": "bjw1q", "name": null, "servable": true } ] } ] } ``` ### Custom Audiences Users #### POST accounts/:account\_id/custom\_audiences/:custom\_audience\_id/users[](#post-accounts-account-id-custom-audiences-custom-audience-id-users "Permalink to this headline") This endpoint will allow partners to add, update and remove users from a given `custom_audience_id`. The endpoint will also accept multiple user identifier types per user as well. All data being provided in the `users` field of the request **except** `partner_user_id` must be hashed using `SHA256` and [normalized](/x-ads-api/audiences#twitter-user-id-normalization). **Batch Requests** * The current maximum batch size is `2500` **for this endpoint**. The batch size is determined by the number of operations (`Update`/`Delete`) per request. For example, over 2500 operation objects (`{"operation_type": "Update/Delete", [..] }`) in one array result in an error. * The max request POST body size this endpoint can accept is `5,000,000` bytes. * The rate limits for this endpoint are 1500 per 1 minute window * All parameters are sent in the request body and a `Content-Type` of `application/json` is required. * Batch requests fail or succeed together as a group and all API responses for both error and success preserve the item order of the initial request. **Batch Responses** The response returned by the Ads API contains two fields, a `success_count` and a `total_count`. These values must always be equal, and they are a count of the number of records in the request that have been processed by the backend. A situation where the number of records sent in the request body is **not** equal the `success_count` and `total_count` should be treated as an error condition, requiring a retry. **Batch Errors** * Request-level errors (eg. max batch size exceeded) are shown in the response under the `errors` object. * Item-level errors (eg. missing required parameters) are show in the response under the `operation_errors` object. * The index of the error in the `operation_errors` refers to the index in the input item, with the corresponding error message #### Resource URL[](#resource-url "Permalink to this headline") `https://ads-api.x.com/12/accounts/:account_id/custom_audiences/:custom_audience_id/users` #### Parameters[](#parameters "Permalink to this headline") | Name | Description | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | operation\_type
*required* | The per `users` group operation type being performed.

Type: enum

Possible values: `Update`, `Delete` | | params
*required* | A JSON object containing the `users` array, the `effective_at` and `expires_at` timestamps.

Type: JSON | | users
*required* | An array of JSON objects containing all params for an individual user.

Type: JSON | | effective\_at
*optional* | The UTC time at which the custom audience association(s) should take effect. Expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). Defaults to the current date and time.

Type: string

Example: `2017-07-05T07:00:00Z` | | expires\_at
*optional* | The UTC time at which the custom audience association(s) should expire. The specified time must be later than the value of `effective_at`. Expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). Defaults to 13 months from the request timestamp.

Type: string

Example: `2017-07-05T07:00:00Z` | Given the mutil-key approach to the `users` object, each element of this object is documented below: | Name | Description | | :---------------------------------- | :--------------------------------------------------------------------- | | email
*optional* | Email address(es) for the user.

Type: Array\[String] | | device\_id
*optional* | IDFA/AdID/Android ID

Type: Array\[String] | | handle
*optional* | The @handle(s) belonging to the user

Type: Array\[String] | | twitter\_id
*optional* | The X ID belonging to the user

Type: Array\[String] | | phone\_number
*optional* | Phone number(s) for the user

Type: Array\[String] | | partner\_user\_id
*optional* | The user's ID in the partners' system.

Type: Array\[String] | #### Example Request[](#example-request "Permalink to this headline") `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/1nmth/users` ``` [ { "operation_type": "Update", "params": { "effective_at": "2018-05-15T00:00:00Z", "expires_at": "2019-01-01T07:00:00Z", "users": [ { "email": [ "4798b8bbdcf6f2a52e527f46a3d7a7c9aefb541afda03af79c74809ecc6376f3" ], "handle": [ "7352f353c460e74c7ae226952d04f8aa307b12329c5512ec8cb6f1a0f8f9b2cb", "49e0be2aeccfb51a8dee4c945c8a70a9ac500cf6f5cb08112575f74db9b1470d" ] }, { "email": [ "5bf13d5ad4200407c5bc8b9bb578e425d05ef936fd488e3799a9d0806669223c" ], "twitter_id": [ "34d56c7159a7eea941f359653029410f813f65a1d2d13ecc5ccbdd5a8cb755cf", "00e7b76c9739dec57f4c4a20ec021a20ffcf26bd00f519b17ea00f0ed6048f85" ] } ] } }, { "operation_type": "Delete", "params": { "effective_at": "2018-05-15T00:00:00Z", "expires_at": "2019-01-01T07:00:00Z", "users": [ { "device_id": [ "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92" ], "email": [ "4798b8bbdcf6f2a52e527f46a3d7a7c9aefb541afda03af79c74809ecc6376f3" ], "handle": [ "461222f5dd690a20651c3d19848015cb0369db3f8e937571ffb775de70750847" ], "twitter_id": [ "c623c7e163984493b46c547088542e95d0aaa529bc52bbecce3ff91eb6b7843b" ] }, { "email": [ "5bf13d5ad4200407c5bc8b9bb578e425d05ef936fd488e3799a9d0806669223c" ], "twitter_id": [ "858cdc7f313f84a3f3c48e9a6323307c1ef1bb7439b8e3623e140454b0fd8fa5", "bb074e154657b91d99bd1bb3757409149670e8ae7a0fe9136fae29a26a7881c8" ] } ] } } ] ``` #### Example Response[](#example-response "Permalink to this headline") ``` { "request": { "params": { "account_id": "18ce54d4x5t", "custom_audience_id": "1nmth" } }, "data": { "success_count": 4, "total_count": 4 } } ``` ### Custom Audience Permissions #### GET accounts/:account\_id/custom\_audiences/:custom\_audience\_id/permissions[](#get-accounts-account-id-custom-audiences-custom-audience-id-permissions "Permalink to this headline") Retrieve details for some or all permissions associated with the specified custom audience. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences/:custom_audience_id/permissions` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | custom\_audience\_id
*required* | A reference to the custom audience you are operating with in the request.

Type: string

Example: `1nmth` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | granted\_account\_ids
*optional* | Scope the response to just the desired accounts by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `18ce54aymz3` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/fundamentals/sorting) for more information.

Type: string

Example: `created_at-asc` | | custom\_audience\_permission\_ids
*optional* | Scope the response to just the desired custom audience permissions by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `ri` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/1nmth/permissions` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54d4x5t", "custom_audience_id": "1nmth" } }, "next_cursor": null, "data": [ { "custom_audience_id": "1nmth", "permission_level": "READ_ONLY", "id": "ri", "created_at": "2017-06-08T23:17:59Z", "granted_account_id": "18ce54aymz3", "updated_at": "2017-06-08T23:17:59Z", "deleted": false } ] } ``` #### POST accounts/:account\_id/custom\_audiences/:custom\_audience\_id/permissions[](#post-accounts-account-id-custom-audiences-custom-audience-id-permissions "Permalink to this headline") Create a new permission object allowing the specified audience to be shared with a given account. **Note**: Creating or modifying permissions for a custom audience requires that the audience be owned by the account attempting to modify permissions. You can check the ownership of a custom audience by looking at the `is_owner` response attribute in the response for a given audience. **Note**: Audiences can only be shared between ads accounts under the same business or if the ads account that owns the audience has the `SHARE_AUDIENCE_OUTSIDE_BUSINESS` account feature. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences/:custom_audience_id/permissions` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | granted\_account\_id
*required* | The account you wish to grant the custom audience permissions for.

Type: string

Example: `18ce54aymz3` | | permission\_level
*required* | The type of access to the custom audience that the `granted_account_id` should have.

Type: enum

Possible values: `READ_ONLY`, `READ_WRITE` | | custom\_audience\_id
*required* | A reference to the custom audience you are operating with in the request.

Type: string

Example: `2906h` | **Example Request[](#example-request "Permalink to this headline")** ``` POST https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/2906h/permissions?granted_account_id=18ce54aymz3&permission_level=READ_ONLY ``` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54d4x5t", "granted_account_id": "18ce54aymz3", "permission_level": "READ_ONLY", "custom_audience_id": "2906h" } }, "data": { "custom_audience_id": "2906h", "permission_level": "READ_ONLY", "id": "14m", "created_at": "2017-09-12T23:49:34Z", "granted_account_id": "18ce54aymz3", "updated_at": "2017-09-12T23:49:34Z", "deleted": false } } ``` #### DELETE accounts/:account\_id/custom\_audiences/:custom\_audience\_id/permissions/:custom\_audience\_permission\_id[](#delete-accounts-account-id-custom-audiences-custom-audience-id-permissions-custom-audience-permission-id "Permalink to this headline") Revoke the specified Custom Audience sharing permission. **Note**: Creating or modifying permissions for a custom audience requires that the audience be owned by the account attempting to modify permissions. You can check the ownership of a custom audience by looking at the `is_owner` response attribute in the response for a given audience. When revoked, we guarantee that the granted account (`granted_account_id`) will not be able to target the audience in future campaigns. Existing campaigns will continue to run with the shared audiences; campaigns do not stop and the audience does not get removed from the campaign. It is not possible to copy this campaign after the audience sharing permission has been revoked. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences/:custom_audience_id/permissions/:custom_audience_permission_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | custom\_audience\_id
*required* | A reference to the custom audience you are operating with in the request.

Type: string

Example: `1nmth` | | custom\_audience\_permission\_id
*required* | A reference to the custom audience permission you are operating with in the request.

Type: string

Example: `ri` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/1nmth/permissions/ri` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54d4x5t", "custom_audience_permission_id": "ri", "custom_audience_id": "1nmth" } }, "data": { "custom_audience_id": "1nmth", "permission_level": "READ_ONLY", "id": "ri", "created_at": "2017-06-08T23:17:59Z", "granted_account_id": "18ce54aymz3", "updated_at": "2017-08-30T18:29:35Z", "deleted": true } } ``` ### Custom Audiences #### GET accounts/:account\_id/custom\_audiences[](#get-accounts-account-id-custom-audiences "Permalink to this headline") Retrieve details for some or all Custom Audiences associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | permission\_scope
*optional* | Allows filtering the response to lists you own or lists that have been shared with you. By default, without specifying this parameter you will only see audiences you own.

Type: enum

Default: `OWNER`
Possible values: `OWNER`, `SHARED` | | q
*optional* | An optional query to scope resource by `name`.

**Note**: This performs case-insensitive prefix matching.

Type: string

Min, Max length: `1`, `255` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/fundamentals/sorting) for more information.

Type: string

Example: `created_at-asc` | | custom\_audience\_ids
*optional* | Scope the response to just the desired custom audiences by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `1nmth` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences?custom_audience_ids=1nmth` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "custom_audience_ids": [ "1nmth" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "targetable": true, "name": "twurl-using-subshell-for-file", "targetable_types": [ "CRM", "EXCLUDED_CRM" ], "audience_type": "CRM", "description": null, "permission_level": "READ_WRITE", "owner_account_id": "18ce54d4x5t", "id": "1nmth", "reasons_not_targetable": [], "created_at": "2017-01-08T08:19:58Z", "updated_at": "2017-01-08T16:21:13Z", "partner_source": "OTHER", "deleted": false, "audience_size": 1470 } ] } ``` #### GET accounts/:account\_id/custom\_audiences/:custom\_audience\_id[](#get-accounts-account-id-custom-audiences-custom-audience-id "Permalink to this headline") Retrieve specific Custom Audiences associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences/:custom_audience_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | custom\_audience\_id
*required* | A reference to the custom audience you are operating with in the request.

Type: string

Example: `2906h` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/2906h` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "custom_audience_id": "2906h", "account_id": "18ce54d4x5t" } }, "data": { "targetable": false, "name": "developers", "targetable_types": [ "CRM", "EXCLUDED_CRM" ], "audience_type": "CRM", "description": null, "permission_level": "READ_WRITE", "owner_account_id": "18ce54d4x5t", "id": "2906h", "reasons_not_targetable": [], "created_at": "2017-08-22T23:34:26Z", "updated_at": "2017-08-22T23:34:26Z", "partner_source": "OTHER", "deleted": false, "audience_size": 140321 } } ``` #### POST accounts/:account\_id/custom\_audiences[](#post-accounts-account-id-custom-audiences "Permalink to this headline") Create a new placeholder Custom Audience associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | name
*required* | The display name for this audience. Unique name value must be used. Failure to do so will result in an error.

Type: string

Example: `ads api users` | | description
*optional* | A description for this audience.

Type: string

Example: `Collection of all users of the Ads API` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences?name=developers` **Example Response[](#example-response "Permalink to this headline")** ``` { "data": { "targetable": false, "name": "developers", "targetable_types": [ "CRM", "EXCLUDED_CRM" ], "audience_type": "CRM", "description": null, "permission_level": "READ_WRITE", "owner_account_id": "18ce54d4x5t", "id": "2906h", "reasons_not_targetable": [ "PROCESSING", "TOO_SMALL" ], "created_at": "2017-08-22T23:34:26Z", "updated_at": "2017-08-22T23:34:26Z", "partner_source": "OTHER", "deleted": false, "audience_size": null }, "request": { "params": { "account_id": "18ce54d4x5t", "name": "developers" } } } ``` #### PUT accounts/:account\_id/custom\_audiences/:custom\_audience\_id[](#put-accounts-account-id-custom-audiences-custom-audience-id "Permalink to this headline") Update the specfic Custom Audience associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences/:custom_audience_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | custom\_audience\_id
*required* | A reference to the Custom Audience you are operating with in the request

Type: string

Example: `2906h` | | name
*optional* | The display name for this audience. Unique name value must be used. Failure to do so will result in an error.

Type: string

Example: `ads api users` | | description
*optional* | A description for this audience.

Type: string

Example: `Collection of all users of the Ads API` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/2906h?name=developers_changed` **Example Response[](#example-response "Permalink to this headline")** ``` { "data": { "targetable": false, "name": "developers_changed", "targetable_types": [ "CRM", "EXCLUDED_CRM" ], "audience_type": "CRM", "description": null, "permission_level": "READ_WRITE", "is_owner": true, "id": "2906h", "reasons_not_targetable": [ "PROCESSING", "TOO_SMALL" ], "created_at": "2017-08-22T23:34:26Z", "updated_at": "2017-08-22T23:34:26Z", "partner_source": "OTHER", "deleted": false, "audience_size": null }, "request": { "params": { "account_id": "18ce54d4x5t", "name": "developers_changed" } } } ``` #### POST batch/accounts/:account\_id/custom\_audiences[](#post-batch-accounts-account-id-custom-audiences "Permalink to this headline") Allows for batch creation of Custom Audiences. See the [Custom Audiences Overview](/x-ads-api/audiences) page for information on audiences. **Note:** This batch endpoint is currently in **closed beta** and available to select advertisers. During this beta period, only Flexible Audiences based on mobile custom audiences can be created. **Batch Requests** * The current maximum batch size is 10. * All parameters are sent in the request body and a `Content-Type` of `application/json` is required. * Batch requests fail or succeed together as a group and all API responses for both error and success preserve the item order of the initial request. **Batch Responses** Batch API responses return an ordered collection of items. Otherwise, they are identical in structure to their corresponding single-item endpoints. **Batch Errors** * Request-level errors (eg. max batch size exceeded) are shown in the response under the `errors` object. * Item-level errors (eg. missing required parameter) are shown in the response under the `operation_errors` object. **Flexible Audiences** * Flexible Audiences are immutable once created. * Custom Audiences are passed in a tree structure with boolean logic combinations to create Flexible Audiences * A maximum of 10 Custom Audiences leaf nodes can be used to create a Flexible Audience. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/batch/accounts/:account_id/custom_audiences` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | audience\_type
*required* | The type of audience to create.

Type: enum

Possible values: `FLEXIBLE`, `MOBILE_AUDIENCE` | | child\_segments
*required* | An array containing objects which define the subset of a Custom Audience's members that you would like to target. Each object should contain a `custom_audience_id`, `frequency`, `frequency_comparator`, `lookback_window`, `negate`, and, in some cases, additional `child_segments`.

Type: array | | name
*required* | The display name for the audience. Unique name value must be used. Failure to do so will result in an error.

Type: string

Example: `my_flexible_audience_name` | | operation\_type
*required* | The per item operation type being performed.

Type: enum

Possible values: `Create`, `Update`, `Delete` | | boolean\_operator
*sometimes required* | The logical relationship between the child segments in its parent (containing) object. Required if child\_segments is non-empty for the parent object.

Type: enum

Possible values: `AND`, `OR` | | lookback\_window
*sometimes required* | An integer value specifying the range of days within which the user has taken the specific action and qualified for the given custom audience.

Type: int

Possible values: `1`, `7`, `14`, `30` | | segments
*sometimes required* | An object containing a `boolean_operator` and `child_segments` which define the subset of a Custom Audience's members that you would like to target.

Type: object | | custom\_audience\_id
*sometimes required* | The id of the custom audience to use as a child segment.

Type: string

Example: `tyfo` | | frequency
*optional* | An integer value specifying the frequency within the lookback window that the user has taken the specific action and qualified for the given custom audience.

Type: int

Default value: `1` | | frequency\_comparator
*optional* | The comparator to the `frequency` passed in the request.

**Note**: In the values below, `GTE` refers to greater than or equal, `LT` to less than, and so on.

Type: string

Possible values: `NUM_GTE`, `NUM_GT`, `NUM_EQ`, `NUM_LTE`, `NUM_LT` Default value: `NUM_GTE` | | negate
*optional* | Negates the segment and thus is excluded in the combination.

Type: boolean

Default value: `true`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/batch/accounts/18ce54d4x5t/custom_audiences` ``` [ { "operation_type":"Create", "params":{ "name":"my_flexible_audience_name", "audience_type":"FLEXIBLE", "segments":{ "boolean_operator":"AND", "child_segments":[ { "custom_audience_id":"TYIF", "frequency":1, "frequency_comparator":"NUM_GT", "lookback_window":30, "negate":true, "child_segments":[ ] }, { "boolean_operator":"OR", "child_segments":[ { "custom_audience_id":"TXR1", "lookback_window":30, "child_segments":[ ] }, { "custom_audience_id":"TYFO", "frequency":1, "frequency_comparator":"NUM_GT", "lookback_window":30, "negate":true, "child_segments":[ ] } ] } ] } } } ] ``` **Example Response[](#example-response "Permalink to this headline")** ``` { "data": { "targetable": false, "name": "my_flexible_audience_name", "targetable_types": [ "FLEXIBLE", "EXCLUDED_FLEXIBLE" ], "audience_type": "FLEXIBLE", "id": "13ld7", "reasons_not_targetable": [ "PROCESSING", "TOO_SMALL" ], "metadata": [ { "custom_audience_id": "13ld7", "account_id": "qsx3w2", "name": "my_flexible_audience_name", "audience_source": "FLEXIBLE_AUDIENCE", "upload_status": "UPLOADED", "segments": { "boolean_operator": "AND", "frequency": 1, "frequency_comparator": "NUM_GTE", "negate": false, "child_segments": [ { "custom_audience_id": "tyif", "lookback_window": 30, "frequency": 1, "frequency_comparator": "NUM_GT", "negate": true, "child_segments": [ ] }, { "boolean_operator": "OR", "frequency": 1, "frequency_comparator": "NUM_GTE", "negate": false, "child_segments": [ { "custom_audience_id": "txr1", "lookback_window": 30, "frequency": 1, "frequency_comparator": "NUM_GTE", "negate": false, "child_segments": [ ] }, { "custom_audience_id": "tyfo", "lookback_window": 30, "frequency": 1, "frequency_comparator": "NUM_GT", "negate": true, "child_segments": [ ] } ] } ] } } ], "created_at": "2015-11-10T21:26:43Z", "updated_at": "2015-11-11T01:11:47Z", "partner_source": "OTHER", "deleted": false, "audience_size": null }, "request": [ { "params": { "name": "my_flexible_audience_name", "audience_type": "FLEXIBLE", "segments": { "boolean_operator": "AND", "child_segments": [ { "custom_audience_id": "TYIF", "lookback_window": 30, "frequency": 1, "frequency_comparator": "NUM_GT", "negate": true, "child_segments": [ ] }, { "boolean_operator": "OR", "child_segments": [ { "custom_audience_id": "TXR1", "lookback_window": 30, "child_segments": [ ] }, { "custom_audience_id": "TYFO", "lookback_window": 30, "frequency": 1, "frequency_comparator": "NUM_GT", "negate": true, "child_segments": [ ] } ] } ] }, "account_id": "qsx3w2" }, "operation_type": "Create" } ] } ``` #### DELETE accounts/:account\_id/custom\_audiences/:custom\_audience\_id[](#delete-accounts-account-id-custom-audiences-custom-audience-id "Permalink to this headline") Delete the specified Custom Audience belonging to the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/custom_audiences/:custom_audience_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | custom\_audience\_id
*required* | A reference to the custom audience you are operating with in the request.

Type: string

Example: `2906h` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/custom_audiences/2906h` **Example Response[](#example-response "Permalink to this headline")** ``` { "data": { "targetable": false, "name": "developers", "targetable_types": [ "CRM", "EXCLUDED_CRM" ], "audience_type": "CRM", "description": null, "permission_level": "READ_WRITE", "owner_account_id": "18ce54d4x5t", "id": "2906h", "reasons_not_targetable": [ "TOO_SMALL" ], "created_at": "2017-08-22T23:34:26Z", "updated_at": "2017-08-30T18:09:00Z", "partner_source": "OTHER", "deleted": true, "audience_size": null }, "request": { "params": { "custom_audience_id": "2906h", "account_id": "18ce54d4x5t" } } } ``` ### Do Not Reach Lists #### GET accounts/:account\_id/do\_not\_reach\_lists[](#get-accounts-account-id-do-not-reach-lists "Permalink to this headline") Retrieve details for some or all Do Not Reach List associated with the current account. **Note**: An `account_id` can only have at most one Do Not Reach List **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/do_not_reach_lists` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54bgxky/do_not_reach_lists` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "account_id": "18ce54bgxky" } }, "next_cursor": null, "data": [ { "targetable": false, "name": "Do Not Reach List", "description": "test DNRL", "id": "4kzrq", "reasons_not_targetable": [ "TOO_SMALL" ], "created_at": "2021-10-28T22:09:29Z", "list_size": null, "updated_at": "2021-11-04T03:33:06Z", "deleted": false } ] } ``` #### POST accounts/:account\_id/do\_not\_reach\_lists[](#post-accounts-account-id-do-not-reach-lists "Permalink to this headline") Create a new Do Not Reach List associated with the current account. **Note**: An `account_id` can only have at most one Do Not Reach List **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/do_not_reach_lists` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | description
*optional* | A description for this audience.

Type: string

Example: `A list of users to exclude` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54bgxky/do_not_reach_lists?description=A list of users to exclude` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "description": "A list of users to exclude", "account_id": "18ce54bgxky" } }, "data": { "targetable": false, "name": "Do Not Reach List", "description": "A list of users to exclude", "id": "4ofrq", "reasons_not_targetable": [ "PROCESSING", "TOO_SMALL" ], "created_at": "2022-02-08T23:02:48Z", "list_size": null, "updated_at": "2022-02-08T23:02:48Z", "deleted": false } } ``` #### POST batch/accounts/:account\_id/do\_not\_reach\_lists/:do\_not\_reach\_list\_id/users[](#post-batch-accounts-account-id-do-not-reach-lists-do-not-reach-list-id-users "Permalink to this headline") This endpoint allows users to be added, updated and removed from a given `do_not_reach_list_id`. This endpoint only accepts emails as the valid user identifier type. All data being provided in the `emails` field of the request must be hashed using `SHA256` and [normalized](/x-ads-api/audiences#e-mail-normalization). **Notes** * An `account_id` can only have at most one Do Not Reach List * Users added to this list **must** have an `expires_at` timestamp set to less than 13 months from the current timestamp * Do Not Reach List API does not accept an `effective_at` timestamp and defaults to the current timestamp * Do Not Reach List does not remove users from any or all custom audiences in the account but acts as exclusion targeting for all campaigns served for the account **Batch Requests** * The current maximum batch size is `2500` **for this endpoint**. The batch size is determined by the number of operations (`Update`/`Delete`) per request. For example, over 2500 operation objects (`{"operation_type": "Update/Delete", [..] }`) in one array result in an error. * The max request POST body size this endpoint can accept is `5,000,000` bytes. * The rate limits for this endpoint are 1500 per 1 minute window * All parameters are sent in the request body and a `Content-Type` of `application/json` is required. * Batch requests fail or succeed together as a group and all API responses for both error and success preserve the item order of the initial request. **Batch Responses** The response returned by the Ads API contains two fields, a `success_count` and a `total_count`. These values must always be equal, and they are a count of the number of records in the request that have been processed by the backend. A situation where the number of records sent in the request body is **not** equal the `success_count` and `total_count` should be treated as an error condition, requiring a retry. **Batch Errors** * Request-level errors (eg. max batch size exceeded) are shown in the response under the `errors` object. * Item-level errors (eg. missing required parameters) are show in the response under the `operation_errors` object. * The index of the error in the `operation_errors` refers to the index in the input item, with the corresponding error message **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/batch/accounts/:account_id/do_not_reach_lists/:do_not_reach_list_id/users` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | do\_not\_reach\_list\_id
*required* | A reference to the Do Not Reach List you are operating with in the request

Type: string

Example: `2906h` | | operation\_type
*required* | The per `users` group operation type being performed.

Type: enum

Possible values: `Update`, `Delete` | | params
*required* | A JSON object containing the `emails` array and `expires_at` timestamp.

Type: JSON | | users
*required* | An array of JSON objects containing all params for an individual user.

Type: JSON | | expires\_at
*optional* | The UTC time at which the user association(s) should expire. The specified time must be later than the value of the current timestamp. Expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). Defaults to 13 months from current timestamp.

Type: string

Example: `2017-07-05T07:00:00Z` | Given the mutil-key approach to the `users` object, each element of this object is documented below: | Name | Description | | :------------------------------ | :-------------------------------------------------------------- | | email
*optional* | Email address(es) for the user.

Type: Array\[String] | | phone\_number
*optional* | Phone number(s) for the user

Type: Array\[String] | **Example Request[](#example-request "Permalink to this headline")** ``` `POST https://ads-api.x.com/12/batch/accounts/18ce54bgxky/do_not_reach_lists/4kzro/users` [ { "operation_type": "Update", "params": { "expires_at": "2023-01-22T00:00:00Z", "users": [ { "email": [ "FEAD76F6ADF99FFFB997AA4E3C8AD38FF531BC4C956DBD03CD0163F744D8AABC" ], "phone_number": [ "CCABF1B62A202E0FE28BC6C014983C89A65451DD4482BD66A0ADB65366F38A9A" ] }, { "email": [ "FEAD76F6ADF99FFFB997AA4E3C8AD38FF531BC4C956DBD03CD0163F744D8AABA" ], "phone_number": [ "CCABF1B62A202E0FE28BC6C014983C89A65451DD4482BD66A0ADB65366F38A9E" ] } ] } } ] ``` **Example Response[](#example-response "Permalink to this headline")** ``` { "data": [ { "success_count": 2, "total_count": 2 } ], "request": [ { "params": { "do_not_reach_list_id": "4ofrq", "expires_at": "2023-01-22T00:00:00Z", "account_id": "18ce54bgxky" }, "operation_type": "Update" } ] } ``` #### DELETE accounts/:account\_id/do\_not\_reach\_lists/:do\_not\_reach\_list\_id[](#delete-accounts-account-id-do-not-reach-lists-do-not-reach-list-id "Permalink to this headline") Delete the specified Do Not Reach List belonging to the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/do_not_reach_lists/:do_not_reach_list_id` **Parameters[](#parameters "Permalink to this headline")** None **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54bgxky/do_not_reach_lists/4ofrp` **Example Response[](#example-response "Permalink to this headline")** ``` { "request": { "params": { "do_not_reach_list_id": "4ofrp", "account_id": "18ce54bgxky" } }, "data": { "targetable": false, "name": "Do Not Reach List", "description": null, "id": "4ofrp", "reasons_not_targetable": [ "PROCESSING", "TOO_SMALL" ], "created_at": "2022-02-08T23:02:07Z", "list_size": null, "updated_at": "2022-02-08T23:02:21Z", "deleted": true } } ``` # Campaign Management Source: https://docs.x.com/x-ads-api/campaign-management ## Advertiser API Programatically schedule campaigns and manage ads on X through this suite of APIs. ## What Can You Promote? ### [Promoted Ads](https://business.x.com/help/what-are-promoted-tweets) * Promoted Ads are ordinary ads purchased by advertisers who want to reach a wider group of users or to spark engagement from their existing followers. * Promoted Ads are clearly labeled as Promoted when an advertiser is paying for their placement on X. In every other respect, Promoted Ads act just like regular ads and can be reposted, replied to, liked and more. They have typical delivery rules and are created using [POST statuses/update](/x-api/posts/creation-of-a-post). * **“Promoted-only” Tweets,** created via [POST accounts/:account\_id/tweet](/x-ads-api/creatives#post-accounts-account-id-tweet), can be used in Promoted Tweets campaigns but will not serve to followers or appear on the public timeline. To retrieve a list of promoted-only tweets for a certain account, use [GET accounts/:account\_id/scoped\_timeline](/x-ads-api/creatives). ### [Promoted Accounts](https://business.x.com/help/what-are-promoted-accounts) * Promoted Accounts are part of Who to Follow, which suggests accounts that people don’t currently follow and may find interesting. Promoted Accounts help introduce an even wider variety of accounts people may enjoy. * Promoted Accounts for Timeline, associate a Promoted Tweet with a Promoted Account campaign and will display in user timelines. Promoted Trends are not available in the Ads API. ## Campaigns and Ad Groups (Line Items) Campaigns define the schedule and budget of an ad. The advertiser specifies a daily and overall budget. The campaign can be bound to a specific start and end time or run continuously until the budget is spent. The budget comes from one of the Funding Instruments of the advertising account. Campaign identifiers (:campaign\_id) are the base-36 representation of the base-10 value we present in the X Ads UI. Advertising accounts are limited to a maximum of 200 active campaigns. This limit can be raised to 4,000 active campaigns manually by the advertiser’s X Account Manager upon request. A campaign is considered active until it reaches its end time or gets deleted. Paused campaigns are considered active until their designated end times. Line items spend the budget defined by a campaign. Line items pull together the per-engagement bid, the Tweet or account to promote, and the targeting rules. ## Analytics The X Ads API offers a set of analytics endpoints to track and optimize ad performance. Please see Analytics and Analytics Best Practices for more information. For the billing metric, the data may not be finalized until three days after the event. Before that point, the data should be considered speculative. The final billable number will always be less than the speculative amount. The billable number is corrected for spam and related low-quality traffic. See Timezones for other considerations regarding time. ## Creating a Campaign - Step-by-Step The following example assumes you have installed, configured, and authorized your app and user using [twurl](https://github.com/twitter/twurl). twurl is a command-line tool in the spirit of cURL that gracefully handles X OAuth authentication. twurl is a great tool for quickly testing and debugging Ads API (and REST API) functionality. To see the full headers of the request and response, use `-t` to trace the call, roughly equivalent to cURL’s `-v` option. **For this example, we will create a Promoted Ads campaign that will be targeted by keyword.** 1. **Retrieve the account id.** ``` twurl -H ads-api.x.com /9/accounts/ ``` ```JSON theme={null} { "request": { "params": { } }, "data": [ { "name": "Test account for @AdsAPI", "timezone": "America/Los_Angeles", "timezone_switch_at": null, "id": "xxxxxx", "created_at": "2014-03-09T00:41:49Z", "salt": "f9f9d5a5f23075c618da5eb1d1a9df57", "updated_at": "2015-01-29T00:41:49Z", "approval_status": "ACCEPTED", "deleted": false } ], "data_type": "account", "total_count": 1, "next_cursor": null } ``` 2. **Retrieve the funding instrument id.** Hit the [GET accounts/:account\_id/funding\_instruments](/x-ads-api/campaign-management#get-accounts-account-id-funding-instruments) API using the account id retrieved in the previous command. ``` twurl -H ads-api.x.com /9/accounts/xxxxxx/funding_instruments ``` ```JSON theme={null} { "data": [ { "cancelled": true, "created_at": "2014-03-09T00:41:49Z", "credit_limit_local_micro": null, "currency": "USD", "deleted": false, "description": null, "end_time": null, "funded_amount_local_micro": null, "id": "yyyy", "type": null, "updated_at": "2014-05-29T00:41:49Z" } ], "data_type": "funding_instrument", "next_cursor": null, "request": { "params": { "account_id": "xxxxxx" } }, "total_count": 1 } ``` 3. **Create a campaign and associate it with the funding instrument.** Specify a start time and a budget for the campaign. For the purpose of this example, we will use a budget of \$500 and for the daily limit, \$50. ``` twurl -H ads-api.x.com -d "funding_instrument_id=yyyy&name=My First Campaign&total_budget_amount_local_micro=500000000&daily_budget_amount_local_micro=50000000" /9/accounts/xxxxxx/campaigns ``` ```JSON theme={null} { "data": { "created_at": "2015-02-09T00:00:00Z", "currency": "USD", "daily_budget_amount_local_micro": 50000000, "deleted": false, "end_time": null, "funding_instrument_id": "yyyy", "id": "92ph", "name": "My First Campaign", "entity_status": "PAUSED", "standard_delivery": true, "total_budget_amount_local_micro": 500000000, "updated_at": "2015-02-09T00:00:00Z" }, "data_type": "campaign", "request": { "params": { "account_id": "xxxxxx", "daily_budget_amount_local_micro": 50000000, "funding_instrument_id": "yyyy", "name": "My First Campaign", "total_budget_amount_local_micro": 500000000 } } } ``` 4. **Create a line item associated with the campaign.** Now that we have a campaign id, we can create a line item to associate with it. The line item wraps the bid price, targeting, and actual creative portion of the campaign. For this line item, we will be promoting tweets with a bid of \$1.50. ``` twurl -H ads-api.x.com -d "campaign_id=XXXX&bid_amount_local_micro=1500000&product_type=PROMOTED_TWEETS&placements=ALL_ON_TWITTER&objective=ENGAGEMENTS&entity_status=PAUSED" /9/accounts/xxxxxxx/line_items ``` ```JSON theme={null} { "data_type": "line_item", "data": { "bid_type": "MAX", "name": "Untitled", "placements": [ "ALL_ON_TWITTER" ], "bid_amount_local_micro": 1500000, "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "charge_by": "ENGAGEMENT", "product_type": "PROMOTED_TWEETS", "bid_unit": "ENGAGEMENT", "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "azjx", "entity_status": "PAUSED", "optimization": "DEFAULT", "categories": [], "currency": "USD", "created_at": "2015-02-09T00:00:00Z", "updated_at": "2015-02-09T00:00:00Z", "include_sentiment": "POSITIVE_ONLY", "campaign_id": "92ph", "deleted": false }, "request": { "params": { "placements": [ "ALL_ON_TWITTER" ], "bid_amount_local_micro": 1500000, "product_type": "PROMOTED_TWEETS", "entity_status": "PAUSED", "account_id": "xxxxxxx", "campaign_id": "92ph" } } } ``` 5. **Create a targeting profile associated with the line item.** With the line item created, we can assign targeting criteria. We want to target the phrase keywords “grumpy cat” in the San Francisco Bay Area location. This is going to require a location id lookup and two targeting\_criteria POST requests. ``` twurl -H ads-api.x.com "/9/targeting_criteria/locations?location_type=CITIES&q=San Francisco" ``` ```JSON theme={null} { "data": [ { "name": "San Francisco-Oakland-San Jose CA, US", "targeting_type": "LOCATION", "targeting_value": "5122804691e5fecc" } ], "data_type": "targeting_criterion", "request": { "params": { "location_type": "CITY", "q": "San Francisco" } } } ``` ``` twurl -H ads-api.x.com -X POST -d "line_item_id=yyyy&targeting_type=LOCATION&targeting_value=5122804691e5fecc" /9/accounts/xxxxxx/targeting_criteria ``` ```JSON theme={null} { "data": { "created_at": "2015-02-09T00:00:15Z", "deleted": false, "id": "2u3be", "line_item_id": "yyyy", "name": "San Francisco-Oakland-San Jose CA, US", "targeting_type": "LOCATION", "targeting_value": "5122804691e5fecc", "updated_at": "2013-05-30T21:01:35Z" }, "data_type": "targeting_criterion", "request": { "params": { "account_id": "xxxxxx", "line_item_id": "yyyy", "targeting_type": "LOCATION", "targeting_value": "5122804691e5fecc" } } } ``` ``` twurl -H ads-api.x.com -X POST -d "line_item_id=yyyy&targeting_type=PHRASE_KEYWORD&targeting_value=grumpy cat" /9/accounts/xxxxxx/targeting_criteria ``` ```JSON theme={null} { "data": { "created_at": "2015-02-09T00:00:20Z", "deleted": false, "id": "2u3bd", "line_item_id": "yyyy", "name": "grumpy cat", "targeting_type": "PHRASE_KEYWORD", "targeting_value": "grumpy cat", "updated_at": "2013-05-30T18:05:35Z" }, "data_type": "targeting_criterion", "request": { "params": { "account_id": "xxxxxx", "line_item_id": "yyyy", "targeting_type": "PHRASE_KEYWORD", "targeting_value": "grumpy cat" } } } ``` 6. **Finally, un-pause the line item.** ``` twurl -H ads-api.x.com -X PUT "/9/accounts/xxxxxx/line_items/yyyy/?entity_status=ACTIVE" ``` ```JSON theme={null} { "data_type": "line_item", "data": { "bid_type": "MAX", "name": "grumpy cat", "placements": [], "bid_amount_local_micro": 1500000, "automatically_select_bid": false, "advertiser_domain": null, "primary_web_event_tag": null, "charge_by": "ENGAGEMENT", "product_type": "PROMOTED_TWEETS", "bid_unit": "ENGAGEMENT", "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "yyyy", "entity_status": "ACTIVE", "optimization": "DEFAULT", "categories": [], "currency": "USD", "created_at": "2015-02-09T00:00:20Z", "updated_at": "2015-02-09T00:00:20Z", "include_sentiment": "POSITIVE_ONLY", "campaign_id": "dy1f", "deleted": false }, "request": { "params": { "line_item_id": "yyyy", "entity_status": "ACTIVE", "account_id": "xxxxxx" } } } ``` That’s it! We now have an active, targeted, and funded Promoted Tweets in Timelines campaign which is running. ### Objective Based Campaigns Objective-based campaigns and pricing allow advertisers to pay for the actions that are aligned with their marketing objectives. To support these, set the appropriate `objective` on line items. The parameter used on the line item write endpoints and returned on the read endpoints, is `objective`. This field has the following possible values as of today: * `APP_ENGAGEMENTS` * `APP_INSTALLS` * `FOLLOWERS` * `ENGAGEMENTS` * `REACH` * `VIDEO_VIEWS` * `PREROLL_VIEWS` * `WEBSITE_CLICKS` Objectives impact how we optimize campaigns in our auctions and how we bill on those campaigns. We enable pricing based on objective, such as CPAC for `APP_ENGAGEMENTS`, CPAC *or* CPI for `APP_INSTALLS`, CPLC for `WEBSITE_CLICKS`, CPF for `FOLLOWERS`, CPE for `ENGAGEMENTS`, and CPM for `REACH`. Mobile app promotion campaigns are required to contain either the `APP_ENGAGEMENTS` or `APP_INSTALLS` objective. **Note:** Line items with different objectives are not allowed under the same campaign. | Campaign objective | API objective | Media in Tweets | Pricing model | | :----------------- | :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------- | | App re-engagements | `APP_ENGAGEMENTS` | Image or video app download card required. | CPAC | | App installs | `APP_INSTALLS` | Image or video app download card required. | CPAC or CPI (set using `charge_by`) | | Reach | `REACH` | No restrictions. | CPM | | Followers | `FOLLOWERS` | Tweet not required, but recommended. There are no media restrictions on Tweets for Followers campaigns, though we recommend text-only Tweets. [More information](https://business.x.com/en/help/campaign-setup/create-a-followers-campaign.html#serve) | CPF | | Engagements | `ENGAGEMENTS` | No restrictions. | CPE | | Video Views | `VIDEO_VIEWS` | Video conversation card, video, or GIF required. | CPV or cost per 3s/100% view | | Pre-roll views | `PREROLL_VIEWS` | Video required. | CPV or cost per 3s/100% view | | Website Clicks | `WEBSITE_CLICKS` | Website card recommended, but not required. Tweet must have either a website card or a website link (not both). | CPLC | ### Funding Instruments Funding Instruments are the source of campaign budget. Funding Instruments can not be created via the Ads API, they have to be already established by the advertiser’s account manager at X (for credit lines) or via ads.x.com (for credit cards) to be available. To get a list of all `funding_instruments` on an account, see [GET accounts/:account\_id/funding\_instruments](/x-ads-api/campaign-management#get-accounts-account-id-funding-instruments) and [GET accounts/:account\_id/funding\_instruments/:funding\_instrument\_id](/x-ads-api/campaign-management#get-accounts-account-id-funding-instruments-funding-instrument-id) for the details of a specific one. #### Funding Instrument Attributes Descriptive: `account_id`, funding instrument `id`, funding instrument `type`, `description`, and `io_header`(insertion order header ID). Note that a single `io_header` may be associated with multiple funding instruments. Funding ability: `able_to_fund` and `reasons_not_able_to_fund`. Time: `created_at`, `updated_at`, `start_time`, and `end_time` represented by a string, formatted as “%Y-%m-%dT%l:%M:%S%z”. Boolean status: `paused`, `deleted`, and `cancelled` (true or false). Financial: `currency` ([ISO-4217](http://en.wikipedia.org/wiki/ISO_4217) format), `credit_limit_local_micro`, `credit_remaining_local_micro`, and `funded_amount_local_micro`. The value of a currency is represented in micros. For USD, \$5.50 is encoded as 5.50\*1e6, or 5,500,000. To represent a “whole value”, you need to multiply the local micro by 1e6 (1\_000\_000) for all currencies. #### Attribute Details `credit_limit_local_micro` is only valid for `CREDIT_CARD` or `CREDIT_LINE` type funding instruments and represents the credit limit for that instrument. `funded_amount_local_micro` is only valid for `INSERTION_ORDER` type funding instruments and represents the allocated budget. `credit_remaining_local_micro` is valid for `CREDIT_LINE` and `AGENCY_CREDIT_LINE` type funding instruments. It represents the `credit_limit_local_micro` minus the amount already spent on that funding instrument. It does not represent the difference between `funded_amount_local_micro` and the amount spent. We draw a distinction between credit limit and funded amount because they represent different underlying funding methods and spending agreements we have with advertisers. #### Types of Funding Instruments **Credit Cards** Typically used by self-serve advertisers (without an account manager). **Credit Lines** These are in the form of insertion orders (IOs) and are set by account managers. **Multi-Handle Credit Lines** Advertisers can fund campaigns across multiple handles with this type of credit line. This feature is enabled by their X Account Manager, associating the different @handles to a specific credit line. For example, @NikeSB and @NikeFuel can both have access to the @Nike credit line. This funding instrument is available just like any other. You retrieve the data by submitting a GET request to the funding\_instrument endpoint. Here is a sample response (note the `CREDIT_LINE` type). ```json theme={null} GET https://ads-api.x.com/5/accounts/a0b1c3/funding_instruments { "request": { "params": { "account_id": "a0b1c3" } }, "data": [ { "start_time": "2013-05-30T04:00:00Z", "description": "FakeNike - Credit Line", "credit_limit_local_micro": 150000000000, "end_time": null, "cancelled": false, "id": "i1234", "paused": false, "account_id": "a0b1c3", "reasons_not_able_to_fund": [], "io_header": null, "currency": "USD", "funded_amount_local_micro": 0, "created_at": "2013-05-30T18:16:38Z", "type": "CREDIT_LINE", "able_to_fund": true, "updated_at": "2013-05-30T18:16:38Z", "credit_remaining_local_micro": 123661919751, "deleted": false, } ], "data_type": "funding_instrument", "total_count": 1, "next_cursor": null } ``` The only thing particular about this funding instrument is the type and the fact that it is available to all the accounts that were associated to it. Of course, the remaining credit is impacted by all campaigns funded by this instrument, across all accounts sharing it. The details of what accounts are associated to a specific credit line are not available via the API (nor via ads.x.com). For more information on Funding Instrument enumerations, please click [here](/x-ads-api/introduction). ### Targeting Targeting is a core concept of the Ads API. Targeting is set at the line item level and options vary across placements. To set new targeting criteria you need to use [POST accounts/:account\_id/targeting\_criteria](/x-ads-api/campaign-management#post-accounts-account-id-targeting-criteria) and [PUT accounts/:account\_id/targeting\_criteria](/x-ads-api/campaign-management#get-accounts-account-id-targeting-criteria) to update them. Use [GET accounts/:account\_id/line\_items](/x-ads-api/campaign-management#get-accounts-account-id-line-items) for a list of all line items and [GET  accounts/:account\_id/line\_items/:line\_item\_id](/x-ads-api/campaign-management#get-accounts-account-id-line-items-line-item-id) to retrieve a specific line item. #### Targeting options by placement [Promoted Tweets](https://business.x.com/help/what-are-promoted-tweets) and [Promoted Accounts](https://business.x.com/help/what-are-promoted-accounts) products can be made available on a variety of placements. [Promoted Trends (PTr)](https://business.x.com/help/what-are-promoted-trends) are not available via the API. For possible placement combinations, refer to the [GET line\_items/placements](/x-ads-api/campaign-management#get-line-items-placements) endpoint. Each placement has different options for targeting. Location, Platform and Gender are available for all. The other options are contextual to the type of placement. * **X Search**: Age Targeting, Devices, Events, Gender, Keyword Types (All), Language, Locations, Network Activation, Network Operators, Platform, Platform Version, Tailored Audiences, WiFi Only * **X Timeline**: Age Targeting, Devices, Events, Followers Of, Similar to Followers Of, Gender, Interest, Language, Locations, Network Activation, Network Operators, Non-exact Keyword Types, Partner Audience Types, Platform, Platform Version, Retargeting Types, Tailored Audiences, TV Targeting Types, WiFi Only * **X Profiles & Tweet Details**: Age Targeting, Devices, Events, Followers Of, Similar to Followers Of, Gender, Interest, Language, Locations, Network Activation, Network Operators, Non-exact Keyword Types, Partner Audience Types, Platform, Platform Version, Retargeting Types, Tailored Audiences, TV Targeting Types, WiFi Only #### Understanding targeting types **Age Targeting**: Target users based on specific age buckets. A list of age bucket enums can be found on the [Enumerations](/x-ads-api/introduction) page. [Events](https://business.x.com/help/event-targeting): Specify an event for targeting. Only one event can be used for targeting (per line item). Use the [GET targeting\_criteria/events](/x-ads-api/campaign-management#get-targeting-criteria-events) endpoint to find events available for targeting. [Gender](https://business.x.com/help/geo-gender-and-language-targeting): Target males (1) or females (2). Leave null to target all. [Installed App Store Categories](https://business.x.com/help/installed-app-category-targeting): use this targeting type to target users based on the categories of apps they have installed or have indicated interest in. See [GET targeting\_criteria/app\_store\_categories](/x-ads-api/campaign-management#get-targeting-criteria-app-store-categories). [Interests](https://business.x.com/help/interest-and-username-targeting): Target users by interest. Get the interests list from [GET targeting\_criteria/interests](/x-ads-api/campaign-management#get-targeting-criteria-interests). You can target up to 100 interests. **Followers Of**: Target the followers of any fully promotable users for the current account (note, currently the primary account holder is the only fully-promotable user of that account). [GET accounts/:account\_id/promotable\_users](/x-ads-api/campaign-management#get-accounts-account-id-promotable-users) to get a list of promotable users. [Similar to Followers Of](https://business.x.com/help/interest-and-username-targeting): Target people with the same interests as followers of specific users. You can use up to 100 [Users](/x-api/fundamentals/data-dictionary#user). [Locations](https://business.x.com/help/geo-gender-and-language-targeting): Specify up to 2,000 locations to target. Get list from [GET targeting\_criteria/locations](/x-ads-api/campaign-management#get-targeting-criteria-locations). There are additional requirements for ads that target certain countries. See [Country Targeting and Display Requirements](/x-ads-api/campaign-management#country-targeting-and-display-requirements) for more information. [Keywords](https://business.x.com/help/keyword-targeting): Keyword targeting options are specific by type of placement. You can use up to 1000 keywords for targeting (per line item). See the Keyword Types section for options. [Language Targeting](https://business.x.com/help/geo-gender-and-language-targeting): Target users who understand specific languages. [Mobile Network Operator Targeting](https://business.x.com/help/device-carrier-and-new-mobile-user-targeting): Enables advertisers to target users based on mobile carrier, using the targeting type `NETWORK_OPERATOR` from [GET targeting\_criteria/network\_operators](/x-ads-api/campaign-management#get-targeting-criteria-network-operators). [New Mobile Device Targeting](https://business.x.com/help/device-carrier-and-new-mobile-user-targeting): Reach users based on the date that they first accessed X via their device, using the targeting type `NETWORK_ACTIVATION_DURATION` using operator\_type of LT for less than and  `GTE` for greater than or equal. [Platforms](/x-ads-api/campaign-management#get-targeting-criteria-platforms), [Platform Versions](/x-ads-api/campaign-management#get-targeting-criteria-platform-versions), [Devices](/x-ads-api/campaign-management#get-targeting-criteria-devices), and Wifi-Only: Allows targeting of mobile devices across a variety of vectors. Platforms is a high-level targeting type that can hit broad categories of phone. Example values are `iOS` and `Android`. Devices allow you to target users of specific mobile devices, for example the `iPhone 5s`, `Nexus 4`, or `Samsung Galaxy Note`. Platform versions allow you to target users of versions of specific mobile operating systems, down to the point release. Examples include iOS 7.1 and Android 4.4. Wifi-Only allows you to target only those users who are using their devices on a WiFi network; if this is not set, users using the carrier connection as well as WiFi will be targeted. * Users can target platforms and devices if there is no overlap. I can target Blackberry as a platform and iPad Air as a device simultaneously. * Users can target devices and os versions simultaneously. I can target iPad Air and iOS >= 7.0. * Users cannot target platforms that are broader than devices. I cannot target iOS and iPad Air. \[Tailored Audiences]/x-ads-api/audiences: Reach users through an approved ads partner to target groups of customers and connect with them on X. **[TV Targeting](https://support.x.com/articles/20170766-tv-targeting)** **TV Show Targeting**: reach people that engage with specific TV programs. This targeting criteria can be configured to continuously target while a campaign is active with the `TV_SHOW` targeting type. Use the [GET targeting\_criteria/tv\_markets](/x-ads-api/campaign-management#get-targeting-criteria-tv-markets) and [GET targeting\_criteria/tv\_shows](/x-ads-api/campaign-management#get-targeting-criteria-tv-shows) endpoints to determine TV shows available. **Tweet Engager Retargeting** Tweet engager retargeting enables advertisers to target audiences across devices who have previously been exposed to or engaged with their promoted or organic Tweets on X. With this targeting advertisers can follow up with people who saw or engaged with an advertiser’s content on X and are most likely to further engage or convert with subsequent messaging or offers. Users will be eligible for targeting within minutes of exposure or engagement and will remain eligible for up to 90 days afterwards for engagements and 30 days for exposures. Tweet Engager Targeting Types: * `ENGAGEMENT_TYPE` which accepts either `IMPRESSION` or `ENGAGEMENT` as a targeting value. This specifies whether you wish to target exposed users (`IMPRESSION`) or engaged users (`ENGAGEMENT`). * `CAMPAIGN_ENGAGEMENT` uses a campaign ID as the targeting value. Users who engaged with or were exposed to this campaign (depending on `ENGAGEMENT_TYPE`) are the ones who will be targeted. * `USER_ENGAGEMENT` which uses the promoted user ID as the targeting value to target users who were exposed to or engaged with an advertiser’s organic content (depending on `ENGAGEMENT_TYPE`). This must be the promoted user ID associated with the Ads account. *Note:* `ENGAGEMENT_TYPE` is required in addition to at least one valid `CAMPAIGN_ENGAGEMENT` or `USER_ENGAGEMENT` value. Both tweet engager targeting types may be present and multiple campaigns may be targeted on a given line item. **Video Viewer Targeting**: Video viewer targeting builds on Tweet engager targeting to enable advertisers to target audiences who have previously watched part or all of a video on X. Advertisers can target organic videos, promoted videos, or both. Promoted videos are not limited to video view objective campaigns or line items. Video Viewer Targeting Types: * `VIDEO_VIEW` for users who have clicked to play the video or have viewed 3 seconds of autoplay * `VIDEO_VIEW_PARTIAL` for users who have viewed 50% of the video * `VIDEO_VIEW_COMPLETE` for users who have viewed at least 95% of the video As with Tweet engager targeting, one or both of the following must also be present in targeting criteria for the line item when `ENGAGEMENT_TYPE` is used: * `CAMPAIGN_ENGAGEMENT` uses a campaign ID as the targeting value. Users who watched a video (based on `ENGAGEMENT_TYPE`) in this campaign are the ones who will be targeted. * `USER_ENGAGEMENT` which uses the promoted user ID as the targeting value to target users who watched a video (based on `ENGAGEMENT_TYPE`) in an advertiser’s organic content. This must be the promoted user ID associated with the Ads account. **Keyword Types** See our support document on [keyword targeting](https://business.x.com/en/help/campaign-setup/campaign-targeting/keyword-targeting.html) for a conceptual overview. * **Broad** (default value): match all words, independent of order. Not sensitive to capitalization, plurals or tense. Will automatically be expanded when possible (i.e. “car repair” would also match “automobile fix”). If you want to target without expansion, you need to add a + sign before the keywords, like “+boat +jet”. Using keywords without the + will default to Broad Match. * **Unordered** (deprecated): match all words, independent of order. Not sensitive to capitalization, plurals or tense. * **Phrase**: match the exact keywords string, other keywords can be present. * **Exact**: match exactly the keywords string, not any others. * **Negative**: avoid matching searches that include all of these keywords somewhere in the query, regardless of the order in which they are written, even if other words are present. * **Negative Phrase**: avoid matching searches that include this exact keywords string somewhere in the query, even if other words are present. * **Negative Exact**: avoid matching searches that exactly match these keywords and contain no other words.   **Emoji targeting** Emoji targeting is supported via keyword targeting. To use emoji targeting, simply create keyword targeting for Unicode codepoints representing that emoji such as *U+1F602* (*xF0x9Fx98x82* in UTF-8) for the ‘face with tears of joy’ emoji (😂). Emoji which we accept can be confirmed with the [twemoji](https://x.github.io/twemoji/preview.html) list. Targeting an emoji targets all variations. For a summary of all values with required/optional and specific details for each, see [PUT accounts/:account\_id/targeting\_criteria](/x-ads-api/campaign-management#get-accounts-account-id-targeting-criteria). #### Targeting Criteria Combinations **Updated Campaign Workflow** Create campaigns which target broadly with geo, gender, language, and device/platform criteria. Advertisers can then combine the broad targeting with additional targeting criteria (e.g. interests, keywords, followers, tailored audiences, TV). **If no targeting criteria is specified for a line item, the line item will target all users worldwide.** | | | | :----------------- | :-------------------- | | “Primary” Types | Other Types | | Followers | Locations | | Tailored Audiences | Gender | | Interests | Languages | | Keywords | Devices and platforms | | TV | Age | Targeting criteria will be combined for your ad group such that: * “Primary” Targeting Types will get **∪**‘d (i.e. put in a logical union). * Other Targeting Types will get **AND**‘d. * Same types will get **OR**‘d. **Some examples** At a glance: \[(**Followers**) ∪ (**Tailored Audiences**) ∪ (**Interests**) ∪ (**Keywords**)] AND (**Location**) AND (**Gender**) AND (**Languages**) AND (**Devices and Platforms**) A Geo example: Let’s say we want an ad group for our campaign to serve targeting: * X users in the U.S., England, and Canada (Location) * who are Women (Gender) * derived from Tailored Audiences list (“Primary”) * with Keywords (“Primary”) The targeting criteria will be: \[**US** OR **GB** OR **CA**] AND \[**Female**] AND \[**Tailored Audiences** ∪ **Keyword**] ### Additional examples * Select Gender and Geo but no primary: (**Male**) AND (**US** OR **GB**) * Select Gender, Geo, Interest: (**Female**) AND (**CA**) AND (**Computers** OR **Technology** OR **Startups**) * Select Gender, Geo, Interest, Tailored Audiences, Keywords: (**Male**) AND (**GB**) AND (**Cars** ∪ **Tailored Audiences for CRM** ∪ **autocross**) ### Budget Pacing Advertisers now have more control over how fast their daily budgets are spent on your Promoted Tweet and Account campaigns. Enabling standard delivery, which is the default, ensures an even spend rate throughout the day. By turning **off** standard delivery, we will serve impressions and generate engagements as quickly as possible until your daily budget is exhausted, which may be quite early on in the day depending on targeting and competition. This is called accelerated delivery. **Getting Started** Standard delivery is the default option for all campaigns, so no action is required unless you wish to turn it off. To spend through your daily budget on a campaign as fast as possible set the `standard_delivery` parameter to `false` to set the pace to accelerated delivery (see [GET accounts/:account\_id/campaigns](/x-ads-api/campaign-management#get-accounts-account-id-campaigns)). **Notes** * “Day” is respective to X [advertiser account](/x-ads-api/campaign-management#accounts) timezone (eg. America/Los\_Angeles). * Early results indicate that standard delivery will improve eCPE/CPF for advertisers, with more consistent coverage throughout the day. For more additional information about budgets and pacing please see the [Bidding and Auctions FAQ](https://business.x.com/en/help/troubleshooting/bidding-and-auctions-faqs.html). ### Target Bidding Campaign management #### Bid Strategy We have introduced the concept of Bid Strategy to simplify campaign creation workflow and reduce confusion about combinations of multiple parameters. All previous (marked as legacy) combinations of parameters can be achieved by setting an equivalent goal parameter. Further information can be found in the announcement [here](https://devcommunity.x.com/t/ads-api-version-10/158787/1#changed-default-bid_strategy-values-9). As example: | | | | | :--------------------- | :---------------------------------------------------------------------------------------------- | :--------------------------------------------------------- | | **Campaign Objective** | **Legacy** | **Ads API v10+** | | App Installs | `bid_type`= `AUTO`

`bid_unit` = `APP_INSTALLS`

`charge_by` = `APP_CLICKS` | `goal` = `APP_INSTALLS`

`bid_strategy` = `AUTO` | | Website Clicks | `bid_type` = `TARGET` (Note: `bid_unit` was not needed for some campaign objectives) | `bid_strategy` = `TARGET` | #### Target Bidding Using target bidding, you can specify a target cost you want to pay and the X Ads platform will optimize your campaign for performance while staying near or below your target cost. This feature gives you the flexibility to reach users who are especially likely to take the desired action (such as a link click, a lead or a follow) while maintaining cost control. This is a powerful feature for advertisers who desire more options for campaign setup and optimization (including bidding options). For line items with compatible campaign objectives, we’ve introduced a new pricing mechanism for bid amount that lets you specify a target cost you want to pay. Our ad platform dynamically bids on your behalf to help you drive more results, while working to keep your average cost within 20% of your specified target. The `bid_strategy` setting on line items may be set with a value of `TARGET` to enable target bidding on relevant campaign objectives, such as: * `WEBSITE_CLICKS` * `WEBSITE_CONVERSIONS`  * `APP_INSTALLS`  * `APP_ENGAGEMENTS` * `REACH` ### Country Targeting and Display Requirements Campaign management Country-specific targeting and display requirements are contained on this page. These requirements must be adhered to by all partners. #### Russia [X’s Ads Policies](https://support.x.com/groups/58-advertising/topics/249-advertiser-policies/articles/20171727-illegal-products-and-services) prohibit advertisers from targeting Russia with advertisements that are not in the Russian language. When your users specifically target Russia, you must display the following warning message to your users: Ads targeting Russia must be in the Russian language. ### Partner Managed Funding Instruments The onboarding flow configures an [ads.x.com](https://ads.x.com) account for the X account, which can be managed by the partner through the Ads API, and whose advertising spend is billed to the partner.   #### Partner Initial Set-up The process to initially set-up a new PMFI Ads API partner takes up to 3-weeks from exchange of required information. The following must be shared with your technical contacts at X, as well as the X contact managing the integration with the partner in order to get the process started: * **The partner must share their PGP/GPG public key.** A shared secret key needs to be exchanged between the Ads API partner and X. This will be used to verify data during the onboarding flow. * **The** `app_id` **or** `consumer_secret` **for the [X app](/resources/fundamentals/developer-apps) that will be used for Ads API access.** You can view and edit your existing X apps via the [app dashboard](https://developer.x.com/content/developer-twitter/en/apps) if you are logged into your X account on developer.x.com. If you need to create a X app, you will need to have an approved [developer account](/resources/fundamentals/developer-portal). X allows one app for production+sandbox and one optional app for sandbox-only access. The X app must be created on a corporate, partner-controlled X handle.   #### Advertiser Onboarding Flow The advertiser onboarding flow occurs via a web browser in the following way: 1. The user starts the onboarding flow on the partner’s website and enters the handle they want to onboard. 2. The partner redirects the user to a URL on [ads.x.com](https://ads.x.com) with a signed payload. This payload contains the partner’s API `app_id`, the X `user_id` of the X handle which is to be onboarded and a callback URL and other fields documented below. 3. The user is asked to sign into [ads.x.com](http://ads.x.com) using the standard x.com login page. 4. Once the user is logged in, the onboarding process is initiated. This step includes ad review, account validation and other checks. 5. When all onboarding tasks are completed, the user is redirected to the callback URL that was provided by the Ads API partner, with a payload that indicates success or failure. This includes the 3-legged authorization process.   #### Onboarding redirect payload URL for redirect: [https://ads.x.com/link\\\_managed\\\_account](https://ads.x.com/link\\_managed\\_account) The redirect URL will be called with the following parameters: | | | | | :------------------- | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Name | Type | Description | | callback\_url | URL encoded string | user will be redirected to this url after the account link process completes, regardless of outcome. See the partner redirect url section for protocol details | | client\_app\_id | integer | X API client app id, used to identify the managing partner | | promotable\_user\_id | integer | X user\_id of the @handle whose promotions are to be managed by the managing partner. Used to make sure it is the same as the user who logs into ads.x.com to complete the linking process | | fi\_description | URL encoded String (max 255 characters) | funding instrument name. This will be displayed in the description field in the API when the funding instrument is retrieved. If a funding\_instrument description is given, the existing funding\_instrument will be paused, and a new managed partner funding instrument will be set up. (if one exists with the same name, nothing will happen) | | timezone | String, in Area/Location format | This will be the timezone used to determine the day to which daily budgets apply, and in which charges will be aggregated | | currency | ISO 4217 Currency Code | Currency that will be used to enter bids, and in which charges will be billed | | country | ISO 3166-1 alpha 2 Country Code | Billing Country for the account | | signature | URL encoded, base64 encoded binary code, as explained below | signature that combines a shared secret and the other parameters to verify authenticity of the call, as well as validity of the parameters. | #### Callback URL payload The base redirect URL is provided using the callback\_url parameter on the account link request (see above). The parameters added by [ads.x.com](https://ads.x.com) are: | | | | | :---------------------- | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Name | Type | Description | | status | string | **OK** an account was created, or an existing, eligible account was found.

**ACCOUNT\_INELIGIBLE** if partner specific constraints are not met **USER\_MISMATCH** the X account used to sign into ads.x.com was different from the promotable\_user\_id on the account link request **INCOMPLETE\_SERVING\_BILLING\_INFO** timezone, currency or country were not specified **INVALID\_COUNTRY** an invalid country value was given **INVALID\_CURRENCY** an invalid currency value was given **INVALID\_TIMEZONE** an invalid timezone value was given | | account\_id | URL encoded string | X ads account id of the linked account | | funding\_instrument\_id | URL encoded string | ID of the active partner managed funding instrument | | signature | URL encoded, base64 encoded binary code, as explained below | Base64 encoded HMAC-SHA1 signature that combines a shared secret and the other parameters to verify authenticity of the call, as well as validity of the parameters. To make sure the callback url is only valid for the X user\_id that the account link process was intended for, the X user\_id is to be appended to the shared secret (using &) when signing the request. | To make sure the callback URL is only valid for the X `user_id` that the account link process was intended for, the X `user_id` is to be appended to the shared secret (using &) when signing the request.   #### Signing the request and callback URLs In order to ensure that the requests to `/link_managed_account` and the callback url are valid, the requests need to be signed at the source and verified by the recipient before the recipient takes action on them. Signing the request with a secret that is shared between X and the managing partner ensures that each party only accepts requests sent by the authorized counterpart. The signature generation algorithm is similar to the one used in OAuth. Create a signature base string as follows: * Convert the HTTP Method to uppercase and set the base string equal to this value. * Append the ‘&’ character to the base string. * Percent encode the URL (without parameters) and append it to the base string. * Append the ‘&’ character to the base string. * Append the percent encoded query string, which is built as follows: * Percent encode every key and value that will be signed. * Sort the list of parameters alphabetically by key. * For each key/value pair (and with primary\_promotable\_user\_id for the partner redirect url): * Append the percent encoded key to the query string. * Append the ‘=’ character to the base string. * Append the percent encoded value to the query string. * Separate the percent encoded key=value pairs with the ‘&’ character. * Use the HMAC-SHA1 algorithm, using the previously exchanged shared secret, as the key, and the base string as the value to generate the signature. * Base64 encode the output of Step 2, drop the trailing newline character percent encode the signature generated in Step 3 and add it to the url in a signature parameter   #### Signing examples Signing a link account request Url to sign, assuming a GET request: [https://ads.x.com/link\\\_managed\\\_account?callback\\\_url=https%3A%2F%2Fmanagingpartner.com%2Flink\\\_account\\\_callback\&client\\\_app\\\_id=12345\&fi\\\_description=some%20name\&promotable\\\_user\\\_id=1](https://ads.x.com/link\\_managed\\_account?callback\\_url=https%3A%2F%2Fmanagingpartner.com%2Flink\\_account\\_callback\&client\\_app\\_id=12345\&fi\\_description=some%20name\&promotable\\_user\\_id=1) This url has the following parameters: callback\_url = [https://managingpartner.com/link\\\_account\_callback](https://managingpartner.com/link\\_account_callback) client\_app\_id = 12345 fi\_description = some name promotable\_user\_id = 1 The base string consisting of http method and url without parameters, steps a - d, looks like: GET [https://ads.x.com/link\\\_managed\\\_account](https://ads.x.com/link\\_managed\\_account) The query string, produced by the substeps of e, looks like: callback\_url=[https://managingpartner.com/link\\\_account\\\_callback\&client\\\_app\\\_id=12345\&fi\\\_description=some](https://managingpartner.com/link\\_account\\_callback\&client\\_app\\_id=12345\&fi\\_description=some) name\&promotable\_user\_id=1 Note that the key-value pairs are sorted by key name. The percent encoded query string looks like: callback\_url%3Dhttps%253A%252F%252Fmanagingpartner.com%252Flink\_account\_callback%26client\_app\_id%3D12345%26fi\_description%3Dsome%2520name%26promotable\_user\_id%3D1 The complete base string, combining steps a - d and e: GET [https://ads.x.com/link\\\_managed\\\_account\&callback\\\_url%3Dhttps%253A%252F%252Fmanagingpartner.com%252Flink\\\_account\\\_callback%26client\\\_app\\\_id%3D12345%26fi\\\_description%3Dsome%2520name%26promotable\\\_user\\\_id%3D1](https://ads.x.com/link\\_managed\\_account\&callback\\_url%3Dhttps%253A%252F%252Fmanagingpartner.com%252Flink\\_account\\_callback%26client\\_app\\_id%3D12345%26fi\\_description%3Dsome%2520name%26promotable\\_user\\_id%3D1) Using the hmac-sha1 algorithm we will sign this with the word “secret” as the key. The result is Base64 encoded, and presented without the final “\n” (steps 2 and 3): `KBxQMMSpKRrtg9aw3qxK4fTXvUc=` This signature is then added (percent encoded) to the end of the original url in the signature parameter (step 4): [https://ads.x.com/link\\\_managed\\\_account?callback\\\_url=https%3A%2F%2Fmanagingpartner.com%2Flink\\\_account\\\_callback\&client\\\_app\\\_id=12345\&fi\\\_description=some%20name\&promotable\\\_user\\\_id=1\&signature=KBxQMMSpKRrtg9aw3qxK4fTXvUc%3D](https://ads.x.com/link\\_managed\\_account?callback\\_url=https%3A%2F%2Fmanagingpartner.com%2Flink\\_account\\_callback\&client\\_app\\_id=12345\&fi\\_description=some%20name\&promotable\\_user\\_id=1\&signature=KBxQMMSpKRrtg9aw3qxK4fTXvUc%3D) Signing a partner redirect url (account link request callback) The URL to sign, assuming a GET request: [https://managingpartner.com/link\\\_account\\\_callback?status=OK\&account\\\_id=ABC\&funding\\\_instrument\_id=DEF](https://managingpartner.com/link\\_account\\_callback?status=OK\&account\\_id=ABC\&funding\\_instrument_id=DEF) This url has the following parameters: `account_id` = `ABC`, `funding_instrument_id` = `DEF` and `status` = `OK` The base string consisting of http method and url without parameters, steps a - d, looks like: GET https%3A%2F%2Fmanagingpartner.com%2Flink\_account\_callback&\`\` The query string, produced by the substeps of e, looks like: account\_id=ABC\&funding\_instrument\_id=DEF\&status=OK The percent encoded query string looks like: account\_id%3DABC%26funding\_instrument\_id%3DDEF%26status%3DOK The complete base string, combining steps a - d and e: GET https%3A%2F%2Fmanagingpartner.com%2Flink\_account\_callback\&account\_id%3DABC%26funding\_instrument\_id%3DDEF%26status%3DOK Using the hmac-sha1 algorithm we will sign this with the word “secret” and the X user id for which the original link request was made, 1 (`promotable_user_id` = 1 from above) as the key, “secret&1”. The result is Base64 encoded, and presented without the final “\n” (steps 2 and 3): `jDSHDkHJIFXpPLVxtA3a9d4bPjM=` This signature is then added (percent encoded) to the end of the original url in the signature parameter (step 4): [https://managingpartner.com/link\\\_account\\\_callback?\&status=OK\&account\\\_id=ABC\&funding\\\_instrument\_id=DEF\&signature=jDSHDkHJIFXpPLVxtA3a9d4bPjM%3D](https://managingpartner.com/link\\_account\\_callback?\&status=OK\&account\\_id=ABC\&funding\\_instrument_id=DEF\&signature=jDSHDkHJIFXpPLVxtA3a9d4bPjM%3D) ### Shared Key use / renewal The signing algorithm should have the ability to repeat itself with multiple keys. This will allow multiple shared keys to be used, and enables cycling shared keys on a periodic basis.   ### partner\_managed\_funding\_instrument creation If the fi\_description parameter is given, and no existing partner\_managed\_funding\_instrument with the same name exists in the account, a new partner\_managed\_funding\_instrument will be created, and all existing partner\_managed\_funding\_instruments will be paused. If a partner\_managed\_funding\_instrument with the same name exists, no new one will be created.   ### Repeated on-boarding flow calls / token refresh The on-boarding flow can be repeated in case the API access token was lost. The on-boarding flow implementation will require the user is logged in. If the user matches the promotable\_user\_id, and the associated ads account is found, and everything looks good, the user will be redirected back to the callback url, and the partner can initiate the OAuth flow to obtain an [access token](/resources/fundamentals/authentication#obtaining-access-tokens-using-3-legged-oauth-flow).   ### Non-redirectable error flow If the account link url is invoked with invalid parameters, the user will be shown a page similar to the one shown in the OAuth flow when invalid or expired parameters are given.   #### Ongoing updates to the PMFI Once the advertiser has been onboarded, the funding instrument can be managed using the [PUT accounts/:account\_id/funding\_instruments/:funding\_instrument\_id](/x-ads-api/campaign-management#get-accounts-account-id-funding-instruments) endpoint by only the partner who manages it. ### Placements There are several places where X ads can be displayed. This is set at the [line item](/x-ads-api/campaign-management#line-items) using the `placements` parameter. The possible values are: * `ALL_ON_TWITTER` * `PUBLISHER_NETWORK` * `TWITTER_PROFILE` * `TWITTER_SEARCH` * `TWITTER_TIMELINE` * `SPOTLIGHT` * `TREND` The line item’s `product_type` and `objective` determine which placements are allowed. The [GET line\_items/placements](/x-ads-api/campaign-management#line-item-placements) endpoint can be used to retrieve the valid placement options for each product type. Additionally, the following table lists the valid placement and objective combinations. | Objective | `ALL_ON_TWITTER` | `TWITTER_PROFILE` | `TWITTER_SEARCH` | `TWITTER_TIMELINE` | | :---------------- | :--------------- | :---------------- | :--------------- | :----------------- | | `APP_ENGAGEMENTS` | ✔ | ✔ | ✔ | ✔ | | `APP_INSTALLS` | ✔ | ✔ | ✔ | ✔ | | `REACH` | ✔ | ✔ | ✔ | ✔ | | `FOLLOWERS` | ✔ | ✔ | ✔ | ✔ | | `ENGAGEMENTS` | ✔ | ✔ | ✔ | ✔ | | `VIDEO_VIEWS` | ✔ | ✔ | ✔ | ✔ | | `PREROLL_VIEWS` | ✔ | ✔ | ✔ | ✔ | | `WEBSITE_CLICKS` | ✔ | ✔ | ✔ | ✔ | **Note**: It is not possible to specify *only* `TWITTER_PROFILE` placement. **Note**: `TWITTER_SEARCH` requires [keyword targeting](/x-ads-api/campaign-management#targeting-options). **Note**: The `REACH` objective must include `TWITTER_TIMELINE` placement. It can have either `ALL_ON_TWITTER`, any combination of placements that include `TWITTER_TIMELINE`, or `TWITTER_TIMELINE` on its own. ### Ad groups FAQ This document is meant to be a collection of commonly asked questions about Ad Groups in X's Ads API. #### What is an Ad Group? Ad groups, known as line items in the Ads API, exist under campaigns and are used for targeting and bidding against a set of X users. Advertisers promote Tweets or media (e.g., videos that are promoted as In-stream ads) by associating them with a line item. #### How do we create an Ad Group? Ad Groups are created by calling [POST accounts/:account\_id/line\_items](/x-ads-api/campaign-management#post-accounts-account-id-line-items) multiple times for the same campaign ID, and keeping (possibly completely different) targeting and Tweets associated with those line items. There is a limit of 100 line items per campaign and a limit of 200 active campaigns for a single ads account. Across all campaigns, there is a limit of and 8,000 active line items per ads account. #### Why should we add support for Ad Groups? Ad Groups are intended to make it easier for advertisers to organize, optimize and manage their campaigns. The advantage of Ad Groups is to compare and control different strategies across bid, budget, creative, and targeting. Upon associating multiple Promoted Tweets to a single line item, the auction would select the best Tweet from that group and then select the best Tweet for that campaign from all of the line items. If you have multiple Ad Groups with single Tweets, it would effectively select the Tweet that would likely perform better from that Ad Group. Using Ad Groups enables an advertiser to split up targeting and bidding into a much greater number of possible combinations, and in general allows splitting up targeting into logical groups. Ads API tools in particular could be built around fine tuned optimization rules with Ad Groups that would be more difficult to do via manual edits due to the larger scale of line item and creative combinations. #### How does the line item budget relate to campaign budget in an Ad Groups campaign? The total\_budget\_amount\_local\_micro for a line item cannot exceed the total budget for its parent campaign. Similarly, the line item’s bid\_amount\_local\_micro value should not exceed daily\_budget\_amount\_local\_micro or total\_budget\_amount\_local\_micro of the parent campaign. Setting these values incorrectly may put the overall campaign into a paused and unservable state. Note that the total campaign budget can be less than the sum of the budgets of its child line items, and distribution of budget between line items is partially up to the Ads API tool to effectively optimize and change as daily performance of targeting (line item) could differ significantly day to day due to X’s realtime nature. #### Do Ad Groups perform better than single line items? The performance of a campaign depends upon many factors and effectively a Tweet is the final deciding factor of performance. A Line Item will be treated as a factor of whether or not a Tweet is even in the running to be served to a user. Line items that target the same sets of users are considered to have overlap of users. It is considered a best practice to reduce this overlap of targeting between line items so that the highest performing sets of users can be clearly identified. ## Guides ### Video Views Preroll Objective The following guide outlines the steps required to set up a PREROLL\_VIEWS campaign on the Ads API. Broadly speaking these campaigns are split into two types, Curated Categories and Content Categories (referred to as Standard Categories on the Ads UI).   #### Endpoints Required * [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) * [GET accounts/:account\_id/curated\_categories](/x-ads-api/campaign-management#curated-categories-2) * [GET publishers](/x-ads-api/campaign-management#publishers) * [POST accounts/:account\_id/line\_item\_curated\_categories](/x-ads-api/campaign-management#line-item-curated-categories) * [POST accounts/:account\_id/line\_items](/x-ads-api/campaign-management#campaigns) (create ad group) * [POST accounts/:account\_id/media\_creatives](/x-ads-api/campaign-management#post-accounts-account-id-media-creatives) (to associate video with ad group) * [POST accounts/:account\_id/preroll\_call\_to\_action](/x-ads-api/creatives#preroll-call-to-actions) (set CTA and redirect URL) * [POST batch/accounts/:account\_id/targeting\_criteria](/x-ads-api/campaign-management#post-batch-accounts-account-id-targeting-criteria) (targeting) #### Steps #### Upload the video Uploading the video involves 2 steps: #### Upload the video media 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 Once the state returned using the `STATUS` command is `succeeded`, you’ll use the media\_key returned from that endpoint to add the video to the advertiser’s media library, using the [POST accounts/:account\_id/media\_library](/x-ads-api/creatives#media-library) endpoint. ```json theme={null} POST https://ads-api.x.com/8/55w3kv/media\_library?media\_key=3_931236738554519552 { "request": { "params": { "account_id": "55w3kv", "media\_key": "3\_931236738554519552" } }, "data": { "tweeted": false, "name": null, "file_name": null, "media\_url": "https://video.twimg.com/amplify\_video/1059840836186165250/vid/568x320/Gr2l1fB1X7xotKwC.mp4?tag=8", "media\_category": "AMPLIFY\_VIDEO", "media\_key": "3\_931236738554519552", "created_at": "2017-11-16T19:05:14Z", "media\_status": "TRANSCODE\_COMPLETED", "media_id": 931236738554519552, "media_type": "VIDEO", "updated_at": "2017-11-16T19:05:23Z", "deleted": false } } ``` #### Setup the campaign ### Campaign Creation Create the [campaign](/x-ads-api/campaign-management#post-accounts-account-id-campaigns) and [line item/ad group](/x-ads-api/campaign-management#campaigns). Line items should be created with an `objective` of `VIDEO_VIEWS_PREROLL`, and a `product_type` of `MEDIA`. The `categories` parameter must also be set to the appropriate [advertiser business categories](/x-ads-api/campaign-management#advertiser-business-categories). ```json theme={null} POST https://ads-api.x.com/8/accounts/55w3kv/campaigns?name=test-curated-categories-api&funding\_instrument\_id=103hp9&start\_time=2021-02-10&entity\_status=PAUSED&daily\_budget\_amount\_local\_micro=55000000 { "request": { "params": { "name": "test-curated-categories-api", "start_time": "2021-02-10T00:00:00Z", "daily\_budget\_amount\_local\_micro": 55000000, "funding\_instrument\_id": "103hp9", "entity_status": "PAUSED", "account_id": "55w3kv" } }, "data": { "name": "test-curated-categories-api", "start_time": "2021-02-10T00:00:00Z", "reasons\_not\_servable": \[ "EXPIRED", "PAUSED\_BY\_ADVERTISER", "FUNDING_PROBLEM" \], "servable": false, "purchase\_order\_number": null, "effective_status": "PAUSED", "daily\_budget\_amount\_local\_micro": 55000000, "end_time": null, "funding\_instrument\_id": "103hp9", "duration\_in\_days": null, "standard_delivery": true, "total\_budget\_amount\_local\_micro": null, "id": "f2rp3", "entity_status": "PAUSED", "frequency_cap": null, "currency": "USD", "created_at": "2021-02-08T23:55:38Z", "updated_at": "2021-02-08T23:55:38Z", "deleted": false } } ``` ### Line Item Creation Line items must have the categories parameter set to the appropriate set of IAB categories, retrieved via the [GET content\_categories](/x-ads-api/campaign-management#content-categories) endpoint. These content categories each correspond to one or more IAB categories. In order to use these values, partners must select an appropriate content category and use the entire set of iab\_categories returned in the response, to set the categories parameter on the line items endpoint. Any partial application of the iab\_categories will result in the entire group being set on the line item. For example, ```json theme={null} GET https://ads-api.x.com/8/advertiser\_business\_categories { "request": { "params": {} }, "next_cursor": null, "data": \[ { "id": "1jl", "name": "Consumer Packaged Goods", "iab_categories": \[ "IAB9-26", "IAB9-18", "IAB9-29", "IAB9-1", "IAB9-8", "IAB9-22", "IAB6", "IAB9-5", "IAB9-12", "IAB9-11", "IAB9-23", "IAB9-14", "IAB4", "IAB9-25", "IAB9-17", "IAB23", "IAB9-24", "IAB9-13", "IAB16", "IAB9-4", "IAB9-9", "IAB9-20", "IAB22", "IAB9-28", "IAB9-27", "IAB9-16", "IAB9-31", "IAB9-3", "IAB9-19", "IAB10", "IAB9-2", "IAB9-6", "IAB9-21", "IAB9-10", "IAB9-15" \] }, { "id": "1jm", "name": "Health & Pharma", "iab_categories": \[ "IAB7" \] }, { "id": "1jn", "name": "Alcohol", "iab_categories": \[ "IAB8-5", "IAB8-18" \] }, { "id": "1jo", "name": "Dining", "iab_categories": \[ "IAB8-10", "IAB8-8", "IAB8-7", "IAB8-15", "IAB8-3", "IAB8-4", "IAB8-1", "IAB8-16", "IAB8-12", "IAB8-13", "IAB8-17", "IAB8-11", "IAB8-6", "IAB8-9", "IAB8-2", "IAB8-14" \] }, { "id": "1jp", "name": "Financial Services", "iab_categories": \[ "IAB3", "IAB13", "IAB21" \] }, { "id": "1jq", "name": "Retail", "iab_categories": \[ "IAB18" \] }, { "id": "1jr", "name": "Travel", "iab_categories": \[ "IAB20" \] }, { "id": "1js", "name": "Gaming", "iab_categories": \[ "IAB9-30" \] }, { "id": "1jt", "name": "Technology", "iab_categories": \[ "IAB19-22", "IAB19-13", "IAB19-4", "IAB19-33", "IAB19-26", "IAB19-3", "IAB19-16", "IAB19-9", "IAB19-32", "IAB19-25", "IAB19-30", "IAB19-36", "IAB19-21", "IAB5", "IAB19-12", "IAB19-28", "IAB19-17", "IAB19-8", "IAB19-7", "IAB19-24", "IAB15", "IAB19-11", "IAB19-31", "IAB19-20", "IAB19-15", "IAB19-1", "IAB19-35", "IAB19-29", "IAB19-34", "IAB19-23", "IAB19-2", "IAB19-5", "IAB19-14", "IAB19-27", "IAB19-10", "IAB19-19" \] }, { "id": "1ju", "name": "Telecommunication", "iab_categories": \[ "IAB19-6", "IAB19-18" \] }, { "id": "1jv", "name": "Auto", "iab_categories": \[ "IAB2" \] }, { "id": "1jw", "name": "Media & Entertainment", "iab_categories": \[ "IAB14-8", "IAB14-4", "IAB1-5", "IAB14-7", "IAB1-7", "IAB17", "IAB14-3", "IAB1-1", "IAB12", "IAB1-6", "IAB25-1", "IAB1-2", "IAB14-2", "IAB14-6", "IAB1-3", "IAB1-4", "IAB14-5" \] }, { "id": "1jx", "name": "Politics", "iab_categories": \[ "IAB11-4" \] }, { "id": "1jy", "name": "Gambling", "iab_categories": \[ "IAB9-7" \] }, { "id": "1jz", "name": "Dating", "iab_categories": \[ "IAB14-1" \] }, { "id": "1k0", "name": "Non-Profit", "iab_categories": \[ "IAB11-1", "IAB11-2", "IAB11-3", "IAB11-5" \] } \] } ``` Now, in order to set the `categories` parameter to "Science & Education", the entire set of `iab_categories` i.e., `"IAB5", "IAB15"` must be set for the line item, like so: ```json theme={null} POST https://ads-api.x.com/8/accounts/55w3kv/line\_items?campaign\_id=f2rp3&bid\_amount\_local\_micro=5500000&name=curated-category-line-item&product\_type=MEDIA&placements=ALL\_ON\_TWITTER&objective=PREROLL_VIEWS&categories=IAB3,IAB13,IAB21 { "request": { "params": { "name": "curated-category-line-item", "placements": \[ "ALL\_ON\_TWITTER" \], "bid\_amount\_local_micro": 5500000, "product_type": "MEDIA", "objective": "PREROLL_VIEWS", "account_id": "55w3kv", "categories": \[ "IAB3", "IAB13", "IAB21" \], "campaign_id": "f2rp3" } }, "data": { "bid_type": "MAX", "advertiser\_user\_id": 312226591, "name": "curated-category-line-item", "placements": \[ "ALL\_ON\_TWITTER" \], "start_time": null, "bid\_amount\_local_micro": 5500000, "automatically\_select\_bid": false, "advertiser_domain": null, "target\_cpa\_local_micro": null, "raw_categories": \[ "x", "5l", "9z" \], "primary\_web\_event_tag": null, "charge\_by": "VIEW\_3S_100PCT", "product\_type": "PROMOTED\_TWEETS", "end_time": null, "duration\_in\_days": null, "bid\_unit": "VIEW\_3S_100PCT", "total\_budget\_amount\_local\_micro": null, "objective": "PREROLL_VIEWS", "id": "iqwka", "entity_status": "ACTIVE", "automatic\_tweet\_promotion": null, "optimization": "DEFAULT", "frequency_cap": null, "android\_app\_store_identifier": null, "categories": \[ "IAB3", "IAB13", "IAB21" \], "currency": "USD", "created_at": "2021-02-09T00:00:46Z", "tracking_tags": \[\], "ios\_app\_store_identifier": null, "amplify_config": { "auto_promote": true, "is_open": true }, "updated_at": "2021-02-09T00:00:46Z", "campaign_id": "f2rp3", "creative_source": "MANUAL", "deleted": false } } ``` #### Publisher Selection An advertiser may choose to target either a Content Category or a Curated Category, with additional details described below.  **Note:**  Line items may target either Curated or Content  Categories but not both.  ### Curated Categories Curated Categories allow advertisers to target a preset group of publishers and can be retrieved using the  [GET curated\_categories](/x-ads-api/campaign-management#curated-categories-2) endpoint. These categories are country specific, and therefore require that the line item target the appropriate country based on the country\_code of the category. In order to use one of these categories, the following steps are required in the specific order listed: 1. The line item needs to target the appropriate country based on the country\_code of the Curated Category 2. The [POST line\_item\_curated\_categories](/x-ads-api/campaign-management#line-item-curated-categories) endpoint must be used to associate the line item with a specific curated\_category\_id.  **Note:** Associating a line item with a curated category will also limit the number of publishers that can be denylisted to 5. The full list of user\_id used to denylist specific publishers can be retrieved from the [GET publishers](/x-ads-api/campaign-management#publishers) endpoint. Additionally, a given line item may target no more than one Curated Category at a time. The following example illustrates how to associate a curated category id: b0xt which is only available in the US, with the line item created in the previous step. First, the line item’s targeting criteria is set to the the value 96683cc9126741d ```json theme={null} GET https://ads-api.x.com/8/targeting\_criteria/locations?country\_code=US&location_type=COUNTRIES { "data": \[ { "name": "United States", "country_code": "US", "location_type": "COUNTRIES", "targeting_value": "96683cc9126741d1", "targeting_type": "LOCATION" } \], "request": { "params": { "location_type": "COUNTRIES", "country_code": "US" } }, "next_cursor": null } POST https://ads-api.x.com/8/batch/accounts/55w3kv/targeting_criteria \[ { "operation_type": "Create", "params": { "line\_item\_id": "iqwka", "targeting_type": "LOCATION", "targeting_value": "96683cc9126741d1", "operator_type": "EQ" } } \] { "data": \[ { "line\_item\_id": "iqwka", "name": "United States", "raw_negated": false, "raw\_targeting\_value": "2", "id": "rv9hmc", "raw\_targeting\_type": "GEO", "raw\_operator\_type": "EQUAL_TO", "location_type": "COUNTRIES", "operator_type": "EQ", "created_at": "2021-02-09T00:06:28Z", "targeting_value": "96683cc9126741d1", "updated_at": "2021-02-09T00:06:28Z", "deleted": false, "targeting_type": "LOCATION" } \], "request": \[ { "params": { "line\_item\_id": "iqwka", "account_id": "55w3kv", "operator_type": "EQ", "targeting_value": "96683cc9126741d1", "targeting_type": "LOCATION" }, "operation_type": "Create" } \] } POST https://ads-api.x.com/8/accounts/55w3kv/line\_item\_curated\_categories?line\_item\_id=iqwka&curated\_category_id=9ddrgesiap6o { "request": { "params": { "curated\_category\_id": "9ddrgesiap6o", "line\_item\_id": "iqwka", "account_id": "55w3kv" } }, "data": { "line\_item\_id": "iqwka", "curated\_category\_id": "9ddrgesiap6o", "id": "xq", "created_at": "2021-03-30T17:26:42Z", "updated_at": "2021-03-30T17:26:42Z", "deleted": false } } ``` ### Content Categories Content categories, also referred to as Standard Categories can be retrieved from the [GET curated\_categories](/x-ads-api/campaign-management#get-accounts-account-id-curated-categories) endpoint. These categories can then be targeted by the line item using the batch targeting criteria endpoints. The following example illustrates how to select a particular content category, id: sr which maps to “News & Current Events” and apply it to the line item. **Note**: The entire set of iab\_categories in the [GET curated\_categories](/x-ads-api/campaign-management#get-accounts-account-id-curated-categories) response must be targeted via the targeting criteria endpoint. Failing to do so will result in a validation error.  ```json theme={null} GET https://ads-api.x.com/8/content_categories { "name": "News & Current Events", "id": "sr", "iab_categories": \[ "IAB12", "IAB14" \], "publishers\_in\_last\_thirty\_days": 124, "videos\_monetized\_in\_last\_thirty_days": 5429 } } POST https://ads-api.x.com/8/batch/accounts/55w3kv/targeting_criteria \[ { "operation_type": "Create", "params": { "line\_item\_id": "iqwls", "targeting\_type": "IAB\_CATEGORY", "targeting_value": "IAB12", "operator_type": "EQ" } }, { "operation_type": "Create", "params": { "line\_item\_id": "iqwls", "targeting\_type": "IAB\_CATEGORY", "targeting_value": "IAB14", "operator_type": "EQ" } } \] { "data": \[ { "line\_item\_id": "iqwls", "name": "News", "raw_negated": false, "raw\_targeting\_value": "5h", "id": "saib9p", "raw\_targeting\_type": "IAB_CATEGORY", "raw\_operator\_type": "EQUAL_TO", "operator_type": "EQ", "created_at": "2021-03-30T17:35:50Z", "targeting_value": "IAB12", "updated_at": "2021-03-30T17:35:50Z", "deleted": false, "targeting\_type": "IAB\_CATEGORY" }, { "line\_item\_id": "iqwls", "name": "Society", "raw_negated": false, "raw\_targeting\_value": "5y", "id": "saib9q", "raw\_targeting\_type": "IAB_CATEGORY", "raw\_operator\_type": "EQUAL_TO", "operator_type": "EQ", "created_at": "2021-03-30T17:35:50Z", "targeting_value": "IAB14", "updated_at": "2021-03-30T17:35:50Z", "deleted": false, "targeting\_type": "IAB\_CATEGORY" } \], "request": \[ { "params": { "line\_item\_id": "iqwls", "account_id": "55w3kv", "operator_type": "EQ", "targeting_value": "IAB12", "targeting\_type": "IAB\_CATEGORY" }, "operation_type": "Create" }, { "params": { "line\_item\_id": "iqwls", "account_id": "55w3kv", "operator_type": "EQ", "targeting_value": "IAB14", "targeting\_type": "IAB\_CATEGORY" }, "operation_type": "Create" } \] } ``` ##### Associate the account media (video) with the line item Use the [POST accounts/:account\_id/media\_creatives](/x-ads-api/campaign-management#post-accounts-account-id-media-creatives) endpoint to associate the video with an ad group. ```json theme={null} POST https://ads-api.x.com/8/accounts/55w3kv/media_creatives line\_item\_id=4bii5&account\_media\_id=knb { "data":{ "account\_media\_id":"74g", "approval_status":"ACCEPTED", "created_at":"2016-02-11T22:23:23Z", "deleted":false, "id":"qeq", "landing_url":null, "line\_item\_id":"4bii5", "serving_status":"ACTIVE", "updated_at":"2016-02-11T22:23:23Z" }, "request":{ "params":{ "line\_item\_id":"4bii5", "account\_media\_id":"knb" } } } ``` #### Set the CTA and destination URL It is important to note that unlike most other campaigns on X, the `VIDEO_VIEWS_PREROLL` objective does not utilize Promoted Tweets or Cards. Instead, the video creative is associated with your ad group (line item) and the CTA information is associated with a `preroll_call_to_action` entity. The [POST accounts/:account\_id/preroll\_call\_to\_action](/x-ads-api/creatives#preroll-call-to-actions) endpoint allows you to control the button CTA and the destination URL. ```json theme={null} POST https://ads-api.x.com/8/accounts/55w3kv/preroll\_call\_to_action line\_item\_id=4bii5&call\_to\_action=VISIT\_SITE&call\_to\_action\_url=https%3A%2F%2Fx.com%2FAdsAPI { "data":{ "id":"aaa111", "line\_item\_id":"4bii5", "call\_to\_action":"WATCH_NOW", "call\_to\_action_url":"https://x.com/AdsAPI", "created_at":"2016-02-11T22:23:23Z", "updated_at":"2016-02-11T22:23:23Z", "deleted":false }, "request":{ "params":{ "line\_item\_id":"4bii5", "call\_to\_action":"VISIT_SITE", "call\_to\_action_url":"https://x.com/AdsAPI" } } } ``` #### Set targeting criteria The targetting criterion utilized for pre-roll video ads is only avaialble using our batch targeting criteria endpoint [POST batch/accounts/:account\_id/targeting\_criteria](/x-ads-api/campaign-management#post-batch-accounts-account-id-targeting-criteria). Use `CONTENT_PUBLISHER_USER` as negated targeting to exclude the ad from being paired with a set of users. Provide the X `user_id`  or publisher\_user\_id for the handles to exclude. The [GET publishers](/x-ads-api/campaign-management#publishers) endpoint can be used to retrieve the list of user\_id to exclude for Content Categories. The publisher\_user\_id returned in the [GET curated\_categories](/x-ads-api/campaign-management#curated-categories-2) response can be used to retrieve a similar exclusion list for Curated Categories. **Note:** A maximum of 5 publisher\_user\_id can be excluded for Curated Categories and 50 user\_id for Content Categories. ```json theme={null} POST https://ads-api.x.com/8/batch/accounts/55w3kv/targeting_criteria \[ { "operation_type": "Create", "params": { "line\_item\_id": "iqwls", "targeting\_type": "CONTENT\_PUBLISHER_ID", "targeting_value": "1917731", "operator_type": "NE" } } \] { "data": \[ { "line\_item\_id": "iqwka", "name": "realsaltlake", "raw_negated": true, "raw\_targeting\_value": "aajwo", "id": "sajk32", "raw\_targeting\_type": "CONTENT_PUBLISHER", "raw\_operator\_type": "EQUAL_TO", "operator_type": "NE", "created_at": "2021-03-30T18:02:32Z", "targeting_value": 17288520, "updated_at": "2021-03-30T18:02:32Z", "deleted": false, "targeting\_type": "CONTENT\_PUBLISHER_USER" } \], "request": \[ { "params": { "line\_item\_id": "iqwka", "account_id": "55w3kv", "operator_type": "NE", "targeting_value": "17288520", "targeting\_type": "CONTENT\_PUBLISHER_USER" }, "operation_type": "Create" } \] } ``` #### Launch campaign When you’re ready to launch your campaign, simply un-pause using [PUT accounts/:account\_id/campaigns/:id](/x-ads-api/campaign-management#put-accounts-account-id-line-items-line-item-id). PUT [https://ads-api.x.com/8/accounts/55w3kv/campaigns/f2rp3](https://ads-api.x.com/8/accounts/55w3kv/campaigns/f2rp3)? entity\_status=ACTIVE ```json theme={null} { "request": { "params": { "campaign_id": "f2rp3", "account_id": "55w3kv" } }, "data": { "name": "test-curated-categories-api", "start_time": "2021-02-10T00:00:00Z", "reasons\_not\_servable": \[ \], "servable": false, "purchase\_order\_number": null, "effective_status": "ACTIVE", "daily\_budget\_amount\_local\_micro": 55000000, "end_time": null, "funding\_instrument\_id": "103hp9", "duration\_in\_days": null, "standard_delivery": true, "total\_budget\_amount\_local\_micro": null, "id": "f2rp3", "entity_status": "ACTIVE", "frequency_cap": null, "currency": "USD", "created_at": "2021-02-08T23:55:38Z", "updated_at": "2021-02-08T23:55:38Z", "deleted": false } } ``` #### Analytics Analytics for `VIDEO_VIEWS_PREROLL` campaigns are available using our stats endpoints. ### Keyword Targeting in Timelines Keyword targeting is fundamental to our Promoted Tweets products, giving campaigns better reach. Keyword targeting in timeline enables platforms to target X users based on keywords in their recent Tweets. For example, if an advertiser is targeting the unordered keyword combination “plan + trip”, and a user Tweets, “I’m starting to plan my trip to Cabo, any suggestions?” while the campaign is running, that user may soon afterward see the advertiser’s Promoted Tweet. #### How does it work? TL;DR: from an API standpoint, this change is quite simple: you can now target keywords on Promoted Tweets in Timeline. Just set the `targeting_type` to `unordered_keywords` or `phrase_keywords` for line items. #### Quick Start Guide * Create a new line item with the placement set to include either `ALL_ON_TWITTER` or `TWITTER_TIMELINE` [POST accounts/:account\_id/line\_items](https://dev.x.com/ads/reference/post/accounts/%3Aaccount_id/line_items) * Create the targeting criteria for this newly created line item with either `BROAD_KEYWORD` and set your keyword value(s). [POST accounts/:account\_id/targeting\_criteria](https://dev.x.com/ads/reference/post/accounts/%3Aaccount_id/targeting_criteria) * You can update the keywords with [PUT accounts/:account\_id/targeting\_criteria](https://dev.x.com/ads/reference/put/accounts/%3Aaccount_id/targeting_criteria) * Once your campaign is running, get the stats on your line item to gauge performance. [GET stats/accounts/:account\_id](https://dev.x.com/ads/reference/get/stats/accounts/%3Aaccount_id) ## API Reference ### Accounts #### GET accounts[](#get-accounts "Permalink to this headline") Retrieve details for some or all advertising-enabled accounts the authenticating user has access to. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_ids
*optional* | Scope the response to just the desired account IDs by specifying a comma-separated list of identifiers.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | q
*optional* | An optional query to scope resource by `name`.

**Note**: This performs case-insensitive prefix matching.

Type: string

Min, Max length: `1`, `255` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/fundamentals/sorting) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | ### Example Request[](#example-request "Permalink to this headline") ```json theme={null} GET https://ads-api.x.com/12/accounts?account_ids=18ce54d4x5t ``` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_ids": [ "18ce54d4x5t" ] } }, "next_cursor": null, "data": [ { "name": "API McTestface", "business_name": null, "timezone": "America/Los_Angeles", "timezone_switch_at": "2016-07-21T07:00:00Z", "id": "18ce54d4x5t", "created_at": "2016-07-21T22:42:09Z", "updated_at": "2017-07-06T16:51:04Z", "business_id": null, "approval_status": "ACCEPTED", "deleted": false } ] } ``` #### GET accounts/:account\_id[](#get-accounts-account-id "Permalink to this headline") Retrieve a specific account that the authenticating user has access to. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding GET accounts.

The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t" } }, "data": { "name": "API McTestface", "business_name": null, "timezone": "America/Los_Angeles", "timezone_switch_at": "2016-07-21T07:00:00Z", "id": "18ce54d4x5t", "created_at": "2016-07-21T22:42:09Z", "updated_at": "2017-07-06T16:51:04Z", "industry_type": "TRAVEL", "business_id": null, "approval_status": "ACCEPTED", "deleted": false } } ``` #### POST accounts[](#post-accounts "Permalink to this headline") Note: **SANDBOX ONLY** Create an ads account in the sandbox environment. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api-sandbox.x.com/12/accounts` **Parameters[](#parameters "Permalink to this headline")** None **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api-sandbox.x.com/12/accounts` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": {} }, "next_cursor": null, "data": [ { "name": "Sandbox account", "business_name": null, "timezone": "America/Los_Angeles", "timezone_switch_at": null, "id": "gq12fh", "created_at": "2016-07-18T23:02:20Z", "updated_at": "2016-07-18T23:02:20Z", "business_id": null, "approval_status": "ACCEPTED", "deleted": false } ] } ``` #### PUT accounts/:account\_id[](#put-accounts-account-id "Permalink to this headline") Updates the account name and/or industry type. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts).
The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | name
*optional* | The name of account.

Type: string

Example: `API McTestface` | | industry\_type
*optional* | Industry that the account is associated with.

Type: string

Possible values: `AGENCY`, `BUSINESS_TO_BUSINESS`, `ONLINE_SERVICES`, `EDUCATION`, `FINANCIAL`, `HEALTH`, `GOVERNMENT`, `MEDIA`, `MOBILE`, `RESTAURANT`, `RETAIL`, `TECHNOLOGY`, `TRAVEL`, `OTHER` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t?name='API McTestface 2'&industry_type=TECHNOLOGY` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t" "name"": "API McTestface 2", "industry_type": "TECHNOLOGY" } }, "data": { "name": "API McTestface 2", "business_name": null, "timezone": "America/Los_Angeles", "timezone_switch_at": "2016-07-21T07:00:00Z", "id": "18ce54d4x5t", "created_at": "2016-07-21T22:42:09Z", "updated_at": "2017-07-06T16:51:04Z", "industry_type": "TECHNOLOGY", "business_id": null, "approval_status": "ACCEPTED", "deleted": false } } ``` #### DELETE accounts/:account\_id[](#delete-accounts-account-id "Permalink to this headline") Note: **SANDBOX ONLY** Delete an ads account in the sandbox environment. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api-sandbox.x.com/12/accounts/:account_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts).

The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api-sandbox.x.com/12/accounts/gq12fh` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "name": "Sandbox account", "timezone": "America/Los_Angeles", "timezone_switch_at": null, "id": "gq12fh", "created_at": "2016-07-18T23:02:20Z", "updated_at": "2017-08-23T18:21:10Z", "approval_status": "ACCEPTED", "deleted": true }, "request": { "params": { "account_id": "gq12fh" } } } ``` ### Account Apps [Run in Postman ❯](https://app.getpostman.com/run-collection/1d12b9fc623b8e149f87) #### GET account\_apps[](#get-account-apps "Permalink to this headline") Retrieve details for all mobile apps that are associated with the specified ad account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/account_apps` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/fundamentals/sorting) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/account_apps` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_ids": [ "18ce54d4x5t" ] } }, "next_cursor": null, "data": [ { "app_store_identifier": "com.twitter.android", "conversion_tracking_enabled": false, "deep_link_pattern": "twitter://", "id": "4x", "created_at": "2019-06-20T22:36:16Z", "updated_at": "2021-10-19T20:05:29Z", "os_type": "Android", "deleted": false } ] } ``` ### Account History #### GET accounts/:account\_id/account\_history[](#get-accounts-account-id-account-history "Permalink to this headline") Retrieve a summary of changes made to the `entity_id` specified in the request. **Note**: This endpoint is currently in beta and requires allowlisting. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/account_history` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | entity\_type
*required* | The entity type to retrieve data for.

Type: enum

Example: `PROMOTED_TWEET`
Possible values: `CAMPAIGN`, `LINE_ITEM`, `PROMOTED_TWEET`, `TARGETING_CRITERIA`, `PROMOTED_ACCOUNT` | | entity\_id
*required* | The specific entitiy to retrieve data for.

Type: string

Example: `8u94t` | | start\_time
*required* | Scopes the retrieved data to the specified start time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-19T07:00:00Z` | | end\_time
*required* | Scopes the retrieved data to the specified end time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

**Note**: Must be expressed in whole hours (0 minutes and 0 seconds).

Type: string

Example: `2017-05-26T07:00:00Z` | | user\_id
*optional* | Scopes the response to a specific user.

Type: long

Example: `3271358660` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/account_history?entity_type=CAMPAIGN&entity_id=fc3h5&count=1` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "entity": "CAMPAIGN", "entity_id": "fc3h5", "count": 1 } }, "next_cursor": "1r2407sb4lc", "data": [ { "change_by": { "user_id": "982978172", "platform": "API_OTHER" }, "changes": {}, "change_time": "2021-04-02T20:55:42Z", "entity_id": "fc3h5", "entity": "CAMPAIGN", "entity_data": { "name": "test_campaign", "start_time": "2021-04-02T18:59:11Z", "purchase_order_number": null, "daily_budget_amount_local_micro": 100000000, "end_time": null, "duration_in_days": null, "standard_delivery": true, "total_budget_amount_local_micro": 100000000, "entity_status": "ACTIVE", "frequency_cap": null, "created_at": "2021-04-02T20:55:42Z", "updated_at": "2021-04-02T20:55:42Z", "deleted": false }, "change_type": "CREATE" } ] } ``` ### Advertiser Business Categories #### GET advertiser\_business\_categories[](#get-advertiser-business-categories "Permalink to this headline") Request the valid advertiser business `categories` for Ad Groups (`line_items`) to describe an advertiser's brand to publishers. Note: These categories apply only to `line_items` with the `PREROLL_VIEWS` objective and are separate from the `content_categories` used for targeting criteria. Each `advertiser_business_categories` represents a collection of [IAB Categories](/x-ads-api/campaign-management#iab-categories). When creating an Ad Group with the `PREROLL_VIEWS` objective, one or two `advertiser_business_categories` must be set for the Ad Group. This can be done by setting the value of the `categories` request parameter on the [line item](/x-ads-api/campaign-management#line-items) endpoint to the set of corresponding `iab_categories` available through this endpoint. Additional details can be found in the [Video Views Preroll Objective Guide](/x-ads-api/campaign-management#video-views-preroll-objective) **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/advertiser_business_categories` **Parameters[](#parameters "Permalink to this headline")** No request parameters **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/advertiser_business_categories` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": {} }, "next_cursor": null, "data": [ { "id": "1jl", "name": "Consumer Packaged Goods", "iab_categories": [ "IAB9-26", "IAB9-18", "IAB9-29", "IAB9-1", "IAB9-8", "IAB9-22", "IAB6", "IAB9-5", "IAB9-12", "IAB9-11", "IAB9-23", "IAB9-14", "IAB4", "IAB9-25", "IAB9-17", "IAB23", "IAB9-24", "IAB9-13", "IAB16", "IAB9-4", "IAB9-9", "IAB9-20", "IAB22", "IAB9-28", "IAB9-27", "IAB9-16", "IAB9-31", "IAB9-3", "IAB9-19", "IAB10", "IAB9-2", "IAB9-6", "IAB9-21", "IAB9-10", "IAB9-15" ] }, { "id": "1jm", "name": "Health & Pharma", "iab_categories": [ "IAB7" ] }, { "id": "1jn", "name": "Alcohol", "iab_categories": [ "IAB8-5", "IAB8-18" ] }, { "id": "1jo", "name": "Dining", "iab_categories": [ "IAB8-10", "IAB8-8", "IAB8-7", "IAB8-15", "IAB8-3", "IAB8-4", "IAB8-1", "IAB8-16", "IAB8-12", "IAB8-13", "IAB8-17", "IAB8-11", "IAB8-6", "IAB8-9", "IAB8-2", "IAB8-14" ] }, { "id": "1jp", "name": "Financial Services", "iab_categories": [ "IAB3", "IAB13", "IAB21" ] }, { "id": "1jq", "name": "Retail", "iab_categories": [ "IAB18" ] }, { "id": "1jr", "name": "Travel", "iab_categories": [ "IAB20" ] }, { "id": "1js", "name": "Gaming", "iab_categories": [ "IAB9-30" ] }, { "id": "1jt", "name": "Technology", "iab_categories": [ "IAB19-22", "IAB19-13", "IAB19-4", "IAB19-33", "IAB19-26", "IAB19-3", "IAB19-16", "IAB19-9", "IAB19-32", "IAB19-25", "IAB19-30", "IAB19-36", "IAB19-21", "IAB5", "IAB19-12", "IAB19-28", "IAB19-17", "IAB19-8", "IAB19-7", "IAB19-24", "IAB15", "IAB19-11", "IAB19-31", "IAB19-20", "IAB19-15", "IAB19-1", "IAB19-35", "IAB19-29", "IAB19-34", "IAB19-23", "IAB19-2", "IAB19-5", "IAB19-14", "IAB19-27", "IAB19-10", "IAB19-19" ] }, { "id": "1ju", "name": "Telecommunication", "iab_categories": [ "IAB19-6", "IAB19-18" ] }, { "id": "1jv", "name": "Auto", "iab_categories": [ "IAB2" ] }, { "id": "1jw", "name": "Media & Entertainment", "iab_categories": [ "IAB14-8", "IAB14-4", "IAB1-5", "IAB14-7", "IAB1-7", "IAB17", "IAB14-3", "IAB1-1", "IAB12", "IAB1-6", "IAB25-1", "IAB1-2", "IAB14-2", "IAB14-6", "IAB1-3", "IAB1-4", "IAB14-5" ] }, { "id": "1jx", "name": "Politics", "iab_categories": [ "IAB11-4" ] }, { "id": "1jy", "name": "Gambling", "iab_categories": [ "IAB9-7" ] }, { "id": "1jz", "name": "Dating", "iab_categories": [ "IAB14-1" ] }, { "id": "1k0", "name": "Non-Profit", "iab_categories": [ "IAB11-1", "IAB11-2", "IAB11-3", "IAB11-5" ] } ] } ``` ### Audience Estimate POST accounts/:account\_id/audience\_estimate[](#post-accounts-account-id-audience-estimate "Permalink to this headline") #### Determine the approximate audience size of your campaigns. This endpoint accepts an array of JSON objects containing the parameters for the targeting criteria objects. A list of required and optional targeting criteria parameters are available on the [POST accounts/:account\_id/targeting\_criteria](/x-ads-api/campaign-management#post-accounts-account-id-targeting-criteria) endpoint. Requests must be HTTP POST with a JSON content body with a `Content-Type: application/json` header. **Note**: It is required that you specify at least one **primary** targeting criterion; you can see a list of all primary targeting criteria in our [campaigns targeting page](/x-ads-api/campaign-management#targeting). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/audience_estimate` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | targeting\_criteria
*required* | A JSON object containing all the parameters for the targeting criteria objects. A list of required and optional targeting criteria parameters are available on the [POST accounts/:account\_id/targeting\_criteria](/x-ads-api/campaign-management#post-accounts-account-id-targeting-criteria) endpoint. | | operator\_type
*optional* | Specify the relationship that the targeting criterion should have. For example, to set negated targeting, use `operator_type=NE`.

Type: enum

Possible values: `EQ`, `NE`

Default: `EQ` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/audience_estimate` ```json theme={null} { "targeting_criteria": [ { "targeting_type": "BROAD_KEYWORD", "targeting_value": "nba", "operator_type": "EQ" }, { "targeting_type": "BROAD_KEYWORD", "targeting_value": "tech", "operator_type": "NE" }, { "targeting_type": "LOCATION", "targeting_value": "96683cc9126741d1", "operator_type": "EQ" }, { "targeting_type": "SIMILAR_TO_FOLLOWERS_OF_USER", "targeting_value": "14230524" }, { "targeting_type": "SIMILAR_TO_FOLLOWERS_OF_USER", "targeting_value": "90420314" } ] } ``` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "targeting_criteria": null, "account_id": "18ce54d4x5t" } }, "data": { "audience_size": { "min": 38236294, "max": 42261167 } } } ``` ### Authenticated User Access #### GET accounts/:account\_id/authenticated\_user\_access[](#get-accounts-account-id-authenticated-user-access "Permalink to this headline") Retrieve the permissions of the currently authenticated user (access\_token) as they relate to the specified ads account. These permissions match those exposed on ads.x.com. Possible values include: * `ACCOUNT_ADMIN`: Full access to modify campaigns and view stats, including the ability to add or remove users and change settings * `AD_MANAGER`: Full access to modify campaigns and view stats, but cannot add or remove users or change settings * `CREATIVE_MANAGER`: Access to modify creatives and view previews, but no access to create or modify campaigns * `CAMPAIGN_ANALYST`: Access to view campaigns and view stats, but no access to create or modify campaigns * `ANALYST` ("Organic Analyst" on ads.x.com): Access to view organic analytics and audience insights, but no access to create, modify, or view campaigns * `PARTNER_AUDIENCE_MANAGER`: API-only access to view and modify data partner audiences, but no access to campaigns, creatives, or other audience types. In addition, the `TWEET_COMPOSER` permission indicates that the authenticated user can create nullcasted (or "Promoted-only") Tweets on behalf of the advertiser. This is only available for users with `ACCOUNT_ADMIN`, `AD_MANAGER`, or `CREATIVE_MANAGER` access. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/authenticated_user_access` **Parameters[](#parameters "Permalink to this headline")** None **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/authenticated_user_access` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "user_id": "2417045708", "permissions": [ "ACCOUNT_ADMIN", "TWEET_COMPOSER" ] }, "request": { "params": { "account_id": "18ce54d4x5t" } } } ``` ### Bidding Rules #### GET bidding\_rules[](#get-bidding-rules "Permalink to this headline") Retrieve the bidding rules for some or all currencies. The response will indicate the minimum and maximum CPE (cost-per-engagement) bids. While these bidding rules change rarely, it is suggested that your systems refresh from these endpoints at least monthly. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/bidding_rules` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | currency
*optional* | The type of a currency to filter results by, identified using [ISO-4217](https://en.wikipedia.org/wiki/ISO_4217). This is a three-letter string "USD" or "EUR". Omit this parameter to retrieve all bidding rules. associated with the authenticating user.

Type: string

Example: `USD` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/bidding_rules?currency=USD` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "currency": "USD" } }, "data_type": "bidding_rule", "data": [ { "currency": "USD", "minimum_cpe_bid_local_micro": 10000, "maximum_cpe_bid_local_micro": 1000000000, "minimum_denomination": 10000 } ], "total_count": 1 } ``` ### Campaigns #### GET accounts/:account\_id/campaigns[](#get-accounts-account-id-campaigns "Permalink to this headline") Retrieve details for some or all campaigns associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/campaigns` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_ids
*optional* | Scope the response to just the desired campaigns by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `8wku2` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | funding\_instrument\_ids
*optional* | Scope the response to just the campaigns under specific funding instruments by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `lygyi` | | q
*optional* | An optional query to scope resource by `name`.

Type: string

Min, Max length: `1`, `255` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_draft
*optional* | Include draft campaigns results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/campaigns?campaign_ids=8wku2` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "campaign_ids": [ "8wku2" ] } }, "next_cursor": null, "data": [ { "name": "test", "budget_optimization": "CAMPAIGN", "reasons_not_servable": [ "PAUSED_BY_ADVERTISER", "INCOMPLETE" ], "servable": false, "purchase_order_number": null, "effective_status": "UNKNOWN", "daily_budget_amount_local_micro": 10000000, "funding_instrument_id": "lygyi", "duration_in_days": null, "standard_delivery": false, "total_budget_amount_local_micro": null, "id": "8wku2", "entity_status": "PAUSED", "frequency_cap": null, "currency": "USD", "created_at": "2022-06-03T21:38:07Z", "updated_at": "2022-06-03T21:38:07Z", "deleted": false } ] } ``` #### GET accounts/:account\_id/campaigns/:campaign\_id[](#get-accounts-account-id-campaigns-campaign-id "Permalink to this headline") Retrieve a specific campaign associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/campaigns/:campaign_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_id
*required* | A reference to the campaign you are operating with in the request.

Type: string

Example: `8wku2` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/campaigns/8wku2` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "campaign_id": "8wku2", "account_id": "18ce54d4x5t" } }, "data": { "name": "test", "budget_optimization": "CAMPAIGN", "reasons_not_servable": [ "PAUSED_BY_ADVERTISER", "INCOMPLETE" ], "servable": false, "purchase_order_number": null, "effective_status": "UNKNOWN", "daily_budget_amount_local_micro": 10000000, "funding_instrument_id": "lygyi", "duration_in_days": null, "standard_delivery": false, "total_budget_amount_local_micro": null, "id": "8wku2", "entity_status": "PAUSED", "frequency_cap": null, "currency": "USD", "created_at": "2022-06-03T21:38:07Z", "updated_at": "2022-06-03T21:38:07Z", "deleted": false } } ``` #### POST accounts/:account\_id/campaigns[](#post-accounts-account-id-campaigns "Permalink to this headline") Create a new campaign associated with the current account. **Note**: There is a default limit of 200 active campaigns per account. However, there is no limit to the number of inactive campaigns. This limit can be raised to 8,000 active campaigns. To enable the higher limit, the advertiser must make the request to their X Account Manager. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/campaigns` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | funding\_instrument\_id
*required* | The identifier for the funding instrument to create the campaign under.

Type: string

Example: `lygyi` | | name
*required* | The name for the campaign. Maximum length: 255 characters.

Type: string

Example: `demo` | | budget\_optimization
*optional* | Select the type of budget optimization to be applied

Type: enum

Default: `CAMPAIGN`
Possible values: `CAMPAIGN`, `LINE_ITEM` | | daily\_budget\_amount\_local\_micro
*sometimes required* | The daily budget amount to be allocated to the campaign. The currency associated with the specified funding instrument will be used. For USD, \$5.50 is represented as 5500000.

**Note**: This should be less than or equal to the `total_budget_amount_local_micro` and is required for most Funding Insturment types.

Type: long

Example: `5500000` | | entity\_status
*optional* | The campaign status.

Type: enum

Default: `ACTIVE`
Possible values: `ACTIVE`, `DRAFT`, `PAUSED` | | purchase\_order\_number
*optional* | The booking reference number. Use this field to help with invoice reconciliation. Maximum length: 50 characters.

Type: string

Example: `D00805843` | | standard\_delivery
*optional* | Enable standard or accelerated delivery. See [Budget Pacing](/x-ads-api/campaign-management#budget-pacing) for more information on standard versus accelerated delivery. Only available when `budget_optimization` is set to `CAMPAIGN`.

Type: boolean

Default: `true`
Possible values: `true`, `false` | | total\_budget\_amount\_local\_micro
*optional* | The total budget amount to be allocated to the campaign. The currency associated with the specified funding instrument will be used. For USD, \$37.50 is represented as 37500000.

Type: long

Example: `37500000` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/campaigns?funding_instrument_id=lygyi&name=demo&daily_budget_amount_local_micro=140000000&entity_status=PAUSED&budget_optimization=CAMPIAGN&standard_delivery=false` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "name": "demo", "budget_optimization": "CAMPAIGN", "daily_budget_amount_local_micro": 140000000, "funding_instrument_id": "lygyi", "standard_delivery": false, "entity_status": "PAUSED", "account_id": "18ce54d4x5t" } }, "data": { "name": "demo", "budget_optimization": "CAMPAIGN", "reasons_not_servable": [ "PAUSED_BY_ADVERTISER", "INCOMPLETE" ], "servable": false, "purchase_order_number": null, "effective_status": "UNKNOWN", "daily_budget_amount_local_micro": 140000000, "funding_instrument_id": "lygyi", "duration_in_days": null, "standard_delivery": false, "total_budget_amount_local_micro": null, "id": "hwtbm", "entity_status": "PAUSED", "frequency_cap": null, "currency": "USD", "created_at": "2022-06-03T21:38:07Z", "updated_at": "2022-06-03T21:38:07Z", "deleted": false } } ``` #### POST batch/accounts/:account\_id/campaigns[](#post-batch-accounts-account-id-campaigns "Permalink to this headline") Allows the batch creation of new [campaigns](#post-accounts-account-id-campaigns) with a single request. **Batch Requests** * The current maximum batch size is 40. * All parameters are sent in the request body and a `Content-Type` of `application/json` is required. * Batch requests fail or succeed together as a group and all API responses for both error and success preserve the item order of the initial request. **Batch Responses** Batch API responses return an ordered collection of items. Otherwise, they are identical in structure to their corresponding single-item endpoints. **Batch Errors** * Request-level errors (eg. max batch size exceeded) are shown in the response under the `errors` object. * Item-level errors (eg. missing required campaign parameter) are shown in the response under the `operation_errors` object. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/batch/accounts/:account_id/campaigns` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | operation\_type
*required* | The per item operation type being performed.

Type: enum

Possible values: `Create`, `Delete`, `Update` | | params
*required* | A JSON object containing all the parameters for the campaign objects. For a list of required and optional campaign parameters, see [here](#post-accounts-account-id-campaigns). | **Example Request[](#example-request "Permalink to this headline")** `POST 'Content-Type: application/json' https://ads-api.x.com/12/batch/accounts/18ce54d4x5t/campaigns` ```json theme={null} [ { "operation_type":"Create", "params":{ "name":"batch campaigns", "funding_instrument_id":"lygyi", "daily_budget_amount_local_micro":140000000, "entity_status":"PAUSED", "budget_optimization":"CAMPAIGN" } } ] ``` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "batch campaigns", "budget_optimization": "CAMPAIGN", "reasons_not_servable": [ "PAUSED_BY_ADVERTISER", "INCOMPLETE" ], "servable": false, "purchase_order_number": null, "effective_status": "UNKNOWN", "daily_budget_amount_local_micro": 140000000, "funding_instrument_id": "lygyi", "duration_in_days": null, "standard_delivery": false, "total_budget_amount_local_micro": null, "id": "8yn7m", "entity_status": "PAUSED", "frequency_cap": null, "currency": "USD", "created_at": "2022-06-03T21:38:07Z", "updated_at": "2022-06-03T21:38:07Z", "deleted": false } ], "request": [ { "params": { "name": "batch campaigns", "funding_instrument_id": "lygyi", "daily_budget_amount_local_micro": 140000000, "entity_status": "PAUSED", "budget_optimization":"CAMPAIGN", "account_id": "18ce54d4x5t" }, "operation_type": "Create" } ] } ``` #### PUT accounts/:account\_id/campaigns/:campaign\_id[](#put-accounts-account-id-campaigns-campaign-id "Permalink to this headline") Update the specified campaign associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/campaigns/:campaign_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_id
*required* | A reference to the campaign you are operating with in the request.

Type: string

Example: `8wku2` | | budget\_optimization
*optional* | Select the type of budget optimization to be applied

Type: enum

Default: `CAMPAIGN`
Possible values: `CAMPAIGN`, `LINE_ITEM` | | daily\_budget\_amount\_local\_micro
*optional* | The daily budget amount to be allocated to the campaign. The currency associated with the specified funding instrument will be used. For USD, \$5.50 is represented as 5500000. When not provided the campaign will spend evenly based upon total budget and for duration of campaign flight time.

**Note**: This should be less than or equal to the `total_budget_amount_local_micro`.

Type: long

Example: `5500000` | | entity\_status
*optional* | The campaign status.

Type: enum

Possible values: `ACTIVE`, `PAUSED` | | name
*optional* | The name for the campaign. Maximum length: 255 characters.

Type: string

Example: `demo` | | purchase\_order\_number
*optional* | The booking reference number. Use this field to help with invoice reconciliation. Maximum length: 50 characters.

Type: string

Example: `D00805843` | | standard\_delivery
*optional* | Enable standard or accelerated delivery. See [Budget Pacing](/x-ads-api/campaign-management#budget-pacing) for more information on standard versus accelerated delivery. Only available when `budget_optimization` is set to `CAMPAIGN`.

Type: boolean

Default: `true`
Possible values: `true`, `false` | | total\_budget\_amount\_local\_micro
*optional* | The total budget amount to be allocated to the campaign. The currency associated with the specified funding instrument will be used. For USD, \$37.50 is represented as 37500000.

Type: long

Example: `140000000` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/campaigns/8wku2?total_budget_amount_local_micro=140000000` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "campaign_id": "8wku2", "daily_budget_amount_local_micro": 140000000, "account_id": "18ce54d4x5t" } }, "data": { "name": "test", "budget_optimization": "CAMPAIGN", "reasons_not_servable": [ "PAUSED_BY_ADVERTISER", "INCOMPLETE" ], "servable": false, "purchase_order_number": null, "effective_status": "UNKNOWN", "daily_budget_amount_local_micro": 140000000, "funding_instrument_id": "lygyi", "duration_in_days": null, "standard_delivery": false, "total_budget_amount_local_micro": null, "id": "8wku2", "entity_status": "PAUSED", "frequency_cap": null, "currency": "USD", "created_at": "2022-06-03T21:38:07Z", "updated_at": "2022-06-03T21:53:54Z", "deleted": false } } ``` #### DELETE accounts/:account\_id/campaigns/:campaign\_id[](#delete-accounts-account-id-campaigns-campaign-id "Permalink to this headline") Delete the specified campaign belonging to the current account. **Note**: Deleting a campaign is not reversible and subsequent attempts to delete the resource will return HTTP 404. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/campaigns/:campaign_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_id
*required* | A reference to the campaign you are operating with in the request.

Type: string

Exampple: `8yn7m` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/campaigns/8yn7m` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "campaign_id": "8yn7m", "account_id": "18ce54d4x5t" } }, "data": { "name": "test", "budget_optimization": "CAMPAIGN", "reasons_not_servable": [], "servable": null, "purchase_order_number": null, "effective_status": "RUNNING", "daily_budget_amount_local_micro": 140000000, "funding_instrument_id": "lygyi", "duration_in_days": null, "standard_delivery": false, "total_budget_amount_local_micro": null, "id": "8yn7m", "entity_status": "PAUSED", "frequency_cap": null, "currency": "USD", "created_at": "2022-06-03T21:38:07Z", "updated_at": "2022-06-03T21:56:35Z", "deleted": true } } ``` ### Content Categories #### GET content\_categories[](#get-content-categories "Permalink to this headline") Request the valid content `categories` to be set as `targeting_criteria` for a line item. Each `content_category` maps to one or more [IAB Categories](/x-ads-api/campaign-management#iab-categories). This can be done by setting the `targeting_type` to `IAB_CATEGORY` on the batch `targeting_critera` endpoint to include the set of corresponding `iab_categories` returned by the `content_categories` request. Failure to do so will result in a validation error. Publisher details for each of these content categories can be retrieved using the [GET publishers](/x-ads-api/campaign-management#publishers) endpoint. Additional details can be found in the [Video Views Pre-roll Objective Guide](/x-ads-api/campaign-management#video-views-preroll-objective). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/content_categories` **Parameters[](#parameters "Permalink to this headline")** No request parameters **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/content_categories` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": {} }, "next_cursor": null, "data": [ { "name": "Automotive (Cars, Trucks, Racing)", "id": "ru", "iab_categories": [ "IAB2" ], "publishers_in_last_thirty_days": 12, "videos_monetized_in_last_thirty_days": 316 }, { "name": "Comedy", "id": "sk", "iab_categories": [ "IAB1-4" ], "publishers_in_last_thirty_days": 19, "videos_monetized_in_last_thirty_days": 174 }, { "name": "Digital Creators", "id": "sl", "iab_categories": [ "IAB25-1" ], "publishers_in_last_thirty_days": 110, "videos_monetized_in_last_thirty_days": 1257 }, { "name": "Entertainment & Pop Culture", "id": "sm", "iab_categories": [ "IAB1-1", "IAB1-2", "IAB1-3", "IAB1-5" ], "publishers_in_last_thirty_days": 120, "videos_monetized_in_last_thirty_days": 3482 }, { "name": "Financial & Business News", "id": "sn", "iab_categories": [ "IAB3", "IAB13", "IAB21" ], "publishers_in_last_thirty_days": 29, "videos_monetized_in_last_thirty_days": 1461 }, { "name": "Food & Drink", "id": "so", "iab_categories": [ "IAB8-8", "IAB8-12", "IAB8-17", "IAB8-2", "IAB8-3", "IAB8-7", "IAB8-11", "IAB8-4", "IAB8-14", "IAB8-10", "IAB8-15", "IAB8-13", "IAB8-9", "IAB8-16", "IAB8-6", "IAB8-1" ], "publishers_in_last_thirty_days": 24, "videos_monetized_in_last_thirty_days": 516 }, { "name": "Lifestyle (Fashion, Travel, Wellness)", "id": "sp", "iab_categories": [ "IAB16", "IAB9-21", "IAB9-4", "IAB9-25", "IAB9-8", "IAB4", "IAB9-3", "IAB9-15", "IAB7", "IAB6", "IAB9-11", "IAB9-16", "IAB9-7", "IAB9-20", "IAB9-24", "IAB9-17", "IAB9-12", "IAB9-31", "IAB9-27", "IAB10", "IAB9-10", "IAB9-23", "IAB9-6", "IAB9-18", "IAB9-13", "IAB9-1", "IAB9-28", "IAB20", "IAB9-5", "IAB9-26", "IAB22", "IAB23", "IAB9-9", "IAB9-22", "IAB18", "IAB9-2", "IAB9-19", "IAB9-14", "IAB9-29" ], "publishers_in_last_thirty_days": 67, "videos_monetized_in_last_thirty_days": 2412 }, { "name": "Music", "id": "sq", "iab_categories": [ "IAB1-6" ], "publishers_in_last_thirty_days": 31, "videos_monetized_in_last_thirty_days": 518 }, { "name": "News & Current Events", "id": "sr", "iab_categories": [ "IAB12", "IAB14" ], "publishers_in_last_thirty_days": 125, "videos_monetized_in_last_thirty_days": 5507 }, { "name": "Politics", "id": "s4", "iab_categories": [ "IAB11" ], "publishers_in_last_thirty_days": 19, "videos_monetized_in_last_thirty_days": 1402 }, { "name": "Science & Education", "id": "ss", "iab_categories": [ "IAB5", "IAB15" ], "publishers_in_last_thirty_days": 7, "videos_monetized_in_last_thirty_days": 132 }, { "name": "Sports", "id": "se", "iab_categories": [ "IAB17" ], "publishers_in_last_thirty_days": 403, "videos_monetized_in_last_thirty_days": 18281 }, { "name": "Technology", "id": "sg", "iab_categories": [ "IAB19" ], "publishers_in_last_thirty_days": 13, "videos_monetized_in_last_thirty_days": 1089 }, { "name": "Television", "id": "sh", "iab_categories": [ "IAB1-7" ], "publishers_in_last_thirty_days": 58, "videos_monetized_in_last_thirty_days": 1307 }, { "name": "Esports & Video Games", "id": "s0", "iab_categories": [ "IAB9-30" ], "publishers_in_last_thirty_days": 109, "videos_monetized_in_last_thirty_days": 1844 } ], "total_count": 15 } ``` ### Curated Categories #### GET accounts/:account\_id/curated\_categories[](#get-accounts-account-id-curated-categories "Permalink to this headline") Retrieve a list of available Curated Categories for the given `country_codes` Each `curated_category` is only available in specific countries specified by the `country_codes` in the response. Additional details can be found in the [Video Views Pre-roll Objective Guide](/x-ads-api/campaign-management#video-views-preroll-objective). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/curated_categories` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | country\_codes
*required* | Scope the response to just the desired countries by specifying a comma-separated list of two letter ISO country codes. Up to 200 IDs may be provided.

Type: string

Example: `US` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/curated_categories?country_codes=US` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "country_codes": [ "US" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "name": "Basketball", "description": "Run next to the best of everyday basketball content including college teams, professional teams, and the top sports media handles sharing on and off-season basketball video.", "country_codes": [ "US" ], "publisher_user_ids": [ "20265254", "378174762", "900368808", "18939563", "18371803", "18360370", "770658432928079872", "11026952", "37085464", "16212685", "57422635", "281669945", "7117962", "23065057", "41688179", "29779226", "900280416", "364460082", "902030382", "19409270", "19077044", "18139461", "14992591", "66753565", "667563", "16727749", "40941404", "18481113", "791598918", "16201775", "15900167", "45891626", "191894553", "2181233851", "34352904", "171483987", "454122399", "57415242", "19263978", "902089998", "423540866", "2715223320", "22185437", "17292143", "55590247", "66757066", "22642626", "41604618", "87275465", "22643259", "32414973", "73406718", "20346956", "413422891", "45412765", "19537303", "459511725", "30954864", "21308488", "18552281", "19924520", "24903350", "851142163", "26270913", "20444254", "26074296", "6395222", "15537451", "28672101", "38053254", "24925573", "19564719", "18164425", "22815383", "20196159" ], "id": "929wbl6ymlfk", "created_at": "2019-11-08T21:12:47Z", "updated_at": "2021-03-09T20:36:44Z", "videos_monetized_in_last_thirty_days": 2446 }, { "name": "Gaming Personalities", "description": "Run next to the best of everyday gaming content exclusively from a list of some of online gaming’s biggest and most loved digital creators.", "country_codes": [ "US" ], "publisher_user_ids": [ "90779436", "268270621", "567167802", "246596682", "474919140", "284422688", "185909682", "4767225325", "2559865245", "186888760", "161418822", "141021153", "352881953", "1117931702", "146556805", "357294577", "234526497", "266687361", "214201922", "9451052", "2163885564", "2231422037", "116952434", "399909209", "15993650", "974356091193741312", "210839744", "2313002094", "159916388", "3258981481", "231992478", "182236262", "386884916", "22705686", "4140881832", "995979576", "2244953047", "311775629", "98821255", "2733210014", "2741078150" ], "id": "94ngssfrr01x", "created_at": "2019-12-02T20:45:12Z", "updated_at": "2021-03-09T20:18:13Z", "videos_monetized_in_last_thirty_days": 448 }, { "name": "Baseball", "description": "Run next to the best of everyday baseball content including college teams, professional teams, and the top sports media handles sharing major baseball coverage.", "country_codes": [ "US" ], "publisher_user_ids": [ "22016177", "22798877", "52803520", "20710218", "423532170", "28603812", "41144996", "22819823", "39389304", "252273678", "123307490", "2319354187", "41488578", "37947138", "302066953", "159143990", "35006336", "53178109", "40918816", "39682297", "39397148", "39419180", "53197137", "52863923", "21407926", "31164229", "19607400", "39392910", "241544156", "43024351", "37837907", "165764237", "69117905", "87673496", "23043294", "52824038", "52861612", "33137450", "30008146", "39367703", "21436663", "188575356", "40931019", "41468683", "40927173", "172742915" ], "id": "9lav5usxfmdc", "created_at": "2020-05-18T20:20:27Z", "updated_at": "2021-03-09T20:37:46Z", "videos_monetized_in_last_thirty_days": 190 }, { "name": "Esports Teams", "description": "Run next to the programming from the world’s best esports teams, covering both in-event coverage and other year-round complimentary programming.", "country_codes": [ "US" ], "publisher_user_ids": [ "759527448757215232", "61933836", "477213534", "907193396049182720", "895382891408089089", "862708050116976640", "115038550", "3182089458", "4131266472", "1145702070961496065", "2262070855", "920664872786059264", "1035653581683220481", "14229141", "1101275970995027968", "20734751", "1452520626", "720303639277928448", "2853641871", "912696400571486208", "874362688939413504", "286505380", "892808605170245632", "875087838613733376", "238431491", "867053221940011014", "964529942", "1172506293174710272", "535756639", "2255226817", "1100825469853696000", "1122713320086220803", "1124064709295128581", "899858978418642944", "864977592532688896", "864476897106898944", "862770685445361665", "257268592" ], "id": "9ys3jz3ktreo", "created_at": "2020-10-01T20:02:35Z", "updated_at": "2021-03-09T20:36:20Z", "videos_monetized_in_last_thirty_days": 169 }, { "name": "Football ", "description": "Run next to the best of everyday football content including college teams, professional teams, and the top sports media handles sharing on and off-season football video.", "country_codes": [ "US" ], "publisher_user_ids": [ "21790466", "53103297", "23642374", "817416193854283776", "43403778", "24179879", "26813914", "36375662", "33587536", "180884045", "16332223", "27902825", "180503626", "44468807", "18336787", "818431566", "22146282", "31126587", "40358743", "35865630", "16347506", "72665816", "33583496", "389038362", "36155311", "227342532", "2151130166", "26791995", "44666348", "24109979", "31504542", "713143", "423536031", "25545388", "59471027", "706923475", "19383279", "8824902", "1655877529", "18734310", "240734425", "17076218", "47964412", "2802184770", "19426729", "56443153", "23508439", "25084916", "764347046", "19853312", "348590880" ], "id": "8tujg1lvi8sn", "created_at": "2019-08-15T20:48:51Z", "updated_at": "2021-03-09T20:34:13Z", "videos_monetized_in_last_thirty_days": 254 }, { "name": "Men’s Culture + Lifestyle", "description": "Run next to content from a set of handles curated based on their follower profiles to help you reach a majority male audience, including some of the top handles sharing technology, news, and lifestyle content.", "country_codes": [ "US" ], "publisher_user_ids": [ "17764377", "61933836", "28370738", "3224616765", "22819823", "18927441", "734826612684783616", "14372486", "7157132", "15764136", "590316679", "7302282", "895014043932540928", "7517222", "3489420013", "14063426", "72665816", "214201922", "14980903", "22199141", "21272440", "25319414", "119593082", "4760694445", "765905855195803648", "238431491", "22178780", "241544156", "25093616", "16877611", "22146985", "368703433", "14342661", "415605847", "2181233851", "890891", "15764001", "614754689", "18479513", "23508439", "348590880" ], "id": "8tujj1ep7t34", "created_at": "2019-08-15T20:49:47Z", "updated_at": "2021-03-09T20:39:00Z", "videos_monetized_in_last_thirty_days": 1330 }, { "name": "Women’s Culture + Lifestyle", "description": "Run next to content from a set of handles curated based on their follower profiles to help you reach a majority female audience, including some of the top handles sharing pop culture, news, and lifestyle content.", "country_codes": [ "US" ], "publisher_user_ids": [ "23482952", "20177423", "19074134", "15566901", "32469566", "19784831", "16145224", "16932962", "14934818", "29730065", "24190981", "30278532", "15846407", "24994219", "23993734", "40965341", "16312576", "75094638", "549673665", "18806753", "75306892", "1482663290", "31181674", "971407531972186112", "4020532937", "25087685", "22515362", "80943051", "19247844", "15279429", "16824090", "20710809", "979831113655996416", "32432308", "19472585", "25589776", "739963476370673665", "20188834", "926269727663673349" ], "id": "8tujl1p3yn0g", "created_at": "2019-08-15T20:50:24Z", "updated_at": "2021-03-09T20:17:53Z", "videos_monetized_in_last_thirty_days": 1365 }, { "name": "Light-Hearted", "description": "Run next to a list of handles curated for the volume of positive, feel-good content and conversation they’ve consistently generated on X.", "country_codes": [ "US" ], "publisher_user_ids": [ "20177423", "22449367", "9695312", "19074134", "4805771380", "32469566", "1212860112047460352", "16402507", "16932962", "14934818", "17446621", "29730065", "15846407", "1604444052", "180066380", "16312576", "549673665", "18806753", "16211434", "545336345", "971407531972186112", "4020532937", "833612154", "22515362", "20710809", "32432308", "774311630", "3073349892", "926269727663673349" ], "id": "9fg8gmz96qdg", "created_at": "2020-03-20T19:37:44Z", "updated_at": "2021-03-09T19:57:40Z", "videos_monetized_in_last_thirty_days": 1395 }, { "name": "Soccer", "description": "Run next to the best of everyday soccer content including college teams, professional teams, and the top sports media handles sharing major soccer coverage.", "country_codes": [ "US" ], "publisher_user_ids": [ "21677316", "20636347", "4704552148", "14573900", "22556296", "1415791555", "107146095", "17288520", "213474069", "17493398", "44990136", "452155423", "17744542", "16303450", "2841146601", "2413176055", "29739264", "38580532", "953476292913106945", "27092557", "86356439", "34613288", "3170659367", "119593082", "73412535", "627586654", "15891449", "23011345", "96951800", "15997022", "16960789", "21919642", "102965285", "17224076", "36432200", "1410055968" ], "id": "9ddrgesiap6o", "created_at": "2020-02-28T22:43:26Z", "updated_at": "2021-01-26T17:54:55Z", "videos_monetized_in_last_thirty_days": 421 } ], "total_count": 9 } ``` #### GET accounts/:account\_id/curated\_categories/:curated\_category\_id[](#get-accounts-account-id-curated-categories-curated-category-id "Permalink to this headline") Retrieve details for a specific `curated_category_id` Each `curated_category` is only available in specific countries specified by the `country_codes` in the response. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/curated_categories/:curated_category_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | curated\_category\_id
*required* | A reference to the Curated Category you are operating with in the request.

Type: string

Example: `9ddrgesiap6o` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/curated_categories/9ddrgesiap6o` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "id": "9ddrgesiap6o", "account_id": "18ce54d4x5t" } }, "data": { "name": "Soccer", "description": "Run next to the best of everyday soccer content including college teams, professional teams, and the top sports media handles sharing major soccer coverage.", "country_codes": [], "publisher_user_ids": [ "21677316", "20636347", "4704552148", "14573900", "22556296", "1415791555", "107146095", "17288520", "213474069", "17493398", "44990136", "452155423", "17744542", "16303450", "2841146601", "2413176055", "29739264", "38580532", "953476292913106945", "27092557", "86356439", "34613288", "3170659367", "119593082", "73412535", "627586654", "15891449", "23011345", "96951800", "15997022", "16960789", "21919642", "102965285", "17224076", "36432200", "1410055968" ], "id": "9ddrgesiap6o", "created_at": "2020-02-28T22:43:26Z", "updated_at": "2021-01-26T17:54:55Z", "videos_monetized_in_last_thirty_days": 421 } } ``` ### Features #### GET accounts/:account\_id/features[](#get-accounts-account-id-features "Permalink to this headline") Retrieve the collection of granted features accessible by this ads account. Features are indicated by a descriptive feature key and are only exposed on this endpoint if they are introduced in beta or an otherwise limited release and are available in the Ads API. Features that do not meet this criteria will not be exposed on this endpoint. **Note**: This endpoint serves to aid Ads API ecosystem development by improving visibility into client access to beta releases. API developers can not request access to features on behalf of an advertiser. These requests can only be made by the advertiser to their X account manager. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/features` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | feature\_keys
*optional* | An optional parameter that enables querying for a specific feature key. Requests may include multiple comma-separated keys.

**Note**: Only the features that are accessible by this account will be included in the response.

Type: enum

Possible values: `REACH_AND_FREQUENCY_ANALYTICS`, `REACH_FREQUENCY_CAP`, `WEBSITE_CLICKS_CPM_BILLING` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/features` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t" } }, "data": [ "CITY_TARGETING", "CONVERSATION_CARD", "PROMOTED_MEDIA_POLLS", "REACH_AND_FREQUENCY_ANALYTICS", "REACH_FREQUENCY_CAP", "UNIVERSAL_LOOKALIKE" ] } ``` #### POST accounts/:account\_id/features[](#post-accounts-account-id-features "Permalink to this headline") **SANDBOX ONLY** Add a feature to a sandbox account. The up to date list of account features may be retrieved via the [GET accounts/:account\_id/features](#get-accounts-account-id-features) endpoint. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api-sandbox.x.com/12/accounts/:account_id/features` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `gq180y` | | feature\_keys
*required* | A comma-separated list of account features to add to the account.

Type: enum

Possible values: `AGE_TARGETING`, `ALLOW_SKIPPABLE_VIDEOS_FOR_PREROLL_VIEWS_OBJECTIVE`, `AWARENESS_OBJECTIVE`, `BRAND_TPN`, `CHARGE_FOR_GOOD_CLICK`, `CONVERSATION_CARD`, `CONVERSATION_CARD_FOUR_OPTIONS`, `CONVERSATION_CARD_UNLOCK`, `CPI_CHARGING`, `DIRECT_MESSAGE_CARD`, `DR_TAP`, `ENGAGER_RETARGETING`, `EVENT_TARGETING`, `INSTALLED_APP_CATEGORY_TARGETING`, `MOBILE_CONVERSION_TRANSACTION_VALUE`, `OPTIMIZED_ACTION_BIDDING`, `REACH_AND_FREQUENCY_ANALYTICS`, `REACH_FREQUENCY_CAP`, `VALIDATED_AGE_TARGETING`, `VIDEO_VIEWS_MIDROLL_OBJECTIVE`, `PREROLL_VIEWS_OBJECTIVE`, `VIDEO_APP_DOWNLOAD_CARD` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api-sandbox.x.com/12/accounts/gq180y/features?feature_keys=VALIDATED_AGE_TARGETING` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "gq180y", "feature_keys": [ "VALIDATED_AGE_TARGETING" ] } }, "data": [ "ALLOW_SKIPPABLE_VIDEOS_FOR_PREROLL_VIEWS_OBJECTIVE", "AWARENESS_OBJECTIVE", "CPI_CHARGING", "EVENT_TARGETING", "INSTALLED_APP_CATEGORY_TARGETING", "MOBILE_CONVERSION_TRANSACTION_VALUE", "OPTIMIZED_ACTION_BIDDING", "VALIDATED_AGE_TARGETING", "VIDEO_APP_DOWNLOAD_CARD" ] } ``` #### DELETE accounts/:account\_id/features[](#delete-accounts-account-id-features "Permalink to this headline") **SANDBOX ONLY** Remove a feature from a sandbox account. The up to date list of account features may be retrieved via the [GET accounts/:account\_id/features](#get-accounts-account-id-features) endpoint. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api-sandbox.x.com/12/accounts/:account_id/features` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `gq180y` | | feature\_keys
*required* | A comma-separated list of account features to remove from the account.

Type: enum

Possible values: `AGE_TARGETING`, `ALLOW_SKIPPABLE_VIDEOS_FOR_PREROLL_VIEWS_OBJECTIVE`, `AWARENESS_OBJECTIVE`, `BRAND_TPN`, `CHARGE_FOR_GOOD_CLICK`, `CONVERSATION_CARD`, `CONVERSATION_CARD_FOUR_OPTIONS`, `CONVERSATION_CARD_UNLOCK`, `CPI_CHARGING`, `DIRECT_MESSAGE_CARD`, `DR_TAP`, `ENGAGER_RETARGETING`, `EVENT_TARGETING`, `INSTALLED_APP_CATEGORY_TARGETING`, `MOBILE_CONVERSION_TRANSACTION_VALUE`, `OPTIMIZED_ACTION_BIDDING`, `REACH_AND_FREQUENCY_ANALYTICS`, `REACH_FREQUENCY_CAP`, `VALIDATED_AGE_TARGETING`, `VIDEO_VIEWS_MIDROLL_OBJECTIVE`, `PREROLL_VIEWS_OBJECTIVE`, `VIDEO_APP_DOWNLOAD_CARD` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api-sandbox.x.com/12/accounts/gq180y/features?feature_keys=PREROLL_VIEWS_OBJECTIVE` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "gq180y", "feature_keys": [ "PREROLL_VIEWS_OBJECTIVE" ] } }, "data": [ "CPI_CHARGING", "EVENT_TARGETING", "INSTALLED_APP_CATEGORY_TARGETING", "MOBILE_CONVERSION_TRANSACTION_VALUE", "OPTIMIZED_ACTION_BIDDING", "VIDEO_APP_DOWNLOAD_CARD" ] } ``` ### Funding Instruments #### GET accounts/:account\_id/funding\_instruments[](#get-accounts-account-id-funding-instruments "Permalink to this headline") Retrieve details for some or all funding instruments associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/funding_instruments` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | funding\_instrument\_ids
*optional* | Scope the response to just the desired funding instruments by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `lygyi` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/funding_instruments` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "start_time": "2016-07-22T04:24:04Z", "description": "Visa ending in 0650", "credit_limit_local_micro": 200000000, "end_time": null, "id": "lygyi", "entity_status": "ACTIVE", "account_id": "18ce54d4x5t", "reasons_not_able_to_fund": [], "io_header": null, "currency": "USD", "funded_amount_local_micro": 645940000, "created_at": "2016-07-22T04:24:04Z", "type": "CREDIT_CARD", "able_to_fund": true, "updated_at": "2017-04-05T00:25:13Z", "credit_remaining_local_micro": null, "deleted": false } ] } ``` #### GET accounts/:account\_id/funding\_instruments/:funding\_instrument\_id[](#get-accounts-account-id-funding-instruments-funding-instrument-id "Permalink to this headline") Retrieve a specific funding instrument associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/funding_instruments/:id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | funding\_instrument\_id
*required* | A reference to the funding instrument you are operating with in the request.

Type: string

Example: `lygyi` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/funding_instruments/lygyi` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "funding_instrument_id": "lygyi", "account_id": "18ce54d4x5t" } }, "data": { "start_time": "2016-07-22T04:24:04Z", "description": "Visa ending in 0650", "credit_limit_local_micro": 200000000, "end_time": null, "id": "lygyi", "entity_status": "ACTIVE", "account_id": "18ce54d4x5t", "reasons_not_able_to_fund": [], "io_header": null, "currency": "USD", "funded_amount_local_micro": 645940000, "created_at": "2016-07-22T04:24:04Z", "type": "CREDIT_CARD", "able_to_fund": true, "updated_at": "2017-04-05T00:25:13Z", "credit_remaining_local_micro": null, "deleted": false } } ``` #### POST accounts/:account\_id/funding\_instruments[](#post-accounts-account-id-funding-instruments "Permalink to this headline") **SANDBOX ONLY** Create a funding instrument in the sandbox environment. There is no risk of incurring costs while using a sandbox funding instrument. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api-sandbox.x.com/12/accounts/:account_id/funding_instruments` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :--------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `gq1844` | | currency
*required* | The currency, expressed in [ISO-4217](https://en.wikipedia.org/wiki/ISO_4217).

Type: string

Example: `USD` | | start\_time
*required* | The date for the funding instrument to become active and usable, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

Type: string

Example: `2017-05-19T07:00:00Z` | | type
*required* | The type of funding instrument to create.

Type: enum

Possible values: `AGENCY_CREDIT_LINE`, `CREDIT_CARD`, `CREDIT_LINE`, `INSERTION_ORDER`, `PARTNER_MANAGED` | | end\_time
*sometimes required* | The date for the funding instrument to become inactive, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

Type: string

Example: `2017-05-26T07:00:00Z` | | credit\_limit\_local\_micro
*optional* | The total credit available against this funding instrument.

**Note**: Only applicable to some funding instrument types.

Type: long

Example: `37500000` | | funded\_amount\_local\_micro
*optional* | The total budget amount allocated to this funding instrument.

**Note**: Only applicable to some funding instrument types.

Type: long

Example: `37500000` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api-sandbox.x.com/12/accounts/gq1844/funding_instruments?currency=USD&start_time=2017-07-10T00:00:00Z&type=INSERTION_ORDER&end_time=2018-01-10T00:00:00Z&funded_amount_local_micro=140000000000` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "start_time": "2017-07-10T00:00:00Z", "description": "(no payment method has been set up yet)", "credit_limit_local_micro": null, "end_time": "2018-01-10T00:00:00Z", "id": "hxtet", "entity_status": "ACTIVE", "account_id": "gq1844", "reasons_not_able_to_fund": [], "io_header": null, "currency": "USD", "funded_amount_local_micro": 140000000000, "created_at": "2017-09-09T05:23:28Z", "type": "INSERTION_ORDER", "able_to_fund": true, "updated_at": "2017-09-09T05:23:28Z", "credit_remaining_local_micro": null, "deleted": false }, "request": { "params": { "start_time": "2017-07-10T00:00:00Z", "end_time": "2018-01-10T00:00:00Z", "account_id": "gq1844", "currency": "USD", "funded_amount_local_micro": 140000000000, "type": "INSERTION_ORDER" } } } ``` #### DELETE accounts/:account\_id/funding\_instruments/:funding\_instrument\_id[](#delete-accounts-account-id-funding-instruments-funding-instrument-id "Permalink to this headline") **SANDBOX ONLY** Delete a funding instrument in the sandbox environment. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api-sandbox.x.com/12/accounts/:account_id/funding_instruments/:funding_instrument_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `gq1844` | | funding\_instrument\_id
*required* | A reference to the funding instrument you are operating with in the request.

Type: string

Exampple: `hxt82` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api-sandbox.x.com/12/accounts/gq1844/funding_instruments/hxt82` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "start_time": "2017-08-30T19:23:47Z", "description": "(no payment method has been set up yet)", "credit_limit_local_micro": 500000000, "end_time": null, "id": "hxt82", "entity_status": "ACTIVE", "account_id": "gq1844", "reasons_not_able_to_fund": [ "DELETED" ], "io_header": null, "currency": "USD", "funded_amount_local_micro": null, "created_at": "2017-08-30T19:23:47Z", "type": "CREDIT_CARD", "able_to_fund": false, "updated_at": "2017-09-09T02:08:30Z", "credit_remaining_local_micro": null, "deleted": true }, "request": { "params": { "funding_instrument_id": "hxt82", "account_id": "gq1844" } } } ``` ### IAB Categories #### GET iab\_categories[](#get-iab-categories "Permalink to this headline") Request the valid app `categories` for ad groups (`line_items`). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/iab_categories` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of categories. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `gc-ddf4a` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/iab_categories?count=2` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "id": "IAB1", "parent_id": null, "name": "Arts & Entertainment" }, { "id": "IAB1-1", "parent_id": "IAB1", "name": "Books & Literature" } ], "next_cursor": "uxa8", "request": { "params": { "count": 2 } } } ``` ### Line Items #### GET accounts/:account\_id/line\_items[](#get-accounts-account-id-line-items "Permalink to this headline") Retrieve details for some or all line items associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_items` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_ids
*optional* | Scope the response to just the line items under specific campaigns by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `8gdx6` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | funding\_instrument\_ids
*optional* | Scope the response to just the line items under specific funding instruments by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `lygyi` | | line\_item\_ids
*optional* | Scope the response to just the desired line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `8v7jo` | | q
*optional* | An optional query to scope resource by `name`.

Type: string

Min, Max length: `1`, `255` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_draft
*optional* | Include draft campaigns results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/line_items?line_item_ids=itttx` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "line_item_ids": [ "itttx" ] } }, "next_cursor": null, "data": [ { "advertiser_user_id": "756201191646691328", "name": "li-18", "placements": [ "ALL_ON_TWITTER" ], "start_time": "2021-02-16T00:00:00Z", "bid_amount_local_micro": 320000, "advertiser_domain": null, "target_cpa_local_micro": null, "primary_web_event_tag": null, "goal": "ENGAGEMENT", "daily_budget_amount_local_micro": null, "product_type": "PROMOTED_TWEETS", "end_time": null, "funding_instrument_id": "lygyi", "bid_strategy": "MAX", "duration_in_days": null, "standard_delivery": null, "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "itttx", "entity_status": "PAUSED", "automatic_tweet_promotion": null, "frequency_cap": null, "android_app_store_identifier": null, "categories": [], "currency": "USD", "pay_by": "ENGAGEMENT", "created_at": "2021-02-23T23:37:54Z", "ios_app_store_identifier": null, "updated_at": "2022-06-01T02:01:18Z", "campaign_id": "f4z6x", "creative_source": "MANUAL", "deleted": false } ] } ``` #### GET accounts/:account\_id/line\_items/:line\_item\_id[](#get-accounts-account-id-line-items-line-item-id "Permalink to this headline") Retrieve a specific line item associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_items/:line_item_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Example: `8v7jo` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/line_items/itttx` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "line_item_id": "itttx", "account_id": "18ce54d4x5t" } }, "data": { "advertiser_user_id": "756201191646691328", "name": "li-18", "placements": [ "ALL_ON_TWITTER" ], "start_time": "2021-02-16T00:00:00Z", "bid_amount_local_micro": 320000, "advertiser_domain": null, "target_cpa_local_micro": null, "primary_web_event_tag": null, "goal": "ENGAGEMENT", "daily_budget_amount_local_micro": null, "product_type": "PROMOTED_TWEETS", "end_time": null, "funding_instrument_id": "lygyi", "bid_strategy": "MAX", "duration_in_days": null, "standard_delivery": null, "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "itttx", "entity_status": "PAUSED", "automatic_tweet_promotion": null, "frequency_cap": null, "android_app_store_identifier": null, "categories": [], "currency": "USD", "pay_by": "ENGAGEMENT", "created_at": "2021-02-23T23:37:54Z", "ios_app_store_identifier": null, "updated_at": "2022-06-01T02:01:18Z", "campaign_id": "f4z6x", "creative_source": "MANUAL", "deleted": false } } ``` #### POST accounts/:account\_id/line\_items[](#post-accounts-account-id-line-items "Permalink to this headline") Create a line item associated with the specified campaign belonging to the current account. All line items within a campaign must be of the same `product_type` and `objective`. When using the `PROMOTED_ACCOUNT` product type, associating a Tweet with the `line_item` will add timeline placements on mobile in addition to the standard `PROMOTED_ACCOUNT` placement. Setting either `android_app_store_identifier` or `ios_app_store_identifier` will automatically add the targeting criteria for the line item matching the mobile app being promoted; for example, passing in `ios_app_store_identifier` would add `PLATFORM` [targeting criteria](/x-ads-api/campaign-management#targeting-options) for `iOS`. **Note**: There is a limit of 100 line items per campaign and 256 active line items across all campaigns. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_items` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_id
*required* | The identifier for the campaign to create the line item under.

Type: string

Example: `8slvg` | | end\_time
*required* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the line item will stop serving.

Type: string

Example: `2017-10-05T00:00:00Z` | | objective
*required* | The campaign objective for this line item.

Type: enum

Possible values: `APP_ENGAGEMENTS`, `APP_INSTALLS`, `REACH`, `FOLLOWERS`, `ENGAGEMENTS`, `VIDEO_VIEWS`, `PREROLL_VIEWS`, `WEBSITE_CLICKS` | | placements
*required* | The placement location(s) for this line item to display in. Specify a comma-separated list of placement values.

Type: enum

Possible values: `ALL_ON_TWITTER`, `PUBLISHER_NETWORK`, `TAP_BANNER`, `TAP_FULL`, `TAP_FULL_LANDSCAPE`, `TAP_NATIVE`, `TAP_MRECT`,`TWITTER_PROFILE`, `TWITTER_REPLIES`, `TWITTER_SEARCH`, `TWITTER_TIMELINE` | | product\_type
*required* | The type of promoted product that this line item will contain.

Type: enum

Possible values: `MEDIA`, `PROMOTED_ACCOUNT`, `PROMOTED_TWEETS` | | start\_time
*required* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the line item will begin serving.

Type: string

Example: `2017-07-05T00:00:00Z` | | advertiser\_domain
*sometimes required* | The website domain for this advertiser, without the protocol specification.

**Note**: Required when the line item's placement is set to `PUBLISHER_NETWORK`.

Type: string

Example: `x.com` | | android\_app\_store\_identifier
*sometimes required* | The Google App Store identifier for promoted applications.

**Note**: `APP_INSTALLS` and `APP_ENGAGEMENTS` objectives require setting at least one app store identifier -- either `android_app_store_identifier` or `ios_app_store_identifier`.

Type: string

Example: `com.twitter.android` | | bid\_amount\_local\_micro
*sometimes required* | The bid amount to be associated with this line item. The currency associated with the specified funding instrument will be used. For USD, \$5.50 is represented as 5500000.

**Note**: Required if `bid_strategy` is set to either `MAX` or `TARGET`

**Note**: Only values greater than zero are accepted.

Type: long

Example: `5500000` | | categories
*sometimes required* | The relevant IAB categories for this advertiser. See [GET iab\_categories](/x-ads-api/campaign-management#iab-categories).

**Note**: Required when the line item's placement is set to `PUBLISHER_NETWORK`.

Type: string

Example: `IAB3-1` | | ios\_app\_store\_identifier
*sometimes required* | The numeric portion of the Apple App Store identifier for promoted applications.

**Note**: `APP_INSTALLS` and `APP_ENGAGEMENTS` objectives require setting at least one app store identifier -- either `android_app_store_identifier` or `ios_app_store_identifier`.

Type: string

Example: `333903271` | | primary\_web\_event\_tag
*sometimes required* | The identifier of the primary web event tag. Allows more accurate tracking of engagements for the campaign pertaining to this line item.

**Note**: Required when the line item's goal is set to `WEBSITE_CONVERSIONS`.

Type: string

Example: `nvo4z` | | advertiser\_user\_id
*optional* | The X user identifier for the handle promoting a `PREROLL_VIEWS` ad. Only certain client applications may use this parameter.

Type: string

Example: `312226591` | | audience\_expansion
*optional* | Used to expand the reach of campaigns by targeting users similar to those already targeted.

**Note**: By default, no expansion will be applied.

Type: enum

Possible values: `BROAD`, `DEFINED`, `EXPANDED` | | bid\_strategy
*optional* | The bidding mechanism.

`AUTO` automatically optimizes bidding based on daily budget and campaign flight dates.

`MAX` sets the maximum allowable bid and is **not** available when the objective is set to `REACH` or `FOLLOWERS`.

`TARGET` attempts to make daily bid averages within 20% of the specified `bid_amount_local_micro` and is available when the objective is set to `REACH`, `FOLLOWERS`, OR `WEBSITE_CLICKS`.

**Note**: If set to `AUTO`, `bid_amount_local_micro` will be ignored.

**Note**: Default based on objective.

Type: enum

Possible values: `AUTO`, `MAX`, `TARGET` | | duration\_in\_days
*optional* | The time period within which the `frequency_cap` is achieved.

Type: int

Possible values: `1`, `7`, `30` | | entity\_status
*optional* | The line item status.

Type: enum

Default: `ACTIVE`
Possible values: `ACTIVE`, `DRAFT`, `PAUSED` | | frequency\_cap
*optional* | The maximum number of times an ad could be delivered to a user.

**Note**: Only supported for `REACH`, `ENGAGEMENTS`, `VIDEO_VIEWS`, and `PREROLL_VIEWS` objectives.

Type: int

Example: `5` | | goal
*optional* | The optimization setting to use with this line item.

The `APP_PURCHASES` option is available for `APP_INSTALL`. The `APP_CLICKS` and `APP_INSTALLS` options are available for both `APP_INSTALL` and `APP_ENGAGEMENTS` objectives and may require using a supported [MACT partner](https://business.x.com/en/help/campaign-setup/create-an-app-installs-or-app-engagement-campaign/mobile-app-conversion-tracking.html).

The `SITE_VISITS` option is only available with the `WEBSITE_CLICKS` objective.

**Note**: Default based on objective.

Type: enum

Possible values: `APP_CLICKS`, `APP_INSTALLS`, `APP_PURCHASES`,`ENGAGEMENT`, `FOLLOWERS`, `LINK_CLICKS`, `MAX_REACH`, `PREROLL`, `PREROLL_STARTS`, `REACH_WITH_ENGAGEMENT`, `SITE_VISITS`, `VIDEO_VIEW`, `VIEW_3S_100PCT`, `VIEW_6S`, `VIEW_15S`, `WEBSITE_CONVERSIONS` | | name
*optional* | The name for the line item.

Type: string

Example: `demo`

Min, Max length: `1`, `255` | | pay\_by
*optional* | The unit to charge this line item by. This setting can only be modified for line items using the `APP_INSTALLS` objective.

**Note**: The default `pay_by` is automatically set based upon the campaign objective and line item's bid unit.

The `APP_INSTALLS` goal supports both `APP_CLICK` and `IMPRESSION` values. `IMPRESSION` is the default value.

The `LINK_CLICKS` goal supports both `LINK_CLICK` and `IMPRESSION` values. `IMPRESSION` is the default value but is not supported when setting `TARGET` for `bid_strategy`.

The `SITE_VISITS` goal supports `IMPRESSION` values.

Type: enum

Possible values: `APP_CLICK`, `IMPRESSION`, `LINK_CLICK` | | standard\_delivery
*optional* | Enable standard or accelerated delivery. See [Budget Pacing](/x-ads-api/campaign-management#budget-pacing) for more information on standard versus accelerated delivery. Only available when `budget_optimization` is set to `LINE_ITEM` for the parent campaign

Type: boolean

Default: `true`
Possible values: `true`, `false` | | total\_budget\_amount\_local\_micro
*optional* | The total budget amount to be allocated to the line item. The currency associated with the specified funding instrument will be used. For USD, \$37.50 is represented as 37500000.

Type: long

Example: `37500000` | | daily\_budget\_amount\_local\_micro
*sometimes required* | The daily budget amount to be allocated to the campaign. The currency associated with the specified funding instrument will be used. For USD, \$5.50 is represented as 5500000. When not provided the campaign will spend evenly based upon total budget and for duration of campaign flight time. Only available when `budget_optimization` is set to `LINE_ITEM` for the parent campaign

**Note**: This should be less than or equal to the `total_budget_amount_local_micro`.

Type: long

Example: `5500000` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/line_items?campaign_id=hwtq0&objective=ENGAGEMENTS&product_type=PROMOTED_TWEETS&placements=ALL_ON_TWITTER&bid_amount_local_micro=3210000&entity_status=PAUSED&daily_budget_amount_local_micro=1000000&start_time=2022-06-15` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "placements": [ "ALL_ON_TWITTER" ], "start_time": "2022-06-15T00:00:00Z", "bid_amount_local_micro": 3210000, "daily_budget_amount_local_micro": 1000000, "product_type": "PROMOTED_TWEETS", "objective": "ENGAGEMENTS", "entity_status": "PAUSED", "account_id": "18ce54d4x5t", "campaign_id": "hwtq0" } }, "data": { "advertiser_user_id": "756201191646691328", "name": null, "placements": [ "ALL_ON_TWITTER" ], "start_time": "2022-06-15T00:00:00Z", "bid_amount_local_micro": 3210000, "advertiser_domain": null, "target_cpa_local_micro": null, "primary_web_event_tag": null, "goal": "ENGAGEMENT", "daily_budget_amount_local_micro": 1000000, "product_type": "PROMOTED_TWEETS", "end_time": null, "bid_strategy": "MAX", "duration_in_days": null, "standard_delivery": true, "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "ml5vs", "entity_status": "PAUSED", "automatic_tweet_promotion": null, "frequency_cap": null, "android_app_store_identifier": null, "categories": [], "currency": "USD", "pay_by": "ENGAGEMENT", "created_at": "2022-06-03T23:47:20Z", "ios_app_store_identifier": null, "updated_at": "2022-06-03T23:47:20Z", "campaign_id": "hwtq0", "creative_source": "MANUAL", "deleted": false } } ``` #### POST batch/accounts/:account\_id/line\_items[](#post-batch-accounts-account-id-line-items "Permalink to this headline") Allows the batch creation of new [line items](#post-accounts-account-id-line-items) with a single request. **Batch Requests** * The current maximum batch size is 40. * All parameters are sent in the request body and a `Content-Type` of `application/json` is required. * Batch requests fail or succeed together as a group and all API responses for both error and success preserve the item order of the initial request. **Batch Responses** Batch API responses return an ordered collection of items. Otherwise, they are identical in structure to their corresponding single-item endpoints. **Batch Errors** * Request-level errors (eg. max batch size exceeded) are shown in the response under the `errors` object. * Item-level errors (eg. missing required line item parameter) are shown in the response under the `operation_errors` object. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/batch/accounts/:account_id/line_items` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | operation\_type
*required* | The per item operation type being performed.

Type: enum

Possible values: `Create`, `Delete`, `Update` | | params
*required* | A JSON object containing all the parameters for the line item objects. For a list of required and optional line item parameters, see [here](#post-accounts-account-id-line-items). | **Example Request[](#example-request "Permalink to this headline")** `POST 'Content-Type: application/json' https://ads-api.x.com/12/batch/accounts/18ce54d4x5t/line_items` ```json theme={null} [ { "operation_type":"Create", "params":{ "campaign_id":"8yn7m", "objective":"ENGAGEMENTS", "product_type":"PROMOTED_TWEETS", "placements":"ALL_ON_TWITTER", "bid_amount_local_micro":3210000, "entity_status":"PAUSED" } } ] ``` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "advertiser_user_id": "756201191646691328", "name": null, "placements": [ "ALL_ON_TWITTER" ], "start_time": null, "bid_amount_local_micro": 3210000, "advertiser_domain": null, "target_cpa_local_micro": null, "primary_web_event_tag": null, "goal": "ENGAGEMENT", "daily_budget_amount_local_micro": null, "product_type": "PROMOTED_TWEETS", "end_time": null, "funding_instrument_id": "lygyi", "bid_strategy": "MAX", "duration_in_days": null, "standard_delivery": null, "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "9cqi0", "entity_status": "PAUSED", "automatic_tweet_promotion": null, "frequency_cap": null, "android_app_store_identifier": null, "categories": [], "currency": "USD", "pay_by": "ENGAGEMENT", "created_at": "2017-07-07T17:42:20Z", "ios_app_store_identifier": null, "updated_at": "2017-07-07T17:42:20Z", "campaign_id": "8yn7m", "creative_source": "MANUAL", "deleted": false } ], "request": [ { "params": { "placements": [ "ALL_ON_TWITTER" ], "bid_amount_local_micro": 3210000, "product_type": "PROMOTED_TWEETS", "objective": "ENGAGEMENTS", "entity_status": "PAUSED", "account_id": "18ce54d4x5t", "campaign_id": "8yn7m" }, "operation_type": "Create" } ] } ``` #### PUT accounts/:account\_id/line\_items/:line\_item\_id[](#put-accounts-account-id-line-items-line-item-id "Permalink to this headline") Update the specified line item associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_items/:line_item_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Example: `8v7jo` | | advertiser\_domain
*optional* | The website domain for this advertiser, without the protocol specification.

**Note**: Required when the line item's placement is set to `PUBLISHER_NETWORK`.

Type: string

Example: `x.com` | | advertiser\_user\_id
*optional* | The Twitter user identifier for the handle promoting a `PREROLL_VIEWS` ad. Only certain client applications may use this parameter.

Type: string

Example: `312226591` | | android\_app\_store\_identifier
*optional* | The Google App Store identifier for the promoted application.

**Note**: `APP_INSTALLS` and `APP_ENGAGEMENTS` objectives require setting at least one app store identifier -- either `android_app_store_identifier` or `ios_app_store_identifier`.

Type: string

Example: `com.twitter.android` | | audience\_expansion
*optional* | Used to expand the reach of campaigns by targeting users similar to those already targeted.

Type: enum

Possible values: `BROAD`, `DEFINED`, `EXPANDED` | | bid\_amount\_local\_micro
*optional* | The bid amount to be associated with this line item. The currency associated with the specified funding instrument will be used. For USD, \$5.50 is represented as 5500000.

**Note**: Required if `bid_strategy` is set to either `MAX` or `TARGET`

**Note**: Only values greater than zero are accepted.

Type: long

Example: `140000` | | bid\_strategy
*optional* | The bidding mechanism.

`AUTO` automatically optimizes bidding based on daily budget and campaign flight dates.

`MAX` sets the maximum allowable bid and is **not** available when the objective is set to `REACH` or `FOLLOWERS`.

`TARGET` attempts to make daily bid averages within 20% of the specified `bid_amount_local_micro` and is available when the objective is set to `REACH` or `WEBSITE_CLICKS`.

**Note**: If set to `AUTO`, `bid_amount_local_micro` will be ignored.

**Note**: Default based on objective.

Type: enum

Possible values: `AUTO`, `MAX`, `TARGET` | | categories
*optional* | The relevant IAB categories for this advertiser. See [GET iab\_categories](/x-ads-api/campaign-management#iab-categories).

**Note**: Required when the line item's placement is set to `PUBLISHER_NETWORK`.

Type: string

Example: `IAB3-1` | | duration\_in\_days
*optional* | The time period within which the `frequency_cap` is achieved.

Type: int

Possible values: `1`, `7`, `30` | | entity\_status
*optional* | The line item status.

Type: enum

Possible values: `ACTIVE`, `PAUSED` | | end\_time
*optional* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the line item will stop serving.

Type: string

Example: `2017-10-05T00:00:00Z` | | frequency\_cap
*optional* | The maximum number of times an ad could be delivered to a user.

**Note**: Only supported for `REACH`, `ENGAGEMENTS`, `VIDEO_VIEWS`, and `PREROLL_VIEWS` objectives.

Type: int

Example: `5` | | goal
*optional* | The optimization setting to use with this line item. The `APP_PURCHASES` option is available for `APP_INSTALL`. The `APP_CLICKS` and `APP_INSTALLS` options are available for `APP_INSTALL` and `APP_ENGAGEMENTS` and may require using a supported [MACT partner](https://business.x.com/en/help/campaign-setup/create-an-app-installs-or-app-engagement-campaign/mobile-app-conversion-tracking.html).

**Note**: Default based on objective.

Type: enum

Possible values: `APP_CLICKS`, `APP_INSTALLS`, `APP_PURCHASES`, `ENGAGEMENT`, `FOLLOWERS`, `LINK_CLICKS`, `MAX_REACH`, `PREROLL`, `PREROLL_STARTS`, `REACH_WITH_ENGAGEMENT`, `VIDEO_VIEW`, `VIEW_3S_100PCT`, `VIEW_6S`, `VIEW_15S`, `WEBSITE_CONVERSIONS` | | ios\_app\_store\_identifier
*optional* | The numeric portion of the Apple App Store identifier for promoted applications.

**Note**: `APP_INSTALLS` and `APP_ENGAGEMENTS` objectives require setting at least one app store identifier -- either `android_app_store_identifier` or `ios_app_store_identifier`.

Type: string

Example: `333903271` | | name
*optional* | The name for the line item.

Type: string

Example: `demo` | | pay\_by
*optional* | The unit to charge this line item by. This setting can only be modified for line items using the `APP_INSTALLS` objective.

**Note**: The default `pay_by` is automatically set based upon the campaign objective and line item's bid unit.

The `APP_INSTALLS` goal supports both `APP_CLICK` and `IMPRESSION` values. `IMPRESSION` is the default value.

The `LINK_CLICKS` goal supports both `LINK_CLICK` and `IMPRESSION` values. `IMPRESSION` is the default value but is not supported when setting `TARGET` for `bid_strategy`.

The `SITE_VISITS` goal supports `IMPRESSION` values.

Type: enum

Possible values: `APP_CLICK`, `IMPRESSION`, `LINK_CLICK` | | start\_time
*optional* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the line item will begin serving.

Type: string

Example: `2017-07-05T00:00:00Z` | | total\_budget\_amount\_local\_micro
*optional* | The total budget amount to be allocated to the line item. The currency associated with the specified funding instrument will be used. For USD, \$37.50 is represented as 37500000.

Type: long

Example: `37500000` | | daily\_budget\_amount\_local\_micro
*optional* | The daily budget amount to be allocated to the campaign. The currency associated with the specified funding instrument will be used. For USD, \$5.50 is represented as 5500000. When not provided the campaign will spend evenly based upon total budget and for duration of campaign flight time. Only available when `budget_optimization` is set to `LINE_ITEM` for the parent campaign

**Note**: This should be less than or equal to the `total_budget_amount_local_micro`.

Type: long

Example: `5500000` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/line_items/9cqi0?bid_amount_local_micro=140000` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "line_item_id": "9cqi0", "bid_amount_local_micro": 140000, "account_id": "18ce54d4x5t" } }, "data": { "advertiser_user_id": "756201191646691328", "name": null, "placements": [ "ALL_ON_TWITTER" ], "start_time": "2017-07-10T00:00:00Z", "bid_amount_local_micro": 140000, "advertiser_domain": null, "target_cpa_local_micro": null, "primary_web_event_tag": null, "goal": "ENGAGEMENT", "daily_budget_amount_local_micro": null, "product_type": "PROMOTED_TWEETS", "end_time": null, "bid_strategy": "MAX", "duration_in_days": null, "standard_delivery": null, "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "9cqi0", "entity_status": "PAUSED", "automatic_tweet_promotion": null, "frequency_cap": null, "android_app_store_identifier": null, "categories": [], "currency": "USD", "pay_by": "ENGAGEMENT", "created_at": "2017-07-07T17:42:20Z", "ios_app_store_identifier": null, "updated_at": "2022-06-03T23:51:36Z", "campaign_id": "8yn7m", "creative_source": "MANUAL", "deleted": false } } ``` #### DELETE accounts/:account\_id/line\_items/:line\_item\_id[](#delete-accounts-account-id-line-items-line-item-id "Permalink to this headline") Delete the specified line item belonging to the current account. **Note**: Deleting a line item is not reversible and subsequent attempts to delete the resource will return HTTP 404. **Note**: When a line item is deleted, its child promoted\_tweets are only returned in the GET accounts/:account\_id/promoted\_tweets and GET accounts/:account\_id/promoted\_tweets/:promoted\_tweet\_id endpoints if `with_deleted=true` is specified in the request. These promoted\_tweets are not actually deleted, though (`"deleted": false` in the response). We do not cascade deletes. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_items/:line_item_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Exampple: `9f2ix` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/line_items/9f2ix` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "bid_strategy": "MAX", "advertiser_user_id": "756201191646691328", "name": "Untitled", "placements": [], "start_time": null, "bid_amount_local_micro": 100000, "advertiser_domain": null, "target_cpa_local_micro": null, "primary_web_event_tag": null, "pay_by": "ENGAGEMENT", "product_type": "PROMOTED_TWEETS", "end_time": "2017-07-21T00:00:00Z", "duration_in_days": 1, "total_budget_amount_local_micro": null, "objective": "ENGAGEMENTS", "id": "9f2ix", "entity_status": "ACTIVE", "goal": "ENGAGEMENT", "frequency_cap": 5, "categories": [], "currency": "USD", "created_at": "2017-07-14T00:01:50Z", "updated_at": "2017-08-09T07:41:08Z", "campaign_id": "90r8n", "creative_source": "MANUAL", "deleted": true }, "request": { "params": { "line_item_id": "9f2ix", "account_id": "18ce54d4x5t" } } } ``` ### Line Item Curated Categories Additional details on usage can be found at the [Video Views Pre-roll Objective Guide](/x-ads-api/campaign-management#video-views-preroll-objective) #### GET accounts/:account\_id/line\_item\_curated\_categories[](#get-accounts-account-id-line-item-curated-categories "Permalink to this headline") Retrieve details for some or all line item curated categories associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_item_curated_categories` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/abc1/line_item_curated_categories` **Example Response[](#example-response "Permalink to this headline")** ```josn theme={null} { "request": { "params": { "account_id": "abc1" } }, "next_cursor": null, "data": [ { "line_item_id": "by5pw", "curated_category_id": "7op29tp2jzeo", "id": "1", "created_at": "2018-06-29T04:19:53Z", "updated_at": "2018-06-29T04:19:53Z", "deleted": false } ] } ``` #### GET accounts/:account\_id/line\_item\_curated\_categories/:line\_item\_curated\_category\_id[](#get-accounts-account-id-line-item-curated-categories-line-item-curated-category-id "Permalink to this headline") Retrieves details for a specific line item curated category associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_item_curated_categories/:line_item_curated_category_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_curated\_category\_id
*required* | A reference to the line item curated category you are operating with in the request.

Type: string

Example: `43853bhii885` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/abc1/line_item_curated_categories/yav` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "line_item_curated_category_id": "yav", "account_id": "abc1" } }, "data": { "line_item_id": "by5pw", "curated_category_id": "7op29tp2jzeo", "id": "yav", "created_at": "2018-06-29T04:19:53Z", "updated_at": "2018-06-29T04:19:53Z", "deleted": false } } ``` #### POST accounts/:account\_id/line\_item\_curated\_categories[](#post-accounts-account-id-line-item-curated-categories "Permalink to this headline") Associate a [curated category](/x-ads-api/campaign-management#curated-categories-2) object with the specified line item. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_item_curated_categories` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
`required` | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | curated\_category\_id
`required` | A reference to the curated category entity you are operating with in the request.

Type: string

Example: `10miy` | | line\_item\_id
`required` | A reference to the line item you are operating with in the request.

Type: string

Example: `8v7jo` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/line_item_curated_categories?line_item_id=iqwka&curated_category_id=9ddrgesiap6o` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "curated_category_id": "9ddrgesiap6o", "line_item_id": "iqwka", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "iqwka", "curated_category_id": "9ddrgesiap6o", "id": "xq", "created_at": "2021-03-30T17:26:42Z", "updated_at": "2021-03-30T17:26:42Z", "deleted": false } } ``` #### PUT accounts/:account\_id/line\_item\_curated\_categories/:line\_item\_curated\_category\_id[](#put-accounts-account-id-line-item-curated-categories-line-item-curated-category-id "Permalink to this headline") Update the specified line item curated category. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_item_curated_categories/:line_item_curated_category_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_curated\_category\_id
*required* | A reference to the line item curated category you are operating with in the request.

Type: string

Example: `1bzq3` | | curated\_category\_id
`optional` | A reference to the curated category entity you are operating with in the request.

Type: string

Example: `10miy` | | line\_item\_id
`optional` | A reference to the line item you are operating with in the request.

Type: string

Example: `8v7jo` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/line_item_curated_categories/xq?curated_category_id=8tujl1p3yn0g` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "line_item_curated_category_id": "xq", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "iqwka", "curated_category_id": "8tujl1p3yn0g", "id": "xq", "created_at": "2021-03-30T17:26:42Z", "updated_at": "2021-03-30T18:22:52Z", "deleted": true } } ``` #### DELETE accounts/:account\_id/line\_item\_curated\_categories/:line\_item\_curated\_category\_id[](#delete-accounts-account-id-line-item-curated-categories-line-item-curated-category-id "Permalink to this headline") Delete the specified line item curated category. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/line_item_curated_categories/:line_item_curated_category_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_curated\_category\_id
*required* | A reference to the line item curated category you are operating with in the request.

Type: string

Example: `1bzq3` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/line_item_curated_categories/xq` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "line_item_curated_category_id": "xq", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "iqwka", "curated_category_id": "9ddrgesiap6o", "id": "xq", "created_at": "2021-03-30T17:26:42Z", "updated_at": "2021-03-30T18:22:52Z", "deleted": true } } ``` ### Line Item Placements #### GET line\_items/placements[](#get-line-items-placements "Permalink to this headline") Retrieve valid `placement` and `product_type` combinations. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/line_items/placements` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | product\_type
*optional* | Scope the response to just the valid placements for the specified product type.

Type: enum

Possible values: `MEDIA`, `PROMOTED_ACCOUNT`, `PROMOTED_TWEETS` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/line_items/placements?product_type=PROMOTED_ACCOUNT` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "product_type": "PROMOTED_ACCOUNT", "placements": [ [ "ALL_ON_TWITTER" ], [ "TWITTER_TIMELINE" ] ] } ], "request": { "params": { "product_type": "PROMOTED_ACCOUNT" } } } ``` ### Media Creatives #### GET accounts/:account\_id/media\_creatives[](#get-accounts-account-id-media-creatives "Permalink to this headline") Retrieve details for some or all media creatives associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/media_creatives` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | campaign\_id
*optional* | Scope the response to just the media creatives associated with the specified campaign.

Type: string

Example: `8gdx6` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | line\_item\_ids
*optional* | Scope the response to just the media creatives associated with the specified line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `8v7jo` | | media\_creative\_ids
*optional* | Scope the response to just the desired media creatives by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `1bzq3` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/media_creatives?media_creative_ids=1bzq3` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "media_creative_ids": [ "1bzq3" ] } }, "next_cursor": null, "data": [ { "line_item_id": "8v7jo", "landing_url": "https://dev.x.com", "creative_type": "INTERSTITIAL_LANDSCAPE_TABLET", "id": "1bzq3", "entity_status": "ACTIVE", "created_at": "2017-07-05T06:00:42Z", "account_media_id": "10miy", "updated_at": "2019-01-11T20:21:26Z", "approval_status": "ACCEPTED", "deleted": false } ] } ``` #### GET accounts/:account\_id/media\_creatives/:media\_creative\_id[](#get-accounts-account-id-media-creatives-media-creative-id "Permalink to this headline") Retrieves details for a specific media creative associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/media_creatives/:media_creative_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | media\_creative\_id
*required* | A reference to the media creative you are operating with in the request.

Type: string

Example: `43853bhii885` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/media_creatives/1bzq3` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "media_creative_id": "1bzq3", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "8v7jo", "landing_url": "https://dev.x.com", "creative_type": "INTERSTITIAL_LANDSCAPE_TABLET", "id": "1bzq3", "entity_status": "ACTIVE", "created_at": "2017-07-05T06:00:42Z", "account_media_id": "10miy", "updated_at": "2019-01-11T20:21:26Z", "approval_status": "ACCEPTED", "deleted": false } } ``` #### POST accounts/:account\_id/media\_creatives[](#post-accounts-account-id-media-creatives "Permalink to this headline") Associate an [account media](/x-ads-api/creatives#account-media) object with the specified line item. Use this endpoint to promote in-stream ads (when the account media `creative_type` is `PREROLL`) or image ads (such as `BANNER` or `INTERSTITIAL`) on the Twitter Audience Platform. **Note**: In order to add media assets to the Account Media resource, use the [POST accounts/:account\_id/media\_library](/x-ads-api/creatives#account-media) endpoint. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/media_creatives` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :--------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
`required` | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | account\_media\_id
`required` | A reference to the account media entity you are operating with in the request.

Type: string

Example: `10miy` | | line\_item\_id
`required` | A reference to the line item you are operating with in the request.

Type: string

Example: `8v7jo` | | landing\_url
`sometimes required` | The URL of the website to direct a user to. This should only be used with TAP images (or "display creatives"). This value will be ignored if used with preroll assets. To associate a URL with a preroll asset, use the [POST accounts/:account\_id/preroll\_call\_to\_actions](/x-ads-api/creatives#post-accounts-account-id-preroll-call-to-actions) endpoint.

**Note**: Required when the line item's objective is set to `WEBSITE_CLICKS`.

Type: string

Example: `https://blog.x.com/` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/media_creatives?line_item_id=8v7jo&account_media_id=10miy` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "line_item_id": "8v7jo", "account_media_id": "10miy", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "8v7jo", "landing_url": "https://dev.x.com", "creative_type": "INTERSTITIAL_LANDSCAPE_TABLET", "id": "1bzq3", "entity_status": "ACTIVE", "created_at": "2017-07-05T06:00:42Z", "account_media_id": "10miy", "updated_at": "2019-01-11T20:21:26Z", "approval_status": "ACCEPTED", "deleted": false } } ``` #### DELETE accounts/:account\_id/media\_creatives/:media\_creative\_id[](#delete-accounts-account-id-media-creatives-media-creative-id "Permalink to this headline") Delete the specified media creative belonging to the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/media_creatives/:media_creative_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | media\_creative\_id
*required* | A reference to the media creative you are operating with in the request.

Type: string

Example: `1bzq3` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/media_creatives/1bzq3` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "media_creative_id": "1bzq3", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "8v7jo", "landing_url": "https://dev.x.com", "creative_type": "INTERSTITIAL_LANDSCAPE_TABLET", "id": "1bzq3", "entity_status": "ACTIVE", "created_at": "2017-07-05T06:00:42Z", "account_media_id": "10miy", "updated_at": "2021-04-16T21:02:55Z", "approval_status": "ACCEPTED", "deleted": true } } ``` ### Promoted Accounts #### GET accounts/:account\_id/promoted\_accounts[](#get-accounts-account-id-promoted-accounts "Permalink to this headline") Retrieve details for some or all promoted accounts associated with one or more line items under the current account. Use [GET users/lookup](https://developer.x.com/en/docs/x-api/v1/accounts-and-users/follow-search-get-users/api-reference/get-users-lookup) to obtain user data for the user accounts identified by `user_id` in the response. An HTTP 400 will be returned if none of the specified line items are configured to contain promoted accounts. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_accounts` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :--------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | line\_item\_ids
*optional* | Scope the response to just the promoted accounts associated with the specified line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `9bpb2` | | promoted\_account\_ids
*optional* | Scope the response to just the desired promoted accounts by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `19pl2` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_accounts?promoted_account_ids=19pl2` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "promoted_account_ids": [ "19pl2" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "line_item_id": "9bpb2", "user_id": "756201191646691328", "id": "19pl2", "entity_status": "ACTIVE", "created_at": "2017-07-05T05:54:13Z", "updated_at": "2017-07-05T05:54:13Z", "approval_status": "ACCEPTED", "deleted": false } ] } ``` #### GET accounts/:account\_id/promoted\_accounts/:promoted\_account\_id[](#get-accounts-account-id-promoted-accounts-promoted-account-id "Permalink to this headline") Retrieve a specific reference to an account associated with a line item under the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_accounts/:promoted_account_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | promoted\_account\_id
*required* | A reference to the promoted account you are operating with in the request.

Type: string

Example: `19pl2` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_accounts/19pl2` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "promoted_account_id": "19pl2", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "9bpb2", "user_id": "756201191646691328", "id": "19pl2", "entity_status": "ACTIVE", "created_at": "2017-07-05T05:54:13Z", "updated_at": "2017-07-05T05:54:13Z", "approval_status": "ACCEPTED", "deleted": false } } ``` #### POST accounts/:account\_id/promoted\_accounts[](#post-accounts-account-id-promoted-accounts "Permalink to this headline") Associate an account (`user_id`) with the specified line item. If the specified line item is not configured to be associated with Promoted Accounts, a HTTP 400 `INCOMPATIBLE_LINE_ITEM` error will be returned. If the specified user is ineligible for promotion, a HTTP 400 will be returned and no users will be promoted. If the provided user is already promoted, the request will be ignored. For more information on Promoted Accounts, see our [campaign management](/x-ads-api/campaign-management#advertiser-api) page. **Note**: It is not possible to update (PUT) promoted accounts entities. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_accounts` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Example: `9bpb2` | | user\_id
*required* | A reference to the user you are operating with in the request. Use [GET users/lookup](https://developer.x.com/en/docs/x-api/v1/accounts-and-users/follow-search-get-users/api-reference/get-users-lookup) to retrieve a user ID for a screen name.

Type: long

Example: `756201191646691328` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_accounts?line_item_id=9bpb2&user_id=756201191646691328` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "line_item_id": "9bpb2", "user_id": "756201191646691328", "id": "19pl2", "entity_status": "ACTIVE", "created_at": "2017-07-05T05:54:13Z", "updated_at": "2017-07-05T05:54:13Z", "approval_status": "ACCEPTED", "deleted": false }, "request": { "params": { "user_id": "756201191646691328", "line_item_id": "9bpb2", "account_id": "18ce54d4x5t" } } } ``` #### DELETE accounts/:account\_id/promoted\_accounts/:promoted\_account\_id[](#delete-accounts-account-id-promoted-accounts-promoted-account-id "Permalink to this headline") Disassociate an account from the specified line item. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_accounts/:promoted_account_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | promoted\_account\_id
*required* | The identifier refers to the instance of a Promoted Account associated with a line item.

Type: string

Example: `19pl2` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_accounts/19pl2` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "line_item_id": "9bpb2", "user_id": "756201191646691328", "id": "19pl2", "entity_status": "ACTIVE", "created_at": "2017-07-05T05:54:13Z", "updated_at": "2017-08-23T18:53:15Z", "approval_status": "ACCEPTED", "deleted": true }, "request": { "params": { "promoted_account_id": "19pl2", "account_id": "18ce54d4x5t" } } } ``` ### Promoted Tweets #### GET accounts/:account\_id/promoted\_tweets[](#get-accounts-account-id-promoted-tweets "Permalink to this headline") Retrieve references to Tweets associated with line items under the current account. Use the [GET accounts/:account\_id/tweets](/x-ads-api/creatives#get-accounts-account-id-tweets) endpoint to fetch the Tweet objects. Use the `tweet_id` values for each promoted\_tweets object. **Note**: When parent line items are deleted, promoted\_tweets are only returned if `with_deleted=true` is specified in the request. These promoted\_tweets are not actually deleted, though (`"deleted": false` in the response). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_tweets` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | line\_item\_ids
*optional* | Scope the response to just the Tweets associated with specific line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `96uzp` | | promoted\_tweet\_ids
*optional* | Scope the response to just the desired promoted Tweets by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `1efwlo` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_tweets?promoted_tweet_ids=1efwlo` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "promoted_tweet_ids": [ "1efwlo" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "line_item_id": "96uzp", "id": "1efwlo", "entity_status": "ACTIVE", "created_at": "2017-06-29T05:06:57Z", "updated_at": "2017-06-29T05:08:46Z", "approval_status": "ACCEPTED", "tweet_id": "880290790664060928", "deleted": false } ] } ``` #### GET accounts/:account\_id/promoted\_tweets/:promoted\_tweet\_id[](#get-accounts-account-id-promoted-tweets-promoted-tweet-id "Permalink to this headline") Retrieve a specific reference to a Tweet associated with a line item under the current account. **Note**: When parent line items are deleted, promoted\_tweets are only returned if `with_deleted=true` is specified in the request. These promoted\_tweets are not actually deleted, though (`"deleted": false` in the response). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_tweets/:promoted_tweet_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | promoted\_tweet\_id
*required* | A reference to the promoted Tweet you are operating with in the request.

Type: string

Example: `1efwlo` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_tweets/1efwlo` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "promoted_tweet_id": "1efwlo", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "96uzp", "id": "1efwlo", "entity_status": "ACTIVE", "created_at": "2017-06-29T05:06:57Z", "updated_at": "2017-06-29T05:08:46Z", "approval_status": "ACCEPTED", "tweet_id": "880290790664060928", "deleted": false } } ``` #### POST accounts/:account\_id/promoted\_tweets[](#post-accounts-account-id-promoted-tweets "Permalink to this headline") Associate one or more Tweets with the specified line item. Not all Tweets are appropriate for promotion, depending on the campaign objective. Please see [Objective-based Campaigns](/x-ads-api/campaign-management#objective-based-campaigns) for more information. When using the `PROMOTED_ACCOUNT` product type, associating a Tweet with the `line_item` will add timeline placements on mobile in addition to the standard `PROMOTED_ACCOUNT` placement. **Note**: It is not possible to update (PUT) promoted Tweet entities. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_tweets` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Example: `8v7jo` | | tweet\_ids
*required* | A comma-separated list of identifiers corresponding to specific Tweets. Up to 50 IDs may be provided.

Type: long

Example: `822333526255120384` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_tweets?line_item_id=8v7jo&tweet_ids=822333526255120384` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "line_item_id": "8v7jo", "id": "1e8i2k", "entity_status": "ACTIVE", "created_at": "2017-06-24T04:21:36Z", "updated_at": "2017-06-24T04:21:36Z", "approval_status": "ACCEPTED", "tweet_id": "822333526255120384", "deleted": false } ], "request": { "params": { "line_item_id": "8v7jo", "tweet_ids": [ 822333526255120384 ], "account_id": "18ce54d4x5t" } }, "total_count": 1 } ``` #### DELETE accounts/:account\_id/promoted\_tweets/:promoted\_tweet\_id[](#delete-accounts-account-id-promoted-tweets-promoted-tweet-id "Permalink to this headline") Disassociate a Tweet from the specified line item. **Note**: A deleted promoted\_tweets entity will be displayed as "Paused" in the ads.x.com UI. Similarly, "pausing" from the UI will disassociate the Tweet from its line item. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promoted_tweets/:promoted_tweet_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | promoted\_tweet\_id
*required* | The identifier refers to the instance of a Promoted Tweet associated with a line item. This comes from the `id` field from a response item to [GET accounts/:account\_id/promoted\_tweets](#get-accounts-account-id-promoted-tweets), not the `tweet_id` of the Tweet in question. Supplied within the resource's path.

Type: string

Example: `1gp8a5` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/promoted_tweets/1gp8a5` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "line_item_id": "9pl99", "id": "1gp8a5", "entity_status": "ACTIVE", "created_at": "2017-08-17T17:02:21Z", "updated_at": "2017-08-18T06:43:48Z", "approval_status": "ACCEPTED", "tweet_id": "844796297743757315", "deleted": true }, "request": { "params": { "promoted_tweet_id": "1gp8a5", "account_id": "18ce54d4x5t" } } } ``` ### Promotable Users #### GET accounts/:account\_id/promotable\_users[](#get-accounts-account-id-promotable-users "Permalink to this headline") Retrieve details for some or all promotable users associated with the current account. The promotable user type is either `FULL` or `RETWEETS_ONLY`. This controls the type of content that is allowed to be promoted by the account. Advertisers must obtain permission to promote another user's content and contact Twitter to get them added to your account as a `RETWEETS_ONLY` promotable user. Provided the permissions are set correctly, you can make requests to the promoted product endpoints that directly reference the Tweet ID of the Tweet you'd like to promote. You can use the [POST accounts/:account\_id/promoted-tweets](/x-ads-api/campaign-management#promoted-tweets) endpoint to promote published Tweets and the [POST accounts/:account\_id/scheduled-promoted-tweets](/x-ads-api/campaign-management#promoted-tweets) endpoint to promote another Twitter Ads account's Scheduled Tweets. You do not have to retweet the target Tweet. When you promote a Tweet with this approach, the `tweet_id` that is returned will be different from the Tweet ID that was provided. Behind the scenes, the Tweet is being retweeted as a nullcasted Tweet and then promoted. The `tweet_id` that is returned corresponds to this new Tweet. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promotable_users` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | promotable\_user\_ids
*optional* | Scope the response to just the desired promotable users by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `l310s` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/promotable_users?promotable_user_ids=l310s` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "promotable_user_ids": [ "l310s" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "user_id": "756201191646691328", "id": "l310s", "created_at": "2016-07-21T22:42:09Z", "updated_at": "2016-07-21T22:42:09Z", "deleted": false, "promotable_user_type": "FULL" } ] } ``` #### GET accounts/:account\_id/promotable\_users/:promotable\_user\_id[](#get-accounts-account-id-promotable-users-promotable-user-id "Permalink to this headline") Retrieve a specific promotable user associated with the current account. The promotable user type is either `FULL` or `RETWEETS_ONLY`. This controls the type of content that is allowed to be promoted by the account. Advertisers must obtain permission to promote another user's content. Provided the permissions are set correctly, you can make requests to the promoted product endpoints that directly reference the Tweet ID of the Tweet you'd like to promote. You do not have to retweet the target Tweet. When you promote a Tweet with this approach, the `tweet_id` that is returned will be different from the Tweet ID that was provided. Behind the scenes, the Tweet is being retweeted as a nullcasted Tweet and then promoted. The `tweet_id` that is returned corresponds to this new Tweet. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/promotable_users/:promotable_user_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | promotable\_user\_id
*optional* | A reference to the promotable user you are operating on within the request

Type: string

Example: `l310s` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/promotable_users/l310s` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "promotable_user_id": "l310s", "account_id": "18ce54d4x5t" } }, "data": { "user_id": "2417045708", "id": "l310s", "created_at": "2017-03-10T17:51:24Z", "updated_at": "2017-03-10T17:51:24Z", "deleted": false, "promotable_user_type": "RETWEETS_ONLY" } } ``` ### Publishers #### GET publishers[](#get-publishers "Permalink to this headline") Retrieve a list of Content Category publishers' details Additional details can be found in the [Video Views Preroll Objective Guide](/x-ads-api/campaign-management#video-views-preroll-objective) **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/publishers` **Parameters[](#parameters "Permalink to this headline")** No request parameters **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/publishers` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": {} }, "next_cursor": null, "data": [ { "monetizable_country_codes": [ "US" ], "promotion_eligible_country_codes": [ "US" ], "username": "PeoplesSports", "user_id": "1353868435021721602", "monetization_restricted": true, "content_category_ids": [ "se" ] }, { "monetizable_country_codes": [ "JP" ], "promotion_eligible_country_codes": [ "JP" ], "username": "NewYork_Jack", "user_id": "1331177123436851206", "monetization_restricted": true, "content_category_ids": [ "sk" ] }, { "monetizable_country_codes": [ "JP" ], "promotion_eligible_country_codes": [ "JP" ], "username": "twispatv", "user_id": "1331165719128461314", "monetization_restricted": true, "content_category_ids": [ "sm" ] }, { "monetizable_country_codes": [ "US" ], "promotion_eligible_country_codes": [ "US" ], "username": "LAThieves", "user_id": "1316808678897455105", "monetization_restricted": true, "content_category_ids": [ "s0" ] }, { "monetizable_country_codes": [ "US" ], "promotion_eligible_country_codes": [ "US" ], "username": "Quicktake_EE", "user_id": "1305900477427724290", "monetization_restricted": true, "content_category_ids": [ "sr" ] }, { "monetizable_country_codes": [ "BR" ], "promotion_eligible_country_codes": [ "BR" ], "username": "eufloribella", "user_id": "1300812459054436354", "monetization_restricted": true, "content_category_ids": [ "sm" ] }, { "monetizable_country_codes": [ "EG" ], "promotion_eligible_country_codes": [ "KW", "EG", "SA", "AE", "LB", "QA" ], "username": "Egypt2021EN", "user_id": "1296077573399678977", "monetization_restricted": true, "content_category_ids": [ "se" ] }, { "monetizable_country_codes": [ "US" ], "promotion_eligible_country_codes": [ "US" ], "username": "ClubShayShay", "user_id": "1283068366706454529", "monetization_restricted": true, "content_category_ids": [ "se" ] }, { "monetizable_country_codes": [ "IN", "KW", "ID", "EG", "SG", "TH", "MY", "PH", "ES", "US", "AU", "SA", "AE", "LB", "GB", "FR", "KR", "BR", "MX", "QA", "CA", "JP" ], "promotion_eligible_country_codes": [ "KW", "EG", "SA", "AE", "LB", "QA" ], "username": "hiaahsanshow", "user_id": "1253421442143641601", "monetization_restricted": false, "content_category_ids": [ "sh" ] }, { "monetizable_country_codes": [ "TH" ], "promotion_eligible_country_codes": [ "TH" ], "username": "HoneKrasae", "user_id": "1240684293719904256", "monetization_restricted": true, "content_category_ids": [ "sr" ] }, { "monetizable_country_codes": [ "US" ], "promotion_eligible_country_codes": [ "US" ], "username": "Sportskind", "user_id": "1232708694418300930", "monetization_restricted": true, "content_category_ids": [ "se" ] }, { "monetizable_country_codes": [ "IN", "KW", "ID", "EG", "SG", "TH", "MY", "PH", "ES", "US", "AU", "SA", "AE", "LB", "GB", "FR", "KR", "BR", "MX", "QA", "CA", "JP" ], "promotion_eligible_country_codes": [ "KW", "EG", "SA", "AE", "LB", "QA" ], "username": "almeerathShow", "user_id": "1229410512762437633", "monetization_restricted": false, "content_category_ids": [ "sh" ] }, { "monetizable_country_codes": [ "US" ], "promotion_eligible_country_codes": [ "US" ], "username": "SeeYourVoiceFOX", "user_id": "1225490734653947904", "monetization_restricted": true, "content_category_ids": [ "sh" ] }, { "monetizable_country_codes": [ "IN", "KW", "ID", "EG", "SG", "TH", "MY", "PH", "ES", "US", "AU", "SA", "AE", "LB", "GB", "FR", "KR", "BR", "MX", "QA", "CA", "JP" ], "promotion_eligible_country_codes": [ "US" ], "username": "AUProSports", "user_id": "1219303449768185859", "monetization_restricted": false, "content_category_ids": [ "se" ] } ] } ``` ### Recommendations #### GET accounts/:account\_id/recommendations[](#get-accounts-account-id-recommendations "Permalink to this headline") Status: *Closed Beta* Retrieve campaign recommendations associated with this ads account. Currently there is a limit of 1 recommendation per funding instrument. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/5/accounts/:account_id/recommendations` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/5/accounts/18ce54d4x5t/recommendations` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} "request": { "params": { "account_id": "18ce54d4x5t" } }, "total_count": 1, "data": [ { "funding_instrument_id": "gpvzb", "id": "62ce8zza1q0w", "account_id": "18ce54d4x5t", "status": "PENDING", "message": "Recommendation for testing", "created_at": "2016-11-14T23:07:54Z", "updated_at": "2016-11-14T23:07:54Z" } ] ``` #### GET accounts/:account\_id/recommendations/:recommendation\_id[](#get-accounts-account-id-recommendations-recommendation-id "Permalink to this headline") Status: *Closed Beta* Retrieve a specific campaign recommendation associated with this ads account. The campaign recommendation contains a full set of changes suggested for the campaign structure represented as an object tree. The response tree is intended to work in conjunction with the Batch API endpoints, but it can also be mapped to single update endpoints as appropriate (Create for POST, Update for PUT, Delete for DELETE). **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/5/accounts/:account_id/recommendations/:recommendation_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | recommendation\_id
*required* | A reference to the recommendation ID you are operating within the request

Type: string

Example: `62ce8zza1q0w` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/5/accounts/18ce54d4x5t/recommendations/62ce8zza1q0w` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "recommendation_id": "62ce8zza1q0w", "account_id": "18ce54d4x5t" } }, "data_type": "recommendations", "data": { "changes": [ { "entity_type": "campaigns", "params": { "start_time": "2016-11-08T22:00:00Z", "daily_budget_amount_local_micro": 2200000, "end_time": "2016-11-16T07:59:00Z", "total_budget_amount_local_micro": 12000000, "id": "64m0d" }, "operation_type": "Update", "dependent_entities": [ { "entity_type": "line_items", "params": { "name": "Campaign for recommendations", "placements": [ "TWITTER_TIMELINE" ], "bid_amount_local_micro": 1430000, "id": "6f5kq", "include_sentiment": "ALL" }, "operation_type": "Update", "dependent_entities": [ { "entity_type": "targeting_criteria", "params": { "id": "a8po6p" }, "operation_type": null, "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "line_item_id": "6f5kq", "name": "election results", "targeting_value": "election results", "targeting_type": "PHRASE_KEYWORD" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "promoted_tweets", "params": { "id": "101ftp" }, "operation_type": "Delete", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "line_item_id": "6f5kq", "name": "Male", "targeting_value": 1, "targeting_type": "GENDER" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "line_item_id": "6f5kq", "name": "San Francisco-Oakland-San Jose CA, US", "targeting_value": "", "targeting_type": "LOCATION" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "promoted_tweets", "params": { "id": "101fto" }, "operation_type": "Delete", "dependent_entities": [] }, { "entity_type": "promoted_tweets", "params": { "line_item_id": "6f5kq", "display_properties": [], "paused": false, "approval_status": "ACCEPTED", "tweet_id": "91125952589766656" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "line_item_id": "6f5kq", "name": "Partner audience targeting", "targeting_value": "v2cx", "targeting_type": "NEGATIVE_BEHAVIOR" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "line_item_id": "6f5kq", "name": "AGE_21_TO_34", "targeting_value": "AGE_21_TO_34", "targeting_type": "AGE" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "id": "a8po6o" }, "operation_type": "Delete", "dependent_entities": [] }, { "entity_type": "promoted_tweets", "params": { "line_item_id": "6f5kq", "display_properties": [], "paused": false, "approval_status": "ACCEPTED", "tweet_id": "991101965843460096" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "promoted_tweets", "params": { "line_item_id": "6f5kq", "display_properties": [], "paused": false, "approval_status": "ACCEPTED", "tweet_id": "991127212156096516" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "line_item_id": "6f5kq", "name": "debate", "targeting_value": "debate", "targeting_type": "NEGATIVE_PHRASE_KEYWORD" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "line_item_id": "6f5kq", "name": "60004, IL, US", "targeting_value": "", "targeting_type": "LOCATION" }, "operation_type": "Create", "dependent_entities": [] }, { "entity_type": "targeting_criteria", "params": { "id": "a8po6n" }, "operation_type": null, "dependent_entities": [] }, { "entity_type": "promoted_tweets", "params": { "id": "101ftn" }, "operation_type": null, "dependent_entities": [] } ] } ] } ], "funding_instrument_id": "gpvzb", "id": "62ce8zza1q0w", "account_id": "18ce54d4x5t", "status": "PENDING", "message": "Recommendation for testing", "created_at": "2016-11-14T23:07:54Z", "updated_at": "2016-11-14T23:07:54Z" } } ``` ### Scheduled Promoted Tweets #### GET accounts/:account\_id/scheduled\_promoted\_tweets[](#get-accounts-account-id-scheduled-promoted-tweets "Permalink to this headline") Retrieve details for some or all scheduled promoted Tweets associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/scheduled_promoted_tweets` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | line\_item\_ids
*optional* | Scope the response to just the scheduled Tweets associated with specific line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `8xdpe` | | scheduled\_promoted\_tweet\_ids
*optional* | Scope the response to just the desired scheduled promoted Tweets by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `1xboq` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_promoted_tweets?scheduled_promoted_tweet_ids=1xboq` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "scheduled_promoted_tweet_ids": [ "1xboq" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "line_item_id": "8xdpe", "id": "1xboq", "created_at": "2017-06-01T19:53:32Z", "updated_at": "2017-06-01T20:00:06Z", "scheduled_tweet_id": "870366669373194240", "tweet_id": "870369382207070208", "deleted": false } ] } ``` #### GET accounts/:account\_id/scheduled\_promoted\_tweets/:scheduled\_promoted\_tweet\_id[](#get-accounts-account-id-scheduled-promoted-tweets-scheduled-promoted-tweet-id "Permalink to this headline") Retrieve a specific scheduled promoted Tweet associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/scheduled_promoted_tweets/:scheduled_promoted_tweet_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | scheduled\_promoted\_tweet\_id
*required* | A reference to the scheduled promoted Tweet you are operating with in the request.

Type: string

Example: `1xboq` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_promoted_tweets/1xboq` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "scheduled_promoted_tweet_id": "1xboq", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "8xdpe", "id": "1xboq", "created_at": "2017-06-01T19:53:32Z", "updated_at": "2017-06-01T20:00:06Z", "scheduled_tweet_id": "870366669373194240", "tweet_id": "870369382207070208", "deleted": false } } ``` #### POST accounts/:account\_id/scheduled\_promoted\_tweets[](#post-accounts-account-id-scheduled-promoted-tweets "Permalink to this headline") Associate a scheduled Tweet with the specified line item. **Note**: It is not possible to update (PUT) scheduled promoted Tweet entities. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/scheduled_promoted_tweets` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Example: `8xdpe` | | scheduled\_tweet\_id
*required* | A reference to the scheduled Tweet you are operating with in the request.

Type: long

Example: `870358555227860992` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_promoted_tweets?line_item_id=8xdpe&scheduled_tweet_id=870358555227860992` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "line_item_id": "8xdpe", "id": "1xtfl", "created_at": "2017-06-08T07:25:26Z", "updated_at": "2017-06-08T07:25:26Z", "scheduled_tweet_id": "870358555227860992", "tweet_id": null, "deleted": false }, "request": { "params": { "line_item_id": "8xdpe", "scheduled_tweet_id": 870358555227860992, "account_id": "18ce54d4x5t" } } } ``` #### DELETE accounts/:account\_id/scheduled\_promoted\_tweets/:scheduled\_promoted\_tweet\_id[](#delete-accounts-account-id-scheduled-promoted-tweets-scheduled-promoted-tweet-id "Permalink to this headline") Disassociate a scheduled Tweet from the specified line item. **Note**: `scheduled_promoted_tweets` can only be deleted *before* the scheduled Tweet's `scheduled_at` time. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/scheduled_tweets/:scheduled_tweet_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | scheduled\_promoted\_tweet\_id
*required* | A reference to the scheduled promoted Tweet you are operating with in the request. This is the `id` attribute from a [GET accounts/:account\_id/scheduled\_promoted\_tweets](#get-accounts-account-id-scheduled-promoted-tweets) response object.

Type: string

Example: `1xtfl` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_promoted_tweets/1xtfl` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "line_item_id": "8xdpe", "id": "1xtfl", "created_at": "2017-06-08T07:25:26Z", "updated_at": "2017-06-15T05:14:12Z", "scheduled_tweet_id": "870358555227860992", "tweet_id": null, "deleted": true }, "request": { "params": { "scheduled_promoted_tweet_id": "1xtfl", "account_id": "18ce54d4x5t" } } } ``` ### Targeting Criteria #### GET accounts/:account\_id/targeting\_criteria[](#get-accounts-account-id-targeting-criteria "Permalink to this headline") Retrieve details for some or all of the targeting criteria associated with line items under the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/targeting_criteria` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_ids
*required* | Scope the response to just the targeting criteria under the specified line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `8u94t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | lang
*optional* | An [ISO-639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code. When passed, an additional `localized_name` attribute will be returned in the response for objects where a localized name is available.

Type: string

Example: `fr` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | targeting\_criterion\_ids
*optional* | Scope the response to just the desired targeting criteria by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `dpl3a6` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/targeting_criteria?line_item_ids=8u94t` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "line_item_ids": [ "8u94t" ] } }, "next_cursor": null, "data": [ { "line_item_id": "8u94t", "name": "Custom audience targeting", "id": "dpl3a6", "operator_type": "EQ", "created_at": "2017-05-26T03:29:35Z", "targeting_value": "249yj", "updated_at": "2017-05-26T03:29:35Z", "deleted": false, "targeting_type": "CUSTOM_AUDIENCE" } ] } ``` #### GET accounts/:account\_id/targeting\_criteria/:targeting\_criterion\_id[](#get-accounts-account-id-targeting-criteria-targeting-criterion-id "Permalink to this headline") Retrieve a specific targeting criterion associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/targeting_criteria/:targeting_criterion_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | targeting\_criterion\_id
*required* | A reference to the targeting criterion you are operating with in the request.

Type: string

Example: `eijd4y` | | lang
*optional* | An [ISO-639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code. When passed, an additional `localized_name` attribute will be returned in the response for objects where a localized name is available.

Type: string

Example: `fr` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/targeting_criteria/eijd4y` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "targeting_criterion_id": "eijd4y", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "619jl", "name": "🤖", "id": "eijd4y", "created_at": "2017-07-06T16:51:04Z", "targeting_value": "🤖", "updated_at": "2017-07-06T16:51:04Z", "deleted": false, "targeting_type": "BROAD_KEYWORD" } } ``` #### POST accounts/:account\_id/targeting\_criteria[](#post-accounts-account-id-targeting-criteria "Permalink to this headline") See the [Targeting Options](/x-ads-api/campaign-management#targeting-options) page to find `targeting_value`s for specific targeting types. We recommend that you refresh all data weekly, to ensure that you are working with the latest set of targeting type values. We change values and available targeting criteria from time to time; while the majority of these don't change often, some do. There is no guarantee that these values will not change. Use the `BROAD_KEYWORD`, `EXACT_KEYWORD`, `PHRASE_KEYWORD`, or `UNORDERED_KEYWORD` targeting types with the keywords specified in the `targeting_value`. Exclude keywords by using the `operator_type` request parameter set to `NE`. See [targeting keyword types](/x-ads-api/campaign-management#targeting) for a detailed description of each type. **Note**: It is only possible to target a single age bucket per line item. **Note**: To target a Custom Audience, that audience must be targetable. i.e., `targerable` *must* equal `true`. **Note**: When using targeting type `TV_SHOW`, there must be at least one `LOCATION` targeting criterion on the line item prior to setting the `TV_SHOW` targeting and all `LOCATION` must be within the same locale as the `TV_SHOW` being targeted. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/targeting_criteria` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Example: `69ob` | | operator\_type
*required* | Specify the relationship that the targeting criterion should have. For example, to exclude keywords, use `operator_type=NE`.

Type: enum

Possible values: `EQ`, `NE`, `GTE`, `LT` | | targeting\_type
*required* | The type of targeting that will be applied to this line item.

Possible non-keyword-based values include: `AGE`, `DEVICE`, `EVENT`, `CAMPAIGN_ENGAGEMENT`, `CAMPAIGN_ENGAGEMENT_LOOKALIKE`, `CONVERSATION`, `ENGAGEMENT_TYPE`, `FOLLOWERS_OF_USER`, `GENDER`, `INTEREST`, `LANGUAGE`, `LIVE_TV_EVENT`, `LOCATION`, `NETWORK_ACTIVATION_DURATION`, `NETWORK_OPERATOR`, `PLATFORM`, `PLATFORM_VERSION`, `SIMILAR_TO_FOLLOWERS_OF_USER`, `TV_SHOW`, `USER_ENGAGEMENT`, `USER_ENGAGEMENT_LOOKALIKE`, `WIFI_ONLY`

**Note**: It is only possible to target a single `AGE` bucket per line item.

Possible keyword-based values include: `BROAD_KEYWORD`, `EXACT_KEYWORD`, `PHRASE_KEYWORD`, `UNORDERED_KEYWORD`

Possible custom audience values include: `CUSTOM_AUDIENCE`, `CUSTOM_AUDIENCE_EXPANDED`

Possible installed app store category values: `APP_STORE_CATEGORY`, `APP_STORE_CATEGORY_LOOKALIKE`

Possible Twitter Audience Platform (TAP) app exclusion: `APP_LIST` (may only be used with `operator_type=NE`) | | targeting\_value
*required* | Specify which user, which interest, which location, which event, which platform, which platform version, which device, which keyword or phrase, which gender, which custom audience, which app store category, or which exclusion of an app list this targeting will be applied to, depending on the selected targeting\_type.

Type: string

Example: `174958347` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/targeting_criteria?line_item_id=619jl&targeting_type=BROAD_KEYWORD&targeting_value=technology` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "line_item_id": "619jl", "name": "technology", "id": "fbyjlr", "created_at": "2017-09-06T07:31:21Z", "targeting_value": "technology", "updated_at": "2017-09-06T07:31:21Z", "deleted": false, "targeting_type": "BROAD_KEYWORD" }, "request": { "params": { "line_item_id": "619jl", "targeting_type": "BROAD_KEYWORD", "targeting_value": "technology", "account_id": "18ce54d4x5t" } } } ``` #### POST batch/accounts/:account\_id/targeting\_criteria[](#post-batch-accounts-account-id-targeting-criteria "Permalink to this headline") Allows the batch creation of new Targeting Criteria with a single request. **Batch Requests** * The current maximum batch size is 500. * All parameters are sent in the request body and a `Content-Type` of `application/json` is required. * Batch requests fail or succeed together as a group and all API responses for both error and success preserve the item order of the initial request. **Batch Responses** Batch API responses return an ordered collection of items. Otherwise, they are identical in structure to their corresponding single-item endpoints. **Batch Errors** * Request-level errors (eg. max batch size exceeded) are shown in the response under the `errors` object. * Item-level errors (eg. missing required Targeting Criteria parameter) are shown in the response under the `operation_errors` object. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/batch/accounts/:account_id/targeting_criteria` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :-------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | operation\_type
*required* | The per item operation type being performed.

Type: enum

Possible values: `Create`, `Delete` | | params
*required* | A JSON object containing all the parameters for the targeting criteria objects. For a list of required and optional targeting criteria parameters, see [here](#post-accounts-account-id-targeting-criteria).

In addition, this endpoint supports an `operator_type` parameter that works in conjunction with certain `targeting_type` values. The possible values for this parameter are `EQ` for equal to, `GTE` for greater than or equal to, `LT` for less than, and `NE` for not equal to. | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/batch/accounts/18ce54d4x5t/targeting_criteria` ```json theme={null} [ { "operation_type":"Create", "params":{ "line_item_id":"6f9an", "targeting_type":"LOCATION", "targeting_value":"5122804691e5fecc" } }, { "operation_type":"Delete", "params":{ "targeting_criterion_id":"al2rua" } } ] ``` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data_type": "targeting_criterion", "data": [ { "line_item_id": "6f9an", "name": "San Francisco-Oakland-San Jose CA, US", "id": "al7vt2", "location_type": "CITY", "operator_type": "EQ", "created_at": "2016-11-11T22:59:50Z", "targeting_value": "5122804691e5fecc", "updated_at": "2016-11-11T22:59:50Z", "deleted": false, "targeting_type": "LOCATION" }, { "line_item_id": "6keuo", "name": "accounts", "id": "al2rua", "operator_type": "EQ", "created_at": "2016-11-11T17:50:19Z", "targeting_value": "accounts", "updated_at": "2016-11-11T22:59:50Z", "deleted": true, "targeting_type": "BROAD_KEYWORD" } ], "request": [ { "params": { "line_item_id": "6f9an", "targeting_type": "LOCATION", "targeting_value": "5122804691e5fecc", "account_id": "18ce54d4x5t" }, "operation_type": "Create" }, { "params": { "targeting_criterion_id": "al2rua", "account_id": "18ce54d4x5t" }, "operation_type": "Delete" } ] } ``` #### DELETE accounts/:account\_id/targeting\_criteria/:targeting\_criterion\_id[](#delete-accounts-account-id-targeting-criteria-targeting-criterion-id "Permalink to this headline") Delete the specified targeting criterion belonging to the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/targeting_criteria/:targeting_criterion_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | targeting\_criterion\_id
*required* | A reference to the targeting criterion you are operating with in the request.

Type: string

Example: `dpl3a6` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/targeting_criteria/dpl3a6` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": { "line_item_id": "8u94t", "name": "Custom audience targeting", "id": "dpl3a6", "created_at": "2017-05-26T03:29:35Z", "targeting_value": "249yj", "updated_at": "2017-08-30T18:38:58Z", "deleted": true, "targeting_type": "CUSTOM_AUDIENCE" }, "request": { "params": { "targeting_criterion_id": "dpl3a6", "account_id": "18ce54d4x5t" } } } ``` ### Targeting Options * [App Store Categories](#get-targeting-criteria-app-store-categories) * [Conversation](#get-targeting-criteria-conversations) * [Devices](#get-targeting-criteria-devices) * [Events](#get-targeting-criteria-events) * [Interests](#get-targeting-criteria-interests) * [Languages](#get-targeting-criteria-languages) * [Locations](#get-targeting-criteria-locations) * [Network Operators](#get-targeting-criteria-network-operators) * [Platform Versions](#get-targeting-criteria-platform-versions) * [Platforms](#get-targeting-criteria-platforms) * [TV Markets](#get-targeting-criteria-tv-markets) * [TV Shows](#get-targeting-criteria-tv-shows) #### GET targeting\_criteria/app\_store\_categories[](#get-targeting-criteria-app-store-categories "Permalink to this headline") Discover available app store category-based targeting criteria for Promoted Products. App store categories are available for the iOS App Store and the Google Play store only. Installed app category targeting allows targeting of users based on the categories of apps they have installed or have indicated interest in. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/app_store_categories` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | | q
*optional* | An optional query to scope a targeting criteria. Omit this parameter to retrive all.

Type: string

Example: `music` | | os\_type
*optional* | Scope the results by a specific app store.

Type: enum

Possible values: `ANDROID`, `IOS` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/app_store_categories?q=music&os_type=IOS` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "Games: Music", "targeting_type": "APP_STORE_CATEGORY", "targeting_value": "qouq", "os_type": "IOS" }, { "name": "Music", "targeting_type": "APP_STORE_CATEGORY", "targeting_value": "qov2", "os_type": "IOS" } ], "request": { "params": { "q": "music", "os_type": "IOS" } } } ``` #### GET targeting\_criteria/conversations[](#get-targeting-criteria-conversations "Permalink to this headline") Discover available conversation-based targeting criteria for Promoted Products. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/conversations` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | conversation\_type
*optional* | An optional query to scope to a certain conversation type.

Type: enum

Possible values: `ACTORS`, `ATHLETES`, `BOOK_GENRES`, `BOOKS`, `BRAND_CATEGORIES`, `BRANDS`, `CELEBRITIES`, `COACHES`, `DIGITAL_CREATORS`, `ENTERTAINMENT_BRANDS`, `ENTERTAINMENT_PERSONALITIES`, `FICTIONAL_CHARACTERS`, `JOURNALISTS`, `LIFESTYLES`, `MOVIE_GENRES`, `MOVIES`, `MUSIC_GENRES`, `MUSICIANS`, `NEWS_STORIES`, `NEWS`, `PERSONS`, `PLACES`, `PODCASTS`, `POLITICAL_AFFILIATIONS`, `POLITICIANS`, `PRODUCTS`, `RADIO_STATIONS`, `SPORTS_LEAGUES`, `SPORTS_PERSONALITIES`, `SPORTS_TEAMS`, `SPORTS`, `TRENDS`, `TV_SHOWS`, `VIDEO_GAME_PLATFORMS`, `VIDEO_GAME_PUBLISHERS`, `VIDEO_GAMES` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/conversations?count=2` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "count": 2 } }, "next_cursor": "1f7m7", "data": [ { "targeting_type": "CONVERSATION", "targeting_value": "a1", "name": "NFL", "conversation_type": "SPORTS" }, { "targeting_type": "CONVERSATION", "targeting_value": "a2", "name": "NBA", "conversation_type": "SPORTS" } ] } ``` #### GET targeting\_criteria/devices[](#get-targeting-criteria-devices "Permalink to this headline") Discover available device-based targeting criteria for Promoted Products. Device targeting is available for Promoted Tweets. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/devices` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | q
*optional* | An optional query to scope a targeting criteria. Omit this parameter to retrive all.

Type: string

Example: `apple` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/devices?count=2&q=iphone` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "iPhone 3GS", "manufacturer": "Apple", "os_type": "iOS", "targeting_value": "1q", "targeting_type": "DEVICE" }, { "name": "iPhone 4", "manufacturer": "Apple", "os_type": "iOS", "targeting_value": "1r", "targeting_type": "DEVICE" } ], "request": { "params": { "q": "iphone", "count": 2 } } } ``` #### GET targeting\_criteria/events[](#get-targeting-criteria-events "Permalink to this headline") Discover available event-based targeting criteria for Promoted Products. Only one event can be targeted per line item. **Note**: Events often exist across timezones, leading to complications when considering event times from cross-timezone perspectives. To simplify this, all event `start_time` and `end_time` values on this endpoint are represented in UTC±00:00, irrespective of the event's locale and timezone. This design should be kept in mind when querying and interacting with event `start_time` and `end_time` values. For example, Independence Day for the US is represented as `start_time=2017-07-04T00:00:00Z` and `end_time=2017-07-05T00:00:00Z` in UTC±00:00, and thus avoids the issue of this holiday existing across multiple timezones within the US. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/events` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | event\_types
*required* | An optional query to scope to certain event types.

Type: enum

Possible values: `CONFERENCE`, `HOLIDAY`, `MUSIC_AND_ENTERTAINMENT`, `OTHER`, `POLITICS`, `RECURRING`, `SPORTS` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | country\_codes
*optional* | An optional query to scope a targeting criteria search to particular countries with the 2 letter ISO country code. If this parameter is not specified, all events are returned.

Type: string | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | end\_time
*optional* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the campaign will end.

Type: string

Example: `2017-10-05T00:00:00Z` | | start\_time
*optional* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the line item will begin serving.

**Note**: Defaults to the current time.

Type: string

Example: `2017-07-05T00:00:00Z` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/events?count=1` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "count": 1 } }, "data_type": "events", "data": [ { "reach": { "total_reach": null }, "name": "New Year's", "start_time": "2017-12-31T00:00:00Z", "top_users": [], "top_tweets": [], "top_hashtags": [], "gender_breakdown_percentage": {}, "end_time": "2018-01-02T00:00:00Z", "country_code": null, "device_breakdown_percentage": {}, "targeting_value": "1ex", "is_global": true, "event_type": "HOLIDAY", "country_breakdown_percentage": {} } ], "next_cursor": "uww0" } ``` #### GET targeting\_criteria/interests[](#get-targeting-criteria-interests "Permalink to this headline") Discover available interest-based targeting criteria for Promoted Products. Interests change infrequently, however we suggest you refresh this list at least once weekly. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/interests` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | q
*optional* | An optional query to scope a targeting criteria. Omit this parameter to retrive all.

Type: string

Example: `books` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/interests?q=books` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "Books and literature/Biographies and memoirs", "targeting_type": "INTEREST", "targeting_value": "1001" } ], "request": { "params": { "q": "books", "count": 1 } }, "next_cursor": "6by4n4" } ``` #### GET targeting\_criteria/languages[](#get-targeting-criteria-languages "Permalink to this headline") Discover languages available for targeting. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/languages` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | q
*optional* | An optional query to scope a targeting criteria. Omit this parameter to retrive all.

Type: string

Example: `english` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/languages?q=english` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "English", "targeting_type": "LANGUAGE", "targeting_value": "en" } ], "request": { "params": { "q": "english" } }, "next_cursor": null } ``` #### GET targeting\_criteria/locations[](#get-targeting-criteria-locations "Permalink to this headline") Discover available location-based targeting criteria for Promoted Products. Geo-targeting is available for Promoted Accounts and Promoted Tweets at the country level, state/region level, city level, and postal code level. Postal code targeting must be used if you wish to retrieve analytics at the postal code level. **Note**: To retrieve specific targetable cities, such as San Francisco or New York, use the `CITIES` enum with the `location_type` request parameter. To target Designated Market Areas (DMAs), use the `METROS` enum. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/locations` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | country\_code
*optional* | An optional query to scope a targeting criteria search to a specific country with the 2 letter ISO country code. Omit this parameter to retrieve results for all countries.

Type: string

Example: `JP` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | location\_type
*optional* | Scope the results by a specific kind of location. More granular targeting than `COUNTRIES` may not be available in all locations.

Type: enum

Possible values: `COUNTRIES`, `REGIONS`, `METROS`, `CITIES`, `POSTAL_CODES` | | q
*optional* | An optional query to scope a targeting criteria search. Omit this parameter to retrieve all results.

Type: string

Example: `New York` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/locations?location_type=CITIES&q=los angeles` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "Los Angeles, Los Angeles CA, CA, USA", "country_code": "US", "location_type": "CITIES", "targeting_value": "3b77caf94bfc81fe", "targeting_type": "LOCATION" }, { "name": "East Los Angeles, Los Angeles CA, CA, USA", "country_code": "US", "location_type": "CITIES", "targeting_value": "67571a7baaa5906b", "targeting_type": "LOCATION" }, { "name": "Lake Los Angeles, Los Angeles CA, CA, USA", "country_code": "US", "location_type": "CITIES", "targeting_value": "ea9bfbd43c93400f", "targeting_type": "LOCATION" }, { "name": "Los Gatos, San Francisco-Oakland-San Jose CA, CA, USA", "country_code": "US", "location_type": "CITIES", "targeting_value": "a2de7c70b82b0ca0", "targeting_type": "LOCATION" }, { "name": "Los Altos, Monterey-Salinas CA, CA, USA", "country_code": "US", "location_type": "CITIES", "targeting_value": "6a4364ea6f987c10", "targeting_type": "LOCATION" }, { "name": "Los Banos, CA, USA", "country_code": "US", "location_type": "CITIES", "targeting_value": "b1b6fc646de75904", "targeting_type": "LOCATION" }, { "name": "Los Alamitos, Los Angeles CA, CA, USA", "country_code": "US", "location_type": "CITIES", "targeting_value": "0799ff0a3c1006e9", "targeting_type": "LOCATION" }, { "name": "Los Angeles, US", "country_code": "US", "location_type": "CITIES", "targeting_value": "019940ae78c7b3bc", "targeting_type": "LOCATION" } ], "request": { "params": { "location_type": "CITIES", "q": "los angeles" } }, "next_cursor": null } ``` #### GET targeting\_criteria/network\_operators[](#get-targeting-criteria-network-operators "Permalink to this headline") Discover available network operator-based targeting criteria for Promoted Products. This endpoint enables you to lookup targetingable carriers, such as AT\&T, Verizon, Sprint, T-Mobile, etc., in multiple countries. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/network_operators` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | country\_code
*optional* | An optional query to scope a targeting criteria search to a specific country with the 2 letter ISO country code. If this parameter is not specified only partner audiences for the United States are returned.

Type: string

Default: `US` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | q
*optional* | An optional query to scope a targeting criteria search. Omit this parameter to this parameter to retrieve all results.

Type: string

Examples: `Airpeak` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/network_operators?count=5&country_code=US` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "country_code": "US", "targeting_type": "NETWORK_OPERATOR", "name": "Advantage", "targeting_value": "2l" }, { "country_code": "US", "targeting_type": "NETWORK_OPERATOR", "name": "Aeris", "targeting_value": "1b" }, { "country_code": "US", "targeting_type": "NETWORK_OPERATOR", "name": "Airadigm", "targeting_value": "2t" }, { "country_code": "US", "targeting_type": "NETWORK_OPERATOR", "name": "Airlink PCS", "targeting_value": "14" }, { "country_code": "US", "targeting_type": "NETWORK_OPERATOR", "name": "Airpeak", "targeting_value": "1i" } ], "request": { "params": { "country_code": "US", "count": 5 } }, "next_cursor": "o7x9iet1a5u608olj4" } ``` #### GET targeting\_criteria/platform\_versions[](#get-targeting-criteria-platform-versions "Permalink to this headline") Discover available mobile OS version-based targeting criteria for Promoted Products. Platform version targeting is available for Promoted Accounts and Promoted Tweets. This allows targeting down to the point release of a mobile operating system version, such as Android 8.0 or iOS 10.0. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/platform_versions` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | q
*optional* | An optional query to scope a targeting criteria search. Omit this parameter to this parameter to retrieve all results.

Type: string

Examples: `jelly bean` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/platform_versions` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ {...}, { "name": "Ice Cream Sandwich", "number": "4.0", "os_type": "Android", "targeting_type": "PLATFORM_VERSION", "targeting_value": "17" }, { "name": "Jelly Bean", "number": "4.1", "os_type": "Android", "targeting_type": "PLATFORM_VERSION", "targeting_value": "18" }, {...} ], "data_type": "targeting_criterion", "request": { "params": {} } } ``` #### GET targeting\_criteria/platforms[](#get-targeting-criteria-platforms "Permalink to this headline") Discover available platform-based targeting criteria for Promoted Products. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/platforms` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | q
*optional* | An optional query to scope a targeting criteria search. Omit this parameter to this parameter to retrieve all results.

Type: string

Examples: `ios`, `blackberry` | | lang
*optional* | Using a [ISO-639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code. When passed, an additional localized\_name attribute will be returned in the response.

Type: int, string

Example: `fr` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/platforms` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "iOS", "targeting_type": "PLATFORM", "targeting_value": "0" }, { "name": "Android", "targeting_type": "PLATFORM", "targeting_value": "1" }, { "name": "BlackBerry phones and tablets", "targeting_type": "PLATFORM", "targeting_value": "2" }, { "name": "Mobile web on other devices", "targeting_type": "PLATFORM", "targeting_value": "3" }, { "name": "Desktop and laptop computers", "targeting_type": "PLATFORM", "targeting_value": "4" } ], "request": { "params": {} } } ``` #### GET targeting\_criteria/tv\_markets[](#get-targeting-criteria-tv-markets "Permalink to this headline") Discover available TV markets where TV shows can be targeted. Returns markets by locale that can used to query the [GET targeting\_criteria/tv\_shows](/x-ads-api/campaign-management#get-targeting-criteria-tv-shows) endpoint. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/tv_markets` **Parameters[](#parameters "Permalink to this headline")** None **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/tv_markets` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "name": "France", "country_code": "FR", "locale": "fr-FR" }, { "name": "Chile", "country_code": "CL", "locale": "es-CL" }, { "name": "Germany", "country_code": "DE", "locale": "de-DE" }, { "name": "Netherlands", "country_code": "NL", "locale": "nl-NL" }, { "name": "United States", "country_code": "US", "locale": "en-US" }, { "name": "Venezuela", "country_code": "VE", "locale": "es-VE" }, { "name": "Brazil", "country_code": "BR", "locale": "pt-BR" }, { "name": "Mexico", "country_code": "MX", "locale": "es-MX" }, { "name": "Colombia", "country_code": "CO", "locale": "es-CO" }, { "name": "United Kingdom", "country_code": "GB", "locale": "en-GB" }, { "name": "Argentina", "country_code": "AR", "locale": "es-AR" }, { "name": "Japan", "country_code": "JP", "locale": "ja-JP" }, { "name": "Canada", "country_code": "CA", "locale": "en-CA" }, { "name": "Spain", "country_code": "ES", "locale": "es-ES" }, { "name": "Italy", "country_code": "IT", "locale": "it-IT" }, { "name": "United States - Hispanic", "country_code": "US", "locale": "es-US" }, { "name": "Ireland", "country_code": "IE", "locale": "en-IE" } ], "request": { "params": {} } } ``` #### GET targeting\_criteria/tv\_shows[](#get-targeting-criteria-tv-shows "Permalink to this headline") Discover available TV show-based targeting criteria for Promoted Products. TV show targeting is available for Promoted Tweets in certain markets. See the [GET targeting\_criteria/tv\_markets](/x-ads-api/campaign-management#get-targeting-criteria-tv-markets) endpoint for available markets. **Note**: Any audience that contains fewer than 1,000 users will appear with an `estimated_users` value of `1000`. **Note**: TV channel and genre targeting options are no longer supported. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/targeting_criteria/tv_shows` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | locale
*required* | A required parameter that specifies the tv\_market\_locale to query for available TV shows. TV markets are queried based on `locale` returned from the [GET targeting\_criteria/tv\_markets](/x-ads-api/campaign-management#get-accounts-account-id-targeting-criteria).

Type: string

Example: `en-US` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `50`
Min, Max: `1`, `50` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | q
*optional* | An optional query to scope a targeting criteria search. Omit this parameter to this parameter to retrieve all results.

Type: string

Examples: `ios`, `blackberry` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/targeting_criteria/tv_shows?locale=en-US&q=news&count=1` **Example Response[](#example-response "Permalink to this headline")** ```jdon theme={null} { "data": [ { "name": "NewsWatch", "targeting_value": 10027243420, "genre": "PAID", "locales": [ { "language": "en", "country": "US" } ] } ], "next_cursor": "c-22838-zdQDJrTxSvOYfQOhb2IlGQ", "request": { "params": { "locale": { "countryCode": "US", "languageCode": "en" }, "count": 1, "q": "news" } } } ``` ### Targeting Suggestions #### GET accounts/:account\_id/targeting\_suggestions[](#get-accounts-account-id-targeting-suggestions "Permalink to this headline") Get up to 50 keyword or user targeting suggestions to complement your initial selection. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/targeting_suggestions` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#accounts). The specified account must be associated with the authenticating user.

Type: string

Example: `18ce54d4x5t` | | suggestion\_type
*required* | Specify the type of suggestions to return.

Type: enum

Possible values: `KEYWORD`, `USER_ID` | | targeting\_values
*required* | Comma separated collection of either keywords or user IDs used to seed the suggestions.

**Note**: These two types of suggestions cannot be mixed.

Example: `756201191646691328` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `30`
Min, Max: `1`, `50` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/targeting_suggestions?suggestion_type=KEYWORD&targeting_values=developers&count=2"` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "data": [ { "suggestion_type": "KEYWORD", "suggestion_value": "devs" }, { "suggestion_type": "KEYWORD", "suggestion_value": "software" } ], "request": { "params": { "suggestion_type": "KEYWORD", "targeting_values": [ "developers" ], "count": 2, "account_id": "18ce54d4x5t" } } } ``` ### Tax Settings #### GET accounts/:account\_id/tax\_settings[](#get-accounts-account-id-tax-settings "Permalink to this headline") Retrieve tax setting details associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/tax_settings` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/tax_settings` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t" } }, "data": { "tax_id": "GB896391250", "address_city": "London", "business_relationship": "SELF", "address_street1": "21 March St", "address_last_name": null, "address_company": "ABC, Inc.", "tax_category": "BUSINESS_WITH_VAT", "address_postal_code": "SW1A 1AA", "bill_to": "NOT_SET", "address_region": "London", "address_country": "GB", "address_first_name": null, "invoice_jurisdiction": "NOT_SET", "address_street2": null, "address_email": null } } ``` #### PUT accounts/:account\_id/tax\_settings[](#put-accounts-account-id-tax-settings "Permalink to this headline") Update the tax settings for the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/tax_settings` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | address\_city
*optional* | The city for the account owner's address.

Type: string

Example: `San Francisco` | | address\_country
*optional* | The two-letter country code for the account owner's address.

Type: string

Example: `US` | | address\_email
*optional* | The email associated with the account owner's address.

Type: string

Example: `api@mctestface.com` | | address\_first\_name
*optional* | The first name for the account owner's address.

Type: string

Example: `API` | | address\_last\_name
*optional* | The last name for the account owner's address.

Type: string

Example: `McTestface` | | address\_name
*optional* | The company name for the account owner's address.

Type: string

Example: `ABC, Co.` | | address\_postal\_code
*optional* | The postal code for the account owner's address.

Type: string

Example: `94102` | | address\_region
*optional* | The region for the account owner's address.

Type: string

Example: `California` | | address\_street1
*optional* | The street line for the account owner's address.

Type: string

Example: `21 March St` | | address\_street2
*optional* | The second street line for the account owner's address.

Type: string

Example: `Suite 99` | | bill\_to
*optional* | The entity that is billed.

Type: enum

Possible values: `ADVERTISER`, `AGENCY` | | business\_relationship
*optional* | Whether the account is owned by the advertiser or by the agency.

Type: enum

Possible values: `AGENCY`, `SELF` | | client\_address\_city
*optional* | The city for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `Toronto` | | client\_address\_country
*optional* | The two-letter country code for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `CA` | | client\_address\_email
*optional* | The email associated with the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `ads@brand.com` | | client\_address\_first\_name
*optional* | The first name for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `Brand` | | client\_address\_last\_name
*optional* | The last name for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `Advertiser` | | client\_address\_name
*optional* | The company name for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `Brand, Inc.` | | client\_address\_postal\_code
*optional* | The postal code for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `M5H 2N2` | | client\_address\_region
*optional* | The region for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `Ontario` | | client\_address\_street1
*optional* | The street line for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `280 Queen St W` | | client\_address\_street2
*optional* | The second street line for the advertiser's address.

Set this when the ads account is owned by an agency.

Type: string

Example: `The 6` | | invoice\_jurisdiction
*optional* | Invoice jurisdiction.

Type: enum

Possible values: `LOI_SAPIN`, `NONE`, `NOT_SET` | | tax\_category
*optional* | Whether the taxation should be individual or business.

Type: enum

Possible values: `BUSINESS_NO_VAT`, `BUSINESS_WITH_VAT`, `INDIVIDUAL` | | tax\_exemption\_id
*optional* | VAT exemption ID.

Type: sting

Example: `12345` | | tax\_id
*optional* | VAT registration ID.

Type: string

Possible values: `67890` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/tax_settings?address_name=ABC, Co.` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "address_name": "ABC Co." } }, "data": { "tax_id": "GB896391250", "address_city": "London", "business_relationship": "SELF", "address_street1": "21 March St", "address_last_name": null, "address_company": "ABC, Co.", "tax_category": "BUSINESS_WITH_VAT", "address_postal_code": "SW1A 1AA", "bill_to": "NOT_SET", "address_region": "London", "address_country": "GB", "address_first_name": null, "invoice_jurisdiction": "NOT_SET", "address_street2": null, "address_email": null } } ``` ### Tracking Tags #### GET accounts/:account\_id/tracking\_tags[](#get-accounts-account-id-tracking-tags "Permalink to this headline") Retrieve details for some or all tracking tags associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/tracking_tags` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | count
*optional* | Specifies the number of records to try and retrieve per distinct request.

Type: int

Default: `200`
Min, Max: `1`, `1000` | | cursor
*optional* | Specifies a cursor to get the next page of results. See [Pagination](/x-ads-api/introduction) for more information.

Type: string

Example: `8x7v00oow` | | line\_item\_ids
*optional* | Scope the response to just the tracking tags associated with specific line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `96uzp` | | sort\_by
*optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](/x-ads-api/introduction) for more information.

Type: string

Example: `created_at-asc` | | tracking\_tag\_ids
*optional* | Scope the response to just the desired tracking tags by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided.

Type: string

Example: `3m82` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | | with\_total\_count
*optional* | Include the `total_count` response attribute.

**Note**: This parameter and `cursor` are exclusive.

**Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/tracking_tags?tracking_tag_ids=3m82` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "tracking_tag_ids": [ "3m82" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "line_item_id": "fdwcl", "tracking_tag_url": "https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309", "tracking_tag_type": "IMPRESSION_TAG", "id": "3m82", "created_at": "2019-06-26T17:04:26Z", "updated_at": "2019-06-26T17:04:26Z", "deleted": false } ] } ``` #### GET accounts/:account\_id/tracking\_tags/:tracking\_tag\_id[](#get-accounts-account-id-tracking-tags-tracking-tag-id "Permalink to this headline") Retrieve a specific tracking tag associated with the current account. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/tracking_tags/:tracking_tag_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | tracking\_tag\_id
*required* | A reference to the tracking tag you are operating with in the request.

Type: string

Example: `555j` | | with\_deleted
*optional* | Include deleted results in your request.

Type: boolean

Default: `false`
Possible values: `true`, `false` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/tracking_tags/555j` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "with_deleted": true, "tracking_tag_id": "555j", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "72v2x", "tracking_tag_url": "https://ad.doubleclick.net/ddm/trackimp/N6344.2061500TWITTER-OFFICIAL/B23028778.279118262;dc_trk_aid=473354132;dc_trk_cid=119658253", "tracking_tag_type": "IMPRESSION_TAG", "id": "555j", "created_at": "2020-08-13T23:02:03Z", "updated_at": "2020-08-13T23:02:03Z", "deleted": false } } ``` #### POST accounts/:account\_id/tracking\_tags[](#post-accounts-account-id-tracking-tags "Permalink to this headline") Associate a tracking tag with the specified line item. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/tracking_tags` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | line\_item\_id
*required* | A reference to the line item you are operating with in the request.

Type: string

Example: `8v7jo` | | tracking\_tag\_type
*required* | The type of tracking tag.

Type: enum

Possible value: `IMPRESSION_TAG`, `CLICK_TRACKER` | | tracking\_tag\_url
*required* | The tracking tag url provided by the tracking partner.

Type: string

Example: `https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309` | **Example Request[](#example-request "Permalink to this headline")** `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/tracking_tags?line_item_id=fdwcl&tracking_tag_type=IMPRESSION_TAG&tracking_tag_url=https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "line_item_id": "fdwcl", "tracking_tag_type": "IMPRESSION_TAG", "tracking_tag_url": "https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "fdwcl", "tracking_tag_url": "https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309", "tracking_tag_type": "IMPRESSION_TAG", "id": "3m82", "created_at": "2019-06-26T17:04:26Z", "updated_at": "2019-06-26T17:04:26Z", "deleted": false } } ``` #### PUT accounts/:account\_id/tracking\_tags/:tracking\_tag\_id[](#put-accounts-account-id-tracking-tags-tracking-tag-id "Permalink to this headline") Associate a tracking tag with the specified line item. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/tracking_tags/:tracking_tag_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | tracking\_tag\_url
*required* | The tracking tag url provided by the tracking partner.

Type: string

Example: `https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/tracking_tags/3m82?tracking_tag_url=https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "tracking_tag_id": "3m82", "tracking_tag_url": "https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "fdwcl", "tracking_tag_url": "https://ad.doubleclick.net/ddm/trackimp/N1234.2061500TWITTER-OFFICIAL/B9156151.125630439;dc_trk_aid=1355;dc_trk_cid=8675309", "tracking_tag_type": "IMPRESSION_TAG", "id": "3m82", "created_at": "2019-06-26T17:04:26Z", "updated_at": "2022-01-26T17:04:26Z", "deleted": false } } ``` #### DELETE accounts/:account\_id/tracking\_tags/:tracking\_tag\_id[](#delete-accounts-account-id-tracking-tags-tracking-tag-id "Permalink to this headline") Disassociate a tracking tag from the specified line item. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/tracking_tags/:tracking_tag_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts). The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | tracking\_tag\_id
*required* | A reference to the tracking tag you are operating with in the request.

Type: string

Example: `555j` | **Example Request[](#example-request "Permalink to this headline")** `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/tracking_tags/555j` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "tracking_tag_id": "555j", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "72v2x", "tracking_tag_url": "https://ad.doubleclick.net/ddm/trackimp/N6344.2061500TWITTER-OFFICIAL/B23028778.279118262;dc_trk_aid=473354132;dc_trk_cid=119658253", "tracking_tag_type": "IMPRESSION_TAG", "id": "555j", "created_at": "2020-08-13T23:02:03Z", "updated_at": "2021-08-29T17:12:58Z", "deleted": true } } ``` ### User Settings ([https://app.getpostman.com/run-collection/1d12b9fc623b8e149f87](https://app.getpostman.com/run-collection/1d12b9fc623b8e149f87)) #### GET accounts/:account\_id/user\_settings/:user\_id[](#get-accounts-account-id-user-settings-user-id "Permalink to this headline") Retrieves user settings. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/user_settings/:user_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](/x-ads-api/campaign-management#get-accounts).
The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | user\_id
*required* | A reference to the user you are operating with in the request. Use GET users/lookup to retrieve a user ID for a screen name.

Type: long

Example: `756201191646691328` | **Example Request[](#example-request "Permalink to this headline")** `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/user_settings/756201191646691328` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "user_id": "756201191646691328" } }, "data": { "notification_email": "user@domain.com", "contact_phone": "", "contact_phone_extension": "" } } ``` #### PUT accounts/:account\_id/user\_settings/:user\_id[](#put-accounts-account-id-user-settings-user-id "Permalink to this headline") Updates user settings. Requires user context. Not accessible by account admins. **Resource URL[](#resource-url "Permalink to this headline")** `https://ads-api.x.com/12/accounts/:account_id/user_settings/:user_id` **Parameters[](#parameters "Permalink to this headline")** | Name | Description | | :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id
*required* | The identifier for the leveraged account. Appears within the resource's path and [GET accounts](/x-ads-api/campaign-management#get-accounts).
The specified account must be associated with the authenticated user.

Type: string

Example: `18ce54d4x5t` | | user\_id
*required* | A reference to the user you are operating with in the request. Use GET users/lookup to retrieve a user ID for a screen name.

Type: long

Example: `756201191646691328` | | notification\_email
*optional* | Email to use for account notifications.

Type: string

Example: `user@domain.com` | | contact\_phone
*optional* | Contact phone number.

Type: string

Example: `202-555-0128` | | contact\_phone\_extension
*optional* | Extension for contact `contact_phone`.

Type: string

Example: `1234` | **Example Request[](#example-request "Permalink to this headline")** `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/user_settings/756201191646691328?notification_email='user@domain.com'&subscribe_email_types=ACCOUNT_PERFORMANCE,PERFORMANCE_IMPROVEMENT"` **Example Response[](#example-response "Permalink to this headline")** ```json theme={null} { "request": { "params": { "account_id": "18ce54d4x5t", "user_id": "756201191646691328" "notification_email": "user@domain.com", "subscribed_campaign_events": [ "ACCOUNT_PERFORMANCE", "PERFORMANCE_IMPROVEMENT" ] } }, "data": { "notification_email": "user@domain.com", "contact_phone": "", "Contact_phone_extension": "" } } ``` # Catalog Management Source: https://docs.x.com/x-ads-api/catalog-management ## Overview The Catalog API is a commerce solution that gives advertisers the ability to set up product feeds, group products into sets, and holistically manage catalog products. The Catalog API will be a core tool that enables programmatic catalog management and grants advertisers more control over how their catalogs are ingested and updated. A catalog holds everything regarding the user's product and product set and is associated with the user's handle. Currently, **one user can only create one catalog**.  The Catalog API supports 2 ways to ingest products: 1. Scheduled Feed: the user can add a feed url (CSV, TSV, and XML) that will be periodically fetched (X currently supports a file up to 8GB) 2. Batch Products API: the user can view, create, update and delete product attributes with batch (JSON) requests X merchants are able to create product sets based on filter rules. By doing so, they will be able to attach additional metadata to their products, and create special product sets to showcase these products through organic commerce features or dynamic product ads. The Catalog API and X shopping manager supports two types of product sets.  1. Manual: the user can select up to 50 products, and set a name and a description 2. Filter: the user can add up to 30 filters to automatically generate sets with products that satisfy these filters. Types of filters include price, Google product category, product type, inventory, sale price, and custom fields. Catalog API comes with 4 endpoints to manage catalog: * [Product Catalogs](https://developer.x.com/en/docs/x-ads-api/catalog-management/api-reference/product-catalogs) - Creating a container for product information * [Products](https://developer.x.com/en/docs/x-ads-api/catalog-management/api-reference/products) -  Synchronously adding, updateing, viewing, and removing products from the catalogs * [Product Sets](https://developer.x.com/en/docs/x-ads-api/catalog-management/api-reference/product-sets) -  Sorting products into groups based on product information * [Schedule Feeds](https://developer.x.com/en/docs/twitter-ads-api/catalog-management/api-reference/scheduled-feeds) - Seting up the asynchronous ingestion of file-based product data into the catalogs ### Prerequisites for the Catalog API Catalog API endpoints are currently available via early-access only. To apply for access, please reach out to your X representative.  You are required to accept the terms of service via [X Shopping Manager](https://ads.x.com/shopping_manager). For the details on Shopping Manager and product specifications, please refer to [Product specifications guide](https://business.x.com/en/help/shopping-specs.html). ### Rate Limits  The following table lists the rate limits for each endpoint. #### Product Catalog | HTTP Method | Rate limit | | :---------------------------------------------- | :------------------- | | GET /product\_catalogs | 1,000 per 15 minutes | | POST /product\_catalogs | 20 per 15 minutes | | PUT /product\_catalogs/:product\_catalog\_id | 20 per 15 minutes | | DELETE /product\_catalogs/:product\_catalog\_id | 20 per 15 minutes | #### Batch Products API | HTTP Method | Rate limit | | :------------------------------------------------------- | :------------------- | | GET /product\_catalogs/:product\_catalog\_id/products | 1,000 per 15 minutes | | PUT /product\_catalogs/:product\_catalog\_id/products | 600 per 15 minutes | | DELETE /product\_catalogs/:product\_catalog\_id/products | 150 per 15 minutes | #### Product Sets | HTTP Method | Rate limit | | :------------------------------------------------------------------------------ | :------------------- | | GET /product\_catalogs/:product\_catalog\_id/product\_sets | 2,000 per 15 minutes | | POST /product\_catalogs/:product\_catalog\_id/product\_sets | 100 per 15 minutes | | PUT /product\_catalogs/:product\_catalog\_id/product\_sets/:product\_set\_id | 500 per 15 minutes | | DELETE /product\_catalogs/:product\_catalog\_id/product\_sets/:product\_set\_id | 100 per 15 minutes | #### Product Feeds | HTTP Method | Rate limit | | :-------------------------------------------------------------------------------- | :------------------- | | GET /product\_catalogs/:product\_catalog\_id/product\_feeds | 1,000 per 15 minutes | | POST /product\_catalogs/:product\_catalog\_id/product\_feeds | 20 per 15 minutes | | PUT /product\_catalogs/:product\_catalog\_id/product\_feeds/:product\_feed\_id | 20 per 15 minutes | | DELETE /product\_catalogs/:product\_catalog\_id/product\_feeds/:product\_feed\_id | 20 per 15 minutes | # Creatives Source: https://docs.x.com/x-ads-api/creatives ## Overview Creatives are any entity that can be promoted in a campaign. Posts can include text, images, GIFs, videos, or cards. Cards can include images or videos. Image, GIF, or video creatives are uploaded using either the [POST media/upload](/x-api/media/upload-media#post-media-upload) — a simple upload endpoint that only supports images — or the POST media/upload (chunked) endpoints. These can be added to cards: * POST accounts/:account\_id/cards Tweets: * POST accounts/:account\_id/tweets - To add cards to Tweets, use the card\_uri parameter. Scheduled Tweets: * POST accounts/:account\_id/scheduled\_tweets For additional details on cards, please see the Cards page. The Promoted Video page provides details on associating videos with cards or Tweets. ### Cards The Ads API supports several card types that can be used in Tweets, which can then be promoted in campaigns. **Note**: once Tweeted, card details are publicly visible. This may include information about the user who owns the card. #### Image The following image specifications apply to assets used in [Cards](/x-ads-api/creatives#cards-2). Images must be 3MB or less and have an a width of at least 800px. In addition, we support the following width:height aspect ratios. * Website: 1:1 and 1.91:1 * Image App Download: 1:1 and 1.91:1 * Poll: 1.91:1 * Image Conversation: 1.91:1 * Image Direct Message: 1.91:1 We support the following image formats: .bmp, .jpeg, and .png. #### Video The following video specifications apply to assets used in [Cards](/x-ads-api/creatives#cards-2). We support the following width:height aspect ratios. * Video Website: 16:9 and 1:1 * Video App Download: 16:9 and 1:1 * Poll: 16:9 * Video Conversation: 16:9 * Video Direct Message: 16:9 ### Promoted Video This document provides a brief overview of the process for uploading and promoting video through the Ads API. The Ads API supports Promoted Video in [Tweets](/x-ads-api/creatives#tweets-2) and in the following cards: * [Video Website](https://devcommunity.x.com/t/ads-api-version-11/168814) * [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)](/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 To create a Tweet, use the [POST accounts/:account\_id/tweet](/x-ads-api/creatives#get-accounts-account-id-tweets) endpoint along with the video's `id`. In this step, you can also provide a video title, description, and call-to-action (CTA). These values are user-facing. #### 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](/x-api/media/upload-media) endpoint. Create the card using one of the following endpoints: * [POST accounts/:account\_id/cards/video\_website](https://devcommunity.x.com/t/ads-api-version-11/168814) * [POST accounts/:account\_id/cards/video\_app\_download](https://devcommunity.x.com/t/ads-api-version-11/168814) * [POST accounts/:account\_id/cards/video\_conversation](https://devcommunity.x.com/t/ads-api-version-11/168814) using the video's `id` and, optionally, the image's `media_id` (for the poster image). Finally, create the Tweet using the POST accounts/:account\_id/tweet endpoint. Cards are attached to Tweets using the `card_uri` parameter. #### General Information 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)](/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. * Uploaded poster images should be in png or jpg format. There are no aspect ratio or size requirements, but the poster image will be adjusted to fit the video player. ## Guides ### Scheduled Tweets #### Introduction Scheduled Tweets allow an advertiser or user to create a Tweet that can be scheduled to go live at a later date. In addition to being able create and manage these Tweets, the API allows the ability to associate these Tweets with a line item, to be promoted once the Tweet goes live. This allows advertisers to stage create native Tweets and plan their campaign creatives in advance of any key initiatives. For example, staging a Tweet creative to live immediately upon a new product announcement. The full set of functionality provided by the Scheduled Tweets API endpoints are listed below: * Create, modify and view newly scheduled Tweets * Associate a Scheduled Tweet with a line item * Query and manage existing scheduled Tweets * Once a Scheduled Tweet goes live, retrieve the live Tweet `id` #### API Endpoints The entire set of endpoints related to the above functionality is listed below: #### Scheduled Tweet Management * [GET accounts/:account\_id/scheduled\_tweets](/x-ads-api/creatives#get-accounts-account-id-scheduled-tweets) (retrieve a list of all Scheduled Tweets) * [GET accounts/:account\_id/scheduled\_tweets/:scheduled\_tweet\_id](/x-ads-api/creatives#get-accounts-account-id-scheduled-tweets) (lookup a specific Scheduled Tweet using its `id`) * [POST accounts/:account\_id/scheduled\_tweets](/x-ads-api/creatives#post-accounts-account-id-scheduled-tweets) (create a new Scheduled Tweet) * [PUT accounts/:account\_id/scheduled\_tweets/:scheduled\_tweet\_id](/x-ads-api/creatives#example-request-39) (modify an existing Scheduled Tweet) * [DELETE accounts/:account\_id/scheduled\_tweets/:scheduled\_tweet\_id](/x-ads-api/creatives#example-request-40) (delete a Scheduled Tweet using its `id`) * [GET accounts/:account\_id/scheduled\_tweets/preview/:scheduled\_tweet\_id](/x-ads-api/creatives#scheduled-tweets-2) (preview an existing Scheduled Tweet) #### Scheduled Promoted Tweets * [GET accounts/:account\_id/scheduled\_promoted\_tweets](/x-ads-api/campaign-management#get-accounts-account-id-scheduled-promoted-tweets) (retreive a list of all Scheduled Promoted Tweets) * [GET accounts/:account\_id/scheduled\_promoted\_tweets/:scheduled\_promoted\_tweet\_id](/x-ads-api/campaign-management#get-accounts-account-id-scheduled-promoted-tweets-scheduled-promoted-tweet-id) (lookup a Promoted Scheduled Tweet using its `id`) * [POST accounts/:account\_id/scheduled\_promoted\_tweets](/x-ads-api/campaign-management#post-accounts-account-id-scheduled-promoted-tweets) (create a new Scheduled Promoted Tweet) * [DELETE accounts/:account\_id/scheduled\_promoted\_tweets/:scheduled\_promoted\_tweet\_id](/x-ads-api/campaign-management#delete-accounts-account-id-scheduled-promoted-tweets-scheduled-promoted-tweet-id) (delete an existing Scheduled Promoted Tweet using its `id`) #### Scheduled Tweet View * [GET accounts/:account\_id/scheduled\_tweets/preview/:scheduled\_tweet\_id](/x-ads-api/creatives#scheduled-tweets-2) (view an existing Scheduled Tweet) Due to the nature of Scheduled Tweets being separate entities from “live” Tweets, there are two different sets of validations run on any creates or edits to these Tweets. The first set of validation rules are run during the Scheduled Tweet creation step, specifically: #### Scheduled Tweet Create: * Validate that the authenticated user has access to create organic Tweets for a given @handle Promoted-Only Tweet create privileges requires authenticated user to be an account user with [Tweet composer permissions](https://developer.x.com/content/developer-twitter/en/docs/ads/campaign-management/api-reference/authenticated-user-access#get-accounts-account-id-authenticated-user-access) * Validate that there are no more than 30 Tweets that are scheduled to be created within a 15 minute window of the scheduled\_at time. A SCHEDULED\_TWEET\_LIMIT\_EXCEEDED error message indicates that too many Scheduled Tweets have been scheduled within the same future, 15 minute time frame.  Advertisers will need to remove an existing Scheduled Tweet or move the scheduled\_at time earlier or later. #### Scheduled Tweet goes "live": * These validation rules are run at the scheduled\_at time and are identical to those applied on regular Tweet creation in the API. For example, a Scheduled Tweet will not go live and the scheduled\_status will be set to FAILED if the Scheduled Tweet contains both an image and a gif #### Workflow **Create a new Scheduled Tweet** A new Scheduled Tweet can be created using the [POST accounts/:account\_id/scheduled\_tweets](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/scheduled-tweets#post-accounts-account-id-scheduled-tweets) endpoint. This endpoint has the following required parameters, `scheduled_at` time along with the Tweet `text` if no media entities are included in the Tweet. In addition, this endpoint provides a few additional options that allow you to create a scheduled Tweet on behalf of another @handle via the `as_user_id` param along with the ability to add a card (`card_uri`) and any media (`media_ids`). Note, a Tweet can only contain entities of the same type, i.e., either Video, Gif or Image. The `nullcast` param controls whether the Tweet is a “Promoted-Only” Tweet or not. All newly created Scheduled Tweets are "Promoted-Only" (`nullcast=true`) by default. If `nullcast=false` then an Organic Scheduled Tweet is created Once a Scheduled Tweet is successfully created, the response will contain an `id` field, which refers to the unique identifier of the Scheduled Tweet itself. In addition to this field, another field called `tweet_id` is also returned. This field is `null` initially, however once the Tweet goes live this field is populated with the ID of the “live” Tweet. ``` twurl -H 'ads-api.x.com' -X POST "/6/accounts/:account_id/scheduled_tweets" -d 'scheduled_at=2017-12-24T23:59:00Z&text=Happy Holidays!' ``` This will create the following Scheduled Tweet: ```json theme={null} { "request": { "params": { "text": "Happy Holidays!", "scheduled_at": "2017-12-24T23:59:00Z" } }, "data": { "completed_at": null, "id_str": "917507899668099072", "text": "Happy Holidays!", "user_id": "3271358660", "scheduled_status": "SCHEDULED", "id": 917507899668099100, "nullcast": true, "created_at": "2017-10-09T21:51:44Z", "scheduled_at": "2017-12-24T23:59:00Z", "card_uri": null, "updated_at": "2017-10-09T21:51:44Z", "tweet_id": null, "media_keys": [] } } ``` Once this Scheduled Tweet goes live, the `tweet_id` field will be populated with the "live" Tweet's ID. **View a Scheduled Tweet** The [GET accounts/:account\_id/tweet\_previews](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/tweet-previews#get-accounts-account-id-tweet-previews) endpoint can then be used with the Scheduled Tweet `id` from the previous step to generate a preview of the Tweet. The API response will contain an iframe URL that is ready to be used to render a preview for the Scheduled Tweet. The relevant CSS and images will be served directly via X. ``` twurl -H ads-api.x.com -X GET "/6/accounts/18ce54bgxky/tweet_previews?tweet_type=SCHEDULED&tweet_ids=917507899668099072 ``` ```json theme={null} { "request": { "params": { "tweet_type": "SCHEDULED", "tweet_ids": [ "917507899668099072" ], "account_id": "18ce54bgxky" } }, "data": [ { "tweet_id": "1126633863155871744", "preview": "<\iframe class='tweet-preview' src='https://ton.smf1.x.com/ads-manager/tweet-preview/index.html?data=H4sIAAAAAAAAAK1WTW%2FjNhD9Kyyv9dqSZdmOgALJbhJsCyxaIDkUWBUEJY0lNhSpklRSN%2FV%2F71CyZbm297S%2BWJxPzpt5I71T9wbgaELeaW6AOygYxyN9rlryhW9JcEPmUbKIkuiG%2FBjgj8yD8IZOqChoEobz5TKK1ssojOP1KlwtFl7BrDMY4oIW%2FTatlMzB3z7JZ940W%2FJZS1Hwrf0Btc60Kve3oMmGSwsTWgjbSL7tXJjhqgSafA0mYfzHhIJywgmwNHmnFbeV4yU%2Bf0WN3daZlvtDa8Gw2htrdRCZXrlDU92aHIPStA2CKOekMrD5KaWVc02SztIZps%2Bh0rIAg27TXNcpJQYk2ii90VLqt7R3ht%2B4cQoMeVClUIAPd03Th01nvDfx0ClmoJFYk0aouGst82gqROaKskf03KCr7LLvXnXN02K3QTHFaziovYdH0seL5qswitfLZTBq6FGIRfSe9Lm1FTfkY%2BX%2FFcpPAlNRC7eufdFSY1%2BxASh84oo8YitzYXM9IZ%2FuaNcQ1HjMbQc61l0VXDmYlsJVbTYVGq0KwPCi2cf5tQFFnjR2zyDU6YycwX%2Fr3oRzvfKpwTaSZ8NfQUoU%2FUsetanxAV79VElhHbm1oIrSiILcvvgquqSN0Q7y8Uz2TQdjWa5bhZP8IUShEeh8IvIxkVB7SY%2FyKctaIL%2B0kgQrMp8n0SKJ10eWxZ4t%2FBXHUzg4idu6nOnNxsIQ1Yka2D9aDc0sQTNQPJP%2B2sgqvPUrGLERozL68ToNLRELvBj4VuZaOSOy1mmsdAi2dxaWOeyhlRzVl6TYozMnhHIjJLCM5y%2BlwaweHOn96afg%2FuHhnl60ETUvgR1HpJsQntkptrcuO0bOOhuLg1NBPfyH6Swrpw2W9O24rBu8kwH8DuEdns9Kv1hLc5rsxBaTLcN1HIdhHIVRuFov5wtMXH748vO2%2BP0jUzjFXE7%2FbMa3%2BFZl3z1ZxhWyjv2fwlfy9NaY6LhO0lm4WC3WcRSvlqO4UqiXYT7C%2B7vwcT7SWlFAxg3LtMHNfH2ODnZ4kIPVPRo9jnN1r5eDNup%2BIy2y5GxuDrQqYMNb6dje9or44HOyQYTnWs%2FXXoD7%2Ba8WrGO4hwZuK%2B2Qt%2F32tAPhB%2B4xt238qjVQtpIbuuvIP6wbjfAIhStncO3eZ0f9keMHmYHuo%2BCwFoJ%2BDfktdEF0JPfebbxgct30b%2BdhY%2B51u%2FGm2U2IR7rW%2FbJU%2FdcBfpEchHjwoO52%2FwENmVvErwgAAA%3D%3D'>" } ] } ``` A sample view of the newly created Scheduled Tweet is shown above **Associate a Scheduled Tweet with a line item** While Scheduled Tweets can be used to create Organic Tweets, we also allow partners to create a “Promoted-Only” (`nullcast=true`) Tweet either one of which can be associated with a line item. In order to facilitate this, we provide a [POST accounts/:account\_id/scheduled\_promoted\_tweets](https://developer.x.com/content/developer-twitter/en/docs/ads/campaign-management/api-reference/scheduled-promoted-tweets#post-accounts-account-id-scheduled-promoted-tweets) endpoint as well. This endpoint only allows a single Promoted Scheduled Tweet to be associated with a line item in a single API call. In order to associate multiple Scheduled Tweets to the same line item, multiple API calls are necessary. Please note that it is not possible to modify an existing Scheduled Promoted Tweet. ``` twurl -H 'ads-api.x.com' -X POST "/6/accounts/:account_id/scheduled_promoted_tweets" -d 'line_item_id=a44qc&scheduled_tweet_id=917507899668099072' ``` ```json theme={null} { "data": { "line_item_id": "a44qc", "id": "26576", "created_at": "2017-10-09T22:24:16Z", "updated_at": "2017-10-09T22:24:16Z", "scheduled_tweet_id": "917507899668099072", "tweet_id": null, "deleted": false }, "request": { "params": { "line_item_id": "a44qc", "scheduled_tweet_id": 917507899668099100, "account_id": "aaaaa" } } } ``` This endpoint only creates an association between a given Scheduled Tweet and a line item. Once the campaign/line item flight dates are current, the line item with automatically start serving the corresponding “live” Tweet. While we do validate during this step that the Scheduled Tweet is in the `SCHEDULED` state, and that the given Scheduled Tweet is valid for the given objective, no other validations are run. Any remaining validation rules that apply to the line item and Scheduled Tweet are run when the Tweet goes “live” In order to ensure that there are no issues with campaign serving it is recommend that the Scheduled Tweet be `scheduled_at` a time prior to the campaign/line item flight dates. For example, let's say the Scheduled Tweet is set to go live after the campaign start date (and that there is only a single Tweet associated with a single line item), then the campaign will be `ACTIVE`, however given that the Scheduled Tweet is not live yet, there will be no creatives available for serving. **Scheduled Tweet Management** The remaining sets of endpoints allow API consumers to manage all their Scheduled Tweets and Scheduled Promoted Tweets. These APIs can be used to either return a list of all Scheduled Tweets optionally filtered by a given state as well as lookup a given Scheduled Tweet by its `id`. #### What happens when a Scheduled Tweet goes live? Once a given Scheduled Tweet is about to go live, or in other words at the `scheduled_at` time, the following updates are made: * The “live” Tweet is created however this may have a latency of upto 1 second * The `tweet_id` is added to the following entities: * Scheduled Tweet * Promoted Scheduled Tweet * A new Promoted Tweet entity is created #### Best Practices The following best practices are recommended when creating or promoting Scheduled Tweets: * Ensure that the Tweet is valid when creating the Scheduled Tweet (for example, a Tweet can only have either an Image, Video or Gif and not any combination of the three) * Ensure that the campaign flight dates (i.e., the `start_time` and `end_time`) align with the `scheduled_at` time for the Scheduled Tweet * Scheduled Tweets should not be scheduled for more than one year in the future (365 days) * Tweet preview is currently not supported for Scheduled Tweets (this is ability to preview Scheduled Tweets prior to creation) ### Media Library #### Introduction The Media Library endpoints provide the ability to manage images, GIFs, and videos for X Ads accounts. Media assets in the library can be used in Tweets and to create cards. They can also be reused in multiple [creatives](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/overview), eliminating the need to upload the same asset multiple times. #### API Endpoints * [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](/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" ``` ```json theme={null} { "media_id":966947208837742592, "media_id_string":"966947208837742592", "size":74194, "expires_after_secs":86400, "image":{ "image_type":"image/jpeg", "w":800, "h":418 } } ``` Next, using the media ID, add the media to the ads account’s library using the [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) endpoint. ``` twurl -X POST -H ads-api.x.com "/9/accounts/18ce54d4x5t/media_library?file_name=latte.jpeg&media_category=TWEET_IMAGE&media_key=966947208837742592&name=Latte" ``` ```json theme={null} { "request":{ "params":{ "name":"Latte", "file_name":"latte.jpeg", "media_category":"TWEET_IMAGE", "account_id":"18ce54d4x5t", "media_key":966947208837742592 } }, "data":{ "tweeted":false, "name":"Latte", "file_name":"latte.jpeg", "media_url":"https://pbs.twimg.com/media/DWtJXQNUQAAdPZj.jpg", "media_category":"TWEET_IMAGE", "media_key":"3_966947208837742592", "created_at":"2018-02-23T08:05:54Z", "media_status":"TRANSCODE_COMPLETED", "media_key":"966947208837742592", "media_type":"IMAGE", "updated_at":"2018-02-23T08:06:17Z", "deleted":false } } ``` **Note:** Tweeting images, GIFs, or videos directly after the upload also adds media to the library. #### Request Parameters All Media Library POST requests require a media identifier. This value is returned during the upload step. When using the media\_id, as in the example above, a media\_category must also be specified. There are four possible category values: AMPLIFY\_VIDEO, TWEET\_GIF, TWEET\_IMAGE, and TWEET\_VIDEO. Optionally, name and file\_name values can be set for objects in the Media Library. These attributes help users distinguish between media variants in the library. For videos, it’s also possible to set a title and a description. They values are intended to be passed as the video\_title and video\_description request parameters with the [POST accounts/:account\_id/tweet](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet) endpoint. In the Tweet, this text appears under the video. #### Attributes The Media Library, formally introduces the concept of the media\_key. This is the unique identifier for objects in the library. Media keys are string values in the following format: 13\_875943225764098048. These are fully supported in all of our card endpoints. In addition, the Media Library response includes the media\_id, represented as a string. This is included for resources that do not currently accept a media key: [Tweets](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet)\*, [Tweet preview](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/tweets#get-accounts-account-id-tweet-preview)\*, and [Scheduled Tweets](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/scheduled-tweets#post-accounts-account-id-scheduled-tweets). We are working toward supporting media keys everywhere. The aspect\_ratio attribute is returned for GIFs and videos. This can be used to filter media for use in cards that only accept particular aspect ratios. \*These endpoints support the video\_id parameter, which is a media key. #### Usage In this section, the following image will be used in a Tweet and to create a website card. ``` twurl -H ads-api.x.com "/9/accounts/18ce54d4x5t/media_library/3_966947208837742592" ``` ```json theme={null} { "request":{ "params":{ "account_id":"18ce54d4x5t", "media_key":"3_966947208837742592" } }, "data":{ "tweeted":false, "name":"Latte", "file_name":"latte.jpeg", "media_url":"https://pbs.twimg.com/media/DWtJXQNUQAAdPZj.jpg", "media_category":"TWEET_IMAGE", "media_key":"3_966947208837742592", "created_at":"2018-02-23T08:05:54Z", "media_status":"TRANSCODE_COMPLETED", "media_key":"966947208837742592", "media_type":"IMAGE", "updated_at":"2018-02-23T08:06:17Z", "deleted":false } } ``` **Tweet** We can create the Tweet by referencing the images using media\_keys. ``` twurl -X POST -H ads-api.x.com "/9/accounts/18ce54d4x5t/tweet?text=coffee&media_keys=966947208837742592&as_user_id=756201191646691328&trim_user=true" ``` ```json theme={null} { "data":{ "created_at":"Fri Feb 23 08:20:05 +0000 2018", "id":966950781302665218, "id_str":"966950781302665218", "text":"coffee https://t.co/T772Hx5GNT", "truncated":false, "entities":{ "hashtags":[ ], "symbols":[ ], "user_mentions":[ ], "urls":[ ], "media":[ { "id":966947208837742592, "id_str":"966947208837742592", "indices":[ 7, 30 ], "media_url":"http://pbs.twimg.com/media/DWtJXQNUQAAdPZj.jpg", "media_url_https":"https://pbs.twimg.com/media/DWtJXQNUQAAdPZj.jpg", "url":"https://t.co/T772Hx5GNT", "display_url":"pic.x.com/T772Hx5GNT", "expanded_url":"https://x.com/apimctestface/status/966950781302665218/photo/1", "type":"photo", "sizes":{ "thumb":{ "w":150, "h":150, "resize":"crop" }, "large":{ "w":800, "h":418, "resize":"fit" }, "medium":{ "w":800, "h":418, "resize":"fit" }, "small":{ "w":680, "h":355, "resize":"fit" } } } ] }, "source":"Ads API Internal Test App", "in_reply_to_status_id":null, "in_reply_to_status_id_str":null, "in_reply_to_user_id":null, "in_reply_to_user_id_str":null, "in_reply_to_screen_name":null, "user":{ "id":756201191646691328, "id_str":"756201191646691328" }, "geo":null, "coordinates":null, "place":null, "contributors":[ 2417045708 ], "retweet_count":0, "favorite_count":0, "favorited":false, "retweeted":false, "possibly_sensitive":false, "scopes":{ "followers":false }, "lang":"en" }, "request":{ "params":{ "as_user_id":756201191646691328, "text":"coffee", "account_id":"18ce54d4x5t", "media_keys":[ 966947208837742592 ], "trim_user":true } } } ``` **Website Card** All of our cards endpoints support media keys. We will create the [website card](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/website#post-accounts-account-id-cards-website) by referencing the image’s media\_key. ``` twurl -X POST -H ads-api.x.com "/11/accounts/18ce54d4x5t/cards" ``` ```json theme={null} { "name": "components create cards", "components": [ { "type": "MEDIA", "media_key": "3_1323490622599176192" }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "INSTALL" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android" } } ] } ``` We then associate this card with a Tweet using its card\_uri. ### Identifying Cards #### Introduction Cards are customizable ad formats that use media and that can be associated with a website, an app, or with calls to action to drive certain user engagements, such as starting a Direct Message. They can be appended to Tweets, Scheduled Tweets, or Draft Tweets. Cards may be referenced in Tweet objects in one of two ways: by the card's card\_uri or by its preview\_url. Example values for each are presented below. | card\_uri | preview\_url | | :------------------------- | :----------------------------------------------------------------------------------------- | | card://1043282691834048513 | [https://cards.x.com/cards/18ce54d4x5t/68w3s](https://cards.x.com/cards/18ce54d4x5t/68w3s) | **Note**: As of Ads API version 3, only the card\_uri is generated and returned in the cards response for newly created cards. **Note**: As of Ads API version 5, the preview\_url in the cards response is no longer returned. The type of reference in the Tweet object response will depend on the way the Tweet was created. In other words, if the Tweet was created using the card\_uri request parameter, the card URI value will appear in the response. If the preview\_url was included as part of the Tweet text, on the other hand, the preview URL will appear in the response. #### Identifying Tweets with card\_uri For Tweets created using the card's URI value, find the reference to the card in the card\_uri response attribute. The example response below uses the [GET accounts/:account\_id/tweets](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/tweets#get-accounts-account-id-tweets) endpoint. ``` $ twurl -H ads-api.x.com "/9/accounts/18ce54d4x5t/tweets?trim_user=true&tweet_type=PUBLISHED&tweet_ids=1043551275923591168" { "request": { "params": { "tweet_ids": [ "1043551275923591168" ], "tweet_type": "PUBLISHED", "trim_user": true, "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "coordinates": null, "retweeted": false, "source": "Ads API Internal Test App", "entities": { "hashtags": [], "symbols": [], "user_mentions": [], "urls": [] }, "display_text_range": [ 0, 15 ], "favorite_count": 0, "in_reply_to_status_id_str": null, "geo": null, "id_str": "1043551275923591168", "scopes": { "followers": false }, "in_reply_to_user_id": null, "truncated": false, "retweet_count": 0, "scheduled_status": null, "id": 1043551275923591168, "in_reply_to_status_id": null, "nullcast": true, "created_at": "Sat Sep 22 17:23:07 +0000 2018", "place": null, "scheduled_at": null, "tweet_type": "PUBLISHED", "favorited": false, "card_uri": "card://1043282691834048513", "full_text": "Tracking a card", "lang": "en", "contributors": [ 2417045708 ], "in_reply_to_screen_name": null, "in_reply_to_user_id_str": null, "user": { "id": 756201191646691328, "id_str": "756201191646691328" }, "tweet_id": "1043551275923591168" } ] } ``` If using the Standard API, use include\_card\_uri=true in the request. Regardless of which endpoint is used, the card\_uri response attribute will only be rendered if the Tweet was created using a card URI. For scheduled and draft Tweet objects, the response will always include the card\_uri response attribute. #### Identifying Tweets with preview\_url For Tweets created by including the preview URL as part of the Tweet's text, the URL can be found in entities\["urls"]\[i]\["expanded\_url"] (the text field includes a shortened t.co URL), where i is an array index (a Tweet can contain multiple URLs). For scheduled and draft Tweet objects, the preview URL will always appear in the text field. #### Fetching cards To retrieve additional information about a specific card, we provide two endpoints: [GET accounts/:account\_id/cards/all](https://developer.x.com/en/docs/ads/creatives/api-reference/cards-fetch.html#get-accounts-account-id-cards-all) and [GET accounts/:account\_id/cards/all/:card\_id](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/cards-fetch#get-accounts-account-id-cards-all-card-id). The former allows a card to be fetched by card\_uri and the latter by the card's ID. The card's ID is found at the end of the preview\_url. In the example above, the ID is 68w3s. ### Identifying Media #### Introduction Media—images, GIFs, and videos—can be added to Tweets and cards. In addition, videos can be used as pre-roll assets and images can be promoted on the [X Audience Platform](https://developer.x.com/en/docs/ads/measurement/overview/twitter-audience-platform). This section describes how to find media references across these entities. There are two types of media identifiers: IDs and keys. Example values for each are presented below. | **Media ID** | **Media key** | | :------------------ | :---------------------- | | 1029825579531807971 | 13\_1029825579531807971 | The media key is the ID plus a numeric prefix and an underscore. #### Images The following table shows the identifier types currently available in each image-related resource's response as well as the corresponding attribute name(s). | **Resource** | **Identifier** | **Attribute(s)** | | :-------------- | :------------- | :-------------------------------------------------------------- | | Image cards | None | | | Tweet | Both | `entities["media"]["id_str"]` `entities["media"]["media_key"]` | | Scheduled Tweet | Both | `media_ids` and `media_keys` | | Draft Tweet | Both | `media_ids` and `media_keys` | | Account Media | None | | | Media Library | Both | `media_id` and `media_key` | Image cards and Account Media images do not include a reference any media identifier. Tweets only include media IDs. Scheduled and Draft Tweets include both the media ID and media key. The Media Library returns both, too. For Tweets, the id and id\_str fields in the object within the entities\["media"] array correspond to the media ID. In cases where a Tweet includes multiple images, the references to each media entity can only found in extended\_entities\["media"]. In addition to references to identifiers, it's often important to have access to the image's URL. | **Resource** | **Attribute(s)** | **Format** | | :-------------- | :---------------------------------------------------------------------------------- | :--------- | | Image cards | `image` | .jpg | | Tweet\* | `entities["media"][0]["media_url"]` or `extended_entities["media"][i]["media_url"]` | .jpg | | Scheduled Tweet | None | | | Draft Tweet | None | | | Account Media | `media_url` | .jpg | | Media Library | `media_url` | .jpg | \* This URL locations depend on whether the Tweet contains a single image or multiple images. All image cards include an image response attribute that contains the X image URL. (For image app download cards, the name is wide\_app\_image.) For Tweets, the media URL location depends on both the type of media and the endpoint being used. For Tweets with a single image, the URL can be found in entities\["media"]\[0]\["media\_url"]. This is true for both the Ads API and the Standard API. When Tweets contain multiple images, however, the URLs can only be found extended\_entities\["media"]\[i]\["media\_url"]. This is only available in the Standard API. #### Videos The following table shows the identifier types currently available in each video-related resource's response as well as the corresponding attribute name(s). | **Resource** | **Identifier** | **Attribute(s)** | | :--------------- | :------------- | :-------------------------------------------------------------- | | Video cards | May be either | `video_content_id` | | Video poll cards | None | | | Tweet | Both | `entities["media"]["id_str"]` `entities["media"]["media_key"]` | | Scheduled Tweet | Both | `media_ids` and `media_keys` | | Draft Tweet | Both | `media_ids` and `media_keys` | | Account Media | Media key | `video_id` | | Media Library | Both | `media_id` and `media_key` | While video cards (with the exception of poll cards with video) include a video\_content\_id response attribute, there is inconsistency in the type of value returned. In some cases, it's a media ID; in others, it's a media key. Information about how to access the video's URL is shown below. | **Resource** | **Attribute(s)** | **Format** | | :--------------- | :------------------------------------------------------------------ | :----------- | | Video cards | `video_url` and `video_hls_url` | .vmap .m3u8 | | Tweet with video | `extended_entities["media"][i]["video_info"]["variants"][j]["url"]` | .mp4 | | Scheduled Tweet | None | | | Draft Tweet | None | | | Account Media | None | | | Media Library | `media_url` | .mp4 | Video cards include video\_url and video\_hls\_url response attributes with .vmap and .m3u8 URLs, respectively. #### Media Library It's sometimes necessary to retrieve additional information about a media asset. One use case, for video cards, is retrieving the mp4 URL instead of the vmap one. This is available in the Media Library. For details on the available information, see our [Media Library Guide](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/guides/media-library). Most assets belonging to the ads account's FULL promotable user can be found in the library. There are some exceptions, though. **Fetching media** As stated above, image cards do not contain references to either media IDs or media keys. As a result, it's not possible to fetch their assets through the Media Library. This is also true for [Account Media](https://developer.x.com/content/developer-twitter/en/docs/ads/creatives/api-reference/account-media#account-media) images. Video cards require that the video asset be part of the Media Library (or the Videos resource before it) prior to creating it. As a result, these assets will always be retrievable in the Media Library. This is also true for Account Media PREROLL assets. Finally, media in Tweets are always guaranteed to be in the Media Library. The following table summarizes which assets are retrievable in the Media Library, taking into account whether the resource response includes an identifier to use in the lookup. | **Resource** | **In the Media Library** | | :------------------------------- | :----------------------- | | Image cards | No | | Video cards | Yes\* | | Tweets (any media)\*\* | Yes | | Scheduled Tweets | Yes | | Draft Tweets | Yes | | Account media images | No | | Account media videos (`PREROLL`) | Yes | \* For cards where the `video_content_id` is a media key. When the value is a media ID, the asset still exists in the Media Library, but retrieving it involves appending a numeric prefix and underscore to it. \*\* Tweets only return media IDs. While the asset is guaranteed to exist in the Media Library, fetching it involves appending a numeric prefix and underscore to it. **Interactions with Account Media** There are two cases where media assets added to the library are automatically added to the Account Media resource. * When an AMPLIFY\_VIDEO asset is added to the Media Library, it is automatically added as an Account Media asset as a PREROLL creative type. * When images that have specific dimensions (see "Creative Types" in our [enumerations page](https://developer.x.com/content/developer-twitter/en/docs/ads/general/overview/enums)) are added to the Media Library, they are automatically added as Account Media assets. The creative type (e.g., INTERSTITIAL) depends on the image dimensions. ### Tweets #### Introduction The X Ads API supports three types of Tweets: published, scheduled, and draft. #### Nullcasted Tweets Tweets may either be nullcasted (a.k.a. "Promoted-only") or organic. Nullcasted Tweets, once published, do not appear in the user's public timeline, though they are public. Organic Tweets, on the other hand, are served to the user's followers and do appear in the user's public timeline. **Creating Tweets** Each of the three Tweet create endpoints supports a Boolean nullcast parameter that gives the API user the option to create nullcasted or organic Tweets. Nullcasted Tweets can be created by the user or by anyone who has permission to create Tweets on the user's behalf. Organic Tweets can only be created by the [full promotable user](https://developer.x.com/content/developer-twitter/en/docs/tutorials/ads-api-hierarchy-terminology#promotable-users). **Updating Tweets** It is possible to update the nullcast property for scheduled and draft Tweets. For scheduled Tweets, edits can be made until the Tweet's scheduled\_at time. Draft Tweets can be edited indefinitely. Once published, though, it's not possible to change a Tweet from nullcasted to organic or vice versa. #### Promoting Tweets Only published and scheduled Tweets may be promoted. These can either be nullcasted or organic; there's no restriction. An advertiser may promote their own Tweets or another user's Tweets as long as they've obtained permission to do so. (See: [Promoting another user's Tweets](https://developer.x.com/content/developer-twitter/en/docs/tutorials/promoting-another-users-content) for more information.) Multiple Tweets can be promoted in a single campaign. Similarly, a single Tweet may be promoted in one or more campaigns. To promote published Tweets, use the [POST accounts/:account\_id/promoted\_tweets endpoint](https://developer.x.com/content/developer-twitter/en/docs/ads/campaign-management/api-reference/promoted-tweets#post-accounts-account-id-promoted-tweets). This associates published Tweets with a line item. To promote scheduled Tweets, use the [POST accounts/:account\_id/scheduled\_promoted\_tweets](https://developer.x.com/content/developer-twitter/en/docs/ads/campaign-management/api-reference/scheduled-promoted-tweets#post-accounts-account-id-scheduled-promoted-tweets) endpoint. #### Tweet IDs Published, scheduled, and draft Tweet IDs are [numeric](https://developer.x.com/content/developer-twitter/en/docs/basics/twitter-ids)—they are 64-bit unsigned integers. For example, the following published Tweet's ID is 1166476031668015104. When published or scheduled Tweets are promoted, a corresponding promoted Tweet entity is created. These entities have their own IDs, which are alpha-numeric and are represented as base-36-encoded values. For example, promoting the published Tweet above—that is, associating it a line item 6c62d—returns the following API response. ``` $ twurl -X POST -H ads-api.x.com "/9/accounts/18ce54d4x5t/promoted_tweets?line_item_id=6c62d&tweet_ids=1166476031668015104" { "request": { "params": { "tweet_ids": [ 1166476031668015104 ], "line_item_id": "6c62d", "account_id": "18ce54d4x5t" } }, "data": [ { "line_item_id": "6c62d", "id": "3qwlq6", "entity_status": "ACTIVE", "created_at": "2019-09-12T21:39:10Z", "updated_at": "2019-09-12T21:39:10Z", "approval_status": "ACCEPTED", "tweet_id": "1166476031668015104", "deleted": false } ], "total_count": 1 } ``` In addition to the Tweet ID and the line item ID, which were passed into the create request, the response includes an id field with a value of 3qw1q6, which is the promoted Tweet ID. ### Carousels #### Introduction The X Ads API supports creating and retrieving video carousels and image carousels. The carousel is a card type that can contain between 2 and 6 media assets. The carousel card can direct a user to a website or encourage them to install a mobile app. For more information about carousels, their benefits, best practices, and FAQs, see our [Carousel Ads on X](https://business.x.com/en/advertising/carousels.html) page. A carousel, like any other card type, can be used in Tweets and those Tweets can then be promoted. The workflow is the same as what you're already used to: 1. Upload media 2. Create the card 3. Create the Tweet 4. Promote the Tweet The only difference is with how the card is created. While other card create requests accept query parameters, carousel card create requests only accept JSON POST bodies. #### Endpoints The Ads API supports creating and retrieving carousels. To create a carousel—any kind—use the [POST accounts/:account\_id/cards](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/cards#post-accounts-account-id-cards) endpoint. To retrieve carousels, use the [GET accounts/:account\_id/cards](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/cards#get-accounts-account-id-cards) endpoint. #### JSON POST Body Carousels are created using two components. The first specifies the media assets that will be used. The second specifies information about either the website or the app. Specifically, a carousel card is created by using the following components, in order: * One `SWIPEABLE_MEDIA`component, which accepts an array of media keys * *One* of the following: * A `DETAILS` component to specify website information * A `BUTTON` component to specify app information The `SWIPEABLE_MEDIA` component must include a `media_keys` array where you can specify between 2 and 6 images or videos. The order in which the media keys are passed in determine the order in which they will be rendered. ``` { "type": "SWIPEABLE_MEDIA", "media_keys": [ "13_1089771253848666112", "13_1191948012077092867" ] } ``` As a reminder, you can obtain media keys by making a request to the [GET accounts/:account\_id/media\_library](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/media-library#get-accounts-account-id-media-library) endpoint. The composition of the second component object depends on whether you wish to direct a user to a website or encourage them to install an app. The table below summarizes the two options. (**Note**: all of the listed keys are required.) | | **Website** | **App** | | :------------------------- | :----------------------------------------------------------------- | :------------------------------------------------ | | Specify the component type | `"type": "DETAILS"` | `"type": "BUTTON"` | | Title/Label | `"title": "X"` | `"label": { "type": "ENUM", "value": "INSTALL" }` | | Destination | `"destination": { "type": "WEBSITE", "url": "https://www.x.com" }` | `"destination": { "type": "APP", ... }` | Putting this together, an example website carousel JSON POST body is shown below. ``` { "name": "website carousel", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "13_1089771253848666112", "13_1191948012077092867" ] }, { "type": "DETAILS", "title": "X", "destination": { "type": "WEBSITE", "url": "https://www.x.com" } } ] } ``` App destination objects within `BUTTON` components require a country code and at least one app identifier. They optionally accept deep links. For a description of these fields, see the [reference documentation](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/cards#app-destination). Putting this together, an example app carousel JSON POST body is shown below. ``` { "name": "app carousel", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "13_1089771253848666112", "13_1191948012077092867" ] }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "INSTALL" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android", "iphone_app_id": "333903271" } } ] } ``` #### Example This section demonstrates how to create a video website carousel card and how to use it in a Tweet. As mentioned above, the workflow is the same as what you're already used to: upload media, create the card, create the Tweet. The only difference is how the card is created. **Media** To start, either upload new media assets or use existing ones. For details on how to upload new media assets and add them to the Media Library, see our [Media Library Guide](https://developer.x.com/en/docs/twitter-ads-api/creatives/guides/media-library). Once your media assets are in the Media Library, fetch them using the [GET accounts/:account\_id/media\_library](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/media-library#get-accounts-account-id-media-library) endpoint. Use the `media_type` request parameter to scope the results to a particular media type. ``` $ twurl -H ads-api.x.com "/10/accounts/18ce54d4x5t/media_library?media_type=VIDEO" ``` ``` { "request": { "params": { "account_id": "18ce54d4x5t", "media_type": "VIDEO" } }, "next_cursor": null, "data": [ { "tweeted": true, "duration": 5283, "name": "Sunrise", "file_name": "sunrise.mp4", "description": null, "media_url": "https://video.twimg.com/amplify_video/1089771253848666112/vid/1280x720/tyL-pUBP7GgkS9bl.mp4?tag=9", "poster_media_key": "3_1089771253848666112", "media_key": "13_1089771253848666112", "created_at": "2019-01-28T06:24:48Z", "media_status": "TRANSCODE_COMPLETED", "poster_media_url": "https://pbs.twimg.com/amplify_video_thumb/1089771253848666112/img/WOYvToSRZFXUSDzd.jpg", "title": null, "media_type": "VIDEO", "aspect_ratio": "16:9", "updated_at": "2019-08-23T19:05:33Z", "deleted": false }, { "tweeted": true, "duration": 15248, "name": "snow", "file_name": "snow.mp4", "description": "Two, three, and to the four", "media_url": "https://video.twimg.com/amplify_video/1191948012077092867/vid/1280x720/2cOvadcctqqea6Hx.mp4?tag=13", "poster_media_key": "3_1191948012077092867", "media_key": "13_1191948012077092867", "created_at": "2019-11-06T05:18:46Z", "media_status": "TRANSCODE_COMPLETED", "poster_media_url": "https://pbs.twimg.com/amplify_video_thumb/1191948012077092867/img/IUbhTRd62SEeIVTf.jpg", "title": "One", "media_type": "VIDEO", "aspect_ratio": "16:9", "updated_at": "2020-03-27T22:23:18Z", "deleted": false } ] } ``` **Carousel Creation** Use the [POST accounts/:account\_id/cards](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/cards#post-accounts-account-id-cards) endpoint to create your carousel. Use the media keys from the previous request. Remember, the order in which the media keys are passed in determine the order in which they are rendered. ``` $ twurl -A "Content-Type: application/json" -X POST -H ads-api.x.com "/10/accounts/18ce54d4x5t/cards" -d '{"name":"website carousel","components":[{"type": "SWIPEABLE_MEDIA","media_keys":["13_1089771253848666112","13_1191948012077092867"]},{"type": "DETAILS","title": "X","destination":{"type":"WEBSITE", "url":"https://www.x.com"}}]}' ``` ``` { "request": { "params": { "account_id": "18ce54d4x5t" } }, "data": { "name": "website carousel", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "13_1089771253848666112", "13_1191948012077092867" ] }, { "type": "DETAILS", "title": "X", "destination": { "type": "WEBSITE", "url": "https://www.x.com/", "tco_url": "https://t.co/dyTMHWKWZb" } } ], "id": "ars7m", "created_at": "2020-11-11T07:51:47Z", "card_uri": "card://1326432421105995776", "updated_at": "2020-11-11T07:51:47Z", "deleted": false, "card_type": "UNIFIED" } } ``` Notice that like with other cards, the carousel card response includes a `card_uri`, which will be used when creating a Tweet. **Tweet** Use the [POST accounts/:account\_id/tweet](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/tweets#post-accounts-account-id-tweet) endpoint to create your Tweet. Use the `card_uri` from the previous request. (Response truncated for readability.) ``` $ twurl -H ads-api.x.com "/9/accounts/18ce54d4x5t/tweet_previews?tweet_type=PUBLISHED&tweet_ids=1326434098324385792" ``` ``` { "data": { "created_at": "Wed Nov 11 07:58:27 +0000 2020", "id": 1326434098324385792, "id_str": "1326434098324385792", "text": "Swipe", "truncated": false, "entities": { "hashtags": [], "symbols": [], "user_mentions": [], "urls": [] }, "source": "Ads API Internal Test App", "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "in_reply_to_screen_name": null, "user": { "id": 756201191646691300, "id_str": "756201191646691328", ... }, "geo": null, "coordinates": null, "place": null, "contributors": [ 2417045708 ], "retweet_count": 0, "favorite_count": 0, "favorited": false, "retweeted": false, "possibly_sensitive": false, "scopes": { "followers": false }, "lang": "en" }, "request": { "params": { "text": "Swipe", "as_user_id": 756201191646691300, "card_uri": "card://1326432421105995776", "account_id": "18ce54d4x5t" } } } ``` **Tweet Previews** Use the [GET accounts/:account\_id/tweet\_previews](https://developer.x.com/en/docs/twitter-ads-api/creatives/api-reference/tweet-previews#get-accounts-account-id-tweet-previews) endpoint to see your Tweet. ### creative-metadata-tagging #### Introduction This guide is for creative partners, agencies and creative developers to tag assets used within X campaigns to better understand individual asset value and performance. **Note:** Media assets must only be tagged by the partner or developer creating the media asset. If the user of the media asset did not create the media asset, do not implement metadata tagging. Creative Metadata Tagging provides attribution of images and videos created by Creative Partners wherever the asset is uploaded to X, or whomever the entity that is uploading the asset. To create the connection between the creative asset and the creative partner, the [XMP](https://exiftool.org/TagNames/XMP.html) standard is used. #### Tagging Creative Assets The following table shows the identifier types currently available in each image-related resource's response as well as the corresponding attribute name(s). A tagging tool is needed to tag creative assets. [ExifTool](https://exiftool.org/), a platform-independent Perl library plus a [command-line application](https://exiftool.org/exiftool_pod.html) for reading, writing, and editing meta information, is recommended. See all supported [file types](https://exiftool.org/#supported). 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](/x-api/media/upload-media).  **Note:** X's use of the contributor XMP tag ensures metadata captures values for campaigns on X exclusively. `exiftool -contributor="" -creative_file.jpg` `exiftool -date="" -creative_file.jpg` The app\_id  can be found in the [Developer Console](https://developer.x.com/en/portal/dashboard) under Projects & Apps. Example: 16489123 The following example adds app\_id as the contributor tag and date as the date tag for an image: ``` app_id:858382169 ``` ``` date:2022-03-13 ``` ``` creative_file: eiffel_tower.jpg ``` ``` exiftool -contributor=858382169 eiffel_tower.jpg ``` ```    1 image files updated ``` ``` exiftool -date=2022-03-13 eiffel_tower.jpg ``` ```   1 image files updated ``` Verify that your image has been properly tagged:  `exiftool -xmp:all -G1 ` Example: `exiftool -xmp:all -G1 eiffel_tower.jpg` ``` [XMP]        XMP Toolkit            : Image::ExifTool 12.30 ``` ``` [XMP]        Contributor            : 858382169 ``` ``` [XMP]        Date                   : 2022:03:13 ``` #### Questions? If you would like to confirm that your tagging and attribution is successful, please provide sample assets that have been tagged to [adsapi-program@x.com](mailto:adsapi-program@x.com) for a X representative to review. ## API Reference ### Account Media #### GET accounts/:account\_id/account\_media Retrieve details for some or all account media associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/account_media` #### Parameters | Name | Description | | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | account\_media\_ids *optional* | Scope the response to just the desired account media by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided. Type: string Example: `3wpx` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `200` Min, Max: `1`, `1000` | | creative\_types *optional* | Scope the response to just the account media that match the specified creative types. More than one creative type may be specified by comma-separating enum values. Type: enum Possible values: `BANNER`, `BANNER_TABLET`, `INTERSTITIAL`, `INTERSTITIAL_LANDSCAPE`, `INTERSTITIAL_LANDSCAPE_TABLET`, `INTERSTITIAL_TABLET`, `MEDIUM_RECTANGLE`, `PREROLL`, `VAST_PREROLL` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `8x7v00oow` | | sort\_by *optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](https://developer.x.com/x-ads-api/fundamentals/sorting) for more information. Type: string Example: `created_at-asc` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | | with\_total\_count *optional* | Include the `total_count` response attribute. **Note**: This parameter and `cursor` are exclusive. **Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/account_media?account_media_ids=3wpx` #### Example Response ``` { "request": { "params": { "account_media_ids": [ "3wpx" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "video_id": "13_771791717175468032", "media_url": null, "creative_type": "PREROLL", "id": "3wpx", "created_at": "2016-09-02T19:27:52Z", "updated_at": "2016-09-02T19:27:52Z", "deleted": false } ] } ``` Retrieve a specific account media object associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/account_media/:account_media_id` #### Parameters | Name | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | account\_media\_id *required* | A reference to the account media you are operating with in the request. Type: string Example: `2pnfd` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/account_media/2pnfd` #### Example Response ``` { "request": { "params": { "account_media_id": "2pnfd", "account_id": "18ce54d4x5t" } }, "data": { "video_id": null, "media_url": "https://pbs.twimg.com/ad_img/890749735862026242/Up07zMym?format=jpg&name=orig", "creative_type": "INTERSTITIAL_LANDSCAPE_TABLET", "id": "2pnfd", "created_at": "2017-07-28T01:44:41Z", "updated_at": "2017-07-28T01:44:41Z", "deleted": false } } ``` Delete the specified account media object belonging to the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/account_media/:account_media_id` #### Parameters | Name | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | account\_media\_id *required* | A reference to the account media you are operating with in the request. Type: string Example: `2pnfd` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/account_media/2pnfd` #### Example Response ``` { "data": { "video_id": null, "media_url": "https://pbs.twimg.com/ad_img/890749735862026242/Up07zMym?format=jpg&name=orig", "creative_type": "INTERSTITIAL_LANDSCAPE_TABLET", "id": "2pnfd", "created_at": "2017-07-28T01:44:41Z", "updated_at": "2017-08-25T17:16:26Z", "deleted": true }, "request": { "params": { "account_id": "18ce54d4x5t", "account_media_id": "2pnfd" } } } ``` ### Cards **Note**: To associate a card with a Tweet, use the `card_uri` parameter with either the [POST accounts/:account\_id/tweet](https://developer.x.com/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet), [POST statuses/update](https://developer.x.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update#post-statuses-update), [POST accounts/:account\_id/scheduled\_tweets](https://developer.x.com/en/docs/ads/creatives/api-reference/scheduled-tweets#post-accounts-account-id-scheduled-tweets), or the [POST accounts/:account\_id/draft\_tweets](https://developer.x.com/en/docs/ads/creatives/api-reference/draft-tweets#post-accounts-account-id-draft-tweets) endpoints. Retrieve details for some or all cards associated with the current account. **Note**: This only returns cards that were created using the POST accounts/:account\_id/cards endpoint. Cards created using other endpoints are not returned. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards` #### Parameters | Name | Description | | :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_types *optional* | Scope the response to just the desired card types by specifying a comma-separated list of enum values. Type: enum Possible values: `IMAGE_APP`, `IMAGE_CAROUSEL_APP`, `IMAGE_CAROUSEL_WEBSITE`, `IMAGE_MULTI_DEST_CAROUSEL_WEBSITE`, `IMAGE_WEBSITE`, `MIXED_MEDIA_MULTI_DEST_CAROUSEL_WEBSITE`, `MIXED_MEDIA_SINGLE_DEST_CAROUSEL_APP`, `MIXED_MEDIA_SINGLE_DEST_CAROUSEL_WEBSITE`, `VIDEO_APP`, `VIDEO_CAROUSEL_APP`, `VIDEO_CAROUSEL_WEBSITE`, `VIDEO_MULTI_DEST_CAROUSEL_WEBSITE`, `VIDEO_WEBSITE` | | card\_id *optional* | Scope the response to just the desired cards by specifying a comma-separated list of identifiers. Up to 200 card IDs may be provided. Type: string Example: `1044294149527166979,1044301099031658496` | | card\_uris *optional* | Scope the response to just the desired cards by specifying a comma-separated list of identifiers. Up to 200 card URI values may be provided. Type: string Example: `card://1044294149527166979,card://1044301099031658496` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `100` Min, Max: `1`, `200` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `8x7v00oow` | | include\_legacy\_cards *optional* | Include legacy website and app cards in the response. Legacy cards are those whose resource URL has the following format: accounts/:account\_id/cards/:card\_type. See [this developer forum post](https://devcommunity.x.com/t/new-cards-endpoints-and-carousel-ads/145782/6) for additional context. Type: boolean Default: `false` Possible values: `true`, `false` | | q *optional* | An optional query to scope cards by `name`. Omit this parameter to retrieve all. Maximum length: 80 characters. **Note**: This performs case-insensitive prefix matching. Type: string Example: `dtc` | | sort\_by *optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](https://developer.x.com/x-ads-api/fundamentals/sorting) for more information. Type: string Example: `created_at-asc` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards?count=1` #### Example Response ``` { "request": { "params": { "count": 1, "account_id": "18ce54d4x5t" } }, "next_cursor": "8wzvldqtc", "data": [ { "name": "deep link", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "3_1073727809120419840", "3_1075096386931052545" ] }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "OPEN" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android", "googleplay_deep_link": "twitter://user?screen_name=apimctestface" } } ], "created_at": "2020-10-28T20:47:52Z", "card_uri": "card://1321554298900107264", "id": "1321554298900107264", "updated_at": "2020-10-28T20:47:52Z", "deleted": false, "card_type": "IMAGE_CAROUSEL_APP" } ] } ``` Retrieve details for a single card associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/:card_id` #### Parameters | Name | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | The id of the cards. Type: string Example: `1044294149527166979` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/1321554298900107264` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "card_id": "1321554298900107264" } }, "data": [ { "name": "deep link", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "3_1073727809120419840", "3_1075096386931052545" ] }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "OPEN" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android", "googleplay_deep_link": "twitter://user?screen_name=apimctestface" } } ], "created_at": "2020-10-28T20:47:52Z", "card_uri": "card://1321554298900107264", "id": "1321554298900107264", "updated_at": "2020-10-28T20:47:52Z", "deleted": false, "card_type": "IMAGE_CAROUSEL_APP" } ] } ``` #### POST accounts/:account\_id/cards Create a new card associated to the specified account. Card create requests only accept JSON POST bodies. The `Content-Type` must be set to `application/json`. See our [Carousels Guide](https://developer.x.com/en/docs/twitter-ads-api/creatives/guides/carousels) for a detailed usage example. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards` #### Parameters The JSON POST body must include a card `name` and an array of `components`. Components are represented as objects and describe the advertiser-facing attributes of the card. The following example shows the general structure of the payload (but includes non-working information). ``` { "name": "some name", "components": [ { "type": "TYPE_ENUM", "key": "value" } ] } ``` Additional information on components below. | Name | Description | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | name *required* | The name for the card. Maximum length: 80 characters. Type: string Example: `component based card` | | components *sometimes required* | Describes the components to use to create the card. Additional information below. Cannot be specified along with `slides`. **Note**: The order of the components is important. Type: array of objects | | slides *sometimes required* | Use this array of array to create Multi-Destination Carousels. Describes each card as a grouping of components. Each slide should be a complete representation of a card. Cannot be specified along with `components`. **Note**: The order of each slide is important. Type: array of array | #### Components Every component must include a `type` field which determines the object's schema. The Ads API supports the following component types, grouped into media- and description-based components. * Media: * `MEDIA`: single video or image * `SWIPEABLE_MEDIA`: between 2-6 videos or images * Description: * `DETAILS` * `BUTTON` Each component has a set of required fields (in addition to the `type` key). These are listed in the following table. | Component `type` | Field | Value type | | :---------------- | :---------------------- | :--------------- | | `MEDIA` | `media_key` | string | | `SWIPEABLE_MEDIA` | `media_keys` | array of strings | | `DETAILS` | `title` `destination` | string object | | `BUTTON` | `label` `destination` | object object | The following is an example of a `BUTTON` component in the context of the `components` array (intentionally omitting the `name` key). (The ellipses indicate places where more information would need to be specified.) ``` { "components": [ { "type": "BUTTON", "label": { ... }, "destination": { ... } } ] } ``` The order in which the component objects are specified defines the top-to-bottom order in which they will be rendered. Cards must be created using one media-based component and either a `DETAILS` or `BUTTON` component. Description-based components are rendered under media and have associated destinations, either URLs or mobile apps. **Label** Labels define the text shown on buttons and, therefore, only apply to the `BUTTON` component. Label objects have two required keys: `type` and `value`. The `type` must be set to `ENUM` and the `value` can be one of: `BOOK`, `CONNECT`, `INSTALL`, `OPEN`, `ORDER`, `PLAY`, or `SHOP`. Building on the previous example, the following shows the `label` object within the `BUTTON` component. ``` { "components": [ { "type": "BUTTON", "label": { "type": "ENUM", "value": "INSTALL" }, "destination": { ... } } ] } ``` **Destination** Destinations are where advertisers intend to take users. They are always required within `DETAILS` or `BUTTON` components. There are two destination types: `WEBSITE` or `APP`. **Note**: Website destinations can only be used with `DETAILS` components and app destinations can only be used with `BUTTON` components. **Website Destination** | Name | Description | | :---------------- | :------------------------------------------------------------------------------------------------------------------ | | type *required* | The destination type, which determines its schema. Type: enum Possible values: `WEBSITE` | | url *required* | The URL of the website to redirect a user to. Type: string Example: `https://devcommunity.x.com/c/advertiser-api` | **App Destination** | Name | Description | | :------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | type *required* | The destination type, which determines its schema. Type: enum Possible values: `APP` | | country\_code *required* | The [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) two-letter code for the country where the app is sold. Type: string Example: `US` | | googleplay\_app\_id *sometimes required* | The Google Play application package name. **Note**: At least one of following is required: `ios_app_store_identifier` or `googleplay_app_id`. Type: string Example: `com.twitter.android` | | ios\_app\_store\_identifier *sometimes required* | The iOS app store identifier. **Note**: At least one of following is required: `ios_app_store_identifier` or `googleplay_app_id`. Type: string Example: `333903271` | | googleplay\_deep\_link *optional* | A deep link into the Android app you're promoting. **Note**: Can only be used if an `googleplay_app_id` has been provided. Type: string | | ios\_deep\_link *optional* | A deep link into the iOS app you're promoting. **Note**: Can only be used if an `ios_app_store_identifier` has been provided. Type: string | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/cards` ``` { "name": "components create cards", "components": [ { "type": "MEDIA", "media_key": "3_1323490622599176192" }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "INSTALL" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android" } } ] } ``` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t" } }, "data": { "name": "components create cards", "components": [ { "type": "MEDIA", "media_key": "3_1323490622599176192" }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "INSTALL" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android" } } ], "created_at": "2020-11-11T05:42:25Z", "card_uri": "card://1326399865065238531", "id": "1321554298900107264", "updated_at": "2020-11-11T05:42:25Z", "deleted": false, "card_type": "IMAGE_APP" } } ``` Update the specified associated with the current account. Card edit requests only accept JSON POST bodies. The `Content-Type` must be set to `application/json`. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/1321554298900107264` #### Parameters The JSON POST body must include the parameters that will be updated. The request will **replace** each field with the parameters specified within the payload. Components are represented as objects and describe the advertiser-facing attributes of the card. The following example shows the general structure of the payload (but includes non-working information). ``` { "name": "some name", "components": [ { "type": "TYPE_ENUM", "key": "value" } ] } ``` Additional information on components and slides in **POST accounts/:account\_id/cards**. | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | name *optional* | The name for the card. Maximum length: 80 characters. Type: string Example: `component based card` | | components *optional* | Describes the components to use to update the card. Additional information below. Cannot be specified along with `slides`. **Note**: The order of the components is important. Type: array of objects | | slides *optional* | Use this array of array to update Multi-Destination Carousels. Describes each card as a grouping of components. Each slide should be a complete representation of a card. Cannot be specified along with `components`. **Note**: The order of each slide is important. Type: array of array | #### Example Request This example updates both the name and removes one of the media\_keys from the components field from the example above. `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/1321554298900107264` ``` { "name": "changed name", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "3_1075096386931052545" ] }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "OPEN" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android", "googleplay_deep_link": "twitter://user?screen_name=apimctestface" } } ] } ``` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "card_id": "1321554298900107264" } }, "data": [ { "name": "changed name", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "3_1075096386931052545" ] }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "OPEN" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android", "googleplay_deep_link": "twitter://user?screen_name=apimctestface" } } ], "created_at": "2020-10-28T20:47:52Z", "card_uri": "card://1321554298900107264", "id": "1321554298900107264", "updated_at": "2020-10-29T20:47:52Z", "deleted": false, "card_type": "IMAGE_CAROUSEL_APP" } ] } ``` Delete the specified card belonging to the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/:card_id` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | The id of the card to be deleted. Type: string Example: `1044294149527166979` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/1321554298900107264` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "card_id": "1321554298900107264" } }, "data": [ { "name": "deep link", "components": [ { "type": "SWIPEABLE_MEDIA", "media_keys": [ "3_1073727809120419840", "3_1075096386931052545" ] }, { "type": "BUTTON", "label": { "type": "ENUM", "value": "OPEN" }, "destination": { "type": "APP", "country_code": "US", "googleplay_app_id": "com.twitter.android", "googleplay_deep_link": "twitter://user?screen_name=apimctestface" } } ], "created_at": "2020-10-28T20:47:52Z", "card_uri": "card://1321554298900107264", "id": "1321554298900107264", "updated_at": "2020-10-29T20:47:52Z", "deleted": true, "card_type": "IMAGE_CAROUSEL_APP" } ] } ``` ### Cards Fetch Retrieve multiple cards, by `card_uri`, associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/all` #### Parameters | Name | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_uris *required* | Scope the response to just the desired cards by specifying a comma-separated list of identifiers. Up to 200 card URI values may be provided. Type: string Example: `card://1044294149527166979,card://1044301099031658496` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/all?card_uris=card://1044294149527166979,card://1044301099031658496` #### Example Response ``` { "request": { "params": { "card_uris": [ "card://1044294149527166979", "card://1044301099031658496" ], "account_id": "18ce54d4x5t" } }, "data": [ { "name": "X App", "googleplay_app_id": "com.twitter.android", "image_display_height": "836", "country_code": "US", "id": "692xn", "wide_app_image": "https://pbs.twimg.com/media/Dc263l9VwAAAeEH.jpg", "created_at": "2018-09-24T18:35:01Z", "image_display_width": "1600", "card_uri": "card://1044294149527166979", "updated_at": "2018-09-24T18:35:01Z", "app_cta": "INSTALL", "deleted": false, "card_type": "IMAGE_APP_DOWNLOAD" }, { "video_poster_height": "9", "name": "Developer Platform", "website_shortened_url": "https://t.co/zadeUSVD18", "video_height": "9", "video_url": "https://video.twimg.com/amplify_video/vmap/991374284135137280.vmap", "content_duration_seconds": "24", "video_owner_id": "756201191646691328", "video_content_id": "13_991374284135137280", "website_display_url": "developer.x.com", "id": "6933h", "video_width": "16", "video_hls_url": "https://video.twimg.com/amplify_video/991374284135137280/pl/sQrBsE9mFvNep9Cx.m3u8?tag=2", "website_dest_url": "https://developer.x.com", "created_at": "2018-09-24T19:02:38Z", "card_uri": "card://1044301099031658496", "title": "Developer Platform", "website_url": "https://developer.x.com", "updated_at": "2018-09-24T19:02:38Z", "video_poster_url": "https://pbs.twimg.com/amplify_video_thumb/991374284135137280/img/YbbGQHvWRjoFgrLz.jpg", "video_poster_width": "16", "deleted": false, "card_type": "VIDEO_WEBSITE" } ] } ``` Retrieve a specific card, by `card_id`, associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/all/:card_id` #### Parameters | Name | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | A reference to the card you are operating with in the request. Type: string Example: `508pf` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/all/508pf` #### Example Response ``` { "request": { "params": { "card_id": "508pf", "account_id": "18ce54d4x5t" } }, "data": { "video_poster_height": "9", "name": "video website card", "video_height": "9", "video_url": "https://video.twimg.com/amplify_video/vmap/867520357225418752.vmap", "content_duration_seconds": "21", "video_owner_id": "756201191646691328", "video_content_id": "13_867520357225418752", "website_display_url": "developer.x.com", "id": "508pf", "video_width": "16", "video_hls_url": "https://video.twimg.com/amplify_video/867520357225418752/pl/TPHeH5ZlHFCa2TeJ.m3u8", "website_dest_url": "https://developer.x.com/en/docs/ads/creatives/api-reference/video-website#post-accounts-account-id-cards-video-website", "created_at": "2017-11-10T09:00:35Z", "card_uri": "card://928910245920829440", "title": "VWC", "website_url": "https://t.co/F81hp59pUF", "updated_at": "2018-01-05T05:43:31Z", "video_poster_url": "https://pbs.twimg.com/amplify_video_thumb/867520357225418752/img/E3pnXM0sCKnRsFih.jpg", "video_poster_width": "16", "deleted": false, "card_type": "VIDEO_WEBSITE" } } ``` ### Draft Tweets #### GET accounts/:account\_id/draft\_tweets Retrieve details for some or all Draft Tweets associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/draft_tweets` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `100` Min, Max: `1`, `200` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `c-jh1g0ryb` | | user\_id *optional* | Specify the user to retrieve Draft Tweets for. Defaults to the `FULL` promotable user on the account when not set. Type: string Example: `756201191646691328` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/draft_tweets?count=1` #### Example Response ``` { "request": { "params": { "count": 1 } }, "data": [ { "name" null, "text": "hello, world", "user_id": "756201191646691328", "id": "994791681219231744", "nullcast": true, "created_at": "2018-05-11T04:09:53Z", "card_uri": null, "updated_at": "2018-05-11T04:09:53Z", "media_keys": [] } ], "next_cursor": "c-jh1g0ryb" } ``` Retrieve a specific Draft Tweet associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/draft_tweets/:draft_tweet_id` #### Parameters | Name | Description | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | draft\_tweet\_id *required* | A reference to the Draft Tweet you are operating with in the request. Type: string Example: `994788364334325760` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/draft_tweets/994788364334325760` #### Example Response ``` { "request": { "params": { "draft_tweet_id": "994788364334325760" } }, "data": { "name": null, "text": "#TwitterDev", "user_id": "756201191646691328", "id": "994788364334325760", "nullcast": true, "created_at": "2018-05-11T03:56:42Z", "card_uri": "card://958225772740714496", "updated_at": "2018-05-11T03:56:42Z", "media_keys": [] } } ``` #### POST accounts/:account\_id/draft\_tweets Create a Draft Tweet for the account's full promotable user (default) or the user specified in the `as_user_id` parameter. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/draft_tweets` #### Parameters | Name | Description | | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | as\_user\_id *required* | The user ID of the advertiser on behalf of whom you are posting the Tweet. The advertiser must grant your handle (or handles) access to their ads account via [ads.x.com](https://ads.x.com/). This permission allows you to call the API using the OAuth tokens of your own handle rather than the advertiser's. Type: string Example: `756201191646691328` | | text *sometimes required* | The text of your status update. Required if no `media_keys` are specified. Type: string Example: `Just setting up my X.` | | card\_uri *optional* | Associate a card with the Tweet using the `card_uri` value from any cards response, if available. Type: string Example: `card://960880280705392541` | | media\_keys *optional* | Associate media with the Tweet by specifying a comma-separated list of identifiers. Include up to 4 images, 1 animated GIF, or 1 video. **Note**: The media asset must be in the account's [Media Library](https://developer.x.com/en/docs/ads/creatives/api-reference/media-library#media-library). Type: string Example: `13_1153584529292270722` | | nullcast *optional* | Whether to create a nullcasted (or "Promoted-only") Tweet. Type: boolean Default: `true` Possible values: `true`, `false` | | name *optional* | The name for the Draft Tweet. Maximum length: 80 characters. Type: string Example: `Tweet with name` | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/draft_tweets?as_user_id=756201191646691328&text=Just setting up my X.` #### Example Response ``` { "request": { "params": { "text": "Just setting up my X.", "as_user_id": "756201191646691328" } }, "data": { "name": null, "text": "Just setting up my X.", "user_id": "756201191646691328", "id": "994747471329873920", "nullcast": true, "created_at": "2018-05-11T01:14:13Z", "card_uri": null, "updated_at": "2018-05-11T01:14:13Z", "media_keys": [] } } ``` Update the specified Draft Tweet belonging to the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/draft_tweets/:draft_tweet_id` #### Parameters | Name | Description | | :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | draft\_tweet\_id *required* | A reference to the Draft Tweet you are operating with in the request. Type: string Example: `994747471329873920` | | card\_uri *optional* | Associate a card with the Tweet using the `card_uri` value from any cards response, if available. **Note**: Unset (remove) by specifying the parameter without a value. Type: string Example: `card://970582057284129151` | | media\_keys *optional* | Associate media with the Tweet by specifying a comma-separated list of identifiers. Include up to 4 images, 1 animated GIF, or 1 video. **Note**: The media asset must be in the account's [Media Library](https://developer.x.com/en/docs/ads/creatives/api-reference/media-library#media-library). **Note**: Unset (remove) by specifying the parameter without a value. Type: string Example: `13_1153584529292270722` | | nullcast *optional* | Whether to create a nullcasted (or "Promoted-only") Tweet. Type: boolean Possible values: `true`, `false` | | text *optional* | The text of your status update. Type: string Example: `just setting up my twttr` | | name *optional* | The name for the Draft Tweet. Maximum length: 80 characters. Type: string Example: `Tweet with name` | #### Example Request `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/draft_tweets/994747471329873920?text=just setting up my twttr` #### Example Response ``` { "request": { "params": { "draft_tweet_id": 994747471329873920, "text": "just setting up my twttr" } }, "data": { "name": null, "text": "just setting up my twttr", "user_id": "756201191646691328", "id": "994747471329873920", "nullcast": true, "created_at": "2018-05-11T01:14:13Z", "card_uri": null, "updated_at": "2018-05-11T01:16:59Z", "media_keys": [] } } ``` Permanently delete the specified Draft Tweet belonging to the current account. **Note**: We **strongly** recommend deleting drafts once a Tweet or Scheduled Tweet has been created using its metadata. **Note**: This is a hard delete. As a result, it is not possible to retrieve deleted Draft Tweets. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/draft_tweets/:draft_tweet_id` #### Parameters | Name | Description | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | draft\_tweet\_id *required* | A reference to the Draft Tweet you are operating with in the request. Type: string Example: `994787835663155200` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/draft_tweets/994787835663155200` #### Example Response ``` { "request": { "params": { "draft_tweet_id": "994787835663155200" } }, "data": { "name": null, "text": "hello, world", "user_id": "756201191646691328", "id": "994787835663155200", "nullcast": true, "status": "DELETED", "created_at": "2018-05-11T03:54:36Z", "card_uri": null, "updated_at": "2018-05-11T04:07:31Z", "media_keys": [] } } ``` #### POST accounts/:account\_id/draft\_tweets/preview/:draft\_tweet\_id Preview a Draft Tweet on a mobile device. A successful request sends a notification to every device the authenticated user is logged in to. Clicking on the notification opens a timeline that allows the user to see and interact with the Draft Tweet, enabling them to test auto-play, volume, fullscreen, video website card docking, and other behaviors. **Note**: On-device previews are only visible to the user who receives the notification. **Note**: Notifications only get sent to X official apps. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/draft_tweets/preview/:draft_tweet_id` #### Parameters | Name | Description | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | draft\_tweet\_id *required* | A reference to the Draft Tweet you are operating with in the request. Type: string Example: `996132315829948416` | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/draft_tweets/preview/996132315829948416` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "draft_tweet_id": "996132315829948416" } }, "message": "See @apimctestface's notifications in the X app to preview your Tweet." } ``` ### Image Conversation Cards **Note**: To associate a card with a Tweet, use the `card_uri` parameter with either the [POST accounts/:account\_id/tweet](https://developer.x.com/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet), [POST statuses/update](https://developer.x.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update#post-statuses-update), or [POST accounts/:account\_id/scheduled\_tweets](https://developer.x.com/en/docs/ads/creatives/api-reference/scheduled-tweets#post-accounts-account-id-scheduled-tweets) endpoints. #### GET accounts/:account\_id/cards/image\_conversation Retrieve details for some or all image conversation cards associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/image_conversation` #### Parameters | Name | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_ids *optional* | Scope the response to just the desired image conversation cards by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided. Type: string Example: `59woh` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `200` Min, Max: `1`, `1000` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `8x7v00oow` | | q *optional* | An optional query to scope cards by `name`. Omit this parameter to retrieve all. Maximum length: 80 characters. **Note**: This performs case-insensitive prefix matching. Type: string Example: `night` | | sort\_by *optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](https://developer.x.com/x-ads-api/fundamentals/sorting) for more information. Type: string Example: `created_at-asc` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | | with\_total\_count *optional* | Include the `total_count` response attribute. **Note**: This parameter and `cursor` are exclusive. **Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/image_conversation?card_ids=59woh` #### Example Response ``` { "request": { "params": { "card_type": "image_conversation", "card_ids": [ "59woh" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "name": "image conversation card", "first_cta": "#moon", "image_display_height": "670", "media_url": "https://pbs.twimg.com/media/DUhZuzxUQAAWZqr.jpg", "thank_you_text": "thanks", "id": "59woh", "first_cta_tweet": "stars", "media_key": "3_957113581522141184", "created_at": "2018-01-27T04:58:42Z", "image_display_width": "1280", "card_uri": "card://923498485702009837", "title": "Full moon", "updated_at": "2018-01-27T04:58:42Z", "deleted": false, "card_type": "IMAGE_CONVERSATION" } ] } ``` Retrieve a specific image conversation card associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/image_conversation/:card_id` #### Parameters | Name | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | A reference to the image conversation card you are operating with in the request. Type: string Example: `59woh` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/image_conversation/59woh` #### Example Response ``` { "request": { "params": { "card_type": "image_conversation", "card_id": "59woh", "account_id": "18ce54d4x5t" } }, "data": { "name": "image conversation card", "first_cta": "#moon", "image_display_height": "670", "media_url": "https://pbs.twimg.com/media/DUhZuzxUQAAWZqr.jpg", "thank_you_text": "thanks", "id": "59woh", "first_cta_tweet": "stars", "media_key": "3_957113581522141184", "created_at": "2018-01-27T04:58:42Z", "image_display_width": "1280", "card_uri": "card://923498485702009837", "title": "Full moon", "updated_at": "2018-01-27T04:58:42Z", "deleted": false, "card_type": "IMAGE_CONVERSATION" } } ``` #### POST accounts/:account\_id/cards/image\_conversation Create a new image conversation card associated with the specified account. See [Uploading Media](https://developer.x.com/rest/public/uploading-media) for useful information on uploading images to our endpoints. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/image_conversation` #### Parameters | Name | Description | | :---------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | first\_cta *required* | The Call-To-Action (CTA) hashtag for the first option. Maximum length: 20 characters (not counting the #). Type: string Example: `#ShareNow` | | first\_cta\_tweet *required* | The Tweet text to be used when the first CTA is clicked. Type: string Example: `I Heart @AdsAPI` | | media\_key *required* | The media key for an image to be used in this card. **Note**: The image must be in the account's [Media Library](https://developer.x.com/en/docs/ads/creatives/api-reference/media-library#media-library). **Note**: A minimum image width of 800px and a width:height aspect ratio of 1.91:1 is required. Type: string Example: `3_1151345051104991073` | | name *required* | The name for the card. Type: string Example: `image conversation card` | | thank\_you\_text *required* | The text to be displayed after the CTA is clicked. Maximum length: 23 characters. Type: string Example: `Thank you` | | second\_cta *sometimes required* | The Call-To-Action (CTA) hashtag for the second option. Maximum length: 20 characters (not counting the #). Type: string **Note**: Required if `title` is `not` set. Example: `#ShareAgain` | | second\_cta\_tweet *sometimes required* | The Tweet text to be used when the second CTA is clicked. **Note**: Required if `second_cta` is set. Type: string Example: `I Heart @AdsAPI Again` | | title *sometimes required* | The title for the card, which appears below the image and above the CTAs. Maximum length: 23 characters. Type: string **Note**: Required if `second_cta` is `not` set. Example: `Start a conversation` | | third\_cta *optional* | The Call-To-Action (CTA) hashtag for the third option. Maximum length: 20 characters (not counting the #). Type: string Example: `#ShareMore` | | third\_cta\_tweet *sometimes required* | The Tweet text to be used when the third CTA is clicked. Type: string **Note**: Required if `third_cta` is set. Example: `I Heart @TwitterDev` | | fourth\_cta *optional* | The Call-To-Action (CTA) hashtag for the fourth option. Maximum length: 20 characters (not counting the #). Type: string Example: `#ShareExtra` | | fourth\_cta\_tweet *sometimes required* | The Tweet text to be used when the fourth CTA is clicked. Type: string **Note**: Required if `fourth_cta` is set. Example: `I Heart @TwitterDev Again` | | unlocked\_image\_media\_key *optional* | A `media_key` of an image which will be used in the instant unlock scenario. This is a write-only field. In the response, the API will provide a X URL for this image. **Note**: The image must be in the account's media library. **Note**: A minimum image width of 800px and a width:height aspect ratio of 5:2 is required. Type: string Example: `3_883112887618682880` | | thank\_you\_url *optional* | The URL to be displayed with the thank you text. Type: string Example: `https://example.com/thankyou` | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/image_conversation?media_key=3_957113581522141184&name=image conversation card&first_cta=#moon&first_cta_tweet=stars&thank_you_text=thanks&title=Full moon` #### Example Response ``` { "data": { "name": "image conversation card", "first_cta": "#moon", "image_display_height": "670", "media_url": "https://pbs.twimg.com/media/DUhZuzxUQAAWZqr.jpg", "thank_you_text": "thanks", "id": "59woh", "first_cta_tweet": "stars", "media_key": "3_957113581522141184", "created_at": "2018-01-27T04:58:42Z", "image_display_width": "1280", "card_uri": "card://923498485702009837", "title": "Full moon", "updated_at": "2018-01-27T04:58:42Z", "deleted": false, "card_type": "IMAGE_CONVERSATION" }, "request": { "params": { "name": "image conversation card", "first_cta": "#moon", "image_display_height": "670", "media_url": "https://pbs.twimg.com/media/DUhZuzxUQAAWZqr.jpg", "thank_you_text": "thanks", "media_key": "3_957113581522141184", "account_id": "18ce54d4x5t", "first_cta_tweet": "stars", "image_display_width": "1280", "title": "Full moon", "card_type": "IMAGE_CONVERSATION" } } } ``` Update the specified image conversation card belonging to the current account. See [Uploading Media](https://developer.x.com/rest/public/uploading-media) for useful information on uploading images to our endpoints. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/image_conversation/:card_id` #### Parameters | Name | Description | | :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | A reference to the image conversation card you are operating with in the request. Type: string Example: `4i0qg` | | first\_cta *optional* | The Call-To-Action (CTA) hashtag for the first option. Maximum length: 20 characters (not counting the #). Type: string Example: `#ShareNow` | | first\_cta\_tweet *optional* | The Tweet text to be used when the first CTA is clicked. Type: string Example: `I Heart @AdsAPI` | | second\_cta *optional* | The Call-To-Action (CTA) hashtag for the second option. Maximum length: 20 characters (not counting the #). Type: string **Note**: Required if `title` is `not` set. Example: `#ShareAgain` | | second\_cta\_tweet *optional* | The Tweet text to be used when the second CTA is clicked. **Note**: Required if `second_cta` is set. Type: string Example: `I Heart @AdsAPI Again` | | third\_cta *optional* | The Call-To-Action (CTA) hashtag for the third option. Maximum length: 20 characters (not counting the #). Type: string Example: `#ShareMore` | | third\_cta\_tweet *optional* | The Tweet text to be used when the third CTA is clicked. Type: string **Note**: Required if `third_cta` is set. Example: `I Heart @TwitterDev` | | fourth\_cta *optional* | The Call-To-Action (CTA) hashtag for the fourth option. Maximum length: 20 characters (not counting the #). Type: string Example: `#ShareExtra` | | fourth\_cta\_tweet *optional* | The Tweet text to be used when the fourth CTA is clicked. Type: string **Note**: Required if `fourth_cta` is set. Example: `I Heart @TwitterDev Again` | | media\_key *optional* | The media key for an image to be used in this card. **Note**: The image must be in the account's [Media Library](https://developer.x.com/en/docs/ads/creatives/api-reference/media-library#media-library). **Note**: A minimum image width of 800px and a width:height aspect ratio of 1.91:1 is required. Type: string Example: `3_1151345051104991073` | | name *optional* | The name for the card. Type: string Example: `moon card` | | thank\_you\_text *optional* | The text to be displayed after the CTA is clicked. Maximum length: 23 characters. Type: string Example: `Thank you` | | thank\_you\_url *optional* | The URL to be displayed with the thank you text. Type: string Example: `https://example.com/thankyou` | | title *optional* | The title for the card, which appears below the image and above the CTAs. Maximum length: 23 characters. Type: string **Note**: Required if `second_cta` is `not` set. Example: `Start a conversation` | | unlocked\_image\_media\_key *optional* | A `media_key` of an image which will be used in the instant unlock scenario. This is a write-only field. In the response, the API will provide a X URL for this image. **Note**: The image must be in the account's media library. **Note**: A minimum image width of 800px and a width:height aspect ratio of 5:2 is required. Type: string Example: `3_883112887618682880` | #### Example Request `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/image_conversation/59woh?name=moon card` #### Example Response ``` { "data": { "name": "moon card", "id": "59woh", "created_at": "2018-01-27T04:58:42Z", "card_uri": "card://923498485702009837", "updated_at": "2018-01-29T21:04:39Z", "deleted": false, "card_type": "IMAGE_CONVERSATION" }, "request": { "params": { "account_id": "18ce54d4x5t", "card_type": "IMAGE_CONVERSATION", "card_id": "59woh", "name": "moon card" } } } ``` Permanently delete the specified image conversation card belonging to the current account. **Note**: This is a hard delete. As a result, it is not possible to retrieve deleted cards. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/image_conversation/:card_id` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | A reference to the image conversation card you are operating with in the request. Type: string Example: `4i0qe` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/image_conversation/4i0qe` #### Example Response ``` { "data": { "name": "image conversation card", "id": "4i0qe", "created_at": "2017-07-07T00:03:01Z", "updated_at": "2017-08-23T13:26:23Z", "deleted": true, "card_type": "IMAGE_CONVERSATION" }, "request": { "params": { "card_id": "4i0qe", "card_type": "image_conversation", "account_id": "18ce54d4x5t" } } } ``` ### Media Library #### GET accounts/:account\_id/media\_library Retrieve details for some or all media library objects associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/media_library` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `20` Min, Max: `1`, `50` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `c-1` | | media\_type *optional* | Scope the response to just the desired media type. Type: enum Possible values: `GIF`, `IMAGE`, `VIDEO` | | q *optional* | An optional query to scope resource by `name`, `title`, `file_name`, and `description` fields. **Note**: This performs case-insensitive *term* matching. Type: string Min, Max length: `1`, `255` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/media_library?count=1` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "count": 1 } }, "data": [ { "tweeted": true, "name": null, "file_name": "coffee https://t.co/4tcPU9XUon", "media_url": "https://pbs.twimg.com/media/DJvnJf_UEAAXnzC.jpg", "media_category": "TWEET_IMAGE", "media_key": "3_908573900237180928", "created_at": "2017-09-15T06:11:12Z", "media_status": "TRANSCODE_COMPLETED", "media_type": "IMAGE", "updated_at": "2017-11-16T06:00:01Z", "deleted": false } ], "next_cursor": "c-1" } ``` Retrieve a specific media library object associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/media_library/:media_key` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | media\_key *required* | A reference to the media library object you are operating with in the request. Type: string Example: `13_909110614026444802` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/media_library/13_909110614026444802` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "media_key": "13_909110614026444802" } }, "data": { "tweeted": true, "duration": 39973, "name": null, "file_name": "buildings https://t.co/xFdzrHM5QG", "description": null, "media_url": "https://video.twimg.com/amplify_video/909110614026444802/vid/1280x720/mfahmfkKVjjk1nGm.mp4", "media_category": "AMPLIFY_VIDEO", "poster_media_url": "https://pbs.twimg.com/amplify_video_thumb/909110614026444802/img/QZUNoaiCia0UFNrw.jpg", "poster_media_key": "3_909110614026444802", "media_key": "13_909110614026444802", "created_at": "2017-09-16T17:43:55Z", "media_status": "TRANSCODE_COMPLETED", "title": "buildings", "media_type": "VIDEO", "aspect_ratio": "16:9", "updated_at": "2017-09-27T13:04:00Z", "deleted": false } } ``` Associate a media object with the current account. For additional details, please see our [Media Library guide](https://developer.x.com/en/docs/ads/creatives/guides/media-library). **Note**: When adding a video with the `AMPLIFY_VIDEO` media category to the Media Library, it is automatically available as a `PREROLL` [account\_media](https://developer.x.com/en/docs/ads/creatives/api-reference/account-media#get-accounts-account-id-account-media) asset. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/media_library` #### Parameters | Name | Description | | :------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | media\_key *required* | The `media_key` for the uploaded content. A `media_key` is returned in the POST media/upload response when a `media_category` is specified. Type: string Example: `3_931236738554519552` | | description *optional* | The description that appears under the video when Tweeted. Maximum length: 200 characters. This is not rendered in the Tweet by default. To display the video's `description`, use the `video_description` parameter with the [POST accounts/:account\_id/tweet](https://developer.x.com/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet) endpoint. **Note**: Can only be used with videos. Type: string Example: `This is the description under the video.` | | file\_name *optional* | The file name for the media library object. Maximum length: 255. The file name can be seen in the media detail of every media asset in the Media Library UI on ads.x.com. This will be empty when the `file_name` is not set. Type: string Example: `coffee.jpeg` | | name *optional* | The name for the media library object. Maximum length: 100. This is the label under every media asset in the Media Library UI on ads.x.com. The label will be "Untitled" when the `name` is not set. Type: string Example: `Latte` | | poster\_media\_key *optional* | Specify a poster image for the video using the `media_key` of an uploaded image. If not specified, the first frame will be used. **Note**: Can only be used with videos. Type: string Example: `3_890599134483242005` | | title *optional* | The title (headline) that appears under the video when Tweeted. Maximum length: 70 characters. This is not rendered in the Tweet by default. To display the video's `title`, use the `video_title` parameter with the [POST accounts/:account\_id/tweet](https://developer.x.com/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet) endpoint. **Note**: Can only be used with videos. Type: string Example: `Video title` | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/media_library?media_key=3_931236738554519552` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "media_key": "3_931236738554519552" } }, "data": { "tweeted": false, "name": null, "file_name": null, "media_url": "https://pbs.twimg.com/media/DOxq4TtV4AAlvh_.jpg", "media_category": "TWEET_IMAGE", "media_key": "3_931236738554519552", "created_at": "2017-11-16T19:05:14Z", "media_status": "TRANSCODE_COMPLETED", "media_type": "IMAGE", "updated_at": "2017-11-16T19:05:23Z", "deleted": false } } ``` Update the specified media library object belonging to the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/media_library/:media_key` #### Parameters | Name | Description | | :------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | media\_key *required* | A reference to the media library object you are operating with in the request. Type: string Example: `16_844800354743074820` | | description *optional* | The description that appears under the video when Tweeted. Maximum length: 200 characters. This is not rendered in the Tweet by default. To display the video's `description`, use the `video_description` parameter with the [POST accounts/:account\_id/tweet](https://developer.x.com/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet) endpoint. **Note**: Can only be used with videos. Type: string Example: `This is the description under the video.` | | file\_name *optional* | The file name for the media library object. Maximum length: 255. The file name can be seen in the media detail of every media asset in the Media Library UI on ads.x.com. This will be empty when the `file_name` is not set. Type: string Example: `coffee.jpeg` | | name *optional* | The name for the media library object. Maximum length: 100. This is the label under every media asset in the Media Library UI on ads.x.com. The label will be "Untitled" when the `name` is not set. Type: string Example: `Latte` | | poster\_media\_key *optional* | Specify a poster image for the video using the `media_key` of an uploaded image. **Note**: Can only be used with videos. Type: string Example: `3_885003359340375465` | | title *optional* | The title (headline) that appears under the video when Tweeted. Maximum length: 70 characters. This is not rendered in the Tweet by default. To display the video's `title`, use the `video_title` parameter with the [POST accounts/:account\_id/tweet](https://developer.x.com/en/docs/ads/creatives/api-reference/tweets#post-accounts-account-id-tweet) endpoint. **Note**: Can only be used with videos. Type: string Example: `Video title` | #### Example Request `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/media_library/16_844800354743074820?title=cat GIF&description=in space` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "media_key": "16_844800354743074820", "title": "cat GIF", "description": "in space" } }, "data": { "tweeted": true, "duration": null, "name": null, "file_name": null, "description": "in space", "media_url": "https://video.twimg.com/tweet_video/C7lVclqVwAQqTCZ.mp4", "media_category": "TWEET_GIF", "poster_media_url": "https://pbs.twimg.com/tweet_video_thumb/C7lVclqVwAQqTCZ.jpg", "poster_media_key": "3_844800354743074820", "media_key": "16_844800354743074820", "created_at": "2017-10-20T09:51:54Z", "media_status": "TRANSCODE_COMPLETED", "title": "cat GIF", "media_type": "GIF", "aspect_ratio": "125:79", "updated_at": "2017-10-23T06:37:56Z", "deleted": false } } ``` Delete the specified media library object belonging to the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/media_library/:media_key` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | media\_key *required* | A reference to the media library object you are operating with in the request. Type: string Example: `7_860318603387600896` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/media_library/7_860318603387600896` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "media_key": "7_860318603387600896" } }, "data": { "tweeted": true, "duration": 14330, "name": "mountains-on-ads.x.com", "file_name": "mountains.mp4", "description": "", "media_url": "https://video.twimg.com/ext_tw_video/860318603387600896/pu/vid/1280x720/xI3DbvWKxdvICsFW.mp4", "media_category": "TWEET_VIDEO", "poster_media_url": "https://pbs.twimg.com/media/C_B3bTRVYAAFBFt.jpg", "poster_media_key": "3_860318839740915712", "media_key": "7_860318603387600896", "created_at": "2017-05-05T02:21:53Z", "media_status": "TRANSCODE_COMPLETED", "title": "uploaded on ads.x.com", "media_type": "VIDEO", "aspect_ratio": "16:9", "updated_at": "2017-05-05T02:26:58Z", "deleted": true } } ``` ### Poll Cards #### GET accounts/:account\_id/cards/poll Retrieve details for some or all poll cards associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/poll` #### Parameters | Name | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_ids *optional* | Scope the response to just the desired poll cards by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided. Type: string Example: `57i77` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `200` Min, Max: `1`, `1000` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `8x7v00oow` | | q *optional* | An optional query to scope cards by `name`. Omit this parameter to retrieve all. Maximum length: 80 characters. **Note**: This performs case-insensitive prefix matching. Type: string Example: `night` | | sort\_by *optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](https://developer.x.com/x-ads-api/fundamentals/sorting) for more information. Type: string Example: `created_at-asc` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | | with\_total\_count *optional* | Include the `total_count` response attribute. **Note**: This parameter and `cursor` are exclusive. **Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/poll?card_ids=57i77` #### Example Response ``` { "request": { "params": { "card_type": "poll", "card_ids": [ "57i77" ], "account_id": "18ce54d4x5t" } }, "next_cursor": null, "data": [ { "video_poster_height": "9", "name": "best coast poll", "start_time": "2018-01-09T04:51:34Z", "first_choice": "East", "video_height": "9", "video_url": "https://video.twimg.com/amplify_video/vmap/950589518557540353.vmap", "content_duration_seconds": "8", "second_choice": "West", "end_time": "2018-01-16T04:51:34Z", "id": "57i77", "video_width": "16", "video_hls_url": "https://video.twimg.com/amplify_video/950589518557540353/vid/1280x720/BRkAhPxFoBREIaFA.mp4", "created_at": "2018-01-09T04:51:34Z", "duration_in_minutes": "10080", "card_uri": "card://950590850777497601", "updated_at": "2018-01-09T04:51:34Z", "video_poster_url": "https://pbs.twimg.com/amplify_video_thumb/950589518557540353/img/nZ1vX_MXYqmvbsXP.jpg", "video_poster_width": "16", "deleted": false, "card_type": "VIDEO_POLLS" } ] } ``` Retrieve a specific poll card associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/poll/:card_id` #### Parameters | Name | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | A reference to the poll card you are operating with in the request. Type: string Example: `57i8t` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/poll/57i8t` #### Example Response ``` { "request": { "params": { "card_type": "poll", "card_id": "57i8t", "account_id": "18ce54d4x5t" } }, "data": { "name": "text only poll", "start_time": "2018-01-09T05:03:05Z", "first_choice": "Morning", "second_choice": "Evening", "end_time": "2018-01-11T05:03:05Z", "id": "57i8t", "created_at": "2018-01-09T05:03:05Z", "duration_in_minutes": "2880", "card_uri": "card://950593749658189824", "updated_at": "2018-01-09T05:03:05Z", "deleted": false, "card_type": "TEXT_POLLS" } } ``` #### POST accounts/:account\_id/cards/poll Create a new poll card associated with the specified account. This endpoint supports creating poll cards with either an image, a video, or no media. Polls with media are referred to as Media Forward Polls. **Note**: The Media Forward Polls product is in beta and requires the `PROMOTED_MEDIA_POLLS` account feature. **Note**: It is not possible to update (PUT) poll cards. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/poll` #### Parameters | Name | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | duration\_in\_minutes *required* | The amount of time (in minutes) the poll will remain open. After the specified `duration_in_minutes`, the poll will close and votes will no longer be accepted. This corresponds to `end_time` in the response. **Note**: This starts as soon as the card is created and not when it is added to a Tweet. Type: int Min, Max: `5`, `10080` | | first\_choice *required* | The first poll choice. Maximum length: 25 characters. Type: string Example: `One` | | name *required* | The name for the card. Type: string Example: `poll card` | | second\_choice *required* | The second poll choice.Maximum length: 25 characters. Type: string Example: `Two` | | fourth\_choice *optional* | The fourth poll choice. Maximum length: 25 characters. **Note**: The first, second, and third choices must be set when using this parameter. Type: string Example: `Four` | | media\_key *optional* | The `media_key` of a media library image or video which will be used in this card. This is a write-only field. In the response, the API will provide a X URL for this media. **Note**: The image or video must be in the account's media library. **Note**: A minimum image width of 800px and a width:height aspect ratio of 1.91:1 is required. | | third\_choice *optional* | The third poll choice. Maximum length: 25 characters. **Note**: The first and second choices must be set when using this parameter. Type: string Example: `Three` | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/poll?duration_in_minutes=10080&first_choice=East&second_choice=West&media_key=13_950589518557540353&name=best coast poll` #### Example Response ``` { "request": { "params": { "first_choice": "East", "name": "best coast poll", "second_choice": "West", "media_key": "13_950589518557540353", "duration_in_minutes": 10080 } }, "data": { "video_poster_height": "9", "name": "best coast poll", "start_time": "2018-01-09T04:51:34Z", "first_choice": "East", "video_height": "9", "video_url": "https://video.twimg.com/amplify_video/vmap/950589518557540353.vmap", "content_duration_seconds": "8", "second_choice": "West", "end_time": "2018-01-16T04:51:34Z", "id": "57i77", "video_width": "16", "video_hls_url": "https://video.twimg.com/amplify_video/950589518557540353/vid/1280x720/BRkAhPxFoBREIaFA.mp4", "created_at": "2018-01-09T04:51:34Z", "duration_in_minutes": "10080", "card_uri": "card://950590850777497601", "updated_at": "2018-01-09T04:51:34Z", "video_poster_url": "https://pbs.twimg.com/amplify_video_thumb/950589518557540353/img/nZ1vX_MXYqmvbsXP.jpg", "video_poster_width": "16", "deleted": false, "card_type": "VIDEO_POLLS" } } ``` Permanently delete the specified poll card belonging to the current account. Note: This is a hard delete. As a result, it is not possible to retrieve deleted cards. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/cards/poll/:card_id` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | card\_id *required* | A reference to the poll card you are operating with in the request. Type: string Example: `57i9t` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/cards/poll/57i9t` #### Example Response ``` { "data": { "name": "poll with image", "start_time": "2018-01-09T05:10:51Z", "id": "57i9t", "created_at": "2018-01-09T05:10:51Z", "updated_at": "2018-01-09T05:11:04Z", "deleted": true, "card_type": "IMAGE_POLLS" }, "request": { "params": { "card_id": "57i9t", "card_type": "poll", "account_id": "18ce54d4x5t" } } } ``` ### Preroll Call To Actions #### GET accounts/:account\_id/preroll\_call\_to\_actions Retrieve details for some or all preroll Call-To-Actions (CTAs) associated with line items under the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/preroll_call_to_actions` #### Parameters | Name | Description | | :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | line\_item\_ids *optional* | Scope the response to just the preroll CTAs associated with the specified line items by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided. Type: string Example: `8v53k` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `200` Min, Max: `1`, `1000` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `8x7v00oow` | | preroll\_call\_to\_action\_ids *optional* | Scope the response to just the desired preroll CTAs by specifying a comma-separated list of identifiers. Up to 200 IDs may be provided. Type: string Example: `8f0` | | sort\_by *optional* | Sorts by supported attribute in ascending or descending order. See [Sorting](https://developer.x.com/x-ads-api/fundamentals/sorting) for more information. Type: string Example: `created_at-asc` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | | with\_total\_count *optional* | Include the `total_count` response attribute. **Note**: This parameter and `cursor` are exclusive. **Note**: Requests which include `total_count` will have lower rate limits, currently set at 200 per 15 minutes. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/preroll_call_to_actions?line_item_ids=8v53k` #### Example Response ``` { "request": { "params": { "account_id": "18ce54d4x5t", "line_item_ids": [ "8v53k" ] } }, "next_cursor": null, "data": [ { "line_item_id": "8v53k", "call_to_action_url": "https://www.x.com", "call_to_action": "VISIT_SITE", "id": "8f0", "created_at": "2017-07-07T19:28:40Z", "updated_at": "2017-07-07T19:28:40Z", "deleted": false } ] } ``` Retrieve a specific Call-to-Action (CTAs) associated with this account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/preroll_call_to_actions/:preroll_call_to_action_id` #### Parameters | Name | Description | | :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | preroll\_call\_to\_action\_id *required* | A reference to the preroll call to action you are operating with in the request. Type: string Example: `8f0` | | with\_deleted *optional* | Include deleted results in your request. Type: boolean Default: `false` Possible values: `true`, `false` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/preroll_call_to_actions/8f0` #### Example Response ``` { "request": { "params": { "preroll_call_to_action_id": "8f0", "account_id": "18ce54d4x5t" } }, "data": { "line_item_id": "8v53k", "call_to_action_url": "https://www.x.com", "call_to_action": "VISIT_SITE", "id": "8f0", "created_at": "2017-07-07T19:28:40Z", "updated_at": "2017-07-07T19:28:40Z", "deleted": false } } ``` #### POST accounts/:account\_id/preroll\_call\_to\_actions Set the optional Call-to-Action (CTA) for a `PREROLL_VIEWS` line item. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/preroll_call_to_actions` #### Parameters | Name | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | call\_to\_action *required* | The CTA text for the displayed button within the ad. Type: enum Possible values: `GO_TO`, `SEE_MORE`, `SHOP`, `VISIT_SITE`, `WATCH_NOW` | | call\_to\_action\_url *required* | The URL to redirect the user to when the CTA button is clicked. Type: string Example: `https://www.x.com` | | line\_item\_id *required* | A reference to the line item you are operating with in the request. Type: string Example: `8v53k` | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/preroll_call_to_actions?line_item_id=8v53k&call_to_action=VISIT_SITE&call_to_action_url=https://www.x.com` #### Example Response ``` { "data": { "line_item_id": "8v53k", "call_to_action_url": "https://www.x.com", "call_to_action": "VISIT_SITE", "id": "8f0", "created_at": "2017-07-07T19:28:40Z", "updated_at": "2017-07-07T19:28:40Z", "deleted": false }, "request": { "params": { "line_item_id": "8v53k", "call_to_action": "VISIT_SITE", "call_to_action_url": "https://www.x.com", "account_id": "18ce54d4x5t" } } } ``` Update the optional Call-to-Action (CTA) for a `PREROLL_VIEWS` line item. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/preroll_call_to_actions/:preroll_call_to_action_id` #### Parameters | Name | Description | | :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | preroll\_call\_to\_action\_id *required* | A reference to the preroll CTA you are operating with in the request. Type: string Example: `8f0` | | call\_to\_action *optional* | The CTA text for the displayed button within the ad. Type: enum Possible values: `GO_TO`, `SEE_MORE`, `SHOP`, `VISIT_SITE`, `WATCH_NOW` | | call\_to\_action\_url *optional* | The URL to redirect the user to when the CTA button is clicked. Type: string Example: `https://www.x.com` | #### Example Request `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/preroll_call_to_actions/8f0?call_to_action=WATCH_NOW` #### Example Response ``` { "data": { "line_item_id": "8v53k", "call_to_action_url": "https://www.x.com", "call_to_action": "WATCH_NOW", "id": "8f0", "created_at": "2017-07-07T19:28:40Z", "updated_at": "2017-09-09T05:51:26Z", "deleted": false }, "request": { "params": { "preroll_call_to_action_id": "8f0", "call_to_action": "WATCH_NOW", "account_id": "18ce54d4x5t" } } } ``` Delete the specified preroll Call-to-Action (CTA) belonging to the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/preroll_call_to_actions/:preroll_call_to_action_id` #### Parameters | Name | Description | | :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | preroll\_call\_to\_action\_id *required* | A reference to the preroll CTA you are operating with in the request. Type: string Example: `8f0` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/preroll_call_to_actions/8f0` #### Example Response ``` { "data": { "line_item_id": "8v53k", "call_to_action_url": "https://www.x.com", "call_to_action": "VISIT_SITE", "id": "8f0", "created_at": "2017-07-07T19:28:40Z", "updated_at": "2017-08-30T06:08:21Z", "deleted": true }, "request": { "params": { "preroll_call_to_action_id": "8f0", "account_id": "18ce54d4x5t" } } } ``` ### Scheduled Tweets #### GET accounts/:account\_id/scheduled\_tweets Retrieve details for some or all Scheduled Tweets associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/scheduled_tweets` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | count *optional* | Specifies the number of records to try and retrieve per distinct request. Type: int Default: `100` Min, Max: `1`, `200` | | cursor *optional* | Specifies a cursor to get the next page of results. See [Pagination](https://developer.x.com/x-ads-api/introduction) for more information. Type: string Example: `c-j3cn6n40` | | user\_id *optional* | Specify the user to retrieve Scheduled Tweets for. Defaults to the `FULL` promotable user on the account when not set. Type: long Example: `756201191646691328` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_tweets?count=1` #### Example Response ``` { "request": { "params": { "count": 1 } }, "data": [ { "name": "test name", "completed_at": "2017-06-18T22:00:05Z", "text": "where you want to be", "user_id": "756201191646691328", "scheduled_status": "SUCCESS", "id": "875828692081037312", "nullcast": true, "created_at": "2017-06-16T21:33:27Z", "scheduled_at": "2017-06-18T22:00:00Z", "card_uri": null, "updated_at": "2017-06-19T18:02:20Z", "tweet_id": "876560168963645440", "media_keys": [] } ], "next_cursor": "c-j41uw400" } ``` Retrieve a specific Scheduled Tweet associated with the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/scheduled_tweets/:scheduled_tweet_id` #### Parameters | Name | Description | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | scheduled\_tweet\_id *required* | A reference to the Scheduled Tweet you are operating with in the request. Type: string Example: `917438609065623552` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_tweets/917438609065623552` #### Example Response ``` { "request": { "params": { "scheduled_tweet_id": "917438609065623552" } }, "data": { "name": null, "completed_at": null, "text": "", "user_id": "756201191646691328", "scheduled_status": "SCHEDULED", "id": "917438609065623552", "nullcast": true, "created_at": "2017-10-09T17:16:24Z", "scheduled_at": "2018-01-01T00:00:00Z", "card_uri": null, "updated_at": "2017-10-09T17:16:24Z", "tweet_id": null, "media_keys": [ "3_917438348871983104" ] } } ``` #### POST accounts/:account\_id/scheduled\_tweets Create a Scheduled Tweet for the account's full promotable user (default) or the user specified in the `as_user_id` parameter. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/scheduled_tweets` #### Parameters | Name | Description | | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | scheduled\_at *required* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the Tweet should be published (or go live). **Note**: Tweets can only be scheduled up to one year in the future. **Note**: Tweets should only be scheduled at minute-granularity; seconds will be ignored. Type: string Example: `2017-12-31T23:59:00Z` | | as\_user\_id *required* | The user ID of the advertiser on behalf of whom you are posting the Tweet. The advertiser must grant your handle (or handles) access to their ads account via [ads.x.com](https://ads.x.com/). This permission allows you to call the API using the OAuth tokens of your own handle rather than the advertiser's. Type: long Example: `756201191646691328` | | text *sometimes required* | The text of your status update. Required if no `media_keys` are specified. Type: string Example: `just setting up my twttr` | | card\_uri *optional* | Associate a card with the Tweet using the `card_uri` value from any cards response, if available. Type: string Example: `card://855591459410511943` | | media\_keys *optional* | Associate media with the Tweet by specifying a comma-separated list of identifiers. Include up to 4 images, 1 animated GIF, or 1 video. **Note**: The media asset must be in the account's [Media Library](https://developer.x.com/en/docs/ads/creatives/api-reference/media-library#media-library). Type: string Example: `13_1153584529292270722` | | nullcast *optional* | Whether to create a nullcasted (or "Promoted-only") Tweet. Type: boolean Default: `true` Possible values: `true`, `false` | | name *optional* | The name for the Scheduled Tweet. Maximum length: 80 characters. Type: string Example: `Tweet with name` | #### Example Request `POST https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_tweets?as_user_id=756201191646691328&media_keys=3_917438348871983104&scheduled_at=2018-01-01` #### Example Response ``` { "request": { "params": { "media_keys": [ "3_917438348871983104" ], "scheduled_at": "2018-01-01T00:00:00Z", "as_user_id": 756201191646691328 } }, "data": { "name": null, "completed_at": null, "text": "", "user_id": "756201191646691328", "scheduled_status": "SCHEDULED", "id": "917438609065623552", "nullcast": true, "created_at": "2017-10-09T17:16:24Z", "scheduled_at": "2018-01-01T00:00:00Z", "card_uri": null, "updated_at": "2017-10-09T17:16:24Z", "tweet_id": null, "media_keys": [ "3_917438348871983104" ] } } ``` Update the specified Scheduled Tweet belonging to the current account. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/scheduled_tweets/:scheduled_tweet_id` #### Parameters | Name | Description | | :-------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | scheduled\_tweet\_id *required* | A reference to the Scheduled Tweet you are operating with in the request. Type: string Example: `870321875435442177` | | card\_uri *optional* | Associate a card with the Tweet using the `card_uri` value from any cards response, if available. **Note**: Unset (remove) by specifying the parameter without a value. Type: string Example: `card://875146925316386347` | | media\_keys *optional* | Associate media with the Tweet by specifying a comma-separated list of identifiers. Include up to 4 images, 1 animated GIF, or 1 video. **Note**: The media asset must be in the account's [Media Library](https://developer.x.com/en/docs/ads/creatives/api-reference/media-library#media-library). **Note**: Unset (remove) by specifying the parameter without a value. Type: string Example: `13_1153584529292270722` | | nullcast *optional* | Whether to create a nullcasted (or "Promoted-only") Tweet. Type: boolean Possible values: `true`, `false` | | scheduled\_at *optional* | The time, expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), that the Tweet should be published (or go live). Type: string Example: `2017-12-31T23:59:59Z` | | text *optional* | The text of your status update. Type: string Example: `just setting up my twttr` | | name *optional* | The name for the Scheduled Tweet. Maximum length: 80 characters. Type: string Example: `Tweet with name` | #### Example Request `PUT https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_tweets/875057751231037440?text=winter solstice` #### Example Response ``` { "request": { "params": { "scheduled_tweet_id": "875057751231037440", "text": "winter solstice" } }, "data": { "name": null, "completed_at": null, "scheduled_status": "SCHEDULED", "text": "winter solstice", "user_id": "756201191646691328", "id": "875057751231037440", "nullcast": true, "created_at": "2017-06-14T18:30:00Z", "scheduled_at": "2017-12-21T00:00:00Z", "card_uri": null, "updated_at": "2017-06-14T18:30:00Z", "tweet_id": null, "media_keys": [] } } ``` Permanently delete the specified Scheduled Tweet belonging to the current account. **Note**: This is a hard delete. As a result, it is not possible to retrieve deleted Scheduled Tweets. #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/scheduled_tweets/:scheduled_tweet_id` #### Parameters | Name | Description | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | scheduled\_tweet\_id *required* | A reference to the Scheduled Tweet you are operating with in the request. Type: string Example: `870321875435442177` | #### Example Request `DELETE https://ads-api.x.com/12/accounts/18ce54d4x5t/scheduled_tweets/875064008595787776` #### Example Response ``` { "request": { "params": { "scheduled_tweet_id": 875064008595787776 } }, "data": { "name": null, "completed_at": null, "scheduled_status": "DELETED", "text": "hello, world", "user_id": "756201191646691328", "id": "875064008595787776", "nullcast": true, "created_at": "2017-06-14T18:54:52Z", "scheduled_at": "2017-06-15T00:00:00Z", "card_uri": null, "updated_at": "2017-06-14T19:01:16Z", "tweet_id": null, "media_keys": [] } } ``` ### Tweet Previews #### GET accounts/:account\_id/tweet\_previews Preview published, scheduled, or draft Tweets. * Supports previewing *multiple* Tweets—up to 200—in a single API request * Accurate, up-to-date rendering of Tweet layout and style * Supports all the latest formats and card types * Returns an iframe #### Resource URL `https://ads-api.x.com/12/accounts/:account_id/tweet_previews` #### Parameters | Name | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | account\_id *required* | The identifier for the leveraged account. Appears within the resource's path and is generally a required parameter for all Advertiser API requests excluding [GET accounts](https://developer.x.com/en/docs/ads/campaign-management/api-reference/accounts#get-accounts). The specified account must be associated with the authenticated user. Type: string Example: `18ce54d4x5t` | | tweet\_ids *required* | A comma-separated list of identifiers. Up to 200 IDs may be provided. **Note**: The IDs should correspond to the specified `tweet_type`. For example, if a Scheduled Tweet ID is passed in and `tweet_type=PUBLISHED` is specified, a preview for that ID will not be returned. Type: long Example: `1122911801354510336,1102836745790316550` | | tweet\_type *required* | The Tweet type for the specified `tweet_ids`. Type: enum Possible values: `DRAFT`, `PUBLISHED`, `SCHEDULED` | #### Example Request `GET https://ads-api.x.com/12/accounts/18ce54d4x5t/tweet_previews?tweet_ids=1122911801354510336,1102836745790316550&tweet_type=PUBLISHED` #### Example Response ``` { "request": { "params": { "tweet_ids": [ "1122911801354510336", "1102836745790316550" ], "tweet_type": "PUBLISHED", "account_id": "18ce54d4x5t" } }, "data": [ { "tweet_id": "1122911801354510336", "preview": "