boltZharp Docs
descriptionAPI Reference

Logs, Ingestion & APM

Send logs from anything that can make an HTTP request, search them, manage the log sources your platforms drain into, and read traces, errors and session recordings.

Paths are relative to https://api.zharp.io/api/v1. Sending data uses an X-API-KEY header — an Ingest only key is made for this, though any key works. Searching and everything else uses Authorization: Bearer with a read key, or admin to change log sources. See Authentication.

Sending logs

There's an SDK for JavaScript, Python, Go, PHP & Laravel, Android and Dart/Flutter — they batch, retry and capture crashes for you. Anything else can send the same requests itself.

POST/agent/logs
Scope: ingest only or any key
Send a batch of log lines. Lines are stored within seconds; the answer comes as soon as they're accepted. This is what every Zharp SDK sends.

The key can go in the path instead, for senders that can't set headers: /agent/logs/<key>. Up to 10 MB a request, any number of lines; send X-Zharp-SDK: yourname/1.0 if you're writing a client.

Body (JSON)
logs
object[]required
The lines, each as below. A bare list, or a single line, works too.
logs[].message
stringrequired
The line. Anything else is turned into text; empty becomes (empty message).
logs[].timestamp
string | number
ISO 8601, or epoch seconds or milliseconds. Default: when it arrives.
logs[].service
string
The service it's filed under. Default unlabeled.
logs[].level
string
debug, info (default), warn, error, fatal — or warning, critical, syslog numbers and the like, which map onto those.
logs[].environment
string
Its environment tag — env works too.
logs[].version
string
Its version tag.
logs[].source
string
What wrote it — nginx, postgresql, json… — which picks how it's parsed. See parsed formats.
logs[].metadata
object
Anything else to keep with it. An error's stack here shows as a stack trace.
Request
curl -X POST "https://api.zharp.io/api/v1/agent/logs" \
  -H "X-API-KEY: $ZHARP_INGEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "logs": [
      {
        "timestamp": "2026-10-26T09:12:01.000Z",
        "service": "checkout",
        "level": "error",
        "message": "Payment failed",
        "environment": "production",
        "version": "1.4.2",
        "source": "node",
        "metadata": { "orderId": "A-1042", "stack": "Error: card declined\\n    at charge (pay.js:42)" }
      }
    ]
  }'
Response
{ "statusCode": 200, "message": "Logs ingested successfully" }
Errors
  • 400No lines, or a field of the wrong type (level as a number, say).
  • 401No key, or a key that's invalid, disabled or expired.
  • 413A request over 10 MB — send smaller batches.
  • 429The workspace has sent its log allowance for the month — 1 GB on Free, 30 GB on Pro, 200 GB on Business. errorCode is limitExceeded; logs are accepted again on the 1st.
POST/logs/ingest
Scope: ingest only or any key
The same, as a plain list of lines — what most log shippers can be set up to send. Lines and limits are as above.
Body (JSON)
(body)
object[]required
The lines. { "logs": [...] } works too.
Request
curl -X POST "https://api.zharp.io/api/v1/logs/ingest" \
  -H "X-API-KEY: $ZHARP_INGEST_KEY" \
  -H "Content-Type: application/json" \
  -d '[{ "service": "checkout", "level": "warn", "message": "Slow response from Stripe", "metadata": { "ms": 3200 } }]'
Response
{ "message": "Logs ingested successfully" }
Errors
  • 400No lines, or a field of the wrong type.
  • 401No valid key.
  • 413Over 10 MB.
  • 429The month's log allowance is used up.
info
Your monthly log allowance

What a workspace sends is counted each calendar month (UTC): 1 GB on Free, 30 GB on Pro, 200 GB on Business, unlimited on Enterprise. A batch that crosses the line is still accepted; the next is refused until the 1st. See this month's use with GET /workspaces/:id/usage.

Searching logs

GET/logs
Scope: readPermission: logs:view
Log lines, newest first, filtered any way you combine these. Only lines within the plan's retention are kept: 7 days on Free, 30 on Pro, 90 on Business.

id is a string — the numbers are too large for JavaScript. Page with offset; there's no total. A line's parsed fields are in metadata.parsed, with the parser that read them in metadata.parser.

Query parameters
serviceName
string
A service — repeat it, or comma-separate, for several.
environment
string
An environment tag (env works too). Lists as above.
version
string
A version tag.
source
string
What wrote it: nginx, postgresql…
level
string
One level: error, warn…
search
string
Text in the message, ignoring case.
filter
string
A field condition on what was parsed from the line or its metadata: http.status_code>=500, client.ip=10.0.0.4, error.type:* (has it). Repeat for up to 10, all of which must hold.
from
string (ISO 8601)
From this time — no earlier than the retention allows.
to
string (ISO 8601)
Up to this time.
limit
integer
1–1000. Default 100.
offset
integer
Lines to skip, for paging. Default 0.
Request
curl "https://api.zharp.io/api/v1/logs?serviceName=checkout&level=error&filter=http.status_code%3E%3D500&limit=50" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "Logs retrieved successfully", "data": [ {
    "id": "918273",
    "workspaceId": 3,
    "sourceId": null,
    "serviceName": "checkout",
    "environment": "production",
    "serviceVersion": "1.4.2",
    "source": "node",
    "level": "error",
    "message": "Payment failed",
    "timestamp": "2026-10-26T09:12:01.000Z",
    "metadata": { "orderId": "A-1042", "stack": "…" },
    "createdAt": "2026-10-26T09:12:01.512Z"
  } ] }
Errors
  • 400A value out of range, or a filter that can't be read.
GET/logs/facets
Scope: readPermission: logs:view
What's been logged recently: each service in each environment with its line count, versions and sources, and every environment, version and source seen.
Query parameters
hours
integer
How far back, 1–720. Default 24.
Request
curl "https://api.zharp.io/api/v1/logs/facets?hours=24" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{
  "message": "Log services retrieved",
  "data": {
    "hours": 24,
    "services": [{ "serviceName": "checkout", "environment": "production", "lines": 5120, "lastSeen": "…", "versions": ["1.4.2"], "sources": ["node"] }],
    "environments": ["production", "staging"],
    "versions": ["1.4.2"],
    "sources": ["node", "nginx"]
  }
}
GET/logs/services
Scope: readPermission: logs:view
Every service name the workspace has stored lines for.
Request
curl "https://api.zharp.io/api/v1/logs/services" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "Log services retrieved", "data": ["api", "checkout", "unlabeled"] }

Excluding logs

A drop rule stops the workspace keeping logs it doesn't want — a cluster's system pods, health-check noise. Matching lines are dropped the moment they arrive, from every agent, SDK and log source: they aren't stored, don't reach the live tail and don't count toward the month's allowance. Lines already stored stay until they expire.

GET/logs/drop-rules
Scope: readPermission: logs:view
The workspace's drop rules, oldest first.
Request
curl "https://api.zharp.io/api/v1/logs/drop-rules" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "Log drop rules retrieved", "data": [{ "id": 3, "field": "service", "pattern": "aws-ebs-*", "createdAt": "2026-10-27T09:12:00.000Z" }] }
POST/logs/drop-rules
Scope: adminPermission: workspace:edit
Stop keeping logs that match. Takes effect within 30 seconds.
Body (JSON)
field
stringrequired
service, environment, source or message.
pattern
stringrequired
For a service, environment or source: the whole value, any case, with * matching anything — aws-ebs-*, kube-*. For a message: text it contains, any case (not a regex). Up to 200 characters; a pattern that is only * is refused.
Request
curl -X POST "https://api.zharp.io/api/v1/logs/drop-rules" \
  -H "Authorization: Bearer $ZHARP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "field": "service", "pattern": "aws-ebs-*" }'
Response
{ "message": "Matching logs will no longer be kept", "data": { "id": 3, "field": "service", "pattern": "aws-ebs-*", "createdAt": "…" } }
Errors
  • 400An unknown field, an empty or too-broad pattern, or more than 100 rules.
  • 409The same rule already exists.
DELETE/logs/drop-rules/:id
Scope: adminPermission: workspace:edit
Keep matching logs again, from now on.
Path parameters
id
integerrequired
The rule's id.
Request
curl -X DELETE "https://api.zharp.io/api/v1/logs/drop-rules/3" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "Matching logs will be kept again", "data": null }
Errors
  • 404No such rule in this workspace.

Log sources

A log source is a URL a platform drains its logs into — Vercel, Render, Heroku, Fly.io, Cloudflare, AWS CloudWatch and others. Each has its own token, so you can roll or remove one without touching the rest. See Log Streaming.

providerPlatform
vercel, netlify, render, flyio, railway, heroku, digitaloceanHosting platforms' log drains
cloudflare, supabaseLogpush and Supabase log drains
aws-cloudwatch, gcp-logging, azure-monitorCloud providers' log export
docker-logs, k8s-logs, datadog-forwarderContainers and forwarders
GET/log-sources
Scope: readPermission: logs:view
The workspace's log sources, with whether each is receiving and how fast.

status is waiting (nothing yet), receiving (lines in the last 5 minutes) or idle. token is masked unless the key is admin and its owner isn't Read-only. GET /log-sources/:id adds the last day's line count.

Query parameters
provider
string
Only this provider's.
Request
curl "https://api.zharp.io/api/v1/log-sources" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{
  "message": "Log sources retrieved",
  "data": [{ "id": 4, "provider": "vercel", "name": "Vercel", "token": "zh_src_9f…", "lastReceivedAt": "…", "createdAt": "…", "status": "receiving", "linesPerMin": 12.4 }]
}
POST/log-sources
Scope: adminPermission: monitors:edit
Create a log source and its token. Set the drain URL in your platform to /ingest/<provider>/<token>.
Body (JSON)
provider
stringrequired
One of the providers above.
name
string
Up to 120 characters. Default: the platform's name.
Request
curl -X POST "https://api.zharp.io/api/v1/log-sources" \
  -H "Authorization: Bearer $ZHARP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "vercel",
    "name": "Vercel — production"
  }'
Response
{ "message": "Log source created", "data": { "id": 4, "provider": "vercel", "name": "Vercel — production", "token": "zh_src_9f2c…", "status": "waiting", "linesPerMin": 0, … } }
Errors
  • 400An unknown provider, or a name that's too long.
POST/log-sources/:id/roll
Scope: adminPermission: monitors:edit
Give a source a new token. The old one stops working at once — update the drain URL in your platform.

DELETE /log-sources/:id removes a source; lines it already sent stay.

Path parameters
id
integerrequired
The log source's id.
Request
curl -X POST "https://api.zharp.io/api/v1/log-sources/4/roll" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "Log source token rolled", "data": { "id": 4, "token": "zh_src_51ab…", … } }
Errors
  • 404No such source in this workspace.
POST/ingest/:provider/:token
Scope: token in the URL
Where a platform drains its logs. The provider's own format is read — NDJSON, logplex, Firehose, gzip — so set this URL in the platform; you don't call it yourself.

Up to 5,000 lines a request; more are dropped and counted in truncated.

Path parameters
provider
stringrequired
The source's provider.
token
stringrequired
The source's token. It can also come as Authorization: Bearer, an X-Zharp-Token header or ?token=, for platforms that put it there.
Request
curl -X POST "https://api.zharp.io/api/v1/ingest/vercel/zh_src_9f2c…" \
  -H "Content-Type: application/x-ndjson" \
  --data-binary $'{"message":"GET / 200","projectName":"shop"}\n'
Response
{ "received": 1 }
Errors
  • 401A missing token, or one for another source or provider.
  • 413Over 10 MB.
  • 429The month's log allowance is used up.

Traces and APM

POST/agent/traces
Scope: ingest only or any key
Send spans: OpenTelemetry's OTLP/JSON (resourceSpans), as a collector exports it, or a flat { "spans": [...] } list as the JavaScript APM SDK sends. Up to 5,000 spans a request.

Spans already stored are skipped, so a retried batch isn't doubled. See Tracing Setup.

Body (JSON)
resourceSpans
object[]
OTLP/JSON. service.name, deployment.environment and service.version are read from the resource.
spans
object[]
Or flat: each with traceId, spanId, parentSpanId, name, kind, serviceName, startTime/endTime (ISO) or …UnixNano, statusCode, attributes.
Request
curl -X POST "https://api.zharp.io/api/v1/agent/traces" \
  -H "X-API-KEY: $ZHARP_INGEST_KEY" \
  -H "Content-Type: application/json" \
  -d @otlp-export.json
Response
{ "statusCode": 200, "message": "Traces stored", "data": { "received": 120, "stored": 118 } }
Errors
  • 401No valid key.
  • 413Over 5,000 spans, or over 10 MB.

Reading traces

Every /apm read takes a window — 15m, 1h (default), 6h, 24h or 7d — and needs the monitors:view permission, which every role has.

GET/apm/services
Scope: readPermission: monitors:view
Services that sent spans in the window, busiest first, with their volume, error rate and response times.

errorRate is a percentage.

Query parameters
window
string
15m, 1h (default), 6h, 24h or 7d.
Request
curl "https://api.zharp.io/api/v1/apm/services?window=24h" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{
  "message": "Services retrieved",
  "data": { "window": "24h", "services": [{ "serviceName": "checkout", "spans": 4210, "traces": 380, "errors": 12, "errorRate": 0.3, "avgMs": 41.3, "p95Ms": 180.2, "lastSeen": "…", "environments": ["production"], "versions": ["1.4.2"] }] }
}
GET/apm/operations
Scope: readPermission: monitors:view
A service's operations — GET /orders/:id, say — slowest first.
Query parameters
service
stringrequired
The service.
window
string
15m, 1h (default), 6h, 24h or 7d.
limit
integer
Up to 200. Default 50.
Request
curl "https://api.zharp.io/api/v1/apm/operations?service=checkout" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "…", "data": { "window": "1h", "service": "checkout", "operations": [{ "name": "GET /orders/:id", "calls": 900, "errors": 3, "errorRate": 0.3, "avgMs": 22.1, "p95Ms": 95.4, "maxMs": 812 }] } }
Errors
  • 400No service.
GET/apm/traces
Scope: readPermission: monitors:view
Traces in the window, newest first, each described by its root span.
Query parameters
service
string
Only this service's.
operation
string
Only traces that ran this operation.
errorsOnly
boolean
true for traces with an error anywhere in them.
window
string
15m, 1h (default), 6h, 24h or 7d.
limit
integer
Up to 200. Default 50.
Request
curl "https://api.zharp.io/api/v1/apm/traces?service=checkout&errorsOnly=true" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "…", "data": { "window": "1h", "traces": [{ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736", "name": "POST /checkout", "serviceName": "web", "startTime": "…", "durationMs": 182.4, "statusCode": "ERROR" }] } }
GET/apm/traces/:traceId
Scope: readPermission: monitors:view
One whole trace, every span with its offset from the start — enough to draw the waterfall.
Path parameters
traceId
stringrequired
The trace's id, hex.
Request
curl "https://api.zharp.io/api/v1/apm/traces/4bf92f3577b34da6a3ce929d0e0e4736" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{
  "message": "…",
  "data": {
    "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
    "startTime": "…",
    "durationMs": 182,
    "spanCount": 14,
    "errorCount": 1,
    "services": ["web", "checkout"],
    "spans": [{ "spanId": "00f067aa0ba902b7", "parentSpanId": null, "name": "POST /checkout", "kind": "SERVER", "serviceName": "web", "durationMs": 182, "offsetMs": 0, "statusCode": "OK", "attributes": { … } }, …]
  }
}
Errors
  • 404No such trace in this workspace.
GET/apm/errors
Scope: readPermission: monitors:view
Failing spans grouped by what failed, most frequent first, each with its latest trace.

Session recordings from the browser SDK are listed with GET /apm/replays and fetched whole with GET /apm/replays/:sessionId.

Query parameters
service
string
Only this service's.
window
string
15m, 1h (default), 6h, 24h or 7d.
Request
curl "https://api.zharp.io/api/v1/apm/errors?window=24h" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{ "message": "…", "data": { "window": "24h", "errors": [{ "serviceName": "checkout", "name": "POST /pay", "message": "card_declined", "count": 17, "lastSeen": "…", "latestTraceId": "…" }] } }

Agents

GET/agent/list
Scope: read
Hosts, containers and clusters running the Zharp agent, most recently seen first.
  • status: active (seen in the last 2 minutes), idle, or offline after 15.
  • kind: host, docker, kubernetes or sidecar.
  • GET /agent/databases lists the databases agents report, for private database monitors.
  • PATCH /agent/:id/rename with { "name": "web-prod" } (an admin key) sets its display name; an empty name goes back to the hostname.
Request
curl "https://api.zharp.io/api/v1/agent/list" \
  -H "Authorization: Bearer $ZHARP_API_KEY"
Response
{
  "statusCode": 200,
  "message": "Agents retrieved successfully",
  "data": [{ "id": 3, "hostname": "web-01", "customName": null, "os": "linux", "version": "1.6.2", "kind": "host", "status": "active", "lastSeenAt": "…", "metrics": { … } }]
}