Utilities API
The TrueScrape cross-platform utility API exposes 2 public endpoints covering Find a creator's other social profiles. Every call is a GET against public, logged-out pages and returns the same unified schema as every other platform here. Calls cost 1–10 credits each. A cache hit is free, and a failed or empty response is never charged.
Endpoints
| Endpoint | Returns | Credits | Cacheable | Batchable |
|---|---|---|---|---|
| /v1/find-social-profiles | Find a creator's other social profiles | 10 | yes | yes |
| /v1/detect-age-gender | Estimate age and gender from an image | 1 | yes | yes |
Reference
/v1/find-social-profiles10 creditscacheablebatchableFind a creator's other social profiles
Starts from one public profile and reports the other accounts that profile can be shown to own: profiles it links to directly, profiles reached through a recognised link-in-bio page (Linktree, Komi, Pillar, Lnk.Bio, LinkMe), and same-handle accounts on the other supported platforms that link back to the source or share a website only the owner controls. Covers Instagram, TikTok, YouTube, X and Facebook. A same-handle account with no corroboration is reported under candidates, never as a profile: the handle matching is a fact handed over, not a claim. There is no search-engine discovery and no display-name matching; neither is evidence of ownership. Every result carries the evidence type it rests on. Each entry in failures carries the inner error’s code and message, so "we checked and found nothing" (upstream_not_found, empty_result) stays distinguishable from "we could not check" (upstream_blocked, not_configured); only the second kind sets partial. Not covered: any platform outside the five, and links a creator has not published. An X/Twitter source can only be resolved by same-handle probing, because X exposes no bio links at all to a logged-out caller. The 20-second budget stops the composite scheduling new inner fetches; it does not abort one already in flight, so a slow leg is reported as a timeout while its request finishes unread. Link-in-bio expansion is verified against fixtures only, because no channel sampled live from our network published a link-in-bio URL to expand. A partial: true answer is cached for the same seven days as a complete one, so pass cache_max_age=0 to refetch after a transient block rather than being served the gap for a week. Pricing is per source checked: the 10 credits buy the legs this call runs (the starting profile, each link-in-bio page expanded, and each same-handle probe), and a leg listed in unchecked is not charged for, so a partial answer costs ceil(10 x legs answered / legs attempted) and never more than 10. **Any response with an empty profiles AND an empty candidates costs nothing at all**, whatever unchecked says: an answer carrying no account has delivered nothing to charge for, and the unchecked disclosure ships free. When every leg was checked and none carries another account that free answer is an empty_result; when nothing beyond the starting profile could be checked it is an error, because "no other accounts" would then be a fact we did not establish.
| Parameter | Type | Required | Description |
|---|---|---|---|
| platform | instagram | tiktok | youtube | x | twitter | facebook | yes | Platform the handle belongs to. x is an alias for twitter. |
| handle | string | yes | Bare handle (@nasa). YouTube also accepts a UC… channel id. URLs are rejected. |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns an object with source, profiles, candidates, failures, unchecked, partial.
Fields
| Field | Type | Present |
|---|---|---|
| source | object | always |
| source.platform | instagram | tiktok | youtube | twitter | facebook | always |
| source.handle | string | always |
| source.url | string | always |
| source.displayName | string | null | always |
| profiles | object[] | always |
| profiles[].platform | instagram | tiktok | youtube | twitter | facebook | always |
| profiles[].handle | string | always |
| profiles[].url | string | always |
| profiles[].evidence | reciprocal_link | source_link | link_in_bio | shared_website | always |
| profiles[].confidence | number | always |
| candidates | string[] | always |
| failures | object[] | always |
| failures[].url | string | always |
| failures[].code | one of 20 strings | always |
| failures[].message | string | always |
| unchecked | object[] | always |
| unchecked[].url | string | always |
| unchecked[].code | one of 20 strings | always |
| unchecked[].message | string | always |
| partial | boolean | always |
curl "$API/v1/find-social-profiles?platform=instagram&handle=%40mkbhd&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
platform: 'instagram',
handle: '@mkbhd',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/find-social-profiles?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 10 credits, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/find-social-profiles",
params={
"platform": "instagram",
"handle": "@mkbhd",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or upstream_rate_limited. Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_blocked / upstream_schema_drift. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/detect-age-gender1 creditcacheablebatchableEstimate age and gender from an image
Apparent age and gender estimated from a public image URL. This is a model ESTIMATE, not measured platform data, and every response is labelled as such. Requires an inference provider to be configured on the deployment.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | yes | Public image URL |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns an object with imageUrl, ageRange, estimatedAge, gender, confidence, isEstimate, source, fetchedAt.
Fields
| Field | Type | Present |
|---|---|---|
| imageUrl | string | always |
| ageRange | string | null | always |
| estimatedAge | number | null | always |
| gender | string | null | always |
| confidence | number | null | always |
| isEstimate | true | always |
| source | string | always |
| fetchedAt | string | always |
curl "$API/v1/detect-age-gender?url=8XkPqR2nLvE&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
url: '8XkPqR2nLvE',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/detect-age-gender?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/detect-age-gender",
params={
"url": "8XkPqR2nLvE",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or upstream_rate_limited. Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_blocked / upstream_schema_drift. Not charged |
| 504 | upstream_timeout. Not charged |
Questions
- How much does the cross-platform utility API cost?
- Calls cost 1–10 credits each. Every endpoint is cacheable, and a cache hit costs nothing. A failed request and an empty result are both free, on every endpoint.
- Does the cross-platform utility API need a login or cookies?
- No. Every endpoint reads public, logged-out pages only — no account, no cookies, no session. An API key identifies your own TrueScrape account and nothing else.
- Can I fetch many cross-platform utility targets in one request?
- Yes. 2 of 2 are batchable: one POST to /v1/jobs/batch takes many targets and returns a job id to poll.
- Can I watch cross-platform utility endpoints for changes?
- No. All 2 carry x-subscribable: false in the spec, so none can be subscribed to.