API Reference
The Glassbrain REST API lets you ingest traces, create replay sessions, request AI fix suggestions, and query usage data programmatically. All endpoints accept and return JSON. This reference covers every available endpoint with request examples in curl, JavaScript, and Python.
Authentication
All API requests require authentication via an API key. Include your key in the x-api-key header of every request.
curl -H "x-api-key: gb_live_your_api_key_here" \
https://glassbrain.dev/api/v1/traces/trc_a1b2c3d4e5f6To find your API key:
- Open your Glassbrain dashboard
- Navigate to your project settings
- Click the "API Keys" tab
- Copy your live or test key
Important: Keep your API key secret. Do not commit it to version control or expose it in client-side code. Use environment variables to store your key. Keys prefixed with gb_live_ have production access. Keys prefixed with gb_test_ are restricted to test environments.
Base URL
All API endpoints are relative to the following base URL:
All requests must use HTTPS. HTTP requests are rejected with a 301 redirect.
POST/api/v1/traces
Ingest a new trace. This is the primary endpoint for sending error, warning, and info events to Glassbrain. The SDK calls this endpoint automatically when auto-capture is enabled.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| project_id | string | Yes | Your project ID |
| type | string | Yes | One of error, warning, or info |
| message | string | Yes | Human-readable event description (max 10,000 characters) |
| stack_trace | string | No | Full stack trace string |
| metadata | object | No | Arbitrary JSON object (max 64 KB) |
| source | string | No | Origin of the trace, e.g. client or server |
| browser | string | No | Browser name and version |
| os | string | No | Operating system name and version |
| url | string | No | URL where the event occurred |
| user_agent | string | No | Raw user agent string |
Examples
curl -X POST https://glassbrain.dev/api/v1/traces \
-H "Content-Type: application/json" \
-H "x-api-key: gb_live_your_api_key_here" \
-d '{
"project_id": "prj_x7y8z9w0",
"type": "error",
"message": "Cannot read properties of undefined (reading map)",
"stack_trace": "TypeError: Cannot read properties of undefined (reading map)\n at UserList (/app/components/UserList.tsx:14:22)",
"metadata": { "component": "UserList" },
"source": "client",
"browser": "Chrome 120.0.6099.109",
"os": "macOS 14.2.1",
"url": "https://myapp.com/dashboard/users"
}'400 font-semibold">const response = 400 font-semibold">await fetch(400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/traces", {
method: 400 font-semibold">class="text-emerald-400">"POST",
headers: {
400 font-semibold">class="text-emerald-400">"Content-Type": 400 font-semibold">class="text-emerald-400">"application/json",
400 font-semibold">class="text-emerald-400">"x-api-key": process.env.GLASSBRAIN_API_KEY!,
},
body: JSON.stringify({
project_id: 400 font-semibold">class="text-emerald-400">"prj_x7y8z9w0",
400 font-semibold">type: 400 font-semibold">class="text-emerald-400">"error",
message: 400 font-semibold">class="text-emerald-400">"Cannot read properties 400 font-semibold">of 400">undefined (reading 'map')",
stack_trace: error.stack,
metadata: { component: 400 font-semibold">class="text-emerald-400">"UserList" },
source: 400 font-semibold">class="text-emerald-400">"client",
browser: navigator.userAgent,
url: window.location.href,
}),
});
400 font-semibold">const data = 400 font-semibold">await response.json();
console.log(data.trace.id); 400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">// 400 font-semibold">class="text-emerald-400">"trc_a1b2c3d4e5f6"400 font-semibold">import requests
400 font-semibold">import os
response = requests.post(
400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/traces",
headers={
400 font-semibold">class="text-emerald-400">"Content-Type": 400 font-semibold">class="text-emerald-400">"application/json",
400 font-semibold">class="text-emerald-400">"x-api-key": os.environ[400 font-semibold">class="text-emerald-400">"GLASSBRAIN_API_KEY"],
},
json={
400 font-semibold">class="text-emerald-400">"project_id": 400 font-semibold">class="text-emerald-400">"prj_x7y8z9w0",
400 font-semibold">class="text-emerald-400">"type": 400 font-semibold">class="text-emerald-400">"error",
400 font-semibold">class="text-emerald-400">"message": 400 font-semibold">class="text-emerald-400">"Cannot read properties of undefined (reading 'map')",
400 font-semibold">class="text-emerald-400">"stack_trace": traceback.format_exc(),
400 font-semibold">class="text-emerald-400">"metadata": {400 font-semibold">class="text-emerald-400">"component": 400 font-semibold">class="text-emerald-400">"UserList"},
400 font-semibold">class="text-emerald-400">"source": 400 font-semibold">class="text-emerald-400">"server",
},
)
data = response.json()
print(data[400 font-semibold">class="text-emerald-400">"trace"][400 font-semibold">class="text-emerald-400">"id"]) 400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic"># 400 font-semibold">class="text-emerald-400">"trc_a1b2c3d4e5f6"Response
Returns 201 Created on success with the full trace object:
{
"trace": {
"id": "trc_a1b2c3d4e5f6",
"project_id": "prj_x7y8z9w0",
"type": "error",
"message": "Cannot read properties of undefined (reading 'map')",
"stack_trace": "TypeError: Cannot read properties of undefined (reading 'map')\n at UserList (/app/components/UserList.tsx:14:22)",
"metadata": { "component": "UserList" },
"source": "client",
"browser": "Chrome 120.0.6099.109",
"os": "macOS 14.2.1",
"url": "https://myapp.com/dashboard/users",
"user_agent": null,
"created_at": "2026-04-03T10:23:45.123Z"
}
}Error Responses
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{ "field": "type", "message": "Must be one of: error, warning, info" },
{ "field": "message", "message": "Required field" }
]
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key. Include a valid key in the x-api-key header."
}
}{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Monthly trace limit reached. Upgrade your plan for higher limits.",
"limit": 1000,
"used": 1000,
"resets_at": "2026-05-01T00:00:00.000Z"
}
}GET/api/v1/traces/:id
Retrieve a single trace by its ID. Returns the full trace object including all metadata.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string | The trace ID, prefixed with trc_ |
Examples
curl https://glassbrain.dev/api/v1/traces/trc_a1b2c3d4e5f6 \
-H "x-api-key: gb_live_your_api_key_here"400 font-semibold">const response = 400 font-semibold">await fetch(
400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/traces/trc_a1b2c3d4e5f6",
{
headers: {
400 font-semibold">class="text-emerald-400">"x-api-key": process.env.GLASSBRAIN_API_KEY!,
},
}
);
400 font-semibold">const data = 400 font-semibold">await response.json();
console.log(data.trace.message);
console.log(data.trace.400 font-semibold">type);400 font-semibold">import requests
400 font-semibold">import os
response = requests.get(
400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/traces/trc_a1b2c3d4e5f6",
headers={
400 font-semibold">class="text-emerald-400">"x-api-key": os.environ[400 font-semibold">class="text-emerald-400">"GLASSBRAIN_API_KEY"],
},
)
data = response.json()
print(data[400 font-semibold">class="text-emerald-400">"trace"][400 font-semibold">class="text-emerald-400">"message"])
print(data[400 font-semibold">class="text-emerald-400">"trace"][400 font-semibold">class="text-emerald-400">"type"])Response
Returns 200 OK with the trace object:
{
"trace": {
"id": "trc_a1b2c3d4e5f6",
"project_id": "prj_x7y8z9w0",
"type": "error",
"message": "Cannot read properties of undefined (reading 'map')",
"stack_trace": "TypeError: Cannot read properties of undefined...",
"metadata": { "component": "UserList" },
"source": "client",
"browser": "Chrome 120.0.6099.109",
"os": "macOS 14.2.1",
"url": "https://myapp.com/dashboard/users",
"user_agent": "Mozilla/5.0 ...",
"created_at": "2026-04-03T10:23:45.123Z"
}
}Error Responses
{
"error": {
"code": "NOT_FOUND",
"message": "Trace not found. Verify the trace ID and ensure it belongs to a project associated with your API key."
}
}POST/api/v1/replay
Create a new replay session for a trace. The session lets you step through the trace execution and optionally modify inputs to test alternative scenarios. See the Time-Travel Replay documentation for concepts and use cases.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| trace_id | string | Yes | The ID of the trace to replay |
| mode | string | Yes | Either snapshot or live |
| modifications | object | No | Object containing input overrides, environment variable overrides, and mock responses |
The modifications object supports the following nested fields:
| Field | Type | Description |
|---|---|---|
| inputs | object | Key-value pairs to override in the request input (e.g., request_body, query_params, headers) |
| env_overrides | object | Environment variable overrides as key-value pairs |
| mock_responses | object | Keyed by span ID, each value replaces that span's response data |
Examples
curl -X POST https://glassbrain.dev/api/v1/replay \
-H "Content-Type: application/json" \
-H "x-api-key: gb_live_your_api_key_here" \
-d '{
"trace_id": "trc_a1b2c3d4e5f6",
"mode": "snapshot",
"modifications": {
"inputs": {
"request_body": {
"user_id": "usr_456",
"include_deleted": false
}
},
"mock_responses": {
"span_db_query_001": {
"rows": [{ "id": 1, "name": "Alice", "active": true }]
}
}
}
}'400 font-semibold">const response = 400 font-semibold">await fetch(400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/replay", {
method: 400 font-semibold">class="text-emerald-400">"POST",
headers: {
400 font-semibold">class="text-emerald-400">"Content-Type": 400 font-semibold">class="text-emerald-400">"application/json",
400 font-semibold">class="text-emerald-400">"x-api-key": process.env.GLASSBRAIN_API_KEY!,
},
body: JSON.stringify({
trace_id: 400 font-semibold">class="text-emerald-400">"trc_a1b2c3d4e5f6",
mode: 400 font-semibold">class="text-emerald-400">"snapshot",
modifications: {
inputs: {
request_body: { user_id: 400 font-semibold">class="text-emerald-400">"usr_456" },
},
},
}),
});
400 font-semibold">const data = 400 font-semibold">await response.json();
console.log(data.replay.id); 400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">// 400 font-semibold">class="text-emerald-400">"rpl_f1e2d3c4b5a6"
console.log(data.replay.total_steps); 400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">// 8400 font-semibold">import requests
400 font-semibold">import os
response = requests.post(
400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/replay",
headers={
400 font-semibold">class="text-emerald-400">"Content-Type": 400 font-semibold">class="text-emerald-400">"application/json",
400 font-semibold">class="text-emerald-400">"x-api-key": os.environ[400 font-semibold">class="text-emerald-400">"GLASSBRAIN_API_KEY"],
},
json={
400 font-semibold">class="text-emerald-400">"trace_id": 400 font-semibold">class="text-emerald-400">"trc_a1b2c3d4e5f6",
400 font-semibold">class="text-emerald-400">"mode": 400 font-semibold">class="text-emerald-400">"snapshot",
400 font-semibold">class="text-emerald-400">"modifications": {
400 font-semibold">class="text-emerald-400">"inputs": {
400 font-semibold">class="text-emerald-400">"request_body": {400 font-semibold">class="text-emerald-400">"user_id": 400 font-semibold">class="text-emerald-400">"usr_456"},
},
},
},
)
data = response.json()
print(data[400 font-semibold">class="text-emerald-400">"replay"][400 font-semibold">class="text-emerald-400">"id"]) 400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic"># 400 font-semibold">class="text-emerald-400">"rpl_f1e2d3c4b5a6"
print(data[400 font-semibold">class="text-emerald-400">"replay"][400 font-semibold">class="text-emerald-400">"total_steps"]) 400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic"># 8Response
Returns 201 Created with the replay session:
{
"replay": {
"id": "rpl_f1e2d3c4b5a6",
"trace_id": "trc_a1b2c3d4e5f6",
"mode": "snapshot",
"status": "ready",
"total_steps": 8,
"current_step": 0,
"modifications_applied": 2,
"created_at": "2026-04-03T14:30:00.000Z",
"expires_at": "2026-04-03T15:30:00.000Z"
}
}Error Responses
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{ "field": "mode", "message": "Must be one of: snapshot, live" }
]
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Trace not found. Cannot create a replay for a non-existent trace."
}
}POST/api/v1/suggestions
Request AI-powered fix suggestions for a trace. The AI engine analyzes the trace's error message, stack trace, and metadata to generate one or more code fix suggestions with diffs and confidence scores. See AI Fix Suggestions for details on how the engine works.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| trace_id | string | Yes | The ID of the trace to analyze |
| max_suggestions | number | No | Maximum number of suggestions to return (default: 3, max: 5) |
Examples
curl -X POST https://glassbrain.dev/api/v1/suggestions \
-H "Content-Type: application/json" \
-H "x-api-key: gb_live_your_api_key_here" \
-d '{
"trace_id": "trc_a1b2c3d4e5f6",
"max_suggestions": 3
}'400 font-semibold">const response = 400 font-semibold">await fetch(400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/suggestions", {
method: 400 font-semibold">class="text-emerald-400">"POST",
headers: {
400 font-semibold">class="text-emerald-400">"Content-Type": 400 font-semibold">class="text-emerald-400">"application/json",
400 font-semibold">class="text-emerald-400">"x-api-key": process.env.GLASSBRAIN_API_KEY!,
},
body: JSON.stringify({
trace_id: 400 font-semibold">class="text-emerald-400">"trc_a1b2c3d4e5f6",
max_suggestions: 3,
}),
});
400 font-semibold">const data = 400 font-semibold">await response.json();
400 font-semibold">for (400 font-semibold">const suggestion 400 font-semibold">of data.suggestions) {
console.log(suggestion.title);
console.log(400 font-semibold">class="text-emerald-400">"Confidence:", suggestion.confidence);
console.log(400 font-semibold">class="text-emerald-400">"File:", suggestion.file_path);
console.log(400 font-semibold">class="text-emerald-400">"---");
}400 font-semibold">import requests
400 font-semibold">import os
response = requests.post(
400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/suggestions",
headers={
400 font-semibold">class="text-emerald-400">"Content-Type": 400 font-semibold">class="text-emerald-400">"application/json",
400 font-semibold">class="text-emerald-400">"x-api-key": os.environ[400 font-semibold">class="text-emerald-400">"GLASSBRAIN_API_KEY"],
},
json={
400 font-semibold">class="text-emerald-400">"trace_id": 400 font-semibold">class="text-emerald-400">"trc_a1b2c3d4e5f6",
400 font-semibold">class="text-emerald-400">"max_suggestions": 3,
},
)
data = response.json()
400 font-semibold">for suggestion 400 font-semibold">in data[400 font-semibold">class="text-emerald-400">"suggestions"]:
print(suggestion[400 font-semibold">class="text-emerald-400">"title"])
print(f400 font-semibold">class="text-emerald-400">"Confidence: {suggestion['confidence']}")
print(f400 font-semibold">class="text-emerald-400">"File: {suggestion['file_path']}")
print(400 font-semibold">class="text-emerald-400">"---")Response
Returns 200 OK with an array of suggestions sorted by confidence (highest first):
{
"suggestions": [
{
"id": "sug_m1n2o3p4q5r6",
"trace_id": "trc_a1b2c3d4e5f6",
"title": "Add null check before mapping users array",
"description": "The error occurs because the 'users' prop is null when the component renders. The .map() method is called on a null value, which throws a TypeError. Adding a nullish coalescing operator with a fallback to an empty array prevents the error.",
"code_diff": "--- a/components/UserList.tsx\n+++ b/components/UserList.tsx\n@@ -12,7 +12,7 @@\n export function UserList({ users }: UserListProps) {\n return (\n <ul>\n- {users.map((user) => (\n+ {(users ?? []).map((user) => (\n <li key={user.id}>{user.name}</li>\n ))}\n </ul>",
"confidence": 0.94,
"file_path": "components/UserList.tsx",
"line_start": 15,
"line_end": 15,
"created_at": "2026-04-03T10:25:12.000Z"
},
{
"id": "sug_s7t8u9v0w1x2",
"trace_id": "trc_a1b2c3d4e5f6",
"title": "Add default prop value for users",
"description": "Set a default value for the users prop in the component definition so it is never undefined when accessed.",
"code_diff": "--- a/components/UserList.tsx\n+++ b/components/UserList.tsx\n@@ -10,7 +10,7 @@\n-export function UserList({ users }: UserListProps) {\n+export function UserList({ users = [] }: UserListProps) {\n return (",
"confidence": 0.87,
"file_path": "components/UserList.tsx",
"line_start": 11,
"line_end": 11,
"created_at": "2026-04-03T10:25:12.000Z"
}
],
"trace_id": "trc_a1b2c3d4e5f6",
"generated_at": "2026-04-03T10:25:12.000Z"
}Error Responses
{
"error": {
"code": "FORBIDDEN",
"message": "Suggestions are only available for error-type traces. This trace has type 'info'."
}
}{
"error": {
"code": "SUGGESTION_LIMIT_EXCEEDED",
"message": "Monthly suggestion limit reached (10/10). Upgrade to Pro for unlimited suggestions.",
"limit": 10,
"used": 10,
"resets_at": "2026-05-01T00:00:00.000Z"
}
}GET/api/v1/projects/:id/usage
Retrieve usage statistics for a project. Returns current period counts for traces, suggestions, and replays along with plan limits.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string | The project ID, prefixed with prj_ |
Examples
curl https://glassbrain.dev/api/v1/projects/prj_x7y8z9w0/usage \
-H "x-api-key: gb_live_your_api_key_here"400 font-semibold">const response = 400 font-semibold">await fetch(
400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/projects/prj_x7y8z9w0/usage",
{
headers: {
400 font-semibold">class="text-emerald-400">"x-api-key": process.env.GLASSBRAIN_API_KEY!,
},
}
);
400 font-semibold">const data = 400 font-semibold">await response.json();
console.log(400 font-semibold">class="text-emerald-400">"Traces used:", data.usage.traces.used);
console.log(400 font-semibold">class="text-emerald-400">"Traces limit:", data.usage.traces.limit);400 font-semibold">import requests
400 font-semibold">import os
response = requests.get(
400 font-semibold">class="text-emerald-400">"https:400 font-semibold">class="text-[rgba(255,255,255,0.3)] italic">//glassbrain.dev/api/v1/projects/prj_x7y8z9w0/usage",
headers={
400 font-semibold">class="text-emerald-400">"x-api-key": os.environ[400 font-semibold">class="text-emerald-400">"GLASSBRAIN_API_KEY"],
},
)
data = response.json()
print(f400 font-semibold">class="text-emerald-400">"Traces: {data['usage']['traces']['used']}/{data['usage']['traces']['limit']}")
print(f400 font-semibold">class="text-emerald-400">"Suggestions: {data['usage']['suggestions']['used']}/{data['usage']['suggestions']['limit']}")Response
Returns 200 OK with usage data:
{
"usage": {
"project_id": "prj_x7y8z9w0",
"plan": "pro",
"period_start": "2026-04-01T00:00:00.000Z",
"period_end": "2026-05-01T00:00:00.000Z",
"traces": {
"used": 12453,
"limit": 50000
},
"suggestions": {
"used": 87,
"limit": -1
},
"diff_views": {
"used": 87,
"limit": -1
},
"replays": {
"used": 34,
"limit": -1
}
}
}A limit of -1 indicates unlimited usage for that feature on the current plan.
Rate Limits
API rate limits are enforced per project on a monthly billing cycle. When you exceed your plan's limit, the API returns a 429 Too Many Requests response. Limits reset on the first day of each calendar month.
| Plan | Traces / Month | Suggestions / Month | Diff Views / Month |
|---|---|---|---|
| Free | 1,000 | 10 | 5 |
| Pro | 50,000 | Unlimited | Unlimited |
| Team | 200,000 | Unlimited | Unlimited |
| Business | 500,000 | Unlimited | Unlimited |
In addition to monthly limits, the API enforces a per-second rate limit of 100 requests per second across all endpoints. This protects the service from burst traffic. If you exceed this limit, you will receive a 429 response with a Retry-After header indicating how many seconds to wait before retrying.
Error Codes
All error responses follow a consistent format with an error object containing a machine-readable code and a human-readable message:
{
"error": {
"code": "ERROR_CODE",
"message": "A human-readable description of what went wrong."
}
}| HTTP Status | Error Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | The request body is malformed or missing required fields. The response includes a details array listing each invalid field and the specific validation error. |
401 | UNAUTHORIZED | The x-api-key header is missing or contains an invalid API key. Verify your key in your project settings. |
403 | FORBIDDEN | Your API key is valid but does not have permission for the requested operation. This occurs when accessing a resource that belongs to a different project, or when requesting a feature not available on your plan. |
404 | NOT_FOUND | The requested resource does not exist. Verify the ID in the URL path and ensure the resource belongs to a project associated with your API key. |
429 | RATE_LIMIT_EXCEEDED | You have exceeded your plan's monthly limit or the per-second rate limit. The response includes limit, used, and resets_at fields. Check the Retry-After header for per-second limits. |
500 | INTERNAL_ERROR | An unexpected error occurred on the Glassbrain server. These errors are automatically reported and investigated. If you encounter persistent 500 errors, contact support at support@glassbrain.dev. |