Skip to main content

Session Management

Creating FIX API Keys

Generate RSA Key Pair

First, generate a 2048 bit RSA PKCS#8 key pair:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out kalshi-fix.key
openssl rsa -in kalshi-fix.key -pubout -out kalshi-fix.pub
This creates two files:
  • kalshi-fix.key: Your private key (keep secure, never share)
  • kalshi-fix.pub: Your public key (share with Kalshi)

Create API Key

  1. Navigate to https://demo.kalshi.co/account/profile
  2. Click the “Create key” button
  3. Name your API Key
  4. Copy and paste the contents of kalshi-fix.pub into the “RSA public key” field
  5. Click “Create”
  6. Copy the generated FIX API Key (UUID format)
Store your private key securely. It is equivalent to your username + password and should never be sent to anyone, including Kalshi employees.

Logon Process

Logon Message (35=A)

The initiator must send a Logon message to establish a session. The acceptor will either:
  • Respond with a Logon message acknowledging successful logon
  • Respond with a Logout (35=5) message if the logon fails

Required Fields

TagNameDescriptionValue
98EncryptMethodMethod of encryptionNone<0>
96RawDataClient logon message signatureBase64 encoded signature
108HeartbeatIntHeartbeat interval (seconds)N > 3
1137DefaultApplVerIDDefault application versionFIX50SP2<9>

Optional Fields

TagNameDescriptionDefault
108HeartbeatIntHeartbeat <0> interval (seconds)30
8013CancelOrdersOnDisconnectCancel orders on any disconnection (including graceful logout)N
20126ListenerSessionListen-only session (KalshiNR/RT only, requires SkipPendingExecReports=Y)N
20127ReceiveSettlementReportsReceive settlement reports (KalshiRT only)N
20200MessageRetentionPeriodHow long session messages will be store for retransmission (KalshiRT and KalshiRFQ only)3
21003SkipPendingExecReportsSkip PENDING_{NEW|REPLACE|CANCEL} execution reportsN
21007EnableIocCancelReportPartially filled IOC orders produce a cancel reportN

Signature Generation

The RawData field must contain a PSS RSA signature of the pre-hash string:
PreHashString = SendingTime + SOH + MsgType + SOH + MsgSeqNum + SOH + SenderCompID + SOH + TargetCompID
Critical: SendingTime in SignatureThe SendingTime in the PreHashString must match exactly the value in field 52 of your logon message.
  • Using a FIX library: Most libraries auto-add SendingTime. Use that exact value (don’t manually add a timestamp) when generating the signature.
  • Building the message yourself: Include SendingTime in your message and use that same value in the PreHashString. Format: YYYYMMDD-HH:MM:SS.mmm
from base64 import b64encode
from Cryptodome.Signature import pss
from Cryptodome.Hash import SHA256
from Cryptodome.PublicKey import RSA

# Load private key
private_key = RSA.import_key(open('kalshi-fix.key').read().encode('utf-8'))

# Build message string
# IMPORTANT: Use the EXACT SendingTime that will appear in field 52
# If constructing the message yourself: generate SendingTime and use it here
# If using a library: use the SendingTime value that your library will set
sending_time = "20230809-05:28:18.035"
msg_type = "A"
msg_seq_num = "1"
sender_comp_id = "your-fix-api-key-uuid"
target_comp_id = "Kalshi"  # Or appropriate TargetCompID

msg_string = chr(1).join([
    sending_time, msg_type, msg_seq_num,
    sender_comp_id, target_comp_id
])

# Generate signature
msg_hash = SHA256.new(msg_string.encode('utf-8'))
signature = pss.new(private_key).sign(msg_hash)
raw_data_value = b64encode(signature).decode('utf-8')

Session Maintenance

Heartbeat Protocol

  • Default interval: 30 seconds
  • Both sides must respond to TestRequest messages
  • Connection terminates if heartbeat response not received within interval

Message Sequence Numbers

Sequence Number Rules

  1. Must be unique and increase by one for each message
  2. Empty MsgSeqNum results in session termination
  3. Lower than expected = serious failure, connection terminated
  4. Higher than expected = recoverable with ResendRequest (if supported)

Reconnection Procedure

For unexpected sequence numbers:
  1. Document the issue with logs for out-of-band communication
  2. Check order status via REST API or UI
  3. Establish new session with reset sequence numbers

ResendRequest (35=2)

Only available on Order Entry with Retransmission (KalshiRT) sessions.

Limitations

  • Lookback window limited to 3 hours (or up to 24 hours if MessageRetentionPeriod was set on Logon request)
  • If you provide a BeginSeqNo that is beyond the lookback window, you will receive a Reject message
TagNameDescription
7BeginSeqNoLower bound (inclusive)
16EndSeqNoUpper bound (inclusive)

Error Handling

Reject (35=3)

Sent when a message cannot be processed due to session-level rule violations.
TagNameDescriptionRequired
45RefSeqNumSequence number of rejected messageYes
58TextHuman-readable error descriptionNo
371RefTagIDTag number that caused rejectNo
372RefMsgTypeMsgType of referenced messageNo
373SessionRejectReasonRejection reason codeNo
Reject indicates serious errors that may result from faulty logic. Log and investigate these errors.

BusinessMessageReject (35=j)

Sent for business logic violations rather than session-level errors.
TagNameDescriptionRequired
45RefSeqNumSequence number of rejected messageYes
58TextHuman-readable error descriptionNo
371RefTagIDTag number that caused rejectNo
372RefMsgTypeMsgType of referenced messageNo
379BusinessRejectRefIDBusiness-level ID of rejected messageNo
380BusinessRejectReasonBusiness rejection reason codeYes

Session Termination

Logout (35=5)

Graceful session termination:
  1. Initiator sends Logout message
  2. Acceptor responds with Logout (empty Text field)
  3. Transport layer connection terminated
TagNameDescription
58TextError description (if any)
If CancelOrdersOnDisconnect=Y, all orders are canceled even on graceful logout.

Message Headers

All messages must include standard FIX headers:
TagNameDescriptionRequirements
8BeginStringProtocol versionFIXT.1.1 (must be first)
9BodyLengthMessage length in bytesMust be second
34MsgSeqNumMessage sequence numberUnique, incrementing
35MsgTypeMessage typeMust be third
52SendingTimeUTC timestampWithin 120 seconds of server time

Message Trailers

TagNameDescription
10CheckSumStandard FIX checksumMust be last field
CheckSum is calculated by summing ASCII values of all characters (except checksum field) modulo 256.

Best Practices

  1. Session Configuration
    • Use unique ClOrdIDs across all message types
    • Implement proper heartbeat handling
    • Monitor sequence numbers carefully
  2. Error Recovery
    • Implement automatic reconnection logic
    • Store order state locally for recovery
    • Use drop copy session for missed messages
  3. Security
    • Rotate API keys periodically
    • Monitor for unauthorized access
    • Use secure storage for private keys