Archive X threads as Markdown
EN PT ID

Bluesky to Markdown 2026: Save Posts as Markdown

August 10, 2026 · 10 min read · Guide

Bluesky is built on the AT Protocol (“atproto”), and that choice makes it one of the easiest social platforms in 2026 to export. Every public post, thread, and author feed is served by a public API that returns clean JSON — no account, no token, and no rate-limit handshake for basic reads. That means “Bluesky to Markdown” is not a niche workaround; it is a normal, supported workflow.

This guide shows why saving Bluesky posts as Markdown is worth doing, how the public atproto endpoints work, and the fastest routes to a portable local archive — from a single curl to a batch script you can run on your whole timeline.

Quick take: Bluesky exposes public posts through public.api.bsky.app with no authentication. Resolve a handle to a DID, fetch the feed, convert the record text to Markdown, and you have a durable, searchable copy in seconds. It is the same “grab the content, keep the Markdown” pattern ThreadGrab uses for X — and it works for Bluesky too.

Why Save Bluesky Posts as Markdown

Bluesky is more open than most networks, but open does not mean permanent. Posts can be deleted, handles can change, and an account or instance issue can take your reading list with it. If a post matters — a research note, a useful thread, a reference you will cite later — the only copy you truly control is the one on your own disk.

Markdown is the right format for that job for three concrete reasons:

A screenshot preserves the pixels, not the content. Markdown preserves the words, the structure, the links, and the metadata — which is why the archiving community has standardized on it.

How the Bluesky Public API Works

Bluesky runs on atproto, and the read paths are deliberately public. Two endpoints do almost all the work for archiving:

The base URL is https://public.api.bsky.app. The full endpoint reference is published by Bluesky at docs.bsky.app, and the feed endpoint documents the exact response shape. There is no login step for public content: you resolve, you fetch, you are done. The response is structured JSON, so converting it to Markdown is a few lines of code in any language.

Note: you only need an app password (a token) if you want to read private posts or perform actions on an account. Pure public archiving never touches auth, which also makes it easy to script on a headless server or inside a cron job.

Method 1 — Save a Single Post with curl

The fastest way to grab one Bluesky post as Markdown is to call the public API directly. First, resolve the author’s handle to a DID:

curl "https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=bsky.app"

The response returns the persistent identifier:

{ "did": "did:plc:z72i7hdynmk6r22z27h6tvur" }

Now fetch the author’s feed, keeping only the fields you want. A short Python one-liner turns the JSON into clean Markdown bullets:

import json, urllib.request
did = "did:plc:z72i7hdynmk6r22z27h6tvur"
url = ("https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed"
       "?actor=" + did + "&limit=5")
data = json.load(urllib.request.urlopen(url))
for item in data["feed"]:
    rec = item["post"]["record"]
    print("## " + rec.get("createdAt", "")[:10])
    print(rec.get("text", "") + "\n")

Run it and you get a readable Markdown block per post — the date as a heading, the text as a paragraph. That is the whole workflow for a single manual save.

Method 2 — Batch-Archive a Whole Timeline

For an entire account’s recent posts, add pagination. getAuthorFeed returns a cursor you pass back to fetch the next page, so a small loop captures everything without missing anything:

import json, urllib.request, urllib.parse
did = "did:plc:z72i7hdynmk6r22z27h6tvur"
cursor = None
for page in range(3):
    params = {"actor": did, "limit": 50}
    if cursor:
        params["cursor"] = cursor
    url = ("https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?"
           + urllib.parse.urlencode(params))
    data = json.load(urllib.request.urlopen(url))
    for item in data.get("feed", []):
        rec = item["post"]["record"]
        print("## " + rec.get("createdAt", "")[:10] + "\n"
              + rec.get("text", "") + "\n")
    cursor = data.get("cursor")
    if not cursor:
        break

Each run appends the posts to a Markdown file, and the cursor guarantees you see every page. Run it weekly inside a cron job and you get a rolling, searchable archive of any Bluesky account you follow.

Method 3 — ThreadGrab for Cross-Platform Markdown

The API route is powerful but hands-on. If you want the same result for X threads, X long-form Articles, and Bluesky posts without writing and maintaining scripts, ThreadGrab wraps the “grab the content, keep the Markdown” pattern into one tool. It converts social posts to clean Markdown, downloads linked media to your own storage, and gives you a portable library that does not depend on any single platform’s API or pricing.

That matters when your archive spans platforms. A single-script API approach works beautifully for one Bluesky account; a mixed X-and-Bluesky library benefits from a tool that normalizes the output format everywhere.

Which Method Should You Use?

ScenarioBest fitWhy
One-off manual savecurl + resolveHandleNo setup, seconds to run
Whole account / timelinePaged getAuthorFeed scriptCursor pagination, zero auth
Ongoing weekly archiveAutomated cron + Markdown fileRolling, searchable history
Mixed X + Bluesky libraryThreadGrabNormalized Markdown across platforms

There is no single right answer. Start with the curl one-liner to prove the concept, then level up to a paged script or ThreadGrab as your archive grows.

Building a Portable Bluesky Archive

Whichever method you pick, keep the output structured so it stays useful. A simple convention beats a clever one:

bluesky-archive/
  profile.json
  2026-08-05-bsky-app.md
  2026-08-04-group-chats-100.md
  media/
    image-01.jpg

Name each file by the post’s date and slug, store the DID and capture time in a small profile.json alongside, and keep image links relative so the folder mirrors cleanly anywhere. Because everything is plain text plus files, the archive is portable across git repos, cloud drives, and local disks — no Bluesky server required to read it.

The Bottom Line

Bluesky’s atproto design is a genuine advantage for archiving. Public posts are one public API call away from clean Markdown — no auth, no paywall, no proprietary export tool. Whether you grab a single post with curl, page an entire timeline with a short script, or use ThreadGrab to keep X and Bluesky in one normalized library, the outcome is the same: content you actually control.

Posts disappear, handles change, algorithms move on. The Markdown you saved today is the copy you can still read next year — and the copy you can feed to any tool, LLM, or notes app that comes next.

Keep the posts that matter. ThreadGrab converts X threads and Bluesky posts to clean Markdown, downloads the media to your own disk, and keeps your cross-platform library portable — so the content you care about is never trapped on one network.

Try ThreadGrab

FAQ

Can I save Bluesky posts as Markdown?

Yes. Bluesky's public atproto API serves posts as plain JSON with no authentication, so you can fetch a post, thread, or full author feed and format it as Markdown on your own machine. Dedicated tools and browser extensions also render Bluesky posts as Markdown with a single click.

Does the Bluesky API require an account or token?

For public posts, no. The public atproto endpoint public.api.bsky.app lets you resolve a handle to a DID and fetch author feeds or individual posts without signing in. You only need an app password if you want to read private content or act on the account.

How do I get a Bluesky post as Markdown?

The simplest method is to replace the domain in the post URL or call the public API directly. For example, resolve a handle with com.atproto.identity.resolveHandle, then call app.bsky.feed.getAuthorFeed or getPosts and convert the record text to Markdown. A local script can automate this for every post you want to keep.

Is ThreadGrab compatible with Bluesky?

Yes. ThreadGrab is built around converting social content into clean Markdown and downloading linked media to your own storage. It supports the same workflow for Bluesky stays as it does for X threads and long-form posts, so you end up with a portable, searchable Markdown library across platforms.

Why save Bluesky posts as Markdown instead of screenshots?

Markdown is plain text, so it is searchable, versionable in git, indexable by an LLM, and readable in any notes app. A screenshot is an image: you cannot Ctrl+F it, diff it, or paste it into an AI workflow. Markdown preserves the text, structure, and links while staying portable and future-proof.