Social Media API
Stream Social Media Data From Major Platforms
Read profiles, pages, posts and hashtags on Facebook, Instagram, LinkedIn, Reddit, TikTok, Glassdoor and the BBB, and stream their content.
Social Media API
Public social data and its content, streamed in one format#
Look up a public profile, page, post, hashtag or company on nine networks, then collect its content: posts, comments, reactions, followers, ads, interviews or complaints. Run a one-off job or a 5-day subscription that keeps collecting new content; Zembra handles the collection, scheduling and network changes, and returns every network in the same job, target and stream structure.
At a glance
- 9 networks: Facebook, Instagram, LinkedIn, Reddit, TikTok, X, Threads, Glassdoor and the BBB
- Resource details for 50 credits, returned in the response
- One-off jobs: 75 credits + 3 per item collected
- 5-day subscriptions: 150 credits + 3 per item, rechecked every 3 to 24 hours
- Reading results with
GETis free; webhooks deliver new items in pages of 100
Why teams choose Zembra for this#
Coverage
Nine networks and 22 resource types, from Instagram users and hashtags to Facebook advertisers, subreddits, LinkedIn companies, TikTok videos, Glassdoor companies and BBB companies.
One structure
Every response has the same three parts: the job or subscription, the target resource, and the stream of content items, with the network's full payload kept in rawData.
Pay for what is collected
The base fee is charged when the job starts and 3 credits per item as items arrive. sizeLimit and dateLimit cap what is collected and what you pay.
Adaptive rechecks
Subscriptions recheck a resource at an interval derived from how often it publishes, never more often than every 3 hours and never less than once a day.
Push or pull
Poll with free GET calls, or receive each batch of new items on your webhook, Slack or email, in pages of up to 100 items.
Fair failures
Rejected calls and errors cost nothing. A resource that does not exist on the network costs a 25-credit minimum, and asking again for a subscription you already have is free.
How it works
From request to data#
-
1
Look up the resource (optional)
GET /social/{network}/{resource}/{slug}returns the resource's details right away, such as an Instagram user's name, post count and follower counts. 50 credits. -
2
Start a job or a subscription
POST /social/job/…/{content}for a one-off collection, orPOST /social/subscription/…/{content}to keep collecting for 5 days. The answer is immediate (201) with the job and the target; the base fee is charged. -
3
Zembra collects the content
Items are collected in the background until your
sizeLimitordateLimitis reached. Each item collected is charged 3 credits. -
4
Receive it
GETthe job or subscription for free:206while the first collection is still running,200once it has finished. Webhooks receive each batch of new items as it is processed. -
5
Subscriptions keep going
For 5 days, Zembra rechecks the resource every 3 to 24 hours and pushes only the new items. With
allowNewContent, new posts keep coming even aftersizeLimitis reached.
Pricing
Pay per use, no commitment#
Pay per use in credits: a base fee when the request is accepted, then 3 credits per item collected. Credits get cheaper as your volume grows: how credit pricing works.
What a call costs#
| Call | Credits | Starts at |
|---|---|---|
| Resource details Per lookup; +5 with permalink. |
50 | $0.01 |
| One-off job, base fee When the job is created; +25 with permalink. |
75 | $0.015 |
| Subscription, base fee When the subscription is created; 0 when it already exists (409); +25 with permalink. |
150 | $0.03 |
| Per item collected Charged as items are processed, capped by sizeLimit. |
3 | $0.0006 |
| Resource not found Minimum charge on a 404 for details, jobs and subscriptions. |
25 | $0.005 |
| Reading, listing, updating or cancelling All GET, PATCH and DELETE calls. |
0 | Free |
Examples#
| Project | Credits | Price |
|---|---|---|
| Details of 10 profiles | 500 | Free on sign-up |
| 100 posts of an Instagram user, one-off | 375 | Free on sign-up |
| A 5-day subscription collecting 50 comments | 300 | Free on sign-up |
Prices use the credit tiers of the cost estimator, where you can estimate your own project. The first 10,000 credits are free.
API reference
Requests and parameters#
Base URL https://api.zembra.io, authenticated with Authorization: Bearer <token>. A sandbox at https://sandbox.zembra.io returns realistic dummy data without using credits.
curl --location 'https://api.zembra.io/social/job/instagram/user/zembra/posts' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--data '{
"internalId": "",
"sizeLimit": null,
"dateLimit": null
}'
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.zembra.io/social/job/instagram/user/zembra/posts");
request.Headers.Add("Authorization", "Bearer {token}");
var content = new StringContent("{
\"internalId\": \"\",
\"sizeLimit\": null,
\"dateLimit\": null
}", null, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.zembra.io/social/job/instagram/user/zembra/posts"
method := "POST"
payload := strings.NewReader(`{
"internalId": "",
"sizeLimit": null,
"dateLimit": null
}`)
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Authorization", "Bearer {token}")
req.Header.Add("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{
\"internalId\": \"\",
\"sizeLimit\": null,
\"dateLimit\": null
}");
Request request = new Request.Builder()
.url("https://api.zembra.io/social/job/instagram/user/zembra/posts")
.method("POST", body)
.addHeader("Authorization", "Bearer {token}")
.addHeader("Content-Type", "application/json")
.build();
Response response = client.newCall(request).execute();
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer {token}");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"internalId": "",
"sizeLimit": null,
"dateLimit": null
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://api.zembra.io/social/job/instagram/user/zembra/posts", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
<?php
$client = new Client();
$headers = [
'Authorization' => 'Bearer {token}',
'Content-Type' => 'application/json'
];
$body = '{
"internalId": "",
"sizeLimit": null,
"dateLimit": null
}';
$request = new Request('POST', 'https://api.zembra.io/social/job/instagram/user/zembra/posts', $headers, $body);
$res = $client->sendAsync($request)->wait();
echo $res->getBody();
import requests
import json
url = "https://api.zembra.io/social/job/instagram/user/zembra/posts"
payload = json.dumps({
"internalId": "",
"sizeLimit": None,
"dateLimit": None
})
headers = {
'Authorization': 'Bearer {token}',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
require "uri"
require "json"
require "net/http"
url = URI("https://api.zembra.io/social/job/instagram/user/zembra/posts")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = "Bearer {token}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"internalId": "",
"sizeLimit": nil,
"dateLimit": nil
})
response = https.request(request)
puts response.read_body
Response
{
"message": "Streaming Instagram posts order received for user < zembra >. Please allow a few minutes before requesting data.",
"data": {
"job": {
"jobId": "",
"network": "instagram",
"slug": "zembra",
"resource": "user",
"content": "posts",
"internalId": "",
"requestedAt": "2023-01-01T00:00:00Z",
"expiresAt": "2023-01-01T00:00:00Z",
"sizeLimit": 100,
"dateLimit": null,
"cost": 150,
"active": false
},
"target": {
"slug": "zembra",
"remoteId": "32146585423198958",
"displayUrl": "https://instagram.com/instagram/profile.jpg",
"url": "https://instagram.com/zembra/",
"link": "https://my.zembratech.com/dashboard/instagram/users/MWJmZTc5MDZjMzg5OGI3OGMyNjJiODNjOGJjMWI3Y2Q/posts",
"network": "instagram",
"resource": "user",
"rawData": []
}
}
}
{
"message": "Invalid resource for selected network"
}
{
"message": "Invalid bearer token"
}
{
"message": "Cannot process request: balance too low!"
}
{
"message": "Requested network [Facebook] is not enabled for your account"
}
{
"message": "Requested post was not found on Instagram."
}
GET /social/{network}/{resource}/{slug}: Get a resource's details#
network | path, required: bbb, facebook, glassdoor, instagram, linkedin, reddit, threads, tiktok, x Must be enabled on your account. |
|---|---|
resource | path, required: depends on the network For example user, hashtag or post on Instagram; page, post, advertiser or keyword on Facebook; company, profile, post or keyword on LinkedIn. |
slug | path, required, URL-encoded, up to 2,048 characters The resource's identifier on the network, for example an Instagram username. Each resource type checks its own format. |
permalink | boolean, optional Host permanent copies of media links (+5 credits). |
POST /social/job/{network}/{resource}/{slug}/{content}: Collect a resource's content once#
content | path, required: depends on the resource What to collect, for example posts, followers or follows of an Instagram user, comments or reactions of a post, ads of a Facebook advertiser, interviews of a Glassdoor company, complaints of a BBB company. |
|---|---|
sizeLimit | integer, optional, at least 10 The most recent items to collect, and to pay for. Default: no limit. |
dateLimit | integer, optional, Unix seconds from 2010-01-01 to 5 minutes ago Only keep items published after this date. |
internalId | string, optional, 3–255 characters Your own reference, returned with the job and in webhooks. |
permalink | boolean, optional Host permanent copies of media links (+25 credits). |
status, type, country, media, from, to | optional, Facebook advertiser and keyword ads only Filter ads: status active or inactive; type credit, employment, housing or issues; a 2-letter country; media image, meme, video or none; a from and to date. |
country | optional, 2-letter code, LinkedIn only Country context for the LinkedIn request. |
GET /social/job/{jobId}: Read a job's content (free)#
limit | integer, optional, ≥ 0 How many items to return. Only paginates what you read; it never changes what is collected. |
|---|---|
offset | integer, optional, ≥ 0 Items to skip. |
GET /social/jobs: List your jobs (free)#
limit | integer, optional, 1–100 Default 100. |
|---|---|
offset | integer, optional Jobs to skip. |
sortBy | optional: timestamp, internalId, network Default timestamp, newest first. |
sortDirection | optional: asc, desc Overrides the default direction. |
internalId, slug | string, optional, 3–1,024 characters Partial match filters. |
network, resource, content | optional Exact filters. |
all | boolean, optional Include deleted jobs. |
POST /social/subscription/{network}/{resource}/{slug}/{content}: Collect a resource's content for 5 days#
sizeLimit | integer, optional, at least 10 How many items to collect. Asking for more than an existing subscription starts a new one. |
|---|---|
dateLimit | integer, optional, Unix seconds from 2010-01-01 to 5 minutes ago Only keep items published after this date. |
allowNewContent | boolean, optional Keep collecting items published after the subscription started, even past sizeLimit. Default: your account setting. |
internalId | string, optional, 3–255 characters Your own reference. |
permalink | boolean, optional Host permanent copies of media links (+25 credits). |
GET /social/subscription/{network}/{resource}/{slug}/{content}: Read a subscription's content (free)#
limit | integer, optional, ≥ 0 How many items to return. |
|---|---|
offset | integer, optional, ≥ 1 Items to skip. |
GET /social/subscriptions: List your subscriptions (free)#
limit, offset, sortBy, sortDirection, internalId, slug, network, resource, content, all | optional Same filters as the job list; limit 1–100, default 100. |
|---|
PATCH /social/subscription/{network}/{resource}/{slug}/{content}: Change a subscription (free)#
internalId | string, optional, 3–255 characters A new reference. |
|---|---|
allowNewContent | boolean, optional Turn collection of newly published items on or off. |
DELETE /social/subscription/{network}/{resource}/{slug}/{content}: Cancel a subscription (free)#
Status codes#
200 | Details found, or the job or subscription has finished its first collection. |
|---|---|
201 | Job or subscription created; the target's details are in the response. |
206 | Collection still running: the items collected so far. |
400 | Invalid network, resource, content, slug or parameter. Free. |
401 | Invalid bearer token. |
402 | Balance too low to start the request. |
403 | The network is not enabled for your account. |
404 | The resource was not found on the network (25 credits), or the job or subscription does not exist. |
409 | You already have this subscription; it is returned at no cost. |
410 | The job has expired or was deleted. |
429 | Rate limit reached. |
503 | The network is temporarily down. Free. |
Data
What you get back#
A job and its stream#
job.jobId | string (UUID) The job's ID, used with GET /social/job/{jobId}. |
|---|---|
job.network, resource, slug, content | string What the job collects. |
job.sizeLimit, dateLimit, internalId, permalink, extraParams | as sent The options the job was created with. |
job.requestedAt, expiresAt | datetime (UTC) A job's data stays readable for 5 days. |
job.cost | integer Credits charged so far: base fee plus items collected. |
job.active | boolean False once the job is deleted. |
target | object The resource: slug, remoteId, url, network, resource, the network-specific fields (for an Instagram user: name, profileUrl, posts, followed, follow) and rawData. |
stream[] | array The content items. Fields depend on the content type; an Instagram post has slug, remoteId, type, timestamp, displayUrl, text, likes, comments, url and rawData. |
returned | integer Number of items in this response. |
Example response
{
"status": "SUCCESS",
"message": "List of Instagram posts for user < my-brand >.",
"data": {
"job": {
"jobId": "3f2b7c1e-8a4d-4c55-9e0a-6d1f2b3c4d5e",
"network": "instagram",
"resource": "user",
"slug": "my-brand",
"content": "posts",
"sizeLimit": 100,
"dateLimit": null,
"internalId": "brand-42",
"permalink": false,
"extraParams": [],
"requestedAt": "2026-01-01 08:00:00",
"expiresAt": "2026-01-06 08:00:00",
"url": null,
"cost": 375,
"active": true
},
"target": {
"slug": "my-brand",
"remoteId": "1784140000000000",
"name": "My Brand",
"profileUrl": "https://...",
"posts": 812,
"followed": 25400,
"follow": 310,
"rawData": {
"…": "everything else the network shows"
},
"url": "https://www.instagram.com/my-brand",
"network": "instagram",
"resource": "user"
},
"stream": [
{
"slug": "C1a2B3c4D5e",
"remoteId": "3290000000000000000",
"type": "image",
"timestamp": "2025-12-30 17:12:00",
"displayUrl": "https://...",
"text": "New collection out today!",
"likes": 1520,
"comments": 48,
"rawData": {
"…": "the full post payload"
},
"url": "https://www.instagram.com/p/C1a2B3c4D5e/"
}
],
"returned": 1
}
}
What you can collect#
Facebook: page posts, post comments and reactions, advertiser and keyword ads. Instagram: user posts, followers and follows, hashtag posts, post comments and reactions. LinkedIn: company posts and people, profile posts, keyword posts, post comments, reactions and shares. Reddit: subreddit, user and keyword posts, post comments, keyword comments, subreddits and users. TikTok: user and hashtag videos, video comments. X and Threads: user posts. Glassdoor: company interviews. BBB: company complaints.
Jobs or subscriptions#
A job collects once and its data stays readable for 5 days. A subscription lasts 5 days and keeps rechecking the resource. There is one subscription per resource and content: asking again returns the existing one with 409 at no cost, unless you ask for a higher sizeLimit or an older dateLimit, which starts a new one.
How often subscriptions recheck#
The interval is the average gap between the resource's 12 most recent items, kept between 3 hours and 24 hours. Content without timestamps, such as followers or Facebook reactions, is rechecked every 24 hours.
Webhooks#
Each batch of new items goes to every webhook on your account, by HTTP POST, Slack or email, with a reference, the type (social-job or social-subscription), your internalId, the job or subscription, the target and the items, in pages of up to 100 items (page, pages).
When your balance runs low#
Before each collection pass Zembra checks that your balance covers the items it is about to collect; if not, collection stops instead of running into debt. Items are charged as they are processed, so GET only returns items you have paid for.
Enabled networks#
Networks are enabled per account. Instagram, LinkedIn and X are enabled once the account has bought credits; a call to a network that is not enabled answers 403.
Batch requests#
POST /batch/social/details, /batch/social/job and /batch/social/subscription take many resources in one call, with the same options per row; one batch call per minute.
Performance
Speed and limits#
Rate limits #
100 requests per minute per account on calls that start work, 1,000 per minute on reads; batch endpoints accept one call per minute.
Collection size #
sizeLimit is at least 10; without it, everything available is collected (and charged).
Date range #
dateLimit is a Unix timestamp between 2010-01-01 and 5 minutes ago.
Retention #
A job's data can be read for 5 days; after that GET answers 410. Subscriptions run for 5 days.
Lists and pages #
Job and subscription lists return at most 100 entries per call; webhooks carry at most 100 items per delivery.
Identifiers #
Slugs up to 2,048 characters; internalId 3–255 characters.
Supported platforms
7 platforms, one format#
Use cases
What teams build with it#
Social listening
Follow what people post about a brand on hashtags, keywords and comments, with new items pushed as they appear.
Competitive intelligence
Track competitors' posts, follower counts and Facebook ads, filtered by status, country or date.
Employer branding
Collect Glassdoor interviews and LinkedIn company posts and people to understand how a company is seen by candidates.
Complaint monitoring
Pull BBB complaints next to reviews to see every channel customers use to complain.
FAQ
Common questions#
Which networks are supported?
Facebook, Instagram, LinkedIn, Reddit, TikTok, X, Threads, Glassdoor and the Better Business Bureau. Instagram, LinkedIn and X are enabled once your account has bought credits. #
What is the difference between a job and a subscription?
A job collects a resource's content once (75 credits + 3 per item). A subscription keeps collecting new content for 5 days, rechecking every 3 to 24 hours (150 credits + 3 per item). #
How do I control the cost?
Set sizeLimit to the number of items you need, or dateLimit to skip older items. You pay the base fee plus 3 credits per item actually collected. #
Do I have to poll for results?
No. Add a webhook and each batch of new items is pushed to you, in pages of up to 100. Polling with GET also works and is free. #
What happens if the profile does not exist?
You get a 404 and a 25-credit minimum charge. Other errors, such as an invalid parameter or a network that is down, cost nothing. #
Can I get more than the network's display fields?
Yes. Every resource and item includes rawData, the full payload as the network returned it. #
How long is the data kept?
A job's data can be read for 5 days after it was requested; a subscription runs for 5 days. #
Start today for free
Create an account and get 10,000 free credits to try the Social Media API and every other Zembra service. No commitment, no minimum.