Bluesky-API: Wie man per Code postet (2026)

Bluesky-API: Wie man per Code postet (2026)

Eine Sitzung erstellen, ein Blob hochladen, einen app.bsky.feed.post-Datensatz schreiben. Keine App-Prüfung, kein OAuth-Bildschirm, aber Sie berechnen Link-Facets selbst.

Das Posten auf Bluesky erfordert drei Aufrufe: com.atproto.server.createSession, um Tokens zu erhalten, com.atproto.repo.uploadBlob, wenn Sie Bilder haben, und com.atproto.repo.createRecord, um einen app.bsky.feed.post-Datensatz zu schreiben. Es gibt keine App-Prüfung und keinen OAuth-Einwilligungsbildschirm, den es freizugeben gilt. Der Preis dieser Einfachheit ist, dass Links und Erwähnungen nicht für Sie erkannt werden: Sie berechnen Byte-Offsets über UTF-8 selbst.

Was kann die Bluesky-API veröffentlichen, und was nicht?

Ein Beitragsdatensatz ist ein einfaches JSON-Dokument. Die erforderlichen Felder sind $type (app.bsky.feed.post), text und createdAt als ISO-8601-Zeitstempel. Alles andere ist optionale Struktur.

FunktionUnterstützung
Bilder pro BeitragMaximal 4
BildgrößeJeweils 1.000.000 Bytes, laut Beitrags-Dokumentation
Gesamte Blob-Größe pro BeitragMaximal 2.000.000 Bytes
Alt-TextPro Bild erforderlich, mit einem Seitenverhältnis
Zitierte Beiträgeapp.bsky.embed.record
Link-Kartenapp.bsky.embed.external, mit eigenem Thumbnail
Antwortenreply mit starken Referenzen root und parent
Sprach-Tagslangs, ein Array wie ["en-US"]

Die Lücken, die man vorab benennen sollte:

  • Nichts wird automatisch erkannt. Links, Erwähnungen und Hashtags sind inaktiver Text, sofern Sie keine Facets anhängen. Eine eingefügte URL ohne Facet ist nicht klickbar.
  • Link-Karten sind Ihre Aufgabe. app.bsky.embed.external erwartet Titel, Beschreibung und Thumbnail. Nichts scannt die Seite für Sie.
  • Erwähnungen brauchen eine aufgelöste DID. Sie können keinen Handle in ein Facet einsetzen; Sie lösen den Handle zuerst zu einer DID auf.
  • Bilder müssen vor dem Upload von EXIF-Daten befreit werden, laut Dokumentation.
  • Die Beitrags-Dokumentation nennt kein Zeichen- oder Grapheme-Limit. Wir konnten von dieser Seite keines verifizieren, daher zitieren wir keine Zahl. Siehe den Beitrag zum Bluesky-Zeichenlimit für das, was der Client tatsächlich durchsetzt.

Hinweis: Die Zahlen hier wurden mit Stand September 2026 gegen die Bluesky-Entwicklerdokumentation (docs.bsky.app, das nun auf bsky.network/docs weiterleitet) geprüft. Plattformen ändern diese ohne Vorankündigung.

Wie sieht das Auth-Modell aus?

Das ist der kürzeste Auth-Abschnitt, den Sie für irgendeine Social-Media-Plattform lesen werden.

Es gibt keine App-Registrierung, keine App-Prüfung und keinen OAuth-Einwilligungsbildschirm für den App-Passwort-Weg. Ein Nutzer erstellt ein App-Passwort in seinen Bluesky-Einstellungen und gibt es Ihrer Software. Sie tauschen den Handle plus dieses App-Passwort bei com.atproto.server.createSession gegen ein Zugriffs-JWT und ein Refresh-JWT ein. Zugriffstokens sind kurzlebig; Sie erneuern sie mit dem Refresh-JWT.

Zwei Konsequenzen. Ein App-Passwort ist ein Zugangsdatum, das Ihr Nutzer Ihnen direkt übergibt: Kein Einwilligungsbildschirm bedeutet keine plattformvermittelte Berechtigungsvergabe, daher liegt die Speicherpflicht vollständig bei Ihnen. Verschlüsseln Sie sie, und geben Sie Nutzern eine sichtbare Möglichkeit, die Verbindung zu trennen.

Und das Netzwerk gehört nicht einem einzigen Unternehmen. AT-Protocol-Konten liegen auf einem Personal Data Server, und ein PDS ist selbst hostbar. Ihr Client spricht mit dem PDS-Host des Nutzers, daher funktioniert das Hartcodieren von bsky.social heute, entspricht aber nicht dem Modell des Protokolls. Lesen Sie den Host aus dem DID-Dokument des Nutzers.

Wie posten Sie tatsächlich? Die Aufrufreihenfolge

  1. POST /xrpc/com.atproto.server.createSession mit identifier (Handle oder DID) und password (das App-Passwort). Gibt accessJwt, refreshJwt und did zurück.
  2. Wenn Sie Bilder haben: POST /xrpc/com.atproto.repo.uploadBlob pro Bild, mit den rohen Bytes und dem korrekten Content-Type. Jeder Aufruf gibt eine Blob-Referenz zurück.
  3. Berechnen Sie Facets für alle Links oder Erwähnungen, als Byte-Offsets in die UTF-8-Kodierung von text.
  4. POST /xrpc/com.atproto.repo.createRecord mit repo auf Ihre DID gesetzt, collection auf app.bsky.feed.post gesetzt, und dem eigentlichen Datensatz.
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 }],
        },
      ],
    },
  }),
});

Die Facet-Arithmetik, im Detail erklärt

byteStart und byteEnd sind Offsets in die UTF-8-Bytes des Textes, nicht in JavaScript-String-Indizes und nicht in Zeichen.

"café".length ist in JavaScript 4, aber die UTF-8-Kodierung sind 5 Bytes. Jedes Emoji vor Ihrem Link verschiebt den Offset um 4 Bytes, während sich der String-Index nur um 2 verschiebt. Machen Sie das falsch, wird der Link als kaputter Text dargestellt oder hebt die falsche Textstelle hervor, ohne dass der Server einen Fehler meldet: Der Datensatz ist gültig, er zeigt nur auf die falschen Bytes.

Kodieren Sie den String einmal, finden Sie Offsets im Byte-Array, und mischen Sie niemals die beiden Koordinatensysteme. Das Link-Facet-Feature nutzt uri, nicht url.

Wie sehen die Bluesky-Ratenlimits aus?

Schreibvorgänge werden über ein Punktesystem pro Konto gemessen: CREATE kostet 3 Punkte, UPDATE 2, DELETE 1, gegen ein Budget von 5.000 Punkten pro Stunde und 35.000 pro Tag. Das entspricht ungefähr 1.666 Erstellungen pro Stunde und 11.666 pro Tag.

LimitWert
Schreibpunkte5.000/Stunde, 35.000/Tag
Erstellungen (abgeleitet)~1.666/Stunde, ~11.666/Tag
Gesamte PDS-Anfragen3.000 pro 5 Minuten, pro IP
createSession30 pro 5 Minuten, 300 pro Tag, pro Konto
Blob-Upload-Obergrenze52.428.800 Bytes (50 MB)

Das createSession-Limit ist dasjenige, das Planungstools ins Straucheln bringt. 30 pro 5 Minuten pro Konto bedeutet, dass Sie die Sitzung zwischenspeichern und erneuern, statt sich bei jedem Beitrag neu anzumelden. Ein Worker, der bei jedem Job eine neue Sitzung erstellt, bringt sich selbst ins Rate-Limit, lange bevor er irgendein Posting-Limit erreicht.

Beachten Sie die zwei Blob-Zahlen: Der PDS akzeptiert Blobs bis zu 50 MB, während die Beitrags-Dokumentation ein Limit von 1.000.000 Bytes pro Bild und 2.000.000 Bytes insgesamt für Beitragsbilder angibt. Bemessen Sie nach der kleineren Zahl.

Hinweis: Die Zahlen hier wurden mit Stand September 2026 gegen die Bluesky-Entwicklerdokumentation zu Ratenlimits geprüft. Plattformen ändern diese ohne Vorankündigung.

Was wird Sie tatsächlich drei Wochen kosten?

Bluesky ist wirklich die günstigste der großen Plattformen zur Integration, aber „günstig“ heißt nicht „kostenlos“.

Facet-Berechnung für echten Text. URLs, nachgestellte Satzzeichen, Handles und Hashtags erkennen, jeden Treffer in UTF-8-Byte-Offsets umrechnen und das korrekt halten, wenn ein Nutzer den Text bearbeitet. Hier stecken die Fehler.

Blob-Budgetierung. 2 MB insgesamt über bis zu 4 Bilder bedeutet serverseitiges Neuskalieren und Neukodieren, das Entfernen von EXIF-Daten und die Entscheidung, was zu tun ist, wenn die Fotos des Nutzers nicht hineinpassen.

Link-Karten. Damit Beiträge wie Beiträge aussehen, rufen Sie die Zielseite ab, extrahieren Titel, Beschreibung und Bild, laden dieses Bild als Blob hoch und bauen das external-Embed. Das ist ein kleiner Crawler mit Timeouts und Fehlerbehandlung.

Sitzungs-Caching, wegen der Obergrenze von 30 pro 5 Minuten, und PDS-Host-Auflösung, weil die Annahme von bsky.social selbst gehostete Konten auf eine Weise kaputt macht, die Sie von Ihrer Seite aus nicht beheben können.

Wenn Sie Inhalte spiegeln, decken sowohl Cross-Posting von X zu Bluesky als auch Cross-Posting von Threads zu Bluesky die Unterschiede bei Textlänge und Medien ab, die Sie klären müssen, bevor der Datensatz überhaupt gültig ist.

Die Kurzfassung

  • Drei Aufrufe: createSession, uploadBlob für Bilder, createRecord mit einem app.bsky.feed.post-Datensatz.
  • Keine App-Prüfung, kein OAuth-Bildschirm. App-Passwörter, vom Nutzer übergeben.
  • Links und Erwähnungen brauchen Facets mit Byte-Offsets über UTF-8. Nichts wird automatisch erkannt.
  • 4 Bilder pro Beitrag, jeweils 1.000.000 Bytes, 2.000.000 Bytes insgesamt, Alt-Text erforderlich.
  • Schreibvorgänge kosten 3 Punkte pro Erstellung, gegen 5.000/Stunde und 35.000/Tag. createSession liegt bei 30 pro 5 Minuten.
  • Konten können auf einem selbst gehosteten PDS liegen. Hartcodieren Sie den Host nicht.

Auf Bluesky posten, neben 14 weiteren Netzwerken

Bluesky ist das einfache. Dasselbe Produkt braucht meist auch X (OAuth 2.0 PKCE, Chunked-Media-Upload, Abrechnung pro Anfrage), Threads (Meta-App-Prüfung, erst Container dann Veröffentlichung, 60-Tage-Token-Erneuerung) und TikTok (eine Content-Posting-API-Prüfung, bevor überhaupt veröffentlicht werden kann). Jede hat ihr eigenes Auth-Modell, ihre eigene Medien-Pipeline und ihr eigenes asynchrones Fehlermodell.

BulkPublish ist eine REST-API über 15 Plattformen hinweg, Bluesky eingeschlossen, mit Facet-Berechnung, Blob-Neuskalierung und Sitzungserneuerung, die serverseitig erledigt werden. Die Entwicklerdokumentation und die REST-API-Referenz enthalten die Endpunkte, und Bluesky-Beiträge planen behandelt es ohne Code.

Verwandte Artikel