Publier sur Bluesky nécessite trois appels : com.atproto.server.createSession pour obtenir des jetons, com.atproto.repo.uploadBlob si vous avez des images, et com.atproto.repo.createRecord pour écrire un enregistrement app.bsky.feed.post. Il n’y a ni validation d’application ni écran de consentement OAuth à faire approuver. Le prix de cette simplicité : les liens et les mentions ne sont pas détectés pour vous, vous calculez vous-même les décalages d’octets sur l’UTF-8.
Que peut publier l’API Bluesky, et que ne peut-elle pas ?
Un enregistrement de publication est un simple document JSON. Les champs requis sont $type (app.bsky.feed.post), text et createdAt sous forme d’horodatage ISO 8601. Tout le reste est une structure optionnelle.
| Fonctionnalité | Prise en charge |
|---|---|
| Images par publication | 4 maximum |
| Taille d’image | 1 000 000 octets chacune, selon la documentation des publications |
| Taille totale des blobs par publication | 2 000 000 octets maximum |
| Texte alternatif | Obligatoire par image, avec un rapport largeur/hauteur |
| Publications citées | app.bsky.embed.record |
| Cartes de lien | app.bsky.embed.external, avec votre propre miniature |
| Réponses | reply avec des références fortes root et parent |
| Balises de langue | langs, un tableau comme ["en-US"] |
Les manques à signaler d’emblée :
- Rien n’est détecté automatiquement. Les liens, mentions et hashtags sont du texte inerte tant que vous n’attachez pas de facettes. Une URL collée sans facette n’est pas cliquable.
- Les cartes de lien sont à votre charge.
app.bsky.embed.externalattend le titre, la description et la miniature. Rien ne scrute la page à votre place. - Les mentions nécessitent un DID résolu. Vous ne pouvez pas mettre un identifiant dans une facette ; vous devez d’abord résoudre l’identifiant en DID.
- Les métadonnées EXIF doivent être retirées avant l’envoi des images, selon la documentation.
- La documentation des publications indique l’absence de limite de caractères ou de graphèmes. Nous n’avons pas pu vérifier un chiffre sur cette page, nous n’en citons donc pas. Consultez l’article sur la limite de caractères Bluesky pour ce que le client applique réellement.
Remarque : les chiffres ici ont été vérifiés dans la documentation développeur de Bluesky (docs.bsky.app, qui redirige désormais vers bsky.network/docs) en septembre 2026. Les plateformes les modifient sans préavis.
Quel est le modèle d’authentification ?
C’est la section d’authentification la plus courte que vous lirez pour n’importe quelle plateforme sociale.
Il n’y a aucune inscription d’application, aucune validation d’application et aucun écran de consentement OAuth pour le chemin par mot de passe d’application. Un utilisateur crée un mot de passe d’application dans ses paramètres Bluesky et le communique à votre logiciel. Vous échangez l’identifiant plus ce mot de passe d’application contre un JWT d’accès et un JWT de rafraîchissement via com.atproto.server.createSession. Les jetons d’accès sont de courte durée ; vous les rafraîchissez avec le JWT de rafraîchissement.
Deux conséquences. Un mot de passe d’application est une donnée d’identification que votre utilisateur vous remet directement : l’absence d’écran de consentement signifie l’absence d’octroi de portée médié par la plateforme, donc l’obligation de stockage repose entièrement sur vous. Chiffrez-les, et donnez aux utilisateurs un moyen visible de se déconnecter.
Et le réseau n’appartient pas à une seule entreprise. Les comptes AT Protocol vivent sur un serveur de données personnel (PDS), et un PDS peut être auto-hébergé. Votre client s’adresse à l’hôte PDS de l’utilisateur, donc coder en dur bsky.social fonctionne aujourd’hui mais ne correspond pas au modèle du protocole. Lisez l’hôte depuis le document DID de l’utilisateur.
Comment publier concrètement ? La séquence d’appels
POST /xrpc/com.atproto.server.createSessionavecidentifier(identifiant ou DID) etpassword(le mot de passe d’application). RetourneaccessJwt,refreshJwtetdid.- Si vous avez des images :
POST /xrpc/com.atproto.repo.uploadBlobpar image, avec les octets bruts et le bonContent-Type. Chacune retourne une référence de blob. - Calculez les facettes pour les liens ou mentions, sous forme de décalages d’octets dans l’encodage UTF-8 de
text. POST /xrpc/com.atproto.repo.createRecordavecreporéglé sur votre DID,collectionréglé surapp.bsky.feed.post, et l’enregistrement lui-même.
const base = 'https://bsky.social/xrpc';
const auth = await fetch(`${base}/com.atproto.server.createSession`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identifier: handle, password: appPassword }),
}).then((r) => r.json());
const text = 'Notes on the release: https://example.com/changelog';
const url = 'https://example.com/changelog';
const enc = new TextEncoder();
const byteStart = enc.encode(text.slice(0, text.indexOf(url))).length;
await fetch(`${base}/com.atproto.repo.createRecord`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${auth.accessJwt}`,
},
body: JSON.stringify({
repo: auth.did,
collection: 'app.bsky.feed.post',
record: {
$type: 'app.bsky.feed.post',
text,
createdAt: new Date().toISOString(),
facets: [
{
index: { byteStart, byteEnd: byteStart + enc.encode(url).length },
features: [{ $type: 'app.bsky.richtext.facet#link', uri: url }],
},
],
},
}),
});
L’arithmétique des facettes, en détail
byteStart et byteEnd sont des décalages dans les octets UTF-8 du texte, pas dans les indices de chaîne JavaScript et pas dans les caractères.
"café".length vaut 4 en JavaScript, mais l’encodage UTF-8 fait 5 octets. Tout emoji placé avant votre lien décale le décalage de 4 octets tout en ne déplaçant l’indice de chaîne que de 2. Une erreur ici et le lien s’affiche comme du texte cassé ou met en surbrillance la mauvaise portion, sans aucune erreur du serveur : l’enregistrement est valide, il pointe simplement vers les mauvais octets.
Encodez la chaîne une seule fois, trouvez les décalages dans le tableau d’octets, et ne mélangez jamais les deux systèmes de coordonnées. La fonctionnalité de facette de lien utilise uri, pas url.
Quelles sont les limites de débit de Bluesky ?
Les écritures sont mesurées avec un système de points par compte : CREATE coûte 3 points, UPDATE 2, DELETE 1, contre un budget de 5 000 points par heure et 35 000 par jour. Cela représente environ 1 666 créations par heure et 11 666 par jour.
| Limite | Valeur |
|---|---|
| Points d’écriture | 5 000/heure, 35 000/jour |
| Créations (calculé) | ~1 666/heure, ~11 666/jour |
| Requêtes PDS globales | 3 000 par tranche de 5 minutes, par IP |
createSession | 30 par tranche de 5 minutes, 300 par jour, par compte |
| Plafond d’envoi de blob | 52 428 800 octets (50 Mo) |
La limite createSession est celle qui piège les planificateurs. 30 par tranche de 5 minutes et par compte signifie que vous mettez la session en cache et la rafraîchissez, plutôt que de vous connecter à chaque publication. Un service qui crée une session à chaque tâche se limitera lui-même bien avant d’atteindre une quelconque limite de publication.
Notez les deux chiffres de blob : le PDS accepte des blobs jusqu’à 50 Mo, tandis que la documentation des publications indique une limite de 1 000 000 octets par image et de 2 000 000 octets au total pour les images d’une publication. Dimensionnez selon le plus petit des deux.
Remarque : les chiffres ici ont été vérifiés dans la documentation des limites de débit de Bluesky en septembre 2026. Les plateformes les modifient sans préavis.
Qu’est-ce qui vous coûtera vraiment trois semaines ?
Bluesky est réellement la plateforme majeure la moins coûteuse à intégrer, mais « peu coûteux » ne veut pas dire « gratuit ».
Le calcul des facettes sur du texte réel. Détecter les URL, la ponctuation en fin de mot, les identifiants et les hashtags, convertir chaque correspondance en décalages d’octets UTF-8, et garder cela correct quand un utilisateur modifie le texte. C’est là que vivent les bugs.
La gestion du budget de blobs. 2 Mo au total sur jusqu’à 4 images signifie redimensionner et réencoder côté serveur, retirer les métadonnées EXIF, et décider quoi faire quand les photos de l’utilisateur ne tiennent pas.
Les cartes de lien. Pour que les publications ressemblent à de vraies publications, vous récupérez la page cible, en extrayez le titre, la description et l’image, envoyez cette image comme blob, et construisez l’intégration external. C’est un petit robot d’exploration avec des délais d’expiration et une gestion des échecs.
La mise en cache de session, à cause du plafond de 30 par tranche de 5 minutes, et la résolution de l’hôte PDS, car présumer bsky.social casse les comptes auto-hébergés d’une façon que vous ne pouvez pas corriger de votre côté.
Si vous mettez en miroir du contenu existant, republier de X vers Bluesky et republier de Threads vers Bluesky couvrent tous deux les différences de longueur de texte et de média à concilier avant même que l’enregistrement soit valide.
En bref
- Trois appels :
createSession,uploadBlobpour les images,createRecordavec un enregistrementapp.bsky.feed.post. - Pas de validation d’application, pas d’écran OAuth. Des mots de passe d’application, remis par l’utilisateur.
- Les liens et mentions nécessitent des facettes avec des décalages d’octets en UTF-8. Rien n’est détecté automatiquement.
- 4 images par publication, 1 000 000 octets chacune, 2 000 000 octets au total, texte alternatif obligatoire.
- Les écritures coûtent 3 points par création, contre 5 000/heure et 35 000/jour.
createSessionest limité à 30 par tranche de 5 minutes. - Les comptes peuvent vivre sur un PDS auto-hébergé. Ne codez pas l’hôte en dur.
Publier sur Bluesky aux côtés de 14 autres réseaux
Bluesky est le cas facile. Le même produit a généralement aussi besoin de X (OAuth 2.0 PKCE, envoi de média fragmenté, facturation par requête), de Threads (validation d’application Meta, conteneur puis publication, rafraîchissement de jeton sur 60 jours) et de TikTok (un audit de l’API de publication de contenu avant de pouvoir publier quoi que ce soit). Chacun a son propre modèle d’authentification, son pipeline média et son modèle d’échec asynchrone.
BulkPublish est une seule API REST couvrant 15 plateformes, Bluesky inclus, avec le calcul des facettes, le redimensionnement des blobs et le rafraîchissement de session gérés côté serveur. La documentation développeur et la référence de l’API REST contiennent les points de terminaison, et programmer des publications Bluesky couvre cela sans code.