Pubky Core API Reference
The Pubky Core protocol defines a RESTful HTTP API for storing and retrieving data on Homeservers. This document describes the complete API specification.
Base URL
All API endpoints are relative to the Homeserver base URL:
https://homeserver.example.com
Homeserver URLs are discovered via PKARR records published to the Mainline DHT.
Authentication
See Authentication for conceptual overview.
Public Key Authentication
All requests must be authenticated using Ed25519 signatures:
Headers:
Authorization: Pubky <public_key>:<signature>:<timestamp>
Signature Generation:
- Create message:
METHOD:PATH:TIMESTAMP:BODY_HASH - Sign message with Ed25519 private key
- Encode signature as base64
Example (conceptual):
Method: PUT
Path: /pub/myapp/data
Timestamp: 1704067200
Body: {"hello":"world"}
Body Hash: sha256(body) = abc123...
Message to sign: "PUT:/pub/myapp/data:1704067200:abc123..."
Signature: sign_ed25519(message, private_key)
Authorization: Pubky 8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo:SGVsbG8gV29ybGQ=:1704067200
Session Tokens
For long-lived connections, use session tokens:
Request:
POST /auth/session
Authorization: Pubky <public_key>:<signature>:<timestamp>
Content-Type: application/json
{
"capabilities": [
"read:/pub/",
"write:/pub/myapp/"
],
"ttl": 3600
}Response:
{
"token": "session_abc123...",
"expires_at": 1704070800
}Usage:
GET /pub/myapp/data
Authorization: Bearer session_abc123...Storage Endpoints
PUT - Store Data
Store or update data at a path.
Request:
PUT /:path
Authorization: Pubky <public_key>:<signature>:<timestamp>
Content-Type: application/octet-stream
<binary data>Path Format:
- Must start with
/pub/(public) or/private/(future) - Maximum length: 1024 bytes
- Allowed characters:
a-z,A-Z,0-9,-,_,/,.
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"path": "/pub/myapp/data",
"size": 1234,
"created_at": 1704067200
}Error Responses:
400 Bad Request: Invalid path or data401 Unauthorized: Invalid authentication403 Forbidden: Insufficient permissions413 Payload Too Large: Data exceeds limit (default: 10MB)507 Insufficient Storage: Quota exceeded
GET - Retrieve Data
Retrieve data from a path.
Request:
GET /:path
Authorization: Pubky <public_key>:<signature>:<timestamp>Response:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 1234
<binary data>Error Responses:
401 Unauthorized: Invalid authentication403 Forbidden: Insufficient permissions404 Not Found: Path does not exist
DELETE - Remove Data
Delete data at a path.
Request:
DELETE /:path
Authorization: Pubky <public_key>:<signature>:<timestamp>Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"path": "/pub/myapp/data",
"deleted_at": 1704067200
}Error Responses:
401 Unauthorized: Invalid authentication403 Forbidden: Insufficient permissions404 Not Found: Path does not exist
LIST - Enumerate Data
List entries under a path prefix (with pagination).
Request:
GET /:path?limit=20&cursor=abc123&reverse=false
Authorization: Pubky <public_key>:<signature>:<timestamp>Query Parameters:
limit(optional): Maximum entries to return (default: 100, max: 1000)cursor(optional): Pagination cursor from previous responsereverse(optional): List in reverse order (newest first)
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"entries": [
{
"path": "/pub/myapp/posts/001",
"size": 512,
"created_at": 1704067200,
"updated_at": 1704067200
},
{
"path": "/pub/myapp/posts/002",
"size": 1024,
"created_at": 1704067300,
"updated_at": 1704067300
}
],
"cursor": "next_page_cursor_xyz",
"has_more": true
}Error Responses:
401 Unauthorized: Invalid authentication403 Forbidden: Insufficient permissions
Capabilities System
Capabilities define what operations a session can perform:
Capability Syntax
<operation>:<path_prefix>
Operations:
read: GET, LIST operationswrite: PUT, DELETE operations*: All operations
Examples:
read:/pub/ # Read all public data
write:/pub/myapp/ # Write to /pub/myapp/* only
*:/pub/myapp/posts/ # Full access to posts
read:/pub/social/profile # Read specific path
Capability Checking
When a request is made:
- Check session capabilities
- Match requested path against capability patterns
- Verify operation is allowed
- Execute or deny request
Event Streaming
Subscribe to real-time updates on data changes via Server-Sent Events (SSE). Two endpoints serve different use cases:
GET /events-stream — Real-Time SSE Stream
The primary event API. Clients subscribe to specific users on a homeserver without processing unwanted traffic.
Request:
GET /events-stream?user=<z32_pubkey>&user=<z32_pubkey>:<cursor>&limit=100&live=true&path=/pub/Query Parameters:
user(required, repeatable): User public key in z32 format. Append:<cursor>to resume from a position (e.g.user=abc123:42). Up to 50 users per requestlimit(optional): Maximum events before closing (1–65535). Without limit andlive=false, all historical events are sent then the stream closeslive(optional): Whentrue, delivers all historical events first, then streams new events in real-time. Cannot combine withreversereverse(optional): Whentrue, delivers events newest-first then closes. Cannot combine withlivepath(optional): Filter events by path prefix (e.g./pub/pubky.app/)
Response (Server-Sent Events):
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: PUT
data: pubky://o1gg96ewuojmopcjbz8895478wdtxtzzuxnfjjz8o8e77csa1ngo/pub/posts/003
data: cursor: 42
data: content_hash: AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=
event: DEL
data: pubky://o1gg96ewuojmopcjbz8895478wdtxtzzuxnfjjz8o8e77csa1ngo/pub/temp
data: cursor: 43Event Types:
PUT: Data was created or updated. Includes acontent_hash(base64-encoded Blake3 hash)DEL: Data was deleted
SSE Data Format (one data: line per field):
- First line: full
pubky://resource URL cursor: <u64>— event ID for pagination/resumptioncontent_hash: <base64>— 32-byte Blake3 hash (PUT events only)
GET /events/ — Paginated Event Feed
Paginated feed of all events across all users on the homeserver. Intended for indexers and aggregators like Pubky Nexus.
Request:
GET /events/?cursor=<event_cursor>&limit=1000Returns up to 1000 events per batch. Use the returned cursor to paginate through the full history.
Signup Token Validation
Homeservers that require signup tokens (via Homegate) expose an endpoint to check token validity.
GET /signup_tokens/{token}
Check whether a signup token is valid, used, or unknown.
Response (200 OK):
{
"status": "valid",
"created_at": "2025-03-18T12:00:00Z"
}Status values: valid (unused), used (already redeemed)
Error Responses:
400 Bad Request: Missing or invalid token format, or homeserver does not require signup tokens404 Not Found: Token does not exist
Rate Limiting: This endpoint is rate-limited to 10 requests per IP per minute by default.
Admin Endpoints
Homeserver administrators can access management endpoints:
GET /admin/stats
Get server statistics.
Response:
{
"users": 1000,
"total_storage": 1073741824,
"requests_per_minute": 150,
"uptime_seconds": 86400
}GET /admin/users/:public_key
Get user information.
Response:
{
"public_key": "8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo",
"storage_used": 10485760,
"storage_quota": 104857600,
"created_at": 1704000000,
"last_activity": 1704067200
}See Admin API for complete admin documentation.
Metrics Endpoint
Prometheus-compatible metrics for monitoring.
GET /metrics
Response:
# HELP pubky_requests_total Total HTTP requests
# TYPE pubky_requests_total counter
pubky_requests_total{method="GET",status="200"} 1000
pubky_requests_total{method="PUT",status="200"} 500
# HELP pubky_storage_bytes Total storage used
# TYPE pubky_storage_bytes gauge
pubky_storage_bytes 1073741824
# HELP pubky_active_sessions Current active sessions
# TYPE pubky_active_sessions gauge
pubky_active_sessions 50
Rate Limiting
Homeservers implement rate limiting to prevent abuse:
Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067260Rate Limit Exceeded:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": "rate_limit_exceeded",
"message": "Too many requests, try again in 60 seconds"
}Default Limits:
- Anonymous: 10 requests/minute
- Authenticated: 100 requests/minute
- Admin: Unlimited
Error Responses
All errors follow a consistent format:
{
"error": "error_code",
"message": "Human-readable error message",
"details": {
"additional": "context"
}
}Common Error Codes:
invalid_path: Path format is invalidinvalid_signature: Authentication signature invalidexpired_session: Session token expiredinsufficient_permissions: Operation not allowedstorage_quota_exceeded: User quota exceededrate_limit_exceeded: Too many requestsserver_error: Internal server error
Best Practices
Optimize Storage
Store structured data efficiently:
// Good: Separate entries for each post
PUT /pub/myapp/posts/001 (small JSON)
PUT /pub/myapp/posts/002 (small JSON)
PUT /pub/myapp/posts/003 (small JSON)
// Bad: Single large entry
PUT /pub/myapp/all_posts (large JSON array)
Handle Rate Limits
async function putWithRetry(session, path, data, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await session.storage.putText(path, data);
} catch (error) {
if (error.status === 429) { // Too Many Requests
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
continue;
}
throw error;
}
}
}Resources
- Pubky Core Overview: Main documentation
- SDK Documentation: Client libraries
- Homeserver Documentation: Server setup
- Official Docs: pubky.github.io/pubky-core
- Repository: github.com/pubky/pubky-core
The Pubky Core API provides a simple, RESTful interface for decentralized data storage.