> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xquik.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect X account API & profile login workflow

> Connect an X account with username, email, password, and its saved Authenticator App TOTP secret for tweet, reply, DM, and profile actions. Connecting is free.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="201" id="response-x-accounts-connect-201">
      ```json theme={null}
      {
        "id": "42",
        "xUserId": "9876543210",
        "xUsername": "elonmusk",
        "status": "active",
        "health": "healthy",
        "createdAt": "2025-01-15T12:00:00Z"
      }
      ```
    </Tab>

    <Tab title="202" id="response-x-accounts-connect-202">
      ```json theme={null}
      {
        "object": "x_account_connection_attempt",
        "id": "xatt_0123456789abcdef0123456789abcdef",
        "status": "pending",
        "pollAfterMs": 3000
      }
      ```
    </Tab>

    <Tab title="400" id="response-x-accounts-connect-400">
      ```json theme={null}
      {
        "error": "invalid_input",
        "message": "Invalid input. Check the request body."
      }
      ```
    </Tab>

    <Tab title="401" id="response-x-accounts-connect-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required. Provide a valid API key or bearer token."
      }
      ```
    </Tab>

    <Tab title="409" id="response-x-accounts-connect-409">
      ```json theme={null}
      {
        "error": "account_already_connected",
        "message": "This X account is already connected."
      }
      ```
    </Tab>

    <Tab title="422" id="response-x-accounts-connect-422">
      ```json theme={null}
      {
        "error": "login_failed",
        "message": "Login failed. Check credentials and try again."
      }
      ```
    </Tab>

    <Tab title="429" id="response-x-accounts-connect-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Retry later."
      }
      ```
    </Tab>

    <Tab title="502" id="response-x-accounts-connect-502">
      ```json theme={null}
      {
        "error": "x_user_lookup_failed",
        "message": "X username lookup failed. Try again later."
      }
      ```
    </Tab>

    <Tab title="503" id="response-x-accounts-connect-503">
      ```json theme={null}
      {
        "error": "service_unavailable",
        "message": "Service temporarily unavailable. Try again later."
      }
      ```
    </Tab>
  </Tabs>
</Panel>

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

<Callout icon="circle-check" color="#16a34a">
  **Free.** This endpoint does not consume credits.
</Callout>

<Warning>
  Xquik encrypts credentials at rest. It uses them only to maintain the
  connection. Xquik never stores plaintext passwords.
</Warning>

A durable Xquik connection requires Authenticator App 2FA and `totp_secret`.
Missing the key? Restart Authentication App 2FA in X to reveal a new secret.
Copy it, add it to your authenticator app, and finish X's 6-digit confirmation.
Then send the saved long key as `totp_secret`.

Xquik does not support custom, dedicated, or user-supplied proxies. Xquik does
not guarantee one fixed public IP per connected account.

A connection can finish immediately, continue as a tracked attempt, or ask
for an email code. If it continues, follow the returned `Location`. Do not
send the credentials again while the attempt is `pending`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://xquik.com/api/v1/x/accounts \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "username": "your_x_username",
      "email": "account@example.invalid",
      "password": "<ACCOUNT_PASSWORD>",
      "totp_secret": "<TOTP_SECRET>"
    }' | jq
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/x/accounts", {
    method: "POST",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      username: "your_x_username",
      email: "account@example.invalid",
      password: "<ACCOUNT_PASSWORD>",
      totp_secret: "<TOTP_SECRET>",
    }),
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://xquik.com/api/v1/x/accounts",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      json={
          "username": "your_x_username",
          "email": "account@example.invalid",
          "password": "<ACCOUNT_PASSWORD>",
          "totp_secret": "<TOTP_SECRET>",
      },
  )
  data = response.json()
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
  )

  func main() {
      body, _ := json.Marshal(map[string]interface{}{
          "username":    "your_x_username",
          "email":       "account@example.invalid",
          "password":    "<ACCOUNT_PASSWORD>",
          "totp_secret": "<TOTP_SECRET>",
      })

      req, err := http.NewRequest("POST", "https://xquik.com/api/v1/x/accounts", bytes.NewReader(body))
      if err != nil {
          panic(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
      req.Header.Set("Content-Type", "application/json")

      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      var data map[string]interface{}
      if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
          panic(err)
      }
      fmt.Println(data)
  }
  ```
</CodeGroup>

| X account connection result | Response source | Next step |
| - | - | - |
| Connected account ID | `id` from `201` | Use this ID for write actions and recovery. |
| X username | `xUsername` | Confirm the intended profile connected. |
| X user ID | `xUserId` | Store it. It stays the same when the username changes. |
| Connection state | `status` | Continue only after the account becomes active. |
| Login health | `health` | Require `healthy` before durable actions. |
| Pending attempt | `id` from `202 pending` | Poll the returned `Location` after `Retry-After`. |
| Email challenge | `id` from `202 requires_email_code` | Submit the matching email code before expiry. |
| Challenge expiry | `expiresAt` | Stop using an expired challenge. |

## Headers

<ParamField header="x-api-key" type="string" required>
  Your API key. Session cookie authentication is also supported. Generate a key from the [dashboard](https://xquik.com/dashboard).
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

## Body

<ParamField body="username" type="string" required>
  X username to connect. Xquik strips the `@` prefix if included.
</ParamField>

<ParamField body="email" type="string" required>
  Email address associated with the X account.
</ParamField>

<ParamField body="password" type="string" required>
  Password for the X account. Xquik encrypts it at rest on receipt.
</ParamField>

<ParamField body="totp_secret" type="string" required>
  Authenticator App TOTP secret required for a durable connection. This is the
  base32-encoded secret, not the 6-digit code.
</ParamField>

## 2FA secret key setup

Xquik needs the authenticator app secret key, not a live 6-digit code. The key is the long base32 string X shows while you set up **Authentication App** 2FA, for example `JBSWY3DPEHPK3PXP`.

<CardGroup cols={2}>
  <Card title="Use the secret key" icon="key-round">
    Paste the long base32 key into `totp_secret`. Xquik uses it to generate fresh 2FA codes during login challenges.
  </Card>

  <Card title="Do not use backup codes" icon="ban">
    Do not paste the 6-digit authenticator code, the 12-character backup code, a passkey, or a security key prompt.
  </Card>
</CardGroup>

If you already saved the secret key, send it as `totp_secret`. If you did not save it, create a fresh authenticator app secret on X:

<CardGroup cols={1}>
  <Card title="You saved the key" icon="clipboard-check">
    Paste that saved base32 secret into `totp_secret`. Do not paste the current 6-digit authenticator code.
  </Card>

  <Card title="2FA is on, key is missing" icon="rotate-ccw">
    X shows the text secret only during Authentication App setup. Turn Authentication App off, then on again. Copy the new key, then finish setup on X.
  </Card>

  <Card title="2FA is not enabled" icon="shield-check">
    Start Authentication App setup on X and reveal the text secret. Copy it and add it to your authenticator app. Confirm the 6-digit code on X, then connect.
  </Card>
</CardGroup>

1. Open X **Settings and Privacy > Security and Account Access > Security**.
2. Open **Two-Step Verification > Authentication App**.
3. Turn Authentication App off.
4. Turn Authentication App on again.
5. When the QR code appears, choose **Can't scan the QR code?** to reveal the text secret.
6. Copy the long secret key and store it in a password manager before leaving the setup screen.
7. Add that key to your authenticator app if you are setting it up fresh.
8. Finish enabling 2FA on X by entering the current 6-digit code from your authenticator app.
9. Send the saved long key in `totp_secret` when you call Xquik.

<Warning>
  Do not stop after copying the secret key. Complete the X-side 2FA confirmation before starting the Xquik connection, or the key will not work.
</Warning>

<Note>
  Xquik can't complete passkey or security key prompts. If X asks this account for a passkey, delete the passkey on X and keep Authenticator App 2FA.
</Note>

## Response

### 201 Created

<ResponseField name="id" type="string">Unique account ID.</ResponseField>
<ResponseField name="xUsername" type="string">Connected X username.</ResponseField>
<ResponseField name="xUserId" type="string">X user ID.</ResponseField>
<ResponseField name="status" type="string">Account connection status (for example `"active"`).</ResponseField>
<ResponseField name="health" type="string">Derived login and cookie health. One of `healthy`, `locked`, `needsReauth`, `recovering`, `suspended`, `temporaryIssue`. See [Account health](/api-reference/x-accounts/list#account-health) for meanings.</ResponseField>
<ResponseField name="createdAt" type="string">ISO 8601 time when Xquik connected the account.</ResponseField>

```json theme={null}
{
  "id": "3",
  "xUsername": "your_x_username",
  "xUserId": "9876543210",
  "status": "active",
  "health": "healthy",
  "createdAt": "2026-02-20T08:15:00.000Z"
}
```

### 202 Connecting

<ResponseField name="object" type="string">
  Always `x_account_connection_attempt`.
</ResponseField>

<ResponseField name="id" type="string">
  Connection attempt ID.
</ResponseField>

<ResponseField name="status" type="string">
  Always `pending`.
</ResponseField>

<ResponseField name="pollAfterMs" type="integer">
  Milliseconds to wait before checking the status URL.
</ResponseField>

```json theme={null}
{
  "object": "x_account_connection_attempt",
  "id": "xatt_0123456789abcdef0123456789abcdef",
  "status": "pending",
  "pollAfterMs": 3000
}
```

The response includes:

* `Location: /api/v1/x/account-connection-attempts/{id}`
* `Retry-After: 3`
* `Cache-Control: no-store`

Wait for `Retry-After`, then call [Get X Account Connection Status](/api-reference/x-accounts/connection-attempt). Keep checking while `status` is `pending`. Do not create another attempt.

### 202 Email code required

<ResponseField name="object" type="string">
  Always `x_account_connection_challenge`.
</ResponseField>

<ResponseField name="id" type="string">
  Challenge ID to submit with the email verification code.
</ResponseField>

<ResponseField name="status" type="string">
  Always `requires_email_code`.
</ResponseField>

<ResponseField name="expiresAt" type="string">
  ISO 8601 expiration time for the challenge.
</ResponseField>

<ResponseField name="message" type="string">
  Human-readable next step.
</ResponseField>

<ResponseField name="username" type="string">
  X username being connected.
</ResponseField>

```json theme={null}
{
  "object": "x_account_connection_challenge",
  "id": "xch_8vGd8Y9JvH6dV0xA",
  "status": "requires_email_code",
  "expiresAt": "2026-05-08T12:10:00Z",
  "message": "Enter the email verification code to continue.",
  "username": "elonmusk"
}
```

Submit the code to [Submit X Account Email Code](/api-reference/x-accounts/submit-challenge).

### 400 Invalid input

```json theme={null}
{ "error": "invalid_input", "message": "Invalid input. Check the request body." }
```

Missing `username`, `email`, `password`, or `totp_secret`, or invalid field format.

### 401 Unauthenticated

```json theme={null}
{ "error": "unauthenticated", "message": "Missing or invalid API key" }
```

Missing or invalid API key.

### 409 Duplicate

```json theme={null}
{ "error": "account_already_connected", "message": "This X account is already connected." }
```

The specified X account is already connected to your Xquik account.

### 429 Rate limit exceeded

```json theme={null}
{
  "error": "rate_limit_exceeded",
  "message": "Connection safety limit reached. Try again later.",
  "retryAfter": 900
}
```

You reached the connection safety limit. The `Retry-After: 900` header gives the seconds to wait before retrying. See the [rate limits guide](/guides/rate-limits) for details.

### 429 Login cooldown

```json theme={null}
{
  "error": "login_cooldown",
  "message": "Login is temporarily paused",
  "reason": "automated",
  "retryAfterMs": 3600000
}
```

A prior login attempt triggered a cooldown (for example, X flagged the session). Wait for `retryAfterMs` before retrying. The response includes a `Retry-After` header in seconds.

### 422 Login failed

```json theme={null}
{
  "error": "login_failed",
  "message": "Login failed. Check credentials and try again.",
  "retryAfterMs": 300000
}
```

X rejected the submitted username, email, password, or TOTP secret. Retry with the current password and the saved Authenticator App secret key, not a 6-digit code. When `retryAfterMs` is present, wait for that duration. The response also includes `Retry-After` in seconds.

```json theme={null}
{
  "error": "passkey_required",
  "message": "X asked this account to sign in with a passkey. Delete the passkey in X's security settings, keep Authentication app 2FA, then try again.",
  "retryAfterMs": 3600000
}
```

X showed this account a passkey sign-in, which Xquik can't complete. Delete the passkey in X **Settings and Privacy > Security and Account Access > Security > Passkey**, keep Authenticator App 2FA on, then connect again after `retryAfterMs`. The response also includes `Retry-After` in seconds.

```json theme={null}
{
  "error": "login_failed",
  "reason": "login_unconfirmed",
  "message": "X didn't confirm the sign-in after your password. Wait 5 minutes, then try again. Retrying sooner can make X lock the account.",
  "retryAfterMs": 300000
}
```

X showed no known page after the password. Xquik sends the password only once per attempt. Wait for `Retry-After`, then try again. Retrying sooner can make X lock the account.

### 502 X user lookup failed

```json theme={null}
{ "error": "x_user_lookup_failed", "message": "X user not found. Check the username." }
```

Xquik could not resolve the X username. Verify the handle is correct and that the account exists.

### 503 Service unavailable

```json theme={null}
{ "error": "service_unavailable", "message": "Service temporarily unavailable. Try again." }
```

The X connection service is temporarily unavailable. Retry after a short delay.

<Note>
  **Related.** [Get X Account Connection Status](/api-reference/x-accounts/connection-attempt) for a pending attempt, [List X Accounts](/api-reference/x-accounts/list) to see connected accounts, or [Re-authenticate](/api-reference/x-accounts/reauth) if a connection expires later.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.