
- Auth
- API key id más una firma RSA sobre el timestamp, el método y la ruta, en cada petición.
- Lectura
- Una sola API REST para mercados, libros y velas; un websocket para ejecuciones y deltas del libro.
- Trading
- Órdenes limitadas en centavos o dólares por contrato; el libro está en contratos.
- Comisión de taker
- 0.07 x contratos x p x (1-p), redondeada hacia arriba al centavo por orden. Los makers no pagan nada en casi todas las series.
- Liquidación
- Publicada por el exchange; los mercados de cripto liquidan sobre una media que el exchange transmite a medida que se acumula.
- Geografía
- Cuentas de Estados Unidos. Los programas de recompensas están cerrados a usuarios de fuera de Estados Unidos.
- Docs
- docs.kalshi.com
Cinco pasos, simplificados a propósito. Las rutas de los endpoints siguen la referencia pública en el momento de escribir esto; consulta la documentación antes de depender de ellas.
Firma cada petición
Genera una API key en los ajustes de la cuenta; recibes un key id y un archivo de clave privada. Cada petición lleva el id, un timestamp en milisegundos y una firma RSA-PSS sobre el timestamp, el método HTTP y la ruta.
import time, base64, requests from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding BASE = "https://api.elections.kalshi.com/trade-api/v2" key = serialization.load_pem_private_key(open("kalshi.pem", "rb").read(), password=None) def headers(method, path): ts = str(int(time.time() * 1000)) msg = (ts + method + "/trade-api/v2" + path).encode() sig = key.sign(msg, padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.DIGEST_LENGTH), hashes.SHA256()) return {"KALSHI-ACCESS-KEY": KEY_ID, "KALSHI-ACCESS-SIGNATURE": base64.b64encode(sig).decode(), "KALSHI-ACCESS-TIMESTAMP": ts}Lista los mercados de una serie
r = requests.get(f"{BASE}/markets", params={"series_ticker": "KXBTC15M", "status": "open"}, headers=headers("GET", "/markets")) markets = r.json()["markets"] ticker = markets[0]["ticker"]Lee el libro de órdenes
El endpoint del libro cambió de forma: la versión nueva lista el precio en dólares y el tamaño por nivel. Un parser escrito para los antiguos arrays en centavos devuelve profundidad cero sin ningún error.
path = f"/markets/{ticker}/orderbook" ob = requests.get(BASE + path, headers=headers("GET", path)).json()["orderbook_fp"] yes_bids = ob["yes_dollars"] # [[price_dollars, size], ...] no_bids = ob["no_dollars"] best_yes_ask = 1 - float(no_bids[-1][0]) # un ask de YES es el espejo del mejor bid de NOLa escalera puede estar cruzada (mejor bid de YES más mejor bid de NO por encima de 1.00) en una parte apreciable de las instantáneas. Protégete contra eso antes de cotizar.
Envía y cancela una orden
order = {"ticker": ticker, "action": "buy", "side": "yes", "type": "limit", "count": 20, "yes_price": 85, "client_order_id": "win-1757500800"} r = requests.post(f"{BASE}/portfolio/orders", json=order, headers=headers("POST", "/portfolio/orders")) order_id = r.json()["order"]["order_id"] requests.delete(f"{BASE}/portfolio/orders/{order_id}", headers=headers("DELETE", f"/portfolio/orders/{order_id}"))Lee ejecuciones y liquidaciones
fills = requests.get(f"{BASE}/portfolio/fills", headers=headers("GET", "/portfolio/fills")).json()["fills"] settled = requests.get(f"{BASE}/portfolio/settlements", headers=headers("GET", "/portfolio/settlements")).json()Usa el websocket para las ejecuciones en un bot en vivo; consultar el endpoint de órdenes en bucle tiene límite de peticiones.
Historial gratuito del que nadie habla
El endpoint de candlesticks devuelve el historial de bid, ask y volumen de cualquier mercado en el intervalo que elijas, desde su listado. Para investigar, sustituye al logger que todavía no has escrito.
path = f"/series/KXBTC15M/markets/{ticker}/candlesticks"
c = requests.get(BASE + path, params={"start_ts": t0, "end_ts": t1, "period_interval": 1}, headers=headers("GET", path)).json()