Ingest API
The Ingest API allows you to send log events to LogRack. You can send individual log entries or batch multiple events in a single request for better performance.
/api/ingestingest:writeRequest Format
Send a JSON body with an events array containing one or more log entries:
{
"events": [
{
"message": "Your log message here",
"level": "info",
"ts": "2024-01-15T14:30:00.000Z",
"metadata": { "key": "value" },
"streamName": "api-logs"
}
]
}Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | The log message content. Maximum length: 64KB (65,536 characters). |
level | string | Yes | Log severity level. Must be one of: trace, debug, info, warn, error, fatal. |
ts | string | No | ISO-8601 timestamp in UTC (e.g., "2024-01-15T14:30:00.000Z"). Defaults to current server time if omitted. |
metadata | object | No | Arbitrary JSON object for structured data. Can contain any valid JSON values. |
streamName | string | No | Stream name to associate logs with. Auto-creates stream if it does not exist. Cannot be used with streamId. |
streamId | string | No | Stream ID to associate logs with. Cannot be used with streamName. |
Log Levels
The following log levels are supported, in order of severity:
Limits
Response
{
"success": true,
"data": {
"acceptedCount": 3,
"rejectedCount": 0
}
}Partial Success
If some events fail validation, valid ones are still ingested:
{
"success": true,
"data": {
"acceptedCount": 2,
"rejectedCount": 1,
"errors": [
{
"index": 1,
"reason": "Invalid log level: critical. Must be one of: trace, debug, info, warn, error, fatal"
}
]
}
}Examples
curl -X POST https://your-domain/api/ingest \
-H "Authorization: Bearer lrk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"events": [{
"message": "User logged in",
"level": "info",
"metadata": { "userId": "123" }
}]
}'curl -X POST https://your-domain/api/ingest \
-H "Authorization: Bearer lrk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"events": [
{ "message": "Request started", "level": "debug" },
{ "message": "Processing data", "level": "info", "metadata": { "items": 42 } },
{ "message": "Request completed", "level": "info" }
]
}'async function sendLog(apiKey, message, level, metadata = {}) {
const response = await fetch('/api/ingest', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
events: [{
message,
level,
metadata,
ts: new Date().toISOString(), // Optional: defaults to server time
}],
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Failed to send log');
}
return response.json();
}
// Usage
await sendLog('lrk_your_api_key', 'User signed up', 'info', {
userId: 'user_123',
plan: 'premium',
});import requests
from datetime import datetime, timezone
def send_logs(api_key: str, events: list[dict]) -> dict:
"""Send log events to LogRack."""
response = requests.post(
"https://your-domain/api/ingest",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={"events": events},
)
response.raise_for_status()
return response.json()
# Single log
send_logs("lrk_your_api_key", [{
"message": "Payment processed",
"level": "info",
"metadata": {"amount": 99.99, "currency": "USD"},
}])
# Batch of logs
send_logs("lrk_your_api_key", [
{"message": "Order created", "level": "info"},
{"message": "Inventory checked", "level": "debug"},
{"message": "Payment processed", "level": "info"},
{"message": "Order confirmed", "level": "info"},
])Query API
Query log events with filtering, pagination, and time range selection. Results are returned in pages with cursor-based pagination for efficient navigation through large result sets.
/api/logsquery:readQuery Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
start | string | No | 1 hour ago | Start of time range (inclusive). ISO-8601 format in UTC. |
end | string | No | now | End of time range (exclusive). ISO-8601 format in UTC. |
limit | number | No | 100 | Maximum number of results. Max: 500. |
cursor | string | No | — | Opaque cursor for pagination. Use nextCursor from previous response. |
order | string | No | desc | Sort order by timestamp. Values: "asc" or "desc". |
level | string | No | — | Filter by log level. Can be repeated: level=error&level=warn. |
q | string | No | — | Search query for message field. Case-insensitive substring match. Max: 200 chars. |
metaKey | string | No | — | Filter logs where metadata contains this key. |
metaValue | string | No | — | Filter logs where metadata[metaKey] equals this value. Requires metaKey. |
streamId | string | No | — | Filter logs by stream ID. |
Response Format
{
"success": true,
"data": {
"items": [
{
"id": "cm5abc123def456",
"timestamp": "2024-01-15T14:30:00.000Z",
"ingestedAt": "2024-01-15T14:30:01.234Z",
"level": "error",
"message": "Connection timeout after 30s",
"metadata": {
"service": "api-gateway",
"requestId": "req_789xyz"
}
},
{
"id": "cm5abc123def457",
"timestamp": "2024-01-15T14:29:55.000Z",
"ingestedAt": "2024-01-15T14:29:56.123Z",
"level": "warn",
"message": "High latency detected",
"metadata": {
"latencyMs": 2500
}
}
],
"nextCursor": "eyJ0cyI6IjIwMjQtMDEtMTVUMTQ6Mjk6NTUuMDAwWiIsImlkIjoiY201YWJjMTIzZGVmNDU3In0",
"pageInfo": {
"limit": 50,
"returned": 2
}
}
}Pagination
The API uses cursor-based pagination for efficient navigation. When more results are available, the response includes a nextCursor value. Pass this value as the cursor parameter in your next request to fetch the next page.
When nextCursor is null, you've reached the end of the results. Cursors are opaque strings — don't parse or modify them.
Examples
curl -X GET "https://your-domain/api/logs?limit=50&order=desc" \
-H "Authorization: Bearer lrk_your_api_key"curl -X GET "https://your-domain/api/logs?level=error&level=warn&q=timeout&limit=100" \
-H "Authorization: Bearer lrk_your_api_key"curl -X GET "https://your-domain/api/logs?start=2024-01-15T00:00:00Z&end=2024-01-16T00:00:00Z" \
-H "Authorization: Bearer lrk_your_api_key"curl -X GET "https://your-domain/api/logs?cursor=eyJ0cyI6IjIwMjQtMDEtMTVUMTQ6MzA6MDAuMDAwWiIsImlkIjoiYWJjMTIzIn0" \
-H "Authorization: Bearer lrk_your_api_key"Single Log API
Retrieve a single log entry by its unique ID. Returns a flat response (not wrapped in success/data envelope).
/api/logs/:idquery:readResponse Format
{
"id": "cm5abc123def456",
"timestamp": "2024-01-15T14:30:00.000Z",
"ingestedAt": "2024-01-15T14:30:01.234Z",
"level": "error",
"message": "Connection timeout after 30s",
"metadata": {
"service": "api-gateway",
"requestId": "req_789xyz"
}
}Error Response
{
"error": "NOT_FOUND",
"message": "Log entry not found"
}Example
curl -X GET "https://your-domain/api/logs/cm5abc123def456" \
-H "Authorization: Bearer lrk_your_api_key"Stats API
Get aggregate statistics about your logs, including total count and breakdown by log level.
/api/logs/statsquery:readQuery Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
start | string | No | 1 hour ago | Start of time range. ISO-8601 UTC. |
end | string | No | now | End of time range. ISO-8601 UTC. |
Response Format
{
"success": true,
"data": {
"total": 15234,
"byLevel": {
"debug": 2100,
"info": 10500,
"warn": 1800,
"error": 780,
"fatal": 54
},
"timeRange": {
"start": "2024-01-15T00:00:00.000Z",
"end": "2024-01-16T00:00:00.000Z"
}
}
}Example
curl -X GET "https://your-domain/api/logs/stats?start=2024-01-15T00:00:00Z&end=2024-01-16T00:00:00Z" \
-H "Authorization: Bearer lrk_your_api_key"Tail API
The Tail API enables real-time log streaming via polling. It returns logs newer than a specified timestamp, ordered oldest to newest, making it ideal for live log viewers.
/api/tailquery:readQuery Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
since | string | Yes | — | Fetch logs newer than this timestamp. ISO-8601 format in UTC. Required. |
limit | number | No | 200 | Maximum number of results. Max: 500. |
level | string | No | — | Filter by log level. Can be repeated. |
q | string | No | — | Search query for message field. Case-insensitive. |
metaKey | string | No | — | Filter logs where metadata contains this key. |
metaValue | string | No | — | Filter logs where metadata[metaKey] equals this value. |
streamId | string | No | — | Filter logs by stream ID. |
Response Format
{
"success": true,
"data": {
"items": [
{
"id": "cm5abc123def458",
"timestamp": "2024-01-15T14:31:15.000Z",
"ingestedAt": "2024-01-15T14:31:16.123Z",
"level": "error",
"message": "Database connection failed",
"metadata": {
"database": "postgres",
"retryCount": 3
}
}
],
"nextSince": "2024-01-15T14:31:16.123Z",
"count": 1
}
}Polling Pattern
The Tail API is designed for polling. After each request, use the nextSince value from the response as the since parameter for your next request. This ensures you don't miss any logs and don't receive duplicates.
Poll every 1-2 seconds for responsive real-time updates. The nextSince value is either the timestamp of the most recent log returned, or the current server time if no logs were returned.
async function tailLogs(apiKey, onLogs) {
let since = new Date(Date.now() - 10000).toISOString(); // Start 10s ago
while (true) {
const response = await fetch(`/api/tail?since=${since}`, {
headers: { 'Authorization': `Bearer ${apiKey}` },
});
const { data } = await response.json();
if (data.items.length > 0) {
onLogs(data.items);
}
since = data.nextSince; // Use nextSince for next poll
await new Promise(r => setTimeout(r, 1500)); // Poll every 1.5s
}
}Examples
curl -X GET "https://your-domain/api/tail?since=2024-01-15T14:30:00Z" \
-H "Authorization: Bearer lrk_your_api_key"curl -X GET "https://your-domain/api/tail?since=2024-01-15T14:30:00Z&level=error&streamId=cm5stream123" \
-H "Authorization: Bearer lrk_your_api_key"Streams API
Streams allow you to organize logs into logical groups (e.g., by service, environment, or feature). Each log event can optionally belong to a stream, and you can filter queries and tails by stream.
List Streams
/api/streamsstreams:readcurl -X GET "https://your-domain/api/streams" \
-H "Authorization: Bearer lrk_your_api_key"{
"success": true,
"data": {
"streams": [
{
"id": "cm5stream123abc",
"name": "api-logs",
"description": "API server access logs",
"createdAt": "2024-01-10T09:00:00.000Z",
"updatedAt": "2024-01-10T09:00:00.000Z"
},
{
"id": "cm5stream456def",
"name": "worker-jobs",
"description": "Background job processing logs",
"createdAt": "2024-01-12T11:30:00.000Z",
"updatedAt": "2024-01-14T15:45:00.000Z"
}
],
"total": 2
}
}Create Stream
/api/streamsstreams:write| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique stream name within your tenant. Max 100 characters. |
description | string | No | Human-readable description. |
curl -X POST "https://your-domain/api/streams" \
-H "Authorization: Bearer lrk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "payment-service",
"description": "Payment processing logs"
}'{
"success": true,
"data": {
"id": "cm5stream789ghi",
"name": "payment-service",
"description": "Payment processing logs",
"createdAt": "2024-01-15T14:30:00.000Z",
"updatedAt": "2024-01-15T14:30:00.000Z"
}
}Get Stream
/api/streams/:idstreams:readcurl -X GET "https://your-domain/api/streams/cm5stream123abc" \
-H "Authorization: Bearer lrk_your_api_key"Update Stream
/api/streams/:idstreams:write| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | New stream name (must be unique). |
description | string | null | No | New description. Set to null to remove. |
curl -X PATCH "https://your-domain/api/streams/cm5stream123abc" \
-H "Authorization: Bearer lrk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "api-access-logs",
"description": "Updated: API server access and error logs"
}'{
"success": true,
"data": {
"id": "cm5stream123abc",
"name": "api-access-logs",
"description": "Updated: API server access and error logs",
"createdAt": "2024-01-10T09:00:00.000Z",
"updatedAt": "2024-01-15T14:35:00.000Z"
}
}Delete Stream
/api/streams/:idstreams:writeReturns 204 No Content on success. Log events associated with the deleted stream will have their streamId set to null.
curl -X DELETE "https://your-domain/api/streams/cm5stream123abc" \
-H "Authorization: Bearer lrk_your_api_key"Authentication
All API requests must include an API key in the Authorization header using the Bearer scheme:
Authorization: Bearer lrk_your_api_keyAPI Key Scopes
API keys are scoped to specific operations. Request only the scopes you need:
| Scope | Permissions |
|---|---|
ingest:write | Send log events via POST /api/ingest |
query:read | Query logs, stats, and tail (GET /api/logs, /api/logs/stats, /api/tail) |
streams:read | List and view streams (GET /api/streams) |
streams:write | Create, update, delete streams (POST, PATCH, DELETE /api/streams) |
keys:read | List API keys (GET /api/api-keys) |
keys:write | Create and revoke API keys |
You can create API keys from the API Keys page. API keys are shown only once at creation time — store them securely.
Rate Limiting
API requests are rate limited per API key. When you exceed the rate limit, the API returns a 429 Too Many Requests response.
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when window resets |
Retry-After | Seconds to wait (only on 429 responses) |
Error Codes
All error responses follow a consistent format with an error code and message:
{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid or does not exist"
}
}Common Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | Invalid request body or missing required fields |
400 | BATCH_TOO_LARGE | Batch exceeds maximum size of 1000 events |
400 | INVALID_JSON | Request body is not valid JSON |
400 | MISSING_SINCE | The 'since' parameter is required for tail requests |
401 | MISSING_AUTH | No Authorization header provided |
401 | INVALID_API_KEY | API key is invalid or does not exist |
401 | REVOKED_API_KEY | API key has been revoked |
403 | INSUFFICIENT_SCOPE | API key lacks the required scope for this operation |
404 | NOT_FOUND | Resource not found (log entry, stream, etc.) |
404 | STREAM_NOT_FOUND | Stream with the specified ID does not exist |
409 | DUPLICATE_STREAM_NAME | A stream with this name already exists |
429 | RATE_LIMIT_EXCEEDED | Too many requests; check Retry-After header |
504 | QUERY_TIMEOUT | Query exceeded 30 second timeout |