GET /api/groups
curl --request GET \
--url https://vij.example.com/api/groupsimport requests
url = "https://vij.example.com/api/groups"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://vij.example.com/api/groups', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://vij.example.com/api/groups",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://vij.example.com/api/groups"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://vij.example.com/api/groups")
.asString();require 'uri'
require 'net/http'
url = URI("https://vij.example.com/api/groups")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"groups": [
{
"group": "abc123def456",
"message": "TypeError: Cannot read property 'map' of undefined",
"name": "TypeError",
"stack": "TypeError: Cannot read property 'map' of undefined\n at processData (app.js:123:45)\n at renderList (components/List.tsx:67:12)",
"count": 2547,
"severity": "error",
"firstSeen": "2024-01-01T10:00:00Z",
"lastSeen": "2024-01-03T14:30:00Z",
"affectedApps": ["frontend", "mobile-app"],
"affectedEnvironments": ["production"],
"affectedUsers": 456,
"recentOccurrences": [
"65a1b2c3d4e5f6g7h8i9j0k1",
"65a1b2c3d4e5f6g7h8i9j0k2",
"65a1b2c3d4e5f6g7h8i9j0k3"
]
},
{
"group": "def456ghi789",
"message": "Payment processing failed",
"name": "Error",
"stack": "Error: Payment processing failed\n at processPayment (payment.js:45:12)\n at handleCheckout (checkout.js:123:5)",
"count": 1234,
"severity": "error",
"firstSeen": "2024-01-02T08:00:00Z",
"lastSeen": "2024-01-03T15:00:00Z",
"affectedApps": ["frontend"],
"affectedEnvironments": ["production", "staging"],
"affectedUsers": 234,
"recentOccurrences": [
"65a1b2c3d4e5f6g7h8i9j0k4",
"65a1b2c3d4e5f6g7h8i9j0k5"
]
}
],
"total": 347,
"page": 1,
"limit": 50,
"pages": 7
}
Analytics API
GET /api/groups
Retrieve grouped errors with occurrence counts
GET
/
api
/
groups
GET /api/groups
curl --request GET \
--url https://vij.example.com/api/groupsimport requests
url = "https://vij.example.com/api/groups"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://vij.example.com/api/groups', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://vij.example.com/api/groups",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://vij.example.com/api/groups"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://vij.example.com/api/groups")
.asString();require 'uri'
require 'net/http'
url = URI("https://vij.example.com/api/groups")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"groups": [
{
"group": "abc123def456",
"message": "TypeError: Cannot read property 'map' of undefined",
"name": "TypeError",
"stack": "TypeError: Cannot read property 'map' of undefined\n at processData (app.js:123:45)\n at renderList (components/List.tsx:67:12)",
"count": 2547,
"severity": "error",
"firstSeen": "2024-01-01T10:00:00Z",
"lastSeen": "2024-01-03T14:30:00Z",
"affectedApps": ["frontend", "mobile-app"],
"affectedEnvironments": ["production"],
"affectedUsers": 456,
"recentOccurrences": [
"65a1b2c3d4e5f6g7h8i9j0k1",
"65a1b2c3d4e5f6g7h8i9j0k2",
"65a1b2c3d4e5f6g7h8i9j0k3"
]
},
{
"group": "def456ghi789",
"message": "Payment processing failed",
"name": "Error",
"stack": "Error: Payment processing failed\n at processPayment (payment.js:45:12)\n at handleCheckout (checkout.js:123:5)",
"count": 1234,
"severity": "error",
"firstSeen": "2024-01-02T08:00:00Z",
"lastSeen": "2024-01-03T15:00:00Z",
"affectedApps": ["frontend"],
"affectedEnvironments": ["production", "staging"],
"affectedUsers": 234,
"recentOccurrences": [
"65a1b2c3d4e5f6g7h8i9j0k4",
"65a1b2c3d4e5f6g7h8i9j0k5"
]
}
],
"total": 347,
"page": 1,
"limit": 50,
"pages": 7
}
The GET
Use Case: Find the most impactful errors to fix first.
Use Case: Monitor active issues and recent deployments.
Use Case: Identify new bugs introduced recently.
Use Case: Prioritize critical errors over warnings.
Response: All individual error logs in that group.
/api/groups endpoint retrieves deduplicated error groups, showing unique errors with their occurrence counts and metadata.
Query Parameters
string
Filter groups by environment.Example:
/api/groups?environment=productionstring
Filter groups by application ID.Example:
/api/groups?appId=my-frontend-appstring
Filter by highest severity in the group.Values:
error, warning, infoExample: /api/groups?severity=errorstring
Filter groups with occurrences after this date (ISO 8601).Example:
/api/groups?startDate=2024-01-01T00:00:00Zstring
Filter groups with occurrences before this date (ISO 8601).Example:
/api/groups?endDate=2024-01-31T23:59:59Znumber
Filter groups with at least this many occurrences.Example:
/api/groups?minCount=10number
default:"1"
Page number for pagination (1-indexed).Example:
/api/groups?page=2number
default:"50"
Number of results per page (max: 100).Example:
/api/groups?limit=100string
default:"count"
Field to sort by.Values:
count, lastSeen, firstSeen, severityExample: /api/groups?sortBy=lastSeenstring
default:"desc"
Sort order.Values:
asc (ascending), desc (descending)Example: /api/groups?sortOrder=ascResponse Fields
array
Array of error group objects.
Show Group object properties
Show Group object properties
string
Unique fingerprint hash identifying this error group.
string
Representative error message from the group.
string
Error name/type (e.g.,
TypeError, ReferenceError).string
Representative stack trace from the group.
number
Total number of occurrences in this group.
string
Highest severity level in the group:
error, warning, or info.string
ISO 8601 timestamp of the first occurrence.
string
ISO 8601 timestamp of the most recent occurrence.
array
List of application IDs where this error occurred.
["frontend", "mobile-app"]
array
List of environments where this error occurred.
["production", "staging"]
number
Number of unique users affected (if userId in metadata).
array
Sample of recent log IDs from this group (last 5).
["65a1b2c3d4e5f6g7h8i9j0k1", "65a1b2c3d4e5f6g7h8i9j0k2"]
number
Total number of groups matching the query.
number
Current page number.
number
Results per page.
number
Total number of pages.
Example Requests
curl "https://vij.example.com/api/groups"
curl "https://vij.example.com/api/groups?environment=production&severity=error"
curl "https://vij.example.com/api/groups?minCount=100&sortBy=count&sortOrder=desc"
curl "https://vij.example.com/api/groups?sortBy=lastSeen&sortOrder=desc&limit=10"
curl "https://vij.example.com/api/groups?startDate=$(date -u +%Y-%m-%dT00:00:00Z)"
Example Response
{
"groups": [
{
"group": "abc123def456",
"message": "TypeError: Cannot read property 'map' of undefined",
"name": "TypeError",
"stack": "TypeError: Cannot read property 'map' of undefined\n at processData (app.js:123:45)\n at renderList (components/List.tsx:67:12)",
"count": 2547,
"severity": "error",
"firstSeen": "2024-01-01T10:00:00Z",
"lastSeen": "2024-01-03T14:30:00Z",
"affectedApps": ["frontend", "mobile-app"],
"affectedEnvironments": ["production"],
"affectedUsers": 456,
"recentOccurrences": [
"65a1b2c3d4e5f6g7h8i9j0k1",
"65a1b2c3d4e5f6g7h8i9j0k2",
"65a1b2c3d4e5f6g7h8i9j0k3"
]
},
{
"group": "def456ghi789",
"message": "Payment processing failed",
"name": "Error",
"stack": "Error: Payment processing failed\n at processPayment (payment.js:45:12)\n at handleCheckout (checkout.js:123:5)",
"count": 1234,
"severity": "error",
"firstSeen": "2024-01-02T08:00:00Z",
"lastSeen": "2024-01-03T15:00:00Z",
"affectedApps": ["frontend"],
"affectedEnvironments": ["production", "staging"],
"affectedUsers": 234,
"recentOccurrences": [
"65a1b2c3d4e5f6g7h8i9j0k4",
"65a1b2c3d4e5f6g7h8i9j0k5"
]
}
],
"total": 347,
"page": 1,
"limit": 50,
"pages": 7
}
Use Cases
Dashboard Overview
Display top error groups:const { groups } = await fetch(
'https://vij.example.com/api/groups?sortBy=count&sortOrder=desc&limit=10'
).then(r => r.json());
groups.forEach(group => {
console.log(`${group.message}: ${group.count} occurrences`);
});
Identify New Issues
Find groups that appeared recently:const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
const { groups } = await fetch(
`https://vij.example.com/api/groups?startDate=${yesterday}&sortBy=firstSeen&sortOrder=desc`
).then(r => r.json());
// New error groups in last 24 hours
console.log(`${groups.length} new error groups detected`);
High-Impact Errors
Find errors affecting many users:const { groups } = await fetch(
'https://vij.example.com/api/groups?minCount=100&sortBy=count&sortOrder=desc'
).then(r => r.json());
// High-frequency errors
groups.forEach(group => {
console.log(`${group.message}: ${group.affectedUsers} users affected`);
});
Application Health
Compare error groups across apps:const frontendGroups = await fetch(
'https://vij.example.com/api/groups?appId=frontend'
).then(r => r.json());
const backendGroups = await fetch(
'https://vij.example.com/api/groups?appId=backend-api'
).then(r => r.json());
console.log(`Frontend: ${frontendGroups.total} unique errors`);
console.log(`Backend: ${backendGroups.total} unique errors`);
Filtering Examples
By Frequency
# High-frequency errors (100+ occurrences)
GET /api/groups?minCount=100
# Low-frequency errors (1-10 occurrences)
GET /api/groups?minCount=1&maxCount=10
By Time
# Groups seen in last 24 hours
startDate=$(date -u -d '1 day ago' +%Y-%m-%dT%H:%M:%SZ)
GET /api/groups?startDate=$startDate
# Groups first seen this week
startDate=$(date -u -d 'last monday' +%Y-%m-%dT00:00:00Z)
GET /api/groups?startDate=$startDate&sortBy=firstSeen
By Severity
# Critical errors only
GET /api/groups?severity=error
# All severities in production
GET /api/groups?environment=production
Combined Filters
# High-frequency production errors
GET /api/groups?environment=production&severity=error&minCount=50&sortBy=count&sortOrder=desc
# Recent low-frequency warnings
GET /api/groups?severity=warning&minCount=1&maxCount=10&sortBy=lastSeen&sortOrder=desc
Sorting Options
By Count (Most Frequent)
GET /api/groups?sortBy=count&sortOrder=desc
By Last Seen (Most Recent)
GET /api/groups?sortBy=lastSeen&sortOrder=desc
By First Seen (Newest Issues)
GET /api/groups?sortBy=firstSeen&sortOrder=desc
By Severity
GET /api/groups?sortBy=severity&sortOrder=desc
Getting Group Details
To get all occurrences of a specific group:# Get group fingerprint from /api/groups
group="abc123def456"
# Fetch all occurrences
curl "https://vij.example.com/api/logs?group=$group"
Pagination
Navigate through large result sets:async function getAllGroups() {
let page = 1;
let allGroups = [];
let hasMore = true;
while (hasMore) {
const response = await fetch(
`https://vij.example.com/api/groups?page=${page}&limit=100`
).then(r => r.json());
allGroups = allGroups.concat(response.groups);
hasMore = page < response.pages;
page++;
}
return allGroups;
}
Performance Optimization
Use time filters
Use time filters
# Good - last 30 days
startDate=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ)
GET /api/groups?startDate=$startDate
# Slow - all time
GET /api/groups
Filter by app or environment
Filter by app or environment
# Faster - single app
GET /api/groups?appId=my-app
# Slower - all apps
GET /api/groups
Use minCount to reduce results
Use minCount to reduce results
# Faster - high-frequency only
GET /api/groups?minCount=50
# Slower - includes one-off errors
GET /api/groups
Limit page size appropriately
Limit page size appropriately
# Good for most cases
GET /api/groups?limit=50
# Use larger for batch processing
GET /api/groups?limit=100
Client Implementation
TypeScript
interface GroupsQuery {
environment?: string;
appId?: string;
severity?: 'error' | 'warning' | 'info';
startDate?: string;
endDate?: string;
minCount?: number;
page?: number;
limit?: number;
sortBy?: 'count' | 'lastSeen' | 'firstSeen' | 'severity';
sortOrder?: 'asc' | 'desc';
}
interface ErrorGroup {
group: string;
message: string;
name: string;
stack: string;
count: number;
severity: string;
firstSeen: string;
lastSeen: string;
affectedApps: string[];
affectedEnvironments: string[];
affectedUsers: number;
recentOccurrences: string[];
}
interface GroupsResponse {
groups: ErrorGroup[];
total: number;
page: number;
limit: number;
pages: number;
}
async function fetchGroups(query: GroupsQuery = {}): Promise<GroupsResponse> {
const params = new URLSearchParams(
Object.entries(query)
.filter(([_, v]) => v !== undefined)
.map(([k, v]) => [k, String(v)])
);
const response = await fetch(
`https://vij.example.com/api/groups?${params}`
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
// Usage
const groups = await fetchGroups({
environment: 'production',
severity: 'error',
minCount: 10,
sortBy: 'count',
sortOrder: 'desc',
limit: 20
});
React Hook
import { useState, useEffect } from 'react';
function useErrorGroups(query: GroupsQuery) {
const [groups, setGroups] = useState<ErrorGroup[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
async function load() {
try {
setLoading(true);
const data = await fetchGroups(query);
setGroups(data.groups);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}
load();
}, [JSON.stringify(query)]);
return { groups, loading, error };
}
// Usage in component
function ErrorGroupsList() {
const { groups, loading, error } = useErrorGroups({
environment: 'production',
severity: 'error',
minCount: 10
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{groups.map(group => (
<li key={group.group}>
{group.message} ({group.count} occurrences)
</li>
))}
</ul>
);
}
Exporting Group Data
Export groups for reporting:# Export as JSON
curl "https://vij.example.com/api/groups?environment=production" > groups.json
# Export as CSV (requires custom endpoint or processing)
curl "https://vij.example.com/api/groups/export?format=csv&environment=production" > groups.csv
Monitoring Trends
Track how groups change over time:// Daily group count tracking
async function trackGroupTrends() {
const today = await fetch('/api/groups').then(r => r.json());
const yesterday = await fetch(
`/api/groups?endDate=${new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()}`
).then(r => r.json());
const change = today.total - yesterday.total;
console.log(`Group change: ${change > 0 ? '+' : ''}${change}`);
if (change > 10) {
alert(`Warning: ${change} new error groups detected!`);
}
}
Related Endpoints
GET /api/logs
Get individual error logs by group
GET /api/stats
Get statistics including group counts
Error Grouping
Learn how error grouping works
Dashboard Features
View groups in the dashboard