HTTP Trace Injection
Send traces to Neatlogs via HTTP without using the SDK. Useful for custom integrations, non-Python/TypeScript environments, or direct API calls.
In addition to SDK-based instrumentation (Python SDK and TypeScript scripts), you can inject traces directly via HTTP API. This is useful when you want to:
- Use Neatlogs from non-Python/non-TypeScript environments
- Integrate with custom frameworks or orchestration tools
- Send traces from external systems or agents that can't use the SDK
- Build lightweight integrations without SDK dependencies
Quick Start
Send a POST request to the Neatlogs batch ingestion endpoint:
curl -X POST https://neatlogs.com/api/v1/traces/v4/batch \
-H "Content-Type: application/json" \
-H "x-api-key: <your-api-key>" \
-d '{
"workflow_name": "my-workflow",
"spans": [
{
"span_id": "sp_123",
"name": "my-operation",
"start_time": 1717555210000,
"end_time": 1717555215000,
"attributes": {
"openinference.span.kind": "WORKFLOW",
"status": "success"
}
}
]
}'Authentication
Include your API key in the request header:
x-api-key: <your-api-key>Get your API key from Settings → API Keys in Neatlogs.
Endpoint
Endpoint: POST /api/v1/traces/v4/batch
Base URL: https://neatlogs.com
Full URL: https://neatlogs.com/api/v1/traces/v4/batch
Request Format
Body
{
"workflow_name": "string (required) - Name of the workflow/operation",
"spans": [
{
"span_id": "string (required) - Unique span identifier",
"parent_span_id": "string (optional) - UUID of parent span",
"name": "string (required) - Span name/operation",
"start_time": "number (required) - Unix timestamp in milliseconds",
"end_time": "number (required) - Unix timestamp in milliseconds",
"attributes": {
"openinference.span.kind": "string (required) - LLM, TOOL, AGENT, WORKFLOW, etc.",
"status": "string (optional) - success or error",
"error_message": "string (optional) - Error details if status is error",
"custom_field": "any (optional) - Custom attributes"
},
"input": "object (optional) - Input data",
"output": "object (optional) - Output data"
}
],
"metrics": [
{
"name": "string - Metric name",
"value": "number - Metric value",
"unit": "string (optional) - Unit (ms, tokens, etc.)"
}
]
}Response
Success (202 Accepted):
{
"status": "accepted",
"batch_id": "batch_123456"
}Error (400/401):
{
"error": "Invalid payload format"
}Span Kinds
The openinference.span.kind attribute determines the span type. Supported kinds:
| Kind | Description | Use Case |
|---|---|---|
WORKFLOW | Top-level workflow/operation | Entry point, agent orchestration |
LLM | Language model call | Claude, GPT, Mistral calls |
TOOL | External tool/API call | Database, API, third-party service |
AGENT | AI agent execution | Agent loop, decision making |
CHAIN | Sequential operation chain | Multi-step workflow |
RETRIEVER | Data retrieval operation | RAG, vector search, database query |
EMBEDDING | Embedding generation | Vector embedding, encoding |
Example: Python
import requests
import time
import uuid
API_KEY = "your-api-key"
BASE_URL = "https://neatlogs.com"
def send_trace(workflow_name, spans):
"""Send a trace to Neatlogs via HTTP."""
response = requests.post(
f"{BASE_URL}/api/v1/traces/v4/batch",
headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
json={
"workflow_name": workflow_name,
"spans": spans,
}
)
if response.status_code == 202:
result = response.json()
print(f"Trace sent: {result['batch_id']}")
else:
print(f"Error: {response.status_code} - {response.text}")
# Example: Send a trace
now_ms = int(time.time() * 1000)
send_trace(
workflow_name="my-agent",
spans=[
{
"span_id": str(uuid.uuid4()),
"name": "process_query",
"start_time": now_ms,
"end_time": now_ms + 5000,
"attributes": {
"openinference.span.kind": "WORKFLOW",
"status": "success"
},
"input": {"query": "What is the weather?"},
"output": {"result": "Sunny, 72°F"}
},
{
"span_id": str(uuid.uuid4()),
"parent_span_id": "parent-uuid",
"name": "call_weather_api",
"start_time": now_ms + 500,
"end_time": now_ms + 3000,
"attributes": {
"openinference.span.kind": "TOOL",
"status": "success"
},
"input": {"location": "San Francisco"},
"output": {"temperature": 72, "condition": "sunny"}
}
]
)Example: JavaScript/TypeScript
async function sendTrace(
workflowName: string,
spans: Array<{
span_id: string;
parent_span_id?: string;
name: string;
start_time: number;
end_time: number;
attributes: Record<string, any>;
input?: any;
output?: any;
}>
) {
const response = await fetch(
"https://neatlogs.com/api/v1/traces/v4/batch",
{
method: "POST",
headers: {
"x-api-key": process.env.NEATLOGS_API_KEY || "",
"Content-Type": "application/json",
},
body: JSON.stringify({
workflow_name: workflowName,
spans: spans,
}),
}
);
if (response.status === 202) {
const result = await response.json();
console.log(`Trace sent: ${result.batch_id}`);
} else {
console.error(`Error: ${response.status} - ${await response.text()}`);
}
}
// Usage
const nowMs = Date.now();
sendTrace("my-agent", [
{
span_id: crypto.randomUUID(),
name: "process_query",
start_time: nowMs,
end_time: nowMs + 5000,
attributes: {
"openinference.span.kind": "WORKFLOW",
status: "success",
},
input: { query: "What is the weather?" },
output: { result: "Sunny, 72°F" },
},
]);Example: cURL
Send a complete trace:
curl -X POST https://neatlogs.com/api/v1/traces/v4/batch \
-H "Content-Type: application/json" \
-H "x-api-key: sk_proj_abc123..." \
-d '{
"workflow_name": "customer-support-agent",
"spans": [
{
"span_id": "sp-root-001",
"name": "handle_customer_request",
"start_time": 1717555210000,
"end_time": 1717555235000,
"attributes": {
"openinference.span.kind": "WORKFLOW",
"status": "success"
},
"input": {
"customer_id": "cust_123",
"question": "How do I reset my password?"
},
"output": {
"answer": "Click 'Forgot Password' on the login page..."
}
},
{
"span_id": "sp-llm-001",
"parent_span_id": "sp-root-001",
"name": "call_llm",
"start_time": 1717555212000,
"end_time": 1717555225000,
"attributes": {
"openinference.span.kind": "LLM",
"status": "success"
},
"input": {
"prompt": "Generate a helpful response about password resets..."
},
"output": {
"response": "Click Forgot Password..."
}
}
]
}'When to Use HTTP vs. SDK
| Method | Best For | Pros | Cons |
|---|---|---|---|
| SDK (Python/TS) | Native applications | Auto-instrumentation, decorators, easy setup | Language-specific |
| HTTP API | Any language/environment | Language-agnostic, lightweight, flexible | Manual span creation |
| MCP Tools | Claude/AI agents | Standard protocol, tool discovery | Requires MCP client |
Timestamp Format
Use Unix milliseconds for start_time and end_time:
// Current time in milliseconds
const nowMs = Date.now();
// Convert from seconds to milliseconds
const unixSeconds = Math.floor(Date.now() / 1000);
const unixMs = unixSeconds * 1000;
// From ISO 8601 string
const iso = "2026-06-05T14:32:10Z";
const isoMs = new Date(iso).getTime();Nested Spans (Parent-Child)
Use parent_span_id to create span hierarchies:
{
"spans": [
{
"span_id": "root-123",
"name": "workflow",
"attributes": { "openinference.span.kind": "WORKFLOW" }
},
{
"span_id": "child-456",
"parent_span_id": "root-123",
"name": "nested_operation",
"attributes": { "openinference.span.kind": "TOOL" }
}
]
}Neatlogs automatically builds the tree structure based on parent-child relationships.
Error Handling
Spans with status error should include error details:
{
"span_id": "sp-error-001",
"name": "api_call",
"attributes": {
"openinference.span.kind": "TOOL",
"status": "error",
"error_message": "Connection timeout after 30s",
"error_type": "TimeoutError"
},
"output": null
}Batch Size & Performance
- Max payload size: 1 MB per request
- Recommended span count: 10–1,000 spans per batch
- Max spans per batch: No hard limit, but keep payload under 1 MB
- Latency: Traces typically appear in Neatlogs within 1-2 seconds
Batch multiple spans in a single request for better performance than sending individual spans.
Comparison with SDK Methods
| Feature | SDK | HTTP API |
|---|---|---|
| Auto-instrumentation | ✓ | ✗ |
| Decorators | ✓ | ✗ |
| Context managers | ✓ | ✗ |
| Manual span creation | ✓ | ✓ |
| Language-agnostic | ✗ | ✓ |
| External systems | ✗ | ✓ |
| Webhooks | ✗ | ✓ |
See Also
- SDK Instrumentation - Decorator-based approach
- Span Kinds - Complete span type reference
- MCP Tools - Tool-based trace management