Node.js/Express backend server that proxies Polymarket API requests and provides AI-powered comment analysis using Claude and OpenAI.
npm installcp .env.example .env
# Edit .env and add your API keys# Production
npm start
# Development (auto-reload)
npm run devServer will start on http://localhost:3000
GET /health
Check if the server is running.
Response:
{
"status": "healthy",
"timestamp": "2026-02-13T10:30:00.000Z",
"environment": "production"
}GET /api/polymarket/comments/:marketId
Fetch comments for a specific Polymarket market.
Parameters:
marketId(path) - Market ID or sluglimit(query, optional) - Max comments to fetch (default: 100)offset(query, optional) - Pagination offset (default: 0)
Example:
GET /api/polymarket/comments/will-trump-win-2024?limit=500Response:
{
"success": true,
"data": [
{
"id": "comment-123",
"body": "Based on recent polling...",
"profile": {
"pseudonym": "TraderJoe",
"positions": [
{
"tokenId": "abc123",
"positionSize": "1250.50"
}
]
},
"createdAt": "2026-01-15T12:00:00Z"
}
],
"count": 237,
"marketId": "will-trump-win-2024"
}Error Response:
{
"success": false,
"error": "Failed to fetch comments from Polymarket",
"message": "Market not found"
}GET /api/polymarket/market/:marketId
Fetch metadata for a specific market.
Parameters:
marketId(path) - Market ID or slug
Example:
GET /api/polymarket/market/will-trump-win-2024Response:
{
"success": true,
"data": {
"id": "market-123",
"question": "Will Trump win 2024?",
"description": "Resolves YES if...",
"endDate": "2024-11-05T00:00:00Z",
"volume": "15000000"
}
}POST /api/analyze/comments
Analyze comments using AI (Claude primary, OpenAI backup).
Request Body:
{
"comments": [
{
"id": "comment-123",
"body": "Comment text...",
"profile": {
"pseudonym": "TraderJoe",
"positions": [{ "positionSize": "1000" }]
}
}
],
"options": {
"filterHolders": true,
"analyzeSentiment": true,
"detectDivergence": true,
"findInsiders": true
}
}Response:
{
"success": true,
"data": {
"summary": {
"totalComments": 237,
"holdersCount": 89,
"overallSentiment": "bullish",
"sentimentDistribution": {
"bullish": 120,
"bearish": 67,
"neutral": 50
}
},
"insights": [
"Large holders are 60% bullish while crowd is 40% bullish",
"Recent polling data mentioned frequently",
"Divergence detected in key swing states"
],
"divergenceAlerts": [
{
"severity": "high",
"description": "Holders with >$1000 positions are 70% bearish while crowd is 65% bullish"
}
],
"topComments": [
{
"commentIndex": 5,
"username": "TraderJoe",
"sentiment": "bullish",
"alphaScore": 9,
"positionSize": "5000",
"reasoning": "Provides detailed polling analysis with sources"
}
],
"insiderMentions": [
{
"comment": "I have connections in...",
"significance": "Claims insider knowledge of campaign strategy"
}
]
},
"metadata": {
"commentsAnalyzed": 237,
"aiProvider": "claude",
"timestamp": "2026-02-13T10:30:00.000Z"
}
}Error Response:
{
"success": false,
"error": "Failed to analyze comments",
"message": "ANTHROPIC_API_KEY not configured"
}Create a .env file with:
# Server
NODE_ENV=production
PORT=3000
# CORS
FRONTEND_URL=*
# AI APIs (at least one required)
ANTHROPIC_API_KEY=sk-ant-api03-xxxxx
OPENAI_API_KEY=sk-proj-xxxxxANTHROPIC_API_KEYOROPENAI_API_KEY- At least one AI API key
NODE_ENV- Environment (default: development)PORT- Server port (default: 3000)FRONTEND_URL- CORS allowed origin (default: *)
- Model:
claude-3-5-sonnet-20241022 - Provider: Anthropic
- Cost: ~$0.003 per analysis
- Max tokens: 4096 output
- Temperature: 0.7
- Model:
gpt-4-turbo-preview - Provider: OpenAI
- Cost: ~$0.01 per analysis
- Max tokens: 4096 output
- Temperature: 0.7
If Claude fails (API error, rate limit, etc.), the system automatically falls back to OpenAI.
Flow:
Request → Try Claude → Success ✅
→ Fail → Try OpenAI → Success ✅
→ Fail → Error ❌
The system builds a structured prompt with:
- All comments with usernames and positions
- Analysis requirements based on options
- JSON output format specification
Sends prompt to Claude (or OpenAI if Claude fails)
Parses AI response as JSON with:
- Summary statistics
- Sentiment distribution
- Key insights
- Divergence alerts
- Top comments with alpha scores
- Insider mentions
If parsing fails:
- Returns fallback structure
- Logs error for debugging
- Ensures app doesn't crash
app.use(cors({
origin: process.env.FRONTEND_URL || '*',
methods: ['GET', 'POST'],
credentials: true
}));Production: Set FRONTEND_URL to your frontend domain
- Stored in environment variables
- Never exposed to frontend
- Validated before use
- Errors logged without exposing keys
Not implemented yet - Coming in v1.1
Recommended: Use Railway's built-in rate limiting or add express-rate-limit
All requests are logged with:
- Endpoint accessed
- Response status
- Error messages (if any)
- Timestamp
View logs:
# Local
npm start
# Railway
Check Deployments → LogsEndpoint: /health
Use for:
- Uptime monitoring (UptimeRobot, Pingdom)
- Load balancer health checks
- CI/CD deployment verification
{
"success": false,
"error": "Error category",
"message": "Detailed error message",
"details": { /* Additional error data */ }
}| Status | Error | Cause |
|---|---|---|
| 400 | Invalid request | Missing/invalid parameters |
| 401 | Authentication failed | Invalid API key |
| 404 | Not found | Invalid endpoint |
| 429 | Rate limit exceeded | Too many requests |
| 500 | Internal server error | Server/API failure |
# Health check
curl http://localhost:3000/health
# Fetch comments
curl "http://localhost:3000/api/polymarket/comments/test-market?limit=10"
# Analyze comments
curl -X POST http://localhost:3000/api/analyze/comments \
-H "Content-Type: application/json" \
-d '{
"comments": [...],
"options": {
"analyzeSentiment": true,
"detectDivergence": true
}
}'Not implemented yet - Coming in v1.1
Planned:
- Jest for unit tests
- Supertest for API tests
- 90%+ code coverage
- Push code to GitHub
- Connect Railway to repo
- Set environment variables
- Deploy automatically
See: DEPLOYMENT.md for detailed guide
- Heroku: Add
Procfile - Vercel: Add
vercel.json - Render: Works out of the box
- DigitalOcean App Platform: Add app spec
{
"express": "^4.18.2", // Web framework
"cors": "^2.8.5", // CORS middleware
"axios": "^1.6.2", // HTTP client
"dotenv": "^16.3.1" // Environment config
}{
"nodemon": "^3.0.2" // Auto-reload
}Total size: ~2.5 MB (node_modules)
const PORT = process.env.PORT || 3000;Railway automatically assigns a port via process.env.PORT
axios.get(url, { timeout: 10000 }) // 10 secondsPrevents hanging requests
const { limit = 100, offset = 0 } = req.query;Default: 100 comments per request
Max: 1000 (Polymarket API limit)
Built with 💚 by the Polymarket community
Version 1.0.0