Intégrez les résultats, calendriers et classements de vos clubs dans vos sites et applications.
L'API est en lecture seule et renvoie du JSON. Elle expose uniquement les équipes des clubs que vous suivez dans votre espace (« Mes clubs »), ainsi que leurs matchs, compétitions, classements et effectifs.
Authorization de chaque requête.curl https://www.sport-manager.fr/api/me/teams \
-H "Authorization: Bearer VOTRE_CLE_API" \
-H "Accept: application/json"
| Forfait | API | Quota | Webhooks |
|---|---|---|---|
| Gratuit | Non | — | — |
| Pro | Oui | 60 requêtes / minute | — |
| Business | Oui | 300 requêtes / minute | Jusqu'à 5 |
Au-delà du quota, l'API répond 429 Too Many Requests avec l'en-tête Retry-After. Le nombre d'appels par clé est affiché dans « Mon compte ».
Toutes les URL sont relatives à https://www.sport-manager.fr/api. Les propriétés sont en camelCase et les valeurs nulles sont conservées.
| Méthode et chemin | Description | Paramètres |
|---|---|---|
GET /me/teams | Équipes des clubs suivis | — |
GET /me/teams/{id} | Détail d'une équipe | — |
GET /me/teams/{teamId}/results | Derniers résultats de l'équipe | season, limit (100 max) |
GET /me/teams/{teamId}/fixtures | Prochains matchs | limit (100 max) |
GET /me/teams/{teamId}/standings | Classements des compétitions de l'équipe | season |
GET /me/teams/{teamId}/squad | Effectif (contrats actifs) | season |
GET /me/games/{id} | Détail d'un match | — |
GET /me/standings/{competitionId} | Classement d'une compétition | — |
GET /me/competitions/{id}/calendar | Calendrier complet (journées et matchs) | — |
GET /me/players/{id} | Fiche joueur (statistiques par compétition, sélections) | — |
GET /me/teams/{teamId}/player-stats Business | Statistiques des joueurs de l'équipe : apparitions, titularisations, buts, penalties, passes décisives, contre-son-camp, cartons | season |
GET /me/competitions/{id}/scorers Business | Classement des buteurs de la compétition (toutes équipes) | limit (20 par défaut, 100 max) |
GET /me/games/{id}/sheet Business | Feuille de match : compositions des deux équipes et événements (buts avec passeur, cartons, remplacements) | — |
Les statistiques joueurs sont calculées à partir des feuilles de match des matchs terminés : elles ne concernent que les compétitions dont les compositions et événements sont saisis.
season accepte current (par défaut), all ou l'identifiant d'une saison (les saisons passées nécessitent un forfait incluant l'historique complet). Chaque ligne de classement et chaque camp d'un match portent isMine quand il s'agit d'une équipe d'un club suivi.
GET /api/me/standings/42
{
"id": 42,
"competition": { "id": 42, "name": "Régional 1", "season": "2026/27", "format": "championship" },
"rows": [
{
"position": 1, "teamId": 7, "clubId": 3, "name": "FC Exemple",
"played": 12, "wins": 9, "draws": 1, "losses": 2,
"goalsFor": 28, "goalsAgainst": 11, "goalDifference": 17,
"points": 28, "zone": { "type": "promotion", "label": "Montée", "color": "#16A34A" },
"isMine": true
}
]
}
Les erreurs suivent le format application/problem+json (RFC 7807) : status, title, detail.
| Code | Signification |
|---|---|
| 401 | Clé absente ou invalide. |
| 403 | Forfait sans accès à l'API ou à la ressource demandée (statistiques joueurs : Business ; saisons passées : historique complet), ou clé sans la capacité de lecture. |
| 404 | Ressource inexistante ou sans lien avec vos clubs suivis (son existence n'est pas révélée). |
| 422 | Paramètre de requête invalide. |
| 429 | Quota par minute dépassé. |
Plutôt que d'interroger l'API en boucle, recevez une requête POST en JSON dès qu'un événement concerne l'un de vos clubs.
Déclarez vos URL (HTTPS uniquement) dans Mon compte → Webhooks.
| Événement | Déclenché quand… |
|---|---|
game.finished |
un résultat est publié ou corrigé — Résultat publié |
game.rescheduled |
la date ou l'heure d'un match change, ou le match est reporté (statut postponed) — Date ou horaire modifié |
standing.updated |
un classement change (positions, points, buts) — Classement mis à jour |
ping | vous cliquez sur « tester » dans Mon compte |
X-SportManager-Event | Nom de l'événement |
X-SportManager-Delivery | Identifiant unique de la notification (identique en cas de nouvel essai : utilisez-le pour dédoublonner) |
X-SportManager-Timestamp | Horodatage Unix de l'envoi |
X-SportManager-Signature | sha256= suivi du HMAC SHA-256 de timestamp + "." + corps avec votre secret |
Répondez par un code 2xx en moins de 10 secondes. Sinon, deux nouveaux essais ont lieu (après 1 puis 5 minutes). Après 10 échecs consécutifs, le webhook est désactivé. L'historique des envois est consultable dans Mon compte.
game.finished{
"event": "game.finished",
"id": "9b1f0c1e-3a57-4d8e-9d0a-2f6c7e1b5a44",
"createdAt": "2026-09-27T17:05:12+02:00",
"data": {
"game": {
"id": 1234, "status": "finished", "kickoffAt": "2026-09-27T15:00:00+02:00", "day": 6,
"venue": "Stade municipal",
"competition": { "id": 42, "name": "Régional 1", "season": "2026/27" },
"home": { "teamId": 7, "clubId": 3, "nationalTeamId": null, "name": "FC Exemple", "score": 2, "outcome": "W", "forfeit": false },
"away": { "teamId": 9, "clubId": 5, "nationalTeamId": null, "name": "AS Voisine", "score": 1, "outcome": "L", "forfeit": false }
},
"followedClubIds": [3]
}
}
// PHP
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_SPORTMANAGER_TIMESTAMP'];
$expected = 'sha256='.hash_hmac('sha256', $timestamp.'.'.$body, getenv('SPORTMANAGER_WEBHOOK_SECRET'));
if (! hash_equals($expected, $_SERVER['HTTP_X_SPORTMANAGER_SIGNATURE'] ?? '')
|| abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit;
}
// Node.js (Express, avec express.raw({ type: 'application/json' }))
const crypto = require('crypto');
app.post('/webhooks/sport-manager', (req, res) => {
const timestamp = req.get('X-SportManager-Timestamp');
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.SPORTMANAGER_WEBHOOK_SECRET)
.update(timestamp + '.' + req.body)
.digest('hex');
const received = req.get('X-SportManager-Signature') || '';
if (expected.length !== received.length
|| !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
// … traiter event.data
res.sendStatus(204);
});
Chaque club suivi dispose d'une URL d'abonnement au format iCalendar, à ajouter dans Google Agenda, Outlook ou Apple Calendrier (page Widgets du club, encadré « Calendrier à synchroniser »). Le flux contient jusqu'à 500 matchs : la saison en cours avec le forfait Gratuit, sinon les 12 derniers mois et tous les matchs à venir. Les scores apparaissent dans le titre dès qu'ils sont publiés.
https://www.sport-manager.fr/calendar/VOTRE_CLE_WIDGETS/slug-du-club.ics
Classement, calendrier, fiche club, dernier et prochain match : copiez le code depuis la page Widgets de votre club.
<script src="https://www.sport-manager.fr/widget.js" async></script>
<sm-standings account="VOTRE_CLE_WIDGETS" club="slug-du-club"
competition="slug-de-la-competition" theme="auto" rows="10" show-form="true"></sm-standings>
theme="auto".
Le forfait Business permet d'y injecter votre propre CSS (marque blanche).
Trois endpoints réservés au forfait Business : /me/teams/{teamId}/player-stats, /me/competitions/{id}/scorers
et /me/games/{id}/sheet. Les autres forfaits reçoivent une erreur 403 explicite. Par ailleurs, les
données des saisons passées (season=all ou identifiant) renvoient 403 pour un forfait sans historique complet.
game.rescheduled
Nouvel événement envoyé quand la date ou l'heure d'un match change, ou quand le match est reporté
(status vaut alors postponed). La charge utile est identique à celle de game.finished.
Cochez-le dans Mon compte → Webhooks pour le recevoir.
Les dates (kickoffAt, dates de journée, createdAt des webhooks) sont exprimées à l'heure de Paris
avec leur décalage réel : 2026-09-27T15:00:00+02:00 en heure d'été, +01:00 en heure d'hiver.
Auparavant, la même heure était annoncée avec +00:00 : un client qui convertissait les dates dans un autre fuseau
obtenait un décalage de 1 à 2 heures. L'heure affichée ne change pas ; si vous aviez compensé ce décalage
de votre côté, retirez cette correction.