Slack search.messages API: Token, Paging and Limits, Tested
search.messages works only with a user token that has search:read. We ran it against our own test workspace: the response shape, the bot token error, 10 search modifiers with their counts, paging limits that fail silently, how long new messages take to show up, and the newer assistant.search.context.
On this page
To search Slack messages from code, call search.messages with a user token (xoxp-) that has the search:read scope, and pass the same query you would type in Slack's search bar, modifiers included. A bot token does not work: it returns not_allowed_token_type. The results are the messages that user can see, with a total, a page of matches and paging data. We ran every call below against our own test workspace on 1 October 2026.
Call search.messages with a user token
Add search:read under User Token Scopes (not Bot Token Scopes) in your app's OAuth & Permissions, reinstall, and copy the User OAuth Token:
curl -s https://slack.com/api/search.messages \
-H "Authorization: Bearer $SLACK_USER_TOKEN" \
-d query=hello -d count=2
The top of our response, with the fields of the first match:
{"ok": true, "query": "hello", "messages": {"total": 11,
"pagination": {"total_count": 11, "page": 1, "per_page": 2, "page_count": 6, "first": 1, "last": 2},
"paging": {"count": 2, "total": 11, "page": 1, "pages": 6},
"matches": [{"type": "message", "user": "U0B7L4YK420", "username": "sieun", "ts": "1790817584.025669", "text": "hello 2",
"permalink": "https://slack-0yr1948.slack.com/archives/C0C5UGH9QPL/p1790817584025669", "score": 310.72}]}}
Each match also carries a channel object (id, name, is_private, is_im) and the message blocks. Slack's own search bar found the same 11 messages for the same query:
The same call with the bot token:
{"ok": false, "error": "not_allowed_token_type"}
There is no bot scope that fixes this. If your app has only a bot token, read the channels it is in with conversations.history instead; our tested script is in get messages from a channel. A user token without search:read gets missing_scope with that scope in needed, as in missing_scope.
Search modifiers in the query
query takes the same modifiers as the search bar. Our test channel held 11 messages containing "hello" and 11 messages from the bot. Another channel, #social, had none. The counts we got back:
| query | total |
|---|---|
hello | 11 |
hello in:#w1-api-test-1001 | 11 |
hello in:#social | 0 |
hello from:@me | 11 |
from:@w1test (the bot) | 11 |
hello 8 | 1 |
"reply from process" | 8 |
hel* | 11 |
hello on:2026-10-01 | 11 |
hello before:2026-10-01 | 0 |
before: leaves out the date you give, so a message sent on 1 October does not match before:2026-10-01. from:@me resolves to the owner of the token. Two words without quotes must both appear, which is why hello 8 matched one message. The full list of modifiers, as people use them in the search bar, is in Slack search operators.
Never appear "away" on Slack again
Cloud-based. No downloads. Works 24/7 even when your laptop is off.
Paging, sorting and limits
These were the surprises. None of them returned an error:
| Parameter | What came back |
|---|---|
count=100 | 100 per page, the most allowed |
count=101 | 20 per page, with "warnings": ["max_count_limit"] in paging |
page=7 when there were 6 pages | ok: true, no matches, "warnings": ["max_total_pages"] |
page=101 | Page 1, with "warnings": ["max_page_limit"] |
highlight=true | Matched words wrapped in \ue000 and \ue001 |
A loop that asks for count=500 gets 20 per page and never sees an error, so check paging.warnings. A loop that runs past the last page gets an empty matches list, which is a safe stop condition.
The default sort=score is not a stable order when scores tie. All our "hello" matches scored 310.72. One call returned "hello 2" and "hello 5" first, a later identical query returned "hello 1", "hello 7" and "hello 3". For a stable order, pass sort=timestamp with sort_dir=asc or desc; with asc we got "hello", "hello 1", "hello 2".
New messages are not searchable at once. We posted three messages with a random word and polled search.messages once a second. They showed up after 15.4, 30.6 and 30.4 seconds. A bot that posts a message and searches for it right away will not find it.
assistant.search.context, the Real-time Search API
Slack's newer search method is assistant.search.context. Our user token with only search:read got:
{"ok": false, "error": "missing_scope", "needed": "search:read.public", "provided": "identify,channels:history,search:read,users:read,chat:write"}
After we added search:read.public to both scope lists and reinstalled, the user token returned the same 11 messages in a flatter shape:
{"author_name": "sieun", "author_user_id": "U0B7L4YK420", "team_id": "T0B7JBCDKC1", "channel_id": "C0C5UGH9QPL", "channel_name": "w1-api-test-1001", "message_ts": "1790817584.025669", "content": "hello 2", "is_author_bot": true, "permalink": "https://slack-0yr1948.slack.com/archives/C0C5UGH9QPL/p1790817584025669"}
results also has files, channels and users lists, and paging is a next_cursor. The bot token, with the same scope, returned {"ok": false, "error": "invalid_action_token"}. A bot can call this method only with an action_token taken from an event a user started. is_author_bot was true for messages we had posted with the user token through the app, so do not read it as "a bot user wrote this". A new message took 51 seconds to appear here in our one try.
FAQ
Is there a search API for files?
Yes, search.files and search.all take the same user token and search:read scope. We tested only search.messages.
Stop Jiggling Your Mouse.
Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.
Related Articles
Slack Bot Scopes: The Scope Each API Method Needs, Measured on 44 Methods
We gave a Slack bot one scope, called 44 common Web API methods, and read the needed field of every missing_scope error. Then we added the scopes and called them again. The table shows which scope clears each method.
Slack File Size Limit: 1 GB Is Exactly 1,073,741,824 Bytes, Tested
We uploaded files of 50 MB, exactly 1 GiB and 1 GiB plus one byte to Slack through its upload API. The limit is 1,073,741,824 bytes, and a failed upload can still leave a broken file behind.
Slack Dark Mode: Where the Setting Is, and Which Custom Emoji Disappear
Slack's dark mode is under Preferences > Appearance > Color Mode, with Light, Dark and System. We switched modes in Slack web with three test emoji and measured which ones vanish.