Everything I know about the technical side of Bitmojis

A lot of this is entirely undocumented and cannot be found in many other public places. I will do my best to guess while teaching. All Bitmojis and IDs featured are strangers and have no connection to me or each other. See the links section for credits. I have been running the Wayback Machine extension during this whole process, so a lot of the information, ranging from rendered Bitmojis to community research, is preserved.

TODO

Table of Contents

Styles

As best as I can tell, there are two styles:

  1. The new 3D style, the new default
  2. The legacy 2D style, hidden off Snapchat and Bitmoji

Image showing the difference between a 3D and a 2D rendered Bitmoji, with labels for each

The IDs for the new poses are randomly mixed in with the old poses. A good way to filter to show only new 3D poses is to set the scale to 3 and above or use a special Bitmoji ID that seemingly randomly only works with 3D poses.

IDs

Seemingly random IDs? Well, first of all, what kind of IDs are there? Some of the below IDs can be found in the response of an authenticated request to https://web.snapchat.com/ami/friends. (Hint: open Snapchat for Web, open the developer tools' network tab, filter by that URL, and reload the page.)

"Below"
Yes, there are some Bitmojis that look very similar to the "Avatar Bitmojis", but they are not. I know this because, from my testing, adding the "bbs=true" query param to the Bitmoji URLs, any non-avatar Bitmojis will render as a special default (the bbs version of 10226441 actually), and because, well, they look different. These may be a 5th category of Bitmoji, as they also don't appear in the templates API, and they don't seem to be "Profile Bitmojis" (the other kind that doesn't appear in the templates API). "bbs" will be explained in more detail later.

5th comic ID type

URLs

There are many types of URLs, different kinds for each engine and even other kinds of assets. The URLs for logically grouped Bitmoji usually have the same common pattern, but it can still be confusing.

Engines

There are 3 types of "engines" that generate Bitmoji. See Libmoji's Rendering-Engines Wiki. I focus more on Bitmoji displaying rather than creation/customisation and the odd nuances.

  1. The Preview Engine, which allows for customization of the Bitmoji's appearance, and is used in the Bitmoji editor. It does NOT take any kind of Avatar or Pose ID, but rather a series of query parameters to manually describe the Bitmoji.

  2. The cPanel Engine, not important nor used. Most of the Bitmojis I've seen are generated by the next engine, but funnily enough, only take advantage of the cPanel Engine's customisation options, such as transparent and scale. Unlike the Preview engine, it DOES take both a Pose ID and an Avatar ID. cPanel stands for "Comic Panel", hence the inclusion of comic_id (or Pose ID, etc - see IDs - Comic ID) internally at Bitmoji.

  3. The Render Engine. The default used across Snapchat and Bitmoji. It also takes an Avatar ID and Pose ID and supports the same options as the cPanel engine with a few extras, such as sex and pd2. I have not tested these, but they supposedly allow Preview Engine-like customisation, albeit with "legacy" traits.

Base URLs

There are many base URLs for Bitmojis, with each engine, and other assets, getting their own base URL (or multiple!).

Render Engine

In the first three URLs, render can be replaced with avatar, but 3d is required. In the last one, 3d can be omitted entirely while render is still present, OR render can be replaced with avatar while 3d is still present.

cPanel Engine

Not much to talk about here luckily. It takes a comic_id and an avatar_id. It also has optional query parameters such as scale ([0-2]) which controls the resolution, transparent ([0-1]) which seems to not work anymore as it's always transparent, style which also seems to do nothing, and as mentioned in the Libmoji wiki, the palette query parameter which, yet again, seems to do nothing. This engine can be considered a legacy engine and deprecated. Example template: ${render_base_url}/${comic_id}-${avatar_id}_${avatar_version}-s5-v1.[webp|png]

Preview Engine

A Preview Engine URL is basically a long series of query parameters, with only two mandatory configurations:

URL template: ${preview_base_url}/${poi}?gender=${gender}${...other_query_parameters}

Others

If it wasn't already complicated enough, there are even more Bitmoji-related URLs!!

A sticker uses the https://sdk.bitmoji.com/me/sticker URL, and a background uses https://sdk.bitmoji.com/3d/background.

Stickers are…a total guess for me. I'm making up these names based on clues scattered around the internet and minified code.

Key: avatar_id_with_version
Values: "avatar_id_version-s5", "avatar_version_uuid", "alternative_avatar_version_uuid"
Examples: 131457743_49, 512001e4-fedb-4894-815f-f8f24433528c, AWVscGhvWez65Dau7FgShF5VPT5C7A

Key: sticker_id
Value: "sticker_id", "sticker_uuid"
Example: 20082713, 0e7afb8b-14de-4d30-8498-a00cfe20fa4d
Notes: Not all stickers support two avatars. Also, this sticker_id seems to be the same as a Pose ID/Comic ID in other Bitmoji contexts. Most of the sticker IDs I've found from an authenticated request to https://us-east-1-bitmoji.api.snapchat.com/direct/pack/popular are found in the templates API, but many are not.

URL Patterns:

Thank God backgrounds are not that complicated. 3d/background/([0-9]+)-([0-1]).([webp|png])

  1. A background ID
  2. A background size
  3. An image type

https://images.bitmoji.com/3d/background/135680541-1.webp

Nuances

There is a lot I, and everyone else, do not know about Bitmojis. Nuances will cover…nuances. Things I can not explain, or that are inconsistent with no observable patterns, or simply do not fit anywhere else. These are largely useless to both the end user and the developer curious about the technology, but I think it's fun to explain them.

S-value

The s-value, for the most consistent results, should be set to 5. That being said, for 3D Bitmojis, it can be multiple values, including, but possibly not limited to, 1, 4, and 5. This is, of course, not the case for 2D poses, which seem to be always 5, no matter what.

3D-exclusive

It's hard to explain this one. Some user IDs seem to be exclusive to 3D poses, while others will happily work with both 2D and 3D poses. I suspect this is a coincidence, based solely on the fact that Bitmoji would have stopped updating old 2D poses, and thus if an avatar has a new piece of clothing or a new hairstyle, etc., it would fail to render a valid 2D pose. Unfortunately, this is near impossible for me to test, as my Bitmoji is currently (2025-05-09) dead.

It is also worth noting that even with the "2D templates" file accessed from the Internet Archive, some Bitmoji's 3D versions fully replaced the 2D versions, using the same comic_id and thus those 2D versions are lost. An example of this is the "Blowing up a balloon that says H B D" Bitmoji, which used the same ID (10134294) for both the 2020 2D and 2025 3D versions.

The below example's URL template, ${render_base_url}/10211914-${avatar_id}_0-s5-v1.webp?scale=2&ua=2, shows two Avatar IDs failing to render a valid 2D pose, and those same ones succeeding to render a valid 3D pose.

Two 2D Bitmojis, with 4 labeled IDs. The second two IDs are missing a corresponding 3D pose.

The 4 labeled IDs now all show a matching valid 3D pose.

Friends Bitmojis with no friends

Most Friend Bitmojis have a complimentary Chat Bitmoji with a matching single-person scenario. A semi-reliable way to find the complimentary Chat Bitmoji is to search for the Friend Bitmoji's tags and/or alt text. It is likely the single-avatar version will have the same tags, but it's not guaranteed, especially as they seem AI-generated. The IDs might also be nearby, as shown below.

Image showing two Bitmojis, one with two avatars in the 'I love you unconditionally' scenario, and the other with a slightly different but complimentary single-avatar version.

The limits of time travel

"There is a limit to this backward research, and at some point you’ll roll back to a number and suddenly the avatar will reverts to the current one." - hatless1der. This is true, but only for some Bitmojis. I have managed to go from version 86 all the way back to version 1 for some people, and from version 248 all the way back to version 14 before reverting began. It is worth noting that the avatar versions that do revert were not only the current version, but also the previous few versions too. That makes me think it's time based (time since a version?, a fixed date where they were purged?). As always, Bitmoji is inconsistent.

Customisation values

Some of the values for the Preview Engine's customisation options can be weird, but can also be helpful in the way they are chosen. All customisation options with "tone" in their name accept a colour value in the 24 bit packed format (otherwise known as decimal, or simply numbers 1 to 16 million or so), implying that the "IDs" are actually colours, and not random. No one has tested this before, but I have, and I can confirm that in addition to the documented values, all 24 bit packed colours work. HOWEVER, there are of course exceptions.

A display of all official skin tones, next to a demonstration of skin tone rounding

In addition to custom colours in URLs, this information can even be used to add a custom colour to your official and public Bitmoji, giving you customisation options previously impossible, and are likely unique to only you. I have not seen this documented anywhere at all, but it is possible, and I am testing its limits. I have done thorough testing, and it works. You can create a bitmoji with fully custom colours, and it will be properly displayed throughout snapchat. With of course, some nuances!

Custom colours in Bitmojis

As mentioned above, Preview Engine URLs have special requirements for custom colours. This causes issues when you try to edit your Bitmoji. The only place you can feasibly use custom colours is in the Bitmoji editor's website, where you have easy interception of the requests. The issue is that edits made on the Bitmoji website do not trigger an update for Snapchat, so your customisations are not reflected properly. Snapchat never knows that you have a new version of your Bitmoji, and so in the UI and when you go to customise your Bitmoji on the Snapchat app, it will use the previous version of your avatar.

To get around this, follow these steps:

  1. Set up the request interception to replace your chosen customisation target with your chosen customisation value.

    • For example, if you want to change your iris colour to pure white (normally impossible), you can set up the request interception to replace option_ids.pupil_tone in the "avatar" request with 16777215 (white, #FFFFFF).
    • This is not important enough for it's own section but what Bitmoji internally calls the "pupil" actually refers to the iris. The pupil is the dark part in the center of your eye that lets in light, and the iris is the coloured ring around it. This decision confuses me.
  2. In the Bitmoji editor, make any other customisations you want, or simply leave them be, and then click "Save". You can inspect the request in the network tab to see if your customisations were applied.

  3. Create a clean install of the Snapchat app. I do this in three main ways:

    • Uninstall the app, then reinstall it.
    • Clear the app's data.
    • And to avoid messing with my main app; Using a test Snapchat account inside Samsung's "Locked Folder" feature to essentially create a second isolated environment.
  4. Open your profile, tap in the area where your Bitmoji is displayed, then tap "Avatar". Or, yknow, just do what you normally do to edit your Bitmoji.

  5. In the Bitmoji editor, make any kind of customisation you want, but importantly you must
    a) Not overwrite any customisations you made in step 1.
    b) Choose at least one customisation option that is different to your previous avatar.

    You may see your custom colours appear in the preview, or you may not. That is not indicative of whether or not your customisations were applied.

    This will trigger an update for Snapchat that there is a new version of your Bitmoji, and by forcing it to use the customised version it will happily pass your custom colours along to their servers.

  6. Be happy knowing you have colours that possibly no one else in the world has.

Check out Long Stuff for code examples.

Resources that have helped me. I discovered a lot of this knowledge independently, only to later find others on a similar path, but I want to link to them anyway.

Long stuff

Possible customisation options

Internally each of these points to an array of group_ids. Match one to the a group which then contain an array of colour options (think of these like presets), which then contain an object with a colours object, which has keys for different piercing positions such as cartilage3, which finally contains an array of tones (24 bit packed colours) for the _tone${x} query parameters. You then use this information to generate a query parameter like &earringL_cartilage3_tone1=16763909 and the other 3 tones. The same happens for the other "group" based customisation options.

Full list of POIs

hair, eye, eyebrows, nose, jaw, head, body, mouth, earring-left, earring-right, face-lines, mannequin-left, mannequin-center, mannequin-right, nosering, browring-left, browring-right, mouthring, tonguering, body-tongue, glasses, hat, mannequin-with-head-left, mannequin-with-head-center, mannequin-with-head-right, top, bottom, one_piece, footwear, sock, and outerwear.

Custom colour code collection

This is some of the code I use to get custom colours in the Bitmoji editor.

I am using requestly to intercept and modify the requests. The main rule you need to set up is:

Type: "Modify request body"
URL equals https://us-east-1-bitmoji.api.snapchat.com/api/avatar-builder-v3/avatar
Request body - Dynamic (Javascript)

function modifyRequestBody(args) {
    // Default args
    const { method, url, body, bodyAsJson } = args;

    // Make a copy to modify
    const modifiedBody = { ...bodyAsJson };

    // Just a sanity check
    if (modifiedBody.option_ids) {
        modifiedBody.option_ids.hair_tone = 16777215; // White hair
        modifiedBody.option_ids.pupil_tone = 16777215; // White IRIS
        modifiedBody.option_ids.skin_tone = 16777215; // White skin (note: skin tone rounding applies)
        // Etc etc
    }

    // Return as a plain string
    return JSON.stringify(modifiedBody);
}

And to fix the previews for things like hair styles breaking when you have a custom hair colour:

Type: "Query param"
URL contains https://preview.bitmoji.com/bm-preview/v3/avatar/hair
ADD param brow_tone, value 1