Comprehensive REST API for the Cluso GraphDB enterprise graph database.
# Community Edition
PORT=8080 ./bin/server
# Enterprise Edition
GRAPHDB_EDITION=enterprise \
GRAPHDB_LICENSE_KEY='your-license-key' \
PORT=8080 ./bin/servercurl -X POST http://localhost:8080/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "alice",
"password": "SecurePass123!"
}'curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "alice",
"password": "SecurePass123!"
}'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "alice",
"role": "viewer"
}
}# Save the token
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# Create a node
curl -X POST http://localhost:8080/nodes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"labels": ["Person"],
"properties": {
"name": "Alice Johnson",
"age": 30
}
}'The GraphDB API supports two authentication methods:
Use for interactive applications and user sessions.
Header:
Authorization: Bearer <access_token>
Token Lifetime:
- Access Token: 15 minutes
- Refresh Token: 7 days
Refresh Example:
curl -X POST http://localhost:8080/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "<your_refresh_token>"
}'Use for server-to-server communication and automation.
Header:
X-API-Key: <your_api_key>
Create API Key (Admin Only):
curl -X POST http://localhost:8080/auth/api-keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Service Key",
"permissions": ["read", "write"],
"expires_in": "720h"
}'The complete API specification is available in OpenAPI 3.0 format:
- File:
docs/internals/openapi.yaml - Interactive Documentation: Use Swagger UI or Redoc
# Using Docker
docker run -p 8081:8080 \
-e SWAGGER_JSON=/openapi.yaml \
-v $(pwd)/docs/internals/openapi.yaml:/openapi.yaml \
swaggerapi/swagger-ui
# Open http://localhost:8081| Category | Endpoint | Method | Description |
|---|---|---|---|
| Authentication | /auth/register |
POST | Register new user |
/auth/login |
POST | Login and get tokens | |
/auth/refresh |
POST | Refresh access token | |
/auth/me |
GET | Get current user info | |
| Health | /health |
GET | Health check (public) |
/metrics |
GET | System metrics (public) | |
| Nodes | /nodes |
GET | List nodes |
/nodes |
POST | Create node | |
/nodes/{id} |
GET | Get node by ID | |
/nodes/{id} |
PUT | Update node | |
/nodes/{id} |
DELETE | Delete node | |
/nodes/batch |
POST | Batch create nodes | |
| Edges | /edges |
GET | List edges |
/edges |
POST | Create edge | |
/edges/{id} |
GET | Get edge by ID | |
/edges/{id} |
PUT | Update edge | |
/edges/{id} |
DELETE | Delete edge | |
/edges/batch |
POST | Batch create edges | |
| Traversal | /traverse |
POST | Graph traversal |
/shortest-path |
POST | Find shortest path | |
| Algorithms | /algorithms |
POST | Run graph algorithms |
| Query | /query |
POST | Custom query language |
/graphql |
POST | GraphQL endpoint | |
| Vector Search | /vector-indexes |
GET | List vector indexes |
/vector-indexes |
POST | Create vector index | |
/vector-indexes/{name} |
GET | Get vector index info | |
/vector-indexes/{name} |
DELETE | Delete vector index | |
/vector-search |
POST | k-NN similarity search |
curl -X POST http://localhost:8080/nodes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"labels": ["Person", "Employee"],
"properties": {
"name": "Bob Smith",
"age": 35,
"department": "Engineering",
"email": "bob@example.com"
}
}'Response:
{
"id": 12345,
"labels": ["Person", "Employee"],
"properties": {
"name": "Bob Smith",
"age": 35,
"department": "Engineering",
"email": "bob@example.com"
}
}curl -X GET http://localhost:8080/nodes/12345 \
-H "Authorization: Bearer $TOKEN"curl -X PUT http://localhost:8080/nodes/12345 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"age": 36,
"department": "Engineering Management"
}
}'curl -X GET "http://localhost:8080/nodes?labels=Person&limit=50&offset=0" \
-H "Authorization: Bearer $TOKEN"curl -X POST http://localhost:8080/nodes/batch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nodes": [
{
"labels": ["Person"],
"properties": {"name": "Alice", "age": 30}
},
{
"labels": ["Person"],
"properties": {"name": "Bob", "age": 25}
},
{
"labels": ["Person"],
"properties": {"name": "Charlie", "age": 35}
}
]
}'curl -X POST http://localhost:8080/edges \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from_node_id": 12345,
"to_node_id": 67890,
"type": "KNOWS",
"weight": 0.8,
"properties": {
"since": "2020-01-15",
"strength": "high"
}
}'Response:
{
"id": 78901,
"from_node_id": 12345,
"to_node_id": 67890,
"type": "KNOWS",
"weight": 0.8,
"properties": {
"since": "2020-01-15",
"strength": "high"
}
}# Get all KNOWS relationships from node 12345
curl -X GET "http://localhost:8080/edges?from_node_id=12345&type=KNOWS" \
-H "Authorization: Bearer $TOKEN"curl -X POST http://localhost:8080/traverse \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"start_node_id": 12345,
"max_depth": 3,
"direction": "outgoing",
"edge_types": ["KNOWS", "WORKS_WITH"]
}'curl -X POST http://localhost:8080/shortest-path \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"start_node_id": 12345,
"end_node_id": 67890,
"weighted": true
}'Response:
{
"path": [12345, 23456, 34567, 67890],
"distance": 3.5,
"nodes": [
{"id": 12345, "labels": ["Person"], "properties": {"name": "Alice"}},
{"id": 23456, "labels": ["Person"], "properties": {"name": "Bob"}},
{"id": 34567, "labels": ["Person"], "properties": {"name": "Charlie"}},
{"id": 67890, "labels": ["Person"], "properties": {"name": "David"}}
]
}curl -X POST http://localhost:8080/algorithms \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"algorithm": "pagerank",
"parameters": {
"iterations": 20,
"damping_factor": 0.85
}
}'Response:
{
"algorithm": "pagerank",
"execution_time_ms": 1250.5,
"results": {
"scores": {
"12345": 0.15,
"67890": 0.23,
"54321": 0.18
}
}
}curl -X POST http://localhost:8080/algorithms \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"algorithm": "betweenness",
"parameters": {
"normalized": true
}
}'curl -X POST http://localhost:8080/algorithms \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"algorithm": "louvain",
"parameters": {
"resolution": 1.0
}
}'curl -X POST http://localhost:8080/query \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "MATCH (p:Person)-[:KNOWS]->(f:Person) WHERE p.age > 25 RETURN p, f LIMIT 10"
}'curl -X POST http://localhost:8080/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query { nodes(labels: [\"Person\"], limit: 10) { id labels properties } }"
}'Vector search enables semantic similarity queries using HNSW (Hierarchical Navigable Small World) indexes. This is ideal for AI/ML applications, recommendation systems, and semantic search.
| Metric | Description | Score Range | Use Case |
|---|---|---|---|
cosine |
Cosine similarity (default) | [-1, 1] | Text embeddings, normalized vectors |
euclidean |
L2 distance | (0, 1] | Spatial data, image features |
dot_product |
Inner product | unbounded | Maximum inner product search |
Create an HNSW index for a node property that will contain vector embeddings.
curl -X POST http://localhost:8080/vector-indexes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"property_name": "embedding",
"dimensions": 384,
"m": 16,
"ef_construction": 200,
"metric": "cosine"
}'Request Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
property_name |
string | Yes | - | Node property to index |
dimensions |
integer | Yes | - | Vector dimensions (1-4096) |
m |
integer | No | 16 | HNSW connections per node |
ef_construction |
integer | No | 200 | Construction quality factor |
metric |
string | No | "cosine" | Distance metric |
Response:
{
"property_name": "embedding",
"dimensions": 384,
"metric": "cosine"
}curl -X GET http://localhost:8080/vector-indexes \
-H "Authorization: Bearer $TOKEN"Response:
{
"indexes": [
{"property_name": "embedding"},
{"property_name": "image_features"}
],
"count": 2
}curl -X GET http://localhost:8080/vector-indexes/embedding \
-H "Authorization: Bearer $TOKEN"Response:
{
"property_name": "embedding"
}curl -X DELETE http://localhost:8080/vector-indexes/embedding \
-H "Authorization: Bearer $TOKEN"Response: 204 No Content
Nodes with vector properties are automatically indexed when a corresponding vector index exists.
curl -X POST http://localhost:8080/nodes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"labels": ["Document"],
"properties": {
"title": "Introduction to Graph Databases",
"embedding": [0.1, 0.2, 0.3, ..., 0.384]
}
}'Note: Vector properties must match the dimensions of the corresponding index. Vectors are automatically synced when nodes are created, updated, or deleted.
Find the k most similar nodes based on vector distance.
curl -X POST http://localhost:8080/vector-search \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"property_name": "embedding",
"query_vector": [0.15, 0.22, 0.31, ...],
"k": 10,
"ef": 100,
"include_nodes": true,
"filter_labels": ["Document"]
}'Request Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
property_name |
string | Yes | - | Vector index to search |
query_vector |
float[] | Yes | - | Query vector |
k |
integer | Yes | - | Number of results (1-1000) |
ef |
integer | No | k*2 | Search quality factor (min 50) |
include_nodes |
boolean | No | false | Include full node data |
filter_labels |
string[] | No | [] | Filter results by labels |
Response:
{
"results": [
{
"node_id": 12345,
"distance": 0.15,
"score": 0.85,
"node": {
"id": 12345,
"labels": ["Document"],
"properties": {
"title": "Introduction to Graph Databases"
}
}
},
{
"node_id": 67890,
"distance": 0.23,
"score": 0.77,
"node": {
"id": 67890,
"labels": ["Document"],
"properties": {
"title": "Graph Theory Fundamentals"
}
}
}
],
"count": 2,
"time": "1.234ms"
}Score Calculation by Metric:
| Metric | Formula | Interpretation |
|---|---|---|
cosine |
1 - distance |
1 = identical, 0 = orthogonal, -1 = opposite |
euclidean |
1 / (1 + distance) |
1 = identical, approaches 0 as distance increases |
dot_product |
-distance |
Higher = more similar |
-
Dimension Selection: Match your embedding model's output dimensions (e.g., 384 for MiniLM, 1536 for OpenAI)
-
HNSW Parameters:
m: Higher values (32-64) improve recall but use more memoryef_construction: Higher values (400+) improve index quality but slow constructionef(search): Set to k*2 minimum; higher for better recall
-
Performance Tips:
- Create indexes before bulk loading vectors
- Use
include_nodes: falsewhen you only need node IDs - Filter by labels to reduce search space
-
Input Validation:
- Vectors must not contain NaN or Infinity values
- All vectors for an index must have identical dimensions
- Query vector dimensions must match index dimensions
# 1. Create the vector index
curl -X POST http://localhost:8080/vector-indexes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"property_name": "embedding",
"dimensions": 384,
"metric": "cosine"
}'
# 2. Add documents with embeddings (from your embedding model)
curl -X POST http://localhost:8080/nodes/batch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nodes": [
{
"labels": ["Document"],
"properties": {
"title": "Machine Learning Basics",
"embedding": [0.1, 0.2, ...]
}
},
{
"labels": ["Document"],
"properties": {
"title": "Deep Learning Guide",
"embedding": [0.15, 0.25, ...]
}
}
]
}'
# 3. Search for similar documents
curl -X POST http://localhost:8080/vector-search \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"property_name": "embedding",
"query_vector": [0.12, 0.22, ...],
"k": 5,
"include_nodes": true
}'A drop-in OpenAI-compatible endpoint backed by the per-tenant LSA index.
Apps that already speak the OpenAI Embeddings API (LangChain, Vercel AI SDK,
llamaindex, etc.) can integrate by setting api_base to your graphdb URL.
Endpoint: POST /v1/embeddings (auth + tenant required)
Request — accepts the OpenAI shape:
{
"input": "graph databases store nodes and relationships",
"model": "text-embedding-ada-002",
"encoding_format": "float"
}input accepts either a single string or an array of strings. model is
recorded but not validated (the only backend is LSA). encoding_format
must be "float" or omitted; base64 is not supported in v1. dimensions,
if provided, must match the LSA index's actual dimension or the request
returns 400.
Response — mirrors OpenAI's /v1/embeddings:
{
"object": "list",
"data": [
{
"object": "embedding",
"embedding": [0.012, -0.034, ...],
"index": 0
}
],
"model": "graphdb-lsa-v1",
"usage": { "prompt_tokens": 5, "total_tokens": 5 }
}LangChain example (Python):
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(
base_url="https://your-graphdb-host/v1",
api_key="your-graphdb-jwt-or-api-key",
model="graphdb-lsa-v1",
)
vec = embeddings.embed_query("graph databases")Trade-offs vs. OpenAI's neural models (read before integrating):
- Vocabulary-bound. LSA can only embed terms that appeared in the corpus the index was built on. Out-of-vocab queries return 400, not a fallback vector. OpenAI's neural models always return some vector; LSA cannot. For LLM retrieval flows, this means failed embeddings should surface as user-facing "no semantic context for that query," not as silent zero-vector matches.
- Scale ceiling: ~100K-500K documents at default 200 dims on a single
modern CPU core. LSA's
TopKByVectoris a single-threaded O(D×k) scan with no SIMD. Above that ceiling P99 latency degrades; consider an external embedding service. - Build-first: the LSA index is built per-tenant via the admin endpoint
POST /hybrid-search/lsa-index. A tenant that hasn't built one yet gets 503 with a message pointing at that endpoint. - Deterministic. Same query, same index → bit-identical vectors. Good for cache keys, reproducible benchmarks, regression tests.
- No external API calls, no GPU. Air-gapped deployments work; vectors never leave the process boundary.
Curl example:
curl -X POST http://localhost:8080/v1/embeddings \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": ["graph databases", "vector search"],
"model": "graphdb-lsa-v1"
}'Rate limits vary by edition:
- Requests: 100 requests/minute per IP
- Batch Operations: 1000 items max per request
- Node Properties: 100 properties max per node
- Edge Properties: 100 properties max per edge
- Requests: 1000 requests/minute per user
- Batch Operations: 10,000 items max per request
- Node Properties: 1000 properties max per node
- Edge Properties: 1000 properties max per edge
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Missing or invalid authentication |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 413 | Payload Too Large - Request body exceeds limit |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error |
All errors return a consistent JSON structure:
{
"error": "Invalid request parameters",
"code": "INVALID_REQUEST",
"details": {
"field": "labels",
"issue": "labels array cannot be empty"
}
}| Code | Description |
|---|---|
INVALID_REQUEST |
Request parameters are invalid |
UNAUTHORIZED |
Missing or invalid authentication |
FORBIDDEN |
Insufficient permissions for operation |
NOT_FOUND |
Requested resource doesn't exist |
CONFLICT |
Resource already exists |
RATE_LIMIT_EXCEEDED |
Too many requests |
# Invalid token
HTTP/1.1 401 Unauthorized
{
"error": "Invalid or expired token",
"code": "UNAUTHORIZED"
}
# Missing authentication
HTTP/1.1 401 Unauthorized
{
"error": "Missing authentication (Bearer token or X-API-Key header required)",
"code": "UNAUTHORIZED"
}
# Insufficient permissions
HTTP/1.1 403 Forbidden
{
"error": "Insufficient permissions",
"code": "FORBIDDEN"
}- Use API keys for services: More secure for server-to-server communication
- Rotate tokens regularly: Implement token refresh flow
- Store tokens securely: Never commit tokens to version control
- Use batch endpoints: Much faster than individual requests
- Respect batch limits: Max 1000 items (Community) or 10,000 (Enterprise)
- Handle partial failures: Check response for individual item status
- Use pagination: Always specify limit and offset for large datasets
- Filter server-side: Use query parameters instead of filtering in client
- Cache results: Cache frequently accessed data
- Enable connection pooling: Reuse HTTP connections
- Use compression: Enable gzip compression for large responses
- Monitor rate limits: Track remaining quota in response headers
- Implement retries: With exponential backoff for 5xx errors
- Log errors: Include request ID for debugging
- Validate inputs: Check data before sending to API
- Documentation: https://dd0wney.github.io/graphdb/
- GitHub Issues: https://github.com/dd0wney/graphdb/issues
| Version | Date | Changes |
|---|---|---|
| 2.0.0 | 2026-01 | Vector search API (HNSW indexes, k-NN search) |
| 1.0.0 | 2024-11 | Initial API release |