API
Create keys for programmatic access, then call /v1/public/traces with the x-api-key header.
API keys
Keys start with vk_. The full secret is shown once at creation. Usage is metered per key (default 1,000 requests/month).
API reference
Programmatic access to supply-chain traces. All public endpoints live under https://vein-api-production.up.railway.app/v1/public/traces and require an API key. Interactive Swagger docs are at /docs.
Base URL & headers
| Field | Type | Required | Description |
|---|---|---|---|
Base URL | string | Yes | https://vein-api-production.up.railway.app/v1 — all paths below are relative to this prefix. |
Content-Type | header | Yes | application/json on POST requests. |
x-api-key | header | Yes | Your secret key (vk_…). Also accepted as Authorization: Bearer vk_… |
/public/tracesCreate a trace
Run a root supply-chain decomposition for a product, component, or service.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | What to trace, e.g. "Electric Vehicle" or "NVDA GPU". |
workspaceId | string | No | Optional workspace to assign the trace to. Ignored if the API key is workspace-scoped. |
Example
curl -X POST "https://vein-api-production.up.railway.app/v1/public/traces" \
-H "Content-Type: application/json" \
-H "x-api-key: vk_YOUR_KEY" \
-d '{"query": "Electric Vehicle"}'Response — 201 / 200
Returns a full trace object (same shape as GET). Root expansion can take several seconds.
{
"id": "clx…",
"publicId": "a1b2c3d4",
"query": "Electric Vehicle",
"rootId": "n_1",
"nodes": {
"n_1": {
"id": "n_1",
"name": "Electric Vehicle",
"depth": 0,
"parent": null,
"children": ["n_2", "n_3"],
"expanded": true,
"category": "Product",
"summary": "Battery-electric road vehicle…",
"is_tradeable": false,
"tickers": [],
"criticality": "",
"is_chokepoint": false,
"chokepoint_reason": "",
"sources": ["https://…"]
},
"n_2": {
"id": "n_2",
"name": "Lithium-ion battery pack",
"depth": 1,
"parent": "n_1",
"children": [],
"expanded": false,
"category": "Component",
"summary": "High-voltage traction battery",
"is_tradeable": true,
"tickers": [
{
"symbol": "TSLA",
"name": "Tesla Inc",
"type": "stock",
"trend": "Vertically integrated pack production",
"market_position": "Leading EV OEM"
}
],
"criticality": "high",
"is_chokepoint": false,
"chokepoint_reason": "",
"sources": []
}
},
"userId": "usr_…",
"workspaceId": null,
"createdAt": "2026-06-25T12:00:00.000Z",
"updatedAt": "2026-06-25T12:00:00.000Z"
}/public/traces/:idGet a trace
Fetch a trace you own (or in a workspace your key can access). :id accepts the internal id or shareable publicId.
Example
curl "https://vein-api-production.up.railway.app/v1/public/traces/a1b2c3d4" \ -H "x-api-key: vk_YOUR_KEY"
Response — 200
Same trace object as create. Returns 404 if not found or not accessible.
/public/traces/:id/expandExpand a node
Decompose one unexpanded node one level deeper. Pick nodeId from nodes[nodeId] where expanded is false.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
nodeId | string | Yes | Key from the trace nodes map, e.g. "n_2". |
Example
curl -X POST "https://vein-api-production.up.railway.app/v1/public/traces/clx…/expand" \
-H "Content-Type: application/json" \
-H "x-api-key: vk_YOUR_KEY" \
-d '{"nodeId": "n_2"}'Response — 200
Updated trace with the node marked expanded: true and new child nodes appended. Repeating expand on an already-expanded node returns the trace unchanged.
Node fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | Stable node id within the trace graph. |
name | string | No | Supply-chain node label. |
depth | number | No | 0 = root; increases downstream. |
parent | string | null | No | Parent node id. |
children | string[] | No | Child node ids (may be unexpanded stubs). |
expanded | boolean | No | Whether this node has been expanded. |
category | string | No | e.g. Component, Raw Material, Commodity. |
summary | string | No | One-sentence description. |
is_tradeable | boolean | No | Whether public tickers are relevant. |
tickers | Ticker[] | No | Up to 4 symbols with trend and market_position. |
criticality | high | medium | low | "" | No | How irreplaceable to parent. |
is_chokepoint | boolean | No | Hidden high-impact dependency flag. |
chokepoint_reason | string | No | Set when is_chokepoint is true. |
sources | string[] | No | Optional https URLs used for grounding. |
Errors & limits
| Field | Type | Required | Description |
|---|---|---|---|
401 Unauthorized | — | No | Missing, invalid, or revoked API key. |
403 Forbidden | — | No | Monthly key limit exceeded, plan depth limit, or unavailable plan feature. |
404 Not Found | — | No | Trace or node does not exist, or you lack access. |
429 Too Many Requests | — | No | Rate limit on unauthenticated web endpoints (not API-key routes). |
Each API call increments your key's monthly usage (default 1,000 calls/month). Workspace-scoped keys automatically assign new traces to that workspace.
Typical workflow
# 1. Create root trace
TRACE=$(curl -s -X POST "https://vein-api-production.up.railway.app/v1/public/traces" \
-H "Content-Type: application/json" \
-H "x-api-key: vk_YOUR_KEY" \
-d '{"query": "Data Center"}')
ID=$(echo "$TRACE" | jq -r '.id')
NODE=$(echo "$TRACE" | jq -r '.nodes | to_entries[] | select(.value.expanded == false) | .key' | head -1)
# 2. Expand first unexpanded child
curl -s -X POST "https://vein-api-production.up.railway.app/v1/public/traces/$ID/expand" \
-H "Content-Type: application/json" \
-H "x-api-key: vk_YOUR_KEY" \
-d "{\"nodeId\": \"$NODE\"}" | jq '.nodes | keys | length'