Skip to Content
ConceptsAPI profiles and devices

API profiles and devices

Every Telegram client announces itself in initConnection: an api_id, and the device model, system version, app version and language fields of the app. An API profile is that set of values for one official client. A session that uses an official profile looks like the official app; one that mixes an official api_id with a library’s own fields does not.

FieldPython (APIData)Node (ApiProfile)Rust (api::ApiProfile)
API id and hashapi_id, api_hashapiId, apiHashapi_id, api_hash
Devicedevice_model, system_versiondeviceModel, systemVersiondevice_model, system_version
Appapp_versionappVersionapp_version
Languageslang_code, system_lang_code, lang_packlangCode, systemLangCode, langPacklang_code, system_lang_code, lang_pack

The eight clients

ClientPythonNode and the coreRustapi_idlang_pack
Telegram DesktopAPI.TelegramDesktop'TelegramDesktop'Client::Desktop2040tdesktop
Telegram AndroidAPI.TelegramAndroid'TelegramAndroid'Client::Android6android
Telegram XAPI.TelegramAndroidX'TelegramAndroidX'Client::AndroidX21724android
Telegram iOSAPI.TelegramIOS'TelegramIOS'Client::Ios10840ios
Telegram for macOSAPI.TelegramMacOS'TelegramMacOS'Client::MacOS2834macos
Telegram Web AAPI.TelegramWeb_A (alias TelegramWeb_Z)'TelegramWeb_A'Client::WebA2496empty
Telegram Web KAPI.TelegramWeb_K'TelegramWeb_K'Client::WebK2496macos
WebogramAPI.Webogram'Webogram'Client::Webogram2496empty

Each client has a default device and its app version from the current client versions. The default profile is fine for one account; for many accounts, give each its own device with Generate.

profile.py
from opentele2.api import API print(API.TelegramDesktop.api_id, API.TelegramDesktop.device_model) # the class holds the profile api = API.TelegramAndroid() # an instance to edit or pass around print(api.app_version, api.lang_pack)

Generate a device

Generate keeps the client’s API id, app version and languages and replaces the device with one picked from the client’s catalog. The pick is seeded by a unique id of your choosing, such as your own account id: the same id always gives the same device.

generate.py
from opentele2.api import API android = API.TelegramAndroid.Generate("my-id") desktop = API.TelegramDesktop.Generate("windows", "my-id") # platform first, then the unique id any_desktop = API.TelegramDesktop.Generate("my-id") # platform picked from the unique id print(desktop.device_model, desktop.system_version, desktop.app_version)

Every client class has Generate(unique_id); TelegramDesktop.Generate also takes the platform, "windows", "macos" or "linux", positionally or as system=.

All three print Vostro 3520 Windows 11 x64 7.3.1 x64.

  • Desktop picks the platform from the unique id when none is given (one of Windows, macOS and Linux), and adds the build architecture to the app version: x64 on Windows, arm64 on macOS, none on Linux.
  • macOS: Telegram Desktop on a Mac reports macOS 26.7.1, the Swift client (TelegramMacOS) the bare version; both pick the same Mac for the same id.
  • Without a unique id the device is drawn from the operating system’s random source, so each call gives another device.
Tip:

The same unique id gives the same device in Python, Node and Rust. Store the id with the account and regenerate the profile whenever you need it, instead of storing the device.

How the pick works

A catalog is a list of (model, system version) pairs, each with a weight: a heavier pair is picked more often. The pick hashes the unique id (SHA-1 of its UTF-8 text, as a big-endian number, modulo 10^12) and indexes the cumulative weights with the hash. An integer id is hashed as its decimal text, so 12345 and "12345" give the same device. The catalogs are embedded in the core and are the only copy; every binding reads them from there.

Device catalogs

PlatformPythonNode and the core
WindowsWindowsDevice'windows'
LinuxLinuxDevice'linux'
macOSmacOSDevice'macos'
Android (Telegram Android and Telegram X)AndroidDevice'android'
iOSIOSDevice (alias iOSDevice)'ios'
Web A and Web KWebBrowserDevice'web_a', 'web_k'
catalogs.py
from opentele2.devices import AndroidDevice, WindowsDevice print(WindowsDevice.device_models[:3]) print(len(WindowsDevice.deviceList)) # every pair repeated by its weight print(AndroidDevice.RandomDevice("my-id")) # the device Generate picks for this id

The catalog lists are read-only: every read is a fresh copy, so editing one changes nothing that RandomDevice picks.

Web devices

A web client’s device is a browser: the model is a desktop browser user agent, and the system version is what the client sends for the platform, the OS name (Windows) for Web A and navigator.platform (Win32) for Web K and Webogram.

The web catalog is a pool drawn with opentele2-browser: up to 60 Chrome, 30 Edge and 20 Firefox desktop user agents, weighted 19 : 2 : 1, around the current Chrome and Firefox majors. When the browser dataset trails a live major, the version window slides down by at most 24 and the drawn majors are shifted back up. The pool is a pure function of the two majors, so a unique id gives the same web device until Chrome or Firefox moves to a new major.

web.py
from opentele2.api import API web = API.TelegramWeb_A.Generate("my-id") print(web.device_model) # a Chrome, Edge or Firefox user agent

Using a profile

  • Python: pass api= to TDesktop, ToTelethon, ToTDesktop, QRLoginToNewClient or the opentele2 TelegramClient; a class (API.TelegramDesktop) or an instance (Generate(...)) both work.
  • Node: pass profile.apiId and profile.apiHash to mtcute’s TelegramClient and initConnectionOptions(profile) from opentele2/mtcute as its initConnectionOptions.
  • Rust: pass api_id to grammers and opentele2_grammers::connection_params(&profile) as its connection params. grammers 0.11 sends an empty lang_pack and no params, so the wire is not identical to Telegram Desktop’s; see initConnection fingerprints.

A profile is stored in the .json companion of a Telethon session: APIData.to_json and APIData.from_json in Python, profileToSessionJson, profileFromSessionJson and profilesEqual in Node, ApiProfile::to_session_json and ApiProfile::from_session_json in Rust.

Last updated on