This document describes the new API endpoints for managing user squads, transfers, and chips in the FPL Predictor application.
These endpoints enable:
- Squad Management: Track user's current squad with purchase/selling prices
- Transfer System: Make transfers with proper validation and points deduction
- Chip System: Manage FPL chips (Wildcard, Free Hit, Bench Boost, Triple Captain)
- Historical Tracking: Store squad snapshots for each gameweek
All endpoints are prefixed with: http://localhost:5000/api/
All endpoints require authentication via JWT token in the Authorization header:
Authorization: Bearer <jwt_token>
Fixture events are read exclusively from the FPL fixture stats. Clients should
use the events array returned by /api/fixtures; each row belongs to either
the home or away team through its teamId.
GET /api/fixtures?gameweek=1
Each fixture includes an events array containing normalized goal, assist,
yellow-card, and red-card rows from FPL. The fixture display is two-sided:
Home | Away, using team_h_name and team_a_name.
POST /api/squad/initialize
Initialize a user's squad from their FPL team data. This should be called once when a user first connects their FPL account.
Request Body:
{
"userId": "mongodb_user_id",
"entryId": 123456,
"gameweek": 1
}Response:
{
"message": "Squad initialized successfully",
"squad": {
"_id": "squad_id",
"userId": "user_id",
"gameweek": 1,
"players": [...],
"bank": 5,
"squadValue": 1000,
"freeTransfers": 1,
"transfersMadeThisWeek": 0,
"pointsDeducted": 0,
"activeChip": null
},
"chips": {...}
}GET /api/squad/:userId
Get user's current squad with calculated selling prices.
Response:
{
"_id": "squad_id",
"userId": "user_id",
"gameweek": 5,
"players": [
{
"playerId": 1,
"position": 1,
"purchasePrice": 75,
"currentPrice": 78,
"sellingPrice": 76,
"profit": 3,
"isCaptain": false,
"isViceCaptain": false,
"multiplier": 1
}
],
"bank": 5,
"squadValue": 1005,
"totalSellingValue": 996,
"totalCurrentValue": 1003,
"freeTransfers": 1,
"transfersMadeThisWeek": 0,
"transferCost": 0,
"pointsDeducted": 0,
"activeChip": null
}Player Price Calculation:
purchasePrice: Price when player was bought (£0.1m units)currentPrice: Current market pricesellingPrice: Price when selling =purchasePrice + floor((currentPrice - purchasePrice) / 2)profit:currentPrice - purchasePrice
GET /api/squad/history/:userId/:gameweek
Get squad snapshot for a specific gameweek.
Response:
{
"_id": "history_id",
"userId": "user_id",
"gameweek": 3,
"players": [...],
"bank": 5,
"squadValue": 1000,
"freeTransfers": 1,
"transfersMadeThisWeek": 2,
"pointsDeducted": 4,
"activeChip": "wildcard",
"pointsScored": 65,
"overallRank": 123456
}GET /api/squad/history/:userId
Get all squad snapshots for a user, sorted by gameweek.
Response:
[
{
"gameweek": 1,
"players": [...],
"pointsScored": 58,
...
},
{
"gameweek": 2,
"players": [...],
"pointsScored": 62,
...
}
]POST /api/squad/update-gameweek
Called at the start of each new gameweek to:
- Reset transfer counters
- Update free transfers (carry over if unused, max 2)
- Revert Free Hit changes
- Clear active chip
Request Body:
{
"userId": "user_id",
"newGameweek": 6
}Response:
{
"message": "Squad updated for new gameweek",
"squad": {...}
}POST /api/transfers
Make a player transfer with automatic validation and cost calculation.
Request Body:
{
"userId": "user_id",
"playerOutId": 123,
"playerInId": 456,
"gameweek": 5
}Validation:
- Players must be same position type
- Sufficient funds available
- Transfer cost calculated based on free transfers and active chips
Response:
{
"message": "Transfer completed successfully",
"transfer": {
"_id": "transfer_id",
"userId": "user_id",
"gameweek": 5,
"playerIn": {
"playerId": 456,
"price": 95
},
"playerOut": {
"playerId": 123,
"purchasePrice": 90,
"sellingPrice": 92
},
"isFree": true,
"pointsCost": 0,
"chipActive": null
},
"squad": {
"players": [...],
"bank": 3,
"squadValue": 1003,
"transfersMadeThisWeek": 1,
"freeTransfers": 1,
"pointsDeducted": 0
},
"playerIn": {
"id": 456,
"name": "Mohamed Salah",
"cost": 95
},
"playerOut": {
"id": 123,
"name": "Bruno Fernandes",
"sellingPrice": 92
}
}Transfer Cost Logic:
- Free if
transfersMadeThisWeek < freeTransfers - Free if Wildcard or Free Hit active
- Otherwise: 4 points per transfer
GET /api/transfers/history/:userId?gameweek=5&limit=10
Get user's transfer history with optional filters.
Query Parameters:
gameweek(optional): Filter by specific gameweeklimit(optional): Limit number of results
Response:
[
{
"_id": "transfer_id",
"userId": "user_id",
"gameweek": 5,
"playerIn": {
"playerId": 456,
"price": 95,
"name": "Mohamed Salah",
"webName": "Salah"
},
"playerOut": {
"playerId": 123,
"purchasePrice": 90,
"sellingPrice": 92,
"name": "Bruno Fernandes",
"webName": "Bruno F."
},
"isFree": false,
"pointsCost": 4,
"chipActive": null,
"createdAt": "2024-01-15T10:30:00Z"
}
]GET /api/transfers/summary/:userId/:gameweek
Get transfer summary for a specific gameweek.
Response:
{
"gameweek": 5,
"totalTransfers": 3,
"freeTransfers": 1,
"paidTransfers": 2,
"totalPointsCost": 8,
"transfers": [...]
}GET /api/chips/:userId?gameweek=5
Get chips available for a specific gameweek.
Query Parameters:
gameweek(required): Gameweek to check availability
Response:
{
"userId": "user_id",
"gameweek": 5,
"availableChips": [
{
"id": "bench_boost_1",
"type": "bench_boost",
"number": "1",
"description": "Bench Boost",
"effect": "Points scored by your bench players are included in your total"
},
{
"id": "triple_captain_1",
"type": "triple_captain",
"number": "1",
"description": "Triple Captain",
"effect": "Your captain points are tripled instead of doubled"
},
{
"id": "wildcard_1",
"type": "wildcard",
"number": "1",
"description": "Wildcard",
"effect": "All transfers in the Gameweek are free of charge"
},
{
"id": "free_hit_1",
"type": "free_hit",
"number": "1",
"description": "Free Hit",
"effect": "Make unlimited free transfers for a single Gameweek. Squad returns to previous state next gameweek"
}
],
"allChips": {
"benchBoost1": {
"available": true,
"usedInGameweek": null,
"availableFrom": 1,
"availableUntil": 19
},
...
}
}Chip Availability Windows:
- Bench Boost 1: GW1-19
- Bench Boost 2: GW20-38
- Triple Captain 1: GW1-19
- Triple Captain 2: GW20-38
- Free Hit 1: GW2-19 (cannot use in consecutive gameweeks)
- Free Hit 2: GW20-38 (cannot use in consecutive gameweeks)
- Wildcard 1: GW2-19
- Wildcard 2: GW20-38
POST /api/chips/activate
Activate a chip for the current gameweek.
Request Body:
{
"userId": "user_id",
"chipName": "wildcard_1",
"gameweek": 5
}Validation:
- Chip must be available
- No other chip active this gameweek
- Gameweek must be within chip's availability window
- Free Hit cannot be used in consecutive gameweeks
Response:
{
"message": "Chip wildcard activated successfully",
"chip": "wildcard_1",
"activeChip": "wildcard",
"squad": {
"gameweek": 5,
"activeChip": "wildcard",
"transfersMadeThisWeek": 0,
"pointsDeducted": 0
}
}Effects:
- Wildcard: Resets transfer counters, all transfers free this GW
- Free Hit: Saves current squad, all transfers free this GW, reverts next GW
- Triple Captain: Sets captain multiplier to 3
- Bench Boost: No automatic effect (handled during points calculation)
POST /api/chips/cancel
Cancel an active chip before the gameweek deadline.
Request Body:
{
"userId": "user_id"
}Restrictions:
- Cannot cancel: Wildcard, Free Hit (once confirmed)
- Can cancel: Bench Boost, Triple Captain (before deadline)
Response:
{
"message": "Chip triple_captain cancelled successfully",
"squad": {
"gameweek": 5,
"activeChip": null
}
}All endpoints return standard error responses:
{
"error": "Error message",
"details": "Additional error details"
}Common HTTP Status Codes:
200- Success400- Bad request (validation error)401- Unauthorized (invalid/missing token)404- Not found (squad/user not found)500- Server error
// 1. Initialize squad
const initResponse = await fetch('/api/squad/initialize', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: 'user123',
entryId: 123456,
gameweek: 1
})
});
// 2. Make a transfer
const transferResponse = await fetch('/api/transfers', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: 'user123',
playerOutId: 123,
playerInId: 456,
gameweek: 1
})
});// 1. Activate wildcard
await fetch('/api/chips/activate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: 'user123',
chipName: 'wildcard_1',
gameweek: 5
})
});
// 2. Make multiple free transfers
await fetch('/api/transfers', {
method: 'POST',
body: JSON.stringify({ userId, playerOutId: 1, playerInId: 10, gameweek: 5 })
});
await fetch('/api/transfers', {
method: 'POST',
body: JSON.stringify({ userId, playerOutId: 2, playerInId: 20, gameweek: 5 })
});
// All transfers are free due to wildcardUser Action → API Endpoint → Controller
↓
Validate Request
↓
Update Database
↓
Update Squad State
↓
Create History Record
↓
Return Response
Stores current squad state for each user.
Stores historical snapshots of squads for each gameweek.
Records all player transfers made.
Tracks chip availability and usage for each user.
- Prices: All prices are stored in £0.1m units (e.g., 75 = £7.5m)
- Selling Price: Always rounds down to nearest £0.1m
- Free Transfers: Max 2 can be banked
- Transfer Cost: 4 points per extra transfer
- Chips: One-time use per gameweek, specific availability windows
- Free Hit: Squad automatically reverts next gameweek
- Saved Transfers: Retained when using Wildcard or Free Hit