Build on Ascended Social
The Public API gives you read-only access to users, posts, videos, live sessions, books, SABS characters, and more — all paginated, all authenticated, all public data only.
Authentication
Bearer token in every request. Keys live in your account settings.
13 Endpoints
Users, posts, videos, live, books, SABS, sigils — all read-only.
Rate Limited
100–10,000 req/min depending on tier. Headers tell you what's left.
Privacy First
Only public data returned. PII, private posts, DMs never exposed.
Quick Start
1. Get an API Key
Create a free account at ascended.social, then go to Settings → Developer to generate a key. It will look like:
sk_live_REDACTED_EXAMPLE_KEY
2. Make your first request
The public health endpoint requires no key and tells you the API is up:
# No API key needed for health check
curl https://ascended.social/api/v1/healthz
# Response
{
"status": "ok",
"timestamp": "2026-06-03T12:00:00.000Z"
}
3. Fetch the post feed
curl -H "Authorization: Bearer sk_live_your_key" \
"https://ascended.social/api/v1/posts?limit=5"
{
"data": [
{
"id": "post_abc123",
"authorId": "usr_xyz789",
"content": "Just witnessed the most incredible sunrise 🌅",
"createdAt": "2026-05-30T06:12:00.000Z"
},
// ... more posts
],
"page": 0,
"pageSize": 5,
"hasMore": true
}
All endpoints except /healthz require the
Authorization: Bearer sk_live_... header. Without it
you get 401 Unauthorized.
Authentication
Pass your API key as a Bearer token in the
Authorization header on every request:
Authorization: Bearer sk_live_<your_key_here>
Key types
| Prefix | Type | Use |
|---|---|---|
| sk_live_ | Production | Live data — use in your backend only |
| sk_test_ | Test / sandbox | For development and CI pipelines |
Never expose API keys in client-side code, browser JavaScript,
or public repositories.
Use environment variables
(process.env.ASCENDED_API_KEY) or a secrets manager.
Keys are hashed and cannot be recovered — if lost, revoke and
regenerate.
Key management
- Create and revoke keys at Settings → Developer
- You can have multiple keys (useful for separating services or environments)
-
Revoked keys return
401immediately and cannot be un-revoked -
last_used_atis updated on every valid request for auditing
Base URL
All API endpoints are served under:
https://ascended.social/api/v1
Example — the posts feed is at:
GET https://ascended.social/api/v1/posts
The interactive API Explorer (Swagger UI) is pre-configured with this base URL. Use it to browse every endpoint and try requests live in your browser.
Versioning
The API version is embedded in the path (/api/v1). The
current stable version is v1. We will not make
breaking changes to v1 — any incompatible changes will be released
under /api/v2 with ample advance notice.
Pagination
All list endpoints use page-based pagination. You control it with two query parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | integer | 0 | 0-indexed page number |
| limit | integer | 20 | Results per page (max 50) |
Response envelope
Every paginated response wraps the array in a standard envelope:
{
"data": [ /* array of results */ ],
"page": 0, // current page (0-indexed)
"pageSize": 20, // items per page you requested
"hasMore": true // false when you've reached the last page
}
Some endpoints add extra top-level keys (e.g. bookId in
the chapters response, userId in character responses).
Walking all pages
async function fetchAll(endpoint) {
const results = [];
let page = 0;
do {
const res = await fetch(
`https://ascended.social/api/v1/${endpoint}?page=${page}&limit=50`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const json = await res.json();
results.push(...json.data);
if (!json.hasMore) break;
page++;
} while (true);
return results;
}
const allPosts = await fetchAll('posts');
Be mindful of rate limits when paginating large datasets. Consider
using limit=50 (the max) to minimize the number of
requests.
Rate Limits
Every response includes these headers so you can track consumption in real time:
X-RateLimit-Limit: 100 # requests allowed per minute
X-RateLimit-Remaining: 87 # requests left this minute
X-RateLimit-Remaining-Month: 9134 # requests left this month (Free/Pro)
Tiers
| Tier | Req / minute | Req / month | How to get it |
|---|---|---|---|
| Free | 100 | 10,000 | All accounts, immediate access |
| Pro | 1,000 | 1,000,000 | Active Pro subscription |
| Enterprise | 10,000 | Unlimited | Contact us |
When you're rate limited
The API returns 429 Too Many Requests. The response
includes a Retry-After header (seconds) and a JSON
body:
{
"error": "Too Many Requests",
"message": "Rate limit exceeded: 100 requests per minute",
"tier": "free",
"retryAfter": 60
}
Handling 429s in code
async function fetchWithRetry(url, opts, retries = 3) {
const res = await fetch(url, opts);
if (res.status === 429 && retries > 0) {
const wait = 1000 * (parseInt(res.headers.get('Retry-After') || '60'));
await new Promise(r => setTimeout(r, wait));
return fetchWithRetry(url, opts, retries - 1);
}
return res;
}
Error Handling
All error responses share a consistent shape:
{
"error": "Not found", // short identifier
"message": "Post not found" // human-readable explanation
}
| Status | When it happens | Action |
|---|---|---|
| 400 |
Invalid query parameter (e.g. non-numeric page)
|
Fix the request parameters |
| 401 | Missing, malformed, or revoked API key |
Check the Authorization header format
|
| 404 | Resource ID doesn't exist or profile is private | The resource isn't available publicly |
| 429 | Per-minute or monthly quota exceeded |
Wait for Retry-After seconds
|
| 500 | Unexpected server error | Check status.ascended.social, retry with backoff |
Robust error handling example
async function apiRequest(path, opts = {}) {
const res = await fetch(`https://ascended.social/api/v1${path}`, {
...opts,
headers: {
Authorization: `Bearer ${process.env.ASCENDED_API_KEY}`,
...opts.headers,
},
});
if (res.status === 404) return null; // Not found — treat as absent
if (res.status === 401) throw new Error('Invalid API key');
if (res.status === 429) {
const err = new Error('Rate limited');
err.retryAfter = parseInt(res.headers.get('Retry-After') || '60');
throw err;
}
if (!res.ok) throw new Error(`API error ${res.status}`);
return res.json();
}
Privacy Model
The Ascended Social Public API is strictly read-only and returns only content users have made publicly visible:
- Private profiles are excluded from all responses (404 is returned)
- Direct messages and private posts are never accessible
- PII (email addresses, phone numbers, IP data) is stripped at the query level
- Row-Level Security (RLS) is enforced at the PostgreSQL layer — no code path can accidentally leak private data
- All API key usage is logged per-key for audit and abuse prevention
By using this API you agree to the API Terms of Use. Do not use the API to build surveillance tools, scrape data in bulk for resale, or contact users without their consent.
API Reference
All endpoints are under https://ascended.social/api/v1.
All except /healthz require
Authorization: Bearer sk_live_.... For interactive
docs, visit
api-docs.ascended.social →
Health
Returns server health. Used by uptime monitors and status pages. No authentication required.
Response
{
"status": "ok",
"timestamp": "2026-06-03T12:00:00.000Z"
}
Users
Returns a user's public profile. Returns 404 if the
profile is private or the user doesn't exist.
Path Parameters
| Param | Type | Description |
|---|---|---|
| userId | string | The user's ID |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | string | Unique user ID |
| username | string | null | URL-safe username |
| displayName | string | null | Display name |
| bio | string | null | Short bio |
| profileImageUrl | string | null | Avatar image URL |
| createdAt | ISO 8601 | Account creation timestamp |
curl -H "Authorization: Bearer sk_live_..." \
https://ascended.social/api/v1/users/usr_abc123
Returns paginated posts authored by a user, newest first.
Query Parameters
| Param | Default | Max |
|---|---|---|
| page | 0 | — |
| limit | 20 | 50 |
Response
{
"data": [{ "id", "content", "createdAt", "updatedAt" }],
"page": 0, "pageSize": 20, "hasMore": false
}
Posts
Global public post feed, newest first. Returns each post with its author's ID.
Query Parameters
| Param | Default | Max |
|---|---|---|
| page | 0 | — |
| limit | 20 | 50 |
Response Fields (per item)
| Field | Type | Description |
|---|---|---|
| id | string | Post ID |
| authorId | string | ID of the post author |
| content | string | Post text content |
| createdAt | ISO 8601 | Publication time |
Returns a single post by ID. Returns 404 if it
doesn't exist or is private.
Response Fields
| Field | Type | Description |
|---|---|---|
| id | string | Post ID |
| authorId | string | Author's user ID |
| content | string | Post text |
| createdAt | ISO 8601 | Published at |
| updatedAt | ISO 8601 | null | Last edited at |
Books
Paginated list of public books in the library, newest first.
Response Fields (per item)
| Field | Type |
|---|---|
| id | string |
| title | string |
| description | string | null |
| createdAt | ISO 8601 |
Returns a single book by ID. Same fields as the list endpoint.
Paginated chapters for a book. Response also includes the
top-level bookId field.
Response
{
"bookId": "book_abc123",
"data": [{ "id", "bookId", "title", "createdAt" }],
"page": 0, "pageSize": 20, "hasMore": false
}
Videos
Paginated list of public videos from the Elements feed, newest first.
Response Fields (per item)
| Field | Type |
|---|---|
| id | string |
| title | string | null |
| description | string | null |
| createdAt | ISO 8601 |
Live Sessions
Returns live and recently ended video sessions, newest first.
Response Fields (per item)
| Field | Type | Description |
|---|---|---|
| id | string | Session ID |
| title | string | null | Session title |
| description | string | null | Description |
| status | string | null | "live", "ended", etc. |
| createdAt | ISO 8601 | Session start time |
SABS — Soul-Bound Ascension System
SABS is Ascended Social's spiritual progression game. Users create characters, join campaigns, and level up through real-world spiritual practices.
Returns paginated public SABS campaigns, newest first.
Response Fields (per item)
| Field | Type | Description |
|---|---|---|
| id | string | Campaign ID |
| title | string | null | Campaign name |
| description | string | null | Description |
| status | string | null | "active", "completed", etc. |
| createdAt | ISO 8601 | Creation time |
Returns paginated SABS characters owned by a user. The top-level
response also includes the userId.
Response
{
"userId": "usr_abc123",
"data": [{ "id", "userId", "name", "createdAt" }],
"page": 0, "pageSize": 20, "hasMore": false
}
Sigils
Sigils are unique spiritual symbols minted by users. They can be offered for trade with other members of the community.
Returns public details for a sigil trade by its ID.
Response Fields
| Field | Type | Description |
|---|---|---|
| id | string | Trade ID |
| state | string | null | "pending", "completed", "expired" |
| expiresAt | ISO 8601 | null | When the offer expires |
| completedAt | ISO 8601 | null | When the trade was accepted |
| createdAt | ISO 8601 | When the offer was created |
JavaScript / TypeScript
Minimal API client
A lightweight wrapper you can copy into any project:
// ascended-api.ts
const BASE = "https://ascended.social/api/v1";
async function ascendedFetch(path: string, params: Record<string, any> = {}) {
const url = new URL(BASE + path);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, String(v)));
const res = await fetch(url.toString(), {
headers: { Authorization: `Bearer ${process.env.ASCENDED_API_KEY}` },
});
if (res.status === 429) {
const retry = parseInt(res.headers.get("Retry-After") || "60");
throw Object.assign(new Error("Rate limited"), { retryAfter: retry });
}
if (!res.ok) throw new Error(`Ascended API error: ${res.status}`);
return res.json();
}
// Usage
const posts = await ascendedFetch("/posts", { limit: 50, page: 0 });
const user = await ascendedFetch(`/users/${userId}`);
const books = await ascendedFetch("/books", { page: 0 });
Fetch all pages (TypeScript)
async function fetchAllPages<T>(path: string): Promise<T[]> {
const results: T[] = [];
let page = 0;
while (true) {
const res: { data: T[]; hasMore: boolean } =
await ascendedFetch(path, { page, limit: 50 });
results.push(...res.data);
if (!res.hasMore) break;
page++;
}
return results;
}
const allBooks = await fetchAllPages<Book>("/books");
Next.js API Route example
// app/api/feed/route.ts
import { NextResponse } from "next/server";
export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const page = searchParams.get("page") || "0";
const res = await fetch(
`https://ascended.social/api/v1/posts?page=${page}&limit=20`,
{ headers: { Authorization: `Bearer ${process.env.ASCENDED_API_KEY}` },
next: { revalidate: 60 } } // Cache for 60s
);
if (!res.ok) return NextResponse.json({ error: "upstream error" }, { status: 502 });
return NextResponse.json(await res.json());
}
Python
Using httpx
# pip install httpx
import httpx, os
BASE = "https://ascended.social/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['ASCENDED_API_KEY']}"}
def fetch_posts(page=0, limit=20):
r = httpx.get(f"{BASE}/posts", params={"page": page, "limit": limit}, headers=HEADERS)
r.raise_for_status()
return r.json()
def fetch_user(user_id: str):
r = httpx.get(f"{BASE}/users/{user_id}", headers=HEADERS)
if r.status_code == 404:
return None
r.raise_for_status()
return r.json()
def fetch_all_books():
books, page = [], 0
while True:
resp = fetch_posts.__globals__['httpx'].get(
f"{BASE}/books", params={"page": page, "limit": 50}, headers=HEADERS
).json()
books.extend(resp["data"])
if not resp["hasMore"]: break
page += 1
return books
# Example
feed = fetch_posts(limit=50)
print(f"Got {len(feed['data'])} posts, hasMore={feed['hasMore']}")
Using requests
# pip install requests
import requests, os
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {os.environ['ASCENDED_API_KEY']}"
})
BASE = "https://ascended.social/api/v1"
resp = session.get(f"{BASE}/posts", params={"page": 0, "limit": 20})
resp.raise_for_status()
data = resp.json()
for post in data["data"]:
print(post["id"], post["content"][:60])
cURL
# Health check (no key needed)
curl https://ascended.social/api/v1/healthz
# Post feed
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
"https://ascended.social/api/v1/posts?limit=20&page=0"
# User profile
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/users/usr_abc123
# Books library
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
"https://ascended.social/api/v1/books?page=0&limit=50"
# Chapters in a book
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/books/book_abc123/chapters
# Video feed
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/videos
# Live sessions
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/live/sessions
# SABS campaigns
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/sabs/campaigns
# SABS characters for a user
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/sabs/characters/by-user/usr_abc123
# Sigil trade
curl -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/sigils/trades/trade_abc123
# Save rate limit headers to a file for inspection
curl -D headers.txt -H "Authorization: Bearer $ASCENDED_API_KEY" \
https://ascended.social/api/v1/posts -o /dev/null 2>&1
grep -i "X-RateLimit" headers.txt
Changelog
v1.0.0 — June 2026
- Initial public release of the Ascended Social Public API
- 13 read-only endpoints: users, posts, books, chapters, videos, live sessions, SABS campaigns, SABS characters, sigil trades, and health
- SHA-256 API key hashing for O(1) indexed key lookup
- Per-minute and per-month rate limiting with response headers
-
Full usage tracking to
api_usagetable (per-request endpoint, method, status, response time) - Row-Level Security enforced at the database layer for all API tables
- Interactive Swagger UI at api-docs.ascended.social
- OpenAPI 3.1 spec at api-docs.ascended.social/openapi.yaml
- Status monitoring at status.ascended.social
Support
Developer Support
Questions about the API, rate limit increases, enterprise plans:
Interactive Explorer
Browse every endpoint and try requests in your browser with no setup.
Service Status
Real-time uptime, latency, and incident history for all API services.
Security Issues
Found a vulnerability? Please report it through our responsible disclosure policy.