Metadata-Version: 2.5
Name: facebook-posts-details
Version: 0.1.0
Summary: Extract public data from Facebook post, reel, video, photo and profile URLs, logged out and without an API key.
Project-URL: Homepage, https://github.com/binarykernal/facebook-posts-details
Project-URL: Repository, https://github.com/binarykernal/facebook-posts-details
Project-URL: Issues, https://github.com/binarykernal/facebook-posts-details/issues
Project-URL: Changelog, https://github.com/binarykernal/facebook-posts-details/blob/main/CHANGELOG.md
Author: binarykernal
License: MIT License
        
        Copyright (c) 2026 Aman
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: cli,comments-count,data-extraction,engagement-metrics,facebook,facebook-api,facebook-graph-api,facebook-page,facebook-photo,facebook-post-details,facebook-post-scraper,facebook-posts,facebook-profile,facebook-reel-scraper,facebook-reels,facebook-scraper,facebook-story,facebook-video,facebook-video-url,fb,fb-scraper,followers-count,free-facebook-api,graph-api-alternative,likes-count,metadata,no-api-key,no-login,opengraph,osint,profile-scraper,public-data,reels,scraper,scraping,share-count,social-media,social-media-analytics,views-count,web-scraping
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Requires-Dist: tls-client3>=1.1.2
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: twine>=4.0; extra == 'dev'
Description-Content-Type: text/markdown

# facebook-posts-details

[![PyPI](https://img.shields.io/pypi/v/facebook-posts-details)](https://pypi.org/project/facebook-posts-details/)
[![Python](https://img.shields.io/pypi/pyversions/facebook-posts-details)](https://pypi.org/project/facebook-posts-details/)
[![License](https://img.shields.io/pypi/l/facebook-posts-details)](https://github.com/binarykernal/facebook-posts-details/blob/main/LICENSE)
[![Downloads](https://img.shields.io/pypi/dm/facebook-posts-details)](https://pypi.org/project/facebook-posts-details/)

Extract public data from Facebook post, reel, video, photo, story and profile
URLs. No login, no API key, no browser.

Get the **likes, comments, shares, views, text, images, video URL and author**
of any public Facebook post or reel — and the **name, bio, followers, category
and profile picture** of any public profile or page — with one function call.

A lightweight alternative to the Facebook Graph API when all you need is public
data: no app review, no access token, no rate-limit dashboard.

## Install

```bash
pip install facebook-posts-details
```

That is all you need — `pip` pulls the dependencies automatically.

## Quick start

```python
import facebook_posts_details as fb

post = fb.post_data("https://www.facebook.com/reel/941309212326403")

post["likes_count"]      # 104378
post["comments_count"]   # 292
post["post_text"]        # '😎😎…..'
post["video_url"]        # 'https://video-del3-1.xx.fbcdn.net/...'
```

```python
profile = fb.profile_data("https://www.facebook.com/zuck")

profile["name"]          # 'Mark Zuckerberg'
profile["followers"]     # 121000000
profile["bio"]           # 'Bringing the world closer together.'
```

Let the URL pick the right one for you:

```python
data = fb.scrape(url)
```

## Functions

| Call | Returns |
|---|---|
| `fb.post_data(url)` | a post, reel, video, photo or story |
| `fb.profile_data(url)` | a profile or page |
| `fb.profile_about(url)` | every "About" tab, merged into one dict |
| `fb.scrape(url)` | whichever of the above fits the URL |

All of them return `None` if the page could not be read, so one bad URL never
stops a batch:

```python
for url in urls:
    data = fb.scrape(url)
    if data is None:
        continue
    ...
```

### Also fetch the author's profile

```python
url = ("https://www.facebook.com/middlestumpcric/posts/"
       "pfbid02UgzMPp1nyS1gzQt9RNBG1AaiqJ1eZWAVSbt5rHkTw7jod5JLNCAD8USh1wkxVYGPl")

post = fb.post_data(url, with_profile=True)

post["user"]["name"]         # 'Middle Stump Cricket'
post["user"]["followers"]    # 1400000
```

Costs one extra request. Without the flag, `post["user"]` is `{}`.

Not every author publishes every field — many personal profiles hide their
follower count, in which case `post["user"]["followers"]` is `None` while
`post["user"]["name"]` still works.

## What you get back

Every dict has a **fixed set of keys**. A field that could not be found is
`None` (or an empty list/dict), never missing — so `post["likes_count"]` never
raises a `KeyError`.

The examples below are illustrative and come from different posts: no single
post has every field. `views_count` appears on some videos and reels,
`transcript` only on videos that have one, `video_url` only where there is a
video.

### `post_data(url)`

| Key | Example |
|---|---|
| `url` | `'https://www.facebook.com/reel/941309212326403'` |
| `post_id` | `'941309212326403'` |
| `post_type` | `'reel'` — one of `post`, `reel`, `video`, `photo`, `story` |
| `post_text` | `'😎😎…..'` |
| `publish_date` | `1786517734` (unix seconds) |
| `publish_date_iso` | `'2026-08-12T06:55:34+00:00'` |
| `likes_count` | `104378` |
| `comments_count` | `292` |
| `share_count` | `1000` |
| `views_count` | `9400000` |
| `post_images` | `[{"url": "https://...", "caption": "May be an image of ..."}]` |
| `video_url` | `'https://video-del3-1.xx.fbcdn.net/...'` |
| `transcript` | `'What does Rahul Gandhi do every...'` |
| `name` | `'Sucha Shooter'` |
| `profile_url` | `'https://www.facebook.com/sucha.shooter.965'` |
| `profile_image` | `'https://...'` |
| `user` | `{}`, or the author's profile with `with_profile=True` |

### `profile_data(url)`

| Key | Example |
|---|---|
| `url` | `'https://www.facebook.com/zuck'` |
| `profile_id` | `'zuck'` (username, or the numeric id) |
| `profile_id_number` | `'4'` |
| `name` | `'Mark Zuckerberg'` |
| `bio` | `'Bringing the world closer together.'` |
| `category_name` | `'Public figure'` |
| `gender` | `'MALE'` |
| `is_verified` | `True` |
| `followers` | `121000000` |
| `followers_text` | `'121M followers'` (as displayed) |
| `following` | `None` |
| `following_text` | `None` |
| `profile_pic` | `'https://...'` |
| `cover_photo` | `'https://...'` |
| `about_section` | `{"intro": {"website": [{"value": "x.com", "url": "https://..."}]}}` |
| `photos` | `['https://...', ...]` |

Good to know:

- Counts are always `int` or `None` — never a string like `"1.4M"`. The
  displayed form is kept in `followers_text` / `following_text`.
- Reels only publish a **rounded** share count, so `share_count` on a reel is
  rounded the same way the page shows it (`1000` for "1K"). Likes and comments
  are exact.
- `post_images` entries are always `{"url", "caption"}`. `caption` is `None`
  when Facebook has no real description for the image.

## Command line

```bash
facebook-posts-details <url>                    # prints JSON
facebook-posts-details <url1> <url2> ...        # several at once
facebook-posts-details <url> --summary          # short readable output
facebook-posts-details <url> --with-profile     # include the author's profile
facebook-posts-details <url> --proxy http://user:pass@host:port
facebook-posts-details <url> --kind profile     # skip URL auto-detection
facebook-posts-details <url> --timeout 60 --retries 3
facebook-posts-details <url> -v                 # show progress on stderr
```

JSON goes to stdout and messages go to stderr, so this works cleanly:

```bash
facebook-posts-details <url> | jq .likes_count
```

Exit status is `1` if any URL returned nothing.

## Proxies

No proxy is used unless you pass one. Proxies work the same way as in
`requests`, and are accepted by `post_data`, `profile_data`, `profile_about`,
`scrape` and the CLI (`--proxy`).

```python
# one URL, used for both http and https
fb.post_data(url, proxy="http://user:pass@1.2.3.4:8080")
fb.post_data(url, proxy="socks5://1.2.3.4:1080")

# a scheme-less value is treated as http
fb.post_data(url, proxy="1.2.3.4:8080")

# or a requests-style mapping
fb.post_data(url, proxy={"http": "http://1.2.3.4:8080",
                         "https": "http://1.2.3.4:8080"})
```

## Examples

### Without a proxy

The normal flow — nothing to configure.

```python
import facebook_posts_details as fb

url = "https://www.facebook.com/reel/941309212326403"
post = fb.post_data(url)

if post is None:
    print("could not read that page")
else:
    print(post["name"], "-", post["post_text"])
    print(post["likes_count"], "likes,", post["comments_count"], "comments")
    for image in post["post_images"]:
        print(image["url"])
```

### With a proxy

Pass `proxy=` to the same call. Everything else is identical.

```python
import facebook_posts_details as fb

PROXY = "http://user:pass@1.2.3.4:8080"

url = "https://www.facebook.com/reel/941309212326403"
post = fb.post_data(url, proxy=PROXY)

print(post["likes_count"], post["comments_count"])
```

### Several URLs, with a different proxy each time

Rotation is up to you — just pass a different proxy per call.

```python
import itertools
import facebook_posts_details as fb

PROXIES = [
    "http://user:pass@1.2.3.4:8080",
    "http://user:pass@5.6.7.8:8080",
]
URLS = [
    "https://www.facebook.com/reel/941309212326403",
    "https://www.facebook.com/zuck",
]

pool = itertools.cycle(PROXIES)

for url in URLS:
    data = fb.scrape(url, proxy=next(pool))
    if data is None:
        print("failed:", url)
        continue
    print(data.get("name"), data.get("likes_count") or data.get("followers"))
```

To run the same loop **without** proxies, drop the argument:

```python
    data = fb.scrape(url)
```

### A post plus its author, saved to JSON

```python
import json
import facebook_posts_details as fb

url = ("https://www.facebook.com/middlestumpcric/posts/"
       "pfbid02UgzMPp1nyS1gzQt9RNBG1AaiqJ1eZWAVSbt5rHkTw7jod5JLNCAD8USh1wkxVYGPl")

post = fb.post_data(
    url,
    with_profile=True,          # also fetch the author's profile
    proxy=None,                 # or "http://user:pass@host:port"
    timeout=60,
    retries=3,
)

with open("post.json", "w", encoding="utf-8") as f:
    json.dump(post, f, indent=2, ensure_ascii=False)

print(post["name"], "->", post["user"]["followers"], "followers")
# Middle Stump Cricket -> 1400000 followers
```

### Command line, with and without a proxy

```bash
# without
facebook-posts-details "https://www.facebook.com/reel/941309212326403" --summary

# with
facebook-posts-details "https://www.facebook.com/reel/941309212326403" \
    --proxy http://user:pass@1.2.3.4:8080 --summary
```

## Timeouts and retries

```python
fb.post_data(url, timeout=60, retries=3)
```

Or change the defaults for every call:

```python
fb.config.DEFAULT_TIMEOUT = 60     # default 30 seconds
fb.config.DEFAULT_RETRIES = 3      # default 2 attempts
```

## Errors

Nothing is printed to stdout. Failures are logged and the call returns `None`.
To see why something failed:

```python
import logging
logging.basicConfig(level=logging.DEBUG)
```

Exceptions are only raised for programming mistakes, e.g. `fb.InvalidURL` if you
pass a `kind` that does not exist.

## Requirements

- Python 3.8 or newer
- [`tls-client3`](https://pypi.org/project/tls-client3/) — installed
  automatically by `pip`

## Legal

Reads only publicly accessible pages, exactly as a logged-out browser would.
You are responsible for complying with Facebook's Terms of Service and any law
that applies to you. Provided as-is under the MIT licence.
