Skip to content

Instantly share code, notes, and snippets.

@lmarkus
Last active March 27, 2024 07:15
Show Gist options
  • Save lmarkus/8722f56baf8c47045621 to your computer and use it in GitHub Desktop.
Save lmarkus/8722f56baf8c47045621 to your computer and use it in GitHub Desktop.
Extracting / Exporting custom emoji from Slack

Extracting Emoji From Slack!

Slack doesn't provide an easy way to extract custom emoji from a team. (Especially teams with thousands of custom emoji) This Gist walks you through a relatively simple approach to get your emoji out.

If you're an admin of your own team, you can get the list of emoji directly using this API: https://api.slack.com/methods/emoji.list. Once you have it, skip to Step 3

HOWEVER! This gist is intended for people who don't have admin access, nor access tokens for using that list.

Follow along...

Step 1

Open your Slack team on your browser (I used FireFox in this example)

Next, Open your developer tools, and go to the network tab. Look for a POST request on /emoji.list step1

Step 2

Right Click on the request, and choose to open it in a new tab. step2

This will cause the browser to replay the request, yielding a JSON file with all your emoji information. step3

Save this file somewhere as emoji.json

Step 3

Run download.sh on the file. (Make sure you chmod +x it to make it executable. Details on the download.sh file.

./download.sh emoji.json

Sit back and wait. This will create a folder called output and will save all your emoji to it.

Optional Step 4

To bulk upload your emoji into a new team, use this chrome extension: https://chrome.google.com/webstore/detail/neutral-face-emoji-tools/anchoacphlfbdomdlomnbbfhcmcdmjej

Notes

1- This downloads files sequentially, one at a time. I didn't want to incurr Slack's rage by hammering their edge server with concurrent downloads. 2- This will duplicate aliased emoji

#!/usr/bin/env bash
######
## UPDATE for 2019: I completely changed my approach on how to obtain the emoji dump.
## The new approach results in a JSON file, so the processing is a bit diferent than
## with the previous version. This version will also take care of aliased emoji.
# Use:
# Make this file executable, and feed it the results from the Slack emoji URL dump. Files will be downloaded to `output`
# chmod +x download.sh
# ./download.sh emoji.json
# Input File
INPUT="$1"
# Create output directory where downloaded emoji will be stored
mkdir -p output;
# Clean Up Source File:
# Break up the file into individual lines for processing (Comma and { to NewLine)
# Slack's emoji JSON brings an unwanted escape character "\". We need to remove it.
# We'll also remove unwanted quote marks `"` and curly braces "{" "}"
RAW_LIST=$(cat "${INPUT}" | tr ",{" "\\n" | sed -E 's/[\\"{}]//g')
# Separate into Custom Emoji (Ignoring slack's default ones) and Aliases
# Filter for custom emoji (ie: Anything on emoji.slack-edge.com), and remove the ":" separator
EMOJI_LIST=$( echo "${RAW_LIST}" | grep "https://emoji.slack-edge.com" | sed 's/:https/ https/')
# Filter for the aliases, and remove the separator
ALIAS_LIST=$( echo "${RAW_LIST}" | grep ":alias:" | sed 's/:alias:/ /' )
# First download all the emoji
echo "${EMOJI_LIST}" |
while read -r line || [[ -n "$line" ]]; do
parts=($line)
url=${parts[1]}
name=${parts[0]}
extension=${url##*.}
echo "Downloading ${name}.${extension}"
curl -s -o "output/${name}.${extension}" "${url}"
done;
# Now duplicate all the aliases
echo "${ALIAS_LIST}" |
while read -r line || [[ -n "$line" ]]; do
parts=($line)
alias=${parts[0]}
source=${parts[1]}
target=$(echo "${EMOJI_LIST}" | grep "${source} ")
extension=${target##*.}
echo "Looking for source of ${alias} in ${source} -> ${target}"
echo "copying output/${source}.${extension} to output/${alias}.${extension}"
cp "output/${source}.${extension}" "output/${alias}.${extension}"
done;
@caseyliqb
Copy link

Yet another renaming snippet in Python to address the problem of the / (in File Explorer) or : (ls in Terminal) being appended to the filename, if you happen not to like any of the solutions above :) Run from same dir as downloaded emojis.

import glob, re, os, sys

filetypes = ('*.png', '*.jpg', '*.gif')
pattern = r'([\w-]+)(:.)(png|jpg|gif)'
replace = r'\1' + '.' + r'\3'
emojis = []

for filetype in filetypes:
	emojis.extend(glob.glob(filetype))

filecount = len([name for name in os.listdir('.') if os.path.isfile(name)])
emojicount = len(emojis)

if not input('{} in folder (one might be this .py file), {} to be renamed - continue? (y/n):'
	.format(filecount, emojicount)).lower().strip()[:1] == "y": sys.exit(1)

for emoji in emojis:
	new_name = re.sub(pattern, replace, emoji)
	os.rename(emoji, new_name)
	print("{} -> {}".format(emoji, new_name))

@HVRyan
Copy link

HVRyan commented Mar 31, 2020

Is there a file limit on these? The emoji.adminlist seems to only give me 100 of our 600 company emoji... (yes, we know there's a lot, but these things aren't all fun and games... /s)

@caseyliqb
Copy link

Is there a file limit on these? The emoji.adminlist seems to only give me 100 of our 600 company emoji... (yes, we know there's a lot, but these things aren't all fun and games... /s)

@HVRyan I suspect that emoji.adminlist only gives you what is visible on the customize page, which doesn't load completely unless you scroll.

You can either use the token trick I described above, or someone mentioned they were able to get emoji.list via the web app.

@williamtroy
Copy link

Works wonders

@kprimas
Copy link

kprimas commented Apr 1, 2020

I was able to extract the json using @caseyliqb method with the token and have run the script. It appears to be downloading the images but when completed the output folder has nothing in it. Am I doing something wrong?

To add: once the script is completed. I receive this response at the end

to output/.png.png
': No such file or directory

@Firenza
Copy link

Firenza commented Jul 19, 2020

For anyone still looking for a solution, I wrote a python script to automate this via the API so you don't have to install a browser extension

https://github.com/Firenza/ExportImportSlackEmoji

@martinpaoloni
Copy link

This worked beautifully. Thanks @lmarkus!

@eputnam
Copy link

eputnam commented Oct 24, 2020

I had to go to /customize/emoji in my browser to get emoji.list but after that it was smooth sailin'. Didn't need a browser extension or anything. Also made a little script to get rogue colons out of the filenames.

FILES=output/*

for f in $FILES
do
  newname=$(echo $f | sed -e s/://g)
  echo "moving $f to $newname"
  mv $f $newname
done

Thanks @lmarkus!

@MikulasZelinka
Copy link

If you need more metadata for preprocessing (more than the emoji name, e.g., author or created date), you can access emoji.adminList and then simply edit the POST request to obtain all emojis at once.

This is needed because by default, adminList contains more metadata but only loads the first 100 emojis; whereas the default emoji.list contains all emojis but without any metadata.

Steps to obtain all emojis with metadata (in Firefox):

  1. Go to https://<your_workspace>.slack.com/customize/emoji.
  2. In the Network tab, locate a POST request for File emoji.adminList?... (ideally, by typing adminList into the search/Filter URLs box).
  3. Right click that row, open in new tab, the resulting JSON contains paging where you can see the total, which is the number of all custom emojis in that workspace. Note this number so that you can then query for all emojis at once. Close this tab.
  4. Right click the same request row with emoji.adminList again and this time, choose Edit and Resend.
  5. Down in Request Body, modify the Content-Disposition: form-data; name="count" from (the default, I assume) 100 to a number higher than what you've seen in step 3.
  6. Click Send.
  7. New POST request for File emoji.adminList?... appears below the first one, right click and open in new tab again and download and preprocess the resulting JSON according to your needs.

@dimitrieh
Copy link

Not ideal but scrolling down in the emoji view of "customize your slack" will generate an "admin emoji list" JSON output for each of the JSON pages.

So for example, if you have 500 emoji in total. Scrolling down all the way will make it so there are 5 admin emoji lists that can be downloaded separately by hand. Not ideal but faster if looking for a quick fix to get all your custom emoji.

You can run a quick script that goes over each of the JSON files in a dir with the download script:

#!/bin/bash
FILES=~/Downloads/emojilists/*
for f in $FILES
do
  echo "Processing $f file..."
  ~/Downloads/download.sh $f
done

@a-chen
Copy link

a-chen commented Dec 24, 2020

Not ideal but scrolling down in the emoji view of "customize your slack" will generate an "admin emoji list" JSON output for each of the JSON pages.

So for example, if you have 500 emoji in total. Scrolling down all the way will make it so there are 5 admin emoji lists that can be downloaded separately by hand. Not ideal but faster if looking for a quick fix to get all your custom emoji.

You can run a quick script that goes over each of the JSON files in a dir with the download script:

#!/bin/bash
FILES=~/Downloads/emojilists/*
for f in $FILES
do
  echo "Processing $f file..."
  ~/Downloads/download.sh $f
done

Yup this worked, thanks

@TomGudman
Copy link

https://github.com/Firenza/ExportImportSlackEmoji

Works like a charm (I needed to run it a second time for the upload to kick in). Thanks @Firenza

@thibault-ketterer
Copy link

thibault-ketterer commented May 31, 2021

works perfectly
I added a continue mode, if you've got a lot of emoji
if emoji is already downloaded in output, it just skips it

#!/usr/bin/env bash
######
## UPDATE for 2019: I completely changed my approach on how to obtain the emoji dump.
## The new approach results in a JSON file, so the processing is a bit diferent than
## with the previous version.  This version will also take care of aliased emoji.

# Use:
# Make this file executable, and feed it the results from the Slack emoji URL dump. Files will be downloaded to `output`
# 	chmod +x download.sh
# 	./download.sh emoji.json




# Input File
INPUT="$1"

# Create output directory where downloaded emoji will be stored
mkdir -p output;


# Clean Up Source File:
# Break up the file into individual lines for processing (Comma and { to NewLine)
# Slack's emoji JSON brings an unwanted escape character "\". We need to remove it.
# We'll also remove unwanted quote marks `"` and curly braces "{"  "}"

RAW_LIST=$(cat "${INPUT}" | tr ",{" "\\n" | sed -E 's/[\\"{}]//g')

# Separate into Custom Emoji (Ignoring slack's default ones) and Aliases

# Filter for custom emoji (ie: Anything on emoji.slack-edge.com), and remove the ":" separator
EMOJI_LIST=$( echo "${RAW_LIST}" | grep "https://emoji.slack-edge.com" | sed 's/:https/ https/')

# Filter for the aliases, and remove the separator
ALIAS_LIST=$( echo "${RAW_LIST}" | grep ":alias:" | sed 's/:alias:/ /' )


# First download all the emoji
echo "${EMOJI_LIST}" |
while read -r line || [[ -n "$line" ]]; do
	parts=($line)
	url=${parts[1]}
	name=${parts[0]}
	extension=${url##*.}

	if [ ! -f "output/${name}.${extension}" ];then
		echo "Downloading ${name}.${extension}"
		curl -s -o "output/${name}.${extension}" "${url}"
	else
		echo "already there output/${name}.${extension}"
	fi

done;

# Now duplicate all the aliases
echo "${ALIAS_LIST}" |
while read -r line || [[ -n "$line" ]]; do
	parts=($line)
	alias=${parts[0]}
	source=${parts[1]}


	target=$(echo "${EMOJI_LIST}" | grep "${source} ")
	extension=${target##*.}


	echo "Looking for source of ${alias} in ${source} -> ${target}"
	echo "copying output/${source}.${extension} to output/${alias}.${extension}"
	cp "output/${source}.${extension}" "output/${alias}.${extension}"

done;

@statico
Copy link

statico commented Jun 25, 2021

As of 2021-06-25, right clicking emoji.list in the Network tab of the dev tools gives me an {"ok":false,"error":"not_authed"} error. Instead I had to click on the request, go to the Response subtab, select all, copy, and paste the output into a new file. (pbpaste >emoji.json on macOS)

@wes-novack
Copy link

As of 2021-08-09, I don't see a POST to an "emoji.list" URL when loading Slack in a browser and inspecting the network calls. Did they switch to something else?

@ZebGirouard
Copy link

@wes-novack , @statico put the new method here on Jun 25. Basically, you can't open the results in a new tab anymore, you have to Copy All (or similar) from the Network tab in developer tools (this is in Firefox for me, but I would imagine Chrome has similarly-named options, since I know it has a Network tab with Response).

@yeti-brad
Copy link

yeti-brad commented Nov 4, 2021

This semi worked for me, I was able to get the JSON file but had to grab it from a few separate JSON POST's (I have a lot of emojis haha). However, when I run the extractor script (download.sh) it outputs them as named "value" i.e. value.png, value.gif or value.jpg so it can only get one of each type then ignores the rest.

Is anyone else experiencing this, it would be pretty painful going through renaming and re-running each time. The JSON file does have the alias on there:

{"name":"yahoo","value":"https:\/\/emoji.slack-edge.com\/T96F2G770\/yahoo\/a9995db7819a6403.png","updated":1600640341}

@skeenan947
Copy link

skeenan947 commented Dec 10, 2021

I decided to write a python script for this, parsing was cleaner without shell magic, and I couldn't get any other examples to work.
You'll need to get a bearer/bot token set up to make this work: docs here

This will create an out directory, and download every emoji in your workspace as .
We have over 2000 custom emoji, and this worked fine. yeah, error handling could be better :) but it works, and you don't actually need to be an admin to run it.

import json
import urllib2
import urlparse, os

OUTDIR='out'
SLACK_TOKEN='Bearer your-token-here'

response = urllib2.urlopen(urllib2.Request('https://slack.com/api/emoji.list',headers={'Authorization': SLACK_TOKEN}))
emoji = json.load(response)['emoji']

for name in emoji:
    url = emoji[name]
    if(url.startswith('alias:')):
        continue
    path = urlparse.urlparse(url).path
    ext = os.path.splitext(path)[1]
    fname = OUTDIR + '/' + name + ext
    print(fname)
    f = urllib2.urlopen(url)
    with open(fname, "wb") as code:
        code.write(f.read())

@MikeyABedneyJr
Copy link

MikeyABedneyJr commented Dec 13, 2021

Might request that there is another noted added to Optional Step 4 as slack now limits the rate of uploading emoji and at the time of posting this comment, Neutral Face Emoji Tools does not indicate any errors occur.

Reading the comments of that tool you can see people's suggestions of rate limiting their browser just for a successful upload.
As the tool is outside the scope of the instructions here and listed as "Optional", this is a 'nice to have' heads up for people finding this page.

@griffero
Copy link

griffero commented Jan 7, 2022

Note that depending on the amount of emojis that you have, you might need to check for more than one json. There is a kind of pagination.

I made a ruby script that just worked. You need the Json file though:

require 'httparty'
require 'json'
file = File.read('./emojis.json')
data_hash = JSON.parse(file)

data_hash['emoji'].each do |emoji|
  url = emoji['url']
  extension = url.split('.').last
  filename = "output/#{emoji['name']}.#{extension}"
  File.open(filename, "w") do |file|
    file.binmode
    HTTParty.get(url, stream_body: true) do |fragment|
      file.write(fragment)
    end
  end
end

@rocketnova
Copy link

@lmarkus Thanks for this very helpful gist and comment thread.

@griffero I liked your ruby script. I modified it slightly and put it in a docker container: https://github.com/rocketnova/slack_emojis

@jonalmeida
Copy link

Is anyone else experiencing this, it would be pretty painful going through renaming and re-running each time. The JSON file does have the alias on there:

{"name":"yahoo","value":"https://emoji.slack-edge.com/T96F2G770/yahoo/a9995db7819a6403.png","updated":1600640341}

@yeti-brad yes, I ran into this issue and after downloading all the separate files, I made them into one "results" array to parse that looked like this:

{
  "results": [
    {
      "name": "here",
      "value": "XXXXXX",
      "updated": 1592413041,
      "collection_id": "XXXXXXX"
    },
    {},
    {}
  ]
}

I then modified @skeenan947 's python script to download the results (had to update parts of it to python3):

import json
import urllib3
import os
import sys

OUTDIR='out'
http = urllib3.PoolManager()

f = open(sys.argv[1])
emojis = json.load(f)['results']

for emoji in emojis:
    url = emoji["value"]
    alias = ""
    if(url.startswith('alias:')):
        continue
    if "is_alias" in emoji:
        alias = " or " + emoji["alias"]
    name = emoji["name"]
    ext = os.path.splitext(url)[1]
    fname = OUTDIR + '/' + name + alias + ext
    print(fname)
    ef = http.request('GET', url)
    with open(fname, "wb") as code:
        code.write(ef.data)

Run it as:

$ download.py emoji_list.json

For the emojis that have aliases, it names them as [name] or [alt name].png. Hopefully, that will help when trying to grep through file names.

@dubiousdesigns
Copy link

dubiousdesigns commented Jul 29, 2022

I wasn't having any luck with the above methods (e.g., files named wrong, errors downloading, etc).

But this node script worked great!
https://github.com/csamp516/slack-emoji

@dlants
Copy link

dlants commented Sep 29, 2022

Yeah this is a bit out of date, but still somewhat workable.

Slack no longer uses the emoji.list route in the slack workspace. Instead, there's a emoji.adminList route on the customize/emoji admin page. That returns things in a similar format, though they are paginated, so you need to either page through manually or modify the request to get all the entries at once.

Once you have the adminList JSON responses downloaded, I used this nodejs script to actually download the image files:

import * as fs from 'fs';
import * as path from 'path';
import * as request from 'request';

async function run() {
  const emojiStr = fs.readFileSync('emojis.json');
  const emojis: any = JSON.parse(emojiStr.toString());

  for (const spec of emojis.emoji) {
    const outputFile = `./emojis/${spec.name}${path.extname(spec.url)}`;
    console.log(`Downloading ${spec.url} into ${outputFile}`);

    await new Promise((resolve, reject) =>
      request(spec.url)
        .pipe(fs.createWriteStream(outputFile))
        .on('close', resolve)
        .on('error', reject)
    );
  }
}

run().then(
  () => {
    console.log('success');
    process.exit(0);
  },
  (err) => {
    console.error(err);
    process.exit(1);
  }
);

@bossxl
Copy link

bossxl commented Oct 3, 2022

slightly updated version of @dlants script.
Allows for multiple admin files and removes request for fetch.

import * as fs from 'fs';
import * as path from 'path';
import fetch from 'node-fetch';

async function run() {
  const listsFolder = './emoji_lists/';
  const files = fs.readdirSync(listsFolder);
  const emojis = { emoji:[] }
  files.forEach(file => {
    const emojiStr = fs.readFileSync(`${listsFolder}/${file}`);
    const fileEmojis = JSON.parse(emojiStr.toString());
    emojis.emoji = emojis.emoji.concat(fileEmojis.emoji);
  });
  for (const spec of emojis.emoji) {
    const outputFile = `./emojis/${spec.name}${path.extname(spec.url)}`;
    console.log(`Downloading ${spec.url} into ${outputFile}`);
    await fetch(spec.url)
    .then(res =>
      res.body.pipe(fs.createWriteStream(outputFile))
    )
  }
}

run().then(
  () => {
    console.log('success');
    process.exit(0);
  },
  (err) => {
    console.error(err);
    process.exit(1);
  }
);

@jjnunogarcia
Copy link

Very quick solution in ruby

require 'json'
require 'down'
require 'fileutils'

file = File.read('./emojis.json')
data = JSON.parse(file)

data['emoji'].each do |child|
    url = child['url']
    extension = File.extname(URI.parse(url).path)
    outputFile = "./emojis/#{child['name']}#{extension}";
    puts "Downloading #{url} into #{outputFile}";
    tempfile = Down.download(url)
    FileUtils.mv(tempfile.path, outputFile)
end

@jaynetics
Copy link

faster ruby script that uses a single http connection and does not require external dependencies. adapted from the one above.

require 'fileutils'
require 'json'
require 'net/http'

file = File.read('./emojis.json')
data = JSON.parse(file)
emojis = data.is_a?(Array) ? data : data.fetch('emoji')

FileUtils.mkdir_p('./emojis')

host = URI.parse(emojis.dig(0, 'url') || fail('no emojis or no url')).host

Net::HTTP.start(host, use_ssl: true) do |http|
  emojis.each do |emoji|
    path = URI.parse(emoji['url']).path
    extension = File.extname(path)
    output_path = "./emojis/#{emoji['name']}#{extension}"
    puts "Downloading #{path} into #{output_path}"
    response = http.get(path)
    File.write(output_path, response.body)
  end
end

@h1bay1
Copy link

h1bay1 commented Mar 21, 2023

I've been developing a Slack app to make Emoji wrangling easier in Slack which is now in the Slack app marketplace.

https://emojibox.app

I've just finished an export feature via the official Slack API for those who don't wish to do anything custom 😄 feel free to add it to your workspace and give it a go 😊 Keen for feedback!

Export

@statico
Copy link

statico commented Mar 21, 2023

Very cool! I'll give it a try

@labynocle
Copy link

if needed, I created a tiny go project to download/backup all your custom emojis from a given slack space :)

@h1bay1
Copy link

h1bay1 commented Sep 5, 2023

Been pondering this problem a bit more and have since found an even easier way. It relies on Slack Connect and using EmojiBox.

Slack connect lets you instantly add emojis from one workspace to another by right clicking them.

Steps go like this.
Step 1: Create a slack connect channel between the 2 workspaces
Step 2: Invite yourself to the channel in the second workspace.
Step 3: Accept the invite
Step 4: Install EmojiBox in the app store if you haven’t already and add @EmojiBox from the old workspace to the slack connect channel.
Step 5: Type “@EmojiBox show all” (and hold onto something cause this logs out all your emojis)
Step 6: Right click any emoji you want in your new workspace and they’ll be instantly added.
Step 7: Archive the slack connect channel once your done.

🔥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment