HTTP/JSON API
Overview Endpoints
| Action | Method | Endpoint |
|---|---|---|
GET |
|
|
GET, HEAD |
|
|
GET, HEAD |
|
|
GET |
|
|
POST |
|
|
GET |
|
|
GET |
|
|
POST |
|
|
POST |
|
|
GET |
|
|
GET |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
Overview Query & Command Parameters
| Parameter | Type | Values |
|---|---|---|
language |
Required |
|
command |
Required |
encoded command string |
awaitResponse |
Optional |
set synchronous ( |
limit |
Optional |
maximum number of results |
params |
Optional |
map of parameters |
serializer |
Optional |
|
autoCommit |
Optional |
Boolean. |
Introduction
The ArcadeDB Server is accessible from the remote through the HTTP/JSON protocol. The protocol is very simple. For this reason, you don’t need a driver, because every modern programming language provides an easy way to execute HTTP requests and parse JSON.
For the examples in this chapter we’re going to use curl.
Every request must be authenticated by passing user and password as HTTP basic authentication (in HTTP Headers).
In the examples below we’re going to always use "root" user with password "arcadedb-password".
Under Windows (Powershell) single and double quotes inside a single or double quoted string need to be replaced with their Unicode entity representations \u0022 (double quote) and
\u0027 (single quote).
This is for example the case in the data argument (-d) of POST requests.
|
Token-Based Authentication
In addition to HTTP Basic Authentication, ArcadeDB supports token-based authentication. This allows you to login once with credentials and receive a token that can be used for subsequent requests, avoiding the need to send username and password with every request.
This is particularly useful when:
-
Building applications that make multiple API calls
-
You want to avoid storing credentials in your application
-
You need to manage user sessions with explicit logout capability
Authentication Flow:
-
Call
POST /api/v1/loginwith Basic Auth credentials to obtain a token -
Use the token in subsequent requests via
Authorization: Bearer <token>header -
Call
POST /api/v1/logoutto invalidate the token when done
Example workflow:
# 1. Login and get a token
$ curl -X POST http://localhost:2480/api/v1/login \
--user root:arcadedb-password
# Response: {"token":"AU-ArcadeDB_0-2af64e60-8455-423a-bc64-ed0e19729f04","user":"root"}
# 2. Use the token for subsequent requests
$ curl http://localhost:2480/api/v1/query/mydb/sql/select%201 \
-H "Authorization: Bearer AU-ArcadeDB_0-2af64e60-8455-423a-bc64-ed0e19729f04"
# 3. Logout when done
$ curl -X POST http://localhost:2480/api/v1/logout \
-H "Authorization: Bearer AU-ArcadeDB_0-2af64e60-8455-423a-bc64-ed0e19729f04"
Authentication tokens expire after a configurable period of inactivity (default is 30 minutes).
See server.httpAuthSessionExpireTimeout to configure this timeout.
Token revocation:
|
Since version 26.9.1, deleting a user or changing its password immediately invalidates every token already
issued to that user, on every server of a cluster. Previously such a token kept working until it expired on
its own. This applies however the change is made: the |
Tokens in a cluster:
|
Since version 26.10.1 a token issued by one server of a cluster is honoured by every other server, so a load
balancer in front of the cluster needs no sticky sessions. The token names the server that issued it,
Every server must be able to map the name in the token to a member of the cluster. This is the same rule a
server uses to find itself in API tokens ( |
Number of concurrent tokens:
|
Since version 26.9.1 the server bounds how many authentication tokens it keeps.
A single user may hold up to The client metadata stored with a session ( |
Server-Side Transactions
ArcadeDB implements server-side transaction over HTTP stateless protocol by using sessions.
A session is created with the /begin request and returns a session id in the response header (example arcadedb-session-id: AS-ee056170-dc9b-4956-8d71-d7cfa01900d4).
Use the session id in the request header of further commands you want to execute in the same transaction and request /commit to commit the server side transaction or /rollback to rollback the changes.
After a period of inactivity (default is 5 seconds, see server.httpSessionExpireTimeout), the server automatically rolls back and purges expired transactions.
If a request names a session id the server can no longer resolve - because it expired, or was already committed or
rolled back - a write is refused with 404, while a read still answers, running outside any transaction. A read
that degraded this way says so: the response carries an arcadedb-session-expired header holding the id that could
not be resolved, so a client can tell it apart from a read that ran inside its transaction. This applies to
GET /query, /begin, /commit, /rollback and the time-series and Prometheus read endpoints.
The time-series and observability endpoints accept a session id so they run under that session’s user and keep it
from expiring while you use it, but they do not take part in its transaction: samples are durable as soon as they
are written, and the reads only read. Since 26.10.1 an error on one of them therefore leaves your open transaction
exactly as it was. Before, it rolled the transaction back while leaving the session alive, so a later /commit
reported success and the work was silently gone. The endpoints this covers:
-
the three time-series endpoints,
/api/v1/ts/{database}/write,/queryand/latest; -
the PromQL endpoints under
/api/v1/ts/{database}/prom, and the Prometheusremote_readandremote_writeendpoints; -
the Grafana query, metadata and health endpoints.
The two errors easiest to hit on the observability routes are a malformed start/end timestamp and a read whose
answer exceeds arcadedb.server.maxResultRows; neither touches your transaction any more.
Transaction Scripts
In case SQL (sql) is supposed to be used as language for a transactions, the language variant SQL Script (sqlscript) is also available.
A sqlscript can consist of one or multiple SQL statements, which is collectively treated as a transaction.
Hence, for such a batch of SQL statements, no begin and commit commands are necessary, since begin and commit implicitly enclose any sqlscript command.
Streaming Change Events
| This feature only works on the server where changes are executed. In a replicated environment, the changes executed on other servers would not fire events on all the servers in the cluster, but only on the local server. The cluster support is coming soon. |
The Java API supports real-time change notifications, which the HTTP API implements via a websocket. You can opt into notifications for all changes that occur on a database, or filter by the operation (i.e. create, update, delete) or underlying entity type.
To connect, point your favorite WebSocket client to the ws://SERVER:PORT/ws endpoint.
You will need to authenticate with HTTP Basic, which for some clients (like most browsers) is only possible via the URI, like this: ws://USERNAME:PASSWORD@SERVER:PORT/ws.
Others will require that you set the Authorization header directly.
Check the documentation for your client of choice for details.
To subscribe/unsubscribe to change events, send JSON messages using the following structure:
| Property | Required | Description |
|---|---|---|
action |
Required |
|
database |
Required |
The database name. |
type |
Optional |
The entity type to filter by. |
changeTypes |
Optional |
Array of change types you’d like to receive. Must be |
Example: to subscribe to all changes (create, update, delete) for the type Movie in the database movies, use:
{"action": "subscribe", "database": "movies", "type": "Movie"}
If instead, you only want updates, send:
{"action": "subscribe", "database": "movies", "type": "Movie", "changeTypes": ["update"]}
If you want every change on the database (use with caution!):
{"action": "subscribe", "database": "movies"}
Once subscribed, you will get JSON messages for any matching changes with the following properties:
| Property | Description |
|---|---|
database |
The source database. |
changeType |
create, update or delete. |
record |
The full record that generated the change event. |
A subscription is not permanent. Starting from v26.9.1, the server re-checks on every event that the connected user is still allowed to read the database: if that access is revoked, or the user is deleted, the subscription is dropped and the connection closed instead of continuing to stream.
A subscriber is also expected to keep reading. Events are sent asynchronously, so a client that stops consuming
them accumulates unsent frames in the server’s memory. Once more than server.eventBusMaxPendingBytes (16 MB by
default) is outstanding towards one connection, that subscription is dropped and the connection closed. Set it to
0 to disable the limit.
In production server mode an error frame answering a failed action carries "The request failed. Check the server
log for the details" instead of the engine’s message, which is written to the server log (since v26.11.1). See
Error details in production mode.
Streaming Inserts over WebSocket
Starting from v26.10.1, the same ws://SERVER:PORT/ws connection can run a duplex insert session: the client sends
records in chunks and gets one acknowledgement per chunk while it is still sending the next, and it decides when
the session commits. This is the WebSocket counterpart of the gRPC InsertBidirectional RPC. Each frame is a JSON
object whose action is one of:
| Action | Description |
|---|---|
start |
Opens a session on |
chunk |
|
commit, rollback |
Ends the session. Answered with |
The options object of start accepts:
| Option | Description |
|---|---|
targetType |
Default type of the records, overridable per record with |
transactionMode |
|
conflictMode |
What to do with a record whose key is already taken: |
keyColumns |
The properties an existing record is matched on. Required for |
updateColumnsOnConflict |
With |
validateOnly |
|
The batchAck frame carries chunkSeq, the counters received, inserted, updated, ignored, failed, and an
errors array with rowIndex, code and message for each record that could not be applied; the rest of the
chunk still goes in. A session the client walks away from - the connection drops, or no frame arrives for
server.wsInsertSessionExpireTimeout milliseconds - is rolled back, and the idle case is reported with an unsolicited
error frame.
In production server mode the engine’s text in an error frame and in the message of an errors entry (for a
conflict, the key values the row carried) is replaced by "The request failed. Check the server log for the details",
and the detail is written to the server log. The code and the exception class are kept. Text the protocol writes about
the request itself, such as a missing keyColumns, an invalid option or an expired session, stays visible. Before
v26.11.1 the message was sent as is in every mode. See Error details in production mode.
|
A /ws connection is allowed server.wsMaxControlFrameSize (64 KB by default) per text frame until it has an
insert session, and server.wsMaxInsertFrameSize (16 MB) while it has one, so a chunk can carry a whole batch
while a connection that never inserts stays on the small budget. A frame over the budget in force closes the
connection with a 1009 close code. A separate cap, server.wsMaxInsertChunkRows (100,000), bounds the number of
records in one chunk after parsing; a chunk over it is refused with an error frame and leaves the session open,
so the client can split the batch and resend it under the same chunkSeq.
Example, upserting people by email:
{"action": "start", "database": "crm", "options": {"targetType": "Person", "conflictMode": "update", "keyColumns": ["email"]}}
{"action": "chunk", "sessionId": "...", "chunkSeq": 1, "records": [{"email": "[email protected]", "name": "Ann"}, {"email": "[email protected]", "name": "Bob"}]}
{"action": "commit", "sessionId": "..."}
Responses
The server answers a HTTP request with a response.
This response can have a body, which will always be in the JSON format.
Generally, a successful response (HTTP status codes 2xx) contains a result field, while an erroneous request (HTTP status code 4xx) has an error field, and a server error (HTTP status code 5xx), in addition to the error field, provides a detail and an exception field.
Retrying Requests Safely (X-Request-Id)
Every request can carry an X-Request-Id header. The server echoes it on the response and logs it with the request
(it generates one when the request has none), which is what correlates a failure with the server log.
On a POST it also makes a retry safe to send verbatim. A successful (2xx) response is kept for
arcadedb.ha.idempotencyCacheTtlMs milliseconds, keyed by the id together with the method, path, database and body,
and bound to the authenticated user. An identical retry is answered from it instead of executing the request a second
time, so a client that lost the answer to a write, a restore or an import can simply send it again with the same id.
-
A failed request is not kept: its retry executes afresh.
-
While the first request is still executing, an identical retry waits briefly for it and then answers
409 Conflictwith aRetry-Afterheader, namingRequestStillInFlightException. The request was not executed again: retry it later with the same id to receive the result of the execution in progress. -
A
restore database,restore backuporimport databaseasked for as an SSE progress stream (Accept: text/event-stream) is replayed as a one-event stream carrying itscompletedevent, since a finished stream cannot be sent again. A retry without that header receives the JSON answer instead, whichever way the first request asked. -
Not replayed: a request inside a client-managed transaction (it carries
arcadedb-session-id), a request asking for an NDJSON stream, a response larger thanarcadedb.ha.idempotencyCacheMaxBodyBytesbytes, and the bulk-load routePOST /api/v1/batch/{database}, whose body is streamed rather than buffered and so cannot be part of the key.
Every response carries the header X-ArcadeDB-Replay-Protection: true, which tells a client that this server answers a
repeated id from that cache. The remote Java driver (RemoteDatabase, RemoteServer) sends one X-Request-Id per
command call, outside a transaction, and once it has seen that header it sends the same call again, with the same id,
to the same server when the response was lost, instead of failing with "the server may already have applied it".
The retry names the server process in X-ArcadeDB-Replay-Instance: a server that restarted, or that no longer holds the answer (too large to keep, expired or evicted), answers 412 and does not run the request again.
(Since v26.10.1.)
Use a new id for every distinct request. The OpenAPI document declares the header and the 409 on every POST
operation that honours them.
Tutorial
Let’s first create an empty database "school" on the server:
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "create database school"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Now let’s create the type "Class":
$ curl -X POST http://localhost:2480/api/v1/command/school \
-d '{"language": "sql", "command": "create document type Class"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
We could insert our first Class by using SQL:
$ curl -X POST http://localhost:2480/api/v1/command/school \
-d '{"language": "sql", "command": "insert into Class set name = '\''English'\'', location = '\''3rd floor'\''"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Or better, using parameters with SQL:
$ curl -X POST http://localhost:2480/api/v1/command/school \
-d '{"language": "sql", "command": "insert into Class set name = :name, location = :location", "params": {"name": "English", "location": "3rd floor"}}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Reference
Get OpenAPI Definition (GET)
Returns a JSON document containing the HTTP API’s OpenAPI definition.
URL Syntax: /api/v1/openapi.json
Response:
-
200OK
Example:
$ curl -X GET "http://localhost:2480/api/v1/openapi.json"
Return:
{"openapi":"3.0.3", ... }
Check if server is ready (GET, HEAD)
Returns a header-only (no content) status about if the ArcadeDB server is ready.
URL Syntax: /api/v1/ready
This endpoint accepts GET or HEAD requests without authentication, and is useful for remote monitoring of server readiness. Use it as a Kubernetes readiness probe. HEAD is supported so tools that check health with a HEAD request (e.g. wget --spider) work out of the box.
Responses:
When arcadedb.server.readinessRequiresHA=true and HA is active, readiness additionally requires the node to have joined the Raft group: it returns 503 (Node has not yet joined the Raft group) until a leader has been elected. Once a leader is known, a second gate requires the node to be a member of the current Raft configuration and, for a follower, to have replayed the committed log to within arcadedb.server.readinessHAMaxLag entries of the commit index; until then it returns 503 (Node is not yet in the Raft configuration or has not caught up). This keeps a (re)joined follower with a wiped or lagging log out of traffic during a rolling restart, so the write quorum is not dropped. The default (false for readinessRequiresHA) preserves the behavior above. See Health probes.
Example:
$ curl -I -X GET "http://localhost:2480/api/v1/ready"
Return:
HTTP/1.1 204 OK
Check server liveness (GET, HEAD)
Returns a header-only (no content) liveness status. Use it as a Kubernetes liveness probe.
URL Syntax: /api/v1/health
This endpoint accepts GET or HEAD requests without authentication and performs no database I/O. It returns 204 whenever the server process and HTTP layer are up; unlike readiness it never reports a busy state, so a warming-up node is not restarted. The one exception: on a High Availability cluster, a node that has given up recovering from a crash loop returns 503, so the orchestrator restarts it. See Health probes.
Response:
Example:
$ curl -I -X GET "http://localhost:2480/api/v1/health"
Return:
HTTP/1.1 204 OK
Get server information (GET)
Returns the current configuration.
URL Syntax: /api/v1/server
The following mode query parameter values are available:
-
basicreturns minimal server information. -
defaultreturns full server configuration (default value when no parameter is given). -
clusterreturns cluster layout.
In default mode the settings array reports, for each server-level setting, the value this server is actually
running on in value and the built-in default in default, with overridden telling you whether the two differ
because the setting was given an explicit value - in the server configuration file, by an embedding application,
or at runtime with set server setting.
Secrets are masked: settings that are secrets in their entirety (the HA cluster token, any key containing
password) report , and arcadedb.server.defaultDatabases, whose value merely *embeds credentials, keeps
its database names, user names and groups while each password is replaced (since v26.10.1).
Responses:
Example:
$ curl -X GET "http://localhost:2480/api/v1/server?mode=basic" \
--user root:arcadedb-password
Return:
{"version": "26.10.1", "serverName": "ArcadeDB_0"}
Send server command (POST)
Sends control commands to server.
URL Syntax: /api/v1/server
The following commands are available:
-
list databasesreturns the list of databases installed in the server -
create database <dbname>creates database with namedbname -
drop database <dbname>deletes database with namedbname -
open database <dbname>opens database with namedbname -
close database <dbname>closes database with namedbname. Refused while a backup, restore, import, export or another close of the same database is running, so it cannot pull the database out from under one; retry once that operation finishes -
create user { "name": "<username>", "password": "<password>", "databases": { "<dbname>": "admin", "<dbname>": "admin" } }creates user credentialsusernameandpasswordand admin access to databasesdbname. -
drop user <username>deletes userusername -
get server events [<filename>]returns a list of server events, optionally a filename of the formserver-event-log-yyyymmdd-HHMMSS.INDEX.jsonl(whereINDEXis a integer, i.e.0) can be given to retrieve older event logs -
shutdown [<serverName>]kills a server gracefully: this one when no name is given, otherwise the named peer of the HA cluster. See Shutdown server. -
set server setting <key> <value>sets the server setting withkeytovalue, see the list of server-level settings -
set database setting <dbname> <key> <value>sets the database’s <dbname> withkeytovalue, see the list of database-level settings -
connect cluster <address>connects this server to a cluster withaddress -
disconnect clusterdisconnects this server from a cluster -
align database <dbname>aligns database<dbname>, see the associated SQL command -
restore database <dbname> <url>restores a database from a backup file. The URL can be a local path (file://), HTTP/HTTPS URL, or classpath resource. The database must not already exist. See how-to/operations/restore.adoc#restore. -
import database <dbname> <url>creates a new database and imports data from the given URL (OrientDB export, GraphML, GraphSON, CSV, JSON). Combinescreate databaseandIMPORT DATABASEin a single command. See how-to/migration/importer.adoc#importer.
Both restore database and import database support SSE progress streaming: send Accept: text/event-stream in the request headers to receive real-time progress events instead of waiting for the response.
With an X-Request-Id, a retry of a restore or import that completed is replayed rather than run again, in either
encoding (see Retrying Requests Safely (X-Request-Id)).
Only root users can run these command, except the list databases command, which every user can run, and this user’s accessible databases are listed.
|
Responses:
Since 26.10.1, a command naming a database that does not exist - open database nosuchdb, for example -
answers 404 instead of 500. Naming a database the server has not got is a mistake in the request, not a
server fault, and it is the same status the server already gave for a database that is closed or dropped. Adjust
any monitoring that alerts on 5xx from this endpoint.
|
Examples:
List databases
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "list databases"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": ["school", "mydatabase"]}
Create database
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "create database mydatabase"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Drop database
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "drop database mydatabase"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Open database
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "open database mydatabase"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Close database
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "close database mydatabase"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Restore database
Restores a database from a backup file (local or remote). This is the fastest way to provision a database from a backup archive.
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "restore database mydatabase https://example.com/backups/mydatabase.zip"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Restoring from a local file:
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "restore database mydatabase file:///path/to/mydatabase-backup.zip"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Create user
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "create user {\"name\": \"myuser\", \"password\": \"mypassword\", \"databases\": {\"mydatabase\": \"admin\"}}"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Drop user
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "drop user myuser"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Shutdown server
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "shutdown"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Shutting down another node of an HA cluster
Naming a server after the command stops that peer instead of the one receiving the request. The command is relayed over the cluster’s own transport — encrypted when the cluster has TLS configured for its side channels, see TLS/SSL for peer-to-peer transfers:
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "shutdown arcadedb-1"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
The name is matched as a substring against each peer’s identifier and its HTTP address, so a partial name is enough:
-
No peer matches it — the request fails with
Cannot find server '<name>' in the cluster. -
More than one peer matches it — the request is refused rather than resolved to an arbitrary one.
arcadedb-1matchesarcadedb-10as well, so on a cluster holding both, name the one you mean exactly. -
The name is this server — this server shuts down, exactly as the plain
shutdowncommand does.
In a Kubernetes StatefulSet each pod’s hostname is the name to use (arcadedb-0, arcadedb-1, …). Prefer POST /api/v1/cluster/leave when the intent is to take a node out of the cluster rather than to stop its process.
|
Get server events
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "get server events"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": [{"time": "2023-06-18 15:37:40.378", "type": "INFO", "component": "Server", "message": "ArcadeDB Server started in \u0027development\u0027 mode (CPUs\u003d8 MAXRAM\u003d4,00GB)"}]}
Set server setting
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "set server setting arcadedb.server.name player0"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Set database setting
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "set database setting mydb arcadedb.typeDefaultBuckets 4"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
Connect cluster
$ curl -X POST http://localhost:2480/api/v1/server \
-d '{"command": "connect cluster 192.168.0.1"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{"result": "ok"}
This adds the given server to this server’s cluster; it does not make this server join another cluster. (Since v26.10.1) An address that resolves to this server itself is refused with 400 instead of being silently accepted as a no-op.
|
Cluster authentication session lookup (POST, internal)
(Since v26.10.1) POST /api/v1/cluster/auth-session is a cluster-internal route that lets servers share authentication tokens (see Tokens in a cluster under Token-Based Authentication). It is not meant for clients and is listed here only so it is not mistaken for a public API. The request must arrive through the cluster-token channel (X-ArcadeDB-Cluster-Token header) on behalf of the root user: a request without that header is refused with 403, even with root credentials, and an invalid cluster token gets 401.
The body is {"token": "<AU-… token>", "action": "validate" | "revoke"} (action defaults to validate). validate answers 200 with {"user": …, "createdAt": …} when this server issued the session, and 404 otherwise (including for a copy held on behalf of another server); revoke drops the session and answers 204. A missing token or an unknown action answers 400.
List Databases (GET)
Returns a list of available databases for the requesting user.
URL Syntax: /api/v1/databases
Responses:
Example:
$ curl -X GET http://localhost:2480/api/v1/databases \
--user root:arcadedb-password
Return:
{"result": ["school", "mydatabase"]}
Does database exist (GET)
Returns boolean answering if database exists.
URL Syntax: /api/v1/exists/{database}
Responses:
Example:
$ curl -X GET http://localhost:2480/api/v1/exists/school \
--user root:arcadedb-password
Return:
{"result": true}
Get operation progress (GET)
Returns the long-running maintenance operations currently in progress on this server for one database, with their step-by-step progress. CHECK DATABASE, REBUILD INDEX, COMPACT INDEX, BACKUP DATABASE and IMPORT DATABASE publish their progress here while they run. The endpoint answers from an in-memory snapshot without touching the database, so it is safe to poll at any frequency (the console and Studio poll it to render a live progress bar). In a cluster each server reports its own operations.
URL Syntax: /api/v1/progress/{database}
Responses:
Example:
$ curl -X GET http://localhost:2480/api/v1/progress/school \
--user root:arcadedb-password
Return (one entry per running operation, empty array when nothing is running):
{"result": [{
"id": 3,
"database": "school",
"operation": "check database fix",
"stepName": "Checking vertices 'Account'",
"stepIndex": 3,
"totalSteps": 9,
"done": 421000,
"total": 1000000,
"percentage": 42,
"startedOn": 1786370000000,
"elapsedMs": 154000
}]}
percentage is -1 when the current step’s total is unknown (for example the backup or the import, whose sizes cannot be computed upfront).
Login - Get Authentication Token (POST)
Authenticates a user and returns an authentication token that can be used for subsequent requests instead of sending credentials each time. See Token-Based Authentication for more details.
URL Syntax: /api/v1/login
This endpoint requires HTTP Basic Authentication with valid credentials. On successful authentication, it returns a JSON response containing the authentication token.
Responses:
-
200OK - returns token and username -
403invalid credentials -
503the server holds the maximum number of concurrent authentication sessions and none could be reclaimed - seeserver.httpAuthSessionMax(since v26.9.1)
Example:
$ curl -X POST http://localhost:2480/api/v1/login \
--user root:arcadedb-password
Return:
{"token": "AU-ArcadeDB_0-2af64e60-8455-423a-bc64-ed0e19729f04", "user": "root"}
The returned token can be used in subsequent requests via the Authorization: Bearer header:
$ curl http://localhost:2480/api/v1/databases \
-H "Authorization: Bearer AU-ArcadeDB_0-2af64e60-8455-423a-bc64-ed0e19729f04"
Logout - Invalidate Authentication Token (POST)
Invalidates an authentication token so it can no longer be used for requests.
The token must be provided via the Authorization: Bearer header.
URL Syntax: /api/v1/logout
Responses:
-
204OK - token invalidated (or was already invalid)
Example:
$ curl -X POST http://localhost:2480/api/v1/logout \
-H "Authorization: Bearer AU-ArcadeDB_0-2af64e60-8455-423a-bc64-ed0e19729f04"
After logout, any attempt to use the invalidated token will result in a 401 Unauthorized response.
List Active Authentication Sessions (GET)
Returns a list of all active authentication sessions. This endpoint is restricted to root users only for security reasons.
URL Syntax: /api/v1/sessions
Responses:
Example:
$ curl http://localhost:2480/api/v1/sessions \
--user root:arcadedb-password
Return:
{
"result": [
{
"token": "AU-ArcadeDB_0-2af64e60-8455-423a-bc64-ed0e19729f04",
"user": "root",
"elapsedMs": 12345
},
{
"token": "AU-6fa459ea-ee8a-3ca4-894e-db77e160355e",
"user": "admin",
"elapsedMs": 5678
}
],
"count": 2
}
The elapsedMs field indicates how many milliseconds have passed since the last request using that token.
Create an API token (POST)
Mints a long-lived API token (at- prefix) for a script or service. Root only. The plaintext token is returned once, in
result.token, and is never shown again: the server keeps only its hash. On a cluster the request is forwarded to the
leader and the token is replicated to every node. GET /api/v1/server/api-tokens lists the tokens and
DELETE /api/v1/server/api-tokens?token=<hash> revokes one. Studio’s API Tokens panel uses the
same routes.
URL Syntax: /api/v1/server/api-tokens
Body: name (required), database (default *, every database), expiresAt (epoch milliseconds, 0 for no expiry) and
permissions (the same structure as a group).
Responses:
$ curl -X POST https://localhost:2490/api/v1/server/api-tokens \
--user root:arcadedb-password \
-d '{"name":"etl-job","database":"mydb"}'
Transport check (since v26.10.1). The token authenticates whoever holds it, so the server checks how the mint reached
it. The transport counts as protected when the request came over HTTPS, from a loopback address, or from a reverse proxy
listed in arcadedb.server.apiTokenTrustedProxies that reports https for every hop through X-Forwarded-Proto or the
RFC 7239 Forwarded header. What happens otherwise depends on arcadedb.server.apiTokenRequireSecureTransport:
-
false(the default in 26.10.1): the token is minted and the server logsMinting an API token over an unprotected transport (scheme=…, peer=…)at WARNING. -
true: the mint is refused with412and a body explaining the three ways out: connect over TLS, list the proxy, or turn the check off.
On a cluster the check also covers the hop between nodes: a mint forwarded to the leader over cleartext inter-node HTTP
is refused with 412 too, even when the client itself used HTTPS.
|
The default of |
Behind a TLS-terminating reverse proxy. When nginx, Envoy or a Kubernetes ingress terminates TLS and forwards to
ArcadeDB over HTTP, the server sees a cleartext request from the proxy’s address. List that address (or its subnet) in
arcadedb.server.apiTokenTrustedProxies, for example -Darcadedb.server.apiTokenTrustedProxies=10.0.0.5,10.1.0.0/16,
and make sure the proxy sets X-Forwarded-Proto (nginx: proxy_set_header X-Forwarded-Proto $scheme;). The rules are
strict on purpose:
-
A peer that is not on the list gains nothing by sending the same headers. They are ignored, because the client asking for a token is the one that would forge them.
-
Entries must be literal addresses or CIDR ranges. A hostname is rejected rather than resolved.
-
A list that does not parse is treated as empty, so a typo denies instead of opening the gate. The server logs the discarded list at SEVERE (
Ignoring the whole of arcadedb.server.apiTokenTrustedProxies (…)). -
List only an L7 proxy that writes the header itself. An L4 balancer (nginx
stream, HAProxymode tcp) passes the client’s own headers through, so listing one lets any client vouch for itself. -
If the request carries a
Forwardedheader, every element must includeproto=https; an element withoutproto=refuses the mint. A proxy that does not useForwardedshould strip any it receives.
The gRPC CreateApiToken RPC applies the same list, reading the x-forwarded-proto and forwarded metadata keys. Unlike
HTTP it always refuses an unprotected mint, whatever apiTokenRequireSecureTransport says.
Execute a query (GET|POST)
This command allows executing idempotent commands, like SELECT and MATCH:
URL Syntax GET: /api/v1/query/{database}/{language}/{command}
URL Syntax POST: /api/v1/query/{database}
Where:
-
databaseis the database name -
languageis the query language used. is the query language used, between "sql", "sqlscript", "graphql", "cypher", "gremlin", "mongo" and any other language supported by ArcadeDB and available at runtime. -
commandthe command to execute in encoded format -
params(optional), is the map of parameters to pass to the query engine via the POST body, where parameters are introduced with a colon:. Parameter values may include the typed-marker objects described in Typed parameter markers to sendbyte[]— e.g. INT8-encoded vectors — without a float32 round trip.
When using the GET variant the query needs to be URL encoded.
Due to security reasons (encoded) slashes / (%2F) which are used for divisions or block comments, cannot be used in queries via the GET method with the query/ endpoint.
|
Question marks (?) cause the server to stop reading the query string when sent via GET.
To use question marks (inside strings) one can use format('%c',63); in this case make sure to replace all percent symbols (%) in the format string with %%.
|
These restrictions do not apply to the POST variant, where the language and command
are send in the body.
Even though a POST method is used, the query in command has to be idempotent.
|
(since v26.10.1) POST /api/v1/query runs without an implicit transaction, like GET /api/v1/query, so a full scan of a large type can use several threads and is as fast as with GET. Inside a session (/begin) a scan stays sequential to keep it consistent with the transaction. POST /api/v1/command still runs in a transaction.
|
EXPLAIN is answered the same way by the GET and the POST variants and by /api/v1/command: the plan arrives
in the explain (indented text) and explainPlan (structured) members of the response envelope, and result is
empty — the plan is the answer, not a row. Requesting it with Accept: application/x-ndjson is refused with 400
("EXPLAIN produces a plan, not a row stream"), because a stream of rows plus a stats trailer has nowhere to carry
one. Before 26.10.1 the GET variant reached neither rule: it serialized the plan as an ordinary result row, and
streamed it as an ndjson record line that no consumer of that encoding can read.
With Accept: application/x-ndjson the rows are streamed one per line (record), followed by a stats trailer. While the
server is still producing the next row it writes an empty line every arcadedb.server.httpStreamingKeepAliveInterval
milliseconds (default 5000, 0 disables), so a client that bounds silence does not mistake a slow query for a dead server.
Consumers must ignore blank lines, which the Java driver does. The Java driver fails a stream after
arcadedb.network.socketTimeout of silence, with a minimum of 30 seconds, as for any other call.
Responses:
-
200OK -
400invalid language, invalid query -
403invalid credentials -
500database does not exist, cannot execute query -
503the server is busy and the query can be retried as it is: the running queries hold the heap budget (QueryHeapBudgetExceededException, seequeryMaxHeapRAM), or the query admission gate did not start it, because it waited longer thanqueryQueueTimeoutor found the queue full (QueryAdmissionException, seequeryMaxConcurrent)
Example:
$ curl -X GET http://localhost:2480/api/v1/query/school/sql/select%20from%20Class \
--user root:arcadedb-password
The query endpoint may also be used via the POST method, which has no character restrictions such as / or ?:
$ curl -X POST http://localhost:2480/api/v1/query/school \
-d '{"language": "sql", "command": "select from Class"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
The POST variant accepts additional parameters in the JSON body:
-
autoCommit(optional) controls transaction behavior (same as in Execute database command):-
true: Forces the query to execute within an atomic transaction -
false: Disables transaction wrapping (query executes directly) -
not specified: Uses automatic behavior (no transaction for read-only queries)
-
Example of forcing a transaction for a query to ensure consistent reads:
$ curl -X POST http://localhost:2480/api/v1/query/mydb \
-d '{"language": "sql", "command": "SELECT FROM Account WHERE balance > 1000", "autoCommit": true}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Execute database command (POST)
Executes a non-idempotent command (as an implicit transaction).
URL Syntax: /api/v1/command/{database}
Where:
-
databaseis the database name
Example to create the new document type "Class":
$ curl -X POST http://localhost:2480/api/v1/command/school \
-d '{"language": "sql", "command": "create document type Class"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
The payload, as a JSON, accepts the following parameters:
-
languageis the query language used, between "sql", "sqlscript", "graphql", "cypher", "gremlin", "mongo" and any other language supported by ArcadeDB and available at runtime. -
commandthe command to execute in encoded format -
awaitResponse(optional) a boolean which is by default "true", if set to "false" the command will be executed asynchronously and only acknowledgement of receiving the command is responded. The completion of the command is noted in the log, yet no results of the command can be returned. -
limit(optional) is the maximum number of results to return -
params(optional), is the map of parameters to pass to the query engine, where parameters are prefixed with a colon:. Parameter values may include the typed-marker objects described in Typed parameter markers to sendbyte[]— e.g. INT8-encoded vectors — without a float32 round trip. -
retries(optional), is the number of times the command (transaction) is retried. When omitted it follows thearcadedb.txRetriesdatabase setting (3 by default), the same attempt count the embedded and the remote Java APIs take it from; up to v26.9.0 it was hard-coded to 1, so nothing was ever retried. Only a concurrent-modification conflict is retried (NeedRetryExceptionandDuplicatedKeyException, the latter capped at a single retry), and always after a full rollback, so a retried attempt starts from committed state with nothing of the failed one surviving. Note the consequence for side effects outside the database: a SQL or JavaScript function that calls another system can now run more than once per request. Send"retries": 1to keep the previous single-attempt behavior for such a command. -
autoCommit(optional) controls transaction behavior:-
true: Forces the command to execute within an atomic transaction that auto-commits on success -
false: Disables automatic transaction wrapping (command executes directly without transaction) -
not specified (default): Uses automatic behavior (commands are wrapped in atomic transactions unless a session is active)
-
NOTE: This parameter is ignored when using session-based transactions (see Begin a transaction). If you provide
autoCommit: truewith an active session, a warning will be logged and the session transaction will be used instead.
-
-
serializer(optional) specify the serializer used for the result:-
graph: returns a graph separating vertices from edges -
record: returns everything as records -
studio: by default it’s like record but with additional metadata for vertex records, such as the number of outgoing edges in@outproperty and total incoming edges in@inproperty. This serializer is used by Studio.
-
Asynchronous commands (using "awaitResponse": false) are queued, and their processing is configurable via async* database settings.
|
Responses:
-
200OK -
202Accepted -
400invalid language, invalid command -
403invalid credentials -
503the server is busy and the command can be retried as it is: the running queries hold the heap budget (QueryHeapBudgetExceededException, seequeryMaxHeapRAM), or the query admission gate did not start it, because it waited longer thanqueryQueueTimeoutor found the queue full (QueryAdmissionException, seequeryMaxConcurrent)
Example of insertion of a new Client by using parameters:
$ curl -X POST http://localhost:2480/api/v1/command/company \
-d '{"language": "sql", "command": "create vertex Client set firstName = :firstName, lastName = :lastName", params: {"firstName": "Jay", "lastName": "Miner"}}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Example of forcing an atomic transaction with autoCommit:
$ curl -X POST http://localhost:2480/api/v1/command/mydb \
-d '{"language": "sql", "command": "INSERT INTO Account SET balance = 1000", "autoCommit": true}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Example of disabling automatic transaction wrapping:
$ curl -X POST http://localhost:2480/api/v1/command/mydb \
-d '{"language": "sql", "command": "SELECT FROM Account LIMIT 1", "autoCommit": false}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Write statistics (stats)
When a command modifies data, the POST /api/v1/command/{database} response includes a stats object alongside result, reporting how many entities the command created, deleted or changed. The object is omitted for read-only queries (a command that changes nothing has no stats field). It is populated for OpenCypher writes — including counts aggregated across UNION branches and CALL { … } subqueries — and mirrors the counters the Bolt driver exposes through its result summary.
The shape is an ArcadeDB-native camelCase object with a stable set of keys (each counter is always present, 0 when unused):
| Key | Meaning |
|---|---|
|
Vertices created / deleted |
|
Edges created / deleted |
|
Properties assigned a value |
|
Type/label memberships added / removed |
|
Indexes created / dropped |
|
Constraints ( |
|
|
Example — a Cypher write returns the counters it produced:
$ curl -X POST http://localhost:2480/api/v1/command/school \
-d '{"language": "cypher", "command": "CREATE (a:Person {name:$n})-[:KNOWS]->(b:Person)", "params": {"n": "Ada"}}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
{
"user": "root",
"result": [],
"stats": {
"nodesCreated": 2,
"nodesDeleted": 0,
"relationshipsCreated": 1,
"relationshipsDeleted": 0,
"propertiesSet": 1,
"labelsAdded": 2,
"labelsRemoved": 0,
"indexesAdded": 0,
"indexesRemoved": 0,
"constraintsAdded": 0,
"constraintsRemoved": 0,
"containsUpdates": true
}
}
The gRPC service carries the same counters in the write-result summary of ExecuteCommand; see gRPC API.
Numbers and arrays in params
Parameters keep the numbers as written: an integer beyond the long range, or a decimal with more than 17 significant digits, is read as a DECIMAL (BigDecimal) instead of being wrapped or rounded to a double. A number token with more than 1000 characters or an exponent beyond 1000 is refused with 400. A JSON array given as a parameter is a list, as when the statement runs embedded: written to a LIST property it is stored as that list, and [0.1, 3.141592653589793] keeps both doubles. (Since v26.10.1: before, such integers wrapped around and arrays with a fraction were narrowed to float32.)
Typed parameter markers ($bytes, $int8)
JSON has no native byte[] type, so HTTP/JSON clients cannot send binary parameters with the standard scalar/array shapes. The query and command endpoints recognise two reserved single-key marker objects in any parameter value (named or positional) and decode them to a Java byte[] before the parameter binder sees them. The primary use case is sending an INT8-encoded vector to a LSM_VECTOR index without the float32 round trip — see Vector Encoding.
| Marker | Decodes to |
|---|---|
|
|
|
|
A single-key map whose key is one of these is decoded; multi-key maps that happen to contain a $bytes or $int8 key are passed through unchanged so user data with leading-$ keys is not silently transformed. The decoder also recurses into nested arrays and maps, so a list of typed markers (e.g. a batch of int8 vectors as a single parameter) works.
Examples:
# Base64-encoded byte array
curl -X POST http://localhost:2480/api/v1/query/mydb \
-H "Content-Type: application/json" \
--user root:arcadedb-password \
-d '{
"language": "sql",
"command": "SELECT expand(`vector.neighbors`(\"Doc[embedding]\", :q, 5))",
"params": {
"q": {"$bytes": "AwIB/38="}
}
}'
# Integer array form (handy for debugging)
curl -X POST http://localhost:2480/api/v1/query/mydb \
-H "Content-Type: application/json" \
--user root:arcadedb-password \
-d '{
"language": "sql",
"command": "SELECT expand(`vector.neighbors`(\"Doc[embedding]\", :q, 5))",
"params": {
"q": {"$int8": [1, 2, 3, -1, 127, -128]}
}
}'
Malformed markers (invalid base64, non-numeric $int8 element, value out of [-128, 127], null payload) return 400 Bad Request with a message naming the marker and the offending value.
Vector search (POST)
(Since v26.10.1) Returns the nearest neighbors of a query vector in a dense LSM_VECTOR or sparse LSM_SPARSE_VECTOR index, without writing the vector.neighbors SQL by hand. ArcadeDB does not compute embeddings: the caller supplies the vector. The same request body and limits apply to the MCP vector_search tool and the gRPC VectorSearch RPC.
URL Syntax: /api/v1/vector/{database}/search
The JSON body accepts:
-
indexName(required) name of the vector index, for exampleDoc[embedding] -
queryVector(required) array of numbers, not empty, finite values only. On a dense index its length must match the index dimensions and at least one value must be non-zero. Withsparse: trueit holds the weights matchingqueryIndices, or the full vector whenqueryIndicesis omitted (zero entries are dropped) -
k(optional, default10) maximum number of results, between1and1000 -
sparse(optional, defaultfalse) search anLSM_SPARSE_VECTORindex instead of a dense one. The index type must match the flag -
queryIndices(optional, sparse only) dimension ids matching thequeryVectorweights: non-negative integers, no duplicates, same length asqueryVector. Refused withoutsparse: true -
efSearch(optional, dense only) HNSW search beam width, between1and10000. Refused withsparse: true -
filter(optional) read-only SQLWHEREpredicate, at most 4096 characters, applied to each neighbor row (record properties are flattened, and@rid,@type,recordplusdistanceorscoreare available). With a filter the search inspects a candidate window ofk * 8rows, at most 8000, reported ascandidateLimit
The response carries:
-
indexName,sparse -
scoring: the ranking direction, for exampledistance_lower_is_better:COSINEfor a dense index, orscore_higher_is_better:dot_product(score_higher_is_better:idf_weighted_dot_productwith the IDF modifier) for a sparse one -
candidateLimit: the candidate window inspected -
truncated:truewhenkresults were returned, so more matches may exist -
count,results: each hit hasrid,propertiesand eitherdistance(dense, lower is better) orscore(sparse, higher is better). Records deleted between the index scan and the load are skipped, so a search can return fewer thankhits
Responses:
The route runs without a transaction. A 400 caused by a bad argument does not roll back a transaction the client opened with /begin.
Example:
$ curl -X POST http://localhost:2480/api/v1/vector/mydb/search \
-d '{"indexName": "Doc[embedding]", "queryVector": [0.12, 0.85, 0.33], "k": 2, "filter": "lang = '\''en'\''"}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{
"indexName": "Doc[embedding]",
"sparse": false,
"scoring": "distance_lower_is_better:COSINE",
"candidateLimit": 16,
"truncated": true,
"count": 2,
"results": [
{ "rid": "#12:4", "distance": 0.018, "properties": { "@rid": "#12:4", "@type": "Doc", "title": "Graphs 101", "lang": "en" } },
{ "rid": "#12:9", "distance": 0.041, "properties": { "@rid": "#12:9", "@type": "Doc", "title": "Vector search", "lang": "en" } }
]
}
Hybrid search (POST)
(Since v26.10.1) Fuses a vector leg, an optional full-text leg and an optional graph expansion leg into one ranked list, using the engine’s vector.fuse function. The same request body and limits apply to the MCP hybrid_search tool and the gRPC HybridSearch RPC.
URL Syntax: /api/v1/vector/{database}/hybrid
The JSON body accepts:
-
vectorIndexName(required) name of the vector index -
queryVector,queryIndices,sparse,efSearch,filter(vector leg): same meaning and limits as in Vector search. Thefilterapplies to the vector leg only -
k(optional, default10) maximum number of fused results, between1and1000. Each retrieval leg fetchesk * 4rows, at most 1000, before fusion -
fulltextIndexNameandfulltextQuery(optional) add the full-text leg: aFULL_TEXTindex and a Lucene-syntax query. They must be given together, otherwise the request is refused. Note the field isfulltextQuery, notqueryTextas in Full-text search -
fusionStrategy(optional, defaultRRF) one ofRRF,DBSF,LINEAR, case-insensitive -
weights(optional) object with per-leg weights:vector(default1.0),fulltext(default1.0),expand(default0.5). Each must be a finite number, not negative. Any other key, or a weight for a leg the request does not run, is refused -
expand(optional) object that adds the graph expansion leg, seeded from the results of the retrieval legs (at most 256 seeds) and ranked by breadth-first discovery order:-
edgeTypes(optional) edge type names to walk; each must exist. Empty or omitted walks every edge type -
direction(optional, defaultout) one ofout,in,both -
maxDepth(optional, default1) between1and3The expansion returns at most 2000 records. Both the vector index and the full-text index must be declared on vertex types. Since the expansion leg has no score, it can only be used with
RRF.
-
The response carries vectorIndexName, sparse, scoring, fused, truncated, count, results, plus:
-
fulltextIndexNamewhen the full-text leg ran -
fusionStrategy(upper-cased) whenfusedistrue -
legs: per-leg accounting.vector.countalways;fulltextwithindexName,similarity(BM25orCLASSIC) andcountwhen that leg ran;expandwithdirection,edgeTypes,maxDepth,truncated(fan-out cap reached),seedCount,seedsTruncatedandcountwhenexpandwas given
Fusion needs at least two legs that returned rows. When only the vector leg did, fused is false and each hit carries the native distance (dense) or score (sparse) instead of a fused score. Otherwise each hit has rid, fusedScore (higher is better), sources (the legs that produced it: vector, fulltext, expand) and properties, plus depth and path (RIDs from the seed, seed included) for a hit found by the expansion.
Responses:
Example:
$ curl -X POST http://localhost:2480/api/v1/vector/mydb/hybrid \
-d '{
"vectorIndexName": "Doc[embedding]",
"queryVector": [0.12, 0.85, 0.33],
"fulltextIndexName": "Doc[content]",
"fulltextQuery": "+graph database",
"k": 5,
"expand": { "edgeTypes": ["Cites"], "direction": "out", "maxDepth": 1 }
}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return (abridged):
{
"vectorIndexName": "Doc[embedding]",
"sparse": false,
"scoring": "distance_lower_is_better:COSINE",
"legs": {
"vector": { "count": 20 },
"fulltext": { "indexName": "Doc[content]", "similarity": "BM25", "count": 7 },
"expand": { "direction": "out", "edgeTypes": ["Cites"], "maxDepth": 1, "truncated": false,
"seedCount": 24, "seedsTruncated": false, "count": 11 }
},
"fused": true,
"fusionStrategy": "RRF",
"fulltextIndexName": "Doc[content]",
"truncated": true,
"count": 5,
"results": [
{ "rid": "#12:4", "fusedScore": 0.0325, "sources": ["vector", "fulltext"], "properties": { "@rid": "#12:4", "@type": "Doc", "title": "Graphs 101" } },
{ "rid": "#12:17", "fusedScore": 0.0081, "sources": ["expand"], "depth": 1, "path": ["#12:4", "#12:17"], "properties": { "@rid": "#12:17", "@type": "Doc", "title": "Property graphs" } }
]
}
Full-text search (POST)
(Since v26.10.1) Runs a Lucene-syntax query against a FULL_TEXT index and returns the matching documents, highest score first (ties ordered by RID). The same request body and limits apply to the MCP full_text_search tool and the gRPC FullTextSearch RPC.
URL Syntax: /api/v1/vector/{database}/fulltext
The JSON body accepts:
-
queryText(required) Lucene-syntax query, for examplejavaor+java -python. Must not be blank -
indexName(optional) the full-text index to search. Wins overtypeNamewhen both are given -
typeName(optional) the type whose full-text index to search. Alone, it works only when the type has exactly one full-text index. An index declared on a supertype is named after the supertype -
properties(optional) array of indexed property names, to pick one of several full-text indexes ontypeName -
limit(optional, default10) maximum number of results, between1and1000
One of indexName or typeName is required. The response carries indexName, similarity (BM25 or CLASSIC), count and results, where each hit has rid, score and properties. The limit is applied per bucket, so a record deleted between the index scan and the load is skipped rather than replaced, and a search can return fewer than limit hits.
Responses:
Example:
$ curl -X POST http://localhost:2480/api/v1/vector/mydb/fulltext \
-d '{"typeName": "Doc", "queryText": "+graph -relational", "limit": 3}' \
-H "Content-Type: application/json" \
--user root:arcadedb-password
Return:
{
"indexName": "Doc[content]",
"similarity": "BM25",
"count": 1,
"results": [
{ "rid": "#12:4", "score": 2.71, "properties": { "@rid": "#12:4", "@type": "Doc", "title": "Graphs 101" } }
]
}
Begin a transaction (POST)
Begins a transaction on the server managed as a session. The response header contains the session id. Set this id in the following requests to execute them in the same transaction scope. See also /commit and /rollback.
URL Syntax: /api/v1/begin/{database}
Where:
-
databaseis the database name
The payload, optional as a JSON, accepts the following parameters:
-
isolationLevelis the isolation level for the current transaction, eitherREAD_COMMITTED(default) orREPEATABLE_READ.
Responses:
Example:
$ curl -I -X POST http://localhost:2480/api/v1/begin/school \
--user root:arcadedb-password
Returns the Session Id in the response header, example:
arcadedb-session-id: AS-ee056170-dc9b-4956-8d71-d7cfa01900d4
Use the session id in the request header of further commands you want to execute in the same transaction and execute /commit to commit the server side transaction or /rollback to rollback the changes. After a period of inactivity (default is 30 seconds), the server automatically rollback and purge expired transactions.
Commit a transaction (POST)
Commits a transaction on the server. Set the session id obtained with the /begin command as a header of the request. See also /begin and /rollback.
URL Syntax: /api/v1/commit/{database}
Where:
-
databaseis the database name
Set the session id returned from the /begin command in the request header. If the session (and therefore the server side transaction) is expired, then an internal server error is returned.
The commit ends the session, whether it succeeds or fails: a commit that fails rolls the transaction back, and the
answer carries the header arcadedb-session-closed: true, so there is no need to send a /rollback
afterwards. (Since v26.10.1: before, the session of a failed commit stayed registered until it timed out.)
A command that fails inside a session rolls the session’s transaction back. When the transaction held changes, they are
lost, so the session is marked as rolled back: the next command naming it is refused with 404, and /commit answers
404 with a message saying that the transaction was rolled back, instead of 204. Send a /rollback (or
the /commit) to end it. When nothing was lost (a failed read, an error before the first write) the session goes on in a
fresh transaction. (Since v26.10.1: before, the commands after the failure ran in autocommit and /commit answered 204.)
Response:
Example:
$ curl -I -X POST http://localhost:2480/api/v1/commit/school \
-H "arcadedb-session-id: AS-ee056170-dc9b-4956-8d71-d7cfa01900d4" \
--user root:arcadedb-password
Rollback a transaction (POST)
Rollbacks a transaction on the server. Set the session id obtained with the /begin command as a header of the request. See also /begin and /commit.
URL Syntax: /api/v1/rollback/{database}
Where:
-
databaseis the database name
Set the session id returned from the /begin command in the request header. If the session (and therefore the server side transaction) is expired, then an internal server error is returned.
The commit ends the session, whether it succeeds or fails: a commit that fails rolls the transaction back, and the
answer carries the header arcadedb-session-closed: true, so there is no need to send a /rollback
afterwards. (Since v26.10.1: before, the session of a failed commit stayed registered until it timed out.)
Response:
Example:
$ curl -I -X POST http://localhost:2480/api/v1/rollback/school \
-H "arcadedb-session-id: AS-ee056170-dc9b-4956-8d71-d7cfa01900d4" \
--user root:arcadedb-password
Batch import vertices and edges (POST)
High-performance bulk import of vertices and edges using the GraphBatch engine under the hood. Supports JSONL and CSV input formats with streaming parsing — the server never loads the entire request body into memory, so you can push millions of records in a single request.
URL Syntax: /api/v1/batch/{database}
Where:
-
databaseis the database name
The Content-Type header determines the input format:
-
application/x-ndjsonorapplication/jsonl— JSONL (newline-delimited JSON) -
text/csv— CSV
| All vertices must appear before edges in the request body. Edges reference vertices either by temporary ID or by existing database RID. |
JSONL Format
Each line is a JSON object with the following meta fields:
| Field | Required | Description |
|---|---|---|
|
Yes |
|
|
Yes |
The vertex or edge type name (must exist in schema) |
|
No |
Client-assigned temporary ID for vertices. Used by edges to reference newly created vertices. |
|
Edges only |
Source vertex reference: a temporary ID (e.g., |
|
Edges only |
Destination vertex reference: a temporary ID or an existing RID |
All other fields are treated as properties.
A control key sent on the kind of line that cannot use it is refused with 400, naming the line:
@id on an edge, @from or @to on a vertex. Before 26.10.1 such a key was silently dropped — neither stored
nor reported — so a client that models both line shapes with one struct got a load that looked clean while the
value was gone. A key carrying nothing is still accepted, so JSON null and the empty string are ignored.
|
Example:
{"@type":"vertex","@class":"Person","@id":"t1","name":"Alice","age":30}
{"@type":"vertex","@class":"Person","@id":"t2","name":"Bob","age":25}
{"@type":"edge","@class":"KNOWS","@from":"t1","@to":"t2","since":2020}
{"@type":"edge","@class":"KNOWS","@from":"#12:0","@to":"t2","weight":0.8}
CSV Format
The first line is the column header. A --- sentinel row separates the vertex section from the edge section, followed by a new header row for edges.
Example:
@type,@class,@id,name,age
vertex,Person,t1,Alice,30
vertex,Person,t2,Bob,25
---
@type,@class,@from,@to,since
edge,KNOWS,t1,t2,2020
CSV parsing supports RFC 4180 quoting (double-quote escaping). Numeric values are auto-detected as integers or floats. Boolean values (true/false) are also auto-detected.
The same rule as JSONL applies to a control value, not to the column: a row carrying a value in a control column
its kind cannot use — @id on an edge row, @from/@to on a vertex row — is refused with 400, while an empty
one is ignored. That keeps a single header naming all five control columns across both sections working, as long as
the inapplicable columns are left empty on each row.
Query Parameters
All parameters are optional and map directly to GraphBatch builder options:
| Parameter | Default | Description |
|---|---|---|
|
100000 |
Maximum edges buffered before auto-flush |
|
false |
If true, property-less edges are stored as connectivity only (saves ~33% I/O) |
|
false |
Enable Write-Ahead Logging for crash safety |
|
true |
Parallelize edge connection across async threads |
|
true |
Pre-allocate edge segments on vertex creation |
|
2048 |
Initial segment size in bytes (64-8192) |
|
true |
Connect the incoming side of the edges whose type is declared bidirectional; a |
|
50000 |
Edges per sub-transaction within a flush |
|
0 |
Hint for auto-tuning batch size |
Response
The response is a JSON object with import statistics:
{
"verticesCreated": 2,
"edgesCreated": 1,
"elapsedMs": 42,
"idMapping": {
"t1": "#9:0",
"t2": "#9:1"
}
}
The idMapping field is included only when temporary IDs (@id) were used. It maps each temporary ID to the actual RID assigned by the database.
Because this response is built in full before anything is sent, the mapping of a very large load is dropped rather than risk exhausting memory: past 10,000 entries the server replies with idMappingOmitted: true and idMappingSize instead. Send idMapping=true to demand it anyway, or use the streaming response below, which has no such limit.
Streaming response
Send Accept: application/x-ndjson to be answered as the load runs instead of once at the end. The response is one JSON object per line:
{"progress":{"phase":"vertices","verticesCreated":10000,"edgesCreated":0,"idMapping":{"t1":"#9:0","...":"..."}}}
{"progress":{"phase":"vertices","verticesCreated":20000,"edgesCreated":0,"idMapping":{"...":"..."}}}
{"summary":{"verticesCreated":20000,"edgesCreated":0,"elapsedMs":1234,"idMappingStreamed":true,"idMappingSize":20000}}
Each progress line acknowledges a committed chunk and carries the temporary-ID mapping for that chunk only. Concatenate them to obtain the whole mapping, and check the total against idMappingSize on the last line. Because neither side ever holds more than one chunk, the 10,000-entry limit above does not apply here: a load of any size gets its complete mapping.
The stream always ends with either a summary line (success) or an error line (failure); a stream that ends with neither did not arrive complete. On failure the error line carries the same counters the buffered response would have, plus the status code it would have been sent under, since the status line can no longer be changed once the stream has started.
Sending no Accept header, or any other value, returns the single buffered object described above, unchanged.
The Java driver’s RemoteGraphBatch uses this encoding automatically, so a bulk load through it is not limited by the mapping size either. Call withProgressListener(…) on the builder to be notified of each chunk.
|
Responses:
-
200OK -
400invalid input format, missing required fields, unknown temporary ID, vertices after edges -
403invalid credentials -
413the body exceedsserver.httpBodyContentMaxSize; the load is cut short and the records loaded before the cut stay loaded -
503/504the request could not be forwarded (for example from a follower to the leader); the records loaded before the failure stay loaded
A 413, 503 or 504 does NOT mean that nothing was applied. A batch that is cut short may be partially applied: the server log carries the counts, but the HTTP answer does not. A client that reads such an error as "rejected, nothing applied" and resends the whole payload after raising the limit or splitting it creates duplicates, unless the payload is idempotent (for example @id-keyed upserts). Deduplicate on the client side, or reload into a clean target database.
|
| This endpoint is NOT atomic by design. The underlying GraphBatch commits internally in chunks for maximum throughput. The response tells you exactly how many records were committed. Treat it as a bulk-loading operation, not a transactional one. |
For maximum throughput, group vertices by type in the input. The endpoint batches consecutive same-type vertices into a single createVertices() call. Interleaving types forces smaller, less efficient batches.
|
Examples
JSONL import with light edges:
$ curl -X POST "http://localhost:2480/api/v1/batch/mydb?lightEdges=true" \
-H "Content-Type: application/x-ndjson" \
--data-binary @graph-data.jsonl \
--user root:arcadedb-password
CSV import:
$ curl -X POST http://localhost:2480/api/v1/batch/mydb \
-H "Content-Type: text/csv" \
--data-binary @graph-data.csv \
--user root:arcadedb-password
Inline JSONL with WAL enabled:
$ curl -X POST "http://localhost:2480/api/v1/batch/mydb?wal=true" \
-H "Content-Type: application/x-ndjson" \
--user root:arcadedb-password \
-d '{"@type":"vertex","@class":"Person","@id":"p1","name":"Alice"}
{"@type":"vertex","@class":"Person","@id":"p2","name":"Bob"}
{"@type":"edge","@class":"KNOWS","@from":"p1","@to":"p2"}'
Python example:
import requests
data = (
'{"@type":"vertex","@class":"Person","@id":"p1","name":"Alice"}\n'
'{"@type":"vertex","@class":"Person","@id":"p2","name":"Bob"}\n'
'{"@type":"edge","@class":"KNOWS","@from":"p1","@to":"p2"}\n'
)
resp = requests.post(
"http://localhost:2480/api/v1/batch/mydb?lightEdges=true",
auth=("root", "arcadedb-password"),
headers={"Content-Type": "application/x-ndjson"},
data=data,
)
print(resp.json())
# {'verticesCreated': 2, 'edgesCreated': 1, 'elapsedMs': 15, 'idMapping': {'p1': '#9:0', 'p2': '#9:1'}}
JavaScript (Node.js) example:
const resp = await fetch("http://localhost:2480/api/v1/batch/mydb", {
method: "POST",
headers: {
"Content-Type": "application/x-ndjson",
"Authorization": "Basic " + btoa("root:arcadedb-password"),
},
body: [
'{"@type":"vertex","@class":"Person","@id":"p1","name":"Alice"}',
'{"@type":"vertex","@class":"Person","@id":"p2","name":"Bob"}',
'{"@type":"edge","@class":"KNOWS","@from":"p1","@to":"p2"}',
].join("\n"),
});
console.log(await resp.json());