Appearance
Sending data
Everything you send goes to one host, edge.simpledatalogs.com. This page covers the native endpoint, /v1/ingest, which takes events and log lines in JSON, NDJSON or plain text.
The endpoint
http
POST https://edge.simpledatalogs.com/v1/ingest
Authorization: Bearer <ingest key>
Content-Type: application/json | application/x-ndjson | text/plain
Content-Encoding: gzip (optional)
User-Agent: <your-sdk-or-app>/<version>| Header | Required | Notes |
|---|---|---|
Authorization | Yes | Bearer sdl_pub_… or Bearer sdl_sec_…. The key alone decides the environment and the deployment |
Content-Type | Recommended | Decides how the body is read; see Body shapes. Without one, the body lands in Unparsed |
Content-Encoding | No | Absent, identity, gzip or x-gzip. Anything else is answered 415 |
User-Agent | Recommended | Name your SDK or app and its version, such as my-game/1.4.0. It is kept with every request |
A request is at most 512 KB as sent. A gzip body counts its compressed bytes. Aim for 256 KB or less per request; see Writing a sender.
Body shapes
Ingest stores the body exactly as sent and answers. The parser reads it afterwards, based on the Content-Type.
| Content-Type | Body |
|---|---|
application/json | One event object, a JSON array of event objects, or a batch / events wrapper |
application/x-ndjson | One event object per line |
text/plain | Log lines in any common format, one entry per line, stack traces included |
| Anything else | Stored as is, and shown on the Unparsed tab with its reason |
The same two events, three ways:
sh
curl -X POST https://edge.simpledatalogs.com/v1/ingest \
-H "Authorization: Bearer sdl_sec_…" \
-H "Content-Type: application/json" \
-d '[
{"event": "order_paid", "event_id": "0199f0c4-6c1e-7a3b-9d2e-5b8f1a2c3d4e", "ts": "2026-10-17T09:14:00Z", "props": {"amount": 29}},
{"level": "error", "message": "Card declined", "event_id": "0199f0c4-6c1f-7c55-8e10-2a9b3c4d5e6f", "ts": "2026-10-17T09:14:02Z"}
]'sh
curl -X POST https://edge.simpledatalogs.com/v1/ingest \
-H "Authorization: Bearer sdl_sec_…" \
-H "Content-Type: application/x-ndjson" \
--data-binary $'{"event":"order_paid","event_id":"0199f0c4-6c1e-7a3b-9d2e-5b8f1a2c3d4e","ts":"2026-10-17T09:14:00Z","props":{"amount":29}}\n{"level":"error","message":"Card declined","event_id":"0199f0c4-6c1f-7c55-8e10-2a9b3c4d5e6f","ts":"2026-10-17T09:14:02Z"}'sh
curl -X POST https://edge.simpledatalogs.com/v1/ingest \
-H "Authorization: Bearer sdl_sec_…" \
-H "Content-Type: text/plain" \
--data-binary $'level=info event=order_paid amount=29\n2026-10-17 09:14:02 ERROR Card declined'The fields of each object are on Events and log objects, and the text formats on Text log lines.
Prefer NDJSON for servers
NDJSON with event_id and ts is the most robust shape for batches: one bad line goes to Unparsed alone, and retries never count twice. Plain text lines carry no event_id, so a retried text request counts twice.
Compressing with gzip
Gzip is worth it for anything bigger than a few events. Set Content-Encoding: gzip and send the compressed bytes:
sh
gzip -c events.ndjson | curl -X POST https://edge.simpledatalogs.com/v1/ingest \
-H "Authorization: Bearer sdl_sec_…" \
-H "Content-Type: application/x-ndjson" \
-H "Content-Encoding: gzip" \
--data-binary @-The 512 KB limit applies to the compressed bytes. A body may inflate to at most 8 MB; a larger one, or a corrupt one, goes to Unparsed whole (decompressed_too_large, invalid_gzip).
br, zstd and deflate are refused with 415.
A successful answer
http
HTTP/1.1 202 Accepted
{"accepted":"0199f0c4-7d20-7e8a-b1c2-d3e4f5a6b7c8"}202 is the only success. It is sent only once the body is durably stored. Anything else means it was not stored, so keep the data and retry; see Responses.
Browsers and CORS
Public keys are meant for websites, so ingest answers browsers on every path.
- Every answer, refusals included, carries
Access-Control-Allow-Origin: *and exposesRetry-After. OPTIONSon any path is a preflight. It answers204without a key and allows theAuthorization,Content-TypeandContent-Encodingheaders.- There is no credentials mode: ingest never uses cookies.
js
await fetch('https://edge.simpledatalogs.com/v1/ingest', {
method: 'POST',
headers: { Authorization: 'Bearer sdl_pub_…', 'Content-Type': 'application/json' },
body: JSON.stringify({ event: 'page_view', event_id: crypto.randomUUID(), ts: new Date().toISOString() }),
})Never put a secret key in a web page
Only sdl_pub_… keys belong in browsers and apps. The JavaScript SDK handles batching, retries and page hide for you.
Other paths
Ingest also speaks the wire protocols of tools you may already use, so they switch by changing an endpoint and a key. Each path shares /v1/ingest's rules: the key check, the rate limit, the 512 KB body limit, gzip, and success only once the body is stored.
| Path | For | Guide |
|---|---|---|
POST /v1/logs | OpenTelemetry logs (OTLP/HTTP, protobuf or JSON) | OpenTelemetry |
POST /v1/batch, /v1/track, /v1/page, /v1/screen, /v1/identify, /v1/group, /v1/alias | Segment HTTP Tracking API | Segment |
POST /api/<project>/envelope/, /api/<project>/store/ | Sentry SDKs | Sentry |
POST /api/v2/logs | Datadog logs intake | Datadog logs |
POST /loki/api/v1/push, /api/prom/push, /otlp/v1/logs | Loki push API | Loki |
POST /services/collector[/event][/1.0], /services/collector/raw[/1.0] | Splunk HEC | Splunk HEC |
POST /vercel/v1/logs | Vercel Log Drains, signed with the ingest key | Vercel |
POST /heroku/v1/logs | Heroku HTTPS log drains | Heroku |
POST /v1/hooks/<token> | Webhooks from Stripe, GitHub and other services; the URL token replaces the key | Inbound webhooks |
POST /openrouter/v1/traces | OpenRouter Broadcast traces (OTLP/JSON), one llm_call event per LLM call | OpenRouter |
GET /up | Health check, answers 200 ok | — |
Other OpenTelemetry traces and metrics (/v1/traces, /v1/metrics) are not stored. They always answer 404 with a message naming the exporter setting to turn off.
Next steps
- Events and log objects: every field and its limits.
- Responses: every status code and what to do about it.
- Writing a sender: batching, the outbox and retries.