Développeurs

Démarrer avec l'API

L'API O'Gérant vous donne, en lecture, ce que vous voyez dans l'application : les consultations publiées, vos scores de correspondance, vos soumissions et vos marchés. De quoi alimenter votre ERP, votre tableau de bord interne ou votre propre flux de veille, sans copier-coller.

À quoi ça sert

Trois usages couvrent presque tous les cas :

Répliquer le flux d'appels d'offres
Vous maintenez déjà un outil interne ? /api/v1/tenders vous rend les consultations filtrées (secteur, acheteur, montant, date limite) que vous injectez chez vous.
Récupérer vos correspondances
/api/v1/matches renvoie les scores calculés pour votre organisation : le tri déjà fait, sans refaire le travail d'analyse de votre côté.
Consolider vos affaires
/api/v1/submissions et /api/v1/marches exposent vos dossiers et vos marchés pour un reporting maison ou une remontée comptable.

Vous voulez être prévenu plutôt qu'interroger ? N'appelez pas l'API en boucle : abonnez-vous aux webhooks. Vous recevez l'événement au moment où il se produit.

Créer une clé d'API

La clé appartient à l'organisation, pas à une personne : elle continue de fonctionner quand son créateur quitte l'entreprise, et elle se révoque sans toucher à aucun compte.

  1. Ouvrez les réglages Dans l'application, Paramètres → votre organisation → API & webhooks. Réservé au propriétaire de l'organisation.
  2. Créez la clé Créer une clé, donnez-lui un nom qui dit à quoi elle sert (« ERP interne », « script de veille ») : c'est ce nom que vous lirez le jour où il faudra en révoquer une.
  3. Copiez-la immédiatement La clé ne s'affiche qu'une fois. Nous n'en stockons qu'une empreinte : personne, chez nous, ne peut vous la relire.
  4. Rangez-la comme un mot de passe Variable d'environnement ou coffre à secrets. Jamais dans un dépôt Git, jamais dans du code qui part dans un navigateur.

Clé perdue ou exposée ? Révoquez-la depuis la même page et créez-en une autre. Une clé révoquée cesse de fonctionner immédiatement.

Votre premier appel

Commencez par /api/v1/me : il répond même si votre organisation n'a encore aucune donnée, et il vous dit ce que votre clé peut faire.

Terminal
export OGERANT_API_KEY="ts_live_…"

curl https://app.ogerant.com/api/v1/me \
  -H "Authorization: Bearer $OGERANT_API_KEY"
Réponse
{
  "organization": { "id": "cmqh97bhf0000m1qdhadgvv23", "name": "AZUL COMPUTING", "ice": "002345678000079" },
  "plan": { "code": "enterprise", "active": true },
  "rate_limit": { "requests_per_hour": null, "remaining": null }
}

requests_per_hour: null signifie « illimité ». Sur les autres formules, vous y lisez votre budget et ce qu'il vous en reste.

Authentification

Une seule façon d'entrer : votre clé, dans l'en-tête Authorization.

En-tête
Authorization: Bearer ts_live_…

L'en-tête X-API-Key: ts_live_… est accepté comme alias, si votre client HTTP s'en accommode mieux.

Le cookie de session ne fonctionne pas ici. C'est volontaire : une API publique qui accepterait votre cookie serait appelable depuis n'importe quel site ouvert dans votre navigateur, à votre insu. L'API attend une clé, ou renvoie 401.

Une clé absente, invalide, révoquée ou expirée donne la même réponse, nous ne confirmons jamais qu'une clé a existé :

401
{ "error": { "code": "unauthorized", "message": "Clé d'API invalide ou révoquée." } }

Pagination

Toutes les listes renvoient la même enveloppe et se parcourent par curseur, pas de numéro de page, pas de décalage qui se décale quand des données arrivent pendant votre parcours.

Enveloppe
{
  "data": [ … ],
  "next_cursor": "418732"
}
Node : parcourir toutes les pages
const key = process.env.OGERANT_API_KEY;
let cursor = null;
const all = [];

do {
  const url = new URL("https://app.ogerant.com/api/v1/tenders");
  url.searchParams.set("limit", "200");
  url.searchParams.set("kind", "ao");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, { headers: { authorization: `Bearer ${key}` } });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const page = await res.json();

  all.push(...page.data);
  cursor = page.next_cursor;
} while (cursor);

console.log(`${all.length} consultations`);

Ne repartez pas de zéro à chaque exécution. Retenez la date du dernier passage et filtrez avec ?published_since=2026-08-01 : vous ne rapatriez que le nouveau, et votre budget horaire vous remercie.

Filtrer les consultations

ParamètreExempleEffet
kindao · bcAppels d'offres ou bons de commande.
qvoirieRecherche sur le titre, la référence et l'acheteur.
sectorTravaux|ServicesSecteurs séparés par | (une virgule peut apparaître dans un nom de secteur).
acheteurONEEChaque entrée couvre tout le sous-arbre de l'organisme.
deadline_days30Date limite dans les N prochains jours.
published_since2026-08-01Publiées à partir de cette date.
min_amount / max_amount100000Bornes sur l'estimation, en MAD.
countryMACode pays ISO à 2 lettres.

Un paramètre que nous ne connaissons pas est ignoré, pas refusé : vos utm_* et autres traceurs ne casseront pas vos appels. Un paramètre connu mais invalide, en revanche, renvoie un 422 qui nomme le champ fautif.

Quotas et limites

Votre formule définit un budget de requêtes par heure glissante. Chaque réponse (y compris les réussites) vous dit où vous en êtes :

En-têtes de réponse
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4993
X-RateLimit-Reset: 1786363200

C'est fait pour être lu avant d'être coupé : si Remaining fond, ralentissez. Une fois le budget épuisé :

429
HTTP/1.1 429
Retry-After: 1837

{ "error": { "code": "rate_limited", "message": "Limite de 5000 requêtes par heure atteinte." } }

Attendez le nombre de secondes indiqué par Retry-After, puis reprenez. Le budget est compté par clé : deux intégrations, deux clés, deux budgets.

Un 429 dès le premier appel ? Votre formule n'inclut pas l'accès API (le message le dit). /api/v1/me vous donne votre formule et votre budget.

Erreurs

Une seule forme, pour toutes les erreurs, sur tous les endpoints :

Format
{ "error": { "code": "not_found", "message": "Consultation introuvable." } }

Branchez votre code sur code, jamais sur message : le premier est stable, le second est une phrase qui peut être reformulée ou traduite. La liste complète est sur Codes d'erreur.

Versionnement et compatibilité

Tout est sous /api/v1/, et ce préfixe est un engagement :

Les autres routes en /api/… ne sont pas publiques. Elles servent l'interface du produit, changent sans préavis et sont marquées x-internal dans notre spécification. Ne construisez rien dessus : seul /api/v1/ est un contrat.

La suite