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/tendersvous rend les consultations filtrées (secteur, acheteur, montant, date limite) que vous injectez chez vous. - Récupérer vos correspondances
/api/v1/matchesrenvoie 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/submissionset/api/v1/marchesexposent 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.
- Ouvrez les réglages Dans l'application, Paramètres → votre organisation → API & webhooks. Réservé au propriétaire de l'organisation.
- 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.
- 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.
- 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.
export OGERANT_API_KEY="ts_live_…"
curl https://app.ogerant.com/api/v1/me \
-H "Authorization: Bearer $OGERANT_API_KEY"
{
"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.
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é :
{ "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.
{
"data": [ … ],
"next_cursor": "418732"
}
?limit=: 50 par défaut, 200 au maximum. Demander plus n'est pas une erreur : la valeur est simplement ramenée à 200.?cursor=: recopiez lenext_cursorde la page précédente.next_cursor: null: vous êtes à la dernière page. Bouclez jusque-là.
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ètre | Exemple | Effet |
|---|---|---|
kind | ao · bc | Appels d'offres ou bons de commande. |
q | voirie | Recherche sur le titre, la référence et l'acheteur. |
sector | Travaux|Services | Secteurs séparés par | (une virgule peut apparaître dans un nom de secteur). |
acheteur | ONEE | Chaque entrée couvre tout le sous-arbre de l'organisme. |
deadline_days | 30 | Date limite dans les N prochains jours. |
published_since | 2026-08-01 | Publiées à partir de cette date. |
min_amount / max_amount | 100000 | Bornes sur l'estimation, en MAD. |
country | MA | Code 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 :
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é :
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 :
{ "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 :
- un champ publié ne change ni de nom ni de type ;
- les ajouts sont additifs : un nouveau champ peut apparaître, votre code doit l'ignorer sans broncher ;
- une rupture, s'il en fallait une, serait
/api/v2, pas une modification dev1.
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
- Référence API : chaque endpoint, ses paramètres, ses réponses, et un bouton « Essayer ».
- Webhooks : recevez les événements au lieu d'interroger en boucle.
- Codes d'erreur : que faire pour chacun.