TwitterAPIs Docs
API ReferenceWrite Actions

Update Avatar API

Replace the profile picture on the account behind your registered session. Send the image base64-encoded in the request body; there is no URL-fetch form and no multipart form. Billed at $0.0016 per call. Cost: $0.0016 per call.

POST user/update_avatar replaces the profile picture on the account behind your registered twitterapis session. It takes one field, image, holding the base64-encoded image bytes, and returns ok. The change is immediate and has no undo, so read your current profile_image_url with user/info first if you may want it back. It costs $0.0016 a call.

POST
/user/update_avatar

Authorization

bearerAuth
AuthorizationBearer <token>

Pass your API key as a bearer token on every request: Authorization: Bearer <API_KEY>.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/user/update_avatar" \  -H "Content-Type: application/json" \  -d '{    "image": "iVBORw0KGgoAAAANSUhEUgAA..."  }'
{  "ok": true}
{  "error": "bad_request",  "message": "Missing or malformed parameter. Fix the request before retrying."}
{  "error": "unauthorized",  "message": "The API key is missing, malformed, or revoked. Check the Authorization header."}
{  "error": "insufficient_credits",  "message": "Your balance is exhausted. Top up your credits to continue."}
{  "error": "forbidden",  "message": "The acting account is not authorized for this write action, or has no logged-in session."}
{  "error": "not_found",  "message": "The resource does not exist, for example a deleted tweet or a private account."}
{  "error": "rate_limited",  "message": "Too many requests. Back off and retry with exponential backoff."}
{  "error": "server_error",  "message": "Something failed on our side. Retry with backoff; if it persists, contact support."}

Pricing

UnitPrice
Per call$0.0016
Per 1,000 calls$1.60

When to use

Use it to set your own account's avatar, for example rotating a brand image on a schedule your system already drives. It only ever writes the account behind your session. To change the wide header image instead, use user/update_banner; to change name, bio, location or link, use user/update_profile.

Notes

  • image is required. A request carrying none of image, banner or data is refused with 400 before anything is sent upstream, and is not billed.
  • An image X cannot decode is refused upstream with an error, not with ok: false. That failure is loud rather than silent, which is why this endpoint does not need the read-back-and-compare check user/update_profile carries: verified against a deliberately malformed payload and against an empty one, both of which were refused.
  • Needs a session for the account you are acting as: either register one once (POST /customer/session), or pass per-call inline credentials as x-auth-token and x-ct0 request headers, which let a single API key act as many accounts. Returns 409 session_required if neither is supplied, 401 session_dead when the session has expired.
  • This takes effect immediately on x.com and there is NO UNDO. X keeps no history of a previous avatar, so if you may want the old image back, fetch profile_image_url from user/info and save the file before you call this.
  • Every accepted upload mints a NEW media id, so profile_image_url changes even when you re-upload the exact image already in place. That makes it a way to confirm the write applied without changing what anyone sees. A successful write DROPS the cached user/info entry for the account, so the read-back below is immediately correct in the ordinary case. One honest limit: that cache lives in the process that served the write, so if your read lands on another instance you may see the previous value for up to two minutes. If a read-back looks unchanged, re-read after the window rather than concluding the write failed. Treating a stale read as a real absence is what destroyed a brand banner once already.

FAQ

Can I pass a URL instead of base64?

No. The image field holds base64-encoded image bytes in the JSON request body. There is no URL-fetch form and no multipart upload; encode the file before you send it.

How do I confirm the new avatar actually applied?

Read the account back with user/info and compare profile_image_url against the value from before the call. X mints a new media id for every accepted upload, so the URL changes even if you uploaded a byte-identical image. The write clears the cached user/info entry first, so the read is normally current; if it comes back unchanged, re-read after about two minutes before concluding the upload failed, because a read served by another instance can still hold the previous value.

Can I restore my previous profile picture?

Only if you saved it yourself. X keeps no history, so once the new image is set the old URL stops resolving and the file is gone from our side and yours. Fetch profile_image_url from user/info and download it before you call this if that matters.

Is this the same as media/upload?

No. media/upload returns a media_id for attaching an image to a tweet. This endpoint sets the account's profile picture directly and returns no media_id; you do not need to call media/upload first.