Refetcher API
One clean HTTP endpoint for real-time Instagram, TikTok, Facebook, X and YouTube data — normalized, pay-as-you-go, and built for developers.
Refetch(er) provides a single unified scrape entry point for Instagram, TikTok, Facebook, and X posts and profiles, YouTube video metrics, recent YouTube comments, and YouTube channel data. Your request body tells the API what resource you want, and the gateway routes it to the correct scraper internally.
To scrape a target you provide a Refetch(er) API key plus a public URL, username, profile URL, channel URL, or batch array. Refetch(er) never asks for social account logins, platform cookies, session tokens, proxy settings, regions, or infrastructure URLs. Successful media results return normalized metrics, post, author, and media fields when public. Profile and channel results return normalized public account metadata plus optional lightweight recent media.
When a platform does not expose a field publicly, the API returns null, false, or an explicit metricAvailability state instead of guessing.
Authentication
All scrape requests require an API key generated from your dashboard. Public status and health routes are readable without a key. Pass your key in either of these HTTP headers:
| Header | Value format | Description |
|---|---|---|
X-API-Key | pc_live_... | The preferred header format for custom requests. |
Authorization | Bearer pc_live_... | Alternative standard authentication header. |
Endpoints
Refetch(er) serves all live scrape requests from a single endpoint, accepting POST JSON payloads that determine which platform and resource to fetch.
The route alias POST https://api.refetcher.com/v1/scrape is fully supported for compatibility, but we recommend calling the root path directly.
GET / and GET /status return the public API status page. GET /health returns a lightweight JSON health response.
Supported resources
Every resource uses the same endpoint. The API detects a platform from a full url or profileUrl. For a bare Instagram, TikTok, Facebook, or X username, you must include platform; handles alone are ambiguous across networks.
Instagram Post/Reel
Metrics, caption, hashtags, dimensions, duration, thumbnails, and public media source URLs for posts and reels.
Instagram Profile
Public profile metadata and optional links-only recent media with browserless cursor pagination.
TikTok Video
Views, likes, comments, shares, saves, author data, media, music, and optional embedded comments.
TikTok Profile
Public creator metadata and links-only recent videos, with multi-page profile pagination for deeper post discovery.
Facebook Post/Reel
Public reactions, comments, shares, views for videos/Reels, and photo or carousel media children when exposed.
Facebook Profile
Public profile metadata and recent post links with browserless cursor pagination.
X Post
Views, likes, retweets, replies, and tweet media attachments for public X posts.
X Profile
Public profile metadata including followers, following, and tweet counts.
YouTube Video
Views, likes, comments, video metadata, and optional newest public top-level comments for videos or Shorts.
YouTube Channel
Subscribers, total views, video count, thumbnails, and optional links-only recent uploads. Use channelVideos for upload metrics.
channelVideos resource is the upload-list exception that includes per-video metrics. Facebook can still stop a timeline read early; check pageInfo.recentPosts.incomplete and limitations before treating a requested window as complete.Request fields
Send a JSON body with one target field (and optional modifiers). Provide exactly one of the URL/username/channel fields per single request, or an array for a batch.
| Field | Type | Default | Description |
|---|---|---|---|
urlopt | string | null | URL of a single Instagram post/profile, TikTok video/profile, Facebook post/profile, YouTube video, or YouTube channel. Provide one target field. |
urlsopt | string[] | [] | List of post/profile/video/channel URLs to scrape in parallel. Default limit 10, hard limit 50; your account's configured limit is enforced for every key. |
typeopt | string | null | Use "channel" for YouTube channel profile data or "channelVideos" for latest uploads. Video/post metrics are inferred from the URL by default. |
platformopt | string | null | Required with username or usernames: "instagram", "facebook", "tiktok", "x", or "twitter". A full URL already identifies its platform. |
usernameopt | string | null | Instagram, TikTok, Facebook, or X username for a profile scrape. Requires platform to prevent an ambiguous handle from scraping the wrong network. |
usernamesopt | string[] | [] | Usernames for batch profile scraping. Requires platform; one platform applies to the batch. |
profileUrlopt | string | null | Instagram, TikTok, Facebook, or X profile URL for a single profile scrape. |
profileUrlsopt | string[] | [] | Profile URLs for batch profile scraping. |
channelUrlopt | string | null | YouTube channel URL, @handle, legacy /user/ URL, or /channel/ ID URL. |
channelUrlsopt | string[] | [] | Batch YouTube channel targets. |
channelIdopt | string | null | Raw YouTube channel ID such as UC.... |
handleopt | string | null | YouTube @handle when platform is "youtube" or type is "channel"/"channelVideos". |
includeRecentPostsopt | boolean | false | When scraping Instagram, TikTok, Facebook, or X profiles, include lightweight recent public media references. Per-post engagement is omitted; fetch the returned post URL for metrics. |
recentPostsLimitopt | integer | 12 / 3 for Facebook | Compatibility alias for profile pagination. It maps to the platform page size: ceil(limit / 12) for Instagram, TikTok, and X; ceil(limit / 3) for Facebook. Use pages for new integrations. |
pagesopt | integer | 1 | Instagram, TikTok, Facebook, and X profiles. Number of recent-media pages to request, 1–25. Instagram, TikTok, and X return up to 12 references per page; Facebook returns up to 3 post links per page. Each returned page counts as one successful scrape at the standard $0.90 per 1,000; failed or empty pages are free. A request can reserve at most 50 billable units total. |
afteropt | string | null | Facebook profile only. Opaque continuation cursor from pageInfo.recentPosts.endCursor. cursor and endCursor are accepted aliases. |
includeRecentCommentsopt | boolean | false | When scraping a YouTube video, include newest public top-level comments. For TikTok video, include embedded public comments if present in the page payload. |
recentCommentsLimitopt | integer | 20 | Max comments to request for YouTube or embedded TikTok comments. Range 1–100. |
includeRecentVideosopt | boolean | true for type:"channel" | When scraping a YouTube channel profile, include latest uploads as links-only references. Use type:"channelVideos" for per-video metrics. |
recentVideosLimitopt | integer | 12 | Max YouTube uploads to request. Range 1–50. |
requiredFieldsopt | string[] | ["views","likes","comments"] | Enforces validation on applicable returned metrics. Instagram image/carousel views are automatically treated as not applicable. Accepted: views, likes, comments, shares, saves. |
Normalized profile schema
Instagram, TikTok, Facebook, and X profile results share one normalized profile object. Fields a platform does not expose return null or false. Each platform page lists its own additions on top of this core.
idhandlenamebiographyfollowersfollowingtotalPoststotalVideostotalLikestalkingAboutwereHerehighlightReelCountisPrivateisVerifiedisBusinessAccountisProfessionalAccountcategoryNameexternalUrlprofilePicUrlprofilePicUrlHd
Batch scrape
Query multiple targets concurrently in one call by passing an array under urls, profileUrls, or a compatible URL field. Optionally supply requiredFields to enforce metric presence.
{
"urls": [
"https://www.instagram.com/reel/C8xExampleAbc/",
"https://www.tiktok.com/@creator/video/7398210000000000000",
"https://www.facebook.com/reel/2781078362247521/",
"https://x.com/SpaceX/status/2064099405758906727",
"https://www.youtube.com/watch?v=8jPQjjsBbIc"
],
"requiredFields": ["views", "likes", "comments"]
}
views: null and metricAvailability.views: "not_applicable". Include "shares" or "saves" in requiredFields only when every target can return them.Quickstart code
Copy-paste templates to send your first request. Swap in your API key and a target URL.
curl -X POST https://api.refetcher.com/ \
-H "X-API-Key: pc_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://www.instagram.com/reel/C8xExampleAbc/"
}'
const apiKey = 'pc_live_your_key_here';
const urlToScrape = 'https://www.instagram.com/reel/C8xExampleAbc/';
fetch('https://api.refetcher.com/', {
method: 'POST',
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: urlToScrape
})
})
.then(res => res.json())
.then(data => {
if (data.success && data.results[0].success) {
const metrics = data.results[0].metrics;
console.log(`Views: ${metrics.views}, Likes: ${metrics.likes}`);
} else {
console.error('Scrape failed:', data.results[0].error || 'Unknown error');
}
})
.catch(err => console.error('Request error:', err));
import requests
import json
api_key = "pc_live_your_key_here"
url_to_scrape = "https://www.instagram.com/reel/C8xExampleAbc/"
response = requests.post(
"https://api.refetcher.com/",
headers={
"X-API-Key": api_key,
"Content-Type": "application/json"
},
json={
"url": url_to_scrape
}
)
if response.status_code == 200:
data = response.json()
if data.get("success") and data["results"][0]["success"]:
metrics = data["results"][0]["metrics"]
print(f"Views: {metrics['views']}, Likes: {metrics['likes']}")
else:
print("Scrape failed:", data["results"][0].get("error", "Unknown error"))
else:
print(f"Server error: {response.status_code}", response.text)
Scrape public Instagram posts, Reels, and profiles. Post and Reel requests return normalized metrics, post, author, and a media object; profile requests return account metadata with optional cursor-paginated recent media.
Post & Reel
Send a single url string in the body. The response returns metrics, post, author, and a media object — media fields include thumbnail/video/audio source URLs, duration, dimensions, separate play/view counts, music attribution, and carousel children when Instagram exposes them. Shares and saves remain null when not publicly available.
{
"url": "https://www.instagram.com/reel/C8xExampleAbc/"
}
{
"url": "https://www.instagram.com/reel/C8xExampleAbc/",
"platform": "instagram",
"success": true,
"metrics": {
"views": 1284203,
"likes": 48211,
"comments": 1903,
"shares": null,
"saves": null
},
"metricAvailability": {
"views": "available",
"likes": "available",
"comments": "available",
"shares": "unavailable",
"saves": "unavailable"
},
"post": {
"id": "C8xExampleAbc",
"shortcode": "C8xExampleAbc",
"normalizedUrl": "https://www.instagram.com/reel/C8xExampleAbc/",
"publishedAt": "2026-06-01T08:30:00.000Z",
"caption": "Check out our new release! #refetcher #api",
"type": "video",
"productType": "clips",
"hashtags": ["refetcher", "api"],
"mentions": [],
"location": null,
"commentsDisabled": false,
"isPinned": false,
"isSponsored": false
},
"author": {
"id": "123456789",
"handle": "creators_hub",
"name": "Creators Hub",
"followers": 94200,
"isVerified": false,
"profilePicUrl": "https://..."
},
"media": {
"thumbnailUrl": "https://...",
"videoUrl": "https://...",
"audioUrl": "https://...",
"durationSeconds": 21.4,
"width": 1080,
"height": 1920,
"playCount": 1284203,
"viewCount": null,
"altText": null,
"music": {
"id": "987654321",
"title": "Original audio",
"artist": "creators_hub",
"isOriginalAudio": true
},
"children": []
},
"scrapedAt": "2026-06-05T10:15:30.000Z"
}
Supported URL shapes
/reel//reels//p//tv/Image and carousel posts return metrics.views: null with metricAvailability.views: "not_applicable". For Reels, metrics.views uses the public play count; when Instagram exposes both values, the raw media object keeps media.playCount and media.viewCount separate. URL normalization can return the canonical shortcode under /reel/ even for some /p/ inputs, so use post.type and metricAvailability to distinguish carousels/images from videos.
Profile
Send a username or a profile URL. Use includeRecentPosts to also return lightweight recent media references. Instagram profile requests use the shared pages model: 1–25 pages, up to 12 references per page, without a browser or login. Results beyond the first page are fetched with Instagram's timeline cursor.
{
"platform": "instagram",
"username": "nasa",
"includeRecentPosts": true,
"recentPostsLimit": 36
}
{
"profileUrl": "https://www.instagram.com/nasa/",
"includeRecentPosts": false
}
{
"url": "https://www.instagram.com/nasa/",
"platform": "instagram",
"type": "profile",
"success": true,
"profile": {
"id": "528817151456180",
"handle": "nasa",
"name": "NASA",
"biography": "Exploring the universe and our home planet.",
"followers": 97400000,
"following": 78,
"totalPosts": 4123,
"totalVideos": 171,
"totalLikes": null,
"highlightReelCount": 42,
"isPrivate": false,
"isVerified": true,
"isBusinessAccount": false,
"isProfessionalAccount": true,
"categoryName": "Science, Technology & Engineering",
"externalUrl": "https://www.nasa.gov/",
"profilePicUrl": "https://...",
"profilePicUrlHd": "https://..."
},
"recentPosts": [],
"pageInfo": {
"recentPosts": {
"hasNextPage": true,
"endCursor": "...",
"requestedLimit": 36,
"returnedCount": 36
}
},
"scrapedAt": "2026-06-05T10:15:30.000Z"
}
The response returns type: "profile", a profile object, and optional recentPosts or recentVideos arrays when recent media is requested.
Field reference
Successful responses can include these normalized fields when Instagram exposes them. Unavailable fields return null, false, an explicit metricAvailability value, or are omitted when empty.
| Object | Fields |
|---|---|
post | idshortcodenormalizedUrlpublishedAtcaptiontypeproductTypehashtagsmentionsregionlanguagelocationcommentsDisabledisPinnedisSponsored |
author | idhandlenamefollowersfollowingtotalLikesvideoCountbioisVerifiedprofilePicUrl |
media | thumbnailUrlvideoUrlaudioUrldurationSecondswidthheightplayCountviewCountaltTextmusicchildren |
profile | Shared profile schema plus talkingAbout and wereHere when present. |
recentVideos, pageInfo | recentVideos can contain profile reels. pageInfo.recentVideos mirrors the cursor shape: hasNextPage, endCursor, requestedLimit, returnedCount, pagesRequested, pagesFetched, incomplete. |
TikTok
Scrape public TikTok videos and creator profiles. Video requests return engagement metrics; profile requests return public creator metadata and links-only recent post references with multi-page pagination.
Video
Send the canonical tiktok.com/@creator/video/7398210000000000000 URL in the body. Redirect-only short links such as vm.tiktok.com are not accepted. Responses populate views, likes, comments, shares, and saves when available. recentComments is present only when requested and may be an empty array when TikTok does not include embedded comments in the public page payload.
{
"url": "https://www.tiktok.com/@creator/video/7398210000000000000",
"includeRecentComments": true,
"recentCommentsLimit": 3
}
{
"url": "https://www.tiktok.com/@creator/video/7398210000000000000",
"platform": "tiktok",
"success": true,
"metrics": {
"views": 2400000,
"likes": 98400,
"comments": 3210,
"shares": 12200,
"saves": 18400
},
"metricAvailability": {
"views": "available",
"likes": "available",
"comments": "available",
"shares": "available",
"saves": "available"
},
"post": {
"id": "7398210000000000000",
"shortcode": null,
"normalizedUrl": "https://www.tiktok.com/@creator/video/7398210000000000000",
"publishedAt": "2026-06-01T08:30:00.000Z",
"caption": "Example video caption",
"type": "video",
"productType": null,
"hashtags": [],
"mentions": [],
"region": "US",
"language": "en",
"location": null,
"commentsDisabled": false,
"isPinned": false,
"isSponsored": false
},
"author": {
"id": "123456789",
"handle": "creator",
"name": "Creator Name",
"followers": 312000,
"following": 128,
"totalLikes": 9800000,
"videoCount": 420,
"bio": "Creator bio",
"isVerified": false,
"profilePicUrl": "https://..."
},
"media": {
"type": "video",
"thumbnailUrl": "https://...",
"videoUrl": "https://...",
"durationSeconds": 18,
"width": 1080,
"height": 1920,
"ratio": "720p"
},
"music": {
"id": "music-id",
"title": "Original sound",
"authorName": "Creator Name",
"durationSeconds": 18,
"original": true,
"playUrl": "https://...",
"coverUrl": "https://..."
},
"recentComments": [
{
"id": "comment-id",
"text": "Example public comment",
"likes": 12,
"replies": 1,
"publishedAt": "2026-06-01T09:00:00.000Z",
"author": {
"id": "comment-author-id",
"handle": "viewer",
"name": "Viewer",
"isVerified": false,
"profilePicUrl": "https://..."
}
}
],
"scrapedAt": "2026-06-09T10:15:30.000Z"
}
Profile
Send a username or profile URL. Enable recent posts to receive the public videos TikTok exposes without login. By default a request returns the first page (up to ~12 recent videos). To scrape more, set pages to the number of pages you want — each page returns up to 12 posts and counts as one successful scrape (so pages: 3 is billed as 3 scrapes at $0.90 per 1,000; a page that returns nothing is free). Recent posts are returned as lightweight references (link, publish time, caption, thumbnail) without per-post engagement; for views, likes, comments, shares, and saves on a specific video, use the Video scraper. For compatibility, the same public video selection can appear under both recentPosts and recentVideos.
{
"type": "profile",
"platform": "tiktok",
"username": "nba",
"includeRecentPosts": true,
"pages": 3
}
{
"url": "https://www.tiktok.com/@nba",
"platform": "tiktok",
"type": "profile",
"success": true,
"profile": {
"id": "6749213842887734277",
"handle": "nba",
"name": "NBA",
"biography": "The official NBA account.",
"followers": 26800000,
"following": 266,
"totalPosts": null,
"totalVideos": 12,
"totalLikes": 1100000000,
"highlightReelCount": null,
"isPrivate": false,
"isVerified": true,
"isBusinessAccount": false,
"isProfessionalAccount": false,
"categoryName": null,
"externalUrl": null,
"profilePicUrl": "https://...",
"profilePicUrlHd": null
},
"recentPosts": [
{
"id": "7648122936318020894",
"url": "https://www.tiktok.com/@nba/video/7648122936318020894",
"type": "video",
"isVideo": true,
"publishedAt": "2026-06-06T03:43:06.000Z",
"caption": "Game 7 highlights"
}
],
"recentVideos": [
{
"id": "7648122936318020894",
"url": "https://www.tiktok.com/@nba/video/7648122936318020894",
"type": "video",
"isVideo": true,
"publishedAt": "2026-06-06T03:43:06.000Z",
"caption": "Game 7 highlights"
}
]
}
Field reference
Successful responses can include these normalized fields when TikTok exposes them. Unavailable fields return null or are omitted when empty.
| Object | Fields |
|---|---|
media | typethumbnailUrlvideoUrldurationSecondswidthheightratioformatbitrateimages |
profile | Shared profile schema plus talkingAbout and wereHere when present. A top-level scrapedAt is returned on the profile result. |
Scrape public Facebook Reels, videos, posts, photos, and carousels, plus public profile metadata. Video/Reel results include public view counts when available; image, photo, carousel, and text posts return views: null with metricAvailability.views: "not_applicable".
Post & Reel
Send the canonical Facebook URL in the body. Carousel and photo items are normalized under media.children. Facebook saves are not publicly exposed and return null. Facebook may resolve share/Reel URLs to the canonical owner URL in post.normalizedUrl.
{
"url": "https://www.facebook.com/reel/2781078362247521/"
}
{
"url": "https://www.facebook.com/reel/2781078362247521/",
"platform": "facebook",
"success": true,
"metrics": {
"views": 3400,
"likes": 31,
"comments": 1,
"shares": 28,
"saves": null
},
"metricAvailability": {
"views": "available",
"likes": "available",
"comments": "available",
"shares": "available",
"saves": "unavailable"
},
"post": {
"id": "2781078362247521",
"shortcode": null,
"normalizedUrl": "https://www.facebook.com/100086691939822/videos/2781078362247521/",
"publishedAt": null,
"caption": "Public caption",
"type": "video",
"productType": "video",
"hashtags": [],
"mentions": [],
"location": null,
"commentsDisabled": null,
"isPinned": null,
"isSponsored": null
},
"author": {
"id": null,
"handle": "Meta",
"name": "Meta",
"followers": null,
"isVerified": null,
"profilePicUrl": null
},
"media": {
"thumbnailUrl": "https://...",
"videoUrl": "https://...",
"durationSeconds": 30,
"width": null,
"height": null,
"altText": null,
"children": []
},
"scrapedAt": "2026-06-10T10:15:30.000Z"
}
Supported URL shapes
/reel//videos//watch/?v=/posts//photo.php/permalink.php/share/p/fb.watchProfile
Send a Facebook username or profile URL. Use pages to request 1–25 pages of recent public post links, up to 3 post links per page, and check pageInfo.recentPosts.incomplete before treating the returned window as complete.
pageInfo.recentPosts.incomplete when appropriate. When an incomplete result still includes endCursor, resend it as after after a short backoff to resume from the failed page.{
"type": "profile",
"platform": "facebook",
"username": "Meta",
"pages": 2
}
{
"url": "https://www.facebook.com/Meta/",
"platform": "facebook",
"type": "profile",
"success": true,
"profile": {
"id": "100080376596424",
"handle": "Meta",
"name": "Meta",
"biography": null,
"followers": null,
"following": null,
"totalPosts": null,
"totalVideos": null,
"totalLikes": null,
"highlightReelCount": null,
"isPrivate": false,
"isVerified": true,
"isBusinessAccount": false,
"isProfessionalAccount": false,
"categoryName": null,
"externalUrl": null,
"profilePicUrl": "https://...",
"profilePicUrlHd": "https://..."
},
"recentPosts": [
{
"id": "1032807172741826",
"shortcode": "1032807172741826",
"url": "https://www.facebook.com/Meta/posts/1032807172741826/",
"type": "carousel",
"isVideo": false,
"publishedAt": null,
"caption": null,
"displayUrl": null,
"dimensions": null
}
],
"recentVideos": [
{
"id": "1358040539510776",
"url": "https://www.facebook.com/reel/1358040539510776/",
"type": "video",
"isVideo": true,
"publishedAt": null
}
],
"postLinks": [
"https://www.facebook.com/reel/1358040539510776/"
],
"pageInfo": {
"recentPosts": {
"hasNextPage": true,
"endCursor": "...",
"requestedLimit": 10,
"returnedCount": 4,
"pagesRequested": 2,
"pagesFetched": 1,
"incomplete": true
},
"recentVideos": null
},
"scrapedAt": "2026-06-10T10:15:30.000Z"
}
This route is optimized for post-link discovery. Recent posts are returned as link-only references without per-post engagement (views, likes, comments, shares, saves), and Facebook often does not expose an exact profile.followers count here either. Fetch an individual returned post link with the Facebook Post scraper when metrics are required. Facebook can throttle timeline pagination independently of profile metadata, so a profile result can still be successful while pageInfo.recentPosts.incomplete is true. To continue after a successful response, send its pageInfo.recentPosts.endCursor as after; the continuation returns the next post-link slice rather than the initial links. No Facebook account login, cookie, or customer proxy setup is required.
Field reference
Successful responses can include these normalized fields when Facebook exposes them. Unavailable fields return null or are omitted when empty.
| Object | Fields |
|---|---|
post | idshortcodenormalizedUrlpublishedAtcaptiontypeproductTypehashtagsmentionsregionlanguagelocationcommentsDisabledisPinnedisSponsored |
author | idhandlenamefollowersfollowingtotalLikesvideoCountbioisVerifiedprofilePicUrl |
profile | Shared profile schema plus talkingAbout, wereHere, isBusinessAccount, isProfessionalAccount, and profilePicUrlHd when Facebook exposes them. |
X
Scrape public X posts (tweets) and profiles. All five core metrics are available for X posts: views, likes, comments (replies), shares (retweets), and saves (bookmarks).
Post
Send the post URL in the body. X post scraping returns views, likes, replies, retweets, and any attached media. In the returned metrics, replies map to comments, retweets to shares, and bookmarks to saves.
Supported URL shapes
x.com/SpaceX/status/2064099405758906727twitter.com/SpaceX/status/2064099405758906727Metric aliasing
replies→commentsretweets→sharesbookmarks→saves
{
"url": "https://x.com/SpaceX/status/2064099405758906727"
}
{
"url": "https://x.com/SpaceX/status/2064099405758906727",
"platform": "x",
"success": true,
"metrics": {
"views": 5350265,
"likes": 148761,
"comments": 3441,
"shares": 21255,
"saves": 3469
},
"metricAvailability": {
"views": "available",
"likes": "available",
"comments": "available",
"shares": "available",
"saves": "available"
},
"post": {
"id": "2064099405758906727",
"shortcode": "2064099405758906727",
"normalizedUrl": "https://x.com/SpaceX/status/2064099405758906727",
"publishedAt": "2026-06-10T19:28:28.000Z",
"caption": "Launch update from the pad",
"type": "text",
"productType": "tweet",
"hashtags": [],
"mentions": []
},
"author": {
"id": "34713599",
"handle": "SpaceX",
"name": null,
"followers": 41700000,
"isVerified": true,
"profilePicUrl": null
},
"media": {
"thumbnailUrl": null,
"videoUrl": null,
"durationSeconds": null,
"width": null,
"height": null,
"altText": null,
"children": []
},
"scrapedAt": "2026-06-10T10:15:30.000Z"
}
metrics object.Profile
Send an X username or profile URL. Enable recent posts to return a list of recent public posts exposed by the profile timeline.
{
"type": "profile",
"platform": "x",
"username": "SpaceX",
"includeRecentPosts": true,
"recentPostsLimit": 12
}
{
"url": "https://x.com/SpaceX",
"platform": "x",
"type": "profile",
"success": true,
"profile": {
"id": "34713599",
"handle": "SpaceX",
"name": "SpaceX",
"biography": "SpaceX designs, manufactures and launches advanced rockets and spacecraft.",
"followers": 41700000,
"following": 123,
"totalPosts": 11400,
"totalVideos": null,
"totalLikes": null,
"talkingAbout": null,
"wereHere": null,
"highlightReelCount": null,
"isPrivate": false,
"isVerified": true,
"isBusinessAccount": false,
"isProfessionalAccount": false,
"categoryName": null,
"externalUrl": "https://www.spacex.com/",
"profilePicUrl": "https://...",
"profilePicUrlHd": null
},
"recentPosts": [
{
"id": "2064099405758906727",
"url": "https://x.com/SpaceX/status/2064099405758906727",
"type": "video",
"isVideo": true,
"publishedAt": "2026-06-01T00:00:00.000Z",
"caption": "Starship flight test"
}
],
"recentVideos": [
{
"id": "2064099405758906727",
"url": "https://x.com/SpaceX/status/2064099405758906727",
"type": "video",
"isVideo": true,
"publishedAt": "2026-06-01T00:00:00.000Z"
}
],
"pageInfo": {
"recentPosts": {
"hasNextPage": true,
"endCursor": "...",
"requestedLimit": 12,
"returnedCount": 12,
"pagesRequested": null,
"pagesFetched": null,
"incomplete": false
},
"recentVideos": {
"hasNextPage": true,
"endCursor": "...",
"requestedLimit": 12,
"returnedCount": 3,
"pagesRequested": null,
"pagesFetched": null,
"incomplete": false
}
},
"scrapedAt": "2026-06-10T10:15:30.000Z"
}
Field reference
Successful responses can include these normalized fields when X exposes them. Unavailable fields return null or are omitted when empty.
| Object | Fields |
|---|---|
post | idshortcodenormalizedUrlpublishedAtcaptiontypeproductTypehashtagsmentionsregionlanguagelocationcommentsDisabledisPinnedisSponsored |
author | idhandlenamefollowersfollowingtotalLikesvideoCountbioisVerifiedprofilePicUrl |
profile | Shared profile schema including totalVideos, totalLikes, talkingAbout, wereHere, highlightReelCount, isBusinessAccount, isProfessionalAccount, categoryName, externalUrl, and profilePicUrlHd. |
recentVideos, pageInfo | recentVideos can contain recent video posts. pageInfo.recentPosts and pageInfo.recentVideos use the cursor fields hasNextPage, endCursor, requestedLimit, returnedCount, pagesRequested, pagesFetched, incomplete. |
limitations | Not returned for normal X post responses. Optional fields such as limitations are omitted when empty. |
YouTube
Scrape public YouTube videos, Shorts, and channels. YouTube does not expose shares or saves through this API, so those metrics return null.
Video
Send a YouTube URL in the body. Enable includeRecentComments to also return the newest public top-level comments. Supported formats include standard watch URLs, Shorts URLs, embeds, live URLs, and youtu.be links.
{
"url": "https://www.youtube.com/watch?v=8jPQjjsBbIc",
"includeRecentComments": true,
"recentCommentsLimit": 20
}
{
"url": "https://www.youtube.com/watch?v=8jPQjjsBbIc",
"platform": "youtube",
"success": true,
"metrics": {
"views": 1650000,
"likes": 42000,
"comments": 1800,
"shares": null,
"saves": null
},
"post": {
"id": "8jPQjjsBbIc",
"normalizedUrl": "https://www.youtube.com/watch?v=8jPQjjsBbIc",
"publishedAt": "2026-06-01T08:30:00.000Z",
"caption": "Example video title",
"description": "Example video description"
},
"author": {
"handle": "UCAuUUnT6oDeKwE6v1NGQxug",
"name": "TED",
"followers": null
},
"media": {
"thumbnailUrl": "https://i.ytimg.com/vi/8jPQjjsBbIc/hqdefault.jpg",
"embedUrl": "https://www.youtube.com/embed/8jPQjjsBbIc"
},
"recentComments": [
{
"id": "comment-id",
"authorName": "Viewer",
"authorChannelId": "UC...",
"authorChannelUrl": "https://www.youtube.com/channel/UC...",
"text": "Example comment",
"likes": 12,
"publishedAt": "2026-06-09T09:00:00.000Z",
"updatedAt": "2026-06-09T09:00:00.000Z",
"replyCount": 1
}
],
"scrapedAt": "2026-06-09T10:15:30.000Z"
}
post/author schema and currently do not include metricAvailability. The media object provides a stable thumbnail CDN URL and permanent embed URL; direct signed media-download URLs are intentionally not returned because they expire and are not reliable across AWS regions. recentComments is present only when requested; if comment retrieval fails, the response includes an empty array and recentCommentsError.Channel
Set type to "channel" for channel profile data or "channelVideos" for latest uploads. You can pass a channel URL, raw channel ID, or @handle. Channel profile responses return type: "channel" and a channel object; their bundled recentVideos are links-only references, matching every other profile scraper. Channel-video responses return type: "channelVideos", a lightweight channel object, and results/videos arrays — this is the one resource that includes per-video views, likes, and comments.
{
"type": "channel",
"platform": "youtube",
"channelUrl": "https://www.youtube.com/@TED",
"includeRecentVideos": true,
"recentVideosLimit": 12
}
{
"type": "channelVideos",
"platform": "youtube",
"handle": "@TED",
"recentVideosLimit": 12
}
Field reference
YouTube uses a platform-specific schema. Successful responses can include these fields when YouTube exposes them.
| Resource | Object | Fields |
|---|---|---|
| Video / Short | media | thumbnailUrlembedUrl |
| Channel | channel | idhandlenamedescriptioncountrypublishedAtsubscribershiddenSubscriberCounttotalViewstotalVideosuploadsPlaylistIdthumbnailUrlurl |
| Channel videos | channel | idhandlenamesubscribershiddenSubscriberCounttotalViewstotalVideos |
True People Search
Resolve a name, phone number, or address into a structured US contact record — current and prior addresses, phone numbers, emails, and known relatives — returned as normalized JSON. There are two endpoints: /people-search finds matching records, and /people-detail returns the full contact record for a match. Each request batches up to 30 independent person queries. Need access? Contact sales.
Search endpoint
POST a queries array (1–30 items) to /people-search to find matching records. Each query searches by name, phone, or address — provide at least one. Add city, state, and age to disambiguate common names. A bare name string ("queries": ["Jordan Avery"]) is also accepted. Every match includes a lightweight summary and a detailUrl you can hand to the detail endpoint for the full record.
curl -X POST https://api.refetcher.com/people-search \
-H "X-API-Key: pc_live_••••" \
-H "Content-Type: application/json" \
-d '{
"queries": [
{ "id": "lead-91", "name": "Jordan Avery", "city": "Plano", "state": "TX" },
{ "phone": "(972) 555-0148" }
]
}'
{
"success": true,
"requestId": "a1b2c3d4",
"summary": { "total": 2, "succeeded": 2, "failed": 0 },
"results": [
{
"success": true,
"platform": "people_search",
"type": "person_search",
"provider": "truepeoplesearch",
"query": { "id": "lead-91", "name": "Jordan Avery", "city": "Plano", "state": "TX" },
"matchCount": 1,
"matches": [
{
"name": "Jordan M Avery",
"age": 38,
"currentCity": "Plano",
"currentState": "TX",
"summary": "Jordan M Avery is 38 and lives in Plano, TX. Previously in Frisco, TX.",
"detailUrl": "https://www.truepeoplesearch.com/find/person/…"
}
],
"searchedAt": "2026-06-25T17:42:08.110Z",
"workerMs": 4180
},
{
"success": true,
"platform": "people_search",
"type": "person_search",
"provider": "truepeoplesearch",
"query": { "id": "q_2", "phone": "9725550148" },
"matchCount": 1,
"matches": [
{
"name": "Jordan M Avery",
"age": 38,
"currentCity": "Plano",
"currentState": "TX",
"summary": "Reverse-phone match for (972) 555-0148.",
"detailUrl": "https://www.truepeoplesearch.com/find/person/…"
}
],
"searchedAt": "2026-06-25T17:42:08.420Z",
"workerMs": 3920
}
],
"totalMs": 4620
}
Request parameters
| Parameter | Type | Description |
|---|---|---|
queries (required) | object[] · string[] | 1–30 person queries. Each item is a query object (below) or a plain name string. |
maxResultsPerQuery | number | Maximum matches returned per query. Default and maximum 10. |
timeoutMs | number | Per-request worker timeout. Default and maximum 150000. |
Search query — provide at least one of name, phone, or address
| Field | Type | Description |
|---|---|---|
name | string | Full name to search. Accepts fullName as an alias. |
firstName / lastName | string | Used to build the search name when name is not supplied. |
phone | string | Reverse-phone lookup. Any format; normalized to a 10-digit US number (a leading 1 is stripped). |
address | string | Reverse-address lookup. Free-form street address. |
city | string | Narrows a name search to a city. |
state | string | Two-letter US state code, e.g. TX. Case-insensitive on input. |
age | number | Approximate age (1–124) to disambiguate common names. |
id | string | Your own correlation id. Echoed back on query.id; defaults to q_1, q_2, … when omitted. |
Search response
The top-level success is true only when every query in the batch succeeded — always check each result's own success flag for partial batches. The envelope carries requestId, a summary (total, succeeded, failed), the results array (one entry per query, in request order), and totalMs. The detail endpoint returns this same envelope.
results[] — per query
| Field | Type | Description |
|---|---|---|
success | boolean | Whether this individual query resolved. |
platform | string | Always people_search. |
type | string | Always person_search. |
provider | string | Source of the record, e.g. truepeoplesearch. |
query | object | The normalized query that was executed, including your id. |
matchCount | number | Number of matches returned for this query. |
matches | object[] | The matched person records (fields below). |
searchedAt | ISO 8601 | When the lookup ran. |
workerMs | number · null | Worker processing time for this query. |
error | object | Present on failure, with category and message (see Errors). |
matches[] — search fields
| Field | Type | Description |
|---|---|---|
name | string | Full name on the record. |
age | number · null | Approximate age when available. |
currentCity | string | Most recent known city. |
currentState | string | Most recent known state. |
summary | string | Short text summary of the match. |
detailUrl | string | Pass this URL to the /people-detail endpoint for the full record. |
Detail endpoint
POST a detailUrl — taken from any /people-search match — to /people-detail to pull the full contact record: every known address, phone number, email, and relative. It takes the same queries array (1–30 items), where each query carries a detailUrl instead of search terms. The response envelope is identical to /people-search; only the matches[] fields are richer.
curl -X POST https://api.refetcher.com/people-detail \
-H "X-API-Key: pc_live_••••" \
-H "Content-Type: application/json" \
-d '{
"queries": [
{ "detailUrl": "https://www.truepeoplesearch.com/find/person/…" }
]
}'
{
"success": true,
"requestId": "e5f6g7h8",
"summary": { "total": 1, "succeeded": 1, "failed": 0 },
"results": [
{
"success": true,
"platform": "people_search",
"type": "person_search",
"provider": "truepeoplesearch",
"query": { "id": "q_1", "detailUrl": "https://www.truepeoplesearch.com/find/person/…" },
"matchCount": 1,
"matches": [
{
"name": "Jordan M Avery",
"age": 38,
"currentCity": "Plano",
"currentState": "TX",
"locations": [
"4120 Maple Ave, Plano, TX 75074",
"88 Birch Ln, Frisco, TX 75035"
],
"phoneNumbers": ["(972) 555-0148", "(214) 555-0102"],
"emails": ["j.avery@example.com"],
"relatives": ["Morgan Avery", "Taylor Avery"],
"detailUrl": "https://www.truepeoplesearch.com/find/person/…"
}
],
"searchedAt": "2026-06-25T17:42:10.300Z",
"workerMs": 5210
}
],
"totalMs": 5320
}
Detail query
| Field | Type | Description |
|---|---|---|
detailUrl (required) | string | The provider URL returned on a /people-search match. Pass one per query to /people-detail. |
id | string | Your own correlation id, echoed back on query.id. Defaults to q_1, q_2, … when omitted. |
Detail response
The envelope and results[] fields are identical to the search endpoint — only the matches[] records are richer. Empty fields are omitted rather than returned as empty strings or arrays, so check for presence before reading nested values.
matches[] — detail fields
| Field | Type | Description |
|---|---|---|
name | string | Full name on the record. |
age | number · null | Approximate age when available. |
currentCity | string | Most recent known city. |
currentState | string | Most recent known state. |
locations | string[] | Current and prior addresses (up to 8). |
phoneNumbers | string[] | Associated phone numbers (up to 8). |
emails | string[] | Associated email addresses (up to 8). |
relatives | string[] | Known relatives and associates (up to 12). |
detailUrl | string | Provider URL for the full underlying record. |
Errors & status
A failed query never breaks the batch — it returns with success: false and an error.category. Transient categories are retried internally against a backup region before being returned. The HTTP status is 200 when at least one query succeeds, and 502 only when every query in the batch fails.
Query error categories
| Category | Description |
|---|---|
bad_query | The query was invalid or unsearchable (e.g. no name, phone, or address). |
timeout | The lookup exceeded the worker timeout. |
rate_limited | The upstream provider throttled the request. |
blocked_or_challenge | The provider returned a challenge or CAPTCHA. |
challenge_timeout | A provider challenge did not clear in time. |
navigation_timeout | The provider page did not start loading in time. |
parse_error | The provider returned an unreadable result page. |
network_error | A transport error reaching the worker. |
upstream_error | A temporary provider-side failure. |
worker_error | The lookup worker failed before returning a result. |
unknown_error | The lookup failed for an unclassified reason. |
HTTP status codes
| Status | Meaning |
|---|---|
200 | At least one query in the batch succeeded. Check the top-level success flag and each result. |
400 | bad_request — malformed body, no valid queries, or more than 30 queries. |
401 | unauthorized — the API key is missing or invalid. |
402 | insufficient_balance — the account cannot cover the requested lookups. |
405 | method_not_allowed — use POST. |
502 | Every query in the batch failed. Inspect each result's error category. |
503 | Authentication or billing was temporarily unavailable. |
Permissible use. Refetcher is not a consumer reporting agency and this data is not a consumer report. It may not be used for credit, employment, insurance, housing, tenant screening, or any other purpose regulated by the FCRA.
Responses & errors
How every response is wrapped, the HTTP statuses you'll see, and the stable error categories returned per target.
Response envelope
A successful request returns 200 OK with per-target records under the results array. Media targets return metrics, post, and author; Instagram, TikTok, Facebook, and X media targets also return media. Profile/channel targets return a type and profile/channel object.
{
"success": true,
"requestId": "aws-request-id-12345",
"summary": {
"total": 1,
"succeeded": 1,
"failed": 0,
"errors": {}
},
"results": [
{
"url": "https://www.instagram.com/reel/C8xExampleAbc/",
"platform": "instagram",
"success": true,
"metrics": {
"views": 1284203,
"likes": 48211,
"comments": 1903,
"shares": null,
"saves": null
},
"metricAvailability": {
"views": "available",
"likes": "available",
"comments": "available",
"shares": "unavailable",
"saves": "unavailable"
},
"post": { "id": "C8xExampleAbc", "type": "video", "caption": "Check out our new release!" },
"author": { "handle": "creators_hub", "followers": 94200 },
"media": { "playCount": 1284203, "durationSeconds": 21.4 },
"scrapedAt": "2026-06-05T10:15:30.000Z"
}
],
"totalMs": 1150
}
{
"success": true,
"requestId": "aws-request-id-67890",
"summary": {
"total": 2,
"succeeded": 1,
"failed": 1,
"errors": {
"private_or_removed": 1
}
},
"results": [
{
"url": "https://www.instagram.com/reel/VALID_URL/",
"platform": "instagram",
"success": true,
"metrics": { "views": 4900, "likes": 210, "comments": 15, "shares": null, "saves": null },
"post": { "id": "VALID_URL", "publishedAt": null, "caption": null },
"author": { "handle": "creator_name", "name": null, "followers": null },
"scrapedAt": "2026-06-05T10:16:00.000Z"
},
{
"url": "https://www.instagram.com/reel/PRIVATE_URL/",
"platform": "instagram",
"success": false,
"error": {
"category": "private_or_removed",
"message": "The post could not be accessed. It may be private, removed, or unavailable."
},
"scrapedAt": "2026-06-05T10:16:01.000Z"
}
],
"totalMs": 1420
}
{
"success": false,
"requestId": "aws-request-id-99999",
"summary": {
"total": 1,
"succeeded": 0,
"failed": 1,
"errors": {
"missing_metrics": 1
}
},
"results": [
{
"url": "https://www.instagram.com/reel/SHORTCODE/",
"platform": "instagram",
"success": false,
"error": {
"category": "missing_metrics",
"message": "The scraper could not read the required public metrics for this post."
},
"scrapedAt": "2026-06-05T10:17:00.000Z"
}
],
"totalMs": 1380
}
Batch & billing: the HTTP status is still 200 OK if at least one target succeeds; the top-level success flag is also true for a partial batch, while failed targets are marked inside results[]. Billing is per successful target, or per successful returned page for profile/channel pagination. A request can reserve at most 50 billable units total. If every target is still private, deleted, or unavailable after retries, the API returns 404 Not Found with private_or_removed per-target errors. Other every-target scrape failures return 502 Bad Gateway; unexpected gateway exceptions return 500 with a top-level error object.
Metric availability: Instagram, TikTok, Facebook, and X post results include metricAvailability with five keys — each value is "available", "unavailable", or "not_applicable". Unavailable metrics return null in metrics; YouTube video results currently omit the availability object.
HTTP status codes
Request-level failures use a top-level error object. Scrape-level failures appear inside results.
| Status | Category | Meaning |
|---|---|---|
200 | Success / partial | At least one target succeeded. Check summary.failed and each item in results for partial failures. |
400 | bad_request | Malformed JSON, no target, too many targets, or an unsupported target format. |
401 | unauthorized | The API key is missing or invalid. |
402 | insufficient_balance | The account cannot reserve enough balance for every target in the request. |
404 | private_or_removed | Every requested target is private, deleted, or otherwise unavailable publicly. |
405 | method_not_allowed | The scrape endpoint was called with a method other than POST. |
500 | Gateway error | An unexpected dispatcher exception prevented a normal per-target response. |
502 | Full failure | Every target failed. Inspect each result's error category. |
503 | billing_unavailable | The service could not reserve account balance before scraping. |
Error categories
Per-target scraper failures are normalized into stable category keys:
| Category | Description |
|---|---|
bad_url | The provided URL format is invalid or cannot be parsed. |
unsupported_platform | The target platform is not supported by Refetch(er). |
private_or_removed | The target is private, was deleted, or restricted public visibility. |
minimum_age_required | Instagram returned its exact account-level minimum-age restriction. This terminal result is not retried through a paid recovery provider and is not billed. |
missing_metrics | The scraper accessed the target but could not extract the required public metrics. |
timeout | The worker exceeded its maximum execution time. |
rate_limited | The platform rate limited the request. When failover is available, the dispatcher retries during the same API request. |
blocked_or_challenge | The scraper encountered an upstream access challenge or block. |
upstream_error | A scraper worker or social platform returned an unusable server response. |
configuration_error | A system configuration issue prevented the scrape. |
unknown_error | An unclassified system exception occurred. |
Retry & failover
Refetch(er) is built to maximize scrape consistency. When a request arrives, the dispatcher orchestrates multi-region failover behind the scenes:
- The dispatcher forwards your query to the primary scraper worker.
- If the primary worker returns a retryable failure, the dispatcher can retry only the failed targets on a backup worker during the same request.
- If every route fails for a target, the API returns the final normalized error payload for that target.
This entire retry lifecycle is transparent to your client. You make a single request to api.refetcher.com, and we handle the network recovery logic, returning the data inside our standard response shape.