---
title: "Accounts and settings"
canonical_url: "https://tweetstream.io/docs/account-management"
last_updated: 2026-09-24
---

# Accounts and settings

Manage monitored accounts and optional event sources through authenticated REST endpoints and account settings.

Canonical URL: https://tweetstream.io/docs/account-management

## Endpoints

Send each request to `https://api.tweetstream.io` with `Authorization: Bearer <API_KEY>`.

| Method | Path | Use |
| --- | --- | --- |
| `POST` | `/api/add-account` | Add monitored accounts |
| `DELETE` | `/api/remove-account` | Remove monitored accounts |
| `GET` | `/api/me` | Read plan limits and usage |
| `POST`, `DELETE` | `/api/affiliate-alerts` | Turn affiliate list alerts on or off |
| `GET`, `POST`, `DELETE` | `/api/binance-square` | List Binance Square accounts or turn one on or off |

## Add and remove accounts

Use the REST endpoints from your backend to change the watchlist.

### Send handles

Send one handle or an array in `accounts`. A leading @ is optional, and matching is case-insensitive.

- `POST /api/add-account`
- `DELETE /api/remove-account`

#### Add accounts

```typescript
const apiKey = process.env.TWEETSTREAM_API_KEY;
if (!apiKey) throw new Error("Missing TWEETSTREAM_API_KEY");

const response = await fetch("https://api.tweetstream.io/api/add-account", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    accounts: ["marketdesk", "realDonaldTrump"],
  }),
});

if (!response.ok && response.status !== 207) {
  const detail = await response.text();
  throw new Error(`Add failed (${response.status}): ${detail}`);
}

console.log(await response.json());
```

#### Remove an account

```typescript
const apiKey = process.env.TWEETSTREAM_API_KEY;
if (!apiKey) throw new Error("Missing TWEETSTREAM_API_KEY");

const response = await fetch("https://api.tweetstream.io/api/remove-account", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    accounts: "marketdesk",
  }),
});

if (!response.ok && response.status !== 207) {
  const detail = await response.text();
  throw new Error(`Remove failed (${response.status}): ${detail}`);
}

console.log(await response.json());
```

### Choose the credential scope

A standard API key updates the standard tracked-account list. An active Ultra key updates the Ultra selection and enforces its paid account limit.

### Read every result

The response contains one result per handle plus a `summary` with `total`, `succeeded`, and `failed` counts. Use the row state, not only the HTTP status, before retrying.

#### Add result

```json
{
  "action": "follow",
  "requestId": "8b4f9c9c-9e7b-4a0c-9c7d-2d4d6f0a9a25",
  "error": null,
  "results": [
    {
      "input": "marketdesk",
      "state": "added"
    },
    {
      "input": "realDonaldTrump",
      "state": "added"
    }
  ],
  "summary": {
    "failed": 0,
    "succeeded": 2,
    "total": 2
  }
}
```

#### Remove result

```json
{
  "action": "unfollow",
  "requestId": "3f1c7d2e-5a4b-4c8d-9e0f-1a2b3c4d5e6f",
  "error": null,
  "results": [
    {
      "input": "marketdesk",
      "state": "removed"
    }
  ],
  "summary": {
    "failed": 0,
    "succeeded": 1,
    "total": 1
  }
}
```

## Check current usage

Call `/api/me` with either the standard or Ultra API key. The private response is not cached.

- `GET /api/me`

### Read plan and usage fields

The response contains base-plan usage and any additive Ultra Speed details.

| Field | Type | Notes |
| --- | --- | --- |
| `credentialScope` | `standard` or `ultra_speed` | Scope of the bearer key used |
| `plan` | `BASIC`, `ELITE`, or `ENTERPRISE` | Runtime plan value: `BASIC` is Minimum, `ELITE` is Pro, and `ENTERPRISE` is Scale |
| `trackedAccounts` | object | Count, limit, and normalized handles |
| `websocket` | object | Current active connection count and plan limit |
| `stripe` | object | Subscription status and billing period fields; treat identifiers as private |
| `ultraSpeed` | `object` or `null` | Ultra status, billing cycle, limits, tracked handles (can repeat top-level `trackedAccounts` handles), scoped WebSocket usage, and cancellation timing |

### Inspect the response

#### Account usage request

```typescript
const apiKey = process.env.TWEETSTREAM_API_KEY;
if (!apiKey) throw new Error("Missing TWEETSTREAM_API_KEY");

const response = await fetch("https://api.tweetstream.io/api/me", {
  headers: {
    Authorization: `Bearer ${apiKey}`,
  },
});

if (!response.ok) {
  const detail = await response.text();
  throw new Error(`Account status failed (${response.status}): ${detail}`);
}

console.log(await response.json());
```

#### Account usage response

```json
{
  "credentialScope": "standard",
  "plan": "ELITE",
  "trackedAccounts": {
    "count": 2,
    "limit": 250,
    "handles": ["marketdesk", "realDonaldTrump"]
  },
  "websocket": {
    "count": 1,
    "limit": 10
  },
  "stripe": {
    "subscriptionStatus": "ACTIVE",
    "customerId": "[redacted]",
    "hasCustomer": true,
    "subscriptionId": "[redacted]",
    "currentPeriodStart": "2026-06-30T00:00:00.000Z",
    "currentPeriodEnd": "2026-07-30T00:00:00.000Z",
    "canceledAt": null
  },
  "ultraSpeed": {
    "active": true,
    "status": "ACTIVE",
    "billingCycle": "MONTHLY",
    "paymentRail": "STRIPE_CARD",
    "accountLimit": 25,
    "trackedAccounts": {
      "count": 1,
      "handles": ["realDonaldTrump"]
    },
    "websocket": {
      "count": 1,
      "limit": 5
    },
    "currentPeriodEnd": "2026-07-30T00:00:00.000Z",
    "cancelAtPeriodEnd": false,
    "canceledAt": null
  }
}
```

## Track affiliate list changes

Affiliate list changes are off by default for tracked business accounts on active or trialing Pro, Scale, and Ultra plans.

### Enable or disable alerts

Use `POST /api/affiliate-alerts`, `DELETE /api/affiliate-alerts`, or the dashboard. Send the tracked handle as `account`; a leading @ is optional. Repeating a request is safe. Success returns `{ account, affiliateAlertsEnabled }`.

- `POST /api/affiliate-alerts`
- `DELETE /api/affiliate-alerts`

#### Change the alert setting

```typescript
async function setAffiliateAlerts(account: string, enabled: boolean) {
  const response = await fetch("https://api.tweetstream.io/api/affiliate-alerts", {
    method: enabled ? "POST" : "DELETE",
    headers: {
      Authorization: `Bearer ${process.env.TWEETSTREAM_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ account }),
  });

  if (!response.ok) {
    throw new Error(await response.text());
  }
  return await response.json();
}

console.log(await setAffiliateAlerts("marketdesk", true));
console.log(await setAffiliateAlerts("marketdesk", false));
```

### Handle setting errors

When enabling alerts, TweetStream checks that the account still qualifies as a business account. A failed check returns `400` with `{ error: "This account is not a business account." }`.

| Status | When it appears | Action |
| --- | --- | --- |
| `200` | The saved setting matches the request | Read `affiliateAlertsEnabled` |
| `400` | The body is invalid or the account is not a business account | Fix the request |
| `401` | The bearer key is missing or invalid | Send a valid API key |
| `403` | The subscription or feature access is inactive | Check plan access |
| `404` | The handle is not tracked by this API key | Check the tracked handle |
| `500` | TweetStream could not confirm eligibility | Retry later |

### Store and replay changes

Changes go to WebSocket, the dashboard feed, and your Discord route. Keyword filters do not apply. Store relationships by `(organization.id, member.id)` and use `eventId` only for exact replay deduplication. Replay recorded changes with `GET /api/history?type=AFFILIATE`. There is no current-list snapshot or pre-feature backfill.

## Stream Binance Square posts

Binance Square accounts are off by default. Enable them in the dashboard or through this API. They remain separate from tracked X accounts and do not use an X account slot.

### List and change settings

`GET /api/binance-square` returns saved settings and the catalog fields `displayName`, `squareHandle`, `profileUrl`, `avatarUrl`, and `enabled`. `POST /api/binance-square` enables an account; `DELETE /api/binance-square` disables it. Send the Square handle in `account`; a leading @ is optional. Repeating a request is safe.

- `GET /api/binance-square`
- `POST /api/binance-square`
- `DELETE /api/binance-square`

- `profileUrl` is canonical. `avatarUrl` is `null` when unavailable.
- Settings belong to your TweetStream user, so active Standard and Ultra credentials share them.
- A saved response returns `{ account, binanceSquareEnabled }` with the canonical handle.

#### List Binance Square accounts

```typescript
const response = await fetch(
  "https://api.tweetstream.io/api/binance-square",
  {
    headers: {
      Authorization: `Bearer ${process.env.TWEETSTREAM_API_KEY}`,
    },
  },
);

if (!response.ok) {
  throw new Error(await response.text());
}

const catalog = await response.json();
console.log(catalog);
// {
//   accounts: [{
//     avatarUrl: "https://bin.bnbstatic.com/static/content/live-admin-api/images/chVikg58jFQ6ScXcVmWNmj.png",
//     displayName: "币安Binance华语",
//     enabled: true,
//     profileUrl: "https://www.binance.com/en/square/profile/Vpo7Qwqy63rk7_Km3zYYaQ",
//     squareHandle: "binancezh"
//   }],
//   canManage: true
// }
```

#### Enable or disable posts

```typescript
async function setBinanceSquarePosts(account: string, enabled: boolean) {
  const response = await fetch("https://api.tweetstream.io/api/binance-square", {
    method: enabled ? "POST" : "DELETE",
    headers: {
      Authorization: `Bearer ${process.env.TWEETSTREAM_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ account }),
  });

  if (!response.ok) {
    throw new Error(await response.text());
  }
  return await response.json();
}

console.log(await setBinanceSquarePosts("binancezh", true));
// { account: "binancezh", binanceSquareEnabled: true }
```

### Handle live content

Enabled accounts send live posts, replies, and quotes over WebSocket and applicable Discord routes. History does not store Square content, so reconnects do not backfill missed posts. Keyword filters still apply. Discord uses the global webhook unless an account-specific route applies.

- Content uses `tweet/content`, with `author.platform` set to `binance_square` and `kind` set to `post`, `reply`, or `quote`.
- Later tweet operations set `d.platform` to `binance_square`; use `(platform, tweetId)` as the post key.
- Replies and quotes include `ref` when reference context is available. Post links use canonical Binance URLs. Square authors have no `author.url`, since Square does not report an account website.

#### Binance Square content event

```json
{
  "v": 1,
  "t": "tweet",
  "op": "content",
  "id": "358051617962575",
  "ts": 1772000001100,
  "d": {
    "tweetId": "358051617962575",
    "kind": "quote",
    "text": "Example Binance Square quote",
    "createdAt": 1772000001000,
    "receivedAt": 1772000001100,
    "link": "https://www.binance.com/en/square/post/358051617962575",
    "author": {
      "id": "Vpo7Qwqy63rk7_Km3zYYaQ",
      "handle": "@binancezh",
      "platform": "binance_square"
    },
    "ref": {
      "type": "quote",
      "tweetId": "358012920288073"
    }
  }
}
```

### Handle setting errors

| Status | When it appears | Action |
| --- | --- | --- |
| `200` | The catalog was listed or the setting was saved | Read the response body |
| `400` | The body is invalid or the account is not in the catalog | Fix the request |
| `401` | The bearer key is missing or invalid | Send a valid API key |
| `403` | The API credential is not active | Check subscription status |
| `500` | TweetStream could not save the setting | Retry later |

## Read handle result states

| State | When it appears | Recommended handling |
| --- | --- | --- |
| `added` | Handle was added to the watchlist | Treat as success |
| `already_following` | Handle is already tracked | Treat as idempotent at the row level |
| `removed` | Handle was removed | Treat as success |
| `not_following` | Handle was not tracked | Treat as idempotent at the row level |
| `invalid_input`, `duplicate`, `not_found`, `failed` | Invalid input, duplicate input, missing account, or operation failure | Show the row-level message and retry only when appropriate |

## Read REST status codes

Add and remove endpoints return one result per handle. The HTTP status describes the batch; each result row describes its handle.

| Status | When it appears | Notes |
| --- | --- | --- |
| `200` | No row failed, including idempotent success rows | `summary.failed` is 0; `already_following` and `not_following` are successful outcomes |
| `207` | Some rows succeeded and some rows failed | Read each `results` row before retrying |
| `400` | The request body is invalid or every row failed for a non-temporary reason | Fix the request or row-level errors before retrying |
| `429` | Too many handle requests | Wait, then retry the batch |
| `503` | Every row failed for a temporary reason | Retry the batch with backoff |

## Check plan limits

- Minimum: 50 monitored accounts and 3 WebSocket connections after trial.
- Trial: 5 monitored accounts and 1 WebSocket connection for 3 days.
- Pro: 250 monitored accounts and 10 WebSocket connections.
- Scale: self-serve higher monitored-account and WebSocket limits from the pricing page.
- History replay is available on Pro, Scale, and Ultra Speed.
