Reference
Clauses
MATCH
Finds patterns in the graph. Variables are bound to matched nodes and relationships.
MATCH (n:Person {name: 'Alice'}) RETURN n MATCH (a:Person)-[:FRIEND]->(b:Person) RETURN a, b
OPTIONAL MATCH
Like MATCH, but returns null for unmatched variables instead of eliminating the row.
MATCH (a:Person) OPTIONAL MATCH (a)-[:FRIEND]->(b) RETURN a.name, b.name
WHERE
Filters results. Used after MATCH or WITH.
MATCH (n:Person) WHERE n.age > 30 RETURN n MATCH (n:Person) WHERE n.name STARTS WITH 'A' AND n.age >= 25 RETURN n
RETURN
Projects output columns. Supports aliases, DISTINCT, and * for all variables.
MATCH (n:Person) RETURN n.name AS personName MATCH (n:Person) RETURN DISTINCT n.city MATCH (n:Person) RETURN *
WITH
Pipes intermediate results between query parts. Same syntax as RETURN.
MATCH (n:Person) WITH n.name AS name, n.age AS age WHERE age > 30 RETURN name
ORDER BY / SKIP / LIMIT
Sorting and pagination. SKIP and LIMIT accept integers or $parameter refs.
MATCH (n:Person) RETURN n.name ORDER BY n.age DESC MATCH (n:Person) RETURN n SKIP 5 LIMIT 10
CREATE
Creates nodes and relationships.
CREATE (n:Person {name: 'Alice', age: 30}) CREATE (a)-[:FRIEND {since: 2020}]->(b)
MERGE
Match-or-create. Finds the pattern if it exists, creates it if not.
MERGE (p:Person {name: 'Alice'}) ON CREATE SET p.created = timestamp() ON MATCH SET p.lastSeen = timestamp()
SET
Updates properties on nodes or relationships.
MATCH (n:Person {name: 'Alice'}) SET n.age = 31 MATCH (n:Person {name: 'Alice'}) SET n += {city: 'NYC'}
DELETE / DETACH DELETE
Removes nodes and relationships. DETACH DELETE also removes connected edges.
MATCH (n:Person {name: 'Alice'}) DELETE n MATCH (n:Person {name: 'Alice'}) DETACH DELETE n
REMOVE
Removes properties from nodes.
MATCH (n:Person) REMOVE n.age
UNWIND
Expands a list into individual rows.
UNWIND [1, 2, 3] AS x RETURN x UNWIND [{name: 'Alice'}, {name: 'Bob'}] AS props CREATE (p:Person) SET p.name = props.name
FOREACH
Iterates over a list and executes mutation clauses per element.
MATCH (a:Person {name: 'Alice'}) FOREACH (name IN ['Bob', 'Carol'] | CREATE (a)-[:FRIEND]->(:Person {name: name}) )
CALL { subquery }
Runs a subquery for each incoming row.
MATCH (p:Person) CALL { WITH p MATCH (p)-[:FRIEND]->(f) RETURN count(f) AS friendCount } RETURN p.name, friendCount
UNION / UNION ALL
Combines results from multiple queries. UNION deduplicates; UNION ALL keeps all.
MATCH (a:Person) RETURN a.name AS name UNION MATCH (b:Company) RETURN b.name AS name
CREATE INDEX / DROP INDEX
CREATE INDEX ON :Person(name) DROP INDEX ON :Person(name)
Patterns
Node Patterns
() -- anonymous node (n) -- bound to variable n (n:Person) -- with label (n:Person:Employee) -- multiple labels (n:Person {name: 'Alice'}) -- with property filter
Relationship Patterns
-[r:FRIEND]-> -- outgoing, typed <-[r:FRIEND]- -- incoming, typed -[r:FRIEND]- -- undirected --> -- outgoing shorthand <-- -- incoming shorthand -- -- undirected shorthand -[:FRIEND|COWORKER]-> -- type alternation -[r:FRIEND {since: 2020}]-> -- with property filter
Variable-Length Paths
-[*1..3]-> -- 1 to 3 hops -[*2]-> -- exactly 2 hops -[*]-> -- 1 to 10 hops (default) -[:FRIEND*1..5]-> -- typed variable-length
Maximum depth is capped at 10.
Named Paths
p = (a:Person)-[:FRIEND*1..3]->(b:Person) RETURN p, nodes(p), relationships(p)
The path variable is an array of alternating [node, edge, node, edge, ...].
shortestPath / allShortestPaths
-- Find the shortest path between two nodes MATCH p = shortestPath((a:Person {name:'Alice'})-[*]-(b:Person {name:'Charlie'})) RETURN p, length(p) -- Find all shortest paths (all paths at minimum depth) MATCH p = allShortestPaths((a:Person)-[*]-(b:Person)) RETURN p
Uses BFS to find the shortest path(s). Supports typed relationships and property filters. Maximum depth defaults to 15.
Expressions
Literals
42 -- integer 3.14 -- float 'hello' -- string true / false -- boolean null -- null [1, 2, 3] -- list {name: 'Alice'} -- map
CASE Expression
Generic form:
CASE WHEN n.age > 30 THEN 'senior' WHEN n.age > 20 THEN 'junior' ELSE 'unknown' END
Simple form:
CASE n.status WHEN 'active' THEN 1 WHEN 'inactive' THEN 0 ELSE -1 END
List Indexing & Slicing
list[0] -- first element list[-1] -- last element list[1..3] -- slice (exclusive end)
Out-of-bounds access returns null.
List Comprehension
[x IN [1,2,3,4,5] WHERE x > 2 | x * 10] -- [30, 40, 50] [x IN range(1, 5) | x * x] -- [1, 4, 9, 16, 25]
Pattern Comprehension
-- Inline pattern matching that returns a list MATCH (a:Person) RETURN [(a)-[:FRIEND]->(f) | f.name] AS friends -- With WHERE filter MATCH (a:Person) RETURN [(a)-[:FRIEND]->(f) WHERE f.age > 25 | f.name] AS olderFriends
Evaluates a pattern inline per row and returns a list of projected values.
Map Projection
-- Select specific properties from a node MATCH (n:Person) RETURN n { .name, .age } AS person -- Mix shorthand and computed properties MATCH (n:Person) RETURN n { .name, upperName: toUpper(n.name) } AS person
Creates a map from selected properties. Shorthand .prop reads from the object; explicit key: expr evaluates an expression.
EXISTS Subquery
MATCH (n:Person) WHERE exists { MATCH (n)-[:FRIEND]->() } RETURN n
Parameters
Query parameters use $name syntax and are resolved from the execution context.
MATCH (n:Person {name: $name}) RETURN n MATCH (n:Person) RETURN n LIMIT $limit
Operators
Comparison
= | Equal |
<> != | Not equal |
< > <= >= | Ordering |
Boolean
AND | Logical and |
OR | Logical or |
XOR | Exclusive or |
NOT | Negation |
Arithmetic
+ - * / % | Standard math |
^ | Power |
+ | String concatenation |
String
STARTS WITH | Prefix match |
ENDS WITH | Suffix match |
CONTAINS | Substring match |
=~ | Regex (full-string, JS syntax) |
Null & List
IS NULL | Null check |
IS NOT NULL | Non-null check |
IN | List membership |
Aggregate Functions
Aggregate functions group rows and reduce them. All support DISTINCT: count(DISTINCT x).
| Function | Description |
|---|---|
count(expr) / count(*) | Count values or rows |
collect(expr) | Collect into a list |
sum(expr) | Sum numeric values |
avg(expr) | Average |
min(expr) / max(expr) | Min / max |
stdev(expr) / stdevp(expr) | Sample / population std dev |
percentileCont(expr, pct) | Continuous percentile |
percentileDisc(expr, pct) | Discrete percentile |
String Functions
| Function | Description |
|---|---|
toLower(str) / toUpper(str) | Case conversion |
trim(str) / ltrim(str) / rtrim(str) | Whitespace trimming |
replace(str, search, repl) | Replace all occurrences |
substring(str, start, len?) | Extract substring |
left(str, n) / right(str, n) | First / last n chars |
split(str, delim) | Split into list |
reverse(str) | Reverse |
toString(val) | Convert to string |
Math Functions
| Function | Description |
|---|---|
abs(x) | Absolute value |
round(x) / floor(x) / ceil(x) | Rounding |
sign(x) | Sign (-1, 0, 1) |
sqrt(x) | Square root |
log(x) / log10(x) | Logarithm |
exp(x) | e^x |
rand() | Random [0, 1) |
| Function | Description |
|---|---|
sin cos tan | Trigonometric |
asin acos atan atan2 | Inverse trig |
degrees(x) / radians(x) | Angle conversion |
pi() / e() | Constants |
List Functions
| Function | Description |
|---|---|
head(list) / last(list) | First / last element |
tail(list) | All except first |
size(list) | Length (also strings) |
range(start, end, step?) | Integer list (inclusive) |
reverse(list) | Reverse |
coalesce(a, b, ...) | First non-null |
Type Functions
| Function | Description |
|---|---|
toInteger(val) / toFloat(val) / toBoolean(val) | Type conversion |
toStringOrNull / toIntegerOrNull / toFloatOrNull / toBooleanOrNull | Null-safe conversion |
valueType(val) | Type name string |
Graph Functions
| Function | Description |
|---|---|
id(entity) | Internal ID |
labels(node) | List of labels |
type(rel) | Relationship type |
properties(entity) / keys(entity) | Property map / keys |
startNode(rel) / endNode(rel) | Source / target node |
nodes(path) / relationships(path) | Extract from path |
length(path) | Path or list length |
timestamp() | Epoch milliseconds |
Examples
Build a social network
CREATE (alice:Person {name: 'Alice', age: 30}) CREATE (bob:Person {name: 'Bob', age: 25}) MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) CREATE (a)-[:FRIEND {since: 2020}]->(b)
Aggregation with grouping
MATCH (p:Person)-[:FRIEND]->(f) RETURN p.name, count(f) AS friends ORDER BY friends DESC
Batch upsert
UNWIND [{name: 'Alice'}, {name: 'Bob'}] AS props MERGE (p:Person {name: props.name}) ON CREATE SET p.status = 'new' ON MATCH SET p.status = 'existing'
Variable-length path
MATCH p = (a:Person {name: 'Alice'})-[:FRIEND*1..3]->(b) RETURN b.name, length(relationships(p)) AS distance
List comprehension
WITH [1,2,3,4,5,6,7,8,9,10] AS nums RETURN [x IN nums WHERE x % 2 = 0 | x * x] AS evenSquares
Base URL
https://api.graphiquity.com
All endpoints are relative to this base. Requests and responses use application/json.
Authentication
All authenticated requests require the Authorization header.
API Key Recommended for apps
Create an API key on the API Keys page. Pass it as a Bearer token:
Authorization: Bearer gq_a1b2c3d4e5f6...
API keys grant access to the /query endpoint. Store keys securely — they cannot be retrieved after creation.
JWT Token For web apps
Authenticate via Cognito to get an ID token:
Authorization: Bearer eyJraWQiOiJ...
JWT tokens grant access to all endpoints and expire after 1 hour.
POST /query
Execute a Cypher query against a graph. JWT API Key
Request Body
| Field | Type | Description | |
|---|---|---|---|
graph | string | Required | Graph name |
cypher | string | Required | Cypher query |
parameters | object | Optional | Query parameters ($param syntax) |
atTime | string | Optional | ISO 8601 timestamp for time travel |
Response
{
"status": 200,
"data": [
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 }
]
}
Examples
curl -X POST https://api.graphiquity.com/query \ -H "Authorization: Bearer gq_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "graph": "my-graph", "cypher": "MATCH (n:Person) WHERE n.age > $minAge RETURN n.name, n.age", "parameters": { "minAge": 25 } }'
const API_KEY = 'gq_YOUR_API_KEY'; const BASE = 'https://api.graphiquity.com'; async function query(graph, cypher, parameters = {}) { const res = await fetch(`${BASE}/query`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ graph, cypher, parameters }), }); const json = await res.json(); if (json.error) throw new Error(json.error); return json.data; } // Usage const people = await query('my-graph', 'MATCH (n:Person) WHERE n.age > $minAge RETURN n.name, n.age', { minAge: 25 } );
import requests API_KEY = "gq_YOUR_API_KEY" BASE = "https://api.graphiquity.com" def query(graph, cypher, parameters=None, at_time=None): body = {"graph": graph, "cypher": cypher} if parameters: body["parameters"] = parameters if at_time: body["atTime"] = at_time resp = requests.post( f"{BASE}/query", headers={"Authorization": f"Bearer {API_KEY}"}, json=body, ) data = resp.json() if "error" in data: raise Exception(data["error"]) return data["data"] # Usage people = query("my-graph", "MATCH (n:Person) WHERE n.age > $minAge RETURN n.name, n.age", {"minAge": 25} )
package main import ("bytes"; "encoding/json"; "fmt"; "net/http") const apiKey = "gq_YOUR_API_KEY" const baseURL = "https://api.graphiquity.com" func query(graph, cypher string, params map[string]interface{}) (map[string]interface{}, error) { body, _ := json.Marshal(map[string]interface{}{ "graph": graph, "cypher": cypher, "parameters": params, }) req, _ := http.NewRequest("POST", baseURL+"/query", bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) return result, nil }
require 'net/http'; require 'json'; require 'uri' API_KEY = "gq_YOUR_API_KEY" BASE = "https://api.graphiquity.com" def query(graph, cypher, parameters: {}) uri = URI("#{BASE}/query") req = Net::HTTP::Post.new(uri) req["Authorization"] = "Bearer #{API_KEY}" req["Content-Type"] = "application/json" req.body = { graph: graph, cypher: cypher, parameters: parameters }.to_json res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } JSON.parse(res.body)["data"] end
import java.net.http.*; import java.net.URI; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://api.graphiquity.com/query")) .header("Authorization", "Bearer " + API_KEY) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(""" {"graph":"my-graph","cypher":"MATCH (n:Person) RETURN n","parameters":{}} """)).build(); HttpResponse<String> res = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()); System.out.println(res.body());
using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); var body = JsonSerializer.Serialize(new { graph = "my-graph", cypher = "MATCH (n:Person) WHERE n.age > $minAge RETURN n", parameters = new { minAge = 25 } }); var res = await client.PostAsync("https://api.graphiquity.com/query", new StringContent(body, Encoding.UTF8, "application/json")); Console.WriteLine(await res.Content.ReadAsStringAsync());
<?php $ch = curl_init('https://api.graphiquity.com/query'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer gq_YOUR_API_KEY', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'graph' => 'my-graph', 'cypher' => 'MATCH (n:Person) RETURN n.name', ]), ]); $result = json_decode(curl_exec($ch), true); print_r($result['data']); ?>
Creating Data
Use CREATE and MERGE via the query endpoint.
// Create a node await query('my-graph', `CREATE (p:Person {name: $name, age: $age}) RETURN p`, { name: 'Alice', age: 30 }); // Create a relationship await query('my-graph', ` MATCH (a:Person {name: $from}), (b:Person {name: $to}) CREATE (a)-[:FRIEND {since: $year}]->(b)`, { from: 'Alice', to: 'Bob', year: 2024 }); // Batch upsert await query('my-graph', ` UNWIND $people AS props MERGE (p:Person {name: props.name}) ON CREATE SET p.age = props.age`, { people: [{name:'Alice',age:30}, {name:'Bob',age:25}] });
# Create a node query("my-graph", "CREATE (p:Person {name: $name, age: $age})", {"name": "Alice", "age": 30}) # Batch upsert query("my-graph", """ UNWIND $people AS props MERGE (p:Person {name: props.name}) ON CREATE SET p.age = props.age """, {"people": [{"name":"Alice","age":30}, {"name":"Bob","age":25}]})
curl -X POST https://api.graphiquity.com/query \ -H "Authorization: Bearer gq_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"graph":"my-graph","cypher":"CREATE (:Person {name:$name})", "parameters":{"name":"Alice"}}'
Temporal Queries (Time Travel)
Pass atTime as an ISO 8601 timestamp to query the graph as it was at that point.
const res = await fetch(`${BASE}/query`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ graph: 'my-graph', cypher: 'MATCH (n:Person) RETURN n.name, n.age', atTime: '2024-01-01T00:00:00Z', }), });
result = query("my-graph", "MATCH (n:Person) RETURN n", at_time="2024-01-01T00:00:00Z")
curl -X POST https://api.graphiquity.com/query \ -H "Authorization: Bearer gq_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"graph":"my-graph","cypher":"MATCH (n) RETURN n","atTime":"2024-01-01T00:00:00Z"}'
Graph Management JWT only
GET /graphs
List all graphs.
POST /graphs
Create a graph.
| Field | Type | Description | |
|---|---|---|---|
name | string | Required | Letters, numbers, hyphens, underscores. Max 64 chars. |
description | string | Optional | Description |
DELETE /graphs/{name}
Delete a graph.
API Key Management JWT only
GET /apikeys
List keys (prefixes only).
POST /apikeys
Create a key. The full key is only shown once.
// Response { "status": 201, "data": { "key": "gq_a1b2c3...", "prefix": "gq_a1b2c3d", "name": "My Key" } }
DELETE /apikeys/{prefix}
Revoke a key by prefix.
Graph Schema
GET /graphs/{name}/schema
Returns the labels and edge types present in the graph. Useful for autocomplete and introspection.
// Response { "status": 200, "data": { "labels": ["Person", "Company"], "edgeTypes": ["FRIEND", "WORKS_AT"] } }
Contracts (Schema Validation)
Contracts enforce per-label and per-edge-type schemas — type checking, required fields, unique constraints, and regex patterns. Each contract has an enforcement mode:
| Mode | Behavior |
|---|---|
OFF | No validation (default) |
WARN | Writes succeed but response includes warnings array |
STRICT | Invalid writes are rejected with HTTP 400 |
GET /graphs/{name}/contracts
List all contracts defined for the graph.
// Response { "status": 200, "data": [ { "kind": "node", "label": "Person", "mode": "STRICT", "properties": { "name": { "type": "string", "required": true, "maxLength": 200 }, "email": { "type": "string", "unique": true }, "age": { "type": "integer" } } } ] }
PUT /graphs/{name}/contracts
Create or replace a contract. The request body is the contract object.
| Field | Type | Description | |
|---|---|---|---|
kind | string | Required | "node" or "edge" |
label | string | Node label (required when kind=node) | |
relType | string | Edge type (required when kind=edge) | |
mode | string | OFF | WARN | STRICT | |
properties | object | Map of property name → definition |
Property definition fields: type (string|integer|float|boolean|timestamp|json|vector), required, unique, maxLength, pattern, enum, dims.
// JavaScript await fetch("https://api.graphiquity.com/graphs/myGraph/contracts", { method: "PUT", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, body: JSON.stringify({ kind: "node", label: "Person", mode: "STRICT", properties: { name: { type: "string", required: true, maxLength: 200 }, email: { type: "string", unique: true, pattern: "^[^@]+@[^@]+$" }, age: { type: "integer" } } }) });
# Python import requests requests.put( "https://api.graphiquity.com/graphs/myGraph/contracts", headers={"Authorization": f"Bearer {token}"}, json={ "kind": "node", "label": "Person", "mode": "STRICT", "properties": { "name": {"type": "string", "required": True, "maxLength": 200}, "email": {"type": "string", "unique": True}, "age": {"type": "integer"} } } )
DELETE /graphs/{name}/contracts
Remove a contract. Pass {"label": "Person"} for node contracts or {"relType": "FRIEND"} for edge contracts.
Cypher Constraint Syntax
You can also create and drop unique constraints via Cypher:
// Create a unique constraint CREATE CONSTRAINT ON (p:Person) ASSERT p.email IS UNIQUE // Drop a unique constraint DROP CONSTRAINT ON (p:Person) ASSERT p.email IS UNIQUE
Usage Analytics
GET /usage
Returns per-day operation counts for the tenant. Data is retained for 90 days.
// Response { "status": 200, "data": [ { "date": "2026-03-02", "operation": "query", "count": 142 }, { "date": "2026-03-02", "operation": "createGraph", "count": 1 } ] }
User Management JWT only
GET /users
List all users in the tenant.
POST /users/invite
Invite a user. Requires owner role.
| Field | Type | Description | |
|---|---|---|---|
email | string | Required | Email address |
role | string | Optional | owner or member (default) |
DELETE /users/{userId}
Remove a user. Requires owner role.
Errors
{ "status": 400, "error": "graph and cypher are required" }
| Status | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad request (missing fields, invalid Cypher) |
401 | Unauthorized |
403 | Forbidden (insufficient role) |
404 | Not found |
409 | Conflict (duplicate resource) |
429 | Rate limit exceeded (60 req/min per tenant) |
500 | Internal error |
Quick Start
1. Get an API key
Go to API Keys and create one. Copy it immediately.
2. Create a graph
Use the Dashboard or the POST /graphs endpoint.
3. Query away
const gq = (cypher, p) => fetch('https://api.graphiquity.com/query', { method: 'POST', headers: { Authorization: 'Bearer gq_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ graph: 'my-graph', cypher, parameters: p }), }).then(r => r.json()).then(r => r.data); await gq("CREATE (:Person {name:'Alice'})-[:FRIEND]->(:Person {name:'Bob'})"); const friends = await gq("MATCH (a)-[:FRIEND]->(b) RETURN a.name, b.name"); // [{ "a.name": "Alice", "b.name": "Bob" }]
import requests def gq(cypher, p={}): return requests.post("https://api.graphiquity.com/query", headers={"Authorization": "Bearer gq_YOUR_KEY"}, json={"graph": "my-graph", "cypher": cypher, "parameters": p}).json()["data"] gq("CREATE (:Person {name:'Alice'})-[:FRIEND]->(:Person {name:'Bob'})") friends = gq("MATCH (a)-[:FRIEND]->(b) RETURN a.name, b.name") # [{"a.name": "Alice", "b.name": "Bob"}]