NewAPI contract changesRead more →

Public social data API

6 platforms live

Public social media data. One API, one schema.

SocialTap returns public social media data from the major platforms, normalized into one JSON schema per resource. One API key, one credit balance, and you only pay for requests that succeed.

Get 100 free creditsRead the docs

cURL
curl -G 'https://staging-fly.socialtap.dev/v1/tiktok/profile' \
  --data-urlencode 'handle=https://www.tiktok.com/@nasa' \
  -H "x-api-key: $SOCIALTAP_API_KEY"
Node.js
const params = new URLSearchParams({
  handle: "https://www.tiktok.com/@nasa",
});
const res = await fetch(`https://staging-fly.socialtap.dev/v1/tiktok/profile?${params}`, {
  headers: { "x-api-key": process.env.SOCIALTAP_API_KEY ?? "" },
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
const { data, meta } = body;
console.log(data, meta.credits_charged);
Python
import os

import requests

res = requests.get(
    "https://staging-fly.socialtap.dev/v1/tiktok/profile",
    params={"handle": "https://www.tiktok.com/@nasa"},
    headers={"x-api-key": os.environ["SOCIALTAP_API_KEY"]},
)
body = res.json()
if not res.ok:
    raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
data, meta = body["data"], body["meta"]
print(data, meta["credits_charged"])
Go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
)

func main() {
	q := url.Values{}
	q.Set("handle", "https://www.tiktok.com/@nasa")
	req, err := http.NewRequest("GET", "https://staging-fly.socialtap.dev/v1/tiktok/profile?"+q.Encode(), nil)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("x-api-key", os.Getenv("SOCIALTAP_API_KEY"))
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	var body struct {
		Data  json.RawMessage `json:"data"`
		Meta  map[string]any  `json:"meta"`
		Error *struct {
			Code    string `json:"code"`
			Message string `json:"message"`
		} `json:"error"`
	}
	if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
		log.Fatal(err)
	}
	if body.Error != nil {
		log.Fatalf("%s: %s", body.Error.Code, body.Error.Message)
	}
	fmt.Println(string(body.Data), body.Meta["credits_charged"])
}
PHP
<?php

$query = http_build_query([
    'handle' => 'https://www.tiktok.com/@nasa',
]);
$ch = curl_init('https://staging-fly.socialtap.dev/v1/tiktok/profile?' . $query);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['x-api-key: ' . getenv('SOCIALTAP_API_KEY')],
]);
$body = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) >= 400) {
    throw new RuntimeException($body['error']['code'] . ': ' . $body['error']['message']);
}
print_r($body['data']);
echo $body['meta']['credits_charged'], PHP_EOL;
Ruby
require "json"
require "net/http"

uri = URI("https://staging-fly.socialtap.dev/v1/tiktok/profile")
uri.query = URI.encode_www_form('handle' => 'https://www.tiktok.com/@nasa')
req = Net::HTTP::Get.new(uri)
req["x-api-key"] = ENV.fetch("SOCIALTAP_API_KEY")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
body = JSON.parse(res.body)
raise "#{body["error"]["code"]}: #{body["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
pp body["data"]
puts body["meta"]["credits_charged"]
Java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

public class Main {
    public static void main(String[] args) throws Exception {
        String query = "handle=" + encode("https://www.tiktok.com/@nasa");
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://staging-fly.socialtap.dev/v1/tiktok/profile?" + query))
            .header("x-api-key", System.getenv("SOCIALTAP_API_KEY"))
            .GET()
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // An error is {"error": {"code", "message", ...}}; success is {data, meta}, meta.credits_charged its price.
        if (response.statusCode() >= 400) throw new RuntimeException(response.body());
        System.out.println(response.body());
    }

    static String encode(String value) {
        return URLEncoder.encode(value, StandardCharsets.UTF_8);
    }
}
.NET
using System.Text.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("SOCIALTAP_API_KEY"));

var query = string.Join("&",
    "handle=" + Uri.EscapeDataString("https://www.tiktok.com/@nasa"));
var res = await http.GetAsync("https://staging-fly.socialtap.dev/v1/tiktok/profile?" + query);
using var body = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
if (!res.IsSuccessStatusCode)
{
    var error = body.RootElement.GetProperty("error");
    throw new Exception($"{error.GetProperty("code")}: {error.GetProperty("message")}");
}
Console.WriteLine(body.RootElement.GetProperty("data"));
Console.WriteLine(body.RootElement.GetProperty("meta").GetProperty("credits_charged"));
Rust
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }, serde_json = "1"
use std::env;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let res = reqwest::blocking::Client::new()
        .get("https://staging-fly.socialtap.dev/v1/tiktok/profile")
        .query(&[("handle", "https://www.tiktok.com/@nasa")])
        .header("x-api-key", env::var("SOCIALTAP_API_KEY")?)
        .send()?;
    let ok = res.status().is_success();
    let body: serde_json::Value = res.json()?;
    if !ok {
        return Err(format!("{}: {}", body["error"]["code"], body["error"]["message"]).into());
    }
    println!("{} {}", body["data"], body["meta"]["credits_charged"]);
    Ok(())
}

handle takes any form. TikTok username with or without the @ (nasa, @nasa), a profile URL (tiktok.com/@nasa, m.tiktok.com/@nasa, with or without query parameters), or a numeric user ID (tiktok.com/share/user/<id> or the bare ID). Usernames are case-insensitive.

200 OK · 1 credit
{
  "data": {
    "approximate_fields": [],
    "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/e5143212a59c09a008bf50f487d54d1f~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=9341391e&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
    "banner_url": null,
    "bio": "Making the seemingly impossible, possible.✨",
    "category": null,
    "country_code": null,
    "created_at": "2026-07-20T15:55:49Z",
    "display_name": "NASA",
    "external_links": [
      {
        "title": null,
        "url": "https://science.nasa.gov/feature/artemis-ii-preliminary-lunar-science-report"
      }
    ],
    "id": "7664638705177150477",
    "is_private": false,
    "is_verified": true,
    "location": null,
    "metrics": {
      "follower_count": 2140425,
      "following_count": 23,
      "post_count": 60,
      "total_like_count": 11385816,
      "total_view_count": null
    },
    "platform": "tiktok",
    "platform_specific": {},
    "type": "organization",
    "unknown_fields": [],
    "url": "https://www.tiktok.com/@nasa",
    "username": "nasa"
  },
  "meta": {
    "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
    "platform": "tiktok",
    "source": "page",
    "is_cached": false,
    "credits_charged": 1,
    "latency_ms": 0,
    "fetched_at": "2026-10-25T12:00:00Z",
    "input": {
      "kind": "profile",
      "id": "7664638705177150477",
      "url": "https://www.tiktok.com/@nasa"
    },
    "credits_remaining": 9999,
    "client_request_id": null,
    "warnings": [],
    "mode": "live"
  }
}
Failed requests 0 credits
Cache hits 0 credits
Credits never expire
From $0.75 / 1k

Coverage

Every platform. The same JSON, field for field.

All APIs

How it works

One integration. Learn a schema once, then add platforms without new parsing code.

01
GET /v1/facebook/postGET /v1/instagram/postx-api-key: st_live_••••••••

One key, one URL pattern

Send x-api-key on every request, the same way for every platform.

02
username · display_name · url · type · bio

One schema per resource

Ads, Comments, Posts, Profiles and Transcripts have the same fields on every platform. null means unknown, never zero.

03
200 ok1 credit
404 not_found0 credits
503 upstream_blocked0 credits
200 from cache0 credits

Pay only for success

Prepaid credits, one balance. Errors cost nothing. Cache hits cost nothing.

For agents

Built for coding agents. Paste one prompt, ship the integration.

llms.txt
Every page is also Markdown; the whole docs are one file.
/mcp
A remote MCP server on the same key: get_post, get_profile and get_transcript and more.
st_test_
Test keys answer from recorded responses: free and deterministic.

Build with AI agents

Prompt
Integrate the SocialTap API (https://staging-fly.socialtap.dev) into this project.

1. Read https://staging-fly.socialtap.dev/llms.txt first, then the Markdown docs page of the endpoint you need (linked there). Use only endpoints and parameters documented there.
2. Send the API key in the x-api-key header, read from the SOCIALTAP_API_KEY environment variable. Never hard-code it; if it is not set, ask me for it.
3. Branch on error.code: retry only the retryable codes the docs list, with backoff. On list endpoints follow page.next_cursor while page.has_more is true.
4. Verify with one real request before building on it, for example:

   curl -G 'https://staging-fly.socialtap.dev/v1/tiktok/profile' \
     --data-urlencode 'handle=https://www.tiktok.com/@nasa' \
     -H "x-api-key: $SOCIALTAP_API_KEY"

   and check that data is not empty.
5. Tell me which endpoint you used, what meta.credits_charged was, and any error code you saw.
Claude Code
claude mcp add --transport http socialtap https://staging-fly.socialtap.dev/mcp --header "authorization: Bearer $SOCIALTAP_API_KEY"
Claude Code (.mcp.json)
{
  "mcpServers": {
    "socialtap": {
      "type": "http",
      "url": "https://staging-fly.socialtap.dev/mcp",
      "headers": {
        "authorization": "Bearer ${SOCIALTAP_API_KEY}"
      }
    }
  }
}
Cursor (mcp.json)
{
  "mcpServers": {
    "socialtap": {
      "url": "https://staging-fly.socialtap.dev/mcp",
      "headers": {
        "authorization": "Bearer ${env:SOCIALTAP_API_KEY}"
      }
    }
  }
}
Codex
codex mcp add socialtap --url https://staging-fly.socialtap.dev/mcp --bearer-token-env-var SOCIALTAP_API_KEY
Codex (config.toml)
[mcp_servers.socialtap]
url = "https://staging-fly.socialtap.dev/mcp"
bearer_token_env_var = "SOCIALTAP_API_KEY"

Endpoints

Call them today. Each has a docs page, a playground and live status.

Open the playground
Facebook Post APIBetaPostGET /v1/facebook/post1 creditFacebook Ad Library Search APIBetaAdGET /v1/facebook/search_ads1 credit / pageFacebook Video Transcript APIBetaTranscriptGET /v1/facebook/transcriptFrom 1 creditInstagram Post APIBetaPostGET /v1/instagram/post1 creditInstagram Posts APIBetaPostGET /v1/instagram/posts1 credit / pageInstagram Profile APIBetaProfileGET /v1/instagram/profile1 creditInstagram Reels Transcript APIBetaTranscriptGET /v1/instagram/transcriptFrom 1 creditRumble Comments APIBetaCommentGET /v1/rumble/comments1 credit / pageRumble Video APIBetaPostGET /v1/rumble/post1 creditRumble Channel Videos APIBetaPostGET /v1/rumble/posts1 credit / pageRumble Channel APIBetaProfileGET /v1/rumble/profile1 creditRumble Search APIBetaPostGET /v1/rumble/search_posts1 credit / pageRumble Transcript APIBetaTranscriptGET /v1/rumble/transcriptFrom 1 creditThreads Profile APIBetaProfileGET /v1/threads/profile1 creditTikTok Comments APIBetaCommentGET /v1/tiktok/comments1 credit / pageTikTok Post APIBetaPostGET /v1/tiktok/post1 creditTikTok Posts APIBetaPostGET /v1/tiktok/posts1 credit / pageTikTok Profile APIBetaProfileGET /v1/tiktok/profile1 creditTikTok Transcript APIBetaTranscriptGET /v1/tiktok/transcriptFrom 1 creditYouTube Comments APIBetaCommentGET /v1/youtube/comments1 credit / pageYouTube Video APIBetaPostGET /v1/youtube/post1 creditYouTube Channel Videos APIBetaPostGET /v1/youtube/posts1 credit / pageYouTube Channel APIBetaProfileGET /v1/youtube/profile1 creditYouTube Transcript APIBetaTranscriptGET /v1/youtube/transcriptFrom 1 credit

Pricing

Prepaid credits. From $0.75 per 1,000, and they never expire.

Every price
20,000 credits

$29

$1.45 per 1,000 credits

80,000 credits

$99

$1.24 per 1,000 credits

400,000 credits

$399

$1.00 per 1,000 credits

2,000,000 creditsBest value

$1,499

$0.75 per 1,000 credits

Your first 100 requests are on us.

Get your API keyRead the docs