import requests
url = "https://app.langwatch.ai/api/gateway/v1/spend-events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.langwatch.ai/api/gateway/v1/spend-events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));curl --request GET \
--url https://app.langwatch.ai/api/gateway/v1/spend-events \
--header 'Authorization: Bearer <token>'{
"data": [
{
"id": "<string>",
"type": "<string>",
"created": "<string>",
"schema_version": "<string>",
"data": {
"event_id": "<string>",
"event_type": "<string>",
"gateway_request_id": "<string>",
"occurred_at": "<string>",
"usage": {
"input_tokens": 123,
"output_tokens": 123,
"cache_read_input_tokens": 123,
"cache_creation_input_tokens": 123,
"reasoning_tokens": 123
},
"cost": {
"total_usd": "<string>",
"nano_usd": 123
},
"status": "<string>",
"needs_reconciliation": true,
"settle_reason": "<string>",
"error": {
"class": "<string>",
"http_status": 123
},
"duration_ms": 123,
"labels": [
"<string>"
],
"metadata": {}
}
}
],
"next_cursor": "<string>"
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}List spend events
Cursor-paged pull over the per-request spend record, ascending by insert order so rows folded late are never skipped by an in-flight cursor. Events are the same canonical objects webhook deliveries carry. Retention is a fixed 13 months, which bounds reconciliation and replay. When feeding a downstream biller, mind its dedup window (Metronome 34 days and Stripe meters 24h+ at the time of writing; both vendors own those numbers, so confirm the current one before you rely on it): re-pulling older ranges into a biller past its window can double-bill. Every filter here is accepted by /spend-summaries too, so a checksum that disagrees can be diffed on exactly the same narrowing; the one difference is status=admitted, which only this read answers, because an admitted request is still in flight and contributes no cost to a rollup. Repeat a filter to widen it (model=a&model=b matches either); name two different filters to narrow. metadata is written key:value, split on the first colon, and repeating a key widens that key. team_id and external_id name Postgres records and are resolved to the projects and keys they cover, so a team with no projects or an external id nobody minted answers with no spend rather than with everything.
import requests
url = "https://app.langwatch.ai/api/gateway/v1/spend-events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.langwatch.ai/api/gateway/v1/spend-events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));curl --request GET \
--url https://app.langwatch.ai/api/gateway/v1/spend-events \
--header 'Authorization: Bearer <token>'{
"data": [
{
"id": "<string>",
"type": "<string>",
"created": "<string>",
"schema_version": "<string>",
"data": {
"event_id": "<string>",
"event_type": "<string>",
"gateway_request_id": "<string>",
"occurred_at": "<string>",
"usage": {
"input_tokens": 123,
"output_tokens": 123,
"cache_read_input_tokens": 123,
"cache_creation_input_tokens": 123,
"reasoning_tokens": 123
},
"cost": {
"total_usd": "<string>",
"nano_usd": 123
},
"status": "<string>",
"needs_reconciliation": true,
"settle_reason": "<string>",
"error": {
"class": "<string>",
"http_status": 123
},
"duration_ms": 123,
"labels": [
"<string>"
],
"metadata": {}
}
}
],
"next_cursor": "<string>"
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"meta": {},
"trace_id": "<string>",
"span_id": "<string>"
}
}Authorizations
Admin API key for organization-level operations (managing projects, API keys). Create one in Settings > API Keys or via POST /api/api-keys. Format: sk-lw-{id}_{secret}.
Query Parameters
Milliseconds since the Unix epoch, not seconds. An epoch in seconds is a valid integer here and answers for 1970, so a mismatched unit reads as an empty window rather than as an error.
x <= 90071992547409911782864000000
Milliseconds since the Unix epoch, not seconds. An epoch in seconds is a valid integer here and answers for 1970, so a mismatched unit reads as an empty window rather than as an error.
x <= 90071992547409911782864000000
500x <= 2001 - 1001 - 1001 - 2001 - 1001 - 2561 - 1001 - 2001 - 1001 - 501 - 2003 - 640success, error, admitted, confirmed, failed, settled Was this page helpful?