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 path is the CLI bundled OAuth client (simplepost account add x) or the Scheduler app, both of which take care of token generation and rotation for you. Use the steps below only when you want to drive the SDK directly with your own X app.

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"],
});