Gladia, Deepgram, AssemblyAI, Speechmatics, Soniox et ElevenLabs, sélectionnables bot par bot, avec votre propre clé API, votre propre région, et le passage intégral de toutes les options spécifiques à chaque fournisseur.

Choisir un fournisseur de speech-to-text n'est pas une décision qu'on prend une bonne fois pour toutes. La précision sur votre domaine, le prix à l'heure, les langues dont vous avez besoin, le fait que le fournisseur accepte ou non de signer un DPA avec des données qui restent dans l'UE, et la tenue de la séparation des interlocuteurs quand trois personnes parlent en même temps : tout cela bouge, et la réponse n'est jamais la même d'un client à l'autre.
Alors nous avons arrêté de choisir. Vous choisissez le fournisseur bot par bot, et vous pouvez changer d'avis sur un enregistrement que vous avez déjà.
Ce que vous pouvez choisir
| Fournisseur | Batch | Live |
|---|---|---|
gladia | ✅ | ✅ |
deepgram | ✅ | ✅ |
assemblyai | ✅ | ✅ |
speechmatics | ✅ | ✅ |
soniox | ✅ | ✅ |
elevenlabs | — | ✅ |
ElevenLabs est en streaming uniquement. Les cinq autres font les deux.
Le batch, c'est ce que la plupart des gens veulent : le meeting se termine, l'audio est soumis, un webhook rapporte la transcription avec les labels de locuteurs et les timings au mot près. Le live, ce sont des segments de transcription qui arrivent sur une WebSocket pendant que le meeting est encore en cours, pour tout ce qui doit réagir dans la salle.
Les deux se configurent indépendamment, et vous pouvez faire tourner les deux sur le même bot avec des fournisseurs différents si vous voulez un flux live rapide et une transcription finale plus précise.
Batch
{
"meeting_url": "https://meet.google.com/abc-defg-hij",
"bot_name": "Bot d'Enregistrement",
"transcription_enabled": true,
"transcription_config": {
"provider": "speechmatics",
"region": "eu2",
"api_key": null,
"custom_params": null
}
}Quatre champs. provider vaut "gladia" par défaut si vous l'omettez. region, api_key et custom_params sont tous à null par défaut et sont détaillés plus bas.
transcription_config est requis quand transcription_enabled est à true, et ignoré quand il est à false.

Une fois le bot terminé, transcription_provider et transcription_ids reviennent sur l'enregistrement du bot et dans le payload du webhook, et GET /v2/bots/:bot_id/status renvoie en direct le transcription_status interrogé auprès du fournisseur lui-même : not-applicable, not-started, queued, processing, done ou error.
Épinglage de région
La plupart de ces fournisseurs tournent dans plusieurs régions, et pour beaucoup de nos clients, c'est là que tout se joue.
| Fournisseur | Régions batch | Régions live |
|---|---|---|
gladia | endpoint global uniquement | us-west, eu-west |
deepgram | global, eu | global, eu |
assemblyai | us, eu | us, eu |
speechmatics | eu1, eu2, us1, us2, au1 | identiques |
soniox | us, eu, jp | identiques |
elevenlabs | — | global, us, eu, in |
Envoyez une région qu'un fournisseur ne propose pas et vous obtenez un 400 à la création du bot, avec la liste des valeurs autorisées, plutôt qu'une mauvaise surprise plus tard. L'API batch de Gladia est uniquement globale, et passer region avec provider: "gladia" dans la config batch est rejeté pour cette raison. Son API live, elle, a bien des régions.
Laissez region à null et nous choisissons une valeur par défaut raisonnable : eu pour Deepgram et AssemblyAI, eu1 pour Speechmatics, us pour Soniox.
Si l'audio ne doit jamais sortir d'une juridiction, combinez ceci avec bring your own storage. Les fournisseurs récupèrent l'audio depuis une URL signée sur le bucket : l'emplacement du bucket et la région du fournisseur déterminent donc ensemble où l'audio circule réellement.
Bring your own key
Passez api_key et nous utilisons votre compte chez ce fournisseur plutôt que le nôtre.
{
"transcription_config": {
"provider": "deepgram",
"region": "eu",
"api_key": "votre-cle-deepgram"
}
}Les clés sont chiffrées en AES-256-GCM avant d'être stockées et restent chiffrées en transit jusqu'au bot, qui déchiffre au moment de l'utilisation. Quand vous supprimez les données d'un bot, la clé stockée est écrasée.
Deux raisons de s'en servir. La première, c'est le coût : la transcription sur notre clé est facturée 0,25 token par heure, sur la vôtre 0,05. La seconde, c'est que certaines choses ne sont possibles que sur votre propre compte : un vocabulaire personnalisé que vous avez entraîné, ou un contrat dont nous n'avons pas les conditions.
Le BYOK est disponible à partir des plans Pro. Sans cela, vous obtenez FST_ERR_BYOK_TRANSCRIPTION_NOT_ENABLED_ON_PLAN.
Les options du fournisseur, transmises telles quelles
Nous n'avons délibérément pas construit de couche d'options normalisée. Chaque fournisseur a des fonctionnalités que les autres n'ont pas, et les ramener à un dénominateur commun reviendrait à jeter la raison même pour laquelle vous avez choisi ce fournisseur.
custom_params part chez le fournisseur à peu près tel que vous l'avez écrit :
{
"transcription_config": {
"provider": "speechmatics",
"region": "eu2",
"custom_params": {
"transcription_config": {
"language": "en",
"diarization": "speaker",
"operating_point": "enhanced"
}
}
}
}La notation par points fonctionne aussi, si c'est plus simple à construire :
{ "custom_params": { "diarization_config.min_speakers": 2 } }Sur la config live, l'objet est déplié avant validation : vous validez donc la structure que le fournisseur recevra réellement. Sur la config batch, la clé plate est validée telle quelle et dépliée plus tard, au moment de la soumission.
Dans la config live, quatre champs sont définis par le pipeline et rejetés si vous les envoyez : encoding, sample_rate, bit_depth, channels. Le sample rate en particulier est négocié avec chaque fournisseur à partir de l'audio du meeting : le surcharger casserait le flux. Utilisez streaming_config.audio_frequency à la place.
La séparation des interlocuteurs est activée par défaut, parce que sans elle vous récupérez un mur de texte sans aucun label de locuteur, et parce qu'au moins un fournisseur ne renvoie silencieusement aucun segment de parole tant que vous ne l'avez pas demandée. Une valeur explicite dans custom_params l'emporte.
Le piège : batch et live n'ont pas la même forme
C'est ce qui coûte une après-midi à pas mal de monde, alors autant le dire clairement. Pour un même fournisseur, l'API live et l'API batch n'acceptent pas la même structure de paramètres.
Gladia en est l'exemple le plus net. En batch, la config de traduction est au niveau racine. En live, elle doit être imbriquée sous realtime_processing :
{
"streaming_config": {
"mode": "transcription",
"output_url": "wss://your-app.example.com/transcripts",
"audio_frequency": 24000,
"transcription": {
"provider": "gladia",
"region": "eu-west",
"api_key": "votre-cle-gladia",
"custom_params": {
"language_config": { "languages": ["ru"] },
"realtime_processing": {
"translation": true,
"translation_config": { "target_languages": ["en"] }
}
}
}
}
}Les paramètres live sont validés en mode strict, parce que les API de session live rejettent purement et simplement les clés inconnues et que nous préférons faire échouer la création de votre bot plutôt que votre meeting. Quand vous envoyez une clé qui appartient un niveau plus bas, l'erreur vous dit où elle va :
'translation' is not a top-level field of the gladia live API —
did you mean 'realtime_processing.translation'?
(the live API shape differs from batch)Les paramètres batch comme live sont vérifiés contre le schéma propre à chaque fournisseur, à la création du bot : une faute de frappe revient en 400 en une seconde plutôt qu'en transcription ratée une heure plus tard. Deux réserves sur le degré de sévérité. Le parsing du batch est permissif : une clé batch inconnue passe la validation et est transmise telle quelle, au fournisseur de s'en débrouiller ; seul le live rejette les clés inconnues. Et les paramètres live d'ElevenLabs sont transmis sans validation, parce qu'il n'existe aucun schéma publié auquel les confronter.
La sortie live
Avec mode: "transcription", nous ouvrons la session chez le fournisseur, lui envoyons l'audio du meeting et transmettons les événements à votre output_url en JSON :
{
"event": "transcript.segment",
"bot_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"data": {
"text": "donc la migration arrive jeudi",
"isFinal": true,
"utteranceStart": 12.44,
"utteranceEnd": 14.91,
"confidence": 0.97,
"speaker": { "name": "Alice", "id": 1 },
"words": []
}
}Vous recevez aussi session.started avec le fournisseur utilisé, translation quand vous l'avez demandée, et error. Nous envoyons un ping toutes les 30 secondes pour éviter qu'un intermédiaire ne ferme une socket inactive.
Une contrainte : les sessions live sur notre clé de plateforme sont limitées à un ensemble de fournisseurs gérés, aujourd'hui Gladia. Tout autre fournisseur en transcription live nécessite une clé BYOK, et nous rejetons le cas à la création du bot avec FST_ERR_STREAMING_TRANSCRIPTION_KEY_UNAVAILABLE plutôt que de le découvrir au démarrage du meeting.
Changer d'avis
POST /v2/bots/:bot_id/retranscribe{
"transcription": {
"provider": "assemblyai",
"region": "eu",
"api_key": "votre-cle-assemblyai"
}
}L'audio stocké est resoumis à un autre fournisseur et la config du bot est mise à jour. C'est comme ça que vous comparez deux fournisseurs en A/B sur vos propres meetings plutôt que sur leur audio de démo, et c'est comme ça que vous rattrapez une transcription qui a échoué.
Supprimer les données d'un bot retire aussi la transcription de chez le fournisseur, avec ?delete_from_provider=true (la valeur par défaut), en utilisant votre propre clé lorsque vous en avez fourni une.
Ce qui se passe quand un fournisseur a une mauvaise journée
Une soumission qui échoue est réessayée deux fois, à 5 et 15 secondes, soit trois tentatives au total. Seules les erreurs où un nouvel essai a une chance de marcher y ont droit : rate limits, 5xx, timeouts de connexion et de polling, défaillances réseau, et le cas où le fournisseur a renvoyé un 2xx avec un corps ne contenant aucun job id. Les erreurs d'authentification, les entrées invalides et les opérations non supportées ne sont jamais réessayées, parce qu'elles échoueront exactement pareil la deuxième fois.
Chaque nouvel essai fait tourner le secret de callback avant de resoumettre. Cela ferme une vraie race condition : si le fournisseur a effectivement accepté un job dont la réponse a expiré de notre côté, son callback arrive avec un secret qui n'existe plus et se fait rejeter, au lieu d'entrer en course avec le job réessayé et de produire deux transcriptions pour un même chunk.
Il n'y a aucun failover automatique vers un second fournisseur, et je préfère le dire franchement plutôt que vous laisser le découvrir pendant un incident. Si le fournisseur que vous avez choisi échoue après les réessais, le bot termine en failed avec TRANSCRIPTION_FAILED, et l'enregistrement comme les artefacts audio sont toujours là. La récupération se fait par un appel retranscribe, en désignant éventuellement un autre fournisseur. Basculer silencieusement de fournisseur voudrait dire une transcription dans une juridiction que vous n'avez pas acceptée, à un prix que vous n'avez pas accepté : c'est donc une décision que nous vous laissons.
Sur le même sujet
- Bring Your Own Storage — contrôlez où vit réellement l'audio que lit le fournisseur
- Référence de l'API Meeting BaaS