Build on the unsuppressed feed.
Candor Reach is the open-source infrastructure layer for the Open Feed Network — a chronological, zero-suppression data pipeline that any developer can read from, write to, and run a node on.
Authentication
Candor Reach uses two auth models depending on the operation. Read operations (subscribing to feeds, querying posts) require a public app key. Write operations (publishing posts, managing accounts) require a signed user token.
App key — read access
GET /v1/feed HTTP/1.1
Host: reach.candor.network
Authorization: App your_app_key_here
Accept: application/json
User token — write access
POST /v1/posts HTTP/1.1
Host: reach.candor.network
Authorization: Bearer user_jwt_token_here
Content-Type: application/json
reach.candor.network/developers to receive an app key. User tokens are issued via the OAuth 2.0 flow — see the Authentication guide for the full flow.
Quickstart
Subscribe to the live feed and publish your first post in under two minutes.
import { CandorReach } from '@candor/reach';
const reach = new CandorReach({
appKey: process.env.CANDOR_APP_KEY,
});
// Subscribe to the live chronological feed
const feed = reach.subscribe({ limit: 20 });
feed.on('post', (post) => {
console.log(`[${post.timestamp}] ${post.author.handle}: ${post.content}`);
});
// Publish a post (requires user token)
const result = await reach.publish({
content: 'Hello from the open feed.',
userToken: process.env.CANDOR_USER_TOKEN,
});
console.log(`Published: ${result.postId}`);
from candor_reach import CandorReach
reach = CandorReach(app_key="your_app_key")
# Fetch the latest 20 posts
posts = reach.feed.get(limit=20)
for post in posts:
print(f"[{post.timestamp}] {post.author.handle}: {post.content}")
# Publish a post
result = reach.publish(
content="Hello from the open feed.",
user_token="your_user_token"
)
print(f"Published: {result.post_id}")
# Fetch the live feed
curl https://reach.candor.network/v1/feed \
-H "Authorization: App your_app_key"
# Publish a post
curl -X POST https://reach.candor.network/v1/posts \
-H "Authorization: Bearer your_user_token" \
-H "Content-Type: application/json" \
-d '{"content":"Hello from the open feed."}'
npm install @candor/reach · pip install candor-reach · go get github.com/candor-network/reach-go
Subscribe to feed
The primary read endpoint. Returns posts in strict chronological order — newest first. No ranking algorithm. No suppression. What was published is what you receive.
Fetch a paginated chronological slice of the feed. Use before for pagination.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| limit optional | integer | Number of posts to return. Default 20, max 100. |
| before optional | string | Post ID cursor — returns posts older than this ID. Use for pagination. |
| after optional | string | Post ID cursor — returns posts newer than this ID. |
| node optional | string | Filter to posts originating from a specific node ID. |
| author optional | string | Filter to posts from a specific account handle or ID. |
Response
before to fetch the next page.{
"posts": [
{
"id": "post_9f8e7d6c5b4a",
"content": "The feed is live.",
"timestamp": "2026-05-23T14:22:01.000Z",
"author": {
"id": "acc_a1b2c3",
"handle": "rolando",
"displayName": "Rolando Cruz"
},
"nodeId": "node_iad_01",
"signature": "ed25519:a3f9c12d..."
}
],
"cursor": "post_9f8e7d6c5b4a",
"hasMore": true,
"nodeCount": 5
}
Publish to feed
Publish a new post to the feed. The post is broadcast to all connected nodes within milliseconds and becomes immediately visible in the chronological feed. Requires a user token.
Request body
| Field | Type | Description |
|---|---|---|
| content required | string | Post text content. Max 5,000 characters. |
| mediaUrls optional | string[] | Array of media attachment URLs. Max 4 items. |
| replyTo optional | string | Post ID this is a reply to. |
| visibility optional | string | public (default) or followers. |
| language optional | string | BCP-47 language code, e.g. en, es. Used for search indexing. |
Response
Delete a post you authored. The deletion is propagated to all nodes within the sync window. Only the original author can delete a post.
Query feed
Fetch a single post by ID, including its full thread context.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| thread optional | boolean | If true, includes the full reply thread. Default false. |
Fetch all posts by a specific account in reverse chronological order. Supports the same limit and before cursor parameters as /v1/feed.
Search
Full-text search across all posts indexed by the network. Results are ordered by relevance within a chronological window — not by engagement or ranking.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| q required | string | Search query. Supports quoted phrases and -exclusions. |
| limit optional | integer | Results per page. Default 20, max 50. |
| since optional | string | ISO 8601 date — restrict to posts after this time. |
| until optional | string | ISO 8601 date — restrict to posts before this time. |
| language optional | string | BCP-47 code to filter by post language. |
Run a node
The Candor Reach network is federated. Any developer can run a node that syncs with the global network, serves local traffic, and contributes to network resilience. Nodes are the foundation of the decentralized architecture.
github.com/candor-network/reach-node.
Register a new node with the network. Once registered, the node begins receiving the feed sync stream and is listed in the public node directory.
Request body
| Field | Type | Description |
|---|---|---|
| nodeUrl required | string | Public HTTPS URL of your node (e.g. https://mynode.example.com). |
| region required | string | ISO 3166-1 alpha-2 country code of the node's primary location. |
| operatorHandle optional | string | Your Candor account handle — shown in the public node directory. |
| publicKey required | string | Ed25519 public key used to verify posts broadcast by this node. |
Response
List all active nodes in the network. Includes region, uptime, and sync lag statistics.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| region optional | string | Filter by country code. |
| status optional | string | active (default) · all · degraded |
Returns health metrics for a specific node: sync lag, post throughput, uptime, and last heartbeat.
Accounts
Create a new account on the Candor Reach network. Accounts are portable across nodes.
Request body
| Field | Type | Description |
|---|---|---|
| handle required | string | Unique username. 3–30 chars, alphanumeric and underscores. Immutable after creation. |
| displayName optional | string | Display name shown on posts. Max 64 chars. |
| email required | string | Used for account recovery only. Never shown publicly. |
| publicKey required | string | Ed25519 public key for post signing and identity verification. |
Fetch a public account profile. Returns display name, bio, follower counts, and node affiliation.
Follow an account. Following filters the /v1/feed endpoint when filter=following is passed. Requires user token.
Unfollow an account. Requires user token.
Fetch notifications for the authenticated user: replies, mentions, follows, and reposts. Requires user token.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| type optional | string | Filter by type: reply · mention · follow · repost. Omit for all. |
| unread optional | boolean | If true, returns only unread notifications. |
| limit optional | integer | Default 20, max 50. |
WebSocket feed
Connect to the live feed over WebSocket for real-time post delivery. Posts arrive within milliseconds of being published to any node in the network.
Establish a persistent WebSocket connection to receive live feed events. Authentication via query parameter on connect.
const ws = new WebSocket(
`wss://reach.candor.network/v1/stream?appKey=${APP_KEY}`
);
ws.onopen = () => {
// Subscribe to the global feed
ws.send(JSON.stringify({
type: 'subscribe',
channel: 'feed.global',
}));
// Or subscribe to a specific account
ws.send(JSON.stringify({
type: 'subscribe',
channel: 'feed.account',
handle: 'rolando',
}));
};
ws.onmessage = ({ data }) => {
const event = JSON.parse(data);
switch (event.type) {
case 'post.created':
console.log('New post:', event.post);
break;
case 'post.deleted':
console.log('Deleted:', event.postId);
break;
case 'node.joined':
console.log('New node:', event.nodeId);
break;
}
};
WebSocket event types
postId and authorHandle.{"type":"pong"} to keep the connection alive.Post object
Account object
Chronological guarantee
Candor Reach makes a hard protocol guarantee: posts are always delivered in timestamp order. There is no ranking algorithm, no engagement scoring, no promoted content, and no suppression mechanism in the protocol layer.
Every post is signed with the publishing node's Ed25519 key. Signatures include the post content, author ID, and timestamp — making it cryptographically verifiable that a post was not modified or reordered after publication.
GET /v1/nodes/:nodeId. No trust required — verify yourself.
Sync lag
In a federated network, posts originating on one node propagate to all other nodes via the sync protocol. Typical sync lag is under 500ms across nodes in the same region, and under 2s globally. The nodeId field on each post indicates where it originated.
Error codes
All errors return a consistent JSON body: { error, code, message }.
Retry-After header value.Rate limits
Limits are per app key for read operations and per user token for write operations.
| Operation | Limit | Window |
|---|---|---|
| GET /v1/feed | 300 requests | Per minute |
| GET /v1/search | 60 requests | Per minute |
| POST /v1/posts | 10 posts | Per minute per user |
| POST /v1/accounts | 5 registrations | Per hour per IP |
| WebSocket messages | 120 messages | Per minute per connection |
| Node registration | 3 registrations | Per day per IP |
X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. When limited, a 429 is returned with a Retry-After header.
Open source
Candor Reach is fully open source under the Apache 2.0 license. You can read the code, run your own node, submit issues, and contribute pull requests.
CONTRIBUTING.md in the main repo. All contributors sign a standard CLA that assigns rights to Open Feed Network Corp while preserving the Apache 2.0 public license.
Test the Candor Shield API in real time. No API key required for this demo.
Real-time health of all Candor infrastructure. Updated every 30 seconds.