POST /api/v1/traces

Batch ingestion endpoint for traces and spans. The Python SDK calls this automatically — use it directly for custom integrations in any language.

Request

POST /api/v1/traces
Authorization: Bearer ts_...
Content-Type: application/json

{
  "batch": [
    {
      "id": "evt-001",
      "type": "trace-create",
      "body": {
        "id": "trace-abc",
        "name": "support-agent",
        "metadata": {"version": "2.1"},
        "tags": ["production"]
      },
      "timestamp": "2026-03-14T10:00:00Z"
    },
    {
      "id": "evt-002",
      "type": "span-create",
      "body": {
        "trace_id": "trace-abc",
        "parent_span_id": null,
        "name": "retrieve-docs",
        "type": "RETRIEVAL",
        "start_time": "2026-03-14T10:00:00.100Z",
        "end_time": "2026-03-14T10:00:00.350Z",
        "input": {"query": "refund policy"},
        "output": {"docs": ["Refunds are processed within 5 days..."]},
        "model": null,
        "usage": null,
        "cost": null,
        "status": "OK",
        "metadata": {}
      },
      "timestamp": "2026-03-14T10:00:00.350Z"
    }
  ]
}

curl Example

curl -X POST https://api.2signal.dev/api/v1/traces \
  -H "Authorization: Bearer ts_your_api_key" \
  -H "Content-Type: application/json" \
  -d @trace.json

Response (201)

{ "accepted": 2 }

The accepted count reflects how many events were successfully queued for processing.

Event Types

TypeDescriptionRequired Fields
trace-createCreate a new traceid, name
span-createCreate a span within a tracetrace_id, name, type

Trace Fields

FieldTypeRequiredDescription
idstringYesUnique trace ID (UUID recommended)
namestringYesName of the trace (e.g., agent name)
metadataobjectNoArbitrary key-value metadata
tagsstring[]NoTags for filtering

Span Fields

FieldTypeRequiredDescription
trace_idstringYesParent trace ID
parent_span_idstringNoParent span ID (for nesting)
namestringYesSpan name
typestringYesAGENT, LLM, TOOL, RETRIEVAL, CHAIN, CUSTOM
start_timeISO 8601NoWhen the span started
end_timeISO 8601NoWhen the span ended
inputanyNoInput data (JSON-serializable)
outputanyNoOutput data (JSON-serializable)
modelstringNoModel name (for LLM spans)
usageobjectNoToken counts
costnumberNoCost in USD
statusstringNoOK or ERROR (default: OK)
error_messagestringNoError description
metadataobjectNoArbitrary metadata

Nesting Spans

Use parent_span_id to create hierarchical span trees:

{
  "batch": [
    {
      "type": "span-create",
      "body": {
        "trace_id": "trace-1",
        "parent_span_id": null,
        "name": "agent",
        "type": "AGENT"
      }
    },
    {
      "type": "span-create",
      "body": {
        "trace_id": "trace-1",
        "parent_span_id": "span-agent",
        "name": "search",
        "type": "TOOL"
      }
    }
  ]
}

Usage Tracking

For LLM spans, include usage to track tokens and cost:

"usage": {
  "prompt_tokens": 150,
  "completion_tokens": 50,
  "total_tokens": 200
},
"cost": 0.003

Batch Limits

  • Max 1,000 events per batch
  • Max 5 MB total payload
  • Each event must have a valid type (trace-create or span-create)
  • The trace-create event should come before its span-create events in the batch

Error Responses

StatusWhen
400Missing batch field, invalid event type, malformed body
401Invalid or missing API key
413Payload exceeds 5 MB or 1,000 events
429Rate limit exceeded

Processing

Events are processed asynchronously. After accepting a batch, the API persists raw events to S3 and queues them for database writes. This means:

  • A 201 response means the events are queued, not yet in the database
  • Traces typically appear in the dashboard within 1–3 seconds
  • Evaluators run after the trace is written to the database

Have questions? Join our community!

Connect with other developers and the 2Signal team.

Join Discord