How to Export Slack Messages on a Free Plan Before the 90 Day Window Closes
Run the Slack standard export, read the JSON, and see what a free plan leaves out. The 90 day visibility rule, missing direct messages, file links that expire, and the API route around them.
The short answer
A workspace owner or primary owner can run a standard export from the Slack admin settings. The export produces a ZIP file holding public channel messages as JSON. On the free plan, that export carries only the messages sent in the last 90 days. Direct messages, group direct messages, and private channels are missing from a standard export on every plan level. Text older than 90 days needs the workspace upgraded before the export runs.
What changed, and where the older messages went
Slack changed its free tier rules on 1 September 2022. Before that date, a free workspace kept access to its 10,000 most recent messages however old they were. Under the current rules, Slack hides messages, images, and attachments older than 90 days.
Slack does not delete the hidden items from its servers. The database marks them as out of reach for a free account. Moving the workspace to the Pro plan makes that history visible again in search and in the channel timelines.
That behaviour drives what the export tool does. The standard export queries the same visibility layer that powers the desktop app, so an export run on a free plan reads only from the active 90-day pool. A workspace created three years ago produces a file holding the last three months. For a team leaving Slack for another platform, upgrading for one billing cycle is the only official way to make the older records appear in a workspace export. Current tier costs sit on our pricing guide, and the guide on how to export Discord server history covers the equivalent retention rules there.
Run the standard export
Only a Workspace Owner or a Primary Workspace Owner can request a data export. Workspace Admins and members do not see the export options in the menu.
- Open Slack on your desktop or in a web browser.
- Click your workspace name in the top left corner of the screen.
- Select Tools and settings from the drop-down menu, then click Workspace settings.
- In the left sidebar of the admin console, click Import/Export Data.
- Click the Export tab at the top of the page.
- Select the export date range. The drop-down offers the last 7 days, the last 30 days, the entire visible history, or a custom range. On a free plan, entire history still stops at the 90-day mark.
- Click the Start Export button.
Slack processes the export in the background, and the wait scales with how many channel posts the workspace holds. When the file is ready, Slack emails the owner who triggered the job and posts a link in the Slackbot direct message thread.
The download link points at a .zip archive hosted on slack-files.com. That link does not stay live indefinitely. Download the archive to a local disk on the day the email lands.
What is in the ZIP
Unpacking the archive shows several standalone JSON files in the root directory alongside a set of folders:
users.jsonlists every user account ever created in the workspace, with user ID, display name, real name, email address, timezone, and avatar URLs.channels.jsonlists every public channel, with channel IDs, topic strings, purpose strings, creation timestamps, and member ID arrays.integration_logs.jsonrecords when bots and integrations joined or left the workspace, and which member added them.- One folder per public channel that existed during the 90-day window, named after the channel.
Inside each channel folder, Slack splits the conversation into JSON files named for the date of the posts, in the format YYYY-MM-DD.json. A channel with activity on 40 distinct days across the quarter holds 40 files.
The JSON holds raw message objects. A standard message object inside one of those files looks like this:
{
"client_msg_id": "7b8f9e1a-3d2c-4b5a-9f8e-1a2b3c4d5e6f",
"type": "message",
"text": "Legal needs another week, so the Northstar launch moves to 12 June.",
"user": "U0123456789",
"ts": "1747206361.000200",
"blocks": [
{
"type": "rich_text",
"block_id": "ab12",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "Legal needs another week, so the Northstar launch moves to 12 June."
}
]
}
]
}
],
"team": "T0123456789",
"reactions": [
{
"name": "white_check_mark",
"users": ["U0987654321"],
"count": 1
}
]
}
An uploaded image or PDF does not put binary data in the JSON object. Slack stores a metadata dictionary under a files array, holding fields such as name, mimetype, filetype, and a remote download link labelled url_private_download.
That design creates a problem for long-term storage. The archive gives you text transcripts while the attachments stay hosted on Slack's servers. Downloading the files means a second script that walks every message object, reads url_private_download, and passes an active Slack API bearer token in the HTTP authorization header. Once you cancel the account, or once Slack purges the workspace, those URLs answer 404 Not Found.
What a standard export does not contain
The standard export has structural limits that apply to every free account, because Slack puts complete data extraction on its higher tiers.
| Content type | In a standard export | Notes |
|---|---|---|
| Public channel messages | Included | Limited to the rolling 90-day window on free accounts. |
| Public channel files | Linked only | URLs are in the JSON; the files themselves come from the API. |
| Private channel messages | Excluded | Needs the Business+ plan or corporate compliance approval. |
| Direct messages (1-on-1) | Excluded | Needs the Business+ tier and formal approval from Slack. |
| Group direct messages | Excluded | Missing from standard archives on every plan level. |
| Huddle audio and transcripts | Excluded | Audio is unrecorded; notes go to canvases or threads. |
| Canvas documents | Excluded | Canvases have their own export routine inside the canvas interface. |
| Message edit history | Excluded | Shows the final edited text, with no revision log. |
| Deleted messages | Excluded | Deleted records are absent from standard export output. |
| Slack Connect channels | Partial | What arrives from the other company's side depends on their workspace settings. Treat these channels as incomplete. |
Direct messages and private channels
The exclusion of direct messages and private channels catches operations managers out, because in many organisations the messages that matter happen in direct messages between department heads or in private management channels.
Slack puts complete exports behind the Business+ and Enterprise Grid tiers. Exporting private data through the admin console also needs an application to Slack's compliance team, supported by proof of legal necessity, employment contracts granting the company ownership of internal communications, or a regulatory mandate such as FINRA or a GDPR discovery request. Slack reviews those requests by hand.
A free plan cannot submit that application. Two practical routes remain for preserving private conversations before they pass the 90-day threshold.
The first is manual. Slack has no "Export this chat" button inside a direct message view, so staff copy the message text into local documents or take screenshots of the agreements that matter.
The second is a user-scoped API token, generated by a technical team member, which pulls the conversations that user account can read. This needs an internal Slack app and gives programmatic access to that user's own inbox without an Enterprise Grid subscription. The Slack channel connection page covers how individual tokens interact with workspace permissions.
Doing it with the API instead
A script against the Slack Web API skips the standard export interface and reaches the private channels and direct messages your own account belongs to, within the API rate limits.
Create an application at api.slack.com and install it to your workspace. Grant the app these user token scopes:
channels:historyandchannels:readfor public channels.groups:historyandgroups:readfor private channels you belong to.im:historyandim:readfor direct messages.mpim:historyandmpim:readfor multi-person direct messages.files:readfor attachment download paths.
The conversations.list endpoint discovers channel IDs and conversations.history reads the messages. Slack paginates conversations.history with a cursor, at a default page size of 100 messages and a maximum of 999 per call.
Slack sorts endpoints into rate limit tiers. conversations.history has historically sat on Tier 3, which allows around 50 requests a minute. Newer apps face a much tighter limit: for non-Marketplace apps created after 31 May 2025, Slack caps conversations.history and conversations.replies at 1 request a minute and 15 objects per request. Work out which of the two your app falls under before you plan the run, because the two produce very different schedules.
Going too fast returns HTTP 429 with a Retry-After header holding the wait in seconds.
Here is a working Python script that reads messages from one conversation ID with pagination:
import os
import time
import requests
SLACK_TOKEN = os.environ.get("SLACK_USER_TOKEN")
CONVERSATION_ID = "C0123456789"
headers = {
"Authorization": f"Bearer {SLACK_TOKEN}",
"Content-Type": "application/json"
}
messages = []
cursor = None
while True:
params = {
"channel": CONVERSATION_ID,
"limit": 200
}
if cursor:
params["cursor"] = cursor
response = requests.get(
"https://slack.com/api/conversations.history",
headers=headers,
params=params
)
data = response.json()
if not data.get("ok"):
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
time.sleep(retry_after)
continue
raise Exception(f"Slack API error: {data.get('error')}")
messages.extend(data.get("messages", []))
cursor = data.get("response_metadata", {}).get("next_cursor")
if not cursor:
break
time.sleep(1.2)
print(f"Retrieved {len(messages)} messages from {CONVERSATION_ID}.")
The script pulls raw JSON payloads for any conversation your user account can see. Add a routine that downloads each file found in the files blocks before the links expire. Our overview of bulk export formats covers handling nested JSON structures and file schemas.
A checklist before the window closes
On a free Slack workspace, data leaves view every day. These steps secure the records:
- Identify the public channels holding technical decisions, customer agreements, vendor invoices, or architecture discussions.
- Run the standard export from the admin console today. The 90-day window advances continuously, so an export run this afternoon holds conversations that will be out of view next week.
- Write a script that parses the output JSON and downloads the linked files from
slack-files.comwith a valid user token. - Tell your team that direct messages are absent from the export, and have them archive project deliverables, client contracts, and their own 1-on-1 notes by hand.
- Review the size of the archive. When the historical data matters for operations or tax compliance, price one month of Slack Pro against it. An upgrade opens the full history, allows a complete standard export back to workspace creation, and buys you time to move.
- Finish the downloads and check the JSON files before you close the workspace or delete any accounts.
Migrating to something else
For a team leaving Slack, the standard JSON archive is the baseline import format. Slack imports the ZIP into another Slack workspace. Mattermost and Rocket.Chat both read it with their own converters.
The 90-day cutoff travels inside the file. Importing a free-plan export into a self-hosted Mattermost server gives that server the 90 days present in the JSON, and the migration tool cannot rebuild the months left behind on Slack's servers. Populating a new platform with the full history means upgrading Slack to Pro, running a full archive export, checking the JSON files, and then feeding that complete file into the new stack.
Keeping the archive instead of exporting it
Manual exports, script maintenance, and expired download links turn archiving into recurring work, and building a downloader for each chat network is work that belongs on your own product.
CommunicationOS indexes conversation history as it arrives. Connect the workspace alongside your other channels, and messages, file attachments, and public threads stay searchable while the account is connected, with no visibility timer to beat. The standard export loads too. Upload the workspace .zip and the 90 days inside it join the same index, which is how a free-plan history crosses over without upgrading the workspace first. What the standard export excludes stays excluded, so the direct messages and private channels in the table above need the connection rather than the file.
Related guides
Trengo alternatives in 2026: what the six real options cost
Trengo charges EUR 299 a month on annual billing for 10 users and 6,000 conversations a year. Here is the arithmetic on Trengo, Front, Intercom, Zendesk, Respond.io and CommunicationOS for a 15-person team over 12 months.
How to Export WhatsApp Chat History in 2026, and What the Export Leaves Out
The ways to get WhatsApp messages out of the app: the per-chat export, the message cap that truncates long threads without warning, why Google Drive and iCloud backups are not exports, and why the Business API has no history to give you.
What a shared inbox costs when conversations are metered
Conversation metering ties your invoice to customer behaviour. How the 24-hour and 7-day windows work, three worked cost scenarios on Trengo's published rates, and a ten-point audit of any vendor pricing page.