Complete API reference for the Rock Spotter platform.
http://localhost:3000/api
For production, replace with your deployed API URL.
Most endpoints require authentication using JWT tokens. Include the token in the Authorization header:
Authorization: Bearer <your-jwt-token>
Tokens are obtained through the login or register endpoints and expire after 7 days.
200 OK - Request successful201 Created - Resource created successfully400 Bad Request - Invalid request parameters401 Unauthorized - Authentication required or invalid403 Forbidden - Insufficient permissions404 Not Found - Resource not found500 Internal Server Error - Server errorCreate a new user account.
POST /api/users/register
Content-Type: application/json
{
"username": "rockfan123",
"email": "user@example.com",
"password": "securepassword"
}
Response:
{
"message": "User registered successfully",
"user": {
"_id": "user_id",
"username": "rockfan123",
"email": "user@example.com",
"profilePicture": "",
"bio": "",
"rockCount": 0,
"huntCount": 0,
"achievements": []
},
"token": "jwt_token_here"
}
Authenticate and receive a JWT token.
POST /api/users/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "securepassword"
}
Response:
{
"message": "Login successful",
"user": { /* user object */ },
"token": "jwt_token_here"
}
Get the authenticated user’s profile.
GET /api/users/profile/me
Authorization: Bearer <token>
Response:
{
"user": {
"_id": "user_id",
"username": "rockfan123",
"email": "user@example.com",
"profilePicture": "https://example.com/avatar.jpg",
"bio": "Rock enthusiast since 2025",
"rockCount": 15,
"huntCount": 3,
"achievements": [/* achievement objects */]
}
}
Update the authenticated user’s profile.
PUT /api/users/profile/me
Authorization: Bearer <token>
Content-Type: application/json
{
"username": "newusername",
"bio": "Updated bio",
"profilePicture": "https://example.com/new-avatar.jpg"
}
Get a specific user’s public profile.
GET /api/users/:id
Retrieve a paginated list of rocks with optional filters.
GET /api/rocks?page=1&limit=20&rockType=igneous&userId=user_id
Query Parameters:
page (optional): Page number (default: 1)limit (optional): Items per page (default: 20, max: 100)rockType (optional): Filter by type (igneous, sedimentary, metamorphic, mineral, fossil, other)userId (optional): Filter by userResponse:
{
"rocks": [
{
"_id": "rock_id",
"title": "Beautiful Granite",
"description": "Found this amazing specimen",
"photo": "https://example.com/rock.jpg",
"location": {
"type": "Point",
"coordinates": [-122.4194, 37.7749],
"address": "San Francisco, CA"
},
"rockType": "igneous",
"user": {
"_id": "user_id",
"username": "rockfan123",
"profilePicture": "https://example.com/avatar.jpg"
},
"likes": ["user_id_1", "user_id_2"],
"comments": [],
"tags": ["granite", "pink"],
"isPublic": true,
"createdAt": "2025-10-16T12:00:00Z"
}
],
"totalPages": 5,
"currentPage": 1
}
Find rocks near a specific location.
GET /api/rocks/nearby?longitude=-122.4194&latitude=37.7749&maxDistance=5000
Query Parameters:
longitude (required): Longitude coordinatelatitude (required): Latitude coordinatemaxDistance (optional): Maximum distance in meters (default: 5000)Retrieve a specific rock with full details.
GET /api/rocks/:id
Response:
{
"rock": {
"_id": "rock_id",
"title": "Beautiful Granite",
"description": "Found this amazing specimen",
"photo": "https://example.com/rock.jpg",
"location": {
"type": "Point",
"coordinates": [-122.4194, 37.7749],
"address": "San Francisco, CA"
},
"rockType": "igneous",
"user": { /* user object */ },
"likes": ["user_id_1"],
"comments": [
{
"_id": "comment_id",
"user": { /* user object */ },
"text": "Amazing find!",
"createdAt": "2025-10-16T12:30:00Z"
}
],
"tags": ["granite", "pink"],
"isPublic": true,
"createdAt": "2025-10-16T12:00:00Z"
}
}
Share a new rock photo.
POST /api/rocks
Authorization: Bearer <token>
Content-Type: application/json
{
"title": "Beautiful Granite",
"description": "Found this amazing specimen near the river",
"photo": "https://example.com/rock.jpg",
"location": {
"type": "Point",
"coordinates": [-122.4194, 37.7749],
"address": "San Francisco, CA"
},
"rockType": "igneous",
"tags": ["granite", "pink", "sparkly"],
"isPublic": true
}
Response:
{
"message": "Rock posted successfully",
"rock": { /* rock object */ }
}
Update a rock post (only by owner).
PUT /api/rocks/:id
Authorization: Bearer <token>
Content-Type: application/json
{
"title": "Updated Title",
"description": "Updated description",
"rockType": "metamorphic",
"tags": ["updated", "tags"],
"isPublic": false
}
Delete a rock post (only by owner).
DELETE /api/rocks/:id
Authorization: Bearer <token>
Toggle like on a rock post.
POST /api/rocks/:id/like
Authorization: Bearer <token>
Response:
{
"message": "Rock liked",
"likes": 5
}
Add a comment to a rock post.
POST /api/rocks/:id/comment
Authorization: Bearer <token>
Content-Type: application/json
{
"text": "Amazing find! Where exactly did you find this?"
}
Retrieve a paginated list of hunts with optional filters.
GET /api/hunts?page=1&limit=20&isActive=true&difficulty=medium
Query Parameters:
page (optional): Page numberlimit (optional): Items per pageisActive (optional): Filter by active status (true/false)difficulty (optional): Filter by difficulty (easy, medium, hard)Response:
{
"hunts": [
{
"_id": "hunt_id",
"title": "Downtown Rock Hunt",
"description": "Find 5 hidden rocks in downtown!",
"creator": { /* user object */ },
"rocks": [
{
"rock": { /* rock object */ },
"hint": "Look near the fountain",
"order": 1
}
],
"difficulty": "medium",
"participants": [],
"startDate": "2025-10-20T00:00:00Z",
"endDate": "2025-10-27T23:59:59Z",
"isActive": true,
"maxParticipants": 100
}
],
"totalPages": 3,
"currentPage": 1
}
Retrieve a specific hunt with full details.
GET /api/hunts/:id
Create a new rock hunt.
POST /api/hunts
Authorization: Bearer <token>
Content-Type: application/json
{
"title": "Downtown Rock Hunt",
"description": "Find 5 hidden rocks in downtown area!",
"rocks": [
{
"rock": "rock_id_1",
"hint": "Look near the fountain in the park",
"order": 1
},
{
"rock": "rock_id_2",
"hint": "Hidden behind the library",
"order": 2
}
],
"difficulty": "medium",
"startDate": "2025-10-20T00:00:00.000Z",
"endDate": "2025-10-27T23:59:59.999Z",
"maxParticipants": 100
}
Update a hunt (only by creator).
PUT /api/hunts/:id
Authorization: Bearer <token>
Content-Type: application/json
{
"title": "Updated Title",
"description": "Updated description",
"difficulty": "hard",
"isActive": false
}
Delete a hunt (only by creator).
DELETE /api/hunts/:id
Authorization: Bearer <token>
Join an active hunt.
POST /api/hunts/:id/join
Authorization: Bearer <token>
Response:
{
"message": "Joined hunt successfully",
"hunt": { /* hunt object with your participation */ }
}
Mark a rock as found in a hunt you’re participating in.
POST /api/hunts/:huntId/rocks/:rockId/found
Authorization: Bearer <token>
Response:
{
"message": "Rock marked as found",
"completed": false,
"progress": {
"found": 3,
"total": 5
}
}
When all rocks are found:
{
"message": "Rock marked as found",
"completed": true,
"progress": {
"found": 5,
"total": 5
}
}
Get your progress in all hunts you’re participating in.
GET /api/hunts/my/progress
Authorization: Bearer <token>
Response:
{
"hunts": [
{
"hunt": {
"_id": "hunt_id",
"title": "Downtown Rock Hunt",
"description": "Find 5 hidden rocks",
"difficulty": "medium",
"totalRocks": 5,
"creator": { /* user object */ }
},
"progress": {
"rocksFound": 3,
"totalRocks": 5,
"completed": false,
"completedAt": null,
"startedAt": "2025-10-20T10:00:00Z"
}
}
]
}
Retrieve all available achievements with optional filters.
GET /api/achievements?type=rocks&rarity=rare
Query Parameters:
type (optional): Filter by type (rocks, hunts, social, geology, special)rarity (optional): Filter by rarity (common, rare, epic, legendary)Response:
{
"achievements": [
{
"_id": "achievement_id",
"name": "First Rock",
"description": "Share your first rock photo",
"icon": "🪨",
"type": "rocks",
"criteria": {
"type": "count",
"target": 1
},
"rarity": "common"
}
]
}
Retrieve a specific achievement.
GET /api/achievements/:id
Create a new achievement.
POST /api/achievements
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Rock Collector",
"description": "Share 10 rock photos",
"icon": "📸",
"type": "rocks",
"criteria": {
"type": "count",
"target": 10
},
"rarity": "rare"
}
Award an achievement to the authenticated user.
POST /api/achievements/award
Authorization: Bearer <token>
Content-Type: application/json
{
"achievementId": "achievement_id"
}
Get achievements earned by a specific user.
GET /api/achievements/user/:userId
Get your own achievements:
GET /api/achievements/user/me/achievements
Authorization: Bearer <token>
All error responses follow this format:
{
"error": "Error message describing what went wrong"
}
401 Unauthorized:
{
"error": "Authentication required"
}
404 Not Found:
{
"error": "Resource not found"
}
403 Forbidden:
{
"error": "Not authorized"
}
400 Bad Request:
{
"error": "Invalid request parameters"
}
Currently, there are no rate limits implemented. In production, rate limiting should be added to prevent abuse.
The API supports Cross-Origin Resource Sharing (CORS) for web clients.
All endpoints validate input data. Invalid data will return a 400 Bad Request with details about validation errors.
curl -X POST http://localhost:3000/api/users/register \
-H "Content-Type: application/json" \
-d '{"username":"rockfan","email":"fan@rocks.com","password":"rocks123"}'
curl -X POST http://localhost:3000/api/rocks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"title":"Granite Boulder",
"description":"Huge granite boulder",
"photo":"https://example.com/granite.jpg",
"location":{"type":"Point","coordinates":[-122.4,37.7],"address":"SF"},
"rockType":"igneous",
"tags":["granite","big"],
"isPublic":true
}'
curl -X POST http://localhost:3000/api/hunts/HUNT_ID/join \
-H "Authorization: Bearer YOUR_TOKEN"
curl -X POST http://localhost:3000/api/hunts/HUNT_ID/rocks/ROCK_ID/found \
-H "Authorization: Bearer YOUR_TOKEN"