Skip to content
Docs Pricing Sign In Start Free

Docs

Sign up free to get an API key and start querying in under 60 seconds. Create Free Account

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

ANDLogical and
ORLogical or
XORExclusive or
NOTNegation

Arithmetic

+ - * / %Standard math
^Power
+String concatenation

String

STARTS WITHPrefix match
ENDS WITHSuffix match
CONTAINSSubstring match
=~Regex (full-string, JS syntax)

Null & List

IS NULLNull check
IS NOT NULLNon-null check
INList membership

Aggregate Functions

Aggregate functions group rows and reduce them. All support DISTINCT: count(DISTINCT x).

FunctionDescription
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

FunctionDescription
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

FunctionDescription
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)
FunctionDescription
sin cos tanTrigonometric
asin acos atan atan2Inverse trig
degrees(x) / radians(x)Angle conversion
pi() / e()Constants

List Functions

FunctionDescription
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

FunctionDescription
toInteger(val) / toFloat(val) / toBoolean(val)Type conversion
toStringOrNull / toIntegerOrNull / toFloatOrNull / toBooleanOrNullNull-safe conversion
valueType(val)Type name string

Graph Functions

FunctionDescription
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

FieldTypeDescription
graphstringRequiredGraph name
cypherstringRequiredCypher query
parametersobjectOptionalQuery parameters ($param syntax)
atTimestringOptionalISO 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.

FieldTypeDescription
namestringRequiredLetters, numbers, hyphens, underscores. Max 64 chars.
descriptionstringOptionalDescription

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:

ModeBehavior
OFFNo validation (default)
WARNWrites succeed but response includes warnings array
STRICTInvalid 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.

FieldTypeDescription
kindstringRequired"node" or "edge"
labelstringNode label (required when kind=node)
relTypestringEdge type (required when kind=edge)
modestringOFF | WARN | STRICT
propertiesobjectMap 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.

FieldTypeDescription
emailstringRequiredEmail address
rolestringOptionalowner or member (default)

DELETE /users/{userId}

Remove a user. Requires owner role.

Errors

{ "status": 400, "error": "graph and cypher are required" }
StatusMeaning
200Success
201Created
400Bad request (missing fields, invalid Cypher)
401Unauthorized
403Forbidden (insufficient role)
404Not found
409Conflict (duplicate resource)
429Rate limit exceeded (60 req/min per tenant)
500Internal error

Quick Start

1. Get an API key

Sign up, then 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"}]