Repository navigation
Releases
v0.3.0
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
v0.1.2
fb reads public Facebook groups in full.
Highlights
fb groupnow 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 jsonlMember 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.2Linux 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
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
--cookieand--cookie-fileflags, theFACEBOOK_COOKIEenvironment variables, and the cookie request header. fb whoaminow 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 generatedREADME.mdindexed by year and month with a stats table.- Re-running is incremental. A small
index.jsonrecords what is on disk, so a second run fetches only new posts;--forceoverwrites. 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/fbScoop (Windows)
scoop bucket add tamnd https://github.com/tamnd/scoop-bucket
scoop install fbContainer
docker run --rm ghcr.io/tamnd/fb:0.1.1 versionDebian / 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.txtis signed with keyless cosign (checksums.txt.sig+checksums.txt.pem).
Documentation: https://facebook-cli.tamnd.com
v0.1.0
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--limitor by date. - Posts in depth. Pull a post with its full comment thread (
--comments,--replies, withparent_idon every reply) and its reaction breakdown (--reactions,--list). Pagination walks to the end, so-n 0takes everything. - Media. Stream photos, videos, and reels; resolve a single photo or video including playable sources with
--streams; list public events. - Discovery.
searchacross Facebook's surfaces by type, andfb idto classify any handle, id, or URL with no network call. - Datasets.
seeda root into URLs,crawlthem into records, anddb querya local SQLite store with SQL. - Output. One formatter for every command:
table,jsonl,json,csv,tsv,yaml,url, andraw, with--fieldsand--template.
Install
Homebrew
brew install tamnd/tap/fbScoop (Windows)
scoop bucket add tamnd https://github.com/tamnd/scoop-bucket
scoop install fbContainer
docker run --rm ghcr.io/tamnd/fb:0.1.0 versionDebian / 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
4with a hint. Supply a cookie with--cookie,--cookie-file, orFACEBOOK_COOKIEto unlock full data. - Every archive ships a CycloneDX SBOM, and
checksums.txtis signed with keyless cosign (checksums.txt.sig+checksums.txt.pem).
Documentation: https://facebook-cli.tamnd.com