Takeout API

Telegram's API allows users to export all of their information through the takeout API.

account.takeout#4dba4501 id:long = account.Takeout;

inputTakeoutFileLocation#29be5899 = InputFileLocation;

---functions---

account.initTakeoutSession#8ef3eab0 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?long = account.Takeout;

invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;

account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;

Use account.initTakeoutSession to initialize a takeout session: pass the appropriate flags to enable usage of the corresponding methods, as described below.

When invoking the methods described below, each query must be wrapped using invokeWithTakeout, with the id returned by account.initTakeoutSession.

After finishing the export, terminate the session using account.finishTakeoutSession (wrapping it in an invokeWithTakeout as usual).

Session flags

The flags passed to account.initTakeoutSession must enable exactly the data the user chose to export:

  • contacts => the user wants to export their contact list.
  • message_users => the user wants to export messages from private chats with users and/or bots.
  • message_chats => the user wants to export messages from basic groups (chats).
  • message_megagroups => the user wants to export messages from supergroups; also set this flag together with message_chats when exporting basic group messages, to fetch messages of supergroups migrated from a basic group.
  • message_channels => the user wants to export messages from channels.
  • files => the user wants to download media: set it if any media type is to be downloaded with a non-zero per-file size limit, or if profile pictures or stories are to be exported.
  • file_max_size => the maximum size in bytes of files to download, always populated when files is populated.

Before starting, invoke users.getUsers with inputUserSelf to fetch the current user's ID: it is used to tell apart incoming and outgoing messages, and to export only the user's own messages when requested.

To complete an export, invoke account.finishTakeoutSession with the success flag set.
When aborting an export, invoke account.finishTakeoutSession without setting the success flag.

Procedure

Example implementation: tdesktop.

Here's an overview of the steps required to export account information.

All requests must be wrapped in an invokeWithTakeout constructor, including upload.getFile calls to save files.

Chats, groups and channels

Split ranges

messageRange#0ae30253 min_id:int max_id:int = MessageRange;

---functions---

messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;

invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;

Only chat, group and channel exports » require pagination using message ranges.

First of all, obtain a list of message ranges by invoking messages.getSplitRanges (wrapping it in an invokeWithTakeout as usual).

Then, when invoking messages.getDialogs, messages.getHistory and messages.getMessages as specified below, wrap the method using invokeWithMessagesRange, before also wrapping it in an invokeWithTakeout as usual.

Start by passing the first message range; continue passing the same message range while paginating using the usual offset_*, limit, etc parameters »; once there are no more results left, switch to the next message range, re-starting offset_*, limit pagination from the beginning.
Repeat until you've finished all the message ranges that were returned by messages.getSplitRanges.

If the selected chat types are limited to public supergroups and channels, i.e. they do not include private chats with users, bot chats or basic groups, it is enough to use only the last returned message range for dialog and message pagination.

Dialog and message export

---functions---

messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;

messages.getHistory#4423e6c5 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;

messages.search#29ee847a flags:# peer:InputPeer q:string from_id:flags.0?InputPeer saved_peer_id:flags.2?InputPeer saved_reaction:flags.3?Vector<Reaction> top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;

If the user wants to download messages from chats, groups or channels, first fetch and save the dialog list using split ranges and messages.getDialogs; at the beginning of each used split range, make an initial request with all offsets set to 0 and limit=1 to fetch total dialog count to display a proper progress bar.

Request and save info about left channels and supergroups if the user wants to export channel and group messages.

For every dialog returned by messages.getDialogs, remember the message range used for the request that returned it; if the same dialog is returned in multiple ranges, keep all of those ranges.
For all dialogs matching the dialog types chosen by the user, download all messages using messages.getHistory, wrapped in each remembered split range; for each such range, make an initial request with all offsets set to 0 and limit=1 to fetch the initial message count to display a proper progress bar.
Use the single message returned by the initial limit=1 request to also skip ranges that don't have any messages, because an empty messages vector or a single messageEmpty is returned.
If the user also chose to download only messages within a specific date interval, skip ranges whose:

  • Last message (returned by that same initial limit=1 call) has a date bigger than the top of the specified date interval.
  • First message (returned by a new call to messages.getHistory with limit=1, offset_id=1, add_offset=-1) has a date smaller than the bottom of the specified date interval.

For each downloaded and saved message, also download and save all attached media, including custom emojis present in messages and captions, respecting the per-file download file size limits imposed by the user.
Messages belonging to forum topics may be fetched separately, topic by topic.

If the user chose to download only their own messages, not messages sent by other users, use messages.search instead of messages.getHistory: set peer to the chat being exported and from_id to inputPeerSelf (for monoforums », set from_id to the linked broadcast channel » instead).
If a messages.getHistory call fails with a CHANNEL_PRIVATE error (for example because the user left or was removed from the channel), switch to the from_id messages.search variant to fetch the user's own messages.

Stories

---functions---

stories.getStoriesArchive#b4352016 peer:InputPeer offset_id:int limit:int = stories.Stories;

If the user wants to export their stories », use stories.getStoriesArchive with inputPeerSelf to fetch, download and store all posted stories.
Paginate by passing the id of the last fetched story as the offset_id of the next request, with limit=100, until an empty page is returned; the count field indicates the total number of stories, for displaying a progress bar.

Personal info

---functions---

users.getFullUser#b60f5918 id:InputUser = users.UserFull;

If the user wants to export personal info, invoke and store the result of users.getFullUser with inputUserSelf.

Profile pictures

---functions---

photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;

If the user wants to export their own profile pictures, invoke photos.getUserPhotos with inputUserSelf to fetch the list of and download all profile pictures.
Paginate by passing the id of the last fetched photo as the max_id of the next request, with offset=0 and limit=100, until an empty page is returned; the count field of photos.photosSlice indicates the total number of photos.

Sessions

---functions---

account.getAuthorizations#e320c158 = account.Authorizations;

account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;

If the user wants to export their sessions, invoke and store the result of account.getAuthorizations and account.getWebAuthorizations.

See here » for more info on how to download all media and files referenced by the exported data.

Contacts

savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:int = SavedContact;

---functions---

contacts.getSaved#82f1e39f = Vector<SavedContact>;

Use contacts.getSaved to export the full contact list, see here » for another alternative method that may be used to fetch the full list of all contacts with a Telegram account, without using a takeout session.

Optionally, the phone number of each saved contact may be resolved to a user ID using contacts.resolvePhone.

Then, also export the user's top peers by invoking contacts.getTopPeers with the correspondents, bots_inline and phone_calls flags set, paginating with offset and limit=100 (hash=0) until, for every returned topPeerCategoryPeers category, offset + len(peers) >= count (i.e. all categories are fully loaded).
A contacts.topPeersDisabled result indicates there is nothing more to fetch.

Left channels

messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
messages.chatsSlice#9cd81144 count:int chats:Vector<Chat> = messages.Chats;

---functions---

channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;

Use channels.getLeftChannels to get a list of channels or supergroups we left, paginating with offset (incremented by the number of returned chats) until messages.chats is returned or until an empty messages.chatsSlice page is returned; the count field of messages.chatsSlice indicates the total number of left channels.

When exporting group messages, a supergroup that was migrated » from a basic group also carries the history of the original basic group: a supergroup channel with a migrated_from_chat_id should have the messages of that basic group appended to it, and the corresponding standalone basic group should be skipped to avoid duplicating the history.

Profile music

document#8fd4c4d8 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:long thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;

users.savedMusic#34a2f297 count:int documents:Vector<Document> = users.SavedMusic;
users.savedMusicNotModified#e3878aa4 count:int = users.SavedMusic;

---functions---

users.getSavedMusic#788d7fe3 id:InputUser offset:int limit:int hash:long = users.SavedMusic;

Use users.getSavedMusic with inputUserSelf to fetch the list of music documents the user pinned to their profile; export only the returned audio documents and their thumbnails.
Paginate using offset, limit=100 and hash=0.
Stop when fewer than limit documents are returned.

Forum topics

---functions---

channels.getMessages#ad8c9a23 channel:InputChannel id:Vector<InputMessage> = messages.Messages;

messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;

Messages of a forum topic » can be exported separately from the rest of the supergroup history.

First, fetch the topic's root message by invoking channels.getMessages passing an inputMessageID with the topic's root message ID.

Then, fetch all replies in the topic using messages.getReplies, passing the topic's root message ID as msg_id; make an initial request with offset_id=0, add_offset=0, limit=100 to obtain the total count, then paginate by setting offset_id to the ID of the last fetched message plus one, with add_offset=-100 and limit=100, until an empty page is returned or the total count of messages is reached.

Download and save the media and custom emojis of each topic message as for normal messages.

Custom emojis

---functions---

messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector<long> = Vector<Document>;

Messages, captions and reactions » may contain custom emojis », referenced only by their document ID through messageEntityCustomEmoji entities and reactionCustomEmoji reactions.

Collect all referenced custom emoji document IDs and resolve them to documents by invoking messages.getCustomEmojiDocuments (at most 100 IDs per request), then download and store each resolved document as a file alongside the message.
Document IDs that cannot be resolved (not returned by the method, or for which the request fails) should be treated as unavailable.

Downloading files

---functions---

upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;

All media — message attachments, profile pictures, stories, profile music, custom emojis and thumbnails — is downloaded using upload.getFile, wrapped in invokeWithTakeout like every other request and sent to the appropriate media DC.

Download files, respecting the per-file size limit chosen by the user, skipping files bigger than the limit.

Handle the following errors:

  • TAKEOUT_FILE_EMPTY => returned for the inputTakeoutFileLocation JSON dump (see below) when there is no additional data to export: treat the file as empty.

  • LOCATION_INVALID, VERSION_INVALID, LOCATION_NOT_AVAILABLE => the file is no longer available, skip it.

  • FILE_REFERENCE_* (a 400 error whose type starts with FILE_REFERENCE_) => the file reference » expired and must be refreshed by re-fetching the source object, then the upload.getFile call must be retried from the same offset with the new reference:

    If the refreshed object does not yield a new file reference, the file should be treated as unavailable.

Other data

inputTakeoutFileLocation#29be5899 = InputFileLocation;

Any personal data related to features that do not have a dedicated takeout method yet (for example, data related to newly introduced Telegram features) can be exported by invoking upload.getFile with an inputTakeoutFileLocation: this downloads a single JSON file containing all such data, or fails with TAKEOUT_FILE_EMPTY if there is nothing to export.