# How to get an X (Twitter) profile picture by username

> Three ways to show someone's X profile picture from their @handle: the official API, scraping pbs.twimg.com, or a one-line image URL.

Published 2026-10-19 · https://avatarsapi.com/blog/get-x-twitter-profile-picture

You have an @handle and want the profile picture in an `<img>`. On X that used to be one unauthenticated URL. It no longer is. Here are the options as of October 2026.

## 1. The official X API

The X API returns `profile_image_url` on the user object (`GET /2/users/by/username/:username?user.fields=profile_image_url`). It works, but:

- you need a developer account and a bearer token, so it cannot run in a browser
- API access is paid, and the free tier is very limited
- the URL it returns is a `_normal` 48×48 image; you replace `_normal` with `_400x400` or remove it for the original size

Fine if you already pay for X API access. Overkill for showing an avatar.

## 2. Hotlinking `pbs.twimg.com`

The image itself lives at a URL like `https://pbs.twimg.com/profile_images/<id>/<hash>_400x400.jpg`. You cannot build that URL from the handle: the id and hash change every time the user uploads a new picture. You need to fetch it from somewhere first, then it goes stale the next time they change their photo.

## 3. An avatar URL that takes the handle

```html
<img src="https://avatarsapi.com/x/jack" alt="@jack">
```

avatarapi resolves the handle on each cache miss, validates that the result is a real image, and serves the bytes from the same URL. No key, no token, nothing on your server. When the user changes their picture, it shows up after the cache lifetime: 7 days by default, or anything from 1 hour to 28 days with `?ttl=` on a Starter Project. `?size=` sets the square size:

```html
<img src="https://avatarsapi.com/x/jack?size=96" alt="@jack">
```

If the account does not exist or has no picture, you get a 404 with a JSON body whose `code` is `ENOTFOUND`, so `onerror` can swap in a placeholder:

```html
<img
  src="https://avatarsapi.com/x/jack"
  onerror="this.onerror=null; this.src='/default-avatar.svg'"
  alt="">
```

Want the underlying URL instead of the bytes? Add `?json`:

```sh
curl "https://avatarsapi.com/x/jack?json"
```

```json
{
  "version": 1,
  "data": { "url": "https://…", "source": "x", "kind": "username" }
}
```

[unavatar](https://unavatar.io/docs) offers the same idea at `unavatar.io/x/:handle`; we compare the two [here](/blog/unavatar-alternative).

## Same pattern, other platforms

The URL shape is the same for the other 71 Sources: `/github/:user`, `/twitch/:user`, `/bluesky/:handle`, `/youtube/:handle`, `/tiktok/:user`, and more. The full list is at [avatarsapi.com/sources](https://avatarsapi.com/sources).

## Which to use

Use the X API if you already have paid access and need other profile fields. Use an avatar URL if all you need is the picture in a UI.
