Kalshi Subaccounts API (2026): Create, Fund and Trade Them

The Short Answer
Kalshi subaccounts split one Direct account's cash and positions into separate buckets: the primary account, number 0, plus up to 63 numbered subaccounts. They are API-only and need the Advanced API tier or above. Create one with POST /portfolio/subaccounts, move cash with POST /portfolio/subaccounts/transfer, which takes cents and is idempotent on client_transfer_id, and read every balance with GET /portfolio/subaccounts/balances. Orders, cancel-all and balance reads then take a subaccount number, and an API key can be locked to a single subaccount. They are not sub-users, which are extra logins that share the whole account's funds.
Key Takeaways
- Subaccounts split funds and positions, while sub-users add people to an account. They are separate features with separate eligibility.
- Kalshi numbers subaccounts sequentially from 1 to 63, and creating one requires the Advanced API tier, which is one self-serve call away.
- Transfers between subaccounts never leave the account, and a reused
client_transfer_idreturns409instead of moving money twice. - Leaving out
subaccountmeans every subaccount on some endpoints and the primary account on others, so pass it explicitly. - Balances live on one exchange shard each, and perpetual futures have their own subaccount endpoints.
What are Kalshi subaccounts?
Kalshi's docs describe them as a way to "partition its balance and positions into independent buckets under one set of API credentials." Every account has a primary subaccount, number 0, and can add numbered subaccounts 1 to 63, for 64 buckets in total. Each has its own cash, positions and orders, so each strategy's money and exposure stay in its own bucket.
Two limits matter before you plan around them. Subaccounts are, in Kalshi's words, "currently an API-only feature", not yet supported in the web or mobile app. And they are for Direct accounts; FCM members manage their customers through separate subtrader endpoints.
How are subaccounts different from sub-users?
They solve different problems. Kalshi's docs put it plainly: "Sub-users control who can act on the account; subaccounts control which funds and positions an action uses."
| Sub-users (Team Members) | Subaccounts | |
|---|---|---|
| What it is | Another login, with its own email and password | A separate balance and set of positions |
| Funds | Shares the account's | Has its own |
| Permissions | Read, Trade and Transfers, set per sub-user | None of its own; an API key can be restricted to one |
| Set up by | Kalshi enables it; the owner adds people in the web app | The account owner, through the API |
| Available to | Institutions, on request | Direct accounts on the Advanced API tier or above |
The two combine. A sub-user's permissions cover the whole account, so to confine someone, or a bot, to one subaccount, give them an API key restricted to it. Only the account owner can create API keys.
Who can create Kalshi subaccounts?
Any Direct account on the Advanced API tier or above. Advanced is the second of Kalshi's seven rate-limit tiers, and it comes from one call to POST /account/api_usage_level/upgrade. Kalshi's criterion is that "at least 1 of the user's last 100 Predictions orders was created via API", so place one order through the API first. The grant is permanent, the call costs 30 rate-limit tokens, and GET /account/limits shows your tier afterwards. The tiers themselves are covered in Kalshi API rate limits.
How do you create and fund a subaccount?
Five portfolio endpoints manage them:
| Endpoint | What it does |
|---|---|
POST /portfolio/subaccounts | Creates the next numbered subaccount and returns its subaccount_number. Optional exchange_index |
POST /portfolio/subaccounts/transfer | Moves cash between two of your subaccounts: client_transfer_id, from_subaccount, to_subaccount, amount_cents |
GET /portfolio/subaccounts/balances | Every balance, one row per subaccount and exchange index |
GET /portfolio/subaccounts/transfers | Transfer history, up to 1,000 a page with a cursor |
GET and PUT /portfolio/subaccounts/netting | Reads or sets each subaccount's netting setting |
With the official TypeScript SDK, kalshi-typescript 3.31.0, and the account owner's key:
import { readFileSync } from 'node:fs';
import { randomUUID } from 'node:crypto';
import { ApiKeysApi, Configuration, PortfolioApi } from 'kalshi-typescript';
const config = new Configuration({
apiKey: 'YOUR_KEY_ID',
privateKeyPem: readFileSync('kalshi-private-key.pem', 'utf8'),
basePath: 'https://external-api.kalshi.com/trade-api/v2',
});
const portfolio = new PortfolioApi(config);
// 1. Create the next numbered subaccount on the default shard.
const { data: created } = await portfolio.createSubaccount({ exchange_index: 0 });
const sub = created.subaccount_number;
// 2. Move $250 into it from the primary account. This endpoint takes cents.
await portfolio.applySubaccountTransfer({
client_transfer_id: randomUUID(),
from_subaccount: 0,
to_subaccount: sub,
amount_cents: 25_000,
});
// 3. List every balance: one row per subaccount and exchange shard.
const { data: balances } = await portfolio.getSubaccountBalances();
for (const b of balances.subaccount_balances) console.log(b.subaccount_number, b.exchange_index, b.balance);
// 4. Mint an API key that can only read and trade this subaccount.
const { data: key } = await new ApiKeysApi(config).generateApiKey({ name: `strategy-${sub}`, key_type: 'ed25519', subaccount: sub });
console.log('restricted key', key.api_key_id); // key.private_key is shown once: store it securely
Transfers "net to zero at the account level", so nothing leaves your account. Keep the client_transfer_id when you retry: Kalshi returns 409 for a repeat instead of applying it twice. Watch the units. This endpoint takes cents, while the cross-shard intra-account transfer takes centicents, a hundredth of a cent. Kalshi's changelog adds that new subaccounts inherit the primary account's netting setting when they are created.
How do restricted API keys work?
Pass a subaccount number when you generate or register an API key, as step 4 above does with POST /api_keys/generate, and the key is locked to that subaccount. A restricted key can place and manage orders, read that subaccount's balance, positions, fills and settlements, use order groups and run the REST request for quote flow, all for its own subaccount. Requests that leave out subaccount act on the locked one, and naming any other is rejected.
It cannot transfer funds, manage subaccounts or API keys, or touch another subaccount's RFQs and quotes. Endpoints outside its scope return 403 with "this API key is restricted to a single sub-account and cannot access this endpoint". Restricted keys can also open WebSocket sessions, with every private channel scoped to their subaccount. That makes them the right credential for a bot: fund a subaccount with the capital it may use, and give the bot only that key. Key creation is covered in how to get a Kalshi API key.
How do you trade from a subaccount?
Pass its number. The V2 create-order body takes subaccount, where 0 is the primary, and the portfolio and cancel endpoints take it as a query parameter:
import { readFileSync } from 'node:fs';
import { Configuration, OrdersApi, PortfolioApi } from 'kalshi-typescript';
const config = new Configuration({
apiKey: 'YOUR_KEY_ID',
privateKeyPem: readFileSync('kalshi-private-key.pem', 'utf8'),
basePath: 'https://external-api.kalshi.com/trade-api/v2',
});
const orders = new OrdersApi(config);
const portfolio = new PortfolioApi(config);
const sub = 1;
// Place an order that draws on subaccount 1's cash and lands in its positions.
const { data: placed } = await orders.createOrderV2({
ticker: 'YOUR_MARKET_TICKER',
side: 'bid',
count: '5',
price: '0.4000',
time_in_force: 'good_till_canceled',
self_trade_prevention_type: 'taker_at_cross',
subaccount: sub,
});
console.log('order', placed.order_id);
// Read that subaccount's balance, then clear only its resting orders.
const { data: balance } = await portfolio.getBalance(sub);
console.log('subaccount', sub, 'balance', balance.balance_dollars);
await orders.cancelAllOrders(sub);
The SDK sends subaccount in the order body and as ?subaccount=1 on the balance and cancel-all calls. Cancel-all costs 2 tokens and clears resting orders across every exchange shard. Kalshi warns that orders placed in the minute after the request may be canceled too, so hold new orders for a minute afterwards.
The parameter's default is the trap. Left out, it means different things on different endpoints:
If subaccount is omitted | Endpoints |
|---|---|
| Every subaccount | DELETE /portfolio/events/orders (cancel-all), GET /portfolio/orders, GET /portfolio/fills |
| The primary account, 0 | GET /portfolio/balance, GET /portfolio/positions, cancel one order |
So a cancel-all without a number clears every strategy's orders, and a positions read without one shows only the primary account. Pass the number every time.
How do subaccounts work with exchange shards?
Each balance lives on one shard. Kalshi now splits trading across matching engines, with crypto and commodities on shard 2 and tennis, baseball and basketball on shard 3, and its docs state that "subaccount balances are local to a specific exchange instance". To fund a subaccount that trades crypto, first move account cash to shard 2, then create the subaccount there with exchange_index: 2, then transfer into it with the same exchange_index. The intra-account transfer endpoint can also move cash between subaccounts on different shards, but Kalshi warns that those transfers run in up to three non-atomic steps, so a failure part way can leave funds in the primary account on either shard. Order groups also work within one shard only.
Does Kalshi support subaccounts for perps?
Yes. The perpetual futures API has its own pair of endpoints, POST /portfolio/margin/subaccounts and POST /portfolio/margin/subaccounts/transfer, with the same numbering and the same 63-subaccount limit. Margin orders take a subaccount, margin cancel-all accepts one, and GET /margin/balance returns cash per subaccount. As of October 2026, Kalshi lists production perps access as rolling out member by member, and transfers between event contract and margin balances as not yet available.
Subaccounts are a clean way to run several strategies side by side. If those strategies span venues, the Predictefy API covers Kalshi among 15+ venues in one normalized schema, and when it routes a Kalshi order, your API credentials are used for the one request that needs them and never stored. As with any trading, each subaccount's capital is at risk, so size it as money you can afford to lose.
Frequently Asked Questions
What is a Kalshi subaccount?
A separate balance and set of positions inside one Kalshi account. Every account has a primary subaccount, number 0, and eligible accounts can add numbered subaccounts 1 to 63. Cash moves between them with transfers that never leave the account.
How many subaccounts can you have on Kalshi?
Up to 63 numbered subaccounts, or 64 including the primary account. Kalshi numbers them sequentially from 1 as you create them with POST /portfolio/subaccounts.
What do you need to create a Kalshi subaccount?
A Direct account on the Advanced API tier or above. You reach Advanced with one call to POST /account/api_usage_level/upgrade, which Kalshi grants permanently once at least 1 of your last 100 Predictions orders was placed through the API.
Can you use Kalshi subaccounts in the app?
Not as of October 2026. Kalshi describes subaccounts as an API-only feature that the web and mobile apps do not yet support, so numbered subaccounts are created, funded and traded through the Trade API.
What is the difference between a sub-user and a subaccount on Kalshi?
A sub-user is an extra login for a team member that shares the account's funds, with Read, Trade and Transfers permissions. A subaccount is a separate pot of cash and positions with no login of its own. Sub-users are for institutions on request; subaccounts are for Direct accounts on the Advanced tier.
Can a Kalshi API key be limited to one subaccount?
Yes. A key created with a subaccount number can read and trade only that subaccount. It cannot transfer funds, create subaccounts or manage API keys, and requests outside its scope return 403.