> ## 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 banner API: update header images

> Update a connected X account's header image. Upload a JPEG or PNG up to 15 MiB at 1500 × 500 pixels, or send an HTTPS URL. Poll the write action. 10 credits.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-write-update-banner-200">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12345",
        "writeActionId": "12345",
        "action": "update_banner",
        "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-banner-202">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12346",
        "writeActionId": "12346",
        "action": "update_banner",
        "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-banner-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-banner-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-banner-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-banner-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-banner-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-banner-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-banner-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-banner-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-banner-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-banner-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-banner-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-banner-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 header
image. Upload a JPEG or PNG file, or provide a fetchable HTTPS image URL. The
maximum file size is 15 MiB. Xquik shrinks larger files under 2 MB for X.
X recommends 1500 × 500 pixels for profile banners.

## Update a Twitter profile banner through the API

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

<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/banner \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Idempotency-Key: banner-update-1895432178065391234" \
    -F "account=myxaccount" \
    -F "file=@banner.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("banner.png"));

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

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

  with open("banner.png", "rb") as f:
      response = requests.patch(
          "https://xquik.com/api/v1/x/profile/banner",
          headers={
              "x-api-key": "xq_YOUR_KEY_HERE",
              "Idempotency-Key": "banner-update-1895432178065391234",
          },
          files={"file": ("banner.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("banner.png")
      if err != nil {
          panic(err)
      }
      defer file.Close()

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

      req, err := http.NewRequest("PATCH", "https://xquik.com/api/v1/x/profile/banner", &buf)
      if err != nil {
          panic(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
      req.Header.Set("Idempotency-Key", "banner-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/banner \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Idempotency-Key: banner-url-update-1895432178065391234" \
    -H "Content-Type: application/json" \
    -d '{
      "account": "myxaccount",
      "url": "https://example.com/banner.png"
    }' | jq
  ```
</CodeGroup>

## Prepare Twitter profile banner dimensions

Use a 1500 × 500 pixel canvas. That size has a 3:1 aspect ratio and
matches X's recommended header
dimensions. Xquik resizes images outside 200x100 to 8192x8192 pixels.

Accept only JPEG or PNG images. This route does not support animated GIFs.
Review every export before uploading. Read X's
[profile banner guidance](https://help.x.com/articles/166743) for current layout
recommendations.

Preview the banner on desktop and a mobile device. The profile picture can
cover the bottom left corner. Keep faces, logos, and text outside that area.
Other sizes or crops can hide parts of the artwork. Check the
image size before queueing the write.

Keep one 1500 × 500 source file for each approved campaign. Export banner images
from that source. Do not resize previous uploads. Record the dimensions
and final file size with the source.

Use approved logos and colors.
A banner change does not prove higher engagement.
Measure profile visits and follows separately.

## Automate a Twitter banner update

Create one idempotency key for the selected account and banner. 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.

## Verify the updated Twitter banner

After a terminal write, use
[Twitter Profile Lookup](/api-reference/x/twitter-profile-lookup). Open its
`profileBannerUrl` value. Compare the rendered header with the approved source.
Check desktop and mobile crops. Store the action ID and verification timestamp.

| Profile banner 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 banner URL. |

## Fix Twitter profile banner 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.

## 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 files over 2 MB.
</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.