Capacitr API Docs
Backend services powering Capacitr's market intelligence platform — turning signal into position across prediction markets, perpetuals, and options.
All APIs are Next.js 14 App Router route handlers deployed on Vercel. The backend integrates Polymarket (prediction markets via Gamma API), Quotient (mispricing intelligence), HyperLiquid (perpetuals), Deribit (options), OpenRouter GPT-4o-mini (LLM analysis), Jina AI (web scraping), and Supabase (PostgreSQL persistence).
All endpoints return JSON. Successful responses include the data directly. Error responses return { "error": "message" } with an appropriate HTTP status code.
Most market intelligence endpoints are unauthenticated. User endpoints require a Privy ID passed as a query parameter or in the request body.
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — missing or invalid parameters |
| 401 | Unauthorized — missing or wrong auth credentials |
| 404 | Not found — resource doesn't exist |
| 500 | Internal server error — check server logs |
Core endpoint — turns any URL or text query into a full market discovery report: prediction markets, perpetuals, options, and Quotient mispricing signals.
Authentication required. Accepts a Privy JWT (Authorization: Bearer <token>) or a Capacitr skill key (X-Capacitr-Skill-Key: <key>). After validating the input, the route reserves one unit from the caller's shared daily UTC quota (default 5) before running the source adapter, scrape, or analysis. If a unit is reserved, it is consumed even if the downstream scrape or analysis fails. The quota resets at UTC midnight; when exhausted the endpoint returns 429 with X-RateLimit-* headers and a JSON body containing error, limit, remaining: 0, and resetAt. GET is not supported and returns 405 Method Not Allowed with Allow: POST.
Accepts a news URL or freetext query. Scrapes the content (URL path via Jina AI), runs LLM extraction to identify tickers, entities, keywords, and categories, then fans out to Polymarket, HyperLiquid, and Deribit to find matching markets. Results are enriched with Quotient mispricing signals and sorted by edge opportunity. Search results are logged to the user_scans Postgres table. A 502 means the upstream scrape or content extraction failed.
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | optional* | A news article or web page URL to analyze. Either url or query must be provided. |
| query | string | optional* | A freetext search query (e.g. "NVIDIA earnings Q1 2025"). Used when no URL is provided. |
POST /api/analyze-link
Content-Type: application/json
Authorization: Bearer eyJ...
# OR: X-Capacitr-Skill-Key: csk_live_...
{
"url": "https://reuters.com/technology/nvidia-earnings-beat-2025"
}
{
"mode": "discover",
"content": {
"summary": "NVIDIA reported record Q1 earnings...",
"keywords": ["NVIDIA", "earnings", "semiconductors", "AI chips"],
"entities": ["NVIDIA", "Jensen Huang"],
"tickers": ["NVDA"]
},
"analysis": {
"narrative": "NVIDIA's record Q1 earnings beat signals strong AI-chip demand and near-term upside.",
"keySubjects": ["NVIDIA", "AI chips"],
"keywords": ["earnings", "semiconductors"],
"catalysts": [{ "claim": "Raised guidance reinforces AI-chip demand", "direction": "bullish", "strength": "high" }]
},
"insights": [
{ "claim": "NVDA guidance raised", "direction": "bullish", "strength": "high", "subjects": ["NVDA"] }
],
"predictions": [
{
"question": "Will NVIDIA stock hit $1000 by Q3 2025?",
"yesPrice": 0.62,
"noPrice": 0.38,
"volume": 485000,
"slug": "will-nvidia-hit-1000"
}
],
"perps": [
{
"asset": "NVDA",
"markPrice": 872.50,
"funding": 0.0003,
"volume24h": 12400000
}
],
"options": [],
"searchId": "123e4567-e89b-12d3-a456-426614174000",
"traceId": "123e4567-e89b-12d3-a456-426614174001",
"searchUrl": "https://markets.capacitr.xyz/markets?searchId=123e4567-e89b-12d3-a456-426614174000",
"recommendedTrades": [],
"bestTrade": null,
"primaryTrades": [],
"complementaryTrades": [],
"nonObviousTrades": [],
"watchlist": []
}
{
"error": "Scan link daily limit reached",
"limit": 5,
"remaining": 0,
"resetAt": "2026-07-16T00:00:00.000Z"
}
Proxies requests to the Quotient API for mispricing intelligence on Polymarket markets. Supports three actions: fetching mispriced markets, looking up specific markets by slug, and querying general market intelligence.
| Param | Type | Required | Description |
|---|---|---|---|
| action | enum | required | One of: mispriced | lookup | intelligence |
| limit | integer | optional | Max results (action=mispriced). Default: 20 |
| min_spread | float | optional | Minimum mispricing spread filter (action=mispriced). Default: 0.05 |
| sort | string | optional | Sort order (action=mispriced). Default: spread_desc |
| slugs | string | optional* | Comma-separated market slugs (required for action=lookup) |
| query | string | optional* | Text query (required for action=intelligence) |
GET /api/quotient?action=mispriced&limit=5&min_spread=0.08
GET /api/quotient?action=lookup&slugs=will-bitcoin-hit-100k,eth-merge-by-q3
Streaming AI chat agent powered by GPT-4o-mini via OpenRouter. Has tool access to Polymarket search and Quotient mispricing data.
Streams a response from the Capacitr market research agent using the Vercel AI SDK (Server-Sent Events). The agent can call tools: searchMarkets (Polymarket text search), getMispricedMarkets (Quotient top opportunities), and getMarketIntelligence (Quotient intelligence for a query). Use the useChat hook from ai/react client-side.
| Field | Type | Required | Description |
|---|---|---|---|
| messages | Message[] | required | Array of chat messages in Vercel AI SDK format: { role: "user"|"assistant", content: string } |
POST /api/chat
Content-Type: application/json
{
"messages": [
{ "role": "user", "content": "What prediction markets exist around the US election?" }
]
}
Returns a streaming text/event-stream response in Vercel AI SDK Data Stream format. Compatible with the useChat hook.
| Tool | Description |
|---|---|
| searchMarkets | Full-text search of Polymarket via Gamma API. Returns top 5 matching markets with prices and volume. |
| getMispricedMarkets | Returns top mispriced markets from Quotient with spread and BLUF analysis. |
| getMarketIntelligence | Runs a Quotient intelligence query for a specific topic, returning relevant markets and edge signals. |
Personalized market signal feed — fetches Reddit and Hacker News posts based on user interests, runs LLM analysis, and returns tradeable predictions enriched with Quotient mispricing data.
Fetches top posts from Reddit subreddits and Hacker News based on the user's interest categories (crypto, politics, ai, tech, finance, sports, pop-culture, music, food, environment). Each post is analyzed by LLM to extract tickers, entities, and generate a prediction card with Polymarket + HyperLiquid + Deribit markets. Results are enriched with Quotient mispricing signals.
| Param | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | The Privy DID of the authenticated user. Used to fetch their interest categories. |
| interests | string | optional | Comma-separated override interest list (skips DB lookup). E.g. "crypto,ai,tech" |
GET /api/feed?privyId=did:privy:abc123
{
"items": [
{
"id": "abc123",
"title": "Bitcoin ETF inflows hit record $2B in single day",
"source": "reddit",
"subreddit": "cryptocurrency",
"url": "https://reddit.com/r/cryptocurrency/...",
"summary": "Bitcoin ETFs recorded record inflows...",
"entities": ["Bitcoin", "BlackRock"],
"tickers": ["BTC"],
"predictions": [...],
"perps": [...],
"options": [],
"categories": ["crypto", "finance"]
}
]
}
User profile management — get/create users, manage interests and risk profiles. All user data is stored in Supabase and keyed by Privy DID.
| Param | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | The Privy DID of the user |
{
"user": {
"id": "uuid",
"privy_id": "did:privy:abc123",
"privy_provider": "twitter",
"display_name": "Atown",
"avatar_url": "https://...",
"twitter_handle": "capacitr_xyz",
"farcaster_fid": null,
"created_at": "2025-01-01T00:00:00Z",
"interests": ["crypto", "ai", "finance"]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | Privy DID |
| displayName | string | optional | User's display name |
| avatarUrl | string | optional | Profile image URL |
| Param | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | The Privy DID of the user |
{ "interests": ["crypto", "ai", "finance"] }
| Field | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | Privy DID |
| interests | string[] | required | Array of interest slugs. Valid: crypto, politics, ai, tech, finance, sports, pop-culture, music, food, environment |
POST /api/interests
Content-Type: application/json
{ "privyId": "did:privy:abc123", "interests": ["crypto", "ai"] }
| Param | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | Privy DID |
{
"profile": {
"tier": "aggressive",
"scores": { "riskTolerance": 8, "timeHorizon": 7, "experience": 9 },
"completed_at": "2025-03-15T10:22:00Z"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | Privy DID |
| tier | string | required | Risk tier: conservative | moderate | aggressive |
| scores | object | optional | Raw quiz scores object |
Called automatically on login. Upserts the Privy user record into Supabase — creates if new, updates profile fields if existing. Called from the PrivyProviderWrapper on onSuccess.
| Field | Type | Required | Description |
|---|---|---|---|
| privyId | string | required | Privy DID |
| provider | string | optional | Auth provider: "twitter" | "farcaster" |
| displayName | string | optional | Display name from OAuth profile |
| avatarUrl | string | optional | Avatar URL from OAuth profile |
| twitterHandle | string | optional | Twitter/X handle (without @) |
| farcasterFid | integer | optional | Farcaster FID |
{ "user": { "id": "uuid", "privy_id": "did:privy:abc123", "display_name": "Atown", ... } }