Endpoints
POST /api/v1/resources/search
Retrieve datapoints for one or more social media resources.
Request Body
{
"resources": [
{
"platform": "instagram",
"type": "account",
"identifier": "natgeo"
}
]
}
Fields
| Field | Type | Required | Description |
|---|---|---|---|
resources | array | yes | List of resources to query |
resources[].platform | string | yes | Platform code: bluesky, facebook, github, instagram, linkedin, threads, tiktok, x, or youtube |
resources[].type | string | yes | Resource type: account, organization, or post. See Resource types |
resources[].identifier | string | yes | Username or ID to look up. Send the bare username or id, not a profile URL — pasted URLs fail validation. For post on Bluesky, X, and Threads, combine the handle and the post id: handle:post_id |
Resource types
Most platforms give every profile a single address, so account is the only type they accept.
LinkedIn is the exception. It keeps people and companies in separate places, and the same name can belong to both: linkedin.com/in/meta is a person’s profile, while linkedin.com/company/meta is the company. Set type to match the one you want.
| Platform | Accepted types |
|---|---|
bluesky | account for profiles, post for individual posts |
facebook, github, instagram, tiktok | account |
linkedin | account for profiles under /in/, organization for pages under /company/ |
threads | account for profiles, post for individual threads |
x | account for profiles, post for individual posts |
youtube | account for channels, post for individual videos |
Pairing a platform with a type it doesn’t accept fails validation: the request returns a 422 error naming the incompatible pair. See Error Responses.
Use organization for any company page, including non-profits, universities, and government bodies.
{
"resources": [
{"platform": "linkedin", "type": "organization", "identifier": "nasa"}
]
}
Because both can exist under the same name, a mismatched type on LinkedIn returns the wrong profile rather than an error. If a LinkedIn lookup comes back empty, check which address the profile actually lives at.
Use post to track a single post instead of a whole profile. On X, a post lives at x.com/<handle>/status/<post_id>, so the identifier combines both parts with a colon:
{
"resources": [
{"platform": "x", "type": "post", "identifier": "jack:20"}
]
}
Threads works the same way: a thread lives at threads.com/@<handle>/post/<post_id>, so combine the handle and the post id from the URL:
{
"resources": [
{"platform": "threads", "type": "post", "identifier": "zuck:C89oeSORn81"}
]
}
So does Bluesky: a post lives at bsky.app/profile/<handle>/post/<post_id>, and the handle keeps its full domain, just like account lookups:
{
"resources": [
{"platform": "bluesky", "type": "post", "identifier": "bsky.app:3l6oveex3ii2l"}
]
}
On YouTube, the identifier for a post is the video id from the watch URL — for youtube.com/watch?v=dQw4w9WgXcQ, send dQw4w9WgXcQ. No channel name is needed:
{
"resources": [
{"platform": "youtube", "type": "post", "identifier": "dQw4w9WgXcQ"}
]
}
Post lookups return post metrics — views, likes, and reposts where the platform reports them — instead of profile metrics. A Bluesky, X, or Threads post identifier without the handle part fails validation with a descriptive 422 error, and on X the post id must be the numeric id from the status URL. X does not report view counts for posts published before view counting existed (late 2022), so views can come back unavailable on older posts. X also sometimes reports only rounded counts (1.2K) for a post; when that happens the rounded figure is returned expanded (1200). Threads and Bluesky do not report view counts at all.
Constraints
- Maximum 10 resources per request
- Duplicate resources (same platform + type + identifier) are automatically removed
Example Request
curl -X POST https://metrika.run/api/v1/resources/search \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resources": [
{"platform": "instagram", "type": "account", "identifier": "natgeo"},
{"platform": "x", "type": "account", "identifier": "nasa"},
{"platform": "instagram", "type": "account", "identifier": "not_a_real_account"}
]
}'
Success Response
200 OK
{
"data": [
{
"platform": "instagram",
"type": "account",
"identifier": "natgeo",
"status": "available",
"metrics": {
"followers": {
"value": 284000000,
"status": "available",
"collected_at": "2026-06-27T10:00:00.000Z",
"semantic": "followers"
},
"following": {
"value": 74,
"status": "available",
"collected_at": "2026-06-27T10:00:00.000Z",
"semantic": "following"
}
}
},
{
"platform": "x",
"type": "account",
"identifier": "nasa",
"status": "available",
"metrics": {
"followers": {
"value": 96800000,
"status": "available",
"collected_at": "2026-06-27T10:00:00.000Z",
"semantic": "followers"
},
"following": {
"value": 337,
"status": "available",
"collected_at": "2026-06-27T10:00:00.000Z",
"semantic": "following"
}
},
"attributes": {
"verified": {
"value": true,
"status": "available",
"collected_at": "2026-06-27T10:00:00.000Z",
"semantic": "verified"
}
}
},
{
"platform": "instagram",
"type": "account",
"identifier": "not_a_real_account",
"status": "not_found"
}
]
}
The data array contains one entry per requested resource in the same order as the request. Until a resource has been resolved and collected, its entry contains only platform, type, identifier, and status, with no datapoints yet. Requesting a new resource starts background collection, so its datapoints appear on a later request once the data is ready.
The status field at the resource level indicates the availability of the resource:
| Status | Description |
|---|---|
available | The resource was found and data is being collected |
pending | Data collection for this resource is in progress |
not_found | The resource could not be found on the platform |
stale | Data was collected before, but recent collection attempts have failed, so existing datapoints may be out of date. Collection is retried regularly |
unavailable | Data for this resource could not be collected. Collection is retried regularly |
The status field inside each metric indicates the availability of the data:
| Status | Description |
|---|---|
available | Metric data is current and ready |
unavailable | Metric is not available for this platform |
pending | Metric is being collected and will be available soon |
Each metric and attribute also carries a semantic field: the platform’s own term for what the value counts. It usually matches the datapoint name, but not always: YouTube followers carry subscribers, TikTok posts carry videos, and Facebook followers carry likes. Datapoint names are stable across platforms, so key your integration on them and use semantic to label values in the platform’s own language.
Error Responses
Unauthorized
401 Unauthorized
The request did not include a valid API token. Include it in the Authorization: Bearer header.
Validation Error
422 Unprocessable Entity
{
"errors": [
"Resources can't be blank"
]
}
The request body failed validation. The errors array contains one or more descriptive messages. For example, pairing a platform with a resource type it doesn’t accept:
{
"errors": [
"Resources type 'organization' is not supported on platform 'instagram'"
]
}
Identifiers that look like URLs, contain spaces or fragments, or don’t match the platform’s address structure are rejected with a 422 rather than being accepted and returning no data:
{
"errors": [
"Resources identifier 'https://www.facebook.com/natgeo/' is not valid on platform 'facebook': send the plain username or id from the profile URL, not the URL itself"
]
}
Quota Exceeded
403 Forbidden
{
"error": "You have exceeded your quota of 500 requests, resets on 2026-08-01."
}
The account’s monthly usage quota has been exhausted. The error message indicates when the allowance resets (the start of the next calendar month).
Rate Limited
429 Too Many Requests
{
"error": "Rate limit exceeded. Please try again later."
}
The rate limit for this account has been exceeded. Back off and retry once the window resets.
Error Code Reference
| Status | Description |
|---|---|
| 200 | Success |
| 401 | Missing or invalid API token |
| 403 | Monthly usage quota exhausted |
| 422 | Request validation failed |
| 429 | Rate limit exceeded |
| 500 | Internal server error |