Skip to main content

X

SimplePost posts to X with OAuth 2.0 user credentials. X API access is paid by X, so make sure your developer account and app have enough API access before testing.

Content support

CapabilitySupport
Text280 characters by default. Up to 25,000 with X Premium long-post access.
MediaImages and videos.
Media limitUp to 4 images, or 1 video. Mixed media is not allowed.
Empty postsNot allowed; provide text or media.
ThreadsPass replyToId to chain a reply onto an existing post.

Set up credentials

The fastest managed path is the Scheduler app. For a local CLI account or direct SDK use, create your own X app using the steps below, export SIMPLE_POST_X_CLIENT_ID, and run simplepost account add x to complete OAuth and persist rotated tokens locally.

1. Create a developer account

  1. Open the X Developer Console.
  2. Sign in with the X account that should own the app.
  3. Start a new project if the console asks for one.
  4. Choose the use case closest to automation or bot posting.

2. Create an app

  1. Open Apps in the left sidebar.
  2. Create a new app.
  3. Use a development environment for personal testing.
  4. Open the app settings after it is created.

3. Configure OAuth

  1. Set App permissions to Read and write.
  2. Set Type of App to Web App, Automated App, or Bot.
  3. Add a Callback URI and Website URL. If you are only posting for yourself, placeholder HTTPS URLs are enough for manual token generation.
  4. Save the Client ID and Client Secret.

4. Generate user tokens

In the app's OAuth 2.0 keys area, generate an access token and refresh token for your own account. Include tweet.write. Save both tokens.

The access token is short-lived. The refresh token lasts longer, but X rotates it every time it is used — see token rotation.

Environment variables

X_CLIENT_ID=
X_CLIENT_SECRET=
X_ACCESS_TOKEN=
X_EXPIRES_AT=
X_REFRESH_TOKEN=

The SDK activates X env credentials only when X_CLIENT_ID is set together with at least one of X_ACCESS_TOKEN or X_REFRESH_TOKEN. X_CLIENT_SECRET is required only for confidential OAuth clients.

SDK options

Reply to a post:

await post({
content: { text: "This is a reply" },
platforms: ["x"],
options: {
x: { replyToId: "1234567890" },
},
});

Programmatic user credentials:

const result = await post({
content: { text: "Hello from X" },
platforms: ["x"],
options: {
x: {
credentials: {
clientId: "X_APP_CLIENT_ID",
clientSecret: "X_APP_CLIENT_SECRET",
accessToken: "USER_ACCESS_TOKEN",
expiresAt: 1234567890,
refreshToken: "USER_REFRESH_TOKEN",
},
},
},
});

const refreshed = result.get("x")?.extraData?.refreshedCredentials;
if (refreshed) {
await saveUserTokens(refreshed);
}

Refresh token rotation

X invalidates the old refresh token whenever a new one is issued. The SDK returns refreshed credentials in result.extraData.refreshedCredentials, but it does not write them to your database, .env, CLI store, or accounts.json. Persist the new token yourself. The CLI and Scheduler handle this for you when posting through their stored X accounts.

REST server account

Add this entry under accounts in the self-hosted REST server's accounts.json:

{
"id": "x-main",
"platform": "x",
"label": "Main brand X account",
"username": "yourbrand",
"platformAccountId": "1234567890",
"credentials": {
"clientId": "...",
"clientSecret": "...",
"refreshToken": "..."
}
}

Examples

await post({
content: {
text: "Posting images",
media: [
{ type: "image", path: "./image_1.jpg" },
{ type: "image", path: "./image_2.jpg" },
],
},
platforms: ["x"],
});