Pagination et débit
Les listes se parcourent par curseur, et chaque réponse annonce le quota de requêtes qu’il vous reste. Deux mécanismes simples, à intégrer dès le premier jour.
Parcourir une liste
Une liste renvoie { data, next_cursor }, du plus récent au plus ancien. Pour la page suivante, repassez next_cursor dans le paramètre cursor ; null signale la dernière page. limit fixe la taille des pages, de 1 à 100, 20 par défaut.
- Des pages stables. Le curseur désigne un point précis de la liste, pas un numéro de page : un paiement qui arrive pendant votre parcours ne décale rien, vous ne lisez jamais deux fois le même élément.
- Un curseur opaque. Ne le construisez pas et ne le décodez pas : son format peut changer. Un curseur illisible répond
400 validation_error. - Le chemin signé ne contient pas la query string. On signe
/v1/charges, pas/v1/charges?limit=100.
// pages.mjs : node pages.mjs
import { createHmac } from "node:crypto";
const KEY = "ak_sandbox";
const SECRET = "sk_sandbox_demo";
async function page(cursor) {
const path = "/v1/charges"; // signé SANS la query string
const query = new URLSearchParams({ limit: "100", ...(cursor ? { cursor } : {}) });
const ts = String(Math.floor(Date.now() / 1000));
const sig = createHmac("sha256", SECRET).update(`${ts}.GET.${path}..`).digest("hex");
const res = await fetch(`https://sandbox.inopas.net${path}?${query}`, {
headers: { "X-Inopas-Key": KEY, "X-Inopas-Timestamp": ts, "X-Inopas-Signature": `sha256=${sig}` },
});
if (res.status === 429) {
await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After") ?? 1) * 1000));
return page(cursor);
}
return res.json();
}
let cursor = null;
let total = 0;
do {
const { data, next_cursor } = await page(cursor);
total += data.length;
cursor = next_cursor; // null : dernière page
} while (cursor && total < 500); // borne de sécurité pour l'exemple
console.log(total, "éléments lus");# pages.py : python3 pages.py
import hashlib, hmac, json, time, urllib.error, urllib.parse, urllib.request
KEY = "ak_sandbox"
SECRET = b"sk_sandbox_demo"
def page(cursor=None):
path = "/v1/charges" # signé SANS la query string
query = urllib.parse.urlencode({"limit": 100, **({"cursor": cursor} if cursor else {})})
ts = str(int(time.time()))
sig = hmac.new(SECRET, f"{ts}.GET.{path}..".encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(f"https://sandbox.inopas.net{path}?{query}", headers={
"X-Inopas-Key": KEY, "X-Inopas-Timestamp": ts, "X-Inopas-Signature": "sha256=" + sig,
})
try:
with urllib.request.urlopen(req) as resp:
return json.load(resp)
except urllib.error.HTTPError as e:
if e.code != 429:
raise
time.sleep(int(e.headers.get("Retry-After") or 1))
return page(cursor)
cursor, total = None, 0
while True:
reponse = page(cursor)
total += len(reponse["data"])
cursor = reponse["next_cursor"] # None : dernière page
if not cursor or total >= 500: # borne de sécurité pour l'exemple
break
print(total, "éléments lus")La limite de débit
Le quota se compte par compte marchand, sur une fenêtre d’une minute. Chaque réponse l’annonce dans trois en-têtes :
RateLimit-Limit- Requêtes permises par fenêtre.
RateLimit-Remaining- Requêtes restantes dans la fenêtre en cours.
RateLimit-Reset- Secondes avant la prochaine fenêtre.
Au-delà, l’API répond 429 rate_limited avec un en-tête Retry-After : attendez ce nombre de secondes, puis reprenez. Lisez le quota dans les en-têtes plutôt que de l’écrire en dur dans votre code : il peut évoluer.
Dans le Sandbox public, le quota est partagé par tous les visiteurs : un 429 peut y venir de l’activité des autres.