Three simple steps to make your first authenticated API request to Kalshi
This guide shows you how to make authenticated requests to the Kalshi API in three simple steps. For the full production and demo endpoint list, see API Environments and Endpoints.
Important: Sign the full URL path from the API root, without query parameters. For https://external-api.demo.kalshi.co/trade-api/v2/portfolio/orders?limit=5, sign /trade-api/v2/portfolio/orders.
Sign with your private key: RSA keys use RSA-PSS with SHA-256; Ed25519 keys sign the message directly.
Encode as base64: Convert the signature to base64 string
The signing process in Python, for either key type:
import base64from cryptography.hazmat.primitives import hashesfrom cryptography.hazmat.primitives.asymmetric import paddingfrom cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKeydef sign_request(private_key, timestamp, method, path): # Strip query parameters from path before signing path_without_query = path.split('?')[0] # Create the message to sign message = f"{timestamp}{method}{path_without_query}".encode('utf-8') if isinstance(private_key, Ed25519PrivateKey): # Ed25519 signs the message itself signature = private_key.sign(message) else: # RSA signs with RSA-PSS over SHA-256 signature = private_key.sign( message, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.DIGEST_LENGTH ), hashes.SHA256() ) # Return base64 encoded return base64.b64encode(signature).decode('utf-8')
Check the API Key ID and private key path, and sign with the algorithm of the parsed key (the PEM header alone does not tell you which)
Signature error
Ensure timestamp is in milliseconds (not seconds)
Path not found
If your BASE_URL already ends with /trade-api/v2, pass only the endpoint path to the helper (e.g. /portfolio/balance, not /trade-api/v2/portfolio/balance) so the request URL is not double-prefixed
Signature error with query params
Sign the request path without query parameters. The examples do this with path.split('?')[0] after building the full URL path