# Upload an image

`POST https://api.twittrapi.com/client/media`

Upload an image and get a media ID for /client/create or /client/reply.

HTML page: https://www.twittrapi.com/docs/account-actions/client-media

> Account actions run with the session cookie of your own X account and go out through your own proxy. Use only accounts you control and follow X's rules; automated posting, following or liking can get the account limited or suspended. Treat the cookie like a password. Trial keys cannot call these endpoints.

## Parameters

| Name | In | Type | Required | Default | Description |
| --- | --- | --- | --- | --- | --- |
| `cookie` | body | string | yes |  | Cookie header of your own logged-in X session. Must contain auth_token and ct0; guest_id is used when present. |
| `proxy` | body | string | yes |  | Your proxy, format http://user:pass@host:port. The requests to X go out through it. Used only for this request. |
| `media` | body | string (base64) | yes |  | The image file, base64-encoded. JPEG, PNG or WebP up to 5 MB, GIF up to 15 MB; at most 8192 x 8192 pixels. |
| `media_type` | body | string | no |  | MIME type, e.g. image/jpeg or image/png. Optional: the API detects the type from the file's bytes and uses the detected type if this is missing or wrong. |

Body parameters are sent as a JSON object with `Content-Type: application/json`.

## Notes

- The response field you need is media_id_string inside response.
- Limits (X): JPEG, PNG, WebP up to 5 MB; GIF up to 15 MB; at most 8192 x 8192 pixels. BMP, TIFF and other formats are refused. For videos use /client/media/video.

## Response

Wrapper built by the API: {"status_code": <X status>, "response": "<X response body as a string>"}. The HTTP status is X's status when X answered 4xx or 5xx, otherwise 200. Parse response a second time to get X's JSON.

With `format=simple`: {data: {mediaId, mediaKey, expiresAfterSecs}}. Pass mediaId in media_ids of /client/create or /client/reply. When X reports an error inside the wrapper (for example a tweet that is already liked), the usual wrapper is returned unchanged.

## Errors

| Status | When |
| --- | --- |
| 400 | The body is not valid JSON. |
| 422 | A required field is missing (including proxy), an ID is not numeric, or the cookie has no ct0. Also: the account is suspended, X rejected the session, or the text was already posted. |
| 407 | The proxy is unreachable or timed out. |
| 400 | The file breaks X's limits (not JPEG, PNG, GIF or WebP; over 5 MB, or 15 MB for a GIF; over 8192 x 8192 pixels; or a damaged image header). Checked before anything is sent to X, refunded. The error names the limit and the file's value, e.g. "Banner must be between 200x100 and 8192x8192 pixels (got 120x60)" or "Profile photo must be at most 2 MB (got 3.4 MB)". |
| 422 | media is not valid base64. |
| X's status | Any status X answers with (for example 401, 403, 404): passed through with X's body. |

Shared errors (401 missing key, 402 no balance, 429 rate limit, 5xx) are listed on https://www.twittrapi.com/docs/helpful-infos/error-handling. Calls that fail with 400, 408, 409 or a 5xx error are refunded.

## Example request

curl:

```bash
curl -X POST 'https://api.twittrapi.com/client/media' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"cookie":"auth_token=...; ct0=...","proxy":"http://user:pass@host:port","media":"<base64>","media_type":"image/jpeg"}'
```

Python:

```python
import httpx

resp = httpx.post(
    "https://api.twittrapi.com/client/media",
    headers={"X-API-KEY": "YOUR_API_KEY"},
    json={
        "cookie": "auth_token=...; ct0=...",
        "proxy": "http://user:pass@host:port",
        "media": "<base64>",
        "media_type": "image/jpeg",
    },
    timeout=60,
)
print(resp.status_code, resp.json())
```

Node.js:

```js
const resp = await fetch("https://api.twittrapi.com/client/media", {
  method: "POST",
  headers: { "X-API-KEY": process.env.TWITTRAPI_API_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({
    cookie: "auth_token=...; ct0=...",
    proxy: "http://user:pass@host:port",
    media: "<base64>",
    media_type: "image/jpeg",
  }),
});
console.log(resp.status, await resp.json());
```
