The Fluree HTTP API provides a complete RESTful interface for database operations. This document provides a high-level overview of API design principles and capabilities.
The API is organized around resources:
- Ledgers: Database instances
- Transactions: Write operations
- Queries: Read operations
- Commits: Transaction history
Operations use standard HTTP methods:
GET- Retrieve information (idempotent, cacheable)POST- Submit operations (transactions, queries)PUT- Update resources (planned)DELETE- Remove resources (planned)
All request and response bodies use JSON by default:
- Native JSON-LD support
- Clean, readable syntax
- Easy integration with modern applications
All requests are stateless:
- No session management required
- Each request contains complete information
- Enables horizontal scaling
Ledgers are identified using aliases with branch names:
ledger-name:branch-name
Examples:
mydb:main- Main branch of mydb ledgercustomers:prod- Production branch of customers ledgertenant/app:dev- Development branch with hierarchical naming
Historical queries use time specifiers in ledger IDs:
ledger:branch@t:100 # Transaction number
ledger:branch@iso:2024-01-15 # ISO timestamp
ledger:branch@commit:bafybeig... # Commit ID
These work in all query contexts (FROM clauses, dataset specs, etc.).
Request format determined by Content-Type header:
application/json- JSON-LD (default)application/sparql-query- SPARQLtext/turtle- Turtle RDF
Response format determined by Accept header:
application/json- Compact JSON (default)application/ld+json- Full JSON-LD with contextapplication/sparql-results+json- SPARQL result format
Except for root diagnostics such as /health and /.well-known/fluree.json,
HTTP API paths are under the discovered API base URL. The standalone server
defaults to /v1/fluree.
POST /update
- Submit update transactions (WHERE/DELETE/INSERT JSON-LD, SPARQL UPDATE, or Cypher
CREATE/MERGE/SET/DELETE) - Parameters:
ledger,context - Returns: Transaction receipt with commit info
POST /insert / POST /upsert
- Insert or upsert data (JSON-LD and Turtle; TriG on upsert)
POST /query
- Execute queries (JSON-LD Query, SPARQL, or Cypher — Cypher on the ledger-scoped
/query/{ledger}route withContent-Type: application/cypher) - Parameters: None (ledger specified in query body)
- Returns: Query results
- Supports history queries via time range in
fromclause (see Time Travel)
GET /ledgers
- List all ledgers
- Parameters: None
- Returns: Array of ledger metadata
GET /info/:ledger-id
- Get specific ledger metadata
- Parameters:
ledger-id(ledger:branch) - Returns: Ledger details (commit_t, index_t, etc.)
POST /create
- Create a new ledger explicitly
- Parameters:
ledger - Returns: Ledger metadata
GET /health
- Health check endpoint
- Parameters: None
- Returns: Server health status
GET /stats
- Server status and statistics
- Parameters: None
- Returns: Detailed server state
https://[host]:[port]/[endpoint]?[parameters]
Example:
http://localhost:8090/v1/fluree/update?ledger=mydb:main
Common parameters:
ledger- Target ledger (format:name:branch)context- Default context URLformat- Response format override
Essential headers:
Content-Type: application/json
Accept: application/json
Authorization: Bearer [token]See Headers for complete list.
JSON-LD format for transactions:
{
"@context": {
"ex": "http://example.org/ns/"
},
"@graph": [
{ "@id": "ex:alice", "ex:name": "Alice" }
]
}JSON-LD Query format:
{
"@context": {
"ex": "http://example.org/ns/"
},
"from": "mydb:main",
"select": ["?name"],
"where": [
{ "@id": "?person", "ex:name": "?name" }
]
}Successful operations return appropriate status codes with JSON bodies.
Transaction Response:
{
"t": 5,
"timestamp": "2024-01-22T10:30:00.000Z",
"commit_id": "bafybeig...commitT5",
"flakes_added": 3,
"flakes_retracted": 1
}Query Response:
[
{ "name": "Alice" },
{ "name": "Bob" }
]Errors return appropriate HTTP status codes with structured error objects:
{
"error": "Invalid IRI: not a valid URI",
"status": 400,
"@type": "err:db/BadRequest"
}See Errors and Status Codes for complete error reference.
Fluree supports multiple authentication mechanisms, configured per endpoint group (data, events, admin, storage proxy). Each can be set to none, optional, or required. See Configuration for full details.
No authentication required (default):
curl http://localhost:8090/v1/fluree/query/mydb:main \
-H "Content-Type: application/json" \
-d '{"select": ["?s"], "where": [{"@id": "?s"}]}'Bearer tokens in the Authorization header. Fluree supports two token types with automatic dual-path dispatch:
Ed25519 JWS (did:key) - Locally minted tokens with an embedded JWK. Created with fluree token create:
TOKEN=$(fluree token create --private-key @~/.fluree/key --read-all --write-all)
curl http://localhost:8090/v1/fluree/query/mydb:main \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"select": ["?s"], "where": [{"@id": "?s"}]}'OIDC/JWKS (RS256) - Tokens from external identity providers, verified against the provider's JWKS endpoint. Requires the oidc feature and --jwks-issuer server configuration:
curl http://localhost:8090/v1/fluree/query/mydb:main \
-H "Authorization: Bearer <oidc-token>" \
-H "Content-Type: application/json" \
-d '{"select": ["?s"], "where": [{"@id": "?s"}]}'The server inspects the token header to determine the verification path:
- Embedded JWK (Ed25519): Verifies against the embedded public key; issuer is a
did:key - kid header (RS256): Verifies against the issuer's JWKS endpoint
Bearer tokens carry permission scopes that control access:
- Read:
fluree.ledger.read.all=trueorfluree.ledger.read.ledgers=[...] - Write:
fluree.ledger.write.all=trueorfluree.ledger.write.ledgers=[...] - Back-compat:
fluree.storage.*claims also imply read access for data endpoints
When a bearer token is present for connection-scoped SPARQL queries (/v1/fluree/query with Content-Type: application/sparql-query), FROM/FROM NAMED clauses are checked against the token's read scope (fluree.ledger.read.all or fluree.ledger.read.ledgers). Out-of-scope ledgers return 404 (no existence leak).
Cryptographically signed request bodies using Ed25519 JWS or Verifiable Credentials. The signed payload carries the request itself plus the signer's identity for policy evaluation.
curl http://localhost:8090/v1/fluree/query/mydb:main \
-H "Content-Type: application/jose" \
-d '<compact-jws-string>'See Signed Requests for detailed documentation.
Production deployments should implement rate limiting:
- Queries: 100 requests per minute
- Transactions: 10 requests per minute
- History: 50 requests per minute
Responses include rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642857600When limits are exceeded:
- Status code:
429 Too Many Requests - Response body includes retry information
Retry-Afterheader indicates wait time
The current API is version 1 (v1).
Future versions may use URL-based versioning:
https://api.example.com/v2/query
Use the upsert endpoint for idempotent transactions:
curl -X POST "http://localhost:8090/v1/fluree/upsert?ledger=mydb:main" \
-H "Content-Type: application/json" \
-d '{...}'Submit multiple entities in a single transaction:
{
"@graph": [
{ "@id": "ex:alice", "ex:name": "Alice" },
{ "@id": "ex:bob", "ex:name": "Bob" },
{ "@id": "ex:carol", "ex:name": "Carol" }
]
}Use WHERE/DELETE/INSERT for conditional changes:
{
"where": [
{ "@id": "ex:alice", "ex:age": "?oldAge" }
],
"delete": [
{ "@id": "ex:alice", "ex:age": "?oldAge" }
],
"insert": [
{ "@id": "ex:alice", "ex:age": 31 }
]
}Query past states using time specifiers:
{
"from": "mydb:main@t:100",
"select": ["?name"],
"where": [
{ "@id": "?person", "ex:name": "?name" }
]
}- GET for read-only operations (health, status)
- POST for write and query operations
Always specify the request format:
Content-Type: application/jsonCheck status codes and parse error responses:
if (response.status !== 200) {
const error = await response.json();
console.error(`Error ${error.code}: ${error.message}`);
}Reuse HTTP connections for better performance:
const agent = new https.Agent({ keepAlive: true });Retry failed requests with exponential backoff:
async function retryRequest(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (err) {
if (i === maxRetries - 1) throw err;
await sleep(Math.pow(2, i) * 1000);
}
}
}Track rate limit headers and back off when approaching limits.
Enable compression for large payloads:
Accept-Encoding: gzip, deflateAlways use HTTPS in production:
- Prevents eavesdropping
- Protects credentials
- Enables trust
Validate all user input before sending to API:
- Check IRI formats
- Validate JSON structure
- Sanitize user data
Never expose credentials in code or logs:
- Use environment variables
- Rotate keys regularly
- Use signed requests for highest security
If exposing API to web applications, configure CORS appropriately:
Access-Control-Allow-Origin: https://your-app.com
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: Content-Type, AuthorizationCombine related entities in single transactions for better performance.
@t:NNNis fastest (direct lookup)@iso:DATETIMErequires binary search@commit:CIDrequires scan
Always use LIMIT for potentially large result sets:
{
"select": ["?name"],
"where": [...],
"limit": 100
}Historical queries (with time specifiers) are immutable and cache well.
For very large result sets, consider streaming responses (when supported).
- Endpoints - Complete endpoint reference
- Headers - HTTP headers and content types
- Signed Requests - Cryptographic authentication
- Errors - Error codes and troubleshooting