Facebook Page Photo Gallery & Engagement Scraper
Collect photos from public Facebook pages, profiles, and albums with full-resolution image URLs, alt text, captions, reaction breakdowns, share and comment counts, and tagged people.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | required | Facebook page, profile, photos tab, tagged tab, albums tab, album, or single-photo URL. Bare handles such as nasa are accepted too. |
resultsLimit | integer | optional | Global cap across all Start URLs. The actor keeps paginating until this many photos are collected or the source runs out. |
newerThan | string | optional | Only include photos posted on or after this date (YYYY-MM-DD). Applied by Facebook for page and tagged tabs. For albums the actor reads each photo timestamp (even with includeDetails off) and applies the bound; rows stay grid-only. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | required | Facebook page, profile, photos tab, tagged tab, albums tab, album, or single-photo URL. Bare handles such as nasa are accepted too. |
resultsLimit | integer | optional | Global cap across all Start URLs. The actor keeps paginating until this many photos are collected or the source runs out. |
newerThan | string | optional | Only include photos posted on or after this date (YYYY-MM-DD). Applied by Facebook for page and tagged tabs. For albums the actor reads each photo timestamp (even with includeDetails off) and applies the bound; rows stay grid-only. |
olderThan | string | optional | Only include photos posted on or before this date (YYYY-MM-DD). Applied by Facebook for page and tagged tabs. For albums the actor reads each photo timestamp (even with includeDetails off) and applies the bound; rows stay grid-only. |
photoTab | string | optional | For page and profile URLs: photos uploaded by the page, or photos where the page is tagged by others. Ignored for album and photo URLs. |
includeAlbums | boolean | optional | For page and profile URLs, also walk every album listed on the page's Albums tab and collect its photos. A photos_albums URL implies this automatically. |
includeDetails | boolean | optional | Open each photo to add its caption, timestamp, reactions, comments, shares, tagged people, owner, album, and post link. Turn off for a fast grid-only export; album date bounds still read only the timestamp needed for filtering. |
storeImages | boolean | optional | Copy each full-size image and thumbnail to Apify storage and return non-expiring URLs. The signed Facebook originals and their expiry are kept in the record. |
Download the published input schema
View complete JSON
{
"description": "Collect public Facebook photos from a page, profile, album, photo permalink, tagged collection, or albums list.",
"properties": {
"includeAlbums": {
"default": false,
"description": "For page and profile URLs, also walk every album listed on the page's Albums tab and collect its photos. A photos_albums URL implies this automatically.",
"prefill": false,
"title": "Also collect albums",
"type": "boolean"
},
"includeDetails": {
"default": true,
"description": "Open each photo to add its caption, timestamp, reactions, comments, shares, tagged people, owner, album, and post link. Turn off for a fast grid-only export; album date bounds still read only the timestamp needed for filtering.",
"prefill": true,
"title": "Include photo details",
"type": "boolean"
},
"newerThan": {
"description": "Only include photos posted on or after this date (YYYY-MM-DD). Applied by Facebook for page and tagged tabs. For albums the actor reads each photo timestamp (even with includeDetails off) and applies the bound; rows stay grid-only.",
"editor": "textfield",
"prefill": "",
"title": "Only photos newer than",
"type": "string"
},
"olderThan": {
"description": "Only include photos posted on or before this date (YYYY-MM-DD). Applied by Facebook for page and tagged tabs. For albums the actor reads each photo timestamp (even with includeDetails off) and applies the bound; rows stay grid-only.",
"editor": "textfield",
"prefill": "",
"title": "Only photos older than",
"type": "string"
},
"photoTab": {
"default": "uploaded",
"description": "For page and profile URLs: photos uploaded by the page, or photos where the page is tagged by others. Ignored for album and photo URLs.",
"editor": "select",
"enum": [
"uploaded",
"tagged"
],
"enumTitles": [
"Photos uploaded by the page",
"Photos the page is tagged in"
],
"prefill": "uploaded",
"title": "Photo tab",
"type": "string"
},
"resultsLimit": {
"default": 100,
"description": "Global cap across all Start URLs. The actor keeps paginating until this many photos are collected or the source runs out.",
"editor": "number",
"minimum": 1,
"prefill": 1,
"title": "Maximum number of photos",
"type": "integer"
},
"startUrls": {
"description": "Facebook page, profile, photos tab, tagged tab, albums tab, album, or single-photo URL. Bare handles such as nasa are accepted too.",
"editor": "requestListSources",
"items": {
"properties": {
"url": {
"description": "Facebook page, profile, photo album, photos tab, tagged tab, or photo permalink.",
"title": "URL of a web page",
"type": "string"
}
},
"required": [
"url"
],
"type": "object"
},
"prefill": [
{
"url": "https://www.facebook.com/humansofnewyork/photos"
}
],
"title": "Facebook URLs",
"type": "array"
},
"storeImages": {
"default": true,
"description": "Copy each full-size image and thumbnail to Apify storage and return non-expiring URLs. The signed Facebook originals and their expiry are kept in the record.",
"prefill": true,
"title": "Store images permanently",
"type": "boolean"
}
},
"required": [
"startUrls"
],
"schemaVersion": 1,
"title": "Facebook Photos Scraper Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
photos | string | optional | Dataset of public Facebook photos with engagement and context fields. Empty fields are omitted. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "One row per public Facebook photo with image and thumbnail links, caption, timestamp, reaction breakdown, comments, shares, tagged people, owner/page/album/post context, and source metadata.",
"properties": {
"photos": {
"description": "Dataset of public Facebook photos with engagement and context fields. Empty fields are omitted.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Facebook Photo Data",
"type": "string"
}
},
"title": "Facebook Page Photo Gallery & Engagement Scraper Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"title": "Facebook Photos Data",
"views": {
"detailed": {
"display": {
"component": "table",
"properties": {
"albumId": {
"format": "text",
"label": "Album ID"
},
"albumName": {
"format": "text",
"label": "Album"
},
"altText": {
"format": "text",
"label": "Alt text"
},
"angryCount": {
"format": "number",
"label": "Angry"
},
"authorName": {
"format": "text",
"label": "Author"
},
"authorUrl": {
"format": "link",
"label": "Author URL"
},
"caption": {
"format": "text",
"label": "Caption"
},
"careCount": {
"format": "number",
"label": "Care"
},
"commentsCount": {
"format": "number",
"label": "Comments"
},
"createdTime": {
"format": "number",
"label": "Created (Unix)"
},
"detailsStatus": {
"format": "text",
"label": "Details"
},
"hahaCount": {
"format": "number",
"label": "Haha"
},
"imageExpiresAt": {
"format": "text",
"label": "Image expires"
},
"imageHeight": {
"format": "number",
"label": "Height"
},
"imageUrl": {
"format": "link",
"label": "Image"
},
"imageUrlOriginal": {
"format": "link",
"label": "Image (original)"
},
"imageWidth": {
"format": "number",
"label": "Width"
},
"inputUrl": {
"format": "link",
"label": "Input URL"
},
"likeCount": {
"format": "number",
"label": "Like"
},
"likesCount": {
"format": "number",
"label": "Reactions total"
},
"loveCount": {
"format": "number",
"label": "Love"
},
"ocrText": {
"format": "text",
"label": "OCR text (alt text)"
},
"pageFollowers": {
"format": "number",
"label": "Page followers"
},
"pageId": {
"format": "text",
"label": "Page ID"
},
"pageName": {
"format": "text",
"label": "Page"
},
"pageUrl": {
"format": "link",
"label": "Page URL"
},
"photoId": {
"format": "text",
"label": "Photo ID"
},
"photoUrl": {
"format": "link",
"label": "Photo URL"
},
"postId": {
"format": "text",
"label": "Post ID"
},
"postUrl": {
"format": "link",
"label": "Post URL"
},
"reactionsByType": {
"format": "object",
"label": "Reactions by type"
},
"reactionsTotal": {
"format": "number",
"label": "Reactions"
},
"sadCount": {
"format": "number",
"label": "Sad"
},
"scrapedAt": {
"format": "text",
"label": "Scraped at"
},
"shareCount": {
"format": "number",
"label": "Shares"
},
"sourceType": {
"format": "text",
"label": "Source"
},
"taggedPeople": {
"format": "array",
"label": "Tagged people"
},
"thumbnailUrl": {
"format": "link",
"label": "Thumbnail"
},
"thumbnailUrlOriginal": {
"format": "link",
"label": "Thumbnail (original)"
},
"timestamp": {
"format": "text",
"label": "Posted"
},
"uploaderId": {
"format": "text",
"label": "Uploader ID"
},
"uploaderName": {
"format": "text",
"label": "Uploader"
},
"uploaderUrl": {
"format": "link",
"label": "Uploader URL"
},
"wowCount": {
"format": "number",
"label": "Wow"
}
}
},
"title": "Detailed",
"transformation": {
"fields": [
"photoId",
"photoUrl",
"imageUrl",
"imageUrlOriginal",
"imageExpiresAt",
"imageWidth",
"imageHeight",
"thumbnailUrl",
"thumbnailUrlOriginal",
"caption",
"altText",
"ocrText",
"createdTime",
"timestamp",
"likesCount",
"likeCount",
"loveCount",
"careCount",
"hahaCount",
"wowCount",
"sadCount",
"angryCount",
"reactionsByType",
"reactionsTotal",
"commentsCount",
"shareCount",
"taggedPeople",
"authorName",
"authorUrl",
"uploaderId",
"uploaderName",
"uploaderUrl",
"pageId",
"pageName",
"pageUrl",
"pageFollowers",
"albumId",
"albumName",
"postId",
"postUrl",
"sourceType",
"detailsStatus",
"inputUrl",
"scrapedAt"
]
}
},
"overview": {
"display": {
"component": "table",
"properties": {
"caption": {
"format": "text",
"label": "Caption"
},
"commentsCount": {
"format": "number",
"label": "Comments"
},
"imageUrl": {
"format": "link",
"label": "Image"
},
"pageName": {
"format": "text",
"label": "Page"
},
"photoId": {
"format": "text",
"label": "Photo ID"
},
"photoUrl": {
"format": "link",
"label": "Photo URL"
},
"reactionsTotal": {
"format": "number",
"label": "Reactions"
},
"shareCount": {
"format": "number",
"label": "Shares"
},
"thumbnailUrl": {
"format": "link",
"label": "Thumbnail"
},
"timestamp": {
"format": "text",
"label": "Posted"
}
}
},
"title": "Overview",
"transformation": {
"fields": [
"photoId",
"photoUrl",
"imageUrl",
"thumbnailUrl",
"caption",
"pageName",
"timestamp",
"reactionsTotal",
"commentsCount",
"shareCount"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
Facebook Page Photo Gallery & Engagement Scraper
Collect public Facebook photos from pages, profiles, albums, tagged collections, and single-photo links. Each row carries the full-size image and thumbnail, caption, upload time, reaction breakdown, comment and share counts, tagged people, owner/page context, album, and post link. No login, cookies, browser, or proxy setup.
What this actor does
- Walks a page or profile's Photos tab or its Tagged tab; add both URLs to collect both
- Expands albums, a page's Albums list, or a direct album URL into its photos
- Accepts single-photo permalinks and photo paths
- Follows Facebook's cursor pagination 8 photos at a time, de-duplicates, and keeps going past the first 8-photo batch
- Applies
newerThan/olderThanon Facebook's side for page and tagged tabs (Unix-second query arguments) - Opens each photo for caption, timestamp, reactions (7 types), comments, shares, tagged people, album, post, and owner
- Copies images to durable storage by default and keeps the signed original plus its expiry
Input
| Field | Type | Default | Description |
|---|---|---|---|
startUrls |
array | – | Page, profile, /photos, /photos_by, /photos_stream, /photos_of, /photos_albums, album, photo permalink, or bare handle |
resultsLimit |
integer | 100 |
Global cap across all Start URLs |
newerThan |
string | – | Keep photos posted on or after this YYYY-MM-DD |
olderThan |
string | – | Keep photos posted on or before this YYYY-MM-DD |
photoTab |
select | uploaded |
For page/profile URLs: uploaded or tagged |
includeAlbums |
boolean | false |
Also walk every album on a page/profile |
includeDetails |
boolean | true |
Open each photo; turn off for a fast grid-only export. Album date bounds still read only the timestamp needed for filtering |
storeImages |
boolean | true |
Store permanent image and thumbnail copies in Apify storage |
proxyConfiguration is no longer an input. The actor selects its own connection path; old saved tasks that still send the key are accepted and the key is ignored.
Examples
A page's recent photos:
{
"startUrls": [{"url": "https://www.facebook.com/humansofnewyork/photos"}],
"resultsLimit": 50
}
Tagged photos of a page:
{
"startUrls": [{"url": "https://www.facebook.com/nike"}],
"photoTab": "tagged",
"resultsLimit": 100
}
Every album on a page:
{
"startUrls": [{"url": "https://www.facebook.com/nasa/photos_albums"}],
"resultsLimit": 300
}
A date window:
{
"startUrls": [{"url": "https://www.facebook.com/nationalgeographic/photos"}],
"newerThan": "2025-01-01",
"olderThan": "2025-12-31",
"resultsLimit": 200
}
A single photo:
{
"startUrls": [{"url": "https://www.facebook.com/photo/?fbid=1547667900257527"}]
}
Output per photo
Legacy fields keep their names: photoId, photoUrl, imageUrl, caption, altText, timestamp, likesCount, commentsCount, reactionsByType, reactionsTotal, shareCount, taggedPeople, authorName, authorUrl, inputUrl, scrapedAt.
Added fields include imageUrlOriginal, imageExpiresAt, imageWidth, imageHeight, thumbnailUrl, thumbnailUrlOriginal, ocrText (Facebook's accessibility caption, not a second OCR pass), createdTime, likeCount…angryCount, uploaderId/Name/Url, pageId/Name/Url/Followers, albumId, albumName, postId, postUrl, sourceType, and detailsStatus.
likesCount keeps its old meaning (total reactions); likeCount is the LIKE reaction only. Empty values are omitted. A photo without a caption simply has no caption key; a zero count is kept when Facebook reports zero. With includeDetails off, album rows have only the grid thumbnail and thumbnail dimensions; imageUrl requires details. Album date bounds still work in this mode: the actor reads just the timestamp and keeps the row grid-only.
Pagination and limits
Facebook returns 8 photos per collection request. The actor follows the cursor until the source ends, resultsLimit is reached, three consecutive requests add no new ids, or a duplicate-heavy safety budget is hit. A collection also stops after 2,000 pages (about 16,000 photos) as a safety cap. When a target stops early, the status message notes the shortfall; the log may not name the exact stop cause. No 1,000-photo or 5,000-photo artificial cap is applied.
Image links and storage
Facebook's CDN links are signed and expire. With storeImages on (default), each image and thumbnail is copied to Apify storage; when a copy succeeds, imageUrl / thumbnailUrl become permanent public links. imageUrlOriginal and imageExpiresAt preserve the source. If a copy fails, the signed source link is kept and the run continues. With storeImages off, the signed links are returned as-is and the expiry is still recorded.
What this actor cannot collect
- Photo location — Facebook does not expose a place/location for photos on these surfaces
- Multiple image resolutions — the photo viewer exposes one image URI plus width/height
- Private profiles, login-only pages, and groups — only what a logged-out visitor sees
- Comment bodies or reactor identities — counts only; use the comments actor for threads
- Videos and Reels — the Photos collections contain photos; use the posts/reels actors for video
- One row per album — album metadata is attached to its photos; an albums-list URL expands into photos
FAQ
Do I need a Facebook account? No. The actor reads only public, logged-out content and never asks for cookies or credentials.
Why did a target return no photos? It may not exist, be private, or hide its Photos tab from logged-out visitors. The final status message names failed targets.
Why are some captions or owner names missing? Facebook genuinely has no caption for some photos, and occasionally omits the uploader's display name. Empty fields are omitted rather than filled with guesses.
What does detailsStatus mean?
Complete means the photo page was read; GridOnly means details were turned off; Unavailable means the photo page could not be read, so grid fields were kept.
Why do some images need a slower request? Gallery pages are free to read. Individual photo pages are often served as a stripped page to datacenter IPs, so the actor automatically retries the detail request through a residential connection. The gallery does not use that path.
How many photos can I get?
As many as the page exposes to a logged-out visitor, up to resultsLimit. A collection stops after 2,000 pages (about 16,000 photos) even if more remain. Thousands of photos have been walked from one page; duplicate-heavy pages may stop at the safety budget and the status message notes the shortfall.
Can I filter by date?
Yes. Use YYYY-MM-DD. Page and tagged tabs send the window to Facebook. Albums have no server-side date filter, so each photo has to be opened and checked. This works even with includeDetails off: the actor reads only the timestamp needed for the bound, keeps the row grid-only, and stops with a clear status if the window is not reached within the scan budget. An album date run costs one detail request per photo examined.
Are image links permanent?
With storeImages on, yes, when the copy succeeds; a failed copy leaves the signed source link. With it off, Facebook's signed links expire.
Is this affiliated with Facebook? No. This is an independent third-party tool that reads public pages; it is not affiliated with or endorsed by Meta.
Data source
Public facebook.com pages, profiles, photo albums, and photo permalinks, read without login. The actor does not bypass private content or login walls.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build fUY2cTF0SfpZTpIw6. Documentation is regenerated from the default published build; private drafts are not included.