http://localhost:5000/api
Most endpoints require JWT authentication. Include the token in the Authorization header:
Authorization: Bearer <your_jwt_token>Important
Keep your JWT token secure. Do not expose it in client-side code repositories.
All API responses follow a consistent structure.
Success Response:
{
"data": { ... },
"message": "Operation successful"
}Error Response:
{
"error": "Error message description",
"details": [ ... ]
}Create a new user account.
POST /auth/register
Request Body:
{
"username": "johndoe",
"fullName": "John Doe",
"email": "john@example.com",
"password": "strongpassword123"
}Response:
{
"message": "Registration successful. Please verify your email.",
"user": {
"id": "60d0fe4f5311236168a109ca",
"username": "johndoe",
"email": "john@example.com",
"isVerified": false
}
}Verify a user's email address using the OTP sent.
POST /auth/verify-email
Request Body:
{
"email": "john@example.com",
"otp": "123456"
}Authenticate a user and receive a JWT.
POST /auth/login
Request Body:
{
"email": "john@example.com",
"password": "strongpassword123"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "60d0fe4f5311236168a109ca",
"username": "johndoe",
"email": "john@example.com",
"reputation": 0
}
}Retrieve the currently authenticated user's details.
GET /auth/me
Note
Requires Authentication.
Initiate a passwordless login flow.
POST /auth/login-otp-init
Body:
{
"email": "john@example.com"
}Complete the passwordless login.
POST /auth/login-otp-verify
Body:
{
"email": "john@example.com",
"otp": "123456"
}Request a password reset OTP.
POST /auth/forgot-password
Body:
{
"email": "john@example.com"
}Set a new password using the OTP.
POST /auth/reset-password
Body:
{
"email": "john@example.com",
"otp": "123456",
"newPassword": "newpassword123"
}Retrieve a list of available stocks.
GET /stocks
Query Parameters:
sector(optional): Filter by sector (e.g., "Technology").search(optional): Search by name or symbol.
Response:
{
"stocks": [
{
"symbol": "AAPL",
"name": "Apple Inc.",
"sector": "Technology",
"currentPrice": 150.25,
"change": 2.50,
"changePercent": 1.69
}
]
}Retrieve detailed information for a specific stock.
GET /stocks/:symbol
Response:
{
"stock": {
"symbol": "AAPL",
"name": "Apple Inc.",
"sector": "Technology",
"currentPrice": 150.25,
"previousClose": 147.75,
"volume": 52000000,
"marketCap": 2500000000000,
"high24h": 151.00,
"low24h": 148.50,
"description": "Apple Inc. designs, manufactures, and markets smartphones..."
}
}GET /stocks/:symbol/trending
Query Parameters:
limit(optional, default: 5): Number of questions to retrieve.
Retrieve a list of questions based on filters.
GET /questions
Query Parameters:
stockId(optional): Filter by stock ID.userId(optional): Filter by user ID.tag(optional): Filter by specific tag.sort(optional): Sort order (recent,popular,unanswered).limit(optional, default: 20).page(optional, default: 1).
Retrieve a single question and its answers.
GET /questions/:id
Response:
{
"question": {
"id": "...",
"title": "Is AAPL a good buy right now?",
"content": "Given the recent earnings report...",
"stockId": { ... },
"userId": { ... },
"tags": ["analysis", "long-term"],
"upvotes": 15,
"views": 120,
"answerCount": 3,
"answers": [ ... ]
}
}Post a new question.
POST /questions
Note
Requires Authentication.
Body:
{
"stockId": "...",
"title": "Is AAPL a good buy?",
"content": "I'm considering buying AAPL...",
"tags": ["analysis", "long-term"]
}Answer a specific question.
POST /questions/:id/answers
Note
Requires Authentication.
Body:
{
"content": "Yes, AAPL is a solid long-term investment because..."
}PUT /questions/:id/upvote
Note
Requires Authentication.
PUT /questions/:id/downvote
Note
Requires Authentication.
PUT /questions/answers/:answerId/upvote
Note
Requires Authentication.
PUT /questions/answers/:answerId/downvote
Note
Requires Authentication.
Mark an answer as accepted.
PUT /questions/:questionId/answers/:answerId/accept
Note
Requires Authentication. Only the question author can perform this action.
Retrieve recent market predictions.
GET /predictions
Query Parameters:
stockId(optional): Filter by stock.userId(optional): Filter by user.timeframe(optional):1h,1d,1w,1m.evaluated(optional):true,false.limit(optional, default: 20).
Submit a new prediction.
POST /predictions
Note
Requires Authentication.
Body (Price Prediction):
{
"stockId": "...",
"predictionType": "price",
"targetPrice": 160.00,
"timeframe": "1w",
"reasoning": "Based on recent earnings..."
}Body (Direction Prediction):
{
"stockId": "...",
"predictionType": "direction",
"direction": "up",
"timeframe": "1d",
"reasoning": "Market sentiment is positive..."
}GET /predictions/user/:userId
GET /predictions/stats
Note
Requires Authentication.
Response:
{
"stats": {
"totalPredictions": 50,
"accuratePredictions": 35,
"accuracy": 70,
"byTimeframe": {
"1h": { "total": 10, "accurate": 6 },
"1d": { "total": 20, "accurate": 15 }
}
}
}Retrieve top-ranked users.
GET /users/leaderboard
Query Parameters:
limit(optional, default: 10).
Response:
{
"leaderboard": [
{
"username": "johndoe",
"reputation": 250.5,
"totalPredictions": 100,
"accuracy": 75.5
}
]
}GET /users/:userId
Response:
{
"user": {
"username": "johndoe",
"fullName": "John Doe",
"bio": "...",
"reputation": 250.5,
"totalPredictions": 100,
"accuratePredictions": 75,
"accuracy": 75.5,
"tradingExperience": "intermediate",
"location": "New York"
}
}GET /users/:userId/stats
Update user profile information.
PUT /users/profile
Note
Requires Authentication.
Body:
{
"fullName": "John Doe",
"bio": "Experienced trader...",
"location": "New York",
"tradingExperience": "expert",
"phone": "+1234567890"
}GET /health
Response:
{
"status": "ok",
"message": "StockForumX API is running"
}Warning
To ensure fair usage, the API enforces rate limits.
- Limit: 100 requests per 15 minutes per IP.
Response (when exceeded):
{
"error": "Too many requests from this IP, please try again later."
}| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 409 | Conflict |
| 429 | Too Many Requests |
| 500 | Internal Server Error |