Communications (RFQ / Quote)¶
Kalshi's bilateral block-trade rail. An RFQ is a private "make me a market on this contract at this size?" message; a Quote is a counterparty's answer. The requester accepts a side; the maker confirms; the trade settles.
Auth required throughout.
Deprecated since v3.0.0
The flat method names (list_rfqs, get_rfq, create_rfq, delete_rfq,
list_all_rfqs, list_quotes, get_quote, create_quote,
delete_quote, list_all_quotes, accept_quote, confirm_quote) on
CommunicationsResource still work but emit DeprecationWarning and will
be removed in a future release. Switch to the rfqs. / quotes.
sub-namespaces documented below.
Quick reference¶
| Method | Endpoint |
|---|---|
get_id() |
GET /communications/id |
rfqs.list(...) / rfqs.list_all(...) |
GET /communications/rfqs |
rfqs.get(rfq_id) |
GET /communications/rfqs/{rfq_id} |
rfqs.create(...) |
POST /communications/rfqs |
rfqs.delete(rfq_id) |
DELETE /communications/rfqs/{rfq_id} |
quotes.list(...) / quotes.list_all(...) |
GET /communications/quotes |
quotes.get(quote_id) |
GET /communications/quotes/{quote_id} |
quotes.create(...) |
POST /communications/quotes |
quotes.delete(quote_id) |
DELETE /communications/quotes/{quote_id} |
quotes.accept(quote_id, *, accepted_side) |
PUT /communications/quotes/{quote_id}/accept |
quotes.confirm(quote_id) |
PUT /communications/quotes/{quote_id}/confirm |
block_trade_proposals.list(...) / block_trade_proposals.list_all(...) |
GET /communications/block-trade-proposals |
block_trade_proposals.create(...) |
POST /communications/block-trade-proposals |
block_trade_proposals.accept(block_trade_proposal_id, *, subtrader_id=None, subaccount=None) |
POST /communications/block-trade-proposals/{block_trade_proposal_id}/accept |
get_id() returns your participant_id — the value you'll pass as
quote_creator_user_id / rfq_creator_user_id when filtering lists. It stays
at the top level because it has no sub-noun.
Requester flow¶
# Identify yourself for filtering downstream.
me = client.communications.get_id()
# 1) Post an RFQ asking for a price on 500 contracts.
rfq = client.communications.rfqs.create(
market_ticker="KXPRES-24-DJT",
contracts=500,
rest_remainder=True,
)
print(rfq.rfq.rfq_id)
# 2) Poll for incoming quotes (or subscribe to the `communications` WS channel).
quotes = client.communications.quotes.list(rfq_creator_user_id=me.user_id)
for q in quotes:
print(q.quote_id, q.yes_bid, q.no_bid)
# 3) Accept a side on one of them.
accepted = client.communications.quotes.accept(
quotes.items[0].quote_id, accepted_side="yes"
)
Maker flow¶
me = client.communications.get_id()
# 1) Watch for incoming RFQs.
for rfq in client.communications.rfqs.list_all(status="open"):
print(rfq.rfq_id, rfq.market_ticker, rfq.contracts)
# 2) Quote one.
resp = client.communications.quotes.create(
rfq_id="rfq_abc",
yes_bid="0.60",
no_bid="0.40",
rest_remainder=False,
)
# 3) Wait for the counterparty to accept.
# 4) Confirm to lock the fill.
client.communications.quotes.confirm(resp.quote.quote_id)
quotes.list requires a user-id filter
quotes.list and quotes.list_all must be called with at least one of:
quote_creator_user_id=(filter to a specific quoter)rfq_creator_user_id=(filter to a specific RFQ originator)user_filter="self"(server-side shorthand for "the caller's quotes")rfq_user_filter="self"(server-side shorthand for "quotes on the caller's RFQs")
user_filter / rfq_user_filter were added in spec v3.18.0 and let
you avoid round-tripping get_id() first. Passing rfq_id= alone
raises ValueError locally before the round trip — this enforces a
server-side requirement.
Filtering shortcuts (v2.1.0)¶
# All quotes you made — no get_id() needed.
for q in client.communications.quotes.list_all(user_filter="self"):
print(q.quote_id, q.yes_bid)
# All quotes against RFQs you originated.
for q in client.communications.quotes.list_all(rfq_user_filter="self"):
...
# Same shortcut on RFQs:
for rfq in client.communications.rfqs.list_all(user_filter="self"):
...
UserFilterLiteral only accepts "self" today — the spec leaves room for
server-side shorthands like "organization" in the future without an SDK
upgrade.
Filtering quotes by time (v4.1.0)¶
quotes.list() / quotes.list_all() accept min_ts and max_ts (Unix
seconds, int) to bound results by the quote's last-updated timestamp. They
compose with the required user-id filter:
import time
# Quotes you made that were updated in the last hour.
for q in client.communications.quotes.list_all(
user_filter="self",
min_ts=int(time.time()) - 3600,
):
print(q.quote_id, q.updated_ts)
min_ts / max_ts are also accepted by the deprecated list_quotes /
list_all_quotes forwarders.
Post-only quotes¶
quotes.create() accepts post_only=True (added in v2.1.0) to ensure your
resting order is canceled rather than crossed if it would take liquidity:
client.communications.quotes.create(
rfq_id="rfq_abc",
yes_bid="0.60", no_bid="0.40",
rest_remainder=True,
post_only=True, # cancel rather than cross
)
Delete is not server-idempotent
rfqs.delete(rfq_id) and quotes.delete(quote_id) propagate a 404 as
KalshiNotFoundError when the RFQ or quote is already canceled,
expired, or never existed. The SDK does not swallow it — the caller
owns safe-retry idempotency:
RFQ statuses¶
open, accepted, confirmed, canceled. Status filtering accepts these
literal strings.
Block trade proposals (v4.1.0)¶
client.communications.block_trade_proposals backs the
/communications/block-trade-proposals API (OpenAPI 3.21.0) — a bilateral
negotiated trade that both the buyer and seller must accept before it settles.
# Propose a block trade. The proposer names both sides explicitly.
resp = client.communications.block_trade_proposals.create(
buyer_user_id="user_abc",
seller_user_id="user_xyz",
market_ticker="KXPRES-24-DJT",
price_centi_cents=5600, # plain int, centi-cents (NOT a _dollars price)
centicount=50_000, # plain int, centicounts (NOT a _fp count)
maker_side="yes",
expiration_ts="2026-07-01T00:00:00Z",
)
proposal_id = resp.block_trade_proposal_id
# List open proposals on a market.
for proposal in client.communications.block_trade_proposals.list_all(
market_ticker="KXPRES-24-DJT", status="open"
):
print(proposal.id, proposal.buyer_accepted, proposal.seller_accepted)
# Accept (the counterparty side). The body is optional — pass subtrader_id
# or subaccount only if you trade through a sub-trader / subaccount.
client.communications.block_trade_proposals.accept(proposal_id)
Prices are centi-cents, counts are centicounts — plain integers
Unlike the rest of the SDK, BlockTradeProposal.price_centi_cents and
.centicount (and the matching create() kwargs) are plain ints in
centi-cents and centicounts respectively — they are not
FixedPointDollars / _fp wire fields, so no Decimal conversion or
_dollars / _fp aliasing applies. price_centi_cents and centicount
must each be >= 1.
Migrating from v2.x¶
| v2.x (deprecated) | v3.0.0 (canonical) |
|---|---|
client.communications.list_rfqs(...) |
client.communications.rfqs.list(...) |
client.communications.list_all_rfqs(...) |
client.communications.rfqs.list_all(...) |
client.communications.get_rfq(rfq_id) |
client.communications.rfqs.get(rfq_id) |
client.communications.create_rfq(...) |
client.communications.rfqs.create(...) |
client.communications.delete_rfq(rfq_id) |
client.communications.rfqs.delete(rfq_id) |
client.communications.list_quotes(...) |
client.communications.quotes.list(...) |
client.communications.list_all_quotes(...) |
client.communications.quotes.list_all(...) |
client.communications.get_quote(quote_id) |
client.communications.quotes.get(quote_id) |
client.communications.create_quote(...) |
client.communications.quotes.create(...) |
client.communications.delete_quote(quote_id) |
client.communications.quotes.delete(quote_id) |
client.communications.accept_quote(quote_id, accepted_side=...) |
client.communications.quotes.accept(quote_id, accepted_side=...) |
client.communications.confirm_quote(quote_id) |
client.communications.quotes.confirm(quote_id) |
get_id() is unchanged.
Reference¶
kalshi.resources.communications.CommunicationsResource ¶
Bases: SyncResource
Sync communications / RFQ API.
Sub-namespaces:
- :attr:
rfqs— :class:RFQsResource—/communications/rfqssurface. - :attr:
quotes— :class:QuotesResource—/communications/quotessurface.
The misc :meth:get_id endpoint stays at the top level because it has no
sub-noun. The flat list_rfqs / get_rfq / create_rfq /
delete_rfq / list_all_rfqs / list_quotes / get_quote /
create_quote / delete_quote / list_all_quotes / accept_quote
/ confirm_quote methods are :class:typing_extensions.deprecated
forwarders kept for one release.
list_rfqs ¶
list_rfqs(
*,
cursor: str | None = None,
limit: int | None = None,
event_ticker: str | None = None,
market_ticker: str | None = None,
subaccount: int | None = None,
status: RfqStatusLiteral | None = None,
creator_user_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
extra_headers: dict[str, str] | None = None
) -> Page[RFQ]
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.list instead.
list_all_rfqs ¶
list_all_rfqs(
*,
limit: int | None = None,
event_ticker: str | None = None,
market_ticker: str | None = None,
subaccount: int | None = None,
status: RfqStatusLiteral | None = None,
creator_user_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
max_pages: int | None = None,
extra_headers: dict[str, str] | None = None
) -> Iterator[RFQ]
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.list_all instead.
get_rfq ¶
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.get instead.
create_rfq ¶
create_rfq(
*,
request: CreateRFQRequest | None = None,
market_ticker: str | None = None,
rest_remainder: bool | None = None,
contracts: int | None = None,
target_cost: Decimal | str | float | int | None = None,
replace_existing: bool | None = None,
subtrader_id: str | None = None,
subaccount: int | None = None,
extra_headers: dict[str, str] | None = None
) -> CreateRFQResponse
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.create instead.
delete_rfq ¶
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.delete instead.
list_quotes ¶
list_quotes(
*,
cursor: str | None = None,
limit: int | None = None,
min_ts: int | None = None,
max_ts: int | None = None,
status: QuoteStatusLiteral | None = None,
quote_creator_user_id: str | None = None,
rfq_creator_user_id: str | None = None,
rfq_creator_subtrader_id: str | None = None,
rfq_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
rfq_user_filter: UserFilterLiteral | None = None,
extra_headers: dict[str, str] | None = None
) -> Page[Quote]
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.list instead.
list_all_quotes ¶
list_all_quotes(
*,
limit: int | None = None,
min_ts: int | None = None,
max_ts: int | None = None,
status: QuoteStatusLiteral | None = None,
quote_creator_user_id: str | None = None,
rfq_creator_user_id: str | None = None,
rfq_creator_subtrader_id: str | None = None,
rfq_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
rfq_user_filter: UserFilterLiteral | None = None,
max_pages: int | None = None,
extra_headers: dict[str, str] | None = None
) -> Iterator[Quote]
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.list_all instead.
get_quote ¶
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.get instead.
create_quote ¶
create_quote(
*,
request: CreateQuoteRequest | None = None,
rfq_id: str | None = None,
yes_bid: Decimal | str | float | int | None = None,
no_bid: Decimal | str | float | int | None = None,
rest_remainder: bool | None = None,
subaccount: int | None = None,
post_only: bool | None = None,
extra_headers: dict[str, str] | None = None
) -> CreateQuoteResponse
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.create instead.
delete_quote ¶
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.delete instead.
accept_quote ¶
accept_quote(
quote_id: str,
*,
request: AcceptQuoteRequest | None = None,
accepted_side: Literal["yes", "no"] | None = None,
extra_headers: dict[str, str] | None = None
) -> None
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.accept instead.
confirm_quote ¶
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.confirm instead.
kalshi.resources.communications.RFQsResource ¶
Bases: SyncResource
Sync RFQ sub-resource — client.communications.rfqs.
Reached via :attr:CommunicationsResource.rfqs. Backs
/communications/rfqs endpoints.
kalshi.resources.communications.QuotesResource ¶
Bases: SyncResource
Sync Quote sub-resource — client.communications.quotes.
Reached via :attr:CommunicationsResource.quotes. Backs
/communications/quotes endpoints.
delete_for_rfq ¶
delete_for_rfq(
rfq_id: str,
quote_id: str,
*,
extra_headers: dict[str, str] | None = None
) -> None
Delete a quote scoped to its RFQ (spec v3.22.0).
DELETE /communications/rfqs/{rfq_id}/quotes/{quote_id} — once
deleted the quote can no longer be accepted.
accept_for_rfq ¶
accept_for_rfq(
rfq_id: str,
quote_id: str,
*,
request: AcceptQuoteRequest | None = None,
accepted_side: Literal["yes", "no"] | None = None,
extra_headers: dict[str, str] | None = None
) -> None
Accept a quote scoped to its RFQ (spec v3.22.0).
PUT /communications/rfqs/{rfq_id}/quotes/{quote_id}/accept —
acceptance still requires the quoter to confirm.
confirm_for_rfq ¶
confirm_for_rfq(
rfq_id: str,
quote_id: str,
*,
extra_headers: dict[str, str] | None = None
) -> None
Confirm a quote scoped to its RFQ (spec v3.22.0).
PUT /communications/rfqs/{rfq_id}/quotes/{quote_id}/confirm —
starts the order-execution timer.
kalshi.resources.communications.BlockTradeProposalsResource ¶
Bases: SyncResource
Sync block-trade-proposals sub-resource.
client.communications.block_trade_proposals — backs the
/communications/block-trade-proposals endpoints (openapi 3.21.0).
kalshi.resources.communications.AsyncCommunicationsResource ¶
Bases: AsyncResource
Async communications / RFQ API.
Sub-namespaces:
- :attr:
rfqs— :class:AsyncRFQsResource—/communications/rfqs. - :attr:
quotes— :class:AsyncQuotesResource—/communications/quotes.
The flat list_rfqs / get_rfq / create_rfq / delete_rfq /
list_all_rfqs / list_quotes / get_quote / create_quote /
delete_quote / list_all_quotes / accept_quote / confirm_quote
coroutines are :class:typing_extensions.deprecated forwarders kept for
one release.
list_rfqs
async
¶
list_rfqs(
*,
cursor: str | None = None,
limit: int | None = None,
event_ticker: str | None = None,
market_ticker: str | None = None,
subaccount: int | None = None,
status: RfqStatusLiteral | None = None,
creator_user_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
extra_headers: dict[str, str] | None = None
) -> Page[RFQ]
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.list instead.
list_all_rfqs ¶
list_all_rfqs(
*,
limit: int | None = None,
event_ticker: str | None = None,
market_ticker: str | None = None,
subaccount: int | None = None,
status: RfqStatusLiteral | None = None,
creator_user_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
max_pages: int | None = None,
extra_headers: dict[str, str] | None = None
) -> AsyncIterator[RFQ]
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.list_all instead.
get_rfq
async
¶
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.get instead.
create_rfq
async
¶
create_rfq(
*,
request: CreateRFQRequest | None = None,
market_ticker: str | None = None,
rest_remainder: bool | None = None,
contracts: int | None = None,
target_cost: Decimal | str | float | int | None = None,
replace_existing: bool | None = None,
subtrader_id: str | None = None,
subaccount: int | None = None,
extra_headers: dict[str, str] | None = None
) -> CreateRFQResponse
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.create instead.
delete_rfq
async
¶
.. deprecated:: 3.0.0 Use :meth:client.communications.rfqs.delete instead.
list_quotes
async
¶
list_quotes(
*,
cursor: str | None = None,
limit: int | None = None,
min_ts: int | None = None,
max_ts: int | None = None,
status: QuoteStatusLiteral | None = None,
quote_creator_user_id: str | None = None,
rfq_creator_user_id: str | None = None,
rfq_creator_subtrader_id: str | None = None,
rfq_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
rfq_user_filter: UserFilterLiteral | None = None,
extra_headers: dict[str, str] | None = None
) -> Page[Quote]
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.list instead.
list_all_quotes ¶
list_all_quotes(
*,
limit: int | None = None,
min_ts: int | None = None,
max_ts: int | None = None,
status: QuoteStatusLiteral | None = None,
quote_creator_user_id: str | None = None,
rfq_creator_user_id: str | None = None,
rfq_creator_subtrader_id: str | None = None,
rfq_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
rfq_user_filter: UserFilterLiteral | None = None,
max_pages: int | None = None,
extra_headers: dict[str, str] | None = None
) -> AsyncIterator[Quote]
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.list_all instead.
get_quote
async
¶
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.get instead.
create_quote
async
¶
create_quote(
*,
request: CreateQuoteRequest | None = None,
rfq_id: str | None = None,
yes_bid: Decimal | str | float | int | None = None,
no_bid: Decimal | str | float | int | None = None,
rest_remainder: bool | None = None,
subaccount: int | None = None,
post_only: bool | None = None,
extra_headers: dict[str, str] | None = None
) -> CreateQuoteResponse
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.create instead.
delete_quote
async
¶
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.delete instead.
accept_quote
async
¶
accept_quote(
quote_id: str,
*,
request: AcceptQuoteRequest | None = None,
accepted_side: Literal["yes", "no"] | None = None,
extra_headers: dict[str, str] | None = None
) -> None
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.accept instead.
confirm_quote
async
¶
.. deprecated:: 3.0.0 Use :meth:client.communications.quotes.confirm instead.
kalshi.resources.communications.AsyncRFQsResource ¶
Bases: AsyncResource
Async RFQ sub-resource — client.communications.rfqs.
list_all ¶
list_all(
*,
limit: int | None = None,
event_ticker: str | None = None,
market_ticker: str | None = None,
subaccount: int | None = None,
status: RfqStatusLiteral | None = None,
creator_user_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
max_pages: int | None = None,
extra_headers: dict[str, str] | None = None
) -> AsyncIterator[RFQ]
Returns an async iterator — use async for.
kalshi.resources.communications.AsyncQuotesResource ¶
Bases: AsyncResource
Async Quote sub-resource — client.communications.quotes.
list_all ¶
list_all(
*,
limit: int | None = None,
min_ts: int | None = None,
max_ts: int | None = None,
status: QuoteStatusLiteral | None = None,
quote_creator_user_id: str | None = None,
rfq_creator_user_id: str | None = None,
rfq_creator_subtrader_id: str | None = None,
rfq_id: str | None = None,
user_filter: UserFilterLiteral | None = None,
rfq_user_filter: UserFilterLiteral | None = None,
max_pages: int | None = None,
extra_headers: dict[str, str] | None = None
) -> AsyncIterator[Quote]
Returns an async iterator — use async for.
delete_for_rfq
async
¶
delete_for_rfq(
rfq_id: str,
quote_id: str,
*,
extra_headers: dict[str, str] | None = None
) -> None
Delete a quote scoped to its RFQ (spec v3.22.0).
DELETE /communications/rfqs/{rfq_id}/quotes/{quote_id}.
accept_for_rfq
async
¶
accept_for_rfq(
rfq_id: str,
quote_id: str,
*,
request: AcceptQuoteRequest | None = None,
accepted_side: Literal["yes", "no"] | None = None,
extra_headers: dict[str, str] | None = None
) -> None
Accept a quote scoped to its RFQ (spec v3.22.0).
PUT /communications/rfqs/{rfq_id}/quotes/{quote_id}/accept.
confirm_for_rfq
async
¶
confirm_for_rfq(
rfq_id: str,
quote_id: str,
*,
extra_headers: dict[str, str] | None = None
) -> None
Confirm a quote scoped to its RFQ (spec v3.22.0).
PUT /communications/rfqs/{rfq_id}/quotes/{quote_id}/confirm.
kalshi.resources.communications.AsyncBlockTradeProposalsResource ¶
Bases: AsyncResource
Async block-trade-proposals sub-resource.
client.communications.block_trade_proposals — backs the
/communications/block-trade-proposals endpoints (openapi 3.21.0).
list_all ¶
list_all(
*,
limit: int | None = None,
market_ticker: str | None = None,
status: str | None = None,
max_pages: int | None = None,
extra_headers: dict[str, str] | None = None
) -> AsyncIterator[BlockTradeProposal]
Returns an async iterator — use async for.