Démarrer
-
Gardez votre jeton côté serveur
Serv3r.net vous remet un jeton personnel. Stockez-le dans une variable d'environnement de votre serveur, par exemple
SERV3R_TOKEN. Ne le placez jamais dans du code exécuté par un navigateur ou une application mobile : n'importe qui pourrait le lire. -
Appelez le catalogue
Envoyez une requête
GETen HTTPS, avec le jeton dans l'en-têteAuthorization.curl -s https://api.serv3r.net/v1/catalog \ -H "Authorization: Bearer $SERV3R_TOKEN"Toujours https://Une requête en
http://est redirigée vers HTTPS, mais le jeton a déjà circulé en clair entre-temps. Si cela arrive, demandez-en un nouveau. -
Lisez la réponse
Vous recevez un objet JSON. La liste des serveurs est dans
products. Voici un extrait d'une réponse réelle, avec un serveur épuisé et un serveur en stock :{ "generated_at": "2026-09-13T08:35:46Z", "stale": false, "currency": "CAD", "notice": "Quantités indicatives, non réservées.", "products": [ { "id": 157, "name": "Amd Ryzen 3950x", "description": "Cpu:Ryzen 3950x<br>Number of cores:16c/32t<br>Frequency:3,5 GHz/4,7 GHz<br>Ram:128GB (DDR4)<br>Storage:2x 2TB NVME SSD<br>Network Port Speed:1Gbps<br>Bandwidth:Unmetered<br>IP address:1<br><ul></ul>", "category": { "id": 53, "name": "Amd Ryzen" }, "available": false, "stock": { "tracked": true, "qty": 0 }, "billing": "Regular", "prices": [ { "cycle": "monthly", "price": "159.00", "setup": "0.00" } ], "order_url": "https://client.serv3r.net/index.php?cmd=cart&action=add&id=157&cat_id=53" }, { "id": 158, "name": "Amd Ryzen 5600x", "description": "Cpu:Ryzen 5600x<br>Number of cores:6c/12t<br>Frequency:3,7 GHz/4,6 GHz<br>Ram:32GB (DDR4)<br>Storage:2x 2TB NVME SSD<br>Network Port Speed:1Gbps<br>Bandwidth:Unmetered<br>IP address:1<br><ul></ul>", "category": { "id": 53, "name": "Amd Ryzen" }, "available": true, "stock": { "tracked": true, "qty": 2 }, "billing": "Regular", "prices": [ { "cycle": "monthly", "price": "129.00", "setup": "0.00" } ], "order_url": "https://client.serv3r.net/index.php?cmd=cart&action=add&id=158&cat_id=53" } ] }
La réponse
Enveloppe
| Champ | Type | Contenu |
|---|---|---|
generated_at | texte, ISO 8601 UTC | Moment où les données ont été lues. |
stale | booléen | true si nos données n'ont pas pu être rafraîchies : vous recevez alors celles de generated_at. |
currency | texte, ISO 4217 | Devise de tous les prix de la réponse. |
notice | texte | Rappel : les quantités sont indicatives et ne réservent rien. |
products | tableau | Les serveurs, triés par gamme puis par nom. |
Chaque serveur
| Champ | Type | Contenu |
|---|---|---|
id | entier | Identifiant du serveur, stable dans le temps. |
name | texte | Nom commercial. |
description | texte, HTML simple | Caractéristiques techniques. Voir Caractéristiques. |
category.idcategory.name | entier texte | Gamme du serveur, par exemple 52 « Intel Xeon » ou 53 « Amd Ryzen ». |
available | booléen | Le serveur peut être commandé maintenant. |
stock.tracked | booléen | Le stock de ce serveur est suivi. |
stock.qty | entier ou null | Quantité disponible, ou null si le stock n'est pas suivi. |
billing | texte | Mode de facturation. Regular : facturation récurrente. |
prices | tableau | Un tarif par durée d'engagement. Voir Prix. |
order_url | texte ou null | Page de commande du serveur. Voir Lien de commande. |
Stock et disponibilité
Pour savoir si un serveur peut être commandé, lisez available. Pour afficher une quantité, lisez stock :
| stock.tracked | stock.qty | available | À afficher |
|---|---|---|---|
true | 2 | true | 2 en stock |
true | 0 | false | Épuisé |
false | null | true | Disponible |
- Les serveurs épuisés restent dans la liste, avec
availableàfalse. - Les quantités sont mises à jour en continu et vous parviennent en 30 secondes au plus.
- Elles ne réservent rien : un serveur peut partir entre votre lecture et la commande de votre client.
Prix
prices contient un tarif par durée d'engagement proposée. Aujourd'hui, tous les serveurs sont proposés au mois, sans frais d'installation ; d'autres durées peuvent apparaître.
price: montant pour une période.setup: frais d'installation, payés une seule fois.- Les deux sont des textes à deux décimales avec un point, par exemple
"129.00". Pour vos calculs, convertissez-les en type décimal ou en cents plutôt qu'en nombre à virgule flottante.
Valeurs possibles de cycle
hourlyà l'heuredailyau jourweeklyà la semainemonthlyau moisquarterly3 moissemiannually6 moisannually1 anbiennially2 anstriennially3 ansquadrennially4 ansquinquennially5 ansoncepaiement uniqueCaractéristiques
description contient les caractéristiques techniques, une par ligne. Les lignes sont séparées par <br> et suivent la forme Caractéristique:Valeur. Traitez ce champ comme du texte : ne l'insérez pas tel quel dans vos pages.
const specs = product.description
.split("<br>")
.map((line) => line.replace(/<[^>]*>/g, "").trim())
.filter((line) => line.includes(":"))
.map((line) => {
const i = line.indexOf(":");
return [line.slice(0, i), line.slice(i + 1)];
});
Résultat pour l'Amd Ryzen 5600x :
| Caractéristique | Valeur |
|---|---|
| Cpu | Ryzen 5600x |
| Number of cores | 6c/12t |
| Frequency | 3,7 GHz/4,6 GHz |
| Ram | 32GB (DDR4) |
| Storage | 2x 2TB NVME SSD |
| Network Port Speed | 1Gbps |
| Bandwidth | Unmetered |
| IP address | 1 |
Lien de commande
order_url ouvre directement la page de commande du serveur dans l'espace client Serv3r.net, où votre client choisit ses options et paie.
Pour un serveur épuisé, le lien reste valide, mais la page affiche « Rupture de stock ». Masquez votre bouton de commande quand available vaut false.
Codes de réponse
Fiez-vous au code HTTP. Seules les réponses 200, 401 et 503 contiennent du JSON.
| Code | Signification | Que faire |
|---|---|---|
| 200 | Le catalogue est dans la réponse. | Lire products. |
| 304 | Rien n'a changé depuis la version indiquée dans If-None-Match. Réponse vide. | Réutiliser votre copie. |
| 401 | Jeton absent, invalide ou révoqué. Corps : {"error":"unauthorized"}. | Vérifier l'en-tête Authorization. Si le jeton est bon, contacter Serv3r.net. |
| 403 | Méthode autre que GET ou HEAD. | Utiliser GET. |
| 429 | Plus de 30 requêtes par minute depuis la même adresse IP. | Espacer les appels : un toutes les 30 secondes suffit. |
| 503 | Catalogue momentanément indisponible. Corps : {"error":"catalog_unavailable"}, en-tête Retry-After: 30. | Réessayer après 30 secondes en gardant votre dernière copie. |
Bonnes pratiques
- Interrogez l'API depuis votre serveur. Gardez le catalogue en cache chez vous et construisez vos pages à partir de ce cache.
- Un appel toutes les 30 à 60 secondes suffit. Nos données ne se rafraîchissent pas plus vite.
- Renvoyez l'ETag. Placez la valeur de l'en-tête
ETagreçu dansIf-None-Match: si rien n'a changé, vous recevez un 304 sans contenu. - Surveillez
stale. S'il vauttrue, les données datent degenerated_at: affichez-les, sans promettre de disponibilité. - Gardez votre dernière copie en cas d'erreur (503, coupure réseau) et réessayez plus tard.
- Tolérez les nouveaux champs. D'autres informations peuvent s'ajouter à la réponse : ne rejetez pas un objet qui en contient.
let etag = null;
let catalog = null;
async function refresh() {
const headers = { Authorization: `Bearer ${process.env.SERV3R_TOKEN}` };
if (etag) headers["If-None-Match"] = etag;
try {
const res = await fetch("https://api.serv3r.net/v1/catalog", {
headers,
signal: AbortSignal.timeout(10_000),
});
if (res.status === 200) {
etag = res.headers.get("ETag");
catalog = await res.json();
}
// 304 : rien n'a changé. Autre code : on garde la dernière copie.
} catch {
// Réseau indisponible : on garde la dernière copie.
}
return catalog;
}
await refresh();
setInterval(refresh, 60_000);
Exemples de code
Chaque exemple lit le catalogue et affiche, pour chaque serveur, son prix mensuel et son état.
<?php
$ch = curl_init('https://api.serv3r.net/v1/catalog');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SERV3R_TOKEN')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($body === false || $status !== 200) {
throw new RuntimeException("Catalogue indisponible (HTTP $status)");
}
$catalog = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
foreach ($catalog['products'] as $p) {
$mensuel = array_column($p['prices'], 'price', 'cycle')['monthly'] ?? '—';
$etat = match (true) {
!$p['available'] => 'épuisé',
$p['stock']['tracked'] => $p['stock']['qty'] . ' en stock',
default => 'disponible',
};
echo "{$p['name']} — {$mensuel} {$catalog['currency']}/mois — {$etat}\n";
}
const res = await fetch("https://api.serv3r.net/v1/catalog", {
headers: { Authorization: `Bearer ${process.env.SERV3R_TOKEN}` },
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) throw new Error(`Catalogue indisponible (HTTP ${res.status})`);
const catalog = await res.json();
for (const p of catalog.products) {
const mensuel = p.prices.find((x) => x.cycle === "monthly")?.price ?? "—";
const etat = !p.available
? "épuisé"
: p.stock.tracked ? `${p.stock.qty} en stock` : "disponible";
console.log(`${p.name} — ${mensuel} ${catalog.currency}/mois — ${etat}`);
}
import json
import os
import urllib.request
req = urllib.request.Request(
"https://api.serv3r.net/v1/catalog",
headers={"Authorization": f"Bearer {os.environ['SERV3R_TOKEN']}"},
)
# urlopen lève HTTPError pour les codes 401, 429, 503…
with urllib.request.urlopen(req, timeout=10) as res:
catalog = json.load(res)
for p in catalog["products"]:
mensuel = next((x["price"] for x in p["prices"] if x["cycle"] == "monthly"), "—")
if not p["available"]:
etat = "épuisé"
elif p["stock"]["tracked"]:
etat = f"{p['stock']['qty']} en stock"
else:
etat = "disponible"
print(f"{p['name']} — {mensuel} {catalog['currency']}/mois — {etat}")
# Réponse brute, JSON sur une seule ligne
curl -s https://api.serv3r.net/v1/catalog \
-H "Authorization: Bearer $SERV3R_TOKEN"
# Avec les en-têtes, pour repérer ETag et Cache-Control
curl -si https://api.serv3r.net/v1/catalog \
-H "Authorization: Bearer $SERV3R_TOKEN"
# Avec l'ETag reçu : réponse 304 si rien n'a changé
curl -si https://api.serv3r.net/v1/catalog \
-H "Authorization: Bearer $SERV3R_TOKEN" \
-H 'If-None-Match: "valeur-de-l-etag-recu"'
Sortie des exemples PHP, JavaScript et Python le 13 septembre 2026 :
2x Intel Xeon E5-2697 v4 — 129.00 CAD/mois — 1 en stock
Intel Xeon W-1290P — 159.00 CAD/mois — 1 en stock
Amd Ryzen 3950x — 159.00 CAD/mois — épuisé
Amd Ryzen 5600x — 129.00 CAD/mois — 2 en stock
Amd Ryzen 9950x — 259.00 CAD/mois — épuisé