mrkeyoor.com_
Sun 20 Sept 11:46 UTC
PyPIWeb Backendupdated 20 Sept 2026

slack-sdk review

slack-sdk 3.43.0 is Slack's official Python toolkit for Web API methods, incoming webhooks, request signatures, Socket Mode, OAuth, Grid-style Block Kit models, SCIM, and audit logs. WebClient maps methods such as chat.postMessage to Python calls, while Bolt remains the higher-level framework for routing events and interactive actions. Version 3.43.0 rejects empty signing secrets, raises the async aiohttp floor to 3.13.5, exposes ChatStream.ts, and makes thread_ts optional for assistant suggested prompts.

Verdict

slack-sdk 3.43.0 installed in 0.3 seconds and used 3 MB in our sandbox, but import slack failed because aiohttp was absent, so verify your chosen sync or async import before deployment. Install it for several Slack APIs or signature helpers; a single outgoing notification is simpler through an incoming webhook.

We installed it

Lab card: what happened when we installed slack-sdkScreenshot of slack-sdk documentation
Install✓ · 0.3s1 package on disk · 3 MB
Importimport slack · pure Python · py.typed · requires Python >=3.7
Known vulns0(pip-audit)

Answers from our run

Does slack-sdk install cleanly?

Yes. In a fresh container with an empty cache, pip install slack-sdk finished in 0.3s, leaving 1 package and 3 MB on disk. pip-audit reported no known vulnerabilities.

What does slack-sdk need to run?

Python >=3.7, and nothing compiled: it is pure Python. In our run import slack failed, so it needs extra system packages, and the package ships py.typed for type checkers.

slack-sdk or slack-bolt: which should you use?

slack-bolt: Use it for routed events, commands, shortcuts, modals, and interactive actions. slack-sdk 3.43.0 installed in 0.3 seconds and used 3 MB in our sandbox, but import slack failed because aiohttp was absent, so verify your chosen sync or async import before deployment.

When should you not use slack-sdk?

The integration posts one fixed notification; an incoming webhook plus an existing HTTP client has less package and admin surface

API stability4/5The slack-sdk 3.x method naming, SlackResponse mapping, webhook clients, SignatureVerifier, and Socket Mode modules remain familiar as Slack adds endpoints. Stability is also controlled by the hosted platform: files.upload was retired, scopes can change, and version 3.43.0 now rejects an empty signing secret that earlier configuration may have accepted.
Docs4/5Slack's official SDK site covers WebClient, AsyncWebClient, webhooks, OAuth, Socket Mode, request signing, retries, proxies, and TLS. The README includes runnable sync and async calls and warns that Bolt is the event-handling layer. Method-specific scopes and rate-limit tiers live in Slack's separate API reference, so diagnosing one failure often crosses both documentation areas.
Maintenance5/5Slack published version 3.43.0 on 2026-06-30, and the GitHub repository was pushed on 2026-08-26. It was not archived and showed 4,015 stars plus 57 open issues and pull requests. The release contains API additions, signing-secret validation, dependency floor updates, and documentation repairs under the platform vendor's repository.
Ecosystem5/5The supplied count is 22,550,942 weekly downloads. Slack Bolt for Python builds on this SDK, whose modules cover Web API calls, Block Kit models, incoming webhooks, OAuth, signatures, SCIM, audit logs, and Socket Mode. That breadth still has optional boundaries, as our missing-aiohttp import failure demonstrates.

Use it if

  • A Python service calls several Slack Web API methods and should use Slack-maintained request and response handling
  • An HTTP endpoint needs Slack timestamp and signing-signature verification
  • A private service receives event envelopes through Socket Mode instead of a public callback URL
  • A multi-workspace app needs OAuth installation helpers and token storage interfaces
Skip it if

Setup reality

We installed slack-sdk 3.43.0 in 0.3 seconds on Python 3.12. One package occupied 3 MB, pip-audit reported 0 known vulnerabilities, and the pure-Python distribution included py.typed. It requires Python 3.7 or newer, uses the MIT license, and declares 8 direct dependencies. Our import slack probe failed with ModuleNotFoundError: No module named 'aiohttp'. Test the exact import path your application uses.

Slack administration takes more work than pip. Create an app, choose bot scopes, install it to the workspace, keep the xoxb token in a secret store, and invite the bot to private channels it needs. A later scope change requires another installation approval. Version 3.43.0 also makes a non-empty signing secret mandatory for inbound request verification.

WebClient does not attach rate-limit retries by default. Add RateLimitErrorRetryHandler with a bounded count so a 429 respects Retry-After. List APIs use cursor pagination; iterating SlackResponse can fetch later pages, while reading only the first response can silently omit results in a large workspace. Store channel and user IDs because display names can change.

AsyncWebClient needs aiohttp, whose minimum supported version moved to 3.13.5 in this release. Socket Mode uses an xapp app token in addition to the xoxb bot token and expects prompt envelope acknowledgements. Signature verification must see the raw request bytes before JSON or form parsing changes them. Use files_upload_v2 for new upload code because Slack retired the older files.upload flow.

Patterns

Send a bot message post-message

import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
try:
    response = client.chat_postMessage(
        channel='C0123456789', text='Deploy finished',
    )
    print(response['ts'])
except SlackApiError as error:
    print(error.response['error'], error.response.status_code)

Use a channel ID. Keep text even when blocks are supplied because notifications and accessibility clients use that fallback.

Handle Slack error codes branch-api-errors

try:
    client.chat_postMessage(channel=channel_id, text=body)
except SlackApiError as error:
    code = error.response['error']
    if code == 'missing_scope':
        needed = error.response.get('needed')
        raise RuntimeError(f'reinstall with scope: {needed}')
    if code == 'not_in_channel':
        client.conversations_join(channel=channel_id)
        client.chat_postMessage(channel=channel_id, text=body)

SlackApiError includes the API code and HTTP status. Adding a missing scope requires the workspace to approve installation again.

Honor Retry-After on 429 retry-rate-limit

from slack_sdk import WebClient
from slack_sdk.http_retry.builtin_handlers import RateLimitErrorRetryHandler

client = WebClient(token=token)
client.retry_handlers.append(
    RateLimitErrorRetryHandler(max_retry_count=3),
)

The retry handler is not attached by default. Keep its count finite because each Retry-After wait extends request latency.

Follow every result cursor paginate-channels

channels = []
for page in client.conversations_list(limit=200, exclude_archived=True):
    channels.extend(page['channels'])

Iterating SlackResponse fetches subsequent cursors. Looking only at the first page truncates a workspace with more than the requested limit.

Use the current file upload flow upload-file

response = client.files_upload_v2(
    channel='C0123456789',
    file='./report.csv',
    title='Nightly report',
    initial_comment='Numbers are attached',
)
print(response['file']['id'])

files_upload_v2 replaces the retired files.upload method and needs files:write permission.

Validate an inbound Slack request verify-signature

import os
from slack_sdk.signature import SignatureVerifier

verifier = SignatureVerifier(os.environ['SLACK_SIGNING_SECRET'])

def handle(request):
    if not verifier.is_valid_request(
        request.get_data(), dict(request.headers),
    ):
        return 'invalid signature', 401
    return process(request)

Version 3.43.0 requires a non-empty secret. Pass the exact raw body so parsing does not change the signed bytes.

Post through an incoming webhook send-webhook

import os
from slack_sdk.webhook import WebhookClient

webhook = WebhookClient(os.environ['SLACK_WEBHOOK_URL'])
response = webhook.send(text='Build #482 passed')
assert response.status_code == 200 and response.body == 'ok'

WebhookClient returns an HTTP status and body, unlike the JSON-like SlackResponse returned by WebClient.

Construct a Block Kit message build-blocks

from slack_sdk.models.blocks import ActionsBlock, ButtonElement, SectionBlock

blocks = [
    SectionBlock(text='*Deploy 4.2.0* is ready'),
    ActionsBlock(elements=[
        ButtonElement(
            text='Ship it', action_id='ship', value='4.2.0', style='primary',
        ),
    ]),
]
client.chat_postMessage(
    channel=channel_id, blocks=blocks, text='Deploy 4.2.0 is ready',
)

The model classes check payload structure locally. Plain dictionaries are also accepted when copied from Block Kit Builder.

Call Slack from asyncio post-async

import asyncio
import os
from slack_sdk.web.async_client import AsyncWebClient

client = AsyncWebClient(token=os.environ['SLACK_BOT_TOKEN'])

async def notify(channels, text):
    await asyncio.gather(*(
        client.chat_postMessage(channel=channel, text=text)
        for channel in channels
    ))

AsyncWebClient needs aiohttp 3.13.5 or newer for slack-sdk 3.43.0. Bound concurrency because a large gather can hit method limits.

Acknowledge Socket Mode envelopes receive-socket-mode

from slack_sdk.socket_mode import SocketModeClient
from slack_sdk.socket_mode.response import SocketModeResponse

socket = SocketModeClient(
    app_token=os.environ['SLACK_APP_TOKEN'], web_client=client,
)

def receive(active_client, request):
    active_client.send_socket_mode_response(
        SocketModeResponse(envelope_id=request.envelope_id),
    )
    if request.type == 'events_api':
        handle_event(request.payload['event'])

socket.socket_mode_request_listeners.append(receive)
socket.connect()

Acknowledge before slow processing. The xapp app token opens Socket Mode, while the xoxb bot token authorizes Web API calls.

Resolve a Slack user by email lookup-email

def slack_id_for(email):
    try:
        return client.users_lookupByEmail(email=email)['user']['id']
    except SlackApiError as error:
        if error.response['error'] == 'users_not_found':
            return None
        raise

users:read.email is a separate scope from users:read. Use this endpoint instead of downloading every user to search locally.

Post and update a threaded status reply-thread

parent = client.chat_postMessage(
    channel=channel_id, text='Running migration...',
)
client.chat_postMessage(
    channel=channel_id, thread_ts=parent['ts'], text='Step 1/3 done',
)
client.chat_update(
    channel=channel_id, ts=parent['ts'], text='Migration complete',
)

thread_ts points to the top-level message. chat_update also needs the original message's channel and timestamp.

Alternatives

PackageRegistryPick it when
slack-boltPyPIUse it for routed events, commands, shortcuts, modals, and interactive actions
httpxPyPIUse it when one incoming webhook request is the entire Slack integration
slackclientPyPIKeep it only while migrating legacy code; the project README says slack-sdk is its successor

More web backend guides

urllib3 · requests · ws · anyio · httpx · undici · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.