The Ergs REST API provides programmatic access to your datasources, enabling search functionality, data retrieval, and system monitoring. The API returns JSON responses and supports advanced search features including full-text search, pagination, and date filtering.
All API endpoints are available under the /api path prefix. The API returns JSON responses and uses HTTP method-specific routing for security.
http://localhost:8080/api
Currently, no authentication is required for API access.
The API includes CORS middleware allowing cross-origin requests from any domain with the following configuration:
- Access-Control-Allow-Origin:
* - Access-Control-Allow-Methods:
GET, POST, PUT, DELETE, OPTIONS - Access-Control-Allow-Headers:
Content-Type, Authorization
Retrieve a list of all configured datasources with their statistics.
Endpoint: GET /api/datasources
Parameters: None
Response:
{
"datasources": [
{
"name": "github",
"type": "github",
"stats": {
"total_blocks": 150,
"last_updated": "2024-01-15T10:30:00Z"
}
},
{
"name": "notes",
"type": "filesystem",
"stats": {
"total_blocks": 45
}
}
],
"count": 2
}Response Fields:
datasources- Array of datasource objectscount- Total number of datasourcesname- Datasource identifiertype- Datasource type (github, filesystem, etc.)stats- Statistics object (may vary by datasource)
Status Codes:
200- Success500- Internal server error
Retrieve blocks from a specific datasource with optional search and pagination.
Endpoint: GET /api/datasources/{name}
Path Parameters:
name(required) - The datasource name
Query Parameters:
q(optional) - Search query string for full-text search within the datasourcelimit(optional) - Maximum number of results (default: 20, max: 1000)page(optional) - Page number for pagination (default: 1, max: 10000)start_date(optional) - Filter blocks created on or after this date (format: YYYY-MM-DD)end_date(optional) - Filter blocks created on or before this date (format: YYYY-MM-DD, inclusive of entire day)
Date Filtering:
- Dates must be in
YYYY-MM-DDformat (e.g.,2024-01-15) start_datefilters blocks created on or after the specified date at 00:00:00end_datefilters blocks created on or before the specified date at 23:59:59 (entire day is included)- Both parameters can be used together to specify a date range
- Invalid date formats return HTTP 400 with error details
Examples:
# Get recent blocks from 'github' datasource
GET /api/datasources/github
# Search within a datasource
GET /api/datasources/github?q=bug+fix
# Limit results and paginate
GET /api/datasources/github?q=feature&limit=10&page=2
# Filter by date range
GET /api/datasources/github?start_date=2024-01-01&end_date=2024-01-31
# Combined filtering
GET /api/datasources/github?q=authentication&limit=5&start_date=2024-01-01Response:
{
"datasource": "github",
"blocks": [
{
"id": "abc123",
"text": "Fixed critical bug in authentication module",
"source": "https://github.com/user/repo/issues/123",
"created_at": "2024-01-15T10:30:00Z",
"metadata": {
"author": "john.doe",
"labels": ["bug", "critical"],
"repository": "user/repo"
}
}
],
"count": 1,
"query": "bug fix"
}Response Fields:
datasource- Name of the datasourceblocks- Array of block objects (see Block Object Structure)count- Number of blocks returned on this pagequery- The search query used (if any)
Status Codes:
200- Success400- Bad request (invalid parameters, invalid date format, invalid search syntax)404- Datasource not found405- Method not allowed (only GET supported)500- Internal server error
Search across all configured datasources simultaneously with advanced filtering options.
Endpoint: GET /api/search
Query Parameters:
q(required) - Search query string (supports FTS5 full-text search syntax)limit(optional) - Maximum number of results per page (default: 30, max: 1000)page(optional) - Page number for pagination (default: 1, max: 10000)start_date(optional) - Filter blocks created on or after this date (format: YYYY-MM-DD)end_date(optional) - Filter blocks created on or before this date (format: YYYY-MM-DD, inclusive of entire day)datasource(optional) - Limit search to specific datasources (can be specified multiple times)
Search Query Syntax: The search uses SQLite FTS5 (Full-Text Search) with the following supported features:
- Simple terms:
authentication bug(finds blocks containing both terms) - Exact phrases:
"exact phrase"(use double quotes for phrase search) - Boolean operators:
authentication AND securityorbug OR issue - Exclusion:
authentication NOT password - Prefix matching:
auth*(matches authentication, authorize, etc.) - Column search:
text:"search in text"(search specific columns) - Case insensitive: Search is not case sensitive
- Partial matching: Matches partial words
Search Limitations:
- Forward slashes (
/) are not allowed in search terms - Single quotes (
') should be avoided; use double quotes (") for phrases - Complex boolean expressions may cause syntax errors
- Special characters like
&,@,%may need to be avoided
Examples:
# Basic search across all datasources
GET /api/search?q=authentication
# Search with pagination
GET /api/search?q=bug+fix&page=2&limit=50
# Search specific datasources only
GET /api/search?q=golang&datasource=github&datasource=rss
# Date-filtered search
GET /api/search?q=release&start_date=2024-01-01&end_date=2024-01-31
# Complex search with phrase and boolean operators
GET /api/search?q="security update" AND (critical OR urgent)
# Prefix search
GET /api/search?q=config* AND NOT deprecatedResponse:
{
"query": "authentication",
"results": {
"github": {
"datasource": "github",
"blocks": [
{
"id": "def456",
"text": "Implemented OAuth2 authentication",
"source": "https://github.com/user/repo/pull/456",
"created_at": "2024-01-14T15:20:00Z",
"metadata": {
"author": "jane.smith",
"type": "pull_request"
}
}
],
"count": 1,
"query": "authentication"
},
"notes": {
"datasource": "notes",
"blocks": [
{
"id": "ghi789",
"text": "Notes on authentication best practices",
"source": "/home/user/notes/auth.md",
"created_at": "2024-01-13T09:15:00Z",
"metadata": {
"file_path": "/home/user/notes/auth.md",
"file_type": "markdown"
}
}
],
"count": 1,
"query": "authentication"
}
},
"total_count": 2,
"page": 1,
"limit": 30,
"total_pages": 1,
"has_more": false
}Response Fields:
query- The search query usedresults- Object with datasource names as keys, each containing a ListBlocksResponsetotal_count- Total number of results returned on this pagepage- Current page numberlimit- Maximum results per pagetotal_pages- Estimated total number of pageshas_more- Whether there are more results available on subsequent pages
Status Codes:
200- Success400- Bad request (missing query parameter, invalid search syntax, invalid date format)405- Method not allowed (only GET supported)500- Internal server error
Retrieve storage statistics for all configured datasources including block counts and storage information.
Endpoint: GET /api/stats
Parameters: None
Response:
{
"github": {
"total_blocks": 150,
"last_updated": "2024-01-15T10:30:00Z",
"storage_size": "2.5MB"
},
"notes": {
"total_blocks": 45,
"storage_size": "1.2MB"
},
"total_blocks": 195,
"total_datasources": 2
}Response Fields:
- Per-datasource statistics with block counts and storage sizes
total_blocks- Total blocks across all datasourcestotal_datasources- Number of configured datasources
Status Codes:
200- Success405- Method not allowed (only GET supported)500- Internal server error
Simple health check endpoint to verify the service is running and responsive.
Endpoint: GET /health
Parameters: None
Response:
{
"status": "ok",
"timestamp": "2024-01-15T12:00:00Z",
"version": "3.1.0"
}Response Fields:
status- Service status (always "ok" if endpoint responds)timestamp- Current server timestamp in ISO 8601 formatversion- Current Ergs version
Status Codes:
200- Service is healthy405- Method not allowed (only GET supported)
Note: This endpoint is typically used by load balancers, monitoring systems, and container orchestrators to check service availability.
All API endpoints that return blocks use the following standardized structure:
{
"id": "unique-block-identifier",
"text": "The main content/text of the block",
"source": "Source URL or file path",
"created_at": "2024-01-15T10:30:00Z",
"metadata": {
"key": "value",
"additional": "datasource-specific fields"
}
}Standard Fields:
id- Unique identifier for the block within its datasourcetext- Main textual content of the blocksource- Original source (URL, file path, etc.)created_at- Creation timestamp in ISO 8601 formatmetadata- Key-value pairs with additional datasource-specific information
Metadata Examples by Datasource Type:
- GitHub:
author,repository,labels,type(issue/pull_request) - Firefox:
title,visit_count,last_visit_date - RSS:
author,feed_title,categories - Filesystem:
file_path,file_type,size
All endpoints return consistent error responses with appropriate HTTP status codes:
{
"error": "error_type",
"message": "Human readable error message"
}Common HTTP Status Codes:
200- Success400- Bad Request (missing/invalid parameters, invalid date format, search syntax errors)404- Not Found (datasource doesn't exist, invalid endpoint)405- Method Not Allowed (unsupported HTTP method)500- Internal Server Error
Common Error Types:
{
"error": "Invalid search query",
"message": "Forward slashes (/) are not allowed in search terms"
}{
"error": "Invalid search query",
"message": "Unmatched single quotes detected. Use double quotes for phrase searches"
}{
"error": "Invalid search query",
"message": "Invalid search syntax. Check for special characters or invalid operators"
}{
"error": "Invalid date format",
"message": "invalid start_date format: parsing time \"invalid-date\" as \"2006-01-02\": cannot parse \"invalid-date\" as \"2006\""
}{
"error": "Missing query parameter",
"message": "Query parameter 'q' is required"
}{
"error": "datasource_not_found",
"message": "Datasource 'invalid-name' does not exist"
}All endpoints only support GET requests. Other HTTP methods will return:
{
"error": "method_not_allowed",
"message": "Method POST not allowed"
}The API provides intelligent error handling for common search issues:
FTS5 Syntax Errors: The API detects SQLite FTS5 syntax errors and provides user-friendly error messages instead of exposing internal database errors.
Graceful Degradation: Invalid search queries return HTTP 400 with helpful guidance rather than HTTP 500 server errors.
Error Recovery: The API suggests alternatives for common search mistakes (e.g., using double quotes instead of single quotes).
Currently, no rate limiting is implemented at the application level. For production deployments, consider:
- Placing the API behind a reverse proxy (nginx, Apache) with rate limiting
- Using cloud-based API gateways with built-in rate limiting
- Implementing custom middleware for rate limiting based on IP or API key
The API implements several security measures:
Input Validation:
- Datasource names are validated to contain only safe characters (alphanumeric, underscore, hyphen, dot)
- Numeric parameters (page, limit) are bounds-checked to prevent resource exhaustion
- Date parameters use strict parsing to prevent injection attacks
SQL Injection Prevention:
- All database queries use parameterized statements
- Search queries are handled safely through SQLite's FTS5 MATCH parameter binding
- User input is never directly concatenated into SQL strings
Resource Limits:
- Maximum limit per request: 1000 results
- Maximum page number: 10000
- Automatic capping of excessive values rather than rejection
# List all datasources
curl http://localhost:8080/api/datasources
# Search within a specific datasource with error handling
curl -f "http://localhost:8080/api/datasources/github?q=bug&limit=5" || echo "Request failed"
# Search across all datasources with date filtering
curl "http://localhost:8080/api/search?q=authentication&start_date=2024-01-01&end_date=2024-01-31"
# Get system statistics
curl http://localhost:8080/api/stats
# Health check for monitoring
curl http://localhost:8080/health
# Search with pagination
curl "http://localhost:8080/api/search?q=golang&page=2&limit=50"
# Filter by specific datasources
curl "http://localhost:8080/api/search?q=release&datasource=github&datasource=rss"
# Complex search with boolean operators
curl "http://localhost:8080/api/search?q=\"security+update\"+AND+(critical+OR+urgent)"Pagination Limits: The API limits results per page to prevent memory exhaustion and ensure responsiveness.
Database Optimization: Consider running ergs optimize periodically to maintain FTS5 index performance.
Caching: Consider adding HTTP caching for API responses if needed for high-traffic deployments.
For production environments, consider:
- Reverse Proxy: Use nginx or Apache for SSL termination and API rate limiting
- Process Management: Use systemd, Docker, or PM2 to manage the ergs web process
- Monitoring: Set up health checks using the
/healthendpoint - Logging: Configure structured logging and log aggregation
- Security: Implement authentication/authorization if needed
- Rate Limiting: Add rate limiting at the proxy level
See the main documentation for datasource configuration details and deployment best practices.
The WebSocket Firehose provides near real-time delivery of newly ingested blocks across all datasources. It complements the REST endpoints by pushing updates instead of requiring clients to poll.
GET /api/firehose/ws
Establish a standard WebSocket connection. No authentication is currently required. CORS/Origin checks are permissive (all origins allowed). This may change for hardened deployments.
Upon connection the server sends an init message containing the most recent slice of blocks (equivalent to the first page of the REST firehose). If the client supplies a since query parameter (?since=<RFC3339 timestamp>), only blocks with created_at strictly greater than that timestamp are included (high‑precision boundary; supersedes date-based start_date filtering):
{
"type": "init",
"count": <number_of_blocks>,
"blocks": [
{
"id": "block-id",
"text": "Block text",
"source": "datasource_instance_name",
"created_at": "2025-01-01T12:00:00Z",
"metadata": {
"datasource": "datasource_instance_name",
"...": "..."
}
}
]
}
After the initial snapshot, new blocks are streamed as discrete messages:
{
"type": "block",
"block": {
"id": "block-id",
"text": "Block text",
"source": "datasource_instance_name",
"created_at": "2025-01-01T12:00:05Z",
"metadata": {
"datasource": "datasource_instance_name"
}
}
}
When batching is necessary (e.g., fallback polling or catch-up), a block_batch message may be sent:
{
"type": "block_batch",
"count": 3,
"blocks": [ ... same block objects ... ]
}
A heartbeat message is emitted approximately every 30 seconds so clients can detect stale connections:
{
"type": "heartbeat",
"ts": "2025-01-01T12:05:00.123456789Z"
}
Clients should disconnect and reconnect if no heartbeat or block messages are received within 2× the heartbeat interval.
Recoverable errors (e.g., transient search/poll failure) are sent as:
{
"type": "error",
"error": "poll_failed",
"info": "details or diagnostic hint"
}
| type | Purpose |
|---|---|
| init | Initial snapshot with recent blocks (optionally filtered by since) |
| block | Single newly ingested block (push) |
| block_batch | Multiple new blocks (fallback batch) |
| heartbeat | Liveness indicator |
| error | Non-fatal error / diagnostic |
- Ordering is generally chronological by
created_at, but interleaving from multiple datasources can cause slight reordering on high throughput. - Clients should maintain a de-dup key
(source, id)to avoid duplicates if reconnecting or processing bothblock_batchandblockmessages. - When reconnecting, clients can compare the latest
created_atthey have with incoming snapshot data and ignore older blocks.
Clients may pass ?since=<RFC3339> when opening the WebSocket (e.g. wss://host/api/firehose/ws?since=2025-01-01T12:34:56Z).
Rules:
- Must be a valid RFC3339 timestamp (e.g.
2025-01-01T12:34:56Zor with nanoseconds). - Returns only blocks with
created_atstrictly greater thansince. - Overrides any lower-bound implied by
start_date(the WebSocket endpoint ignoresstart_datewhensinceis present). - The server echoes back an updated high-water mark inside subsequent messages (
initand anyblock_batch) via thesincefield so clients can persist and reuse it on reconnect. - If no new blocks exist after that boundary,
init.countmay be zero.
Example:
wss://localhost:8080/api/firehose/ws?since=2025-01-10T09:00:00Z
If the newest block you receive has:
"created_at": "2025-01-10T09:05:12.345678Z"
Store 2025-01-10T09:05:12.345678Z and use it as the next since on reconnect.
Suggested client behavior:
- Connect to
/api/firehose/ws - Process
init→ populate cache - Stream
block/block_batchmessages - On socket close:
- Exponential backoff reconnect
- Optionally perform a REST call (
/api/firehose) to fill any gap
If you need more history than provided in init, use the REST firehose endpoint with pagination:
GET /api/firehose?limit=30&page=N
If the warehouse event bridge (Unix domain socket) is not configured, the server may fall back to periodic polling, sending block_batch messages rather than immediate block messages. Heartbeats continue to function regardless of the backend mode.
- Optional
sinceparameter for reduced initial payload - Authentication / API keys
- Client-driven ack or cursor offsets
- Compression (permessage-deflate)
- Fine-grained filters (e.g., subset of datasources)
const ws = new WebSocket("ws://localhost:8080/api/firehose/ws");
const dedup = new Set();
ws.onmessage = (evt) => {
const msg = JSON.parse(evt.data);
switch (msg.type) {
case "init":
msg.blocks.forEach(handleBlock);
break;
case "block":
handleBlock(msg.block);
break;
case "block_batch":
msg.blocks.forEach(handleBlock);
break;
case "heartbeat":
// Optionally update a lastSeen timestamp
break;
case "error":
console.warn("Firehose error:", msg.error, msg.info);
break;
}
};
function handleBlock(b) {
const key = `${b.source}:${b.id}`;
if (dedup.has(key)) return;
dedup.add(key);
console.log("New block:", b);
}
- Ensure both processes (warehouse & web) share filesystem permissions for the Unix domain socket when using the real-time bridge.
- If deploying under systemd, consider placing the socket under
/run/ergs/bridge.sockwith an appropriateRuntimeDirectory=directive. - The WebSocket layer is stateless; horizontal scaling requires each web instance to connect to the same bridge or a future broker.
Currently open; for production:
- Restrict origins or add token-based auth.
- Limit connection count.
- Consider rate limiting clients that reconnect aggressively.