Get X API credentials for automated posting

Produces an OAuth 2.0 user-context access token. Follow this in order; step 2 has a 30-second deadline.

Do not use the “Bearer Token” from the Keys page.

That one authenticates the application, not the account. It cannot post, and it fails in a way that looks like it worked: the account connects, reports healthy, and only errors later. The deliverable here is a user-context token.

1 · Create the app

  1. Open developer.x.com/en/portal/dashboard and sign in as the account the bot will post from.
  2. Sign up for the Free tier if prompted. It allows roughly 1,500 posts a month, which is enough. Do not buy a paid tier.
  3. Create a Project, then an App inside it.
  4. App → SettingsUser authentication settingsSet up, and enter:
App permissionsRead and write
Type of AppWeb App, Automated App or Bot
Callback URIhttps://localhost:3000/callback
Website URLany URL you own

Save, then open Keys and tokens and copy the OAuth 2.0 Client ID and Client Secret.

The client secret is shown once.

Record it now. Regenerating it invalidates the old one.

2 · Authorize, and grab the code

Open it, confirm the consent screen names the right account, and click Authorize app.

The next page will fail to load. That is correct.

It redirects to https://localhost:3000/callback?... and nothing is listening there. What matters is the URL in the address bar — copy all of it, and take the value of code= (it ends at the first &, if there is one).

The code expires in 30 seconds and is single-use.

Have step 3 ready before you click Authorize. If it expires, just redo step 2.

3 · Exchange the code for a token

Run this in a terminal. It cannot be done from a web page — X’s token endpoint sends no CORS headers, so a browser request is blocked before it is sent.

Fill the three fields above.

A successful response is JSON containing access_token and refresh_token. If you get invalid_grant or invalid_request, the code expired or was already used. Redo step 2.

4 · Verify before handing it over

Confirm the token really is user-context, rather than discovering it later:

curl -H "Authorization: Bearer ACCESS_TOKEN" https://api.x.com/2/users/me
Correct:

JSON with the account’s id and username.

Wrong token:

An error mentioning “Application-Only” or “OAuth 2.0 User Context” means you captured the app-only Bearer Token. Go back to step 2.

What to hand back

These are credentials.

Anyone holding the access token can post as the account. Send them through whatever private channel was agreed, not a chat log, an issue, or a file that outlives the task. Revoke any time at x.com/settings/connected_apps.

What this token can and cannot do

PostYes. Free tier, roughly 1,500 a month
Read timelinesNo. Paid endpoint; the platform reads via Nitter instead, which needs no credential
SearchNo. Paid, and no free substitute currently works
DMsNo. Requires a browser session, captured separately