API Overview
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.
API Design Principles
Resource-Oriented
The API is organized around resources:
- Ledgers: Database instances
- Transactions: Write operations
- Queries: Read operations
- Commits: Transaction history
Standard HTTP Methods
Operations use standard HTTP methods:
GET- Retrieve information (idempotent, cacheable)POST- Submit operations (transactions, queries)PUT- Update resources (planned)DELETE- Remove resources (planned)
JSON-First
All request and response bodies use JSON by default:
- Native JSON-LD support
- Clean, readable syntax
- Easy integration with modern applications
Stateless
All requests are stateless:
- No session management required
- Each request contains complete information
- Enables horizontal scaling
Core Concepts
Ledger Identification
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
Time Travel in URLs
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.).
Content Type Negotiation
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
API Endpoints
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.
Transaction Endpoints
POST /update
- Submit update transactions (WHERE/DELETE/INSERT JSON-LD or SPARQL UPDATE)
- Parameters:
ledger,context - Returns: Transaction receipt with commit info
POST /insert / POST /upsert
- Insert or upsert data (JSON-LD and Turtle; TriG on upsert)
Query Endpoints
POST /query
- Execute queries (JSON-LD Query or SPARQL)
- Parameters: None (ledger specified in query body)
- Returns: Query results
- Supports history queries via time range in
fromclause (see Time Travel)
Ledger Management
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
System Endpoints
GET /health
- Health check endpoint
- Parameters: None
- Returns: Server health status
GET /stats
- Server status and statistics
- Parameters: None
- Returns: Detailed server state
Request Format
URL Structure
https://[host]:[port]/[endpoint]?[parameters]
Example:
http://localhost:8090/v1/fluree/update?ledger=mydb:main
Query Parameters
Common parameters:
ledger- Target ledger (format:name:branch)context- Default context URLformat- Response format override
Request Headers
Essential headers:
Content-Type: application/json
Accept: application/json
Authorization: Bearer [token]
See Headers for complete list.
Request Body
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" }
]
}
Response Format
Success Response
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" }
]
Error Response
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.
Authentication
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.
Development Mode
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 Token Authentication
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
Token Scopes
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
Connection-Scoped SPARQL
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).
Signed Requests (JWS/VC)
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.
Rate Limiting
Default Limits
Production deployments should implement rate limiting:
- Queries: 100 requests per minute
- Transactions: 10 requests per minute
- History: 50 requests per minute
Rate Limit Headers
Responses include rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642857600
Exceeding Limits
When limits are exceeded:
- Status code:
429 Too Many Requests - Response body includes retry information
Retry-Afterheader indicates wait time
API Versioning
Current Version
The current API is version 1 (v1).
Version in URL (Future)
Future versions may use URL-based versioning:
https://api.example.com/v2/query
Common Patterns
Idempotent Transactions
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 '{...}'
Batch Operations
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" }
]
}
Conditional Updates
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 }
]
}
Historical Queries
Query past states using time specifiers:
{
"from": "mydb:main@t:100",
"select": ["?name"],
"where": [
{ "@id": "?person", "ex:name": "?name" }
]
}
Best Practices
1. Use Appropriate HTTP Methods
- GET for read-only operations (health, status)
- POST for write and query operations
2. Set Correct Content-Type
Always specify the request format:
Content-Type: application/json
3. Handle Errors Gracefully
Check status codes and parse error responses:
if (response.status !== 200) {
const error = await response.json();
console.error(`Error ${error.code}: ${error.message}`);
}
4. Use Connection Pooling
Reuse HTTP connections for better performance:
const agent = new https.Agent({ keepAlive: true });
5. Implement Retry Logic
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);
}
}
}
6. Monitor Rate Limits
Track rate limit headers and back off when approaching limits.
7. Use Compression
Enable compression for large payloads:
Accept-Encoding: gzip, deflate
Security Considerations
HTTPS in Production
Always use HTTPS in production:
- Prevents eavesdropping
- Protects credentials
- Enables trust
Validate Input
Validate all user input before sending to API:
- Check IRI formats
- Validate JSON structure
- Sanitize user data
Secure Credentials
Never expose credentials in code or logs:
- Use environment variables
- Rotate keys regularly
- Use signed requests for highest security
Implement CORS Carefully
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, Authorization
Performance Tips
1. Batch Related Operations
Combine related entities in single transactions for better performance.
2. Use Appropriate Time Specifiers
@t:NNNis fastest (direct lookup)@iso:DATETIMErequires binary search@commit:CIDrequires scan
3. Limit Result Sets
Always use LIMIT for potentially large result sets:
{
"select": ["?name"],
"where": [...],
"limit": 100
}
4. Cache Historical Queries
Historical queries (with time specifiers) are immutable and cache well.
5. Use Streaming for Large Results
For very large result sets, consider streaming responses (when supported).
Related Documentation
- Endpoints - Complete endpoint reference
- Headers - HTTP headers and content types
- Signed Requests - Cryptographic authentication
- Errors - Error codes and troubleshooting