Programmatically Publishing Tweets via Twitter API v2 and OAuth 2.0
Guide to programmatically publishing tweets using Twitter API v2. Covers OAuth 2.0 PKCE flow, required scopes, curl implementation, and error recovery.
28 Aug 2025, 18:19 UTC

The Challenge: User-Level Posting via API
Posting a tweet programmatically requires more than a standard API key. Many developers mistakenly use a Bearer Token from the Client Credentials Flow, which only allows read-only access to public data. To publish content, you must obtain a user-specific access token that carries the tweet.write permission scope.
Prerequisites
- Developer Account: A Twitter Developer account with a Project configured for Elevated access. Projects with only Essential access will return a 403 Forbidden error when attempting to post.
- OAuth 2.0 User Token: An access token generated via the Authorization Code Flow with PKCE (Proof Key for Code Exchange). This flow ensures the user has explicitly granted your application the
tweet.writescope. - HTTP Client: A tool capable of sending HTTPS POST requests, such as
curlor a language-specific library (e.g., Python Requests).
Implementation Procedure
To publish a tweet, you must send a JSON payload to the v2 tweets endpoint. The request must be authenticated with the user's Bearer token in the header.
Request Configuration
Run the following command from your local terminal. Replace the placeholders with your actual token and desired message.
# Required permissions: User Access Token with tweet.write scope
# Run this in a bash/zsh terminal
TOKEN="YOUR_USER_ACCESS_TOKEN"
MESSAGE="Hello from the Twitter API v2!"
curl -X POST "https://api.twitter.com/2/tweets" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"text\":\"$MESSAGE\"}"
Expected Response and Validation
A successful request returns an HTTP 201 Created status. The response body will contain a data object:
id: The unique numeric identifier of the new tweet.text: The exact text published.edit_history_tweet_id: The ID of the current version of the tweet.
Check the response headers for x-rate-limit-remaining. This indicates how many more tweets you can post within the current window before being throttled.
Troubleshooting and Recovery
If the request fails, use the HTTP status code to determine the recovery path:
| Status Code | Cause | Recovery Action |
|---|---|---|
| 400 Bad Request | Payload too long or malformed JSON. | Ensure text is UTF-8 encoded and $\le$ 280 characters. |
| 401 Unauthorized | Expired or invalid token. | Refresh the token using the OAuth 2.0 refresh token flow. |
| 403 Forbidden | Missing tweet.write scope or insufficient Project access. |
Verify Project is "Elevated" and re-authorize user with correct scopes. |
| 429 Too Many Requests | Rate limit exceeded. | Inspect retry-after header; implement exponential backoff. |
Practical Verification
To confirm the tweet is live and accessible without relying on a browser, perform a GET request using the id returned from the POST operation:
curl -X GET "https://api.twitter.com/2/tweets/TWEET_ID_HERE" \
-H "Authorization: Bearer $TOKEN"
If the returned JSON matches your original input, the operation was successful. To rollback or remove the tweet, you must issue a DELETE request to the same endpoint with the specific tweet ID.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.