The modern alternative to kevinzg/facebook-scraper
and arc298/instagram-scraper —
real-time Facebook + Instagram data via REST, no OAuth dance, no app
review, no scraper maintenance. Powered by hosted infrastructure at
socialapis.io.
pip install socialapis-sdkfrom socialapis import Facebook, Instagram
fb = Facebook(api_token="...")
page = fb.get_page_info("EngenSA")
print(page.title, page.followers_count, page.category)
ig = Instagram(api_token="...")
profile = ig.get_profile_details("instagram")
print(profile.username, profile.followers_count)Get a free API token → — 200 calls/month, no credit card
If your code currently uses kevinzg/facebook-scraper
or arc298/instagram-scraper, the migration is
literally one import line:
# Before — kevinzg/facebook-scraper (abandoned since 2022)
from facebook_scraper import get_page_info, get_posts
# After — socialapis (alias preserves the name)
from socialapis import FacebookScraper
fb = FacebookScraper(api_token="...")
fb.get_page_info("EngenSA")
fb.get_page_posts("EngenSA")
# Same for Instagram
from socialapis import InstagramScraper
ig = InstagramScraper(api_token="...")FacebookScraper and InstagramScraper are exact aliases of Facebook and Instagram —
identical behavior, identical type signatures. They exist purely to keep the import
line greppable during migration.
kevinzg/facebook-scraper has 9.5k+ GitHub stars and was the default Python library for
scraping Facebook for years. It's been abandoned since 2022. arc298/instagram-scraper
(8.5k stars) is in similar shape. Every Meta DOM change breaks them; fixes pile up in
unmerged PRs; downloads drift to forks that fix one bug and break two.
This SDK is the drop-in successor:
kevinzg/facebook-scraper (2018-era) |
socialapis (2026) |
|
|---|---|---|
| Maintenance | Abandoned 2022 | Active; we run prod for 7M+ calls/mo |
| Reliability | Breaks on every Meta HTML change | Hosted backend; we absorb breakage |
| Type hints | None | Strict throughout |
| Async support | No | Facebook + AsyncFacebook classes |
| HTTP client | requests |
httpx |
| Validation | Manual dict parsing | Pydantic v2 models |
| Auth | None (scrapes anonymously) | Single x-api-token header |
| Pagination | Generator with edge-case bugs | Cursor-based; API decides page size |
| Error handling | Generic exceptions | Typed hierarchy (RateLimitError, etc.) |
| CI / tests | Manual against live FB | Recorded HTTP fixtures, Python 3.10–3.13 |
| Coverage | Page posts, group posts only | 45+ endpoints across FB + IG |
The trade-off: instead of running a scraper yourself, you make a REST call to our hosted API. 200 calls/month free, no credit card. Paid plans start at $4.99/mo for 1,500 calls.
Pages
get_page_id(page)— resolve a URL/slug to numeric IDget_page_info(page)→PageInfo— page metadata (typed model)get_page_posts(page)— recent postsget_page_reels(page)— short-form videosget_page_videos(page)— long-form videos
Groups
get_group_id(group)get_group_details(group)→GroupInfo(typed model)get_group_metadata(group)— lightweight metadata onlyget_group_posts(group)get_group_videos(group_id)
Posts
get_post_id(post)— extract numeric ID from URLget_post_details(post)— reactions, media, authorget_post_details_extended(post)— + views, video URLs, author verificationget_post_comments(post)— passinclude_reply_info="true"for reply cursorsget_comment_replies(comment_feedback_id, expansion_token)get_post_attachments(post_id)get_video_post_details(video_id)
Search
search_pages(query)— supportslocation_idfor geo-filteringsearch_people(query)search_locations(query)— returns location IDs for use in other endpointssearch_posts(query)— supports recency + location filterssearch_videos(query)
Meta Ads Library
get_ads_countries()— supported countriessearch_ads(query)— by keyword + country + activeStatusget_ads_page_details(page_id)get_ad_archive_details(ad_archive_id, page_id)search_ads_by_keywords(query)
Marketplace
search_marketplace(query)— supports lat/lng, price, condition filtersget_listing_details(listing_id)get_seller_details(seller_id)get_marketplace_categories()get_city_coordinates(city)— for lat/lng filteringsearch_vehicles()— bedrooms-style filters; lat/lng requiredsearch_rentals()
Media
download_media(url)— resolve to direct downloadable URL
Profiles
get_user_id(profile)— username/URL → numeric user_idget_profile_details(username)→ProfileInfo(typed model)get_profile_posts(username)get_profile_reels(user_id)get_profile_highlights(user_id)get_highlight_details(highlight_id)
Posts
get_post_id(post)— extract shortcode from any post URLget_post_details(shortcode)
Reels
get_reels_feed()— trending feedget_reels_by_audio(audio_id)— all reels using a specific track
Search + Locations
search(keyword)— popular results (users / hashtags / places)get_location_posts(location_id)— top or recentget_nearby_locations(location_id)
Free calls — don't consume credits.
get_usage()— credit balance, plan, billing periodget_top_ups()— auto top-up settings + historyget_limits()— rate limit, concurrent-task cap, allowed packages
Every endpoint that returns a list lets the API decide page size. To paginate, take the cursor from the response body and pass it back as a kwarg on the next call:
fb = Facebook(api_token="...")
# First page
result = fb.get_page_posts("EngenSA")
posts = result["posts"]
cursor = result.get("next_cursor") # actual key varies by endpoint — check docs
# Next page
while cursor:
result = fb.get_page_posts("EngenSA", cursor=cursor)
posts.extend(result["posts"])
cursor = result.get("next_cursor")We deliberately don't impose a uniform limit=N parameter — it would drift from the
API's actual semantics. The API's response always tells you whether there's more.
Every method accepts arbitrary kwargs and forwards them as query params. If the API adds a new filter tomorrow, you can use it today — no SDK release required:
fb.search_ads("fitness", country="US", activeStatus="Active", some_new_filter="x")
# Sends: ?query=fitness&country=US&activeStatus=Active&some_new_filter=ximport time
from socialapis import (
Facebook,
AuthenticationError, # 401 — bad token
InsufficientCreditsError, # 402 — out of credits
RateLimitError, # 429 — slow down
BadRequestError, # 4xx — bad input
APIServerError, # 5xx — retry safely
APIConnectionError, # network — retry with backoff
)
fb = Facebook(api_token="...")
try:
page = fb.get_page_info("EngenSA")
except RateLimitError as exc:
time.sleep(exc.retry_after_seconds or 5)
page = fb.get_page_info("EngenSA")
except InsufficientCreditsError:
print("Out of credits. Upgrade at https://socialapis.io/pricing")
except AuthenticationError:
print("Bad token. Get one at https://socialapis.io/auth/signup")Every typed exception carries .status_code, .request_id, and .body for debugging.
The request_id is the same value our backend logs — paste it into a support email
and we can find the exact call.
Same method surface; methods are coroutines.
import asyncio
from socialapis import AsyncFacebook
async def main():
async with AsyncFacebook(api_token="...") as fb:
pages = await asyncio.gather(*[
fb.get_page_info(slug)
for slug in ["EngenSA", "Microsoft", "GitHub"]
])
for page in pages:
print(page.title, page.followers_count)
asyncio.run(main())| Tier | Calls / month | Price |
|---|---|---|
| Free | 200 | $0 |
| Pro | 1,500 | $4.99 |
| Ultra | 30,000 | $49 |
| Mega | 120,000 | $179 |
| Enterprise | Custom | Contact us |
One credit per successful response. Failed calls (4xx caused by bad input) don't consume credits.
- JavaScript / TypeScript — coming soon. Notify me →
- PHP — coming soon. Notify me →
- Go — coming soon. Notify me →
- Any language right now: hit the REST API directly with
curl/fetch/requests. Docs at docs.socialapis.io.
- Docs: docs.socialapis.io
- Issues: github.com/SocialAPIsHub/socialapis-python/issues
- Email: [email protected]
- Telegram (fastest): t.me/socialapis
MIT — see LICENSE.