Skip to content

Releases

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 18:21
v0.3.0
8e251ab

This is a rewrite rather than a release, and it breaks nearly everything.
If you have scripts on v0.2.0, read this before upgrading.

mbasic is gone

Every version up to v0.2.0 read mbasic.facebook.com, the HTML-only mobile site, and scraped rows out of it.
That site has been shrinking for years and the pages fb depended on now serve a log-in redirect more often than they serve content.

v0.3.0 reads what a signed-out browser is actually served: the Relay store that www.facebook.com ships inline in the page as <script type="application/json" data-sjs> blocks.
The same data the site's own JavaScript renders from, extracted, stitched and parsed.

Every field on every record comes from a different place than it did. Nothing here is a rename.

Eight surfaces, two tiers

fb surfaces
fb tiers
fb routes

Eight places fb reads, from the Comet page and its GraphQL endpoint down to the picture redirect and the Pages directory, and each command declares which one answers it.

Tier 0 needs nothing at all and is where nearly everything lives: pages, profiles, posts, comments, all seven reaction types, photos with alt text, videos with captions and transcripts, events with RSVP counts, and a public group's shell.

Tier 1 is two cookies from a browser you are already signed into, and it buys exactly three things: the timeline past the first post, group discussions, and search.

fb auth import --c-user <id> --xs <token>

There is still no app id, no developer app and no API token anywhere in this tool.

What comes back is claims

fb edges nasa
fb graph nasa --depth 2 --budget 25
fb rdf nasa --format turtle

Nineteen predicates, stable fb://kind/id URIs, and every claim carrying the URL, the surface and the tier that asserted it.
fb rdf and fb export write it as schema.org with RDF-star provenance.

Every record says what it did not see

{"tier":0,"surfaces":["s1","s3"],"sources":["..."],
 "via":{"followers":"s1","likes":"s3","talking_about":"s3"},
 "missed":["comments past the first 20"],
 "fetched_at":"2026-07-29T18:02:57Z"}

via is there because likes and followers are different numbers from different surfaces, and treating them as one field was a bug in every previous version.
missed is there because a post with 532 comments read from a permalink that ships twenty should not look like a post with twenty comments.

Commands that went away

Gone Instead
fb seed fb crawl <seed>... takes references directly
fb crawl --from, stdin Seeds are arguments
fb db query fb query <sql>
fb db --db --store on crawl, db, query, export
fb discover <seed> fb graph walks; fb discover is now the Pages directory
fb archive writing Markdown fb archive writes one read whole: HTML, Relay payloads, headers, record
--surface mbasic|mobile --tier, which caps a tier or pins a surface
-j --workers Requests are serialised and paced with --rate
--since, --until -n and filtering downstream
--raw, --user-agent, --lang, -y fb archive covers what --raw was for

The store schema is new too: three tables, nodes, claims and reads, replacing one table per record type.
An old .db file will not open as a new one. Recrawl.

Commands that arrived

fb id, fb explain, fb surfaces, fb tiers, fb routes and fb fields answer questions about fb rather than about Facebook, and five of the six make no request at all.

fb edges, fb graph, fb rdf and fb export are the claim graph.

fb serve and fb mcp expose the same eighteen read operations over HTTP as NDJSON and over stdio as MCP tools, generated from the operations rather than written a second time.

Exit codes

Nine of them, and exit 4 is the one to handle: Facebook served the log-in page.
Sometimes that is a private thing and sometimes it is a refusal that lifts in about fifteen minutes. The message says which.

The throttle is per URL rather than per address, so when /nasa walls, /nasa/photos usually still answers.

Install

go install github.com/tamnd/facebook-cli/cmd/fb@latest
brew install --cask tamnd/tap/fb
docker run --rm -v ~/data/fb:/data ghcr.io/tamnd/fb:0.3.0 page nasa

Full documentation: https://facebook-cli.tamnd.com

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 15 Jun 13:00
v0.2.0
e5d344b

Changelog

Other

  • e5d344b: Add discover: a breadth-first walk of the Facebook graph (#5) (@tamnd)

v0.1.2

Choose a tag to compare

@github-actions github-actions released this 13 Jun 17:15
v0.1.2
fb729aa

fb reads public Facebook groups in full.

Highlights

  • fb group now returns a group's name, description, cover image, privacy, and URL, read from the same server-rendered page everything else uses.
  • A group feed lists its posts, and each post carries its id, author, date, reaction and comment counts, media, permalink, and text.
  • Comments on a group post come back through the normal post read, so a public group can be walked end to end.
fb group python -o json
fb group python --posts -n 20 -o jsonl
fb post <group-post-url> --comments -o jsonl

Member count is not part of the crawler surface, so it is only filled in when the group description spells it out. Private groups exit 4.

Install

brew install tamnd/tap/fb
scoop install fb
docker pull ghcr.io/tamnd/fb:0.1.2

Linux packages (deb/rpm/apk), prebuilt binaries for linux/macOS/windows/freebsd, CycloneDX SBOMs, and cosign signatures are attached below. Verify with the checksums.txt, .sig, and .pem assets.

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 13 Jun 16:30
v0.1.1
5c03bbe

This release changes how fb reaches Facebook and adds a command for mirroring a Page to disk.

Reading without a cookie

fb now reads Facebook as an anonymous web crawler. It presents a crawler user agent and reads the same server-rendered pages Facebook serves to search engines, so a public Page, profile, group, or post comes back as HTML with the text, counts, media, and a few preview comments baked in. There is no login, no cookie, and no browser to drive.

  • The cookie layer is gone: the --cookie and --cookie-file flags, the FACEBOOK_COOKIE environment variables, and the cookie request header.
  • fb whoami now reports the access mode and user agent instead of a session.
  • The trade-off is depth: a feed exposes the most recent posts rather than the full history, and a post carries a handful of preview comments rather than its whole thread. Private targets still exit 4.

Archiving

  • fb archive <page> mirrors a Page's recent feed to a browsable tree of Markdown: one file per post under <out>/<page>/YYYY/MM/, plus a generated README.md indexed by year and month with a stats table.
  • Re-running is incremental. A small index.json records what is on disk, so a second run fetches only new posts; --force overwrites. The index and each post are written as the crawl proceeds, so an interrupted run resumes cleanly.
  • Post slugs are transliterated to ASCII, Vietnamese diacritics included, so accented titles produce clean file names.

Fixes

  • Group post permalinks (groups/<id>/posts/<id>) are now recognized when walking a feed.

Install

Homebrew

brew install tamnd/tap/fb

Scoop (Windows)

scoop bucket add tamnd https://github.com/tamnd/scoop-bucket
scoop install fb

Container

docker run --rm ghcr.io/tamnd/fb:0.1.1 version

Debian / Ubuntu, Fedora / RHEL, Alpine
Download the .deb, .rpm, or .apk for your architecture from the assets below and install it with your package manager.

Binary
Download the archive for your platform from the assets below, extract, and put fb on your PATH.

Notes

  • Every archive ships a CycloneDX SBOM, and checksums.txt is signed with keyless cosign (checksums.txt.sig + checksums.txt.pem).

Documentation: https://facebook-cli.tamnd.com

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 13 Jun 15:06
v0.1.0
1995974

The first release of fb, a delightful, scriptable command line for Facebook. It turns facebook.com into typed, structured data from a single pure-Go binary with no runtime dependencies and no browser.

Highlights

  • Entities. Resolve any Page, profile, or group to a rich record, and stream its feed with --posts, bounded by --limit or by date.
  • Posts in depth. Pull a post with its full comment thread (--comments, --replies, with parent_id on every reply) and its reaction breakdown (--reactions, --list). Pagination walks to the end, so -n 0 takes everything.
  • Media. Stream photos, videos, and reels; resolve a single photo or video including playable sources with --streams; list public events.
  • Discovery. search across Facebook's surfaces by type, and fb id to classify any handle, id, or URL with no network call.
  • Datasets. seed a root into URLs, crawl them into records, and db query a local SQLite store with SQL.
  • Output. One formatter for every command: table, jsonl, json, csv, tsv, yaml, url, and raw, with --fields and --template.

Install

Homebrew

brew install tamnd/tap/fb

Scoop (Windows)

scoop bucket add tamnd https://github.com/tamnd/scoop-bucket
scoop install fb

Container

docker run --rm ghcr.io/tamnd/fb:0.1.0 version

Debian / Ubuntu, Fedora / RHEL, Alpine
Download the .deb, .rpm, or .apk for your architecture from the assets below and install it with your package manager.

Binary
Download the archive for your platform from the assets below, extract, and put fb on your PATH.

Notes

  • Facebook gates most reads behind a login. fb reads the no-JavaScript surface and is explicit about the wall: commands that need a session exit 4 with a hint. Supply a cookie with --cookie, --cookie-file, or FACEBOOK_COOKIE to unlock full data.
  • Every archive ships a CycloneDX SBOM, and checksums.txt is signed with keyless cosign (checksums.txt.sig + checksums.txt.pem).

Documentation: https://facebook-cli.tamnd.com