> ## 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.

# Twitter profile picture API: update avatar images

> Update a connected X account's profile picture. Upload a JPEG or PNG file up to 15 MiB, or send an HTTPS image URL. Poll the write action. Costs 10 credits.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-write-update-avatar-200">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12345",
        "writeActionId": "12345",
        "action": "update_avatar",
        "status": "success",
        "terminal": true,
        "retryable": false,
        "safeToRetry": false,
        "statusUrl": "/api/v1/x/write-actions/12345",
        "pollAfterMs": null
      }
      ```
    </Tab>

    <Tab title="202" id="response-x-write-update-avatar-202">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12346",
        "writeActionId": "12346",
        "action": "update_avatar",
        "status": "dispatching",
        "terminal": false,
        "retryable": false,
        "safeToRetry": false,
        "statusUrl": "/api/v1/x/write-actions/12346",
        "pollAfterMs": 2000
      }
      ```
    </Tab>

    <Tab title="400" id="response-x-write-update-avatar-400">
      ```json theme={null}
      {
        "error": "missing_idempotency_key",
        "message": "Idempotency-Key is required. Generate one unique key for this write.",
        "charged": false,
        "chargedCredits": "0",
        "retryable": false,
        "safeToRetry": true
      }
      ```
    </Tab>

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

    <Tab title="402" id="response-x-write-update-avatar-402">
      ```json theme={null}
      {
        "error": "insufficient_credits",
        "message": "Insufficient credits. Top up or subscribe to continue."
      }
      ```
    </Tab>

    <Tab title="403" id="response-x-write-update-avatar-403">
      ```json theme={null}
      {
        "error": "account_needs_reauth",
        "message": "X account needs re-authentication. Re-add the account."
      }
      ```
    </Tab>

    <Tab title="404" id="response-x-write-update-avatar-404">
      ```json theme={null}
      {
        "error": "account_not_found",
        "message": "X account not found. Connect it first at /dashboard/account?tab=x-accounts."
      }
      ```
    </Tab>

    <Tab title="409" id="response-x-write-update-avatar-409">
      ```json theme={null}
      {
        "error": "idempotency_conflict",
        "message": "Idempotency-Key was already used with a different request.",
        "charged": false,
        "chargedCredits": "0",
        "retryable": false,
        "safeToRetry": true
      }
      ```
    </Tab>

    <Tab title="413" id="response-x-write-update-avatar-413">
      ```json theme={null}
      {
        "error": "media_too_large",
        "message": "This image stays over X's size limit after shrinking. Send a smaller one."
      }
      ```
    </Tab>

    <Tab title="415" id="response-x-write-update-avatar-415">
      ```json theme={null}
      {
        "error": "unsupported_media_type",
        "message": "Xquik couldn't read this image. Send a valid image file."
      }
      ```
    </Tab>

    <Tab title="422" id="response-x-write-update-avatar-422">
      ```json theme={null}
      {
        "error": "x_rejected",
        "message": "X rejected this request. Check what you sent & the account on x.com before you try again."
      }
      ```
    </Tab>

    <Tab title="429" id="response-x-write-update-avatar-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Try again later.",
        "retryAfter": 60
      }
      ```
    </Tab>

    <Tab title="500" id="response-x-write-update-avatar-500">
      ```json theme={null}
      {
        "error": "x_write_failed",
        "message": "Write action failed unexpectedly. Contact support if this persists."
      }
      ```
    </Tab>

    <Tab title="503" id="response-x-write-update-avatar-503">
      ```json theme={null}
      {
        "error": "write_tracking_unavailable",
        "message": "Write tracking unavailable. Try again.",
        "charged": false,
        "chargedCredits": "0",
        "retryable": true,
        "safeToRetry": true
      }
      ```
    </Tab>
  </Tabs>
</Panel>

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

This route updates one connected account's avatar.
A request accepts a JPEG or PNG file. It can
also use a fetchable HTTPS image URL. The maximum image size is 15 MiB.
Xquik shrinks larger images under 700 KB for X. X
recommends a 400 × 400 pixel profile picture.

## Update a Twitter profile picture through the API

Call `PATCH /x/profile/avatar` to replace the square profile image. Use
[Update Banner](/api-reference/x-write/update-banner) for the header image.
Use [Update Profile](/api-reference/x-write/update-profile) for names and other
public text fields. This route never retrieves another user's avatar.

<Callout icon="coins" color="#5c3327">
  **10 credits per call** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://xquik.com/api/v1/x/profile/avatar \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Idempotency-Key: avatar-update-1895432178065391234" \
    -F "account=myxaccount" \
    -F "file=@avatar.png" | jq
  ```

  ```javascript Node.js theme={null}
  const fs = require("fs");
  const FormData = require("form-data");

  const form = new FormData();
  form.append("account", "myxaccount");
  form.append("file", fs.createReadStream("avatar.png"));

  const response = await fetch("https://xquik.com/api/v1/x/profile/avatar", {
    method: "PATCH",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Idempotency-Key": "avatar-update-1895432178065391234",
    },
    body: form,
  });
  const data = await response.json();
  ```

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

  with open("avatar.png", "rb") as f:
      response = requests.patch(
          "https://xquik.com/api/v1/x/profile/avatar",
          headers={
              "x-api-key": "xq_YOUR_KEY_HERE",
              "Idempotency-Key": "avatar-update-1895432178065391234",
          },
          files={"file": ("avatar.png", f, "image/png")},
          data={"account": "myxaccount"},
      )
  data = response.json()
  ```

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

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "io"
      "mime/multipart"
      "net/http"
      "os"
  )

  func main() {
      var buf bytes.Buffer
      writer := multipart.NewWriter(&buf)
      writer.WriteField("account", "myxaccount")

      file, err := os.Open("avatar.png")
      if err != nil {
          panic(err)
      }
      defer file.Close()

      part, err := writer.CreateFormFile("file", "avatar.png")
      if err != nil {
          panic(err)
      }
      io.Copy(part, file)
      writer.Close()

      req, err := http.NewRequest("PATCH", "https://xquik.com/api/v1/x/profile/avatar", &buf)
      if err != nil {
          panic(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
      req.Header.Set("Idempotency-Key", "avatar-update-1895432178065391234")
      req.Header.Set("Content-Type", writer.FormDataContentType())

      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>

<CodeGroup>
  ```bash URL Upload theme={null}
  curl -X PATCH https://xquik.com/api/v1/x/profile/avatar \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Idempotency-Key: avatar-url-update-1895432178065391234" \
    -H "Content-Type: application/json" \
    -d '{
      "account": "myxaccount",
      "url": "https://example.com/avatar.png"
    }' | jq
  ```
</CodeGroup>

## Prepare a Twitter avatar image

Choose one direct file or one fetchable HTTPS URL. Never send both sources.
Accept only JPEG or PNG images. Reject GIF, WebP, and files above 15 MiB.
Review the square crop before approval. Keep faces and important marks near
the center. Use only images you own or may publish. Read X's
[profile image guidance](https://help.x.com/articles/166743) before uploading.

## Automate a profile picture update

Create one idempotency key for the selected account and image. Reuse it only
after an exact network interruption. Generate a new key after changing the
image or account. A 200 response is terminal. Poll `statusUrl` after 202 until
`terminal` becomes true. Never start another avatar update before that result.

## Verify the updated Twitter profile image

After a terminal write, use
[Twitter Profile Lookup](/api-reference/x/twitter-profile-lookup). Open its
`profilePicture` URL. Compare the rendered avatar with the approved source.
Review the public square crop. Keep the action ID and verification timestamp.
X may return a profile image URL like
`https://pbs.twimg.com/profile_images/example.jpg`.

| Profile avatar update column | Request or response source | Review rule |
| - | - | - |
| Acting X account | Request `account` | Confirm the intended profile. |
| Uploaded file | Request `file` | Accept JPEG or PNG up to 15 MiB. |
| Image URL | Request `url` | Use one fetchable HTTPS source. |
| Source choice | `file` or `url` | Send exactly one image source. |
| Idempotency key | Request header | Change it after editing the image. |
| Write action ID | Response `id` | Poll the matching lifecycle record. |
| Profile result | Later profile lookup | Confirm the public avatar URL. |

## Fix Twitter profile picture API errors

Fix invalid image fields after 400. Replace authentication after 401. Add
credits after 402. Reconnect after 403. Connect a missing account after 404.
Keep the original action after 409. Replace rejected media after 422. Honor
`Retry-After` after 429. Check `safeToRetry` after 500 or 503.

## Twitter profile picture API questions

### How do I authenticate an avatar update?

Send an `x-api-key` header or OAuth bearer token. The `account` field selects
the connected profile. Never place credentials inside an image URL.

### Can I update several Twitter profile pictures in one request?

No. Each request updates one connected account. Give each request its own
idempotency key. Wait for each account's terminal result.

### Can I use usernames or user IDs for avatar updates?

Yes. Set `account` to a connected username or numeric user ID. The selected
user's profile receives the new image. Verify that identity before uploading.

### Can I update a Twitter profile image with Python or an SDK?

Yes. Call the REST API with `import requests`, as shown above. Generated SDKs
can send the same multipart file, account, API key, and idempotency key.

### Does this route retrieve Twitter profile pictures?

No. It changes one connected account's avatar. Use Twitter Profile Lookup to
retrieve a public `profilePicture` URL.

## Headers

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

<ParamField header="Idempotency-Key" type="string" required>
  Unique key for this intended write. Reuse it only for an exact network replay.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Use `multipart/form-data` for file uploads or `application/json` for URL uploads. Most HTTP clients set the multipart boundary automatically.
</ParamField>

## Body

<ParamField body="account" type="string" required>
  X username or account ID of your connected account to act as.
</ParamField>

<ParamField body="file" type="binary">
  Multipart upload file. Required unless you send `url`. Accepted formats: JPEG, PNG. Maximum file size: 15 MiB. Xquik shrinks images over 700 KB.
</ParamField>

<ParamField body="url" type="string">
  HTTPS image URL. Required unless you send `file`. The URL must use HTTPS and remain directly fetchable.
</ParamField>

## Response

<Tabs>
  <Tab title="404 Account not found">
    Connect the requested account, then submit a newly approved write.
  </Tab>
</Tabs>

## Durable write recovery

<Warning>
  Send one unique `Idempotency-Key` per intended write.
  Replay the same account, target, payload, and media after a lost response.
  Keep the original key for that replay.
</Warning>

1. Store `id`, the nested `hash` in `request`, `billing`, and `statusUrl`.
2. Poll after `Retry-After` or `pollAfterMs` when `terminal` is `false`.
3. Retry only when `safeToRetry` is `true`.
4. Use a new key when `nextAction.requiresNewIdempotencyKey` is `true`.

### 200 terminal or 202 active

* After HTTP `200`, store the result and settled billing.
* After HTTP `202`, poll the same action. Never submit another write.
* After HTTP `400`, fix the named field. Use a new idempotency key.
* After HTTP `401`, fix authentication. Do not retry unchanged.
* After HTTP `402`, fund the account before another write.
* After HTTP `403`, reconnect the account.
* After HTTP `409`, keep the original action. Use a new key for new input.
* After HTTP `422`, fix the rejected request before retrying.
* After HTTP `429`, wait for `Retry-After`. Follow `nextAction`.

See [Get Write Action Status](/api-reference/x-write/get-write-action-status)
for every lifecycle field, terminal state, billing field, and retry rule.


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