❝ Nico

Notes

How do I search a Slack JSON export?

The zip is one folder per conversation and one JSON file per day, and the messages name people by ID. How to read it, and what the export leaves out.

Unzip it. The message text sits in dated JSON files inside one folder per conversation, and every message names its author by ID rather than by name, so users.json is what makes any of it readable. Then search the whole folder at once with grep, a viewer, or something that can read all of it.

The steps

  1. Unzip it somewhere you can see. unzip slack-export.zip -d slack-export. Every public channel becomes a folder named after the channel; private channels, DMs and group DMs become folders named by ID.
  2. Read the index files first. At the top level you get channels.json for public channels, groups.json for private ones, dms.json, mpims.json for group DMs, users.json (org_users.json on Enterprise), integration_logs.json, canvases.json and file_conversations.json. Slack's own guidance is that "you'll need to use both the channel and reference files to find a specific conversation".
  3. Build the ID-to-name map. A message carries "user": "U024BE7LH", never a display name: jq -r '.[] | [.id, .name] | @tsv' users.json > names.tsv.
  4. Search across every day file at once. grep -ril "mobile build" slack-export/ gives you the channel and the date, because the date is the filename.
  5. Read the hit. jq -r '.[] | [.ts, .user, .text] | @tsv' slack-export/eng-planning/2026-08-14.json. Each message has type, user, text and ts, the Unix timestamp it was posted at.

Four things that will trip you up

  • The files are not in the file export. Slack is explicit: "Slack export files in JSON format do not contain any files from the workspace." What you get instead is URLs pointing back at Slack. A zip you are keeping because the workspace might go away does not contain the attachments.
  • ts is a string, not a date. It is a Unix timestamp with a fractional part, and it doubles as the message's ID. Thread replies point at their parent with thread_ts, so a reply read on its own has no visible context.
  • A folder name is not always a channel name. DMs and private channels are named by ID. dms.json and groups.json are how you find out which is which.
  • Renamed channels. The folder carries the name at export time, not the name people used in the message you are looking for.

When grep is not enough

grep finds a word. It does not answer "when did we decide to drop the mobile build, and why" — that answer is spread over three threads in two channels, and the word "decide" may appear in none of them.

For that you need something that can read the whole folder and keep its own notes. Two honest options: a local model via Ollama pointed at the directory, or a coding CLI working in the folder. Both keep the archive where it is. Uploading a four-year workspace export to a web viewer to find one sentence is the trade people make without thinking about it, and it is the one trade this file does not require.

Nico's showcased version of this job is exactly that: four years of history, answerable, without granting anyone a workspace token and without the zip leaving the Mac.

← All notes