BETA
Skip to content

Ingest

Send log entries to Krafter. These endpoints use a project ingest token for authentication, not your team API key.

Base URL: https://app.krafter.dev/api/v1

Authentication

Ingest endpoints require a project ingest token (Authorization: Bearer <ingest_token>), not a team API key. Ingest tokens are generated when you create a project and can be rotated at any time.

Rate limit (log_ingest)

Both POST /logs/ingest and POST /logs/ingest/batch use the dedicated log_ingest bucket300 requests / 60 seconds per project ingest token. The bucket counts HTTP requests, not log entries: a single batch of 1,000 NDJSON lines is one request. To exceed 300 entries / minute, switch from single-entry posting to batch ingest.

Single Log Entry

Send a single log entry as JSON.

POST /logs/ingest

Authentication: Project ingest token

Content-Type: application/json

Request Body

FieldTypeRequiredDescription
messagestringYesThe log message text.
levelstringNoLog severity: debug, info, warn, error, or fatal. Defaults to "info".
streamstringNoLogical grouping for the log entry (e.g., "backend", "auth"). Auto-creates the stream if it does not exist.
servicestringNoName of the service that produced the log.
trace_idstringNoDistributed trace identifier.
span_idstringNoSpan identifier within a trace.
metadataobjectNoArbitrary key-value pairs for structured data.

Example Request

bash
curl -X POST https://app.krafter.dev/api/v1/logs/ingest \
  -H "Authorization: Bearer krl_live_kp7tx2bm4qrs6vwy3jnh5zfd7acmeg2j" \
  -H "Content-Type: application/json" \
  -d '{
    "level": "error",
    "message": "Failed to process payment",
    "stream": "payments",
    "service": "billing-service",
    "trace_id": "tr_abc123",
    "span_id": "sp_456",
    "metadata": {
      "order_id": "ord_789",
      "amount": 49.99,
      "currency": "EUR"
    }
  }'

Example Response

json
// 202 Accepted
{
  "ok": true
}

Minimal Example

Only the message field is required. All other fields are optional:

bash
curl -X POST https://app.krafter.dev/api/v1/logs/ingest \
  -H "Authorization: Bearer krl_live_kp7tx2bm4qrs6vwy3jnh5zfd7acmeg2j" \
  -H "Content-Type: application/json" \
  -d '{"message": "Application started"}'

Responses

StatusBodyWhen
202{"ok": true}Entry accepted and persisted.
422{"error": "message is required"}message field missing or empty.
422{"error": "level must be one of: debug, info, warn, error, fatal"}level was supplied but is not one of the five allowed values.
422{"error": "Stream limit exceeded for this project (max 10000 streams)"}The entry references a stream name that does not yet exist and the project has already reached the per-project cap. Existing streams continue to accept entries.
429{"error": "Daily log ingestion limit exceeded"}The project's daily_limit_mb has been reached for today.
429{"error": "Rate limit exceeded", "retry_after": <seconds>}The log_ingest bucket cap (300 req / 60s) was hit. Includes a retry-after header.
503{"error": "Log storage temporarily unavailable"}ClickHouse insert failed. Safe to retry after a brief backoff.

Stream cardinality

Each project may auto-create up to 10,000 distinct stream names. The cap exists to stop accidental high-cardinality usage (e.g. embedding request IDs into the stream field) from bloating dashboards. Stream names are intended to be logical groupings like "backend", "worker", "auth" — not per-request identifiers. Use the metadata object for high-cardinality data instead.


Batch Ingest

Send multiple log entries in a single request using NDJSON (Newline-Delimited JSON) format. Each line is a separate JSON object representing one log entry.

POST /logs/ingest/batch

Authentication: Project ingest token

Content-Type: application/x-ndjson

Maximum request size: 5 MB

Request Body

One JSON object per line. Each object follows the same schema as the single log entry. Lines are separated by newline characters (\n). Do not use a trailing newline or wrapping array.

Example Request

bash
curl -X POST https://app.krafter.dev/api/v1/logs/ingest/batch \
  -H "Authorization: Bearer krl_live_kp7tx2bm4qrs6vwy3jnh5zfd7acmeg2j" \
  -H "Content-Type: application/x-ndjson" \
  -d '{"level":"info","message":"Request received","stream":"backend","service":"api-server","trace_id":"tr_001"}
{"level":"info","message":"Authenticated user usr_42","stream":"backend","service":"api-server","trace_id":"tr_001"}
{"level":"info","message":"Query executed in 12ms","stream":"backend","service":"api-server","trace_id":"tr_001"}
{"level":"info","message":"Response sent 200 OK","stream":"backend","service":"api-server","trace_id":"tr_001"}'

Example Response

json
// 202 Accepted
{
  "ok": true,
  "count": 4,
  "dropped": 0
}

The count field confirms how many log entries were accepted from the batch. The dropped field reports how many lines were rejected (malformed JSON or schema validation failures) — count + dropped always equals the number of non-empty lines you sent (capped at 10,000).

Best-effort delivery

The batch endpoint is best-effort: lines that fail to JSON-decode or fail validation (missing message, invalid level) are dropped without a per-line error. The dropped field in the response tells you how many were rejected, but not which ones or why.

If dropped > 0, inspect your batch payload for malformed JSON or invalid field values. If you need per-entry delivery confirmation, use the single-entry endpoint instead — it returns a 422 per failing entry so you can react to each one.

Responses

StatusBodyWhen
202{"ok": true, "count": N, "dropped": D}Batch accepted. N is the number of successfully validated and persisted entries; D is the number of lines that failed JSON decoding or schema validation. N + D equals the number of non-empty lines you sent (capped at 10,000).
202{"ok": true, "count": 0, "dropped": D}Either the body was empty (D = 0) or every line failed JSON decoding / validation (D > 0). The request itself succeeded — nothing was persisted.
413{"error": "Payload too large (max 5MB)"}Request body exceeded 5 MB.
422{"error": "Stream limit exceeded for this project (max 10000 streams)"}The batch references one or more stream names that do not yet exist and the project has already reached the per-project cap. The whole batch is rejected — split it so that only existing stream names remain.
429{"error": "Daily log ingestion limit exceeded"}The project's daily_limit_mb has been reached for today.
429{"error": "Rate limit exceeded", "retry_after": <seconds>}The log_ingest bucket cap (300 req / 60s) was hit. Includes a retry-after header.
503{"error": "Log storage temporarily unavailable"}ClickHouse insert failed. Safe to retry after a brief backoff.

NDJSON Format

NDJSON (Newline-Delimited JSON) is a format where each line is a standalone JSON object:

{"level":"info","message":"First log entry","stream":"app"}
{"level":"warn","message":"Second log entry","stream":"app"}
{"level":"error","message":"Third log entry","stream":"app"}

Rules:

  • Each line must be valid JSON on its own
  • Lines are separated by \n (newline)
  • No commas between lines
  • No wrapping array or object
  • No trailing newline required

TIP

For high-throughput applications, buffer log entries and send them in batches of 100--1,000 entries. This reduces HTTP overhead significantly compared to sending entries one at a time.

Built by Krafter Studio