Skip to main content

REST API

Graphmind exposes a REST API on port 8080 (default). Every operation available through the SDKs is also available as an HTTP endpoint.

Base URL

http://localhost:8080

Configure with the --http-port flag or GRAPHMIND_HTTP_PORT environment variable.

Authentication

When authentication is enabled, include a bearer token in the Authorization header:

Authorization: Bearer <token>

Endpoints

POST /api/query

Execute a Cypher query (read or write).

Request:

curl -X POST http://localhost:8080/api/query \
-H 'Content-Type: application/json' \
-d '{"query": "MATCH (p:Person) RETURN p.name, p.age", "graph": "default"}'

Response:

{
"columns": ["p.name", "p.age"],
"records": [["Alice", 30], ["Bob", 25]],
"nodes": [
{"id": "1", "labels": ["Person"], "properties": {"name": "Alice", "age": 30}},
{"id": "2", "labels": ["Person"], "properties": {"name": "Bob", "age": 25}}
],
"edges": []
}

The graph field is optional and defaults to "default".

POST /api/script

Execute a multi-statement Cypher script. Statements are separated by newlines.

Request:

curl -X POST http://localhost:8080/api/script \
-H 'Content-Type: application/json' \
-d '{
"query": "CREATE (a:Person {name: '\''Alice'\''})\nCREATE (b:Person {name: '\''Bob'\''})"
}'

Response:

{
"status": "ok",
"executed": 2,
"errors": [],
"storage": {"nodes": 2, "edges": 0}
}

POST /api/nlq

Translate a natural-language question to Cypher and execute it. Requires an LLM provider to be configured on the server.

Request:

curl -X POST http://localhost:8080/api/nlq \
-H 'Content-Type: application/json' \
-d '{"query": "Who are Alice'\''s friends?", "graph": "default"}'

Response:

{
"cypher": "MATCH (a:Person {name:'Alice'})-[:KNOWS]-(b) RETURN a, b",
"provider": "OpenAI",
"model": "gpt-4o-mini",
"columns": ["a", "b"],
"records": [["Alice", "Bob"]],
"nodes": [...],
"edges": [...]
}

GET /api/status

Server health, version, and graph statistics.

Request:

curl http://localhost:8080/api/status

Response:

{
"status": "healthy",
"version": "0.6.2",
"storage": {
"nodes": 1042,
"edges": 3891
}
}

GET /api/schema

Introspect node labels, edge types, properties, indexes, and statistics.

Request:

curl http://localhost:8080/api/schema

Response:

{
"node_types": [
{"label": "Person", "count": 42, "properties": {"name": "String", "age": "Integer"}},
{"label": "Company", "count": 5, "properties": {"name": "String"}}
],
"edge_types": [
{"type": "KNOWS", "count": 120, "source_labels": ["Person"], "target_labels": ["Person"],
"properties": {"since": "Integer"}}
],
"indexes": [
{"label": "Person", "property": "name", "type": "btree"}
],
"constraints": [],
"statistics": {
"total_nodes": 47,
"total_edges": 120,
"avg_out_degree": 2.55
}
}

POST /api/sample

Sample a subgraph for visualization. Returns a proportional subset of nodes and edges.

Request:

curl -X POST http://localhost:8080/api/sample \
-H 'Content-Type: application/json' \
-d '{"max_nodes": 200, "labels": ["Person", "Company"], "graph": "default"}'

Response:

{
"nodes": [
{"id": 1, "label": "Person", "name": "Alice", "properties": {"age": 30}},
{"id": 2, "label": "Company", "name": "Acme", "properties": {}}
],
"edges": [
{"id": 100, "source": 1, "target": 2, "type": "WORKS_AT", "properties": {}}
],
"total_nodes": 1042,
"total_edges": 3891,
"sampled_nodes": 200,
"sampled_edges": 312
}

All fields in the request body are optional. max_nodes defaults to 200 (max 1000).

POST /api/import/csv

Import nodes from CSV content.

Request:

curl -X POST http://localhost:8080/api/import/csv \
-H 'Content-Type: application/json' \
-d '{
"csv": "name,age,city\nAlice,30,Paris\nBob,25,London",
"label": "Person",
"id_column": "name",
"delimiter": ","
}'

Response:

{
"status": "ok",
"nodes_created": 2,
"label": "Person",
"columns": ["name", "age", "city"]
}

POST /api/import/json

Import nodes from JSON objects.

Request:

curl -X POST http://localhost:8080/api/import/json \
-H 'Content-Type: application/json' \
-d '{
"label": "Person",
"nodes": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
]
}'

Response:

{
"status": "ok",
"nodes_created": 2,
"label": "Person"
}

POST /api/snapshot/export

Export the entire graph as a portable .sgsnap snapshot.

Request:

curl -X POST http://localhost:8080/api/snapshot/export \
-H 'Content-Type: application/json' \
-d '{"graph": "default"}' \
--output graph.sgsnap

The response body is the binary snapshot file.

POST /api/snapshot/import

Import a previously exported .sgsnap snapshot.

Request:

curl -X POST http://localhost:8080/api/snapshot/import \
-H 'Content-Type: application/octet-stream' \
--data-binary @graph.sgsnap

Response:

{
"status": "ok",
"nodes_imported": 1042,
"edges_imported": 3891
}

GET /api/graphs

List all graph namespaces.

Request:

curl http://localhost:8080/api/graphs

Response:

{
"graphs": ["default", "tenant_acme", "tenant_globex"]
}

DELETE /api/graphs/:name

Delete a graph namespace and all its data.

Request:

curl -X DELETE http://localhost:8080/api/graphs/tenant_acme

Response:

{
"status": "ok",
"deleted": "tenant_acme"
}

GET /metrics

Prometheus-compatible metrics endpoint.

Request:

curl http://localhost:8080/metrics

Response (text/plain):

# HELP graphmind_nodes_total Total number of nodes
# TYPE graphmind_nodes_total gauge
graphmind_nodes_total 1042
# HELP graphmind_edges_total Total number of edges
# TYPE graphmind_edges_total gauge
graphmind_edges_total 3891
# HELP graphmind_query_duration_seconds Query execution time
# TYPE graphmind_query_duration_seconds histogram
...

Error Responses

All endpoints return errors in a consistent format:

{
"error": "Parse error: unexpected token at line 1, column 5"
}

HTTP status codes:

CodeMeaning
200Success
400Bad request (invalid Cypher, missing fields)
401Unauthorized (missing or invalid token)
404Not found (unknown endpoint or graph)
500Internal server error

Examples in Python (requests)

import requests

BASE = "http://localhost:8080"

# Query
resp = requests.post(f"{BASE}/api/query", json={
"query": "MATCH (p:Person) RETURN p.name, p.age",
"graph": "default"
})
data = resp.json()
print(data["records"])

# Status
status = requests.get(f"{BASE}/api/status").json()
print(f"Nodes: {status['storage']['nodes']}")

# Schema
schema = requests.get(f"{BASE}/api/schema").json()
for nt in schema["node_types"]:
print(f"{nt['label']}: {nt['count']} nodes")

# Import CSV
csv_data = "name,age\nAlice,30\nBob,25"
resp = requests.post(f"{BASE}/api/import/csv", json={
"csv": csv_data,
"label": "Person"
})
print(resp.json())

Examples in JavaScript (fetch)

const BASE = "http://localhost:8080";

// Query
const result = await fetch(`${BASE}/api/query`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: "MATCH (p:Person) RETURN p.name, p.age",
graph: "default",
}),
}).then((r) => r.json());

console.log(result.records);

// Status
const status = await fetch(`${BASE}/api/status`).then((r) => r.json());
console.log(`Nodes: ${status.storage.nodes}`);

// Schema
const schema = await fetch(`${BASE}/api/schema`).then((r) => r.json());
schema.node_types.forEach((nt) => console.log(`${nt.label}: ${nt.count}`));

// NLQ
const nlq = await fetch(`${BASE}/api/nlq`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "Who knows Alice?" }),
}).then((r) => r.json());

console.log("Generated Cypher:", nlq.cypher);