API reference · Monitoring
Report events
POST/v1/bots/{bot_id}/events
Send up to 500 of your bot's orders, fills, cancels, positions, heartbeats and halts in one call. Idempotent per `client_event_id`; each event is accepted or refused on its own.
On this page
Auth. A report-only token for this bot (Authorization: Bearer lbr_…, Create a report token), or an account token. Never an exchange key.
Limits. Events per minute, per bot and per account: 6,000 and 6,000 on Free, 60,000 and 120,000 on Builder, 300,000 and 600,000 on Standard. A batch that does not fit is refused whole (429, Retry-After). Not counted against your API requests.
Each event:
| Field | Type | Rule |
|---|---|---|
client_event_id | string | required; 1 to 128 of A-Z a-z 0-9 . _ : -; unique per bot (your idempotency key) |
type | string | order, fill, cancel, position, heartbeat, halt, resume or note |
ts | integer | required; ms epoch; at most 5 minutes ahead, not older than your plan keeps |
venue | string | one of the bot's venues; required for order, fill, cancel, position |
ticker | string | required for order, fill, cancel, position |
side | string | yes or no; required for order, fill, position |
action | string | buy or sell; required for order, fill |
price | number | 0 to 1, dollars per contract of side; required for order (the limit) and fill; on position the average cost |
count | number | contracts; above 0 for order and fill; on position what is held after |
fee | number | dollars, fills only |
liquidity | string | maker or taker, fills only |
order_id, client_order_id | string | up to 128 characters |
text | string | up to 500 characters (a note, a halt reason) |
meta | object | up to 4 KiB and 4 levels deep |
Never send keys. An event whose meta has a key that looks like a credential (api_key, secret, private_key, password, token, signature, …) or a value holding a private key block or a Langbot token is refused with credential_field and not stored.
Send the same batch again after a timeout, a 429 or a 503: events already stored come back as duplicate and change nothing.
Parameters
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
bot_id | string | yes | The bot id (bot_…). |
Body
| Name | Type | Required | Description |
|---|---|---|---|
events | array of object | yes | The events, 1 to 500, at most 1 MiB in all. Each is described below. Up to 500 items. |
Request
curl -X POST "https://api.langbot.ai/v1/bots/bot_01M6G72RFQRQ8JGNG8T1447GGP/events" \
-H "Authorization: Bearer $LANGBOT_REPORT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"events": [{"client_event_id": "s1:4", "type": "heartbeat", "ts": 1793693146653}]}'from langbot import LangbotClient
async with LangbotClient() as lb: # reads LANGBOT_API_TOKEN
from langbot.monitor import BotReporter
rep = BotReporter("bot_01M6G72RFQRQ8JGNG8T1447GGP", token="lbr_…", venue="kalshi")
rep.fill("KXMENTION-26NOV03-TARIFF", "yes", "buy", 0.41, 15, fee=0.11) # never blocks
rep.heartbeat()
rep.close() # flushes what is queuedimport { LangbotClient } from "@langbotai/sdk";
const lb = new LangbotClient(); // reads LANGBOT_API_TOKEN
const rep = lb.bots.reporter("bot_01M6G72RFQRQ8JGNG8T1447GGP", "lbr_…");
rep.fill({ venue: "kalshi", ticker: "KXMENTION-26NOV03-TARIFF", side: "yes", action: "buy", price: 0.41, count: 15 });
await rep.close();Response
200 OK
{
"accepted": 4,
"duplicates": 0,
"rejected": 1,
"results": [
{
"index": 0,
"client_event_id": "s1:1",
"status": "accepted"
},
{
"index": 1,
"client_event_id": "s1:2",
"status": "accepted",
"applied": true
},
{
"index": 2,
"client_event_id": "s1:3",
"status": "accepted",
"applied": true
},
{
"index": 3,
"client_event_id": "s1:4",
"status": "accepted"
},
{
"index": 4,
"client_event_id": "s1:5",
"status": "rejected",
"error": {
"code": "credential_field",
"detail": "`meta` looks like it carries a credential (api_key); Langbot never accepts keys or secrets: remove it"
}
}
]
}200 whenever the batch itself is well formed, even if some events are refused: read results[].status (accepted, duplicate, rejected) and error.code (invalid, credential_field, venue_not_enabled, too_old, future_ts, idempotency_conflict, duplicate_in_batch). applied: false on a fill that does not move P&L (a Polymarket fill while a wallet is linked).
Errors
| Status | code | When |
|---|---|---|
| 403 | scope_denied | The report token is bound to another bot. |
| 404 | not_found | No bot with that id on your account (or it was deleted). |
| 413 | too_large | More than 500 events or more than 1 MiB; nothing was stored. |
| 429 | rate_limited | The bot's or account's events per minute are used; resend the same batch after Retry-After seconds. |
| 503 | unavailable | Ingestion is busy; resend the same batch after Retry-After seconds. |
| 401 | auth_required | No token was sent. |
| 401 | token_invalid | The token was not recognised (mistyped, revoked or expired). |
| 422 | validation_error | A parameter failed validation; detail[].loc names it. |
Every error body is {"detail": …, "code": "…"}; branch on code. All codes: Errors.