CommunicationOS
Slack11 August 202610 min readAdam Albastov

How to Export Slack Messages on a Free Plan Before the 90 Day Window Closes

Run the Slack standard export, read the JSON, and understand what a free plan will never give you. The 90 day visibility rule, missing direct messages, file links that expire, and the API route around it.

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 containing public channel messages as JSON. On the free plan, that export only includes messages sent in the last 90 days. Direct messages, group direct messages, and private channels are omitted from standard exports on every plan level. If you need text older than 90 days, you have to upgrade the workspace before exporting.

What changed, and what actually happened to the old messages

Slack changed its free tier rules on 1 September 2022, announced a fortnight ahead of the switch. Before that date, workspaces on the free tier retained access to their 10,000 most recent messages, regardless of how long ago team members posted them. Under the current rules, Slack hides all messages, images, and attachments older than 90 days.

The system does not delete those older items from Slack servers. The database marks them as hidden to free accounts. If you enter a credit card and move to the Pro plan, the hidden history becomes visible in the search bar and channel timelines within minutes.

This behaviour dictates how the export tool behaves. The standard export function queries the same data visibility layer that powers the desktop app. When an owner runs an export on a free plan, the generation script pulls only from the active 90-day pool. A workspace created three years ago will generate a file containing only the last three months of conversations. If your team plans to leave Slack for another platform, upgrading for one billing cycle is the only official method to make those older records appear in a workspace export. You can review current tier costs on our pricing guide or compare how other networks handle retention in our guide on how to export Discord server history.

Run the standard export

Only team members with the Workspace Owner or Primary Workspace Owner role can request a data export. Workspace Admins and regular members do not see the export options in the menu.

  1. Open Slack on your desktop or in a web browser.
  2. Click your workspace name in the top left corner of the screen.
  3. Select Tools and settings from the drop-down menu, then click Workspace settings.
  4. Look at the left sidebar of the admin console and click Import/Export Data.
  5. Click the Export tab at the top of the page.
  6. Select your export date range. The drop-down gives options for the last 7 days, the last 30 days, the entire visible history, or a custom range. On a free plan, choosing entire history still caps the data at the 90-day mark.
  7. Click the Start Export button.

Slack processes the export in the background. Small workspaces finish in two minutes. Workspaces with thousands of channel posts might take an hour. When the server completes the file, Slack sends an email to the owner who triggered the job and posts a link in the slackbot direct message thread.

The download link leads to a .zip archive hosted on slack-files.com. That link does not stay live indefinitely. Download the archive to a local disk the day the email lands.

What is in the ZIP

When you unpack the archive, the root directory shows several standalone JSON files alongside a set of folders:

  • users.json lists every user account ever created in the workspace, with their user ID, display name, real name, email address, timezone, and avatar URLs.
  • channels.json lists every public channel, including channel IDs, topic strings, purpose strings, creation timestamps, and member ID arrays.
  • integration_logs.json lists when bots and integrations joined or left the workspace, and which member added them.
  • A separate folder for every public channel that existed during the 90-day window, matching the channel name.

Inside each channel folder, Slack splits the conversation into individual JSON files named after the date of the posts, using the format YYYY-MM-DD.json. A channel with activity on 40 distinct days across the last quarter will contain 40 separate JSON files.

The JSON records raw message objects. Here is the structure of a standard message object inside one of those files:

{
  "client_msg_id": "7b8f9e1a-3d2c-4b5a-9f8e-1a2b3c4d5e6f",
  "type": "message",
  "text": "The client approved the revised statement of work.",
  "user": "U0123456789",
  "ts": "1698765432.000200",
  "blocks": [
    {
      "type": "rich_text",
      "block_id": "ab12",
      "elements": [
        {
          "type": "rich_text_section",
          "elements": [
            {
              "type": "text",
              "text": "The client approved the revised statement of work."
            }
          ]
        }
      ]
    }
  ],
  "team": "T0123456789",
  "reactions": [
    {
      "name": "white_check_mark",
      "users": ["U0987654321"],
      "count": 1
    }
  ]
}

If a team member uploaded an image or a PDF into the conversation, the JSON object does not embed the binary file data. It stores a metadata dictionary under a files array. That dictionary includes fields such as name, mimetype, filetype, and a remote download link labelled url_private_download.

This architecture creates a major problem for long-term storage. The export archive gives you text transcripts, but your attachments stay hosted on Slack servers. To download those physical files, you must run a secondary script that loops through every message object, reads the url_private_download value, and passes an active Slack API bearer token in the HTTP authorization header. If you cancel your account or if Slack purges the workspace, those URLs return 404 Not Found errors.

What a standard export will never contain

The standard export has strict structural limits that apply to all free accounts. Slack reserves complete data extraction for regulated corporate customers.

Content type Standard export inclusion Notes
Public channel messages Included Limited to the rolling 90-day window on free accounts.
Public channel files Linked only URLs are present in JSON; raw files must be pulled via API.
Private channel messages Excluded Requires Business+ plan or corporate compliance approval.
Direct messages (1-on-1) Excluded Requires Business+ tier and formal approval from Slack.
Group direct messages Excluded Omitted 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 separate export routines inside the canvas UI.
Message edit history Excluded Shows only the final edited text, without revision logs.
Deleted messages Excluded Deleted records are scrubbed from standard export output.
Slack Connect channels Partial What you get 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 surprises most operations managers. In many organisations, the messages that matter most happen inside direct messages between department heads or inside private management channels.

Slack locks complete exports behind their Business+ and Enterprise Grid tiers. To export private data through the admin console, a company must submit an application to Slack compliance teams. The application requires proof of legal necessity, employment contracts granting the company ownership of internal communications, or local regulatory mandates such as FINRA or GDPR discovery requirements. Slack reviews these requests manually.

If you operate on a free plan, you cannot submit that application. You have two practical alternatives for preserving private conversations before they slip past the 90-day threshold:

First, ask individual staff members to copy their critical records manually. Slack does not provide an "Export this chat" button inside individual direct message views. Users must copy message text into local documents or take screenshots of important agreements.

Second, a technical team member can generate a user-scoped API token to pull the conversations that the user account has permission to read. This method requires setting up an internal Slack app, which gives you programmatic access to the user's specific inbox without requiring an Enterprise Grid subscription. Read our deep dive on Slack channel connections to see how individual authentication tokens interact with workspace permissions.

Doing it with the API instead

You can bypass the standard export interface by building a basic script against the Slack Web API. This approach lets you fetch private channels and direct messages that your account belongs to, subject to standard API rate limits.

To use the API, create an application at api.slack.com and install it to your workspace. Grant your app the following user token scopes:

  • channels:history and channels:read for public channels.
  • groups:history and groups:read for private channels you belong to.
  • im:history and im:read for direct messages.
  • mpim:history and mpim:read for multi-person direct messages.
  • files:read to access attachment download paths.

The API exposes the conversations.list endpoint to discover channel IDs and the conversations.history endpoint to read the messages. Slack paginates conversations.history with a cursor. The default page size is 100 messages, with a maximum of 999 messages per call.

Slack sorts endpoints into rate limit tiers. conversations.history has historically sat on Tier 3, which allows roughly fifty requests a minute. That changed for newer apps. Slack now applies a much tighter limit to non-Marketplace apps created after 31 May 2025, dropping conversations.history and conversations.replies to one request a minute returning a handful of objects at a time. Work out which of the two your app falls under before you plan the run. The gap between them is the gap between an afternoon and a fortnight.

When you go too fast, Slack answers with an HTTP 429 and a Retry-After header holding the wait in seconds.

Here is a working Python script that reads messages from a single conversation ID using 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}.")

This script extracts raw JSON payloads for any conversation your user account can see. You can then write a routine to download each file found in the files blocks before the links expire. Check out our technical overview of bulk export formats for strategies on handling nested JSON structures and file schemas.

A checklist before the window closes

If your company relies on a free Slack workspace, data drops out of sight daily. Follow these steps to secure your records:

  1. Identify critical public channels containing technical decisions, customer agreements, vendor invoices, or architecture discussions.
  2. Run the standard export from the admin console today. Because the 90-day window advances continuously, an export generated this afternoon saves conversations that will vanish by next week.
  3. Write a small script to parse the output JSON and download all linked files from slack-files.com using a valid user token.
  4. Notify your team that direct messages are absent from the export. Have team members manually archive project deliverables, client contracts, and essential notes from their 1-on-1 conversations.
  5. Review the size of your archive. If your historical data is crucial for operations or tax compliance, calculate the cost of paying for one month of Slack Pro. Upgrading unlocks the full history, allows a complete standard export of everything since workspace inception, and gives you time to move elsewhere.
  6. Complete all downloads and verify the integrity of your JSON files before you close the workspace or delete any accounts.

Migrating to something else

If you are moving away from Slack, the standard JSON archive serves as the baseline import format for competing platforms. Slack itself can import that ZIP into another Slack workspace. Mattermost and Rocket.Chat both read it too, with their own converters.

The 90-day cutoff travels inside the file. If you import a free-plan export archive into a self-hosted Mattermost server, that new server will only display the 90 days of conversations present in the JSON files. The migration tool cannot reconstruct the hidden months left behind on Slack servers. To populate a new platform with your complete historical context, you must upgrade Slack to Pro, run a full archive export, verify the JSON files, and then feed that complete file into your new communications stack.

Keeping the archive instead of exporting it

Manual exports, script maintenance, and expired download links turn simple business archiving into ongoing maintenance work. Managing raw JSON folders and building custom downloaders for every chat network consumes engineering hours that belong on your core product.

CommunicationOS indexes your communication history automatically. Connect your workspace alongside your other operational channels, and every message, file attachment, and public thread stays searchable indefinitely without running manual exports before a visibility timer runs out.

Stop exporting, start keeping it

Connect an account and the history behind it gets indexed too. Voice notes transcribed, media read, every message searchable for as long as the account is connected.

No card required. Production deployment takes under an hour.

Talk to us