cURL and HTTP API Examples
Use cURL to verify an API key, reproduce an SDK request, automate a server-side script, or test failure handling before adding instrumentation to an application.
Configure the key
Export the key in a shell that does not record secret values:
export TELEMETRY_API_KEY="YOUR_API_KEY"
Use a write-scoped key for ingestion and a separate read-scoped key for queries or reports. Disable shell tracing around commands that expand the key.
Send one event
curl --fail-with-body \
--connect-timeout 2 \
--max-time 10 \
--request POST \
"https://api.telemetry.sh/log" \
--header "Authorization: ${TELEMETRY_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<'JSON'
{
"table": "api_request_completed",
"data": {
"event_id": "evt_request_101",
"route_template": "/api/projects/:id",
"method": "GET",
"status_code": 200,
"status": "success",
"latency_ms": 184
}
}
JSON
Telemetry adds timestamp_utc. Do not send API keys, authorization headers, cookies, raw request bodies, prompts, or private customer content as event fields.
Send a compatible batch
The data field can be an array:
curl --fail-with-body \
--connect-timeout 2 \
--max-time 10 \
--request POST \
"https://api.telemetry.sh/log" \
--header "Authorization: ${TELEMETRY_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<'JSON'
{
"table": "job_completed",
"data": [
{
"event_id": "evt_job_101",
"job_name": "invoice_sync",
"status": "success",
"duration_ms": 912
},
{
"event_id": "evt_job_102",
"job_name": "invoice_sync",
"status": "failed",
"duration_ms": 2401,
"error_type": "provider_timeout"
}
]
}
JSON
Keep every object compatible with the same table schema. A batch reduces request overhead but increases the number of events affected by one failed request.
Run SQL
curl --fail-with-body \
--connect-timeout 2 \
--max-time 30 \
--request POST \
"https://api.telemetry.sh/query" \
--header "Authorization: ${TELEMETRY_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<'JSON'
{
"query": "SELECT route_template, COUNT(*) AS requests, ROUND(AVG(latency_ms), 0) AS avg_latency_ms FROM api_request_completed WHERE timestamp_utc >= now() - INTERVAL '24 hours' GROUP BY route_template ORDER BY requests DESC;"
}
JSON
Check the status, data, and key_order fields instead of assuming a non-empty result.
Export a larger result
Start an asynchronous Parquet export:
curl --fail-with-body \
--connect-timeout 2 \
--max-time 30 \
--request POST \
"https://api.telemetry.sh/query/async" \
--header "Authorization: ${TELEMETRY_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<'JSON'
{
"query": "SELECT * FROM api_request_completed WHERE timestamp_utc >= now() - INTERVAL '30 days' ORDER BY timestamp_utc DESC;",
"format": "parquet"
}
JSON
Poll the returned status_url with the same authorization header. When query_status is completed, download the download_url. Stop polling on failed and enforce an overall deadline.
Inspect and verify the table
curl --fail-with-body \
--connect-timeout 2 \
--max-time 10 \
"https://api.telemetry.sh/tables/api_request_completed/schema" \
--header "Authorization: ${TELEMETRY_API_KEY}"
Send synthetic success, failure, retry, and timeout cases. Confirm field types, units, UTC timestamps, and the absence of sensitive fields before creating a dashboard or alert.
Retry and exit-code policy
--fail-with-body exits nonzero for HTTP failures while preserving the response body for safe diagnostics.
- Exit
6: DNS resolution failed. - Exit
7: connection failed. - Exit
28: timeout; the server may or may not have accepted the request. - HTTP
400: fix the request; do not retry unchanged. - HTTP
401or403: replace the credential or correct its scope. - HTTP
429,502,503, or504: retry with bounded exponential backoff and jitter.
Reuse the same event_id for a logical event. Do not use --retry-all-errors without accounting for duplicate delivery.
See the Log API, Query API, Tables API, and event delivery guide.