Back to Blog
Guide

Slack API: Change a Profile Picture With users.setPhoto, Tested

We called users.setPhoto 18 times on a test Slack workspace: PNG, JPEG and GIF, crop boxes, a 27 MB file and a broken one. What Slack stored, the errors, and how fast the new photo showed up.

Slack Green Team
October 6, 2026
October 6, 2026
4 min read
Share:
slack api
profile picture

To change a Slack profile picture by API, send the image file to users.setPhoto as a multipart upload, with a user token (xoxp-) that has the users.profile:write scope. A bot token does not work: both users.setPhoto and users.deletePhoto returned not_allowed_token_type for our xoxb- token. On 6 October 2026 we made a test app in our free-plan test workspace and called users.setPhoto 18 times with different files and crop values. This page lists what Slack stored for each call, the two errors we got, and how fast an open Slack tab showed the new photo.

The call that worked

This is the script we ran. It reads the token from an environment variable, uploads avatar.png, and crops a square out of it:

import os, requests

token = os.environ["SLACK_USER_TOKEN"]  # xoxp- token with users.profile:write

with open("avatar.png", "rb") as f:
    r = requests.post(
        "https://slack.com/api/users.setPhoto",
        headers={"Authorization": f"Bearer {token}"},
        data={"crop_x": 600, "crop_y": 0, "crop_w": 600},  # optional square crop
        files={"image": f},
        timeout=60,
    )
body = r.json()
print(body["ok"], body.get("error"), body.get("profile", {}).get("avatar_hash"))

Output:

True None 76dabddc02c1

The field name must be image, and the file goes in the body, not as a URL. Slack has no parameter that takes an image URL. If you leave the file out, the reply is {"ok": false, "error": "no_image"}.

A successful reply holds a profile object with a new avatar_hash and eight URLs: image_24, image_32, image_48, image_72, image_192, image_512, image_1024 and image_original. The hash changed on every upload, also when we sent the same file twice. To get a user token with the scope, add users.profile:write under User Token Scopes and install the app; our oauth.v2.access guide shows where the xoxp- token comes back. Token types explains why a bot cannot change a person's photo.

What Slack stored for each file

We downloaded every URL that came back and read the real pixel size of each file.

File we sentResultimage_originalimage_1024Time
512 x 512 PNG, 7 KBok512 x 512 PNG1024 x 1024 (scaled up)1.2 s
200 x 200 PNGok200 x 2001024 x 1024 (scaled up)1.2 s
800 x 800 JPEGok800 x 800 JPEG1024 x 1024 JPEG3.8 s
512 x 512 GIFokGIFGIF2.1 s
1400 x 1400 PNG, 5.9 MBok1400 x 14001024 x 102412.7 s
3000 x 3000 PNG, 27 MBok3000 x 30001024 x 102434.1 s
8000 x 8000 PNG, 0.2 MBok8000 x 80001024 x 102422.0 s
190 bytes of text named .pngbad_image--1.5 s

We did not hit a size limit. A 27 MB file went through, and we got no too_large error at any size we tried. Large files are slow, though, because Slack makes all eight sizes before it replies: 34 seconds for the 27 MB file. Set your HTTP timeout well above the default of many clients.

Slack keeps the file format. A JPEG stays a JPEG and a GIF stays a GIF in every size. Small images are scaled up: our 200 px file came back as a blurry 1024 px image_1024. Send at least 1024 x 1024 if you want the large size to look sharp.

Crop boxes and non-square images

users.setPhoto takes three optional numbers: crop_x and crop_y for the top-left corner, and crop_w for the width of a square crop. We uploaded a 1200 x 600 image with "L" on the left half and "R" on the right half.

A 1200 by 600 test image with the letters L and R, and the three image_512 files Slack returned: without crop values the image is padded with white bars above and below; with crop_x 600 and crop_w 600 only the right half with R is kept; with crop_x 400 and crop_w 400 on a 512 pixel image the result is a narrow 112 by 400 strip

  • • No crop values. Slack did not cut anything off. It padded the image with white to 1200 x 1200, so the avatar shows white bars above and below. On a transparent PNG you get white, not transparency.
  • • crop_x=600, crop_y=0, crop_w=600. Slack kept the right half, 600 x 600, as asked.
  • • Only crop_w=100. crop_x and crop_y default to 0, so we got the top-left 100 x 100 corner.
  • • A crop box that runs past the edge. On a 512 px wide image, crop_x=400 with crop_w=400 returned ok, but Slack clipped the box at the edge. The avatar became a 112 x 400 strip, and image_512 was 143 x 512. Slack does not check the box for you, so check crop_x + crop_w against the image width before you call.

Never appear "away" on Slack again

Cloud-based. No downloads. Works 24/7 even when your laptop is off.

How fast the new photo shows in Slack

We kept a Slack tab open on a channel with one of our own messages, then called users.setPhoto. The avatar on that message, and on the older messages above it, changed to the new image within 2.4 seconds, at our first check, without a page reload. The web client loads avatars from ca.slack-edge.com/<team>-<user>-<avatar_hash>-<size>, so the new hash also gives every client a new URL and skips old caches.

The same Slack channel twice: after users.setPhoto the messages from sieun show the new blue test avatar, and after users.deletePhoto the same messages show Slack's default person icon

Remove the photo with users.deletePhoto

users.deletePhoto takes no arguments besides the token. It needs the same scope and also refuses bot tokens. After the call, our open tab showed the default person icon again within 9 seconds, and users.profile.get returned the same default as before the test: the same avatar_hash and the same image_512 URL. Slack keeps your default avatar color, so deleting the photo brings back the color you had; our page on Slack's default profile picture colors lists them. Calling users.deletePhoto when no photo is set still returns ok: true.

To read the current photo of any user, call users.info or users.profile.get and use profile.image_512 or image_1024. is_custom_image: true means the user uploaded a photo; the field is missing when they use the default. Our users.info guide shows the rest of the profile fields.

FAQ

Can a bot change its own profile picture with users.setPhoto?

No. The bot user's icon comes from the app's settings (Basic Information > Display Information > App icon), and users.setPhoto refused our bot token. To change the icon on single messages, see icon_emoji and icon_url.

Can an admin change another member's photo by API?

users.setPhoto has no user parameter. It always changes the photo of the person who owns the token, so each member has to install the app and grant users.profile:write themselves.

Why does my uploaded photo have white bars?

The image was not square and you sent no crop values. Slack pads it to a square with white. Send crop_x, crop_y and crop_w, or crop the file to a square before you upload it.

Always Active

Stop Jiggling Your Mouse.

Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.

Related Articles

Guide

Export a Slack Canvas to PDF or Markdown: What Works, Tested

Slack has no download or export button for canvases. We checked every menu on a test canvas, used Print canvas for PDF, and turned the API's HTML download into clean Markdown with a script we ran.

Slack Green Team
Guide

Slack Forward Multiple Messages: No Multi-Select, 3 Workarounds Tested

Slack forwards one message at a time. We checked the message menu, shift-click and the forward dialog, then tested three ways to move 10 messages at once: a thread, a copied range, and a script that posts links.

Slack Green Team
Guide

Send a Slack Message From PowerShell: Webhook, Bot Token and Files

Working PowerShell for Slack: Invoke-RestMethod to a webhook, chat.postMessage with a bot token, Block Kit, and a file upload. Every script was run in PowerShell 7.6.6 against a real Slack workspace, with its output.

Slack Green Team